-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
py: Add enum support and minimal metaclass features #18416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Damien George <damien@micropython.org>
Adds four new configuration flags to control metaclass functionality: - MICROPY_PY_METACLASS_INIT: Enable metaclass __init__ invocation - MICROPY_PY_METACLASS_OPS: Enable metaclass operator overloading - MICROPY_PY_METACLASS_PROPERTIES: Enable metaclass properties/methods - MICROPY_PY_METACLASS_PREPARE: Enable __prepare__ method (PEP 3115) These flags allow ports to balance functionality against code size, enabling enum support and python-statemachine compatibility. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Fixes type checking throughout the codebase to support custom metaclasses: - py/vm: Fix LOAD_ATTR fast path to avoid treating type objects as instances - py/objobject: Update object.__new__ to accept custom metaclasses - py/objtype: Replace assert() checks with mp_obj_is_subclass_fast() Adds metaclass method support: - type_make_new now looks up custom __new__ through inheritance chain - type_call checks for custom __call__ on metaclass - Enables metaclass customization of class creation and instantiation Removes DEBUG_printf statements for cleaner production code. This enables enum.Enum and python-statemachine compatibility. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Implements PEP 3115 by calling __prepare__ before executing class body. The __prepare__ method allows metaclasses to return a custom namespace dict (e.g., OrderedDict) for tracking member insertion order. Execution order in __build_class__: 1. Determine metaclass 2. Call __prepare__(name, bases) if it exists 3. Execute class body in returned namespace 4. Call metaclass(name, bases, namespace) to create class This is required for enum.auto() to generate sequential values based on definition order. Guarded by MICROPY_PY_METACLASS_PREPARE. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Metaclass tests: - class_metaclass_init.py: Test __init__ invocation on metaclass - class_metaclass_prepare.py: Test __prepare__ method (PEP 3115) - class_metaclass_property.py: Test properties and methods on metaclasses Enum tests: - enum_auto.py: Test auto() value generation - enum_flag.py: Test Flag and IntFlag bitwise operations - enum_strenum.py: Test StrEnum string-valued enums CPython difference: - types_enum_isinstance.py: Document isinstance() behavior difference These tests validate the metaclass features added in previous commits and provide compatibility verification with micropython-lib enum package. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Adds enum package from micropython-lib to unix port variants: - coverage variant: For testing metaclass and enum features - standard variant: For general enum availability The enum package provides Enum, IntEnum, Flag, IntFlag, StrEnum, and auto() compatible with CPython's enum module. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Updates micropython-lib submodule to include enum package with fixes: - Fix __import__() to use positional args (MicroPython compatibility) - Fix IntFlag bitwise operations to use _value_ attribute directly - Add Flag, IntFlag, StrEnum, auto(), and unique() support These changes enable full enum functionality with the metaclass features added in previous commits. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
The metaclass.py test now produces output because metaclass __init__ invocation is now implemented and functional. Generate the expected output file based on the correct behavior. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
|
Code size report: |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #18416 +/- ##
==========================================
- Coverage 98.38% 98.24% -0.15%
==========================================
Files 171 171
Lines 22294 22447 +153
==========================================
+ Hits 21933 22052 +119
- Misses 361 395 +34 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Adds feature detection to skip tests when required features are not available (e.g. in minimal/standard build variants). - class_metaclass_init.py: Skip if MICROPY_PY_METACLASS_INIT disabled - class_metaclass_property.py: Skip if MICROPY_PY_METACLASS_PROPERTIES disabled - class_metaclass_prepare.py: Already had skip for __prepare__ - enum_auto.py: Add enum import check before __prepare__ check - enum_flag.py: Skip if enum module not available - enum_strenum.py: Skip if enum module not available - metaclass.py: Skip if MICROPY_PY_METACLASS_INIT disabled This fixes CI failures on build variants where these features are disabled by config. Signed-off-by: Andrew Leech <andrew@alelec.net> Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
|
Thanks Andrew, |
Summary
Adds enum support (Enum, IntEnum, Flag, IntFlag, StrEnum, auto, unique) via micropython-lib submodule and implements minimal metaclass features needed to support it and python-statemachine.
Built on dpgeorge's b31c1de metaclass branch from 2020. Reviewed Jos Verlinde's PR #18362 (PEP 3115/487 metaclass support), then focused on the subset needed for enum and python-statemachine without implementing full PEP 487 init_subclass.
Metaclass Features
Implemented as optional ROM-level features with separate config flags:
__init__invocationMICROPY_PY_METACLASS_INITMICROPY_PY_METACLASS_OPSMICROPY_PY_METACLASS_PROPERTIES__prepare__(PEP 3115)MICROPY_PY_METACLASS_PREPARE__init_subclass__(PEP 487)Total C overhead: 540 bytes when all features enabled (FULL level).
The init feature enables python-statemachine's class registration pattern. Properties enable accessing
.eventsand.stateson the class. Operators enablelen(EnumClass)andmember in EnumClass. prepare enables enum's auto() value generation.Enum Features
Complete implementation via micropython-lib submodule, based on PEP 435 (basic enums) and PEP 663 (Flag additions):
Frozen as bytecode: ~5,428 bytes.
Modular structure with lazy loading:
Total implementation: 540 bytes C + 5,428 bytes Python = 5,968 bytes (1.6% increase on STM32 PYBV10).
CPython Compatibility
Tested against CPython 3.13's official enum test suite:
Works:
Not implemented:
Enum('Name', 'A B C')) - use class syntax insteadKnown limitation: IntEnum members fail isinstance(member, int) check but all operations work correctly. Documented in tests/cpydiff/types_enum_isinstance.py.
STM32 Size Measurements (PYBV10)
Individual feature costs:
Cumulative by ROM level:
With enum module frozen:
Note: cumulative cost (540 bytes) is less than sum of individual features (548 bytes) due to code sharing.
Testing
Unix port coverage variant:
Tests added:
Tests fixed:
Implementation Details
Core C files modified:
Enum module:
The enum implementation lives in micropython-lib (separate repository) and is included via submodule reference. Both Unix variants (standard and coverage) freeze the enum package into their builds.