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

Alternative implementation of PEP 673 #13133

Closed
wants to merge 43 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
83a7597
3rd times the charm
Gobot1234 Jul 15, 2022
3278f5f
Remove debug print
Gobot1234 Jul 15, 2022
fe0eea7
Fix CI
Gobot1234 Jul 17, 2022
7f5f286
Remove calls to inspect.stack()
Gobot1234 Jul 17, 2022
05ac624
Merge branch 'master' into piggybacking-TypeVar
Gobot1234 Jul 21, 2022
76f216a
Fix passing an instance as a SelfType
Gobot1234 Jul 30, 2022
e344a12
Merge branch 'piggybacking-TypeVar' of https://github.com/Gobot1234/m…
Gobot1234 Jul 30, 2022
708057d
Merge branch 'master' into piggybacking-TypeVar
Gobot1234 Jul 30, 2022
d8c02db
Fix CI
Gobot1234 Jul 30, 2022
3159549
Fix joining for obj if bool else self
Gobot1234 Aug 1, 2022
f031af7
Fix accessing an instance on cls
Gobot1234 Aug 1, 2022
ab62d93
Fix joining the other way round
Gobot1234 Aug 1, 2022
823168b
Show the name that's not found
Gobot1234 Aug 1, 2022
7ef77c7
Merge branch 'master' into piggybacking-TypeVar
Gobot1234 Aug 8, 2022
c8e9407
I have no clue what's going on here
Gobot1234 Aug 8, 2022
39c208d
So it's fine in modules but the name just isn't present???
Gobot1234 Aug 8, 2022
f05cfec
I'm just going to leave it and construct the Instance myself
Gobot1234 Aug 8, 2022
4a2b2b6
Merge branch 'master' into piggybacking-TypeVar
Gobot1234 Aug 11, 2022
6e16093
Fix a bit of CI
Gobot1234 Aug 11, 2022
0c5c26f
Fix more CI
Gobot1234 Aug 11, 2022
6f29a2f
Fix constraints
Gobot1234 Aug 13, 2022
fb1b479
Merge branch 'master' into piggybacking-TypeVar
Gobot1234 Aug 13, 2022
379c662
Fix bug with typeddict narrowing
Gobot1234 Aug 15, 2022
af3241c
Run black
Gobot1234 Aug 15, 2022
dc93feb
Fix flake8 warnings
Gobot1234 Aug 15, 2022
e01690b
More typeddict fixes
Gobot1234 Aug 15, 2022
87a4cdf
Fix more CI issues
Gobot1234 Aug 30, 2022
a1257d1
Merge branch 'master-up' into piggybacking-TypeVar
Gobot1234 Aug 30, 2022
93a96ff
Avoid printing Self in signatures
Gobot1234 Sep 11, 2022
1ede281
fix more tests
Gobot1234 Sep 13, 2022
2f02d13
Fix attribute error message for TypeVars
Gobot1234 Sep 13, 2022
bf54da0
Fix more reprs
Gobot1234 Sep 16, 2022
2be7770
Fix more CI
Gobot1234 Sep 17, 2022
cf6ac9b
Fix some tests
Gobot1234 Sep 19, 2022
904fcb2
Merge branch 'master-up' into piggybacking-TypeVar
Gobot1234 Sep 25, 2022
c657875
Fix some more failing CI
Gobot1234 Sep 25, 2022
e226e14
Fix more CI
Gobot1234 Sep 27, 2022
1495df0
Merge branch 'master' into piggybacking-TypeVar
Gobot1234 Sep 30, 2022
74feea6
Fix more more CI
Gobot1234 Oct 2, 2022
91e139d
Merge branch 'piggybacking-TypeVar' of https://github.com/Gobot1234/m…
Gobot1234 Oct 2, 2022
15924b2
Fix more tests
Gobot1234 Oct 23, 2022
7f125e9
Fix more tests
Gobot1234 Oct 27, 2022
8e60528
Fix more tests
Gobot1234 Nov 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mypy/applytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Parameters,
ParamSpecType,
PartialType,
SelfType,
Type,
TypeVarId,
TypeVarLikeType,
Expand All @@ -32,6 +33,8 @@ def get_target_type(
return type
if isinstance(tvar, TypeVarTupleType):
return type
if isinstance(tvar, SelfType):
return type
assert isinstance(tvar, TypeVarType)
values = tvar.values
p_type = get_proper_type(type)
Expand Down
3 changes: 3 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
ParamSpecType,
PartialType,
ProperType,
SelfType,
StarType,
TupleType,
Type,
Expand Down Expand Up @@ -3198,6 +3199,8 @@ def check_assignment_to_slots(self, lvalue: Lvalue) -> None:
return

inst = get_proper_type(self.expr_checker.accept(lvalue.expr))
if isinstance(inst, SelfType):
inst = inst.upper_bound
if not isinstance(inst, Instance):
return
if inst.type.slots is None:
Expand Down
6 changes: 5 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
ParamSpecType,
PartialType,
ProperType,
SelfType,
StarType,
TupleType,
Type,
Expand Down Expand Up @@ -1960,7 +1961,10 @@ def check_for_extra_actual_arguments(
def missing_classvar_callable_note(
self, object_type: Type, callable_name: str, context: Context
) -> None:
if isinstance(object_type, ProperType) and isinstance(object_type, Instance):
object_type = get_proper_type(object_type)
if isinstance(object_type, SelfType):
object_type = object_type.upper_bound
if isinstance(object_type, Instance):
_, var_name = callable_name.rsplit(".", maxsplit=1)
node = object_type.type.get(var_name)
if node is not None and isinstance(node.node, Var):
Expand Down
14 changes: 11 additions & 3 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from typing import TYPE_CHECKING, Callable, Sequence, cast

from mypy import meet, message_registry, subtypes
from mypy.erasetype import erase_typevars
from mypy.expandtype import expand_type_by_instance, freshen_function_type_vars
from mypy.maptype import map_instance_to_supertype
Expand Down Expand Up @@ -53,6 +52,7 @@
ParamSpecType,
PartialType,
ProperType,
SelfType,
TupleType,
Type,
TypedDictType,
Expand All @@ -63,13 +63,14 @@
TypeVarType,
UnionType,
get_proper_type,
has_self_types,
has_type_vars,
)

if TYPE_CHECKING: # import for forward declaration only
import mypy.checker

from mypy import state
from mypy import meet, message_registry, state, subtypes


class MemberContext:
Expand Down Expand Up @@ -609,6 +610,8 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
return make_simplified_union(
[analyze_descriptor_access(typ, mx) for typ in descriptor_type.items]
)
elif isinstance(descriptor_type, SelfType):
return instance_type
elif not isinstance(descriptor_type, Instance):
return orig_descriptor_type

Expand Down Expand Up @@ -947,7 +950,12 @@ def analyze_class_attribute_access(
# C[int].x # Also an error, since C[int] is same as C at runtime
if isinstance(t, TypeVarType) or has_type_vars(t):
# Exception: access on Type[...], including first argument of class methods is OK.
if not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit:
if has_self_types(t):
any = AnyType(TypeOfAny.from_omitted_generics)
return make_simplified_union(
[t, Instance(info, [any] * len(info.defn.type_vars))]
) # TODO look into why we can't use named_type here
elif not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit:
if node.node.is_classvar:
message = message_registry.GENERIC_CLASS_VAR_ACCESS
else:
Expand Down
9 changes: 8 additions & 1 deletion mypy/expandtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ParamSpecType,
PartialType,
ProperType,
SelfType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -106,7 +107,10 @@ def freshen_function_type_vars(callee: F) -> F:
tvmap: dict[TypeVarId, Type] = {}
for v in callee.variables:
if isinstance(v, TypeVarType):
tv: TypeVarLikeType = TypeVarType.new_unification_variable(v)
if isinstance(v, SelfType):
tv: TypeVarLikeType = v
else:
tv = TypeVarType.new_unification_variable(v)
elif isinstance(v, TypeVarTupleType):
assert isinstance(v, TypeVarTupleType)
tv = TypeVarTupleType.new_unification_variable(v)
Expand Down Expand Up @@ -158,6 +162,9 @@ def visit_instance(self, t: Instance) -> Type:
return args

def visit_type_var(self, t: TypeVarType) -> Type:
if isinstance(t, SelfType):
return expand_type(t.upper_bound, self.variables)

repl = self.variables.get(t.id, t)
if isinstance(repl, ProperType) and isinstance(repl, Instance):
# TODO: do we really need to do this?
Expand Down
5 changes: 5 additions & 0 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
PartialType,
PlaceholderType,
ProperType,
SelfType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -283,6 +284,8 @@ def visit_erased_type(self, t: ErasedType) -> ProperType:
return self.s

def visit_type_var(self, t: TypeVarType) -> ProperType:
if isinstance(t, SelfType) and isinstance(self.s, Instance) and is_subtype(self.s, t):
return mypy.typeops.make_simplified_union([self.s, t])
if isinstance(self.s, TypeVarType) and self.s.id == t.id:
return self.s
else:
Expand Down Expand Up @@ -337,6 +340,8 @@ def visit_instance(self, t: Instance) -> ProperType:
return join_types(t, self.s)
elif isinstance(self.s, LiteralType):
return join_types(t, self.s)
elif isinstance(self.s, SelfType):
return join_types(t, self.s)
else:
return self.default(self.s)

Expand Down
22 changes: 18 additions & 4 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
ParamSpecType,
PartialType,
ProperType,
SelfType,
TupleType,
Type,
TypeAliasType,
Expand Down Expand Up @@ -406,6 +407,10 @@ def has_no_attr(
extra = " (not async iterable)"
if not self.are_type_names_disabled():
failed = False
if isinstance(original_type, SelfType) and isinstance(
original_type.upper_bound, Instance
):
original_type = original_type.upper_bound
if isinstance(original_type, Instance) and original_type.type.names:
alternatives = set(original_type.type.names.keys())

Expand Down Expand Up @@ -1193,7 +1198,12 @@ def could_not_infer_type_arguments(
) -> None:
callee_name = callable_name(callee_type)
if callee_name is not None and n > 0:
self.fail(f"Cannot infer type argument {n} of {callee_name}", context)
self.fail(
f"Cannot infer type argument "
f"{n-1 if any(isinstance(v, SelfType) for v in callee_type.variables) else n} "
f"of {callee_name}",
context
)
else:
self.fail("Cannot infer function type argument", context)

Expand All @@ -1219,7 +1229,7 @@ def variable_may_be_undefined(self, name: str, context: Context) -> None:

def first_argument_for_super_must_be_type(self, actual: Type, context: Context) -> None:
actual = get_proper_type(actual)
if isinstance(actual, Instance):
if isinstance(actual, (Instance, SelfType)):
# Don't include type of instance, because it can look confusingly like a type
# object.
type_str = "a non-type instance"
Expand Down Expand Up @@ -1850,6 +1860,8 @@ def report_protocol_problems(

class_obj = False
is_module = False
if isinstance(subtype, SelfType):
subtype = subtype.upper_bound
if isinstance(subtype, TupleType):
if not isinstance(subtype.partial_fallback, Instance):
return
Expand Down Expand Up @@ -2542,7 +2554,9 @@ def [T <: int] f(self, x: int, y: T) -> None
if tp.variables:
tvars = []
for tvar in tp.variables:
if isinstance(tvar, TypeVarType):
if isinstance(tvar, SelfType):
pass
elif isinstance(tvar, TypeVarType):
upper_bound = get_proper_type(tvar.upper_bound)
if (
isinstance(upper_bound, Instance)
Expand All @@ -2560,7 +2574,7 @@ def [T <: int] f(self, x: int, y: T) -> None
else:
# For other TypeVarLikeTypes, just use the repr
tvars.append(repr(tvar))
s = f"[{', '.join(tvars)}] {s}"
s = f"[{', '.join(tvars)}] {s}" if tvars else s
return f"def {s}"


Expand Down
8 changes: 4 additions & 4 deletions mypy/plugins/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def typed_dict_get_signature_callback(ctx: MethodSigContext) -> CallableType:
and len(ctx.args[0]) == 1
and isinstance(ctx.args[0][0], StrExpr)
and len(signature.arg_types) == 2
and len(signature.variables) == 1
and len(signature.variables) == 2
and len(ctx.args[1]) == 1
):
key = ctx.args[0][0].value
Expand All @@ -198,7 +198,7 @@ def typed_dict_get_signature_callback(ctx: MethodSigContext) -> CallableType:
# Tweak the signature to include the value type as context. It's
# only needed for type inference since there's a union with a type
# variable that accepts everything.
tv = signature.variables[0]
tv = signature.variables[1]
assert isinstance(tv, TypeVarType)
return signature.copy_modified(
arg_types=[signature.arg_types[0], make_simplified_union([value_type, tv])],
Expand Down Expand Up @@ -260,7 +260,7 @@ def typed_dict_pop_signature_callback(ctx: MethodSigContext) -> CallableType:
and len(ctx.args[0]) == 1
and isinstance(ctx.args[0][0], StrExpr)
and len(signature.arg_types) == 2
and len(signature.variables) == 1
and len(signature.variables) == 2
and len(ctx.args[1]) == 1
):
key = ctx.args[0][0].value
Expand All @@ -269,7 +269,7 @@ def typed_dict_pop_signature_callback(ctx: MethodSigContext) -> CallableType:
# Tweak the signature to include the value type as context. It's
# only needed for type inference since there's a union with a type
# variable that accepts everything.
tv = signature.variables[0]
tv = signature.variables[1]
assert isinstance(tv, TypeVarType)
typ = make_simplified_union([value_type, tv])
return signature.copy_modified(arg_types=[str_type, typ], ret_type=typ)
Expand Down
44 changes: 24 additions & 20 deletions mypy/plugins/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@
"""
from __future__ import annotations

from typing import Iterable, Sequence, TypeVar, cast
from typing import Sequence, TypeVar, cast
from typing_extensions import Final

import mypy.plugin # To avoid circular imports.
from mypy.nodes import TypeInfo
from mypy.semanal_enum import ENUM_BASES
from mypy.subtypes import is_equivalent
from mypy.typeops import make_simplified_union
from mypy.types import CallableType, Instance, LiteralType, ProperType, Type, get_proper_type
from mypy.types import (
CallableType,
Instance,
LiteralType,
ProperType,
SelfType,
Type,
get_proper_type,
)

ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | {
f"{prefix}._name_" for prefix in ENUM_BASES
Expand Down Expand Up @@ -57,16 +65,6 @@ def enum_name_callback(ctx: mypy.plugin.AttributeContext) -> Type:
_T = TypeVar("_T")


def _first(it: Iterable[_T]) -> _T | None:
"""Return the first value from any iterable.

Returns ``None`` if the iterable is empty.
"""
for val in it:
return val
return None


def _infer_value_type_with_auto_fallback(
ctx: mypy.plugin.AttributeContext, proper_type: ProperType | None
) -> Type | None:
Expand All @@ -86,7 +84,7 @@ def _infer_value_type_with_auto_fallback(
# `_generate_next_value_` is `Any`. In reality the default `auto()`
# returns an `int` (presumably the `Any` in typeshed is to make it
# easier to subclass and change the returned type).
type_with_gnv = _first(ti for ti in info.mro if ti.names.get("_generate_next_value_"))
type_with_gnv = next((ti for ti in info.mro if ti.names.get("_generate_next_value_")), None)
if type_with_gnv is None:
return ctx.default_attr_type

Expand All @@ -107,10 +105,13 @@ def _implements_new(info: TypeInfo) -> bool:
subclass. In the latter case, we must infer Any as long as mypy can't infer
the type of _value_ from assignments in __new__.
"""
type_with_new = _first(
ti
for ti in info.mro
if ti.names.get("__new__") and not ti.fullname.startswith("builtins.")
type_with_new = next(
(
ti
for ti in info.mro
if ti.names.get("__new__") and not ti.fullname.startswith("builtins.")
),
None,
)
if type_with_new is None:
return False
Expand Down Expand Up @@ -147,8 +148,11 @@ class SomeEnum:
# however, if we can prove that the all of the enum members have the
# same value-type, then it doesn't matter which member was passed in.
# The value-type is still known.
if isinstance(ctx.type, Instance):
info = ctx.type.type
ctx_type = ctx.type
if isinstance(ctx_type, SelfType):
ctx_type = ctx_type.upper_bound
if isinstance(ctx_type, Instance):
info = ctx_type.type

# As long as mypy doesn't understand attribute creation in __new__,
# there is no way to predict the value type if the enum class has a
Expand All @@ -171,7 +175,7 @@ class SomeEnum:
for t in node_types
if t is None or not isinstance(t, CallableType)
)
underlying_type = _first(proper_types)
underlying_type = next(iter(proper_types), None)
if underlying_type is None:
return ctx.default_attr_type

Expand Down
Loading