-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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 properties with setter type different from getter type #18510
base: master
Are you sure you want to change the base?
Conversation
Diff from mypy_primer, showing the effect of this PR on open source code: optuna (https://github.com/optuna/optuna)
+ optuna/trial/_trial.py:692: error: Unused "type: ignore" comment [unused-ignore]
python-htmlgen (https://github.com/srittau/python-htmlgen)
+ test_htmlgen/element.py:218: error: Unused "type: ignore" comment [unused-ignore]
+ test_htmlgen/element.py:224: error: Unused "type: ignore" comment [unused-ignore]
+ test_htmlgen/element.py:225: error: Unused "type: ignore" comment [unused-ignore]
+ test_htmlgen/element.py:254: error: Unused "type: ignore" comment [unused-ignore]
operator (https://github.com/canonical/operator)
+ ops/_main.py:161: error: Unused "type: ignore" comment [unused-ignore]
pip (https://github.com/pypa/pip)
+ src/pip/_internal/network/auth.py:233: error: Unused "type: ignore" comment [unused-ignore]
pywin32 (https://github.com/mhammond/pywin32)
- win32/Lib/netbios.py:286:24: error: Incompatible types in assignment (expression has type "bytes", variable has type "str") [assignment]
xarray (https://github.com/pydata/xarray)
+ xarray/core/variable.py: note: In member "data" of class "Variable":
+ xarray/core/variable.py:419: error: Incompatible override of a setter type [override]
+ xarray/core/variable.py:419: note: (base class "NamedArray" defined the type as "_arrayfunction[Any, Any] | _arrayapi[Any, Any]",
+ xarray/core/variable.py:419: note: override has type "T_DuckArray | Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]")
+ xarray/tests/test_variable.py:2707: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_variable.py:2711: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:211: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:232: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:269: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:271: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:275: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:534: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:1479: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_datatree.py:1514: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_dataset.py:6451: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_dataset.py:6452: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_dataset.py:6460: error: Unused "type: ignore" comment [unused-ignore]
+ xarray/tests/test_dataset.py:6461: error: Unused "type: ignore" comment [unused-ignore]
urllib3 (https://github.com/urllib3/urllib3)
+ src/urllib3/response.py:1259: error: Incompatible override of a setter type [override]
+ src/urllib3/response.py:1259: note: (base class "BaseHTTPResponse" defined the type as "str | None",
+ src/urllib3/response.py:1259: note: override has type "str")
+ src/urllib3/response.py:1259: note: Setter types should behave contravariantly
aiohttp-devtools (https://github.com/aio-libs/aiohttp-devtools)
+ aiohttp_devtools/runserver/serve.py:396: error: Unused "type: ignore" comment [unused-ignore]
pandas-stubs (https://github.com/pandas-dev/pandas-stubs)
+ tests/test_frame.py:2600: error: Unused "type: ignore" comment [unused-ignore]
+ tests/test_frame.py:2600: error: List item 0 has incompatible type "str"; expected "HashableT" [list-item]
+ tests/test_frame.py:2600: note: Error code "list-item" not covered by "type: ignore" comment
+ tests/test_frame.py:2600: error: List item 1 has incompatible type "str"; expected "HashableT" [list-item]
+ tests/test_frame.py:2601: error: Unused "type: ignore" comment [unused-ignore]
+ tests/test_frame.py:2601: error: List item 0 has incompatible type "int"; expected "HashableT" [list-item]
+ tests/test_frame.py:2601: note: Error code "list-item" not covered by "type: ignore" comment
+ tests/test_frame.py:2601: error: List item 1 has incompatible type "int"; expected "HashableT" [list-item]
+ tests/test_frame.py:2602: error: Unused "type: ignore" comment [unused-ignore]
+ tests/test_frame.py:2602: error: List item 0 has incompatible type "int"; expected "HashableT" [list-item]
+ tests/test_frame.py:2602: note: Error code "list-item" not covered by "type: ignore" comment
+ tests/test_frame.py:2602: error: List item 1 has incompatible type "str"; expected "HashableT" [list-item]
+ tests/test_frame.py:2603: error: Unused "type: ignore" comment [unused-ignore]
+ tests/test_frame.py:2604: error: Unused "type: ignore" comment [unused-ignore]
+ tests/test_frame.py:2605: error: Unused "type: ignore" comment [unused-ignore]
+ tests/test_frame.py:2606: error: Unused "type: ignore" comment [unused-ignore]
trio (https://github.com/python-trio/trio)
+ src/trio/_core/_run.py:408: error: Incompatible types in assignment (expression has type "None", variable has type "CancelStatus") [assignment]
+ src/trio/_core/_run.py:983: error: Incompatible types in assignment (expression has type "None", variable has type "CancelStatus") [assignment]
discord.py (https://github.com/Rapptz/discord.py)
- discord/embeds.py:179: error: Incompatible types in assignment (expression has type "int | Colour | None", variable has type "Colour | None") [assignment]
- discord/components.py:379: error: Incompatible types in assignment (expression has type "str | Emoji | PartialEmoji | None", variable has type "PartialEmoji | None") [assignment]
tornado (https://github.com/tornadoweb/tornado)
+ tornado/test/httpclient_test.py:889: error: Unused "type: ignore" comment [unused-ignore]
+ tornado/test/httpclient_test.py:903: error: Unused "type: ignore" comment [unused-ignore]
pandera (https://github.com/pandera-dev/pandera)
+ tests/core/test_schema_components.py:653: error: Unused "type: ignore" comment [unused-ignore]
+ tests/core/test_schema_components.py:659: error: Unused "type: ignore" comment [unused-ignore]
+ tests/core/test_schema_components.py:663: error: Unused "type: ignore" comment [unused-ignore]
aiohttp (https://github.com/aio-libs/aiohttp)
+ aiohttp/web_fileresponse.py:163: error: Unused "type: ignore" comment [unused-ignore]
+ aiohttp/web_fileresponse.py:164: error: Unused "type: ignore" comment [unused-ignore]
+ aiohttp/web_fileresponse.py:394: error: Unused "type: ignore" comment [unused-ignore]
+ aiohttp/web_fileresponse.py:395: error: Unused "type: ignore" comment [unused-ignore]
mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/http.py:740: error: Unused "type: ignore" comment [unused-ignore]
+ examples/addons/http-modify-form.py:13: error: Unused "type: ignore" comment [unused-ignore]
kornia (https://github.com/kornia/kornia)
+ kornia/augmentation/container/augment.py:449: error: Unsupported right operand type for in ("list[DataKey] | None") [operator]
+ kornia/augmentation/container/augment.py:450: error: Item "None" of "list[DataKey] | None" has no attribute "index" [union-attr]
- kornia/augmentation/container/augment.py:321: error: Unused "type: ignore" comment [unused-ignore]
- kornia/augmentation/container/augment.py:323: error: Unused "type: ignore" comment [unused-ignore]
- kornia/augmentation/container/augment.py:443: error: Unused "type: ignore" comment [unused-ignore]
- kornia/augmentation/container/augment.py:445: error: Unused "type: ignore" comment [unused-ignore]
werkzeug (https://github.com/pallets/werkzeug)
+ src/werkzeug/sansio/response.py:137: error: Unused "type: ignore" comment [unused-ignore]
+ src/werkzeug/sansio/response.py:149: error: Unused "type: ignore" comment [unused-ignore]
+ src/werkzeug/wrappers/response.py:700: error: Unused "type: ignore" comment [unused-ignore]
+ src/werkzeug/test.py:329: error: Unused "type: ignore" comment [unused-ignore]
+ src/werkzeug/debug/__init__.py:303: error: Incompatible types in assignment (expression has type "None", variable has type "str") [assignment]
- tests/test_wrappers.py:1082: error: Incompatible types in assignment (expression has type "Union[ContentRange, Any, None]", variable has type "ContentRange") [assignment]
- tests/test_wrappers.py:1109: error: Incompatible types in assignment (expression has type "None", variable has type "ContentSecurityPolicy") [assignment]
|
OK, I think the primer looks very good. There are few new errors (for example in The weird errors in pandas are because of this: HashableT = TypeVar("HashableT", bound=Hashable)
class DataFrame: # not generic in HashableT
@property
def columns(self) -> Index[str]: ...
@columns.setter
def columns(
self, cols: AnyArrayLike | list[HashableT] | tuple[HashableT, ...]
) -> None: ... What would this even mean, why not simply use |
Oh, I think I understand what they want (probably): HashableT = TypeVar("HashableT", bound=Hashable)
def foo(x: list[HashableT]) -> None: ...
def bar(x: list[Hashable]) -> None: ...
x: list[str]
foo(x) # OK
bar(x) # Invalid because invariance This is a clever trick, but why not simply use |
Some relevant issues (found using https://github.com/A5rocks/mypy-issues):
It looks like this messes up property deleters, though. Like this (taken from #17928): class C:
@property
def x(self) -> int:
return 0
@x.deleter
def x(self) -> None:
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! This is a long-requested feature and many users will be happy.
Looks good, just some ideas about further tests. And also please look into comments by others before merging.
@@ -1076,6 +1079,7 @@ def serialize(self) -> JsonDict: | |||
"name": self._name, | |||
"fullname": self._fullname, | |||
"type": None if self.type is None else self.type.serialize(), | |||
"setter_type": None if self.setter_type is None else self.setter_type.serialize(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a serialization test case?
# N: Following member(s) of "A4" have conflicts: \ | ||
# N: foo: expected "B1", got "str" \ | ||
# N: foo: expected setter type "C1", got "str" | ||
[builtins fixtures/property.pyi] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does mypy daemon need to be modified to detect changed in setter type, i.e. does astdiff
need to be updated? Maybe add a daemon test case that modifies the setter type.
@@ -8184,3 +8395,18 @@ class C: | |||
def f(self) -> None: | |||
__module__ # E: Name "__module__" is not defined | |||
__qualname__ # E: Name "__qualname__" is not defined | |||
|
|||
[case testPropertySetterType] | |||
class A: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also check a generic class with a property that uses a type var type, and a subclass that uses non-trivial generic inheritance?
Fixes #3004
Note: this PR should be reviewed with "hide whitespace" option (in couple long functions I replace huge
if x: ...
withif not x: return; ...
to reduce indent level).The core logic is quite straightforward (almost trivial). The only couple things are:
The tricky part is subclassing and protocols. The summary is as following:
Two things that are not implemented are multiple inheritance, and new generic syntax (inferred variance). In these cases setter types are simply ignored. There is nothing conceptually difficult about these, I simply run out of steam (and the PR is getting big). I left
TODO
s in code for these. In most cases these will generate false negatives (and they are already kind of corner cases) so I think it is OK to postpone these indefinitely.