I'm facing a bug when creating a metaclass that dynamically creates classes with properties given a config file.
In more detail, I expect to dynamically create classes with some properties (regular setter and a getter that runs the result of a custom function on that attribute). So I created a Factory class to register classes at runtime and a metaclass to set the properties given the config dict.
from copy import deepcopy
from typing import Any, Callable, Dict, Tuple
class Meta(type):
def __new__(
cls,
clsname: str,
bases: Tuple,
attrs: Dict[str, Any],
fields: Dict[str, Callable] = None,
) -> object:
if fields is not None:
for field, field_fn in fields.items():
attrs[f"_{field}"] = None
attrs[f"{field}"] = property(
fget=lambda self: deepcopy(field_fn)(getattr(self, f"_{field}")),
fset=lambda self, value: setattr(self, f"_{field}", value),
)
return super().__new__(cls, clsname, bases, attrs)
class Factory:
registry = {}
@classmethod
def register(
cls,
name: str,
cfg: Dict[str, Callable],
) -> None:
class ConfigurableClass(metaclass=Meta, fields=cfg):
pass
Factory.registry[name] = ConfigurableClass
@classmethod
def make(cls, name: str):
return cls.registry[name]()
if __name__ == "__main__":
Factory.register("foo", {"a": lambda x: x + 1, "b": lambda x: x - 1})
obj = Factory.make("foo")
obj.a = 5
obj.b = 5
print(obj.a, obj.b)
# Expected 6 and 4 but get 4 and 4 instead
However, for some reason, the function of the last dict key gets registered for all properties. I even tried to add a deepcopy
in there.
Any help is appreciated, thanks.