-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Refactor all member access to go through checkmember.py (meta issue) #7724
Comments
Fixes #8020 There is a bunch of code/logic duplication around `bind_self()`, mostly because of #7724. This PR makes all three main code paths consistently follow the same structure: 1. `freshen_function_type_vars()` 2. `bind_self()` 3. `expand_type_by_instance(..., map_instance_to_supertype())` (a.k.a `map_type_from_supertype()`) I briefly scrolled through other code paths, and it looks like this was last major/obvious inconsistency (although code around `__getattr__`/`__setattr__`/`__get__`/`__set__` looks a bit suspicious).
This is a follow up for #8021, that applies the fix also to non-callable types. Currently non-callable types are still wrong: ```python R = TypeVar('R') T = TypeVar('T') # Can be any decorator that makes type non-callable. def classproperty(f: Callable[..., R]) -> R: ... class C(Generic[T]): @classproperty def test(self) -> T: ... x: C[int] y: Type[C[int]] reveal_type(x.test) # Revealed type is 'int', OK reveal_type(y.test) # Revealed type is 'T' ??? ``` So, #7724 strikes again. It turns out there is not only duplicated logic for attribute kinds (decorators vs normal methods), but also for callable vs non-callable types. In latter case we still need to expand the type (like in other places, e.g., `analyze_var`).
Plugin management is also really important here, because right now protocols do not support objects with custom # ex.py
class A(object):
attr: str # is set via plugin
def method(self) -> str: # is set via plugin
...
a = A()
reveal_type(a.attr)
# Revealed type is "builtins.float"
reveal_type(a.method())
# Revealed type is "builtins.int" Plugin: from mypy.plugin import Plugin
class MyPlugin(Plugin):
def get_attribute_hook(self, fullname):
if fullname == 'ex.A.attr':
def test(ctx):
return ctx.api.named_type('builtins.float')
return test
def get_method_signature_hook(self, fullname: str):
if fullname == 'ex.A.method':
def test(ctx):
return ctx.default_signature.copy_modified(
ret_type=ctx.api.named_type('builtins.int'),
)
return test
def plugin(version):
return MyPlugin At the moment, everything works as expected. Protocol (problematic part): from typing_extensions import Protocol
class Some(Protocol):
attr: float
def method(self) -> int:
...
def accepts_protocol(some: Some) -> int:
return some.method()
accepts_protocol(a)
# ex.py:26: error: Argument 1 to "accepts_protocol" has incompatible type "A"; expected "Some"
# ex.py:26: note: Following member(s) of "A" have conflicts:
# ex.py:26: note: attr: expected "float", got "str"
# ex.py:26: note: Expected:
# ex.py:26: note: def method(self) -> int
# ex.py:26: note: Got:
# ex.py:26: note: def method(self) -> str
|
At the moment I am trying to refactor
All things in But I have several questions:
|
Ref #12840 Fixes #11871 Fixes #14089 This is an alternative implementation to two existing PRs: #11666, #13133. This PR treats `typing.Self` as pure syntactic sugar, and transforms it into a type variable early during semantic analyzis. This way we can re-use all the existing machinery and handled edge cases for self-types. The only new thing is self-type for _attributes_ (as proposed in the PEP). This required handling in several places, since attribute access is duplicated in several places (see #7724), plus special forms (like NamedTuples and TypedDicts) and dataclasses plugin require additional care, since they use attribute annotations in special ways. I don't copy all the existing tests for "old style" self-types, but only some common use cases, possible error conditions, and relevant new edge cases, such as e.g. special forms mentioned above, and implicit type variable binding for callable types.
Another problem that came to my mind while thinking about this is how to handle deferrals? If e.g. |
There are bunch of places where we duplicate some parts of code for special logic in member access (such as properties, class methods, descriptors,
__getattr__()
, etc):checkmember.py
itselfcheck_op()
andhas_member()
Here are some issues that will be solved (or significantly simplified) by refactoring this to go through the same place (probably
checkmember.py
):Also I think having member access cleaned-up, centralized, and documented will open the way to finally solving #708 (currently the oldest high priority issue), and to having general support for descriptor protocol (for historical reasons, several things like properties are supported via some special-casing).
The text was updated successfully, but these errors were encountered: