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

Michael R. Crusoe (@crusoe) gitlab at salsa.debian.org
Thu Nov 23 19:33:12 GMT 2023



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


Commits:
43c0114b by Michael R. Crusoe at 2023-11-23T19:15:41+01:00
New upstream version 1.7.1
- - - - -


23 changed files:

- PKG-INFO
- mypy.egg-info/PKG-INFO
- mypy/checker.py
- mypy/checkexpr.py
- mypy/meet.py
- mypy/solve.py
- mypy/subtypes.py
- mypy/typeops.py
- mypy/version.py
- mypyc/irbuild/prebuildvisitor.py
- mypyc/test-data/run-functions.test
- test-data/unit/check-dataclasses.test
- test-data/unit/check-expressions.test
- test-data/unit/check-final.test
- test-data/unit/check-functions.test
- test-data/unit/check-inference.test
- test-data/unit/check-narrowing.test
- test-data/unit/check-parameter-specification.test
- test-data/unit/check-selftype.test
- test-data/unit/fine-grained-dataclass-transform.test
- test-data/unit/fixtures/dataclasses.pyi
- test-data/unit/fixtures/len.pyi
- test-data/unit/fixtures/list.pyi


Changes:

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


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


=====================================
mypy/checker.py
=====================================
@@ -1879,6 +1879,7 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
             found_method_base_classes
             and not defn.is_explicit_override
             and defn.name not in ("__init__", "__new__")
+            and not is_private(defn.name)
         ):
             self.msg.explicit_override_decorator_missing(
                 defn.name, found_method_base_classes[0].fullname, context or defn
@@ -1921,7 +1922,7 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
             base_attr = base.names.get(name)
             if base_attr:
                 # First, check if we override a final (always an error, even with Any types).
-                if is_final_node(base_attr.node):
+                if is_final_node(base_attr.node) and not is_private(name):
                     self.msg.cant_override_final(name, base.name, defn)
                 # Second, final can't override anything writeable independently of types.
                 if defn.is_final:
@@ -2679,7 +2680,7 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
             ok = True
         # Final attributes can never be overridden, but can override
         # non-final read-only attributes.
-        if is_final_node(second.node):
+        if is_final_node(second.node) and not is_private(name):
             self.msg.cant_override_final(name, base2.name, ctx)
         if is_final_node(first.node):
             self.check_if_final_var_override_writable(name, second.node, ctx)
@@ -3293,6 +3294,8 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
         """
         if not isinstance(base_node, (Var, FuncBase, Decorator)):
             return True
+        if is_private(node.name):
+            return True
         if base_node.is_final and (node.is_final or not isinstance(base_node, Var)):
             # Give this error only for explicit override attempt with `Final`, or
             # if we are overriding a final method with variable.


=====================================
mypy/checkexpr.py
=====================================
@@ -3617,8 +3617,9 @@ class ExpressionChecker(ExpressionVisitor[Type]):
         self,
         left: Type,
         right: Type,
-        original_container: Type | None = None,
         *,
+        original_container: Type | None = None,
+        seen_types: set[tuple[Type, Type]] | None = None,
         prefer_literal: bool = True,
     ) -> bool:
         """Check for dangerous non-overlapping comparisons like 42 == 'no'.
@@ -3639,6 +3640,12 @@ class ExpressionChecker(ExpressionVisitor[Type]):
         if not self.chk.options.strict_equality:
             return False
 
+        if seen_types is None:
+            seen_types = set()
+        if (left, right) in seen_types:
+            return False
+        seen_types.add((left, right))
+
         left, right = get_proper_types((left, right))
 
         # We suppress the error if there is a custom __eq__() method on either
@@ -3694,17 +3701,21 @@ class ExpressionChecker(ExpressionVisitor[Type]):
                 abstract_set = self.chk.lookup_typeinfo("typing.AbstractSet")
                 left = map_instance_to_supertype(left, abstract_set)
                 right = map_instance_to_supertype(right, abstract_set)
-                return self.dangerous_comparison(left.args[0], right.args[0])
+                return self.dangerous_comparison(
+                    left.args[0], right.args[0], seen_types=seen_types
+                )
             elif left.type.has_base("typing.Mapping") and right.type.has_base("typing.Mapping"):
                 # Similar to above: Mapping ignores the classes, it just compares items.
                 abstract_map = self.chk.lookup_typeinfo("typing.Mapping")
                 left = map_instance_to_supertype(left, abstract_map)
                 right = map_instance_to_supertype(right, abstract_map)
                 return self.dangerous_comparison(
-                    left.args[0], right.args[0]
-                ) or self.dangerous_comparison(left.args[1], right.args[1])
+                    left.args[0], right.args[0], seen_types=seen_types
+                ) or self.dangerous_comparison(left.args[1], right.args[1], seen_types=seen_types)
             elif left_name in ("builtins.list", "builtins.tuple") and right_name == left_name:
