python-attrs / python-attrs/attrs

Making third-party extensions a bit easier

Open
#1,543 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.8k
Forks
480
Avg merge
2h 15m
Merged PRs (30d)
2

Description

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
https://github.com/python-attrs/attrs/blob/f0e420b71af1a5cd5dbf43efd1a42aece6ce8f4e/src/attr/_make.py#L403-L407
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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/attr/_make.py, especially the referenced _CountingAttr handling around lines 403-407 and _determine_whether_to_implement. Trace how field decorators and classmethod handling are currently recognized, then compare the extension patterns described in the issue. Done means proposing and validating a supported way to extend fields or expose the needed helpers, with maintainer agreement on the API and tests for the chosen behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
developer-experience
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.