[med-svn] [Git][python-team/packages/mypy][upstream] New upstream version 0.982

Michael R. Crusoe (@crusoe) gitlab at salsa.debian.org
Tue Oct 4 13:35:33 BST 2022



Michael R. Crusoe pushed to branch upstream at Debian Python Team / packages / mypy


Commits:
11c69ae6 by Michael R. Crusoe at 2022-10-04T10:29:29+02:00
New upstream version 0.982
- - - - -


10 changed files:

- PKG-INFO
- mypy.egg-info/PKG-INFO
- mypy/checker.py
- mypy/meet.py
- mypy/subtypes.py
- mypy/version.py
- test-data/unit/check-classes.test
- test-data/unit/check-inference.test
- test-data/unit/check-overloading.test
- test-data/unit/check-typevar-unbound.test


Changes:

=====================================
PKG-INFO
=====================================
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: mypy
-Version: 0.981
+Version: 0.982
 Summary: Optional static typing for Python
 Home-page: http://www.mypy-lang.org/
 Author: Jukka Lehtosalo


=====================================
mypy.egg-info/PKG-INFO
=====================================
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: mypy
-Version: 0.981
+Version: 0.982
 Summary: Optional static typing for Python
 Home-page: http://www.mypy-lang.org/
 Author: Jukka Lehtosalo


=====================================
mypy/checker.py
=====================================
@@ -752,14 +752,14 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
 
                 # Is the overload alternative's arguments subtypes of the implementation's?
                 if not is_callable_compatible(
-                    impl, sig1, is_compat=is_subtype_no_promote, ignore_return=True
+                    impl, sig1, is_compat=is_subtype, ignore_return=True
                 ):
                     self.msg.overloaded_signatures_arg_specific(i + 1, defn.impl)
 
                 # Is the overload alternative's return type a subtype of the implementation's?
                 if not (
-                    is_subtype_no_promote(sig1.ret_type, impl.ret_type)
-                    or is_subtype_no_promote(impl.ret_type, sig1.ret_type)
+                    is_subtype(sig1.ret_type, impl.ret_type)
+                    or is_subtype(impl.ret_type, sig1.ret_type)
                 ):
                     self.msg.overloaded_signatures_ret_specific(i + 1, defn.impl)
 
@@ -1235,13 +1235,23 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
 
     def check_unbound_return_typevar(self, typ: CallableType) -> None:
         """Fails when the return typevar is not defined in arguments."""
-        if typ.ret_type in typ.variables:
-            arg_type_visitor = CollectArgTypes()
+        if isinstance(typ.ret_type, TypeVarType) and typ.ret_type in typ.variables:
+            arg_type_visitor = CollectArgTypeVarTypes()
             for argtype in typ.arg_types:
                 argtype.accept(arg_type_visitor)
 
             if typ.ret_type not in arg_type_visitor.arg_types:
                 self.fail(message_registry.UNBOUND_TYPEVAR, typ.ret_type, code=TYPE_VAR)
+                upper_bound = get_proper_type(typ.ret_type.upper_bound)
+                if not (
+                    isinstance(upper_bound, Instance)
+                    and upper_bound.type.fullname == "builtins.object"
+                ):
+                    self.note(
+                        "Consider using the upper bound "
+                        f"{format_type(typ.ret_type.upper_bound)} instead",
+                        context=typ.ret_type,
+                    )
 
     def check_default_args(self, item: FuncItem, body_is_trivial: bool) -> None:
         for arg in item.arguments:
@@ -6156,7 +6166,7 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
         )
 
 
-class CollectArgTypes(TypeTraverserVisitor):
+class CollectArgTypeVarTypes(TypeTraverserVisitor):
     """Collects the non-nested argument types in a set."""
 
     def __init__(self) -> None:
@@ -6496,7 +6506,7 @@ def is_unsafe_overlapping_overload_signatures(
     return is_callable_compatible(
         signature,
         other,
-        is_compat=is_overlapping_types_no_promote,
+        is_compat=is_overlapping_types_no_promote_no_uninhabited,
         is_compat_return=lambda l, r: not is_subtype_no_promote(l, r),
         ignore_return=False,
         check_args_covariantly=True,
@@ -6504,7 +6514,7 @@ def is_unsafe_overlapping_overload_signatures(
     ) or is_callable_compatible(
         other,
         signature,
-        is_compat=is_overlapping_types_no_promote,
+        is_compat=is_overlapping_types_no_promote_no_uninhabited,
         is_compat_return=lambda l, r: not is_subtype_no_promote(r, l),
         ignore_return=False,
         check_args_covariantly=False,
@@ -6988,8 +6998,12 @@ def is_subtype_no_promote(left: Type, right: Type) -> bool:
     return is_subtype(left, right, ignore_promotions=True)
 
 
-def is_overlapping_types_no_promote(left: Type, right: Type) -> bool:
-    return is_overlapping_types(left, right, ignore_promotions=True)
+def is_overlapping_types_no_promote_no_uninhabited(left: Type, right: Type) -> bool:
+    # For the purpose of unsafe overload checks we consider list[<nothing>] and list[int]
+    # non-overlapping. This is consistent with how we treat list[int] and list[str] as
+    # non-overlapping, despite [] belongs to both. Also this will prevent false positives
+    # for failed type inference during unification.
+    return is_overlapping_types(left, right, ignore_promotions=True, ignore_uninhabited=True)
 
 
 def is_private(node_name: str) -> bool:


=====================================
mypy/meet.py
=====================================
@@ -215,6 +215,7 @@ def is_overlapping_types(
     right: Type,
     ignore_promotions: bool = False,
     prohibit_none_typevar_overlap: bool = False,
+    ignore_uninhabited: bool = False,
 ) -> bool:
     """Can a value of type 'left' also be of type 'right' or vice-versa?
 
@@ -239,6 +240,7 @@ def is_overlapping_types(
             right,
             ignore_promotions=ignore_promotions,
             prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
+            ignore_uninhabited=ignore_uninhabited,
         )
 
     # We should never encounter this type.
@@ -286,8 +288,10 @@ def is_overlapping_types(
     ):
         return True
 
-    if is_proper_subtype(left, right, ignore_promotions=ignore_promotions) or is_proper_subtype(
-        right, left, ignore_promotions=ignore_promotions
+    if is_proper_subtype(
+        left, right, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited
+    ) or is_proper_subtype(
+        right, left, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited
     ):
         return True
 
@@ -429,8 +433,10 @@ def is_overlapping_types(
     if isinstance(left, Instance) and isinstance(right, Instance):
         # First we need to handle promotions and structural compatibility for instances
         # that came as fallbacks, so simply call is_subtype() to avoid code duplication.
-        if is_subtype(left, right, ignore_promotions=ignore_promotions) or is_subtype(
-            right, left, ignore_promotions=ignore_promotions
+        if is_subtype(
+            left, right, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited
+        ) or is_subtype(
+            right, left, ignore_promotions=ignore_promotions, ignore_uninhabited=ignore_uninhabited
         ):
             return True
 
@@ -471,7 +477,7 @@ def is_overlapping_types(
     # Note: it's unclear however, whether returning False is the right thing
     # to do when inferring reachability -- see  https://github.com/python/mypy/issues/5529
 
-    assert type(left) != type(right)
+    assert type(left) != type(right), f"{type(left)} vs {type(right)}"
     return False
 
 


=====================================
mypy/subtypes.py
=====================================
@@ -68,7 +68,7 @@ IS_SETTABLE: Final = 1
 IS_CLASSVAR: Final = 2
 IS_CLASS_OR_STATIC: Final = 3
 
-TypeParameterChecker: _TypeAlias = Callable[[Type, Type, int, bool], bool]
+TypeParameterChecker: _TypeAlias = Callable[[Type, Type, int, bool, "SubtypeContext"], bool]
 
 
 class SubtypeContext:
@@ -81,6 +81,7 @@ class SubtypeContext:
         ignore_declared_variance: bool = False,
         # Supported for both proper and non-proper
         ignore_promotions: bool = False,
+        ignore_uninhabited: bool = False,
         # Proper subtype flags
         erase_instances: bool = False,
         keep_erased_types: bool = False,
@@ -90,6 +91,7 @@ class SubtypeContext:
         self.ignore_pos_arg_names = ignore_pos_arg_names
         self.ignore_declared_variance = ignore_declared_variance
         self.ignore_promotions = ignore_promotions
+        self.ignore_uninhabited = ignore_uninhabited
         self.erase_instances = erase_instances
         self.keep_erased_types = keep_erased_types
         self.options = options
@@ -116,6 +118,7 @@ def is_subtype(
     ignore_pos_arg_names: bool = False,
     ignore_declared_variance: bool = False,
     ignore_promotions: bool = False,
+    ignore_uninhabited: bool = False,
     options: Options | None = None,
 ) -> bool:
     """Is 'left' subtype of 'right'?
@@ -135,6 +138,7 @@ def is_subtype(
             ignore_pos_arg_names=ignore_pos_arg_names,
             ignore_declared_variance=ignore_declared_variance,
             ignore_promotions=ignore_promotions,
+            ignore_uninhabited=ignore_uninhabited,
             options=options,
         )
     else:
@@ -144,6 +148,7 @@ def is_subtype(
                 ignore_pos_arg_names,
                 ignore_declared_variance,
                 ignore_promotions,
+                ignore_uninhabited,
                 options,
             }
         ), "Don't pass both context and individual flags"
@@ -178,6 +183,7 @@ def is_proper_subtype(
     *,
     subtype_context: SubtypeContext | None = None,
     ignore_promotions: bool = False,
+    ignore_uninhabited: bool = False,
     erase_instances: bool = False,
     keep_erased_types: bool = False,
 ) -> bool:
@@ -193,12 +199,19 @@ def is_proper_subtype(
     if subtype_context is None:
         subtype_context = SubtypeContext(
             ignore_promotions=ignore_promotions,
+            ignore_uninhabited=ignore_uninhabited,
             erase_instances=erase_instances,
             keep_erased_types=keep_erased_types,
         )
     else:
         assert not any(
-            {ignore_promotions, erase_instances, keep_erased_types}
+            {
+                ignore_promotions,
+                ignore_uninhabited,
+                erase_instances,
+                keep_erased_types,
+                ignore_uninhabited,
+            }
         ), "Don't pass both context and individual flags"
     if TypeState.is_assumed_proper_subtype(left, right):
         return True
@@ -216,6 +229,7 @@ def is_equivalent(
     ignore_type_params: bool = False,
     ignore_pos_arg_names: bool = False,
     options: Options | None = None,
+    subtype_context: SubtypeContext | None = None,
 ) -> bool:
     return is_subtype(
         a,
@@ -223,16 +237,20 @@ def is_equivalent(
         ignore_type_params=ignore_type_params,
         ignore_pos_arg_names=ignore_pos_arg_names,
         options=options,
+        subtype_context=subtype_context,
     ) and is_subtype(
         b,
         a,
         ignore_type_params=ignore_type_params,
         ignore_pos_arg_names=ignore_pos_arg_names,
         options=options,
+        subtype_context=subtype_context,
     )
 
 
-def is_same_type(a: Type, b: Type, ignore_promotions: bool = True) -> bool:
+def is_same_type(
+    a: Type, b: Type, ignore_promotions: bool = True, subtype_context: SubtypeContext | None = None
+) -> bool:
     """Are these types proper subtypes of each other?
 
     This means types may have different representation (e.g. an alias, or
@@ -242,8 +260,10 @@ def is_same_type(a: Type, b: Type, ignore_promotions: bool = True) -> bool:
     # considered not the same type (which is the case at runtime).
     # Also Union[bool, int] (if it wasn't simplified before) will be different
     # from plain int, etc.
-    return is_proper_subtype(a, b, ignore_promotions=ignore_promotions) and is_proper_subtype(
-        b, a, ignore_promotions=ignore_promotions
+    return is_proper_subtype(
+        a, b, ignore_promotions=ignore_promotions, subtype_context=subtype_context
+    ) and is_proper_subtype(
+        b, a, ignore_promotions=ignore_promotions, subtype_context=subtype_context
     )
 
 
@@ -307,11 +327,15 @@ def _is_subtype(
     return left.accept(SubtypeVisitor(orig_right, subtype_context, proper_subtype))
 
 
-# TODO: should we pass on the original flags here and in couple other places?
-# This seems logical but was never done in the past for some reasons.
-def check_type_parameter(lefta: Type, righta: Type, variance: int, proper_subtype: bool) -> bool:
+def check_type_parameter(
+    lefta: Type, righta: Type, variance: int, proper_subtype: bool, subtype_context: SubtypeContext
+) -> bool:
     def check(left: Type, right: Type) -> bool:
-        return is_proper_subtype(left, right) if proper_subtype else is_subtype(left, right)
+        return (
+            is_proper_subtype(left, right, subtype_context=subtype_context)
+            if proper_subtype
+            else is_subtype(left, right, subtype_context=subtype_context)
+        )
 
     if variance == COVARIANT:
         return check(lefta, righta)
@@ -319,11 +343,18 @@ def check_type_parameter(lefta: Type, righta: Type, variance: int, proper_subtyp
         return check(righta, lefta)
     else:
         if proper_subtype:
-            return is_same_type(lefta, righta)
-        return is_equivalent(lefta, righta)
+            # We pass ignore_promotions=False because it is a default for subtype checks.
+            # The actual value will be taken from the subtype_context, and it is whatever
+            # the original caller passed.
+            return is_same_type(
+                lefta, righta, ignore_promotions=False, subtype_context=subtype_context
+            )
+        return is_equivalent(lefta, righta, subtype_context=subtype_context)
 
 
-def ignore_type_parameter(lefta: Type, righta: Type, variance: int, proper_subtype: bool) -> bool:
+def ignore_type_parameter(
+    lefta: Type, righta: Type, variance: int, proper_subtype: bool, subtype_context: SubtypeContext
+) -> bool:
     return True
 
 
@@ -386,7 +417,11 @@ class SubtypeVisitor(TypeVisitor[bool]):
             return True
 
     def visit_uninhabited_type(self, left: UninhabitedType) -> bool:
-        return True
+        # We ignore this for unsafe overload checks, so that and empty list and
+        # a list of int will be considered non-overlapping.
+        if isinstance(self.right, UninhabitedType):
+            return True
+        return not self.subtype_context.ignore_uninhabited
 
     def visit_erased_type(self, left: ErasedType) -> bool:
         # This may be encountered during type inference. The result probably doesn't
@@ -522,12 +557,12 @@ class SubtypeVisitor(TypeVisitor[bool]):
                 for lefta, righta, tvar in type_params:
                     if isinstance(tvar, TypeVarType):
                         if not self.check_type_parameter(
-                            lefta, righta, tvar.variance, self.proper_subtype
+                            lefta, righta, tvar.variance, self.proper_subtype, self.subtype_context
                         ):
                             nominal = False
                     else:
                         if not self.check_type_parameter(
-                            lefta, righta, COVARIANT, self.proper_subtype
+                            lefta, righta, COVARIANT, self.proper_subtype, self.subtype_context
                         ):
                             nominal = False
                 if nominal:
@@ -697,6 +732,7 @@ class SubtypeVisitor(TypeVisitor[bool]):
             if not left.names_are_wider_than(right):
                 return False
             for name, l, r in left.zip(right):
+                # TODO: should we pass on the full subtype_context here and below?
                 if self.proper_subtype:
                     check = is_same_type(l, r)
                 else:
@@ -847,6 +883,10 @@ class SubtypeVisitor(TypeVisitor[bool]):
         if isinstance(right, TypeType):
             return self._is_subtype(left.item, right.item)
         if isinstance(right, CallableType):
+            if self.proper_subtype and not right.is_type_obj():
+                # We can't accept `Type[X]` as a *proper* subtype of Callable[P, X]
+                # since this will break transitivity of subtyping.
+                return False
             # This is unsound, we don't check the __init__ signature.
             return self._is_subtype(left.item, right.ret_type)
         if isinstance(right, Instance):


=====================================
mypy/version.py
=====================================
@@ -8,7 +8,7 @@ from mypy import git
 # - Release versions have the form "0.NNN".
 # - Dev versions have the form "0.NNN+dev" (PLUS sign to conform to PEP 440).
 # - For 1.0 we'll switch back to 1.2.3 form.
-__version__ = "0.981"
+__version__ = "0.982"
 base_version = __version__
 
 mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))


=====================================
test-data/unit/check-classes.test
=====================================
@@ -3233,6 +3233,7 @@ def error(u_c: Type[U]) -> P: # Error here, see below
 [out]
 main:11: note: Revealed type is "__main__.WizUser"
 main:12: error: A function returning TypeVar should receive at least one argument containing the same Typevar
+main:12: note: Consider using the upper bound "ProUser" instead
 main:13: error: Value of type variable "P" of "new_pro" cannot be "U"
 main:13: error: Incompatible return value type (got "U", expected "P")
 


=====================================
test-data/unit/check-inference.test
=====================================
@@ -3264,3 +3264,13 @@ from typing import Dict, Iterable, Tuple, Union
 def foo(x: Union[Tuple[str, Dict[str, int], str], Iterable[object]]) -> None: ...
 foo(("a", {"a": "b"}, "b"))
 [builtins fixtures/dict.pyi]
+
+[case testUnionTypeCallableInference]
+from typing import Callable, Type, TypeVar, Union
+
+class A:
+    def __init__(self, x: str) -> None: ...
+
+T = TypeVar("T")
+def type_or_callable(value: T, tp: Union[Type[T], Callable[[int], T]]) -> T: ...
+reveal_type(type_or_callable(A("test"), A))  # N: Revealed type is "__main__.A"


=====================================
test-data/unit/check-overloading.test
=====================================
@@ -6467,3 +6467,29 @@ spam: Callable[..., str] = lambda x, y: 'baz'
 reveal_type(func(spam))  # N: Revealed type is "def (*Any, **Any) -> builtins.str"
 
 [builtins fixtures/paramspec.pyi]
+
+[case testGenericOverloadOverlapWithType]
+import m
+
+[file m.pyi]
+from typing import TypeVar, Type, overload, Callable
+
+T = TypeVar("T", bound=str)
+ at overload
+def foo(x: Type[T] | int) -> int: ...
+ at overload
+def foo(x: Callable[[int], bool]) -> str: ...
+
+[case testGenericOverloadOverlapWithCollection]
+import m
+
+[file m.pyi]
+from typing import TypeVar, Sequence, overload, List
+
+T = TypeVar("T", bound=str)
+
+ at overload
+def foo(x: List[T]) -> str: ...
+ at overload
+def foo(x: Sequence[int]) -> int: ...
+[builtins fixtures/list.pyi]


=====================================
test-data/unit/check-typevar-unbound.test
=====================================
@@ -1,4 +1,3 @@
-
 [case testUnboundTypeVar]
 from typing import TypeVar
 
@@ -6,9 +5,19 @@ T = TypeVar('T')
 
 def f() -> T: # E: A function returning TypeVar should receive at least one argument containing the same Typevar
     ...
-
 f()
 
+U = TypeVar('U', bound=int)
+
+def g() -> U: # E: A function returning TypeVar should receive at least one argument containing the same Typevar \
+              # N: Consider using the upper bound "int" instead
+    ...
+
+V = TypeVar('V', int, str)
+
+# TODO: this should also give an error
+def h() -> V:
+    ...
 
 [case testInnerFunctionTypeVar]
 
@@ -21,7 +30,6 @@ def g(a: T) -> T:
         ...
     return f()
 
-
 [case testUnboundIterableOfTypeVars]
 from typing import Iterable, TypeVar
 
@@ -29,7 +37,6 @@ T = TypeVar('T')
 
 def f() -> Iterable[T]:
     ...
-
 f()
 
 [case testBoundTypeVar]
@@ -40,7 +47,6 @@ T = TypeVar('T')
 def f(a: T, b: T, c: int) -> T:
     ...
 
-
 [case testNestedBoundTypeVar]
 from typing import Callable, List, Union, Tuple, TypeVar
 



View it on GitLab: https://salsa.debian.org/python-team/packages/mypy/-/commit/11c69ae6a6dc155b25bd63381b55c973d8f682b0

-- 
View it on GitLab: https://salsa.debian.org/python-team/packages/mypy/-/commit/11c69ae6a6dc155b25bd63381b55c973d8f682b0
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20221004/f442490f/attachment-0001.htm>


More information about the debian-med-commit mailing list