Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions python-stdlib/enum/README.md
Comment thread
IhorNehrutsa marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Lightweight Python Enum (No Metaclasses)

A lightweight Python implementation of enumerations (`Enum`, `IntEnum`, `StrEnum`) created without using metaclasses. Supports class syntax, functional API, strict value type checking, and immutability.

## ?? Features

* **No Metaclasses**: Dynamic type construction using standard `type()`.
* **Types**: Base `Enum`, as well as strictly typed `IntEnum` and `StrEnum`.
* **Dual Syntax**: Declaration via standard `class` statements or functional API calls.
* **Immutability**: Attribute mutation guard preventing changes after member creation.
* **Serialization**: `.dump()` method for easy string representation and reconstruction via `eval()`.

## ?? Usage

### 1. Class Syntax Declaration

```python
from enum import Enum, IntEnum, StrEnum

class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3

class Status(StrEnum):
PENDING = "pending"
DONE = "done"

# Accessing members
print(Color.RED) # <Color.RED: 1>
print(Color.RED.name) # 'RED'
print(Color.RED.value) # 1
print(Status.PENDING) # 'pending' (for StrEnum, string output equals value)
```

### 2. Functional API

```python
# Enum with sequential integer values
Priority = Enum("Priority", "LOW MEDIUM HIGH", start=1)
print(Priority.HIGH.value) # 3

# Enum from a dictionary mapping
HTTPStatus = IntEnum("HTTPStatus", {"OK": 200, "NOT_FOUND": 404})
print(HTTPStatus.NOT_FOUND.value) # 404
```

### 3. Lookup, Iteration, and Type Enforcement

```python
# Lookup by value or name
item = Color(1) # Color.RED
item = Color("RED") # Color.RED

# Iteration over members
for member in Color:
print(member.name, member.value)

# Strict type checks in IntEnum / StrEnum
try:
class BadEnum(IntEnum):
INVALID = "not an int" # Raises TypeError
except TypeError as e:
print(e)
```

### 4. Serialization & Evaluation

```python
dump_str = Color.dump()
print(dump_str) # "Enum('Color', {'RED': 1, 'GREEN': 2, 'BLUE': 3})"

# Reconstructing the Enum class object
RestoredColor = eval(dump_str)
```
196 changes: 196 additions & 0 deletions python-stdlib/enum/enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# enum.py
# Enum implementation without metaclasses
# version="1.4.0"

# ==============================================================================
# Variable & Abbreviation Definitions:
# ==============================================================================
# Functions & Helper Methods:
# _c(e, n, v, v_t) -> create_enum_item: Helper function to construct a typed enum instance.
# _l(v) -> lookup: Internal classmethod to find an enum member by name or value.
# _i() -> items: Internal classmethod to initialize/retrieve tuple of enum instances.
# _s(s, k, v) -> __setattr__: Inner setter guard raising AttributeError on enum instances.
#
# Helper Arguments & Local Variables:
# e - enum_name / class_name: String representing the Enum class name (e.g., "Color").
# n - name: String representing the enum member key (e.g., "RED").
# v - value: Actual value assigned to the enum member (e.g., 1, "pending").
# v_t - value_type: Target constraint type (`int`, `str`, or `None` for generic Enum).
# l - names_list: List of parsed string keys from comma/space-separated inputs.
# d - dict / mapping: Temporary dictionary for parsed enum members or dump serialization.
# i, __i - items: List or tuple containing initialized enum member instances.
# c - class / new_class: Dynamically created Enum class object via `type()`.
# s, k, o - self, key/attribute_name, other_object (standard short parameters).
# ==============================================================================


def _c(e, n, v, v_t=None):
# Create enum item
def _s(s, k, v):
raise AttributeError("cannot set attribute")

# Type checks for IntEnum and StrEnum
if v_t is int and not isinstance(v, int):
raise TypeError(f"IntEnum member {n!r} value must be int, got {type(v).__name__}")
elif v_t is str and not isinstance(v, str):
raise TypeError(f"StrEnum member {n!r} value must be str, got {type(v).__name__}")

# Ensure 'e' is a clean class name string, even if a class type object was passed
e = e.__name__ if hasattr(e, "__name__") else str(e)

# Create class: type(name, bases, dict), which inherits a base type (int, str, etc.)
return type(
f"{e}.{n}",
(type(v),),
{
"name": n,
"value": v,
"__str__": lambda s: str(v) if v_t in (int, str) else f"{e}.{n}",
"__repr__": lambda s: f"<{e}.{n}: {v!r}>",
"__call__": lambda s: v,
"__setattr__": _s,
},
)(v)


class Enum:
_v_t = None # value type

def __new__(cls, value=None, names=None, *, start=1):
# Functional API: dynamic creation of a new Enum class
if value is not None and names is not None:
is_str_enum = cls._v_t is str

# Parse 'names' parameter into a key-value dictionary:
if isinstance(names, dict):
d = names
elif isinstance(names, str):
# Strings (comma/space-separated)
l = names.replace(",", " ").split() # names_list
d = (
{k: k for k in l}
if is_str_enum
else {k: v for v, k in enumerate(l, start=start)}
)
elif isinstance(names, (list, tuple)):
# Lists or tuples (flat or key-value pairs)
if names and isinstance(names[0], (list, tuple)):
d = dict(names)
else:
d = (
{k: k for k in names}
if is_str_enum
else {k: v for v, k in enumerate(names, start=start)}
)
else:
# For sets and other iterables
d = (
{k: k for k in names}
if is_str_enum
else {k: v for v, k in enumerate(names, start=start)}
)
# Construct and return a new class type
c = type(str(value), (cls,), {})
i = []
for k, v in d.items():
e = _c(value, k, v, v_t=cls._v_t)
setattr(c, k, e)
i.append(e)
if i:
c.__i = tuple(i)
return c

# Lookup existing member by value or name: e.g., Color(1) or Color("RED")
if value is not None and cls not in (Enum, IntEnum, StrEnum):
return cls._l(value)

return super().__new__(cls)

def __init__(self, *args, **kwargs):
# Trigger lazy items initialization for class syntax declarations
if "__i" not in self.__class__.__dict__:
self._i()

def __str__(self):
if hasattr(self, "value") and self.value is not None:
return f"{type(self).__name__}.{self.name}"
return type(self).__name__

def __repr__(self):
if hasattr(self, "value") and self.value is not None:
return f"<{type(self).__name__}.{self.name}: {self.value!r}>"
return f"<enum '{type(self).__name__}'>"

def __call__(self, v):
return self._l(v)

def __getitem__(self, k):
# Instance-level container lookup: Color()["RED"]
for i in self._i():
if i.name == k:
return i
raise KeyError(k)

def __eq__(self, o):
return isinstance(o, Enum) and self._i() == o._i()

@classmethod
def __iter__(cls):
return iter(cls._i())

@classmethod
def __len__(cls):
return len(cls._i())

@classmethod
def __setattr__(cls, k, v):
raise AttributeError("cannot set attribute")

@classmethod
def __delattr__(cls, k):
raise AttributeError("cannot delete attribute")

@classmethod
def items(cls):
return cls._i()

@classmethod
def dump(cls):
# Serialize enum members to string representation for eval compatibility
# obj == eval(dump(obj))
if cls._v_t is None:
e = "Enum"
else:
e = cls._v_t.__name__[0].upper() + cls._v_t.__name__[1:] + "Enum"
d = {i.name: i.value for i in cls._i()}
return f"{e}('{cls.__name__}', {d})"

@classmethod
def _i(cls):
# Get enum items
if "__i" not in cls.__dict__:
# Convert raw class attributes into typed enum instances
i = [] # items
for k, v in list(cls.__dict__.items()):
if not k.startswith("_") and not callable(v):
e = _c(cls.__name__, k, v, v_t=cls._v_t)
setattr(cls, k, e)
i.append(e)
cls.__i = tuple(i)
return cls.__i

@classmethod
def _l(cls, v):
# Lookup by value or name
for i in cls._i():
if i.value == v or i.name == v:
return i
raise ValueError(f"{v!r} is not a valid {cls.__name__}")


class IntEnum(Enum):
_v_t = int


class StrEnum(Enum):
_v_t = str
Loading
Loading