Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Pyright detection of constructor kwargs in attrs-based data classes #2945

Merged
merged 11 commits into from
Feb 5, 2024
Merged

Support Pyright detection of constructor kwargs in attrs-based data classes #2945

merged 11 commits into from
Feb 5, 2024

Conversation

mikenerone
Copy link
Member

@mikenerone mikenerone commented Feb 3, 2024

[The changes related to this update fall into three categories:

  1. attrs>=23.2.0 is now required (based on this comment thread plus needing to bump to at least 22.2.0 anyway to support Pyright).
  2. Adds non-underscored aliases for a few selected private (i.e. leading underscore) attributes in CancelStatus and CancelScope (this is required for Pyright to recognize private attributes as constructor kwargs).
  3. Switching to the "NG" interfaces in recent versions of attrs per previous chat discussion.
  • import attr -> import attrs everywhere
  • attr.ib -> attrs.field
  • attr.s -> attrs.define or attrs.frozen, depending on existing frozen kwarg and with adjustments made to all calls to account for slots=True now being the default).
  • attrs.field(factory=X) -> attrs.Factory(X)
  • attrs.field(default=X) -> simply assign X
  1. One typing correction that was uncovered by that last "NG" update (see related comment).

Note: there's no new logic to be tested. All existing tests pass unchanged (except for non-logic name changes resulting from item 3 above).

Copy link

codecov bot commented Feb 3, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (0dd5bc4) 99.64% compared to head (0b1b5f2) 99.64%.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2945   +/-   ##
=======================================
  Coverage   99.64%   99.64%           
=======================================
  Files         117      117           
  Lines       17564    17564           
  Branches     3166     3166           
=======================================
  Hits        17502    17502           
  Misses         43       43           
  Partials       19       19           
Files Coverage Δ
src/trio/_channel.py 100.00% <100.00%> (ø)
src/trio/_core/_asyncgens.py 100.00% <100.00%> (ø)
src/trio/_core/_entry_queue.py 100.00% <100.00%> (ø)
src/trio/_core/_io_epoll.py 100.00% <100.00%> (ø)
src/trio/_core/_io_kqueue.py 87.20% <100.00%> (ø)
src/trio/_core/_io_windows.py 98.80% <100.00%> (ø)
src/trio/_core/_ki.py 100.00% <100.00%> (ø)
src/trio/_core/_local.py 100.00% <100.00%> (ø)
src/trio/_core/_parking_lot.py 100.00% <100.00%> (ø)
src/trio/_core/_run.py 99.66% <100.00%> (ø)
... and 19 more

Dataclass transforms that Pyright can recognize and honor for things like
constructor kwargs were added in 21.1.0, which was subsequently yanked, leaving
21.2.0 as the minimum existing version containing them.
This affects only two classes:
  1. CancelScope - the only class (I think) that is commonly instantiated with
     kwargs by users.
  2. CancelStatus - the only class that is actually instantiated with kwargs in
     Trio's own code.

No other classes appear to fall into either of these categories, and are left
untouched.
Obviously, `frozen=True` arguments are now redundant, so removed. Further,
`slots=True` is now default, so that's removed from calls while those that
didn't specify are now explicitly `slots=False`.
Similarly to previous commit for attr.frozen, `slots=True` is now default, so
that's removed from calls while those that didn't specify are now explicitly
`slots=False`.
pyproject.toml Outdated Show resolved Hide resolved
@Fuyukai
Copy link
Member

Fuyukai commented Feb 3, 2024

If you're replacing ib -> define, you might as well go through and replace import attr with import attrs everywhere too.

@mikenerone
Copy link
Member Author

mikenerone commented Feb 3, 2024

If you're replacing ib -> define, you might as well go through and replace import attr with import attrs everywhere too.

Happy to do that, as well, but that really does require increasing the constraint to >=21.3.0, so I'll wait for consensus on what version to constrain to in the other thread. Edit: Ended up doing this.

@CoolCat467 CoolCat467 added the dependencies Pull requests that update a dependency file label Feb 4, 2024
Copy link
Member

@CoolCat467 CoolCat467 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good, but I have a few questions.
First, why are we adding aliases in a few parts? EDIT: Explained, I hadn't been aware this was a part of what is required for pyright
Secondly, are we able to enable slots on more of these?

src/trio/_core/_run.py Show resolved Hide resolved
src/trio/_core/_run.py Show resolved Hide resolved
Copy link
Member

@CoolCat467 CoolCat467 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After explanation of alias, looks good to me!

Copy link
Contributor

@TeamSpen210 TeamSpen210 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much nicer indeed.

@mikenerone
Copy link
Member Author