-                return self.dangerous_comparison(left.args[0], right.args[0])
+                return self.dangerous_comparison(
+                    left.args[0], right.args[0], seen_types=seen_types
+                )
             elif left_name in OVERLAPPING_BYTES_ALLOWLIST and right_name in (
                 OVERLAPPING_BYTES_ALLOWLIST
             ):
@@ -6198,11 +6209,16 @@ class PolyTranslator(TypeTranslator):
     See docstring for apply_poly() for details.
     """
 
-    def __init__(self, poly_tvars: Sequence[TypeVarLikeType]) -> None:
+    def __init__(
+        self,
+        poly_tvars: Iterable[TypeVarLikeType],
+        bound_tvars: frozenset[TypeVarLikeType] = frozenset(),
+        seen_aliases: frozenset[TypeInfo] = frozenset(),
+    ) -> None:
         self.poly_tvars = set(poly_tvars)
         # This is a simplified version of TypeVarScope used during semantic analysis.
-        self.bound_tvars: set[TypeVarLikeType] = set()
-        self.seen_aliases: set[TypeInfo] = set()
+        self.bound_tvars = bound_tvars
+        self.seen_aliases = seen_aliases
 
     def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]:
         found_vars = []
@@ -6278,10 +6294,11 @@ class PolyTranslator(TypeTranslator):
         if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]:
             if t.type in self.seen_aliases:
                 raise PolyTranslationError()
-            self.seen_aliases.add(t.type)
             call = find_member("__call__", t, t, is_operator=True)
             assert call is not None
-            return call.accept(self)
+            return call.accept(
+                PolyTranslator(self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type})
+            )
         return super().visit_instance(t)
 
 


=====================================
mypy/meet.py
=====================================
@@ -16,6 +16,7 @@ from mypy.subtypes import (
 from mypy.typeops import is_recursive_pair, make_simplified_union, tuple_fallback
 from mypy.types import (
     MYPYC_NATIVE_INT_NAMES,
+    TUPLE_LIKE_INSTANCE_NAMES,
     AnyType,
     CallableType,
     DeletedType,
@@ -261,6 +262,7 @@ def is_overlapping_types(
     ignore_promotions: bool = False,
     prohibit_none_typevar_overlap: bool = False,
     ignore_uninhabited: bool = False,
+    seen_types: set[tuple[Type, Type]] | None = None,
 ) -> bool:
     """Can a value of type 'left' also be of type 'right' or vice-versa?
 
@@ -274,18 +276,27 @@ def is_overlapping_types(
         # A type guard forces the new type even if it doesn't overlap the old.
         return True
 
+    if seen_types is None:
+        seen_types = set()
+    if (left, right) in seen_types:
+        return True
+    if isinstance(left, TypeAliasType) and isinstance(right, TypeAliasType):
+        seen_types.add((left, right))
+
     left, right = get_proper_types((left, right))
 
     def _is_overlapping_types(left: Type, right: Type) -> bool:
         """Encode the kind of overlapping check to perform.
 
-        This function mostly exists so we don't have to repeat keyword arguments everywhere."""
+        This function mostly exists, so we don't have to repeat keyword arguments everywhere.
+        """
         return is_overlapping_types(
             left,
             right,
             ignore_promotions=ignore_promotions,
             prohibit_none_typevar_overlap=prohibit_none_typevar_overlap,
             ignore_uninhabited=ignore_uninhabited,
+            seen_types=seen_types.copy(),
         )
 
     # We should never encounter this type.
@@ -936,7 +947,7 @@ class TypeMeetVisitor(TypeVisitor[ProperType]):
             return TupleType(items, tuple_fallback(t))
         elif isinstance(self.s, Instance):
             # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>].
-            if self.s.type.fullname == "builtins.tuple" and self.s.args:
+            if self.s.type.fullname in TUPLE_LIKE_INSTANCE_NAMES and self.s.args:
                 return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items])
             elif is_proper_subtype(t, self.s):
                 # A named tuple that inherits from a normal class


=====================================
mypy/solve.py
=====================================
@@ -6,7 +6,7 @@ from collections import defaultdict
 from typing import Iterable, Sequence
 from typing_extensions import TypeAlias as _TypeAlias
 
-from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints
+from mypy.constraints import SUBTYPE_OF, SUPERTYPE_OF, Constraint, infer_constraints, neg_op
 from mypy.expandtype import expand_type
 from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort
 from mypy.join import join_types
@@ -69,6 +69,10 @@ def solve_constraints(
         extra_vars.extend([v.id for v in c.extra_tvars if v.id not in vars + extra_vars])
         originals.update({v.id: v for v in c.extra_tvars if v.id not in originals})
 
+    if allow_polymorphic:
+        # Constraints inferred from unions require special handling in polymorphic inference.
+        constraints = skip_reverse_union_constraints(constraints)
+
     # Collect a list of constraints for each type variable.
     cmap: dict[TypeVarId, list[Constraint]] = {tv: [] for tv in vars + extra_vars}
     for con in constraints:
@@ -431,10 +435,7 @@ def transitive_closure(
                     uppers[l] |= uppers[upper]
             for lt in lowers[lower]:
                 for ut in uppers[upper]:
-                    # TODO: what if secondary constraints result in inference
-                    # against polymorphic actual (also in below branches)?
-                    remaining |= set(infer_constraints(lt, ut, SUBTYPE_OF))
-                    remaining |= set(infer_constraints(ut, lt, SUPERTYPE_OF))
+                    add_secondary_constraints(remaining, lt, ut)
         elif c.op == SUBTYPE_OF:
             if c.target in uppers[c.type_var]:
                 continue
@@ -442,8 +443,7 @@ def transitive_closure(
                 if (l, c.type_var) in graph:
                     uppers[l].add(c.target)
             for lt in lowers[c.type_var]:
-                remaining |= set(infer_constraints(lt, c.target, SUBTYPE_OF))
-                remaining |= set(infer_constraints(c.target, lt, SUPERTYPE_OF))
+                add_secondary_constraints(remaining, lt, c.target)
         else:
             assert c.op == SUPERTYPE_OF
             if c.target in lowers[c.type_var]:
@@ -452,11 +452,24 @@ def transitive_closure(
                 if (c.type_var, u) in graph:
                     lowers[u].add(c.target)
             for ut in uppers[c.type_var]:
-                remaining |= set(infer_constraints(ut, c.target, SUPERTYPE_OF))
-                remaining |= set(infer_constraints(c.target, ut, SUBTYPE_OF))
+                add_secondary_constraints(remaining, c.target, ut)
     return graph, lowers, uppers
 
 
+def add_secondary_constraints(cs: set[Constraint], lower: Type, upper: Type) -> None:
+    """Add secondary constraints inferred between lower and upper (in place)."""
+    if isinstance(get_proper_type(upper), UnionType) and isinstance(
+        get_proper_type(lower), UnionType
+    ):
+        # When both types are unions, this can lead to inferring spurious constraints,
+        # for example Union[T, int] <: S <: Union[T, int] may infer T <: int.
+        # To avoid this, just skip them for now.
+        return
+    # TODO: what if secondary constraints result in inference against polymorphic actual?
+    cs.update(set(infer_constraints(lower, upper, SUBTYPE_OF)))
+    cs.update(set(infer_constraints(upper, lower, SUPERTYPE_OF)))
+
+
 def compute_dependencies(
     tvars: list[TypeVarId], graph: Graph, lowers: Bounds, uppers: Bounds
 ) -> dict[TypeVarId, list[TypeVarId]]:
@@ -494,6 +507,28 @@ def check_linear(scc: set[TypeVarId], lowers: Bounds, uppers: Bounds) -> bool:
     return True
 
 
+def skip_reverse_union_constraints(cs: list[Constraint]) -> list[Constraint]:
+    """Avoid ambiguities for constraints inferred from unions during polymorphic inference.
+
+    Polymorphic inference implicitly relies on assumption that a reverse of a linear constraint
+    is a linear constraint. This is however not true in presence of union types, for example
+    T :> Union[S, int] vs S <: T. Trying to solve such constraints would be detected ambiguous
+    as (T, S) form a non-linear SCC. However, simply removing the linear part results in a valid
+    solution T = Union[S, int], S = <free>.
+
+    TODO: a cleaner solution may be to avoid inferring such constraints in first place, but
+    this would require passing around a flag through all infer_constraints() calls.
+    """
+    reverse_union_cs = set()
+    for c in cs:
+        p_target = get_proper_type(c.target)
+        if isinstance(p_target, UnionType):
+            for item in p_target.items:
+                if isinstance(item, TypeVarType):
+                    reverse_union_cs.add(Constraint(item, neg_op(c.op), c.origin_type_var))
+    return [c for c in cs if c not in reverse_union_cs]
+
+
 def get_vars(target: Type, vars: list[TypeVarId]) -> set[TypeVarId]:
     """Find type variables for which we are solving in a target type."""
     return {tv.id for tv in get_all_type_vars(target)} & set(vars)


=====================================
mypy/subtypes.py
=====================================
@@ -1651,7 +1651,12 @@ def are_parameters_compatible(
                 continue
             return False
         if not are_args_compatible(
-            left_arg, right_arg, ignore_pos_arg_names, allow_partial_overlap, is_compat
+            left_arg,
+            right_arg,
+            is_compat,
+            ignore_pos_arg_names=ignore_pos_arg_names,
+            allow_partial_overlap=allow_partial_overlap,
+            allow_imprecise_kinds=right.imprecise_arg_kinds,
         ):
             return False
 
@@ -1676,9 +1681,9 @@ def are_parameters_compatible(
             if not are_args_compatible(
                 left_by_position,
                 right_by_position,
-                ignore_pos_arg_names,
-                allow_partial_overlap,
                 is_compat,
+                ignore_pos_arg_names=ignore_pos_arg_names,
+                allow_partial_overlap=allow_partial_overlap,
             ):
                 return False
             i += 1
@@ -1711,7 +1716,11 @@ def are_parameters_compatible(
                 continue
 
             if not are_args_compatible(
-                left_by_name, right_by_name, ignore_pos_arg_names, allow_partial_overlap, is_compat
+                left_by_name,
+                right_by_name,
+                is_compat,
+                ignore_pos_arg_names=ignore_pos_arg_names,
+                allow_partial_overlap=allow_partial_overlap,
             ):
                 return False
 
@@ -1735,6 +1744,7 @@ def are_parameters_compatible(
             and right_by_name != right_by_pos
             and (right_by_pos.required or right_by_name.required)
             and strict_concatenate_check
+            and not right.imprecise_arg_kinds
         ):
             return False
 
@@ -1749,9 +1759,11 @@ def are_parameters_compatible(
 def are_args_compatible(
     left: FormalArgument,
     right: FormalArgument,
+    is_compat: Callable[[Type, Type], bool],
+    *,
     ignore_pos_arg_names: bool,
     allow_partial_overlap: bool,
-    is_compat: Callable[[Type, Type], bool],
+    allow_imprecise_kinds: bool = False,
 ) -> bool:
     if left.required and right.required:
         # If both arguments are required allow_partial_overlap has no effect.
@@ -1779,7 +1791,7 @@ def are_args_compatible(
             return False
 
     # If right is at a specific position, left must have the same:
-    if is_different(left.pos, right.pos):
+    if is_different(left.pos, right.pos) and not allow_imprecise_kinds:
         return False
 
     # If right's argument is optional, left's must also be


=====================================
mypy/typeops.py
=====================================
@@ -244,15 +244,15 @@ def map_type_from_supertype(typ: Type, sub_info: TypeInfo, super_info: TypeInfo)
     return expand_type_by_instance(typ, inst_type)
 
 
-def supported_self_type(typ: ProperType) -> bool:
+def supported_self_type(typ: ProperType, allow_callable: bool = True) -> bool:
     """Is this a supported kind of explicit self-types?
 
-    Currently, this means a X or Type[X], where X is an instance or
+    Currently, this means an X or Type[X], where X is an instance or
     a type variable with an instance upper bound.
     """
     if isinstance(typ, TypeType):
         return supported_self_type(typ.item)
-    if isinstance(typ, CallableType):
+    if allow_callable and isinstance(typ, CallableType):
         # Special case: allow class callable instead of Type[...] as cls annotation,
         # as well as callable self for callback protocols.
         return True
@@ -306,7 +306,11 @@ def bind_self(method: F, original_type: Type | None = None, is_classmethod: bool
     self_param_type = get_proper_type(func.arg_types[0])
 
     variables: Sequence[TypeVarLikeType]
-    if func.variables and supported_self_type(self_param_type):
+    # Having a def __call__(self: Callable[...], ...) can cause infinite recursion. Although
+    # this special-casing looks not very principled, there is nothing meaningful we can infer
+    # from such definition, since it is inherently indefinitely recursive.
+    allow_callable = func.name is None or not func.name.startswith("__call__ of")
+    if func.variables and supported_self_type(self_param_type, allow_callable=allow_callable):
         from mypy.infer import infer_type_arguments
 
         if original_type is None:


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


=====================================
mypyc/irbuild/prebuildvisitor.py
=====================================
@@ -119,9 +119,10 @@ class PreBuildVisitor(ExtendedTraverserVisitor):
                 self.funcs_to_decorators[dec.func] = decorators_to_store
         super().visit_decorator(dec)
 
-    def visit_func_def(self, fdef: FuncItem) -> None:
+    def visit_func_def(self, fdef: FuncDef) -> None:
         # TODO: What about overloaded functions?
         self.visit_func(fdef)
+        self.visit_symbol_node(fdef)
 
     def visit_lambda_expr(self, expr: LambdaExpr) -> None:
         self.visit_func(expr)


=====================================
mypyc/test-data/run-functions.test
=====================================
@@ -1286,3 +1286,25 @@ def bar() -> None:
 bar()
 [out]
 {'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': <built-in function bar>}
+
+[case testCallNestedFunctionWithNamed]
+def f() -> None:
+    def a() -> None:
+        pass
+    def b() -> None:
+        a()
+    b()
+[file driver.py]
+from native import f
+f()
+
+[case testCallNestedFunctionWithLambda]
+def f(x: int) -> int:
+    def inc(x: int) -> int:
+        return x + 1
+    return (lambda x: inc(x))(1)
+[file driver.py]
+from native import f
+print(f(1))
+[out]
+2


=====================================
test-data/unit/check-dataclasses.test
=====================================
@@ -2531,3 +2531,16 @@ class Foo:
 
     c: int  # E: Name "c" already defined on line 5
 [builtins fixtures/dataclasses.pyi]
+
+[case testDataclassInheritanceWorksWithExplicitOverrides]
+# flags: --enable-error-code explicit-override
+from dataclasses  import dataclass
+
+ at dataclass
+class Base:
+    x: int
+
+ at dataclass
+class Child(Base):
+    y: int
+[builtins fixtures/dataclasses.pyi]


=====================================
test-data/unit/check-expressions.test
=====================================
@@ -2378,6 +2378,38 @@ assert a == b
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-full.pyi]
 
+[case testStrictEqualityWithRecursiveMapTypes]
+# flags: --strict-equality
+from typing import Dict
+
+R = Dict[str, R]
+
+a: R
+b: R
+assert a == b
+
+R2 = Dict[int, R2]
+c: R2
+assert a == c  # E: Non-overlapping equality check (left operand type: "Dict[str, R]", right operand type: "Dict[int, R2]")
+[builtins fixtures/dict.pyi]
+[typing fixtures/typing-full.pyi]
+
+[case testStrictEqualityWithRecursiveListTypes]
+# flags: --strict-equality
+from typing import List, Union
+
+R = List[Union[str, R]]
+
+a: R
+b: R
+assert a == b
+
+R2 = List[Union[int, R2]]
+c: R2
+assert a == c
+[builtins fixtures/list.pyi]
+[typing fixtures/typing-full.pyi]
+
 [case testUnimportedHintAny]
 def f(x: Any) -> None:  # E: Name "Any" is not defined \
                         # N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any")


=====================================
test-data/unit/check-final.test
=====================================
@@ -1117,3 +1117,16 @@ from typing import Final
 class MyClass:
     a: None
     a: Final[int] = 1  # E: Cannot redefine an existing name as final  # E: Name "a" already defined on line 5
+
+[case testFinalOverrideAllowedForPrivate]
+from typing import Final, final
+
+class Parent:
+    __foo: Final[int] = 0
+    @final
+    def __bar(self) -> None: ...
+
+class Child(Parent):
+    __foo: Final[int] = 1
+    @final
+    def __bar(self) -> None: ...


=====================================
test-data/unit/check-functions.test
=====================================
@@ -3159,6 +3159,16 @@ class D(A, B):
     def f(self, z: int) -> str: pass  # E: Method "f" is not using @override but is overriding a method in class "__main__.A"
 [typing fixtures/typing-override.pyi]
 
+[case testExplicitOverrideAllowedForPrivate]
+# flags: --enable-error-code explicit-override --python-version 3.12
+
+class B:
+    def __f(self, y: int) -> str: pass
+
+class C(B):
+    def __f(self, y: int) -> str: pass  # OK
+[typing fixtures/typing-override.pyi]
+
 [case testCallableProperty]
 from typing import Callable
 


=====================================
test-data/unit/check-inference.test
=====================================
@@ -3767,3 +3767,49 @@ def f(values: List[T]) -> T: ...
 x = foo(f([C()]))
 reveal_type(x)  # N: Revealed type is "__main__.C"
 [builtins fixtures/list.pyi]
+
+[case testInferenceAgainstGenericCallableUnion]
+from typing import Callable, TypeVar, List, Union
+
+T = TypeVar("T")
+S = TypeVar("S")
+
+def dec(f: Callable[[S], T]) -> Callable[[S], List[T]]: ...
+ at dec
+def func(arg: T) -> Union[T, str]:
+    ...
+reveal_type(func)  # N: Revealed type is "def [S] (S`1) -> builtins.list[Union[S`1, builtins.str]]"
+reveal_type(func(42))  # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]"
+
+def dec2(f: Callable[[S], List[T]]) -> Callable[[S], T]: ...
+ at dec2
+def func2(arg: T) -> List[Union[T, str]]:
+    ...
+reveal_type(func2)  # N: Revealed type is "def [S] (S`4) -> Union[S`4, builtins.str]"
+reveal_type(func2(42))  # N: Revealed type is "Union[builtins.int, builtins.str]"
+[builtins fixtures/list.pyi]
+
+[case testInferenceAgainstGenericCallbackProtoMultiple]
+from typing import Callable, Protocol, TypeVar
+from typing_extensions import Concatenate, ParamSpec
+
+V_co = TypeVar("V_co", covariant=True)
+class Metric(Protocol[V_co]):
+    def __call__(self) -> V_co: ...
+
+T = TypeVar("T")
+P = ParamSpec("P")
+def simple_metric(func: Callable[Concatenate[int, P], T]) -> Callable[P, T]: ...
+
+ at simple_metric
+def Negate(count: int, /, metric: Metric[float]) -> float: ...
+ at simple_metric
+def Combine(count: int, m1: Metric[T], m2: Metric[T], /, *more: Metric[T]) -> T: ...
+
+reveal_type(Negate)  # N: Revealed type is "def (metric: __main__.Metric[builtins.float]) -> builtins.float"
+reveal_type(Combine)  # N: Revealed type is "def [T] (def () -> T`4, def () -> T`4, *more: def () -> T`4) -> T`4"
+
+def m1() -> float: ...
+def m2() -> float: ...
+reveal_type(Combine(m1, m2))  # N: Revealed type is "builtins.float"
+[builtins fixtures/list.pyi]


=====================================
test-data/unit/check-narrowing.test
=====================================
@@ -1910,3 +1910,16 @@ if len(x) == a:
 else:
     reveal_type(x)  # N: Revealed type is "Union[Tuple[builtins.int, builtins.int], Tuple[builtins.int, builtins.int, builtins.int]]"
 [builtins fixtures/len.pyi]
+
+[case testNarrowingLenUnionWithUnreachable]
+from typing import Union, Sequence
+
+def f(x: Union[int, Sequence[int]]) -> None:
+    if (
+        isinstance(x, tuple)
+        and len(x) == 2
+        and isinstance(x[0], int)
+        and isinstance(x[1], int)
+    ):
+        reveal_type(x)  # N: Revealed type is "Tuple[builtins.int, builtins.int]"
+[builtins fixtures/len.pyi]


=====================================
test-data/unit/check-parameter-specification.test
=====================================
@@ -1687,9 +1687,18 @@ P = ParamSpec("P")
 T = TypeVar("T")
 
 def apply(fn: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> None: ...
+
 def test(x: int) -> int: ...
 apply(apply, test, x=42)  # OK
 apply(apply, test, 42)  # Also OK (but requires some special casing)
+apply(apply, test, "bad")  # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int], int], str], None]"
+
+def test2(x: int, y: str) -> None: ...
+apply(apply, test2, 42, "yes")
+apply(apply, test2, "no", 42)  # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], str, int], None]"
+apply(apply, test2, x=42, y="yes")
+apply(apply, test2, y="yes", x=42)
+apply(apply, test2, y=42, x="no")  # E: Argument 1 to "apply" has incompatible type "Callable[[Callable[P, T], **P], None]"; expected "Callable[[Callable[[int, str], None], int, str], None]"
 [builtins fixtures/paramspec.pyi]
 
 [case testParamSpecApplyPosVsNamedOptional]
@@ -2086,3 +2095,71 @@ reveal_type(d(b, f1))  # E: Cannot infer type argument 1 of "d" \
                        # N: Revealed type is "def (*Any, **Any)"
 reveal_type(d(b, f2))  # N: Revealed type is "def (builtins.int)"
 [builtins fixtures/paramspec.pyi]
+
+[case testParamSpecGenericWithNamedArg1]
+from typing import Callable, TypeVar
+from typing_extensions import ParamSpec
+
+R = TypeVar("R")
+P = ParamSpec("P")
+
+def run(func: Callable[[], R], *args: object, backend: str = "asyncio") -> R: ...
+class Result: ...
+def run_portal() -> Result: ...
+def submit(func: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R: ...
+
+reveal_type(submit(  # N: Revealed type is "__main__.Result"
+    run,
+    run_portal,
+    backend="asyncio",
+))
+submit(
+    run,  # E: Argument 1 to "submit" has incompatible type "Callable[[Callable[[], R], VarArg(object), DefaultNamedArg(str, 'backend')], R]"; expected "Callable[[Callable[[], Result], int], Result]"
+    run_portal,
+    backend=int(),
+)
+[builtins fixtures/paramspec.pyi]
+
+[case testParamSpecGenericWithNamedArg2]
+from typing import Callable, TypeVar, Type
+from typing_extensions import ParamSpec
+
+P= ParamSpec("P")
+T = TypeVar("T")
+
+def smoke_testable(*args: P.args, **kwargs: P.kwargs) -> Callable[[Callable[P, T]], Type[T]]:
+    ...
+
+ at smoke_testable(name="bob", size=512, flt=0.5)
+class SomeClass:
+    def __init__(self, size: int, name: str, flt: float) -> None:
+        pass
+
+# Error message is confusing, but this is a known issue, see #4530.
+ at smoke_testable(name=42, size="bad", flt=0.5)  # E: Argument 1 has incompatible type "Type[OtherClass]"; expected "Callable[[int, str, float], OtherClass]"
+class OtherClass:
+    def __init__(self, size: int, name: str, flt: float) -> None:
+        pass
+[builtins fixtures/paramspec.pyi]
+
+[case testInferenceAgainstGenericCallableUnionParamSpec]
+from typing import Callable, TypeVar, List, Union
+from typing_extensions import ParamSpec
+
+T = TypeVar("T")
+P = ParamSpec("P")
+
+def dec(f: Callable[P, T]) -> Callable[P, List[T]]: ...
+ at dec
+def func(arg: T) -> Union[T, str]:
+    ...
+reveal_type(func)  # N: Revealed type is "def [T] (arg: T`-1) -> builtins.list[Union[T`-1, builtins.str]]"
+reveal_type(func(42))  # N: Revealed type is "builtins.list[Union[builtins.int, builtins.str]]"
+
+def dec2(f: Callable[P, List[T]]) -> Callable[P, T]: ...
+ at dec2
+def func2(arg: T) -> List[Union[T, str]]:
+    ...
+reveal_type(func2)  # N: Revealed type is "def [T] (arg: T`-1) -> Union[T`-1, builtins.str]"
+reveal_type(func2(42))  # N: Revealed type is "Union[builtins.int, builtins.str]"
+[builtins fixtures/paramspec.pyi]


=====================================
test-data/unit/check-selftype.test
=====================================
@@ -2056,3 +2056,18 @@ reveal_type(C.copy(c))  # N: Revealed type is "__main__.C[builtins.int, builtins
 B.copy(42)  # E: Value of type variable "Self" of "copy" of "B" cannot be "int"
 C.copy(42)  # E: Value of type variable "Self" of "copy" of "B" cannot be "int"
 [builtins fixtures/tuple.pyi]
+
+[case testRecursiveSelfTypeCallMethodNoCrash]
+from typing import Callable, TypeVar
+
+T = TypeVar("T")
+class Partial:
+    def __call__(self: Callable[..., T]) -> T: ...
+
+class Partial2:
+    def __call__(self: Callable[..., T], x: T) -> T: ...
+
+p: Partial
+reveal_type(p())  # N: Revealed type is "Never"
+p2: Partial2
+reveal_type(p2(42))  # N: Revealed type is "builtins.int"


=====================================
test-data/unit/fine-grained-dataclass-transform.test
=====================================
@@ -86,9 +86,9 @@ class A(Dataclass):
 
 [out]
 main:7: error: Unexpected keyword argument "x" for "B"
-builtins.pyi:13: note: "B" defined here
+builtins.pyi:14: note: "B" defined here
 main:7: error: Unexpected keyword argument "y" for "B"
-builtins.pyi:13: note: "B" defined here
+builtins.pyi:14: note: "B" defined here
 ==
 
 [case frozenInheritanceViaDefault]


=====================================
test-data/unit/fixtures/dataclasses.pyi
=====================================
@@ -3,6 +3,7 @@ from typing import (
     Generic, Iterator, Iterable, Mapping, Optional, Sequence, Tuple,
     TypeVar, Union, overload,
 )
+from typing_extensions import override
 
 _T = TypeVar('_T')
 _U = TypeVar('_U')
@@ -29,8 +30,10 @@ class dict(Mapping[KT, VT]):
     def __init__(self, **kwargs: VT) -> None: pass
     @overload
     def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: pass
+    @override
     def __getitem__(self, key: KT) -> VT: pass
     def __setitem__(self, k: KT, v: VT) -> None: pass
+    @override
     def __iter__(self) -> Iterator[KT]: pass
     def __contains__(self, item: object) -> int: pass
     def update(self, a: Mapping[KT, VT]) -> None: pass
@@ -42,7 +45,9 @@ class dict(Mapping[KT, VT]):
 
 class list(Generic[_T], Sequence[_T]):
     def __contains__(self, item: object) -> int: pass
+    @override
     def __getitem__(self, key: int) -> _T: pass
+    @override
     def __iter__(self) -> Iterator[_T]: pass
 
 class function: pass


=====================================
test-data/unit/fixtures/len.pyi
=====================================
@@ -10,7 +10,7 @@ class object:
 class type:
     def __init__(self, x) -> None: pass
 
-class tuple(Generic[T]):
+class tuple(Sequence[T]):
     def __len__(self) -> int: pass
 
 class list(Sequence[T]): pass


=====================================
test-data/unit/fixtures/list.pyi
=====================================
@@ -6,6 +6,7 @@ T = TypeVar('T')
 
 class object:
     def __init__(self) -> None: pass
+    def __eq__(self, other: object) -> bool: pass
 
 class type: pass
 class ellipsis: pass



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

-- 
View it on GitLab: https://salsa.debian.org/python-team/packages/mypy/-/commit/43c0114bc2af1ffbc0e17b5a77e19128a0dd9612
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/20231123/22fdc827/attachment-0001.htm>


More information about the debian-med-commit mailing list