continue porting python-nostr

This commit is contained in:
Thomas Farstrike
2025-05-19 10:08:43 +02:00
parent 1e3e990aec
commit 90df88a08c
4 changed files with 666 additions and 17 deletions
+59 -17
View File
@@ -1,43 +1,85 @@
# dataclasses.py: Minimal MicroPython compatibility layer for Python's dataclasses
# Implements @dataclass with __init__ and __repr__ generation
# dataclasses.py: MicroPython compatibility layer for Python's dataclasses
# Implements @dataclass, field, and Field with support for default_factory
MISSING = object() # Sentinel for missing values
class Field:
"""Represents a dataclass field, supporting default_factory."""
def __init__(self, default=MISSING, default_factory=MISSING, init=True, repr=True):
self.name = None # Set by dataclass decorator
self.type = None # Set by dataclass decorator
self.default = default
self.default_factory = default_factory
self.init = init
self.repr = repr
def field(*, default=MISSING, default_factory=MISSING, init=True, repr=True):
"""Specify a dataclass field with optional default_factory."""
if default is not MISSING and default_factory is not MISSING:
raise ValueError("Cannot specify both default and default_factory")
return Field(default=default, default_factory=default_factory, init=init, repr=repr)
def dataclass(cls):
"""Decorator to emulate Python's @dataclass, generating __init__ and __repr__."""
"""Decorator to emulate @dataclass, generating __init__ and __repr__."""
# Get class annotations and defaults
annotations = getattr(cls, '__annotations__', {})
defaults = {}
fields = {}
# Process class attributes for defaults and field() calls
for name in dir(cls):
if not name.startswith('__'):
attr = getattr(cls, name, None)
if not callable(attr) and name in annotations:
defaults[name] = attr
if not callable(attr):
if isinstance(attr, Field):
fields[name] = attr
fields[name].name = name
fields[name].type = annotations.get(name)
if attr.default is not MISSING:
defaults[name] = attr.default
elif name in annotations:
defaults[name] = attr
# Ensure all annotated fields have a Field object
for name in annotations:
if name not in fields:
fields[name] = Field(default=defaults.get(name, MISSING), init=True, repr=True)
fields[name].name = name
fields[name].type = annotations.get(name)
# Generate __init__ method
def __init__(self, *args, **kwargs):
# Positional arguments
fields = list(annotations.keys())
init_fields = [name for name, f in fields.items() if f.init]
for i, value in enumerate(args):
if i >= len(fields):
if i >= len(init_fields):
raise TypeError(f"Too many positional arguments")
setattr(self, fields[i], value)
setattr(self, init_fields[i], value)
# Keyword arguments and defaults
for name in fields:
if name in kwargs:
# Keyword arguments, defaults, and default_factory
for name, field in fields.items():
if field.init and name in kwargs:
setattr(self, name, kwargs[name])
elif not hasattr(self, name):
if name in defaults:
setattr(self, name, defaults[name])
else:
if field.default_factory is not MISSING:
setattr(self, name, field.default_factory())
elif field.default is not MISSING:
setattr(self, name, field.default)
elif field.init:
raise TypeError(f"Missing required argument: {name}")
# Call __post_init__ if defined
if hasattr(self, '__post_init__'):
self.__post_init__()
# Generate __repr__ method
def __repr__(self):
fields = [
fields_repr = [
f"{name}={getattr(self, name)!r}"
for name in annotations
for name, field in fields.items()
if field.repr
]
return f"{cls.__name__}({', '.join(fields)})"
return f"{cls.__name__}({', '.join(fields_repr)})"
# Attach generated methods to class
setattr(cls, '__init__', __init__)
+55
View File
@@ -0,0 +1,55 @@
# for python-nostr
# enum.py: MicroPython compatibility layer for Python's enum module
# Implements Enum and IntEnum for integer-based enumerations, avoiding metaclass keyword
class Enum:
"""Base class for enumerations."""
def __init__(self, value, name):
self.value = value
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}: {self.value}>"
def __eq__(self, other):
if isinstance(other, Enum):
return self.value == other.value
return self.value == other
def __hash__(self):
return hash(self.value)
def create_enum_class(base_class, name, attrs):
"""Factory function to create an enum class with metaclass-like behavior."""
members = {}
values = {}
for key, value in attrs.items():
if not key.startswith('__') and not callable(value):
enum_item = base_class(value, key)
members[key] = enum_item
values[value] = enum_item
attrs[key] = enum_item
attrs['_members'] = members
attrs['_values'] = values
# Define iteration and lookup
def __iter__(cls):
return iter(cls._members.values())
def __getitem__(cls, value):
return cls._values.get(value)
attrs['__iter__'] = __iter__
attrs['__getitem__'] = __getitem__
# Create class using type
return type(name, (base_class,), attrs)
# Define IntEnum using factory function
IntEnum = create_enum_class(Enum, 'IntEnum', {
'__int__': lambda self: self.value
})
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
# typing.py: MicroPython compatibility layer for Python's typing module
# Provides List for type annotations
class List:
pass