mikenerone commented Feb 4, 2024

Added two more commits for additional shorter syntaxes:

  • attrs.field(factory=X) -> attrs.Factory(X)
  • attrs.field(default=X) -> simply assign X

Along with a fix to a small typing error that this uncovered. The original line in src/trio/_core/_run.py was:

_next_send_fn: Callable[[Any], object] = attr.ib(default=None)

The type annotation didn't allow the default value. I updated the annotation.

Copy link
Member

@jakkdl jakkdl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only skimmed the changes, but seems fine to merge. For ease of review it's perhaps not great to bundle a bugfix with a verbose style change, (and slots changes, with is coolcat's hobbyhorse :P) but probably not worth splitting it up at this point.

Another plus would be a regression test in src/trio/_tests/type_tests/, which are run with both pyright and mypy. The overkill approach would be something that comprehensively tested all parameters to functions, but given how often trio/_tests/test_exports is a hassle it might even be net negative.

Very happy to be moving to modern usage where reading the official docs for attrs is much easier, it confused me a bit when I first started familiarizing myself with the code.

Comment on lines -526 to +534
_cancel_status: CancelStatus | None = attr.ib(default=None, init=False)
_has_been_entered: bool = attr.ib(default=False, init=False)
_registered_deadline: float = attr.ib(default=inf, init=False)
_cancel_called: bool = attr.ib(default=False, init=False)
cancelled_caught: bool = attr.ib(default=False, init=False)
_cancel_status: CancelStatus | None = attrs.field(default=None, init=False)
_has_been_entered: bool = attrs.field(default=False, init=False)
_registered_deadline: float = attrs.field(default=inf, init=False)
_cancel_called: bool = attrs.field(default=False, init=False)
cancelled_caught: bool = attrs.field(default=False, init=False)

# Constructor arguments:
_deadline: float = attr.ib(default=inf, kw_only=True)
_shield: bool = attr.ib(default=False, kw_only=True)
_deadline: float = attrs.field(default=inf, kw_only=True, alias="deadline")
_shield: bool = attrs.field(default=False, kw_only=True, alias="shield")
Copy link
Member

@jakkdl jakkdl Feb 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it was me I'd perhaps do from attrs import define, field, Factory etc, since we're only using a couple different objects from attrs, reuse them a bunch of times, and are widely used enough in the codebase I don't think there'd be much confusion about what library they're from. (and as opposed to attr.ib where ib on its own doesn't make sense). E.g. in this block it would cut down on the sheer number of characters a decent amount.

But seems plenty fine to stick with import attrs

@Fuyukai Fuyukai merged commit 70c8b76 into python-trio:master Feb 5, 2024
30 checks passed
@trio-bot
Copy link

trio-bot bot commented Feb 5, 2024

Hey @mikenerone, it looks like that was the first time we merged one of your PRs! Thanks so much! 🎉 🎂

If you want to keep contributing, we'd love to have you. So, I just sent you an invitation to join the python-trio organization on Github! If you accept, then here's what will happen:

  • Github will automatically subscribe you to notifications on all our repositories. (But you can unsubscribe again if you don't want the spam.)

  • You'll be able to help us manage issues (add labels, close them, etc.)

  • You'll be able to review and merge other people's pull requests

  • You'll get a [member] badge next to your name when participating in the Trio repos, and you'll have the option of adding your name to our member's page and putting our icon on your Github profile (details)

If you want to read more, here's the relevant section in our contributing guide.

Alternatively, you're free to decline or ignore the invitation. You'll still be able to contribute as much or as little as you like, and I won't hassle you about joining again. But if you ever change your mind, just let us know and we'll send another invitation. We'd love to have you, but more importantly we want you to do whatever's best for you.

If you have any questions, well... I am just a humble Python script, so I probably can't help. But please do post a comment here, or in our chat, or on our forum, whatever's easiest, and someone will help you out!

@mikenerone
Copy link
Member Author

Only skimmed the changes, but seems fine to merge. For ease of review it's perhaps not great to bundle a bugfix with a verbose style change, (and slots changes, with is coolcat's hobbyhorse :P) but probably not worth splitting it up at this point.

@jakkdl By way of explanation, because the Pyright fix required a bump to the attrs dep version, this PR really became an "update attrs and use all of the next-gen interfaces that are now prescribed in this new version." From that perspective, this really was just a style PR resulting from the dep update. The "bugfix" part we got for free with the update - there are zero extra lines here to accomplish that. Also, there are no slots changes here, and in fact intentionally zero changes in any functional way like that.

@mikenerone mikenerone deleted the mikenerone/fix-pyright-errors-private-attrs-construction branch February 5, 2024 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants