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

Add conformance test for @final combined with @property #1916

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

samwgoldman
Copy link

The language of the typing spec indicates that the @final decorator may be used with properties:

The method decorator version may be used with all of instance methods, class methods, static methods, and properties.

I noticed this case while working on Pyre. I observed that Mypy and Pyright also differ in behavior, so it seemed like a good conformance test to add.

See discussion here:
https://discuss.python.org/t/proposal-allow-chaining-final-decorator-when-previous-decorators-return-a-non-function/78918

I also added an example to the spec itself. I think it's a practical example, but also happy to remove it as well, since the existing content makes it pretty clear that the example should be allowed.

The language of the typing spec indicates that the @Final decorator
may be used with properties:

> The method decorator version may be used with all of instance methods, class methods, static methods, and properties.

I noticed this case while working on Pyre. I observed that Mypy and
Pyright also differ in behavior, so it seemed like a good conformance
test to add.

See discussion here:
https://discuss.python.org/t/proposal-allow-chaining-final-decorator-when-previous-decorators-return-a-non-function/78918
I thought it would be nice to add an example showing the use of @Final
and @Property together, but I also think the existing content is
reasonably clear that this example is supposed to work.
Copy link

cpython-cla-bot bot commented Jan 31, 2025

All commit authors signed the Contributor License Agreement.
CLA signed

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

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

Looks good, although I think conformance in pyright/qualifiers_final_decorator.toml should be updated to match the new conformance_automated value

@@ -1,4 +1,4 @@
conformant = "Pass"
conformant = "Fail"
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should say "Partial", not "Fail".

@erictraut
Copy link
Collaborator

Thanks for reporting this and adding the conformance test. FYI, I've filed the following bug in the pyright issue tracker: microsoft/pyright#9795.

@samwgoldman
Copy link
Author

Thanks for the guidance!

@hauntsaninja
Copy link
Collaborator

I guess always a risk changing a pyright conformant value, because odds are 50/50 that Eric will have fixed it before the PR is merged :-)

@@ -66,6 +66,22 @@ implementation (or on the first overload, for stubs)::
def method(self, x=None):
...

The ``@final`` decorator can be combined with previous decorators, like
Copy link
Member

Choose a reason for hiding this comment

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

Somebody on Discuss brought up that perhaps you should put @property above @final. Do we need to specify that explicitly?

For classmethod + final, currently both mypy and pyright allow putting the decorators in any order.

My preference would be to make the spec say explicitly that either order is allowed and add conformance tests for it.

Copy link
Collaborator

@erictraut erictraut Jan 31, 2025

Choose a reason for hiding this comment

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

What is the correct behavior if a property getter is marked final but a setter or deleter is not (or vice versa)? I ask because the different ordering would seem to imply different behaviors. Does the final apply to the property object or the method that is wrapped by the property?

Copy link
Author

Choose a reason for hiding this comment

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

I think it's a good point that the spec text here it too specific ("previous" implies an order, should read "other").

On the other hand, I think it gets a bit too precise to enumerate orders. In Pyre (and I suspect Pyright too) the non-conformance arises from attaching the decorator metadata directly to a function type, so a chained decorator that returns a non-function runs into problems.

In terms of typing, the @property and @final signatures compose in either order, so I think it's clear that the definition is legal in either order.

Copy link
Author

@samwgoldman samwgoldman Jan 31, 2025

Choose a reason for hiding this comment

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

What is the correct behavior if a property getter is marked final but a setter or deleter is not (or vice versa)? I ask because the different ordering would seem to imply different behaviors. Does the final apply to the property object or the method that is wrapped by the property?

This is a great question. I don't feel strongly, but one option:

  1. The final-ness applies to the slot on the class, not the type itself (property descriptor or otherwise)
  2. If the final property is a descriptor, then the setter should not be called on that type (either through p: Final = Descriptor() or @final-decorated descriptor)
  3. [Edited to add] Specifically for the @property descriptor, defining a setter/deleter is an error when final.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants