Skip to content

python-stdlib/enum/enum.py: Add Enum class.#980

Open
IhorNehrutsa wants to merge 1 commit into
micropython:masterfrom
IhorNehrutsa:enum
Open

python-stdlib/enum/enum.py: Add Enum class.#980
IhorNehrutsa wants to merge 1 commit into
micropython:masterfrom
IhorNehrutsa:enum

Conversation

@IhorNehrutsa

@IhorNehrutsa IhorNehrutsa commented Mar 5, 2025

Copy link
Copy Markdown

Docs in:
docs/library/enum.rst: Add Enum class. #16842
Usage example:

from enum import Enum

class Color(Enum):
    RED = 'red'
    GREEN = 'green'

# Initialize the enum to process attributes
c = Color()

print(c.RED)        # Output: RED: red
print(c.RED.name)   # Output: RED
print(c.RED.value)  # Output: red
print(c.RED())      # Output: red

EDITED:
Inspired by @shariltumin Dot class from the Way to use dot notation to refer to states in a state machine #15694
and @njourdane enum() func from the Request for package: micropython-enum #269

EDITED April 2026:
Gemini-based version of enum.py and unit test test_enum.py

Code size report:
esp32: +1552 +0.088% ESP32_GENERIC[incl +1552(data)]
mimxrt: +1496 +0.380% TEENSY40
rp2: +1508 +0.163% RPI_PICO_W
samd: +1512 +0.545% ADAFRUIT_ITSYBITSY_M4_EXPRESS

@IhorNehrutsa

IhorNehrutsa commented Mar 5, 2025

Copy link
Copy Markdown
Author

Usage example::

from enum import Enum

class State(Enum):
  Stop = 10
  Run = 20
  Ready = 30

state = State()
print("state:", State())

current_state = state.Stop
print("current_state:", current_state, current_state.name)
if current_state == state.Stop:
  print(" Stop state")
if current_state != state.Ready:
  print(" Not a Ready state")
  print(" Run!")
  current_state = state.Run
print("current_state:", current_state, current_state.name)
# some process
i = -1
while current_state != state.Ready:
  i += 1
  if state.is_value(i):
      if state(i) == state.Ready:
          current_state = state.Ready
  print(".", end="")
print()
print("current_state:", current_state, state(current_state))
print("Done!")

Output is::

state: State(names={'Run': 20, 'Stop': 10, 'Ready': 30})
current_state: Stop: 10 Stop
 Stop state
 Not a Ready state
 Run!
current_state: Run: 20 Run
...............................
current_state: Ready: 30 Ready: 30
Done!

@dpgeorge

Copy link
Copy Markdown
Member

Thanks for the contribution, this looks pretty good!

Did you implement this from scratch, or copy parts from CPython's implementation? I'm just wondering about licensing and copyright.

Can you please add the test to the CI, in tools/ci.sh inside the function ci_package_tests_run.

@IhorNehrutsa

Copy link
Copy Markdown
Author

Did you implement this from scratch, or copy parts from CPython's implementation?

I just saw CPython Enum. It looks like incredible magic. :-)

@dpgeorge

Copy link
Copy Markdown
Member

I just saw CPython Enum. It looks like incredible magic. :-)

That doesn't really answer the question. Did you copy this implementation from CPython?

Also, please make sure the CI all passes, there's currently a failure.

@IhorNehrutsa

Copy link
Copy Markdown
Author

| Did you implement this from scratch, or copy parts from CPython's implementation?

No, I didn't use CPython implementation.

It was inspired by @shariltumin Dot class from the Way to use dot notation to refer to states in a state machine #15694
and @njourdane enum() func from the Request for package: micropython-enum #269

Comment thread python-stdlib/enum/manifest.py Outdated
@IhorNehrutsa
IhorNehrutsa force-pushed the enum branch 2 times, most recently from e70dd06 to 6ab2ebe Compare March 12, 2025 07:34
@IhorNehrutsa

Copy link
Copy Markdown
Author

Should I squash commits?

Comment thread python-stdlib/enum/test_enum.py Outdated
@@ -0,0 +1,91 @@
# enum_test.py

from enum import Enum, enum

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to run this test under CPython 3.12.2 but it doesn't work, for many reasons. And it should run under CPython so we can test that the implementation of MicroPython's enum matches the CPython enum.

For example, enum does not exist in the enum CPython module. Which version of CPython were you testing against?

Comment thread python-stdlib/enum/test_enum.py Outdated
Enabled = True


state = Enum()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython cannot create enums in this way.

Comment thread python-stdlib/enum/test_enum.py Outdated

state = Enum()
print(state)
state = Direction()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython requires a value in the constructor here.

Comment thread python-stdlib/enum/test_enum.py Outdated
print(state)
state = State()
print(state)
state = State({"X": 1.0, "Y": 2.0})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython doesn't allow such an argument to the constructor.

Comment thread python-stdlib/enum/test_enum.py Outdated

print("Direction(Direction.CCW):", Direction(Direction.CCW))
print("Direction('CW'):", Direction("CW"))
print("state(10):", state(10))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython doesn't allow calling an enum.

Comment thread python-stdlib/enum/test_enum.py Outdated
print("state('CW'):", state("CW"))
print("type(state('CW')):", type(state("CW")))

print("state.key_from_value(20):", state.key_from_value(20))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython doesn't have key_from_value().

Comment thread python-stdlib/enum/test_enum.py Outdated
CCW = "CCW"


class State(Direction):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython doesn't allow inheriting enums from each other.

Comment thread python-stdlib/enum/test_enum.py Outdated
print("type(state('CW')):", type(state("CW")))

print("state.key_from_value(20):", state.key_from_value(20))
print("len(state):", len(state))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython doesn't have __len__ on an enum.

Comment thread python-stdlib/enum/test_enum.py Outdated

print("state.keys():", state.keys())
print("state.values():", state.values())
print("state.items():", state.items())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython enums don't have keys/values/items methods.

@jonnor

jonnor commented Apr 25, 2025

Copy link
Copy Markdown

There is a quite comprehensive set of unit-tests for enum available in CPython: https://github.com/python/cpython/blob/main/Lib/test/test_enum.py
With some exceptions like the tests using inspect, threading, pickle it seems possible to port most of them to MicroPython. That would give a very high degree of confidence that the implementation is conformant. And in the cases that one chooses to not be conformant, that can be documented with skipped tests.

@IhorNehrutsa

Copy link
Copy Markdown
Author

I have successfully completed the task that requires the Enum class.
I don't plan to support this PR in the future.
You are welcome to continue working with this PR as you wish.
Thanks everyone.

rtyley added a commit to rtyley/analogue-led-clock that referenced this pull request Mar 22, 2026
We need to mip install `datetime`, tzif-parser requires it.

https://github.com/micropython/micropython-lib/tree/master/python-stdlib/datetime

```
mpremote connect id:a5f14b8bfbff4289 mip install datetime
```

## CPython features originally used in `tzif-parser` which were missing or different in MicroPython

* `sysconfig`
* `enum`
  - micropython/micropython-lib#269
  - micropython/micropython-lib#980
* Regex _named_ Groups
  - https://docs.python.org/3/howto/regex.html#non-capturing-and-named-groups
  - https://docs.micropython.org/en/latest/library/re.html
* `dataclasses`
  - https://docs.python.org/3/library/dataclasses.html
  - https://github.com/orgs/micropython/discussions/13741
  - https://github.com/dhrosa/udataclasses - https://udataclasses.readthedocs.io/en/latest/
