I am researching how we can expand the field with more metadata on our project. The documentation was very helpful for getting started, but I've hit a few places where there is a bit of friction
Adding field decorators requires extending attr._make._CountingAttr
This is way more complicated then it seems at first glance because:
- the
_CountingAttr keeps an internal counter for the ordering
- changing the return type of
field makes it be picked up as a custom default instead of an extension of _CountingAttr
The initial design I considered was
def field(*, metadata=None, **kwargs: Any) -> Any:
if not metadata:
metadata = {}
metadata.setdefault("my_ext", MyMetadata())
return MyCountingAttr(attrs.field(**kwargs, metadata=metadata))
class MyCountingAttr(attr._make._CountingAttr):
def __init__(self, ca):
for f in ca.__slots__:
setattr(self, f, getattr(ca, f))
attr._make._CountingAttr.cls_counter -= 1
self.counter -= 1
def new_decorator(self, meth):
my_metadata: TmtAttrsMetadata = self.metadata["my_ext"]
my_metadata.func = meth
return meth
@attrs.define
class MyMetadata:
func: Callable[..., Any] | None = None
@attrs.define
class Example:
x: field()
@x.new_decorator
def _x_my_func(self):
pass
but this fails when it gets to
|
ca_names = { |
|
name |
|
for name, attr in cd.items() |
|
if attr.__class__ is _CountingAttr |
|
} |
Could not find a way around it besides changing that to a
issubclass check
Many useful internal functions are not exposed
For example attr._make._determine_whether_to_implement would be really nice to not have to re-implement, especially when it comes to the correct handling of classmethod. Admittedly my usage there is quite weird (injecting some methods/classmethods based on inputs to the @define decorator) and it could be better handled with inheritance/metaclass, but it's the first example of the internal helpers that I've found in my code.
I am researching how we can expand the
fieldwith more metadata on our project. The documentation was very helpful for getting started, but I've hit a few places where there is a bit of frictionAdding field decorators requires extending
attr._make._CountingAttrThis is way more complicated then it seems at first glance because:
_CountingAttrkeeps an internal counter for the orderingfieldmakes it be picked up as a customdefaultinstead of an extension of_CountingAttrThe initial design I considered was
but this fails when it gets to
attrs/src/attr/_make.py
Lines 403 to 407 in f0e420b
Could not find a way around it besides changing that to a
issubclasscheckMany useful internal functions are not exposed
For example
attr._make._determine_whether_to_implementwould be really nice to not have to re-implement, especially when it comes to the correct handling ofclassmethod. Admittedly my usage there is quite weird (injecting some methods/classmethods based on inputs to the@definedecorator) and it could be better handled with inheritance/metaclass, but it's the first example of the internal helpers that I've found in my code.