* `struct` (https://docs.python.org/3/library/struct.html#format-characters) features not
  available in MicroPython (https://docs.micropython.org/en/latest/library/struct.html#module-struct):
  - `c` for `char`, which in CPython would be decoded as an instance of [`bytes`](https://docs.python.org/3/library/stdtypes.html#bytes-objects) with length 1. Instead of `c`, we could use `s`, but we know this is a single byte, so `b` does just fine, giving us a Python integer. The integral repeat count prefix `1` is superfluous.
* [MicroPython `bytearray`](https://www.fredscave.com/43-micropython-data-types-bytearray.html) type doesn't have the [`clear()`](https://docs.python.org/3/library/stdtypes.html#sequence.clear) method [mutable sequences](https://docs.python.org/3/library/stdtypes.html#typesseq-mutable) in CPython do
* `Typing.IO`
  - python/typing#829
rtyley added a commit to rtyley/analogue-led-clock that referenced this pull request Mar 22, 2026
We need to mip install `datetime`, tzif-parser requires it.

https://github.com/micropython/micropython-lib/tree/master/python-stdlib/datetime

```
mpremote connect id:a5f14b8bfbff4289 mip install datetime
```

## CPython features originally used in `tzif-parser` which were missing or different in MicroPython

* `sysconfig`
* `enum`
  - micropython/micropython-lib#269
  - micropython/micropython-lib#980
* Regex _named_ Groups
  - https://docs.python.org/3/howto/regex.html#non-capturing-and-named-groups
  - https://docs.micropython.org/en/latest/library/re.html
* `dataclasses`
  - https://docs.python.org/3/library/dataclasses.html
  - https://github.com/orgs/micropython/discussions/13741
  - https://github.com/dhrosa/udataclasses - https://udataclasses.readthedocs.io/en/latest/
* `struct` (https://docs.python.org/3/library/struct.html#format-characters) features not
  available in MicroPython (https://docs.micropython.org/en/latest/library/struct.html#module-struct):
  - `c` for `char`, which in CPython would be decoded as an instance of [`bytes`](https://docs.python.org/3/library/stdtypes.html#bytes-objects) with length 1. Instead of `c`, we could use `s`, but we know this is a single byte, so `b` does just fine, giving us a Python integer. The integral repeat count prefix `1` is superfluous.
* [MicroPython `bytearray`](https://www.fredscave.com/43-micropython-data-types-bytearray.html) type doesn't have the [`clear()`](https://docs.python.org/3/library/stdtypes.html#sequence.clear) method [mutable sequences](https://docs.python.org/3/library/stdtypes.html#typesseq-mutable) in CPython do
* `Typing.IO`
  - python/typing#829
@mchobby

mchobby commented Apr 11, 2026

Copy link
Copy Markdown

@dpgeorge
This PR looks stuck in time. I didn't find enum.py in micropython-lib/python-stdlib/enum.py .
Getting Enum for MicroPython would be really useful .

Need some help on this PR ? Were could I start ?

Dominique

@Josverl

Josverl commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Need some help on this PR ? Were could I start ?

That would be much appreciated.
I would suggest that making it match Cpython more and changing/adding tests to prove that would be the next step

@IhorNehrutsa

Copy link
Copy Markdown
Author

@mchobby @Josverl You may pull down this PR and try to use enum.py. Please write reviews about usage here

@Josverl

Josverl commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

@IhorNehrutsa, may I ask why you closed the documentation PR?

@IhorNehrutsa

Copy link
Copy Markdown
Author

@IhorNehrutsa, may I ask why you closed the documentation PR?

This has been going on for so long that I forgot. :(

@mchobby

mchobby commented Apr 15, 2026

Copy link
Copy Markdown

I'm a bit skeptical about the last @IhorNehrutsa action.
As I understand, the initial PR enum.py behavior was too far from CPython implementation.
I was interested to help in reducing the gap between CPython & MicroPython implementation.
That was looking as a "Good-First-Issue" to me (and for me).

On the meanwhile, @IhorNehrutsa pushed an AI rework of the code and "here is it for you to test".
As last arrival in this PR (and micropython-lib), I'm not in great position to evaluate the appropriateness of that rework.
@Josverl could you have a quick review on this for guidance?

Nevertheless, I do not want to be retrograde but being the "human behind the IA for checking the IA work" is not really what I was expecting.
Cheers

@Josverl

Josverl commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

@mchobby,
I agree with the earlier comments that it isn’t the reviewer’s job to try to interpret or “fix up” clearly AI‑generated submissions. If the submitter isn’t putting in at least the same level of effort as the reviewers, the process becomes unbalanced and unsustainable.
That does not mean that AI is not to be used as a tool to create code, but it does mean that there needs to be more that just that.

@IhorNehrutsa , I’m also genuinely confused by the pattern of behavior around these PRs: starting work, abandoning it, then suddenly re‑engaging with AI‑generated updates, along with the closing, and re‑opening of related PRs. It makes it difficult to understand the actual intent, the direction of the contribution, or whether you are committed to following through.
to be clear - I do appreciate your efforts, and I realize that sometimes the wait for a PR to get merged (or even rejected) can exceed ones attention span. but in order for us to work as a community I prefer to have some level confidence.

There is clear interest in having an Enum class available in MicroPython. But implementing one properly is non‑trivial — it touches (the lack of) metaclasses, memory constraints, and the broader philosophy of keeping the core minimal.

At the same time, it’s important that MicroPython stays close to CPython where feasible, while still respecting MicroPython’s own design principles and constraints. Achieving that balance requires careful design and discussion, not a surface‑level or (mostly) AI‑generated port.

I have not had the time to look at the most recent tests and code - but I do plan to do that in the next few days.

@IhorNehrutsa

Copy link
Copy Markdown
Author

Okay. No one minds if I copy the previous version of enum.py to enum_0.py, the latest to enum_1.py, then we'll choose the best implementation options for enum.py from them.

@IhorNehrutsa

Copy link
Copy Markdown
Author

There is a quite comprehensive set of unit-tests for enum available in CPython: https://github.com/python/cpython/blob/main/Lib/test/test_enum.py With some exceptions like the tests using inspect, threading, pickle it seems possible to port most of them to MicroPython. That would give a very high degree of confidence that the implementation is conformant. And in the cases that one chooses to not be conformant, that can be documented with skipped tests.

Can someone write a MicroPython version of the CPython tests https://github.com/python/cpython/blob/main/Lib/test/test_enum.py ?

@mchobby

mchobby commented Apr 18, 2026

Copy link
Copy Markdown

@dpgeorge
Use case
I'm currently porting almost achieved driver to MicroPython (source https://github.com/LukeYoung3000/Vishay_VCNL4035_Arduino/tree/master ).
This drivers contains lots of constant and enums definition (see https://github.com/LukeYoung3000/Vishay_VCNL4035_Arduino/blob/master/vcnl4035_Defs.h ).

I did implement the C enum as Python class (to stay close of original source code while porting and testing).

Example:

# Proximity Sensor Integration (T = 50 micro sec)
class VCNL4035_PS_IT:
        PS_IT_50us  = const(0) # 1.0 T
        PS_IT_75us  = const(1) # 1.5 T
        PS_IT_100us = const(2) # 2.0 T
        PS_IT_125us = const(3) # 2.5 T
        PS_IT_150us = const(4) # 3.0 T
        PS_IT_175us = const(5) # 3.5 T
        PS_IT_200us = const(6) # 4.0 T
        PS_IT_400us = const(7) # 8.0 T

Optimisation will comes later

Why using enum?
Using python enum just looks more appropriate.
I'm alway writing demonstration/test scripts for the target Hardware.
With enum script could retrieves the "name" of the current value to print it.
This would be definitively useful for debugging and testing.

@mchobby

mchobby commented Apr 18, 2026

Copy link
Copy Markdown

@dpgeorge , @IhorNehrutsa
I do agree with both of you each for a different reason. I have a suggestion.

As said @dpgeorge , if it is call Enum.py then it should work like CPython Enum. This is what common coder would expect.
As said @IhorNehrutsa , memory footprint is important and getting the main behavior would be sufficient (I think that Immutability should be preserved... to stay closer of enum).

Suggestion: name it AlmostEnum.py and document it properly. Having significant Enum behavior working on MicroPython and CPython would then acceptable as most of coder would use the main features.
The Memory footprint would stay low.
The Good practice should avoid the addition (and usage) of new features (not supported in Enum).

Regards,
Dominique

@IhorNehrutsa

IhorNehrutsa commented Apr 18, 2026 via email

Copy link
Copy Markdown
Author

@IhorNehrutsa

Copy link
Copy Markdown
Author

Why does nobody look at #1061?

@Josverl

Josverl commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

@IhorNehrutsa

Andrew has indicated that PR as Draft , and if you look at the implementation it states "This module requires metaclass support.". Metaclass is a rather complex core capability that is not currently part of MicroPython and that both he and I have been working on. Andrew's PR on that micropython/micropython#18416 uses less code than mine , but is still a significant growth.

TLDR; Behind that seemingly simple Enum hides a a rather complex machinery that comes at a cost.

and with that come choices - and weighing of options to compare features, compatibility and firmware sizes.
Perhaps it would be good to document different options and their pro/cons for discussion

@IhorNehrutsa
IhorNehrutsa force-pushed the enum branch 3 times, most recently from b6ab80a to 3ba2692 Compare April 23, 2026 13:18
@IhorNehrutsa

Copy link
Copy Markdown
Author

Code size report:
esp32: +1968 +0.113% ESP32_GENERIC[incl +1968(data)]

@IhorNehrutsa

Copy link
Copy Markdown
Author

Code size report:
esp32: +1712 +0.098% ESP32_GENERIC[incl +1712(data)]

@IhorNehrutsa

Copy link
Copy Markdown
Author

Code size report:
esp32: +2688 +0.154% ESP32_GENERIC[incl +2688(data)]

@Josverl

Josverl commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

@IhorNehrutsa
I suggest to change the status of these PRs to Draft.
You appear to still be working through both the functionality offered as well as the code size -
You keep making regular updates - which is fine - setting the draft / would help let maintainers know when it is stable enough to spend more time reviewing it.

@IhorNehrutsa

Copy link
Copy Markdown
Author

Code size report:
esp32: +1568 +0.090% ESP32_GENERIC[incl +1568(data)]

@IhorNehrutsa

Copy link
Copy Markdown
Author

I think I've reached the maximum functionality and minimum code size. Right now, I'm missing feedback.

@IhorNehrutsa

Copy link
Copy Markdown
Author

Code size report:
esp32: +1600 +0.092% ESP32_GENERIC[incl +1600(data)]
mimxrt: +1568 +0.407% TEENSY40
rp2: +1564 +0.170% RPI_PICO_W
samd: +1564 +0.569% ADAFRUIT_ITSYBITSY_M4_EXPRESS

@IhorNehrutsa

Copy link
Copy Markdown
Author

Ready for review

@IhorNehrutsa

Copy link
Copy Markdown
Author

Is there any feedback?

@Josverl

Josverl commented May 27, 2026

Copy link
Copy Markdown
Contributor

One of the twenty-something post its on my MicroPython to-do list is to do a full review and comparison.

But I can't give you a date

@agatti agatti left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had a quick look at the code, and whilst I'm still going through the internals of this module, I can at least provide a couple of hints on how to make the final output smaller :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should be renamed into README.md.

Comment thread python-stdlib/enum/enum.py Outdated
if name and names:
# Support Functional API: Enum("Name", {"KEY1": VALUE1, "KEY2": VALUE2, ..})
# Dynamically create: class <name>
new_cls = type(name, (cls,), {"_inited": True})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If _inited isn't meant to be accessible from outside the instance, the name could be shortened to save some more space :)

Comment thread python-stdlib/enum/enum.py Outdated

def __setattr__(self, k, v):
if "_inited" in self.__class__.__dict__:
raise AttributeError(f"Enum '{self.__class__.__name__}' is immutable")

@agatti agatti Jun 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exceptions usually start with a lower case letter, but I guess that in this case Enum indicates the class name.

Given that immutability seems to be something missing from the CPython implementation (unless I missed it in the documentation, that is!), we don't have to follow a set template. This string may be reduced to '{self.__class__.__name__}' is immutable, and still be useful.

Comment thread python-stdlib/enum/enum.py Outdated
super().__setattr__(k, v)

def __delattr__(self, k):
raise AttributeError("Enum is immutable")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you reuse the same string as the one in __setattr__ it will just use the old string ID rather than creating a new string in the bytecode.

return self.list() == o.list()


if __name__ == "__main__":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section of the code gets compiled into bytecode too, so it's better if it's refactored into its own example file. Doing that saves 2657 bytes once the module is compiled.

@IhorNehrutsa IhorNehrutsa Jun 15, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section was deleted in #16842 for code size check.
It will be removed from here after final review.

Thancks, Alessandro

@IhorNehrutsa

Copy link
Copy Markdown
Author

Code size report:
esp32: +1552 +0.088% ESP32_GENERIC[incl +1552(data)]
mimxrt: +1496 +0.380% TEENSY40
rp2: +1508 +0.163% RPI_PICO_W
samd: +1512 +0.545% ADAFRUIT_ITSYBITSY_M4_EXPRESS

@IhorNehrutsa
IhorNehrutsa requested a review from agatti June 15, 2026 08:10
Signed-off-by: Ihor Nehrutsa <Ihor.Nehrutsa@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants