[med-svn] [Git][python-team/packages/mypy][master] d/patches: cherry-pick two patches from upstream to fix a regression found in SQLalchemy

Michael R. Crusoe (@crusoe) gitlab at salsa.debian.org
Wed Aug 14 15:01:43 BST 2024



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


Commits:
b032f57d by Michael R. Crusoe at 2024-08-14T15:51:15+02:00
d/patches: cherry-pick two patches from upstream to fix a regression found in SQLalchemy

- - - - -


7 changed files:

- debian/changelog
- + debian/patches/0004-Revert-Fix-Literal-strings-containing-pipe-character.patch
- + debian/patches/0005-An-alternative-fix-for-a-union-like-literal-string-1.patch
- debian/patches/hint-typeshed-package
- debian/patches/intersphinx
- debian/patches/series
- debian/patches/verbose


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+mypy (1.11.1-2) UNRELEASED; urgency=medium
+
+  * d/patches: cherry-pick two patches from upstream to fix a regression
+    found in SQLalchemy
+
+ -- Michael R. Crusoe <crusoe at debian.org>  Wed, 14 Aug 2024 15:51:13 +0200
+
 mypy (1.11.1-1) unstable; urgency=medium
 
   * New upstream version


=====================================
debian/patches/0004-Revert-Fix-Literal-strings-containing-pipe-character.patch
=====================================
@@ -0,0 +1,643 @@
+From: Ivan Levkivskyi <levkivskyi at gmail.com>
+Date: Sun, 4 Aug 2024 12:50:05 +0100
+Subject: Revert "Fix Literal strings containing pipe characters" (#17638)
+
+Reverts python/mypy#17148
+---
+ mypy/fastparse.py                                 | 11 +++-
+ mypy/semanal.py                                   | 31 ++++------
+ mypy/server/astmerge.py                           |  3 +-
+ mypy/stubutil.py                                  | 16 ++---
+ mypy/type_visitor.py                              |  4 --
+ mypy/typeanal.py                                  | 21 ++++---
+ mypy/types.py                                     | 75 +++++++++++++----------
+ mypy/typetraverser.py                             |  3 +-
+ mypyc/irbuild/classdef.py                         |  9 +--
+ test-data/unit/check-final.test                   |  2 -
+ test-data/unit/check-literal.test                 |  4 --
+ test-data/unit/check-namedtuple.test              |  8 +--
+ test-data/unit/check-parameter-specification.test | 23 +------
+ test-data/unit/check-typeguard.test               | 11 ----
+ test-data/unit/check-typeis.test                  | 11 ----
+ 15 files changed, 90 insertions(+), 142 deletions(-)
+
+diff --git a/mypy/fastparse.py b/mypy/fastparse.py
+index 01f6ed4..0d93c36 100644
+--- a/mypy/fastparse.py
++++ b/mypy/fastparse.py
+@@ -329,7 +329,14 @@ def parse_type_string(
+     """
+     try:
+         _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None)
+-        return RawExpressionType(expr_string, expr_fallback_name, line, column, node=node)
++        if isinstance(node, UnboundType) and node.original_str_expr is None:
++            node.original_str_expr = expr_string
++            node.original_str_fallback = expr_fallback_name
++            return node
++        elif isinstance(node, UnionType):
++            return node
++        else:
++            return RawExpressionType(expr_string, expr_fallback_name, line, column)
+     except (SyntaxError, ValueError):
+         # Note: the parser will raise a `ValueError` instead of a SyntaxError if
+         # the string happens to contain things like \x00.
+@@ -1046,8 +1053,6 @@ class ASTConverter:
+             return
+         # Indicate that type should be wrapped in an Optional if arg is initialized to None.
+         optional = isinstance(initializer, NameExpr) and initializer.name == "None"
+-        if isinstance(type, RawExpressionType) and type.node is not None:
+-            type = type.node
+         if isinstance(type, UnboundType):
+             type.optional = optional
+ 
+diff --git a/mypy/semanal.py b/mypy/semanal.py
+index f361490..782985e 100644
+--- a/mypy/semanal.py
++++ b/mypy/semanal.py
+@@ -3437,10 +3437,10 @@ class SemanticAnalyzer(
+     def analyze_lvalues(self, s: AssignmentStmt) -> None:
+         # We cannot use s.type, because analyze_simple_literal_type() will set it.
+         explicit = s.unanalyzed_type is not None
+-        final_type = self.unwrap_final_type(s.unanalyzed_type)
+-        if final_type is not None:
++        if self.is_final_type(s.unanalyzed_type):
+             # We need to exclude bare Final.
+-            if not final_type.args:
++            assert isinstance(s.unanalyzed_type, UnboundType)
++            if not s.unanalyzed_type.args:
+                 explicit = False
+ 
+         if s.rvalue:
+@@ -3506,19 +3506,19 @@ class SemanticAnalyzer(
+ 
+         Returns True if Final[...] was present.
+         """
+-        final_type = self.unwrap_final_type(s.unanalyzed_type)
+-        if final_type is None:
++        if not s.unanalyzed_type or not self.is_final_type(s.unanalyzed_type):
+             return False
+-        if len(final_type.args) > 1:
+-            self.fail("Final[...] takes at most one type argument", final_type)
++        assert isinstance(s.unanalyzed_type, UnboundType)
++        if len(s.unanalyzed_type.args) > 1:
++            self.fail("Final[...] takes at most one type argument", s.unanalyzed_type)
+         invalid_bare_final = False
+-        if not final_type.args:
++        if not s.unanalyzed_type.args:
+             s.type = None
+             if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs:
+                 invalid_bare_final = True
+                 self.fail("Type in Final[...] can only be omitted if there is an initializer", s)
+         else:
+-            s.type = final_type.args[0]
++            s.type = s.unanalyzed_type.args[0]
+ 
+         if s.type is not None and self.is_classvar(s.type):
+             self.fail("Variable should not be annotated with both ClassVar and Final", s)
+@@ -4937,18 +4937,13 @@ class SemanticAnalyzer(
+             return False
+         return sym.node.fullname == "typing.ClassVar"
+ 
+-    def unwrap_final_type(self, typ: Type | None) -> UnboundType | None:
+-        if typ is None:
+-            return None
+-        typ = typ.resolve_string_annotation()
++    def is_final_type(self, typ: Type | None) -> bool:
+         if not isinstance(typ, UnboundType):
+-            return None
++            return False
+         sym = self.lookup_qualified(typ.name, typ)
+         if not sym or not sym.node:
+-            return None
+-        if sym.node.fullname in FINAL_TYPE_NAMES:
+-            return typ
+-        return None
++            return False
++        return sym.node.fullname in FINAL_TYPE_NAMES
+ 
+     def fail_invalid_classvar(self, context: Context) -> None:
+         self.fail(message_registry.CLASS_VAR_OUTSIDE_OF_CLASS, context)
+diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py
+index e6648fb..174c292 100644
+--- a/mypy/server/astmerge.py
++++ b/mypy/server/astmerge.py
+@@ -507,8 +507,7 @@ class TypeReplaceVisitor(SyntheticTypeVisitor[None]):
+         typ.fallback.accept(self)
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> None:
+-        if t.node is not None:
+-            t.node.accept(self)
++        pass
+ 
+     def visit_literal_type(self, typ: LiteralType) -> None:
+         typ.fallback.accept(self)
+diff --git a/mypy/stubutil.py b/mypy/stubutil.py
+index 2f2db0d..04b36e1 100644
+--- a/mypy/stubutil.py
++++ b/mypy/stubutil.py
+@@ -17,16 +17,7 @@ import mypy.options
+ from mypy.modulefinder import ModuleNotFoundReason
+ from mypy.moduleinspect import InspectError, ModuleInspect
+ from mypy.stubdoc import ArgSig, FunctionSig
+-from mypy.types import (
+-    AnyType,
+-    NoneType,
+-    RawExpressionType,
+-    Type,
+-    TypeList,
+-    TypeStrVisitor,
+-    UnboundType,
+-    UnionType,
+-)
++from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType
+ 
+ # Modules that may fail when imported, or that may have side effects (fully qualified).
+ NOT_IMPORTABLE_MODULES = ()
+@@ -302,11 +293,12 @@ class AnnotationPrinter(TypeStrVisitor):
+         The main difference from list_str is the preservation of quotes for string
+         arguments
+         """
++        types = ["builtins.bytes", "builtins.str"]
+         res = []
+         for arg in args:
+             arg_str = arg.accept(self)
+-            if isinstance(arg, RawExpressionType):
+-                res.append(repr(arg.literal_value))
++            if isinstance(arg, UnboundType) and arg.original_str_fallback in types:
++                res.append(f"'{arg_str}'")
+             else:
+                 res.append(arg_str)
+         return ", ".join(res)
+diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py
+index e685c49..59e13d1 100644
+--- a/mypy/type_visitor.py
++++ b/mypy/type_visitor.py
+@@ -382,8 +382,6 @@ class TypeQuery(SyntheticTypeVisitor[T]):
+         return self.query_types(t.items.values())
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> T:
+-        if t.node is not None:
+-            return t.node.accept(self)
+         return self.strategy([])
+ 
+     def visit_literal_type(self, t: LiteralType) -> T:
+@@ -524,8 +522,6 @@ class BoolTypeQuery(SyntheticTypeVisitor[bool]):
+         return self.query_types(list(t.items.values()))
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> bool:
+-        if t.node is not None:
+-            return t.node.accept(self)
+         return self.default
+ 
+     def visit_literal_type(self, t: LiteralType) -> bool:
+diff --git a/mypy/typeanal.py b/mypy/typeanal.py
+index 6651af7..5613e01 100644
+--- a/mypy/typeanal.py
++++ b/mypy/typeanal.py
+@@ -1107,7 +1107,6 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+         return ret
+ 
+     def anal_type_guard(self, t: Type) -> Type | None:
+-        t = t.resolve_string_annotation()
+         if isinstance(t, UnboundType):
+             sym = self.lookup_qualified(t.name, t)
+             if sym is not None and sym.node is not None:
+@@ -1126,7 +1125,6 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+         return None
+ 
+     def anal_type_is(self, t: Type) -> Type | None:
+-        t = t.resolve_string_annotation()
+         if isinstance(t, UnboundType):
+             sym = self.lookup_qualified(t.name, t)
+             if sym is not None and sym.node is not None:
+@@ -1144,7 +1142,6 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+ 
+     def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type:
+         """Analyze signature argument type for *args and **kwargs argument."""
+-        t = t.resolve_string_annotation()
+         if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args:
+             components = t.name.split(".")
+             tvar_name = ".".join(components[:-1])
+@@ -1235,8 +1232,6 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+         # make signatures like "foo(x: 20) -> None" legal, we can change
+         # this method so it generates and returns an actual LiteralType
+         # instead.
+-        if t.node is not None:
+-            return t.node.accept(self)
+ 
+         if self.report_invalid_types:
+             if t.base_type_name in ("builtins.int", "builtins.bool"):
+@@ -1499,7 +1494,6 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+         invalid_unpacks: list[Type] = []
+         second_unpack_last = False
+         for i, arg in enumerate(arglist.items):
+-            arg = arg.resolve_string_annotation()
+             if isinstance(arg, CallableArgument):
+                 args.append(arg.typ)
+                 names.append(arg.name)
+@@ -1580,6 +1574,18 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+         return UnionType.make_union(output, line=t.line)
+ 
+     def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None:
++        # This UnboundType was originally defined as a string.
++        if isinstance(arg, UnboundType) and arg.original_str_expr is not None:
++            assert arg.original_str_fallback is not None
++            return [
++                LiteralType(
++                    value=arg.original_str_expr,
++                    fallback=self.named_type(arg.original_str_fallback),
++                    line=arg.line,
++                    column=arg.column,
++                )
++            ]
++
+         # If arg is an UnboundType that was *not* originally defined as
+         # a string, try expanding it in case it's a type alias or something.
+         if isinstance(arg, UnboundType):
+@@ -2564,8 +2570,7 @@ class FindTypeVarVisitor(SyntheticTypeVisitor[None]):
+         self.process_types(list(t.items.values()))
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> None:
+-        if t.node is not None:
+-            t.node.accept(self)
++        pass
+ 
+     def visit_literal_type(self, t: LiteralType) -> None:
+         pass
+diff --git a/mypy/types.py b/mypy/types.py
+index 3dce98b..7103e53 100644
+--- a/mypy/types.py
++++ b/mypy/types.py
+@@ -271,9 +271,6 @@ class Type(mypy.nodes.Context):
+     def can_be_false_default(self) -> bool:
+         return True
+ 
+-    def resolve_string_annotation(self) -> Type:
+-        return self
+-
+     def accept(self, visitor: TypeVisitor[T]) -> T:
+         raise RuntimeError("Not implemented", type(self))
+ 
+@@ -906,7 +903,14 @@ class TypeVarTupleType(TypeVarLikeType):
+ class UnboundType(ProperType):
+     """Instance type that has not been bound during semantic analysis."""
+ 
+-    __slots__ = ("name", "args", "optional", "empty_tuple_index")
++    __slots__ = (
++        "name",
++        "args",
++        "optional",
++        "empty_tuple_index",
++        "original_str_expr",
++        "original_str_fallback",
++    )
+ 
+     def __init__(
+         self,
+@@ -916,6 +920,8 @@ class UnboundType(ProperType):
+         column: int = -1,
+         optional: bool = False,
+         empty_tuple_index: bool = False,
++        original_str_expr: str | None = None,
++        original_str_fallback: str | None = None,
+     ) -> None:
+         super().__init__(line, column)
+         if not args:
+@@ -927,6 +933,21 @@ class UnboundType(ProperType):
+         self.optional = optional
+         # Special case for X[()]
+         self.empty_tuple_index = empty_tuple_index
++        # If this UnboundType was originally defined as a str or bytes, keep track of
++        # the original contents of that string-like thing. This way, if this UnboundExpr
++        # ever shows up inside of a LiteralType, we can determine whether that
++        # Literal[...] is valid or not. E.g. Literal[foo] is most likely invalid
++        # (unless 'foo' is an alias for another literal or something) and
++        # Literal["foo"] most likely is.
++        #
++        # We keep track of the entire string instead of just using a boolean flag
++        # so we can distinguish between things like Literal["foo"] vs
++        # Literal["    foo   "].
++        #
++        # We also keep track of what the original base fallback type was supposed to be
++        # so we don't have to try and recompute it later
++        self.original_str_expr = original_str_expr
++        self.original_str_fallback = original_str_fallback
+ 
+     def copy_modified(self, args: Bogus[Sequence[Type] | None] = _dummy) -> UnboundType:
+         if args is _dummy:
+@@ -938,19 +959,25 @@ class UnboundType(ProperType):
+             column=self.column,
+             optional=self.optional,
+             empty_tuple_index=self.empty_tuple_index,
++            original_str_expr=self.original_str_expr,
++            original_str_fallback=self.original_str_fallback,
+         )
+ 
+     def accept(self, visitor: TypeVisitor[T]) -> T:
+         return visitor.visit_unbound_type(self)
+ 
+     def __hash__(self) -> int:
+-        return hash((self.name, self.optional, tuple(self.args)))
++        return hash((self.name, self.optional, tuple(self.args), self.original_str_expr))
+ 
+     def __eq__(self, other: object) -> bool:
+         if not isinstance(other, UnboundType):
+             return NotImplemented
+         return (
+-            self.name == other.name and self.optional == other.optional and self.args == other.args
++            self.name == other.name
++            and self.optional == other.optional
++            and self.args == other.args
++            and self.original_str_expr == other.original_str_expr
++            and self.original_str_fallback == other.original_str_fallback
+         )
+ 
+     def serialize(self) -> JsonDict:
+@@ -958,12 +985,19 @@ class UnboundType(ProperType):
+             ".class": "UnboundType",
+             "name": self.name,
+             "args": [a.serialize() for a in self.args],
++            "expr": self.original_str_expr,
++            "expr_fallback": self.original_str_fallback,
+         }
+ 
+     @classmethod
+     def deserialize(cls, data: JsonDict) -> UnboundType:
+         assert data[".class"] == "UnboundType"
+-        return UnboundType(data["name"], [deserialize_type(a) for a in data["args"]])
++        return UnboundType(
++            data["name"],
++            [deserialize_type(a) for a in data["args"]],
++            original_str_expr=data["expr"],
++            original_str_fallback=data["expr_fallback"],
++        )
+ 
+ 
+ class CallableArgument(ProperType):
+@@ -2644,7 +2678,7 @@ class RawExpressionType(ProperType):
+ 
+     This synthetic type is only used at the beginning stages of semantic analysis
+     and should be completely removing during the process for mapping UnboundTypes to
+-    actual types: we turn it into its "node" argument, a LiteralType, or an AnyType.
++    actual types: we either turn it into a LiteralType or an AnyType.
+ 
+     For example, suppose `Foo[1]` is initially represented as the following:
+ 
+@@ -2682,7 +2716,7 @@ class RawExpressionType(ProperType):
+         )
+     """
+ 
+-    __slots__ = ("literal_value", "base_type_name", "note", "node")
++    __slots__ = ("literal_value", "base_type_name", "note")
+ 
+     def __init__(
+         self,
+@@ -2691,13 +2725,11 @@ class RawExpressionType(ProperType):
+         line: int = -1,
+         column: int = -1,
+         note: str | None = None,
+-        node: Type | None = None,
+     ) -> None:
+         super().__init__(line, column)
+         self.literal_value = literal_value
+         self.base_type_name = base_type_name
+         self.note = note
+-        self.node = node
+ 
+     def simple_name(self) -> str:
+         return self.base_type_name.replace("builtins.", "")
+@@ -2709,21 +2741,6 @@ class RawExpressionType(ProperType):
+         ret: T = visitor.visit_raw_expression_type(self)
+         return ret
+ 
+-    def copy_modified(self, node: Type | None) -> RawExpressionType:
+-        return RawExpressionType(
+-            literal_value=self.literal_value,
+-            base_type_name=self.base_type_name,
+-            line=self.line,
+-            column=self.column,
+-            note=self.note,
+-            node=node,
+-        )
+-
+-    def resolve_string_annotation(self) -> Type:
+-        if self.node is not None:
+-            return self.node.resolve_string_annotation()
+-        return self
+-
+     def serialize(self) -> JsonDict:
+         assert False, "Synthetic types don't serialize"
+ 
+@@ -2735,7 +2752,6 @@ class RawExpressionType(ProperType):
+             return (
+                 self.base_type_name == other.base_type_name
+                 and self.literal_value == other.literal_value
+-                and self.node == other.node
+             )
+         else:
+             return NotImplemented
+@@ -3411,8 +3427,6 @@ class TypeStrVisitor(SyntheticTypeVisitor[str]):
+         return f"TypedDict({prefix}{s})"
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> str:
+-        if t.node is not None:
+-            return t.node.accept(self)
+         return repr(t.literal_value)
+ 
+     def visit_literal_type(self, t: LiteralType) -> str:
+@@ -3476,9 +3490,6 @@ class TrivialSyntheticTypeTranslator(TypeTranslator, SyntheticTypeVisitor[Type])
+         return t
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> Type:
+-        if t.node is not None:
+-            node = t.node.accept(self)
+-            return t.copy_modified(node=node)
+         return t
+ 
+     def visit_type_list(self, t: TypeList) -> Type:
+diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py
+index 4d740a8..a28bbf4 100644
+--- a/mypy/typetraverser.py
++++ b/mypy/typetraverser.py
+@@ -130,8 +130,7 @@ class TypeTraverserVisitor(SyntheticTypeVisitor[None]):
+         pass
+ 
+     def visit_raw_expression_type(self, t: RawExpressionType) -> None:
+-        if t.node is not None:
+-            t.node.accept(self)
++        pass
+ 
+     def visit_type_alias_type(self, t: TypeAliasType) -> None:
+         # TODO: sometimes we want to traverse target as well
+diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py
+index 2152da0..7e0a842 100644
+--- a/mypyc/irbuild/classdef.py
++++ b/mypyc/irbuild/classdef.py
+@@ -26,7 +26,7 @@ from mypy.nodes import (
+     TypeParam,
+     is_class_var,
+ )
+-from mypy.types import ENUM_REMOVED_PROPS, Instance, RawExpressionType, get_proper_type
++from mypy.types import ENUM_REMOVED_PROPS, Instance, UnboundType, get_proper_type
+ from mypyc.common import PROPSET_PREFIX
+ from mypyc.ir.class_ir import ClassIR, NonExtClassInfo
+ from mypyc.ir.func_ir import FuncDecl, FuncSignature
+@@ -640,15 +640,16 @@ def add_non_ext_class_attr_ann(
+     if typ is None:
+         # FIXME: if get_type_info is not provided, don't fall back to stmt.type?
+         ann_type = get_proper_type(stmt.type)
+-        if isinstance(stmt.unanalyzed_type, RawExpressionType) and isinstance(
+-            stmt.unanalyzed_type.literal_value, str
++        if (
++            isinstance(stmt.unanalyzed_type, UnboundType)
++            and stmt.unanalyzed_type.original_str_expr is not None
+         ):
+             # Annotation is a forward reference, so don't attempt to load the actual
+             # type and load the string instead.
+             #
+             # TODO: is it possible to determine whether a non-string annotation is
+             # actually a forward reference due to the __annotations__ future?
+-            typ = builder.load_str(stmt.unanalyzed_type.literal_value)
++            typ = builder.load_str(stmt.unanalyzed_type.original_str_expr)
+         elif isinstance(ann_type, Instance):
+             typ = load_type(builder, ann_type.type, stmt.line)
+         else:
+diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test
+index dadf76a..7631831 100644
+--- a/test-data/unit/check-final.test
++++ b/test-data/unit/check-final.test
+@@ -6,13 +6,11 @@
+ [case testFinalDefiningModuleVar]
+ from typing import Final
+ 
+-w: 'Final' = int()
+ x: Final = int()
+ y: Final[float] = int()
+ z: Final[int] = int()
+ bad: Final[str] = int()  # E: Incompatible types in assignment (expression has type "int", variable has type "str")
+ 
+-reveal_type(w)  # N: Revealed type is "builtins.int"
+ reveal_type(x)  # N: Revealed type is "builtins.int"
+ reveal_type(y)  # N: Revealed type is "builtins.float"
+ reveal_type(z)  # N: Revealed type is "builtins.int"
+diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
+index 8f8aaf6..2f5fa1b 100644
+--- a/test-data/unit/check-literal.test
++++ b/test-data/unit/check-literal.test
+@@ -12,12 +12,8 @@ reveal_type(g1)  # N: Revealed type is "def (x: Literal['A['])"
+ 
+ def f2(x: 'A B') -> None: pass  # E: Invalid type comment or annotation
+ def g2(x: Literal['A B']) -> None: pass
+-def h2(x: 'A|int') -> None: pass  # E: Name "A" is not defined
+-def i2(x: Literal['A|B']) -> None: pass
+ reveal_type(f2)  # N: Revealed type is "def (x: Any)"
+ reveal_type(g2)  # N: Revealed type is "def (x: Literal['A B'])"
+-reveal_type(h2)  # N: Revealed type is "def (x: Union[Any, builtins.int])"
+-reveal_type(i2)  # N: Revealed type is "def (x: Literal['A|B'])"
+ [builtins fixtures/tuple.pyi]
+ [out]
+ 
+diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
+index e9d1567..ade2dde 100644
+--- a/test-data/unit/check-namedtuple.test
++++ b/test-data/unit/check-namedtuple.test
+@@ -824,20 +824,14 @@ class Fraction(Real):
+ [builtins fixtures/tuple.pyi]
+ 
+ [case testForwardReferenceInNamedTuple]
+-from typing import List, NamedTuple
++from typing import NamedTuple
+ 
+ class A(NamedTuple):
+     b: 'B'
+     x: int
+-    y: List['B']
+ 
+ class B:
+     pass
+-
+-def f(a: A):
+-    reveal_type(a.b)  # N: Revealed type is "__main__.B"
+-    reveal_type(a.x)  # N: Revealed type is "builtins.int"
+-    reveal_type(a.y)  # N: Revealed type is "builtins.list[__main__.B]"
+ [builtins fixtures/tuple.pyi]
+ 
+ [case testTypeNamedTupleClassmethod]
+diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test
+index e6d8cec..c2afb61 100644
+--- a/test-data/unit/check-parameter-specification.test
++++ b/test-data/unit/check-parameter-specification.test
+@@ -1193,28 +1193,7 @@ def func(callback: Callable[P, str]) -> Callable[P, str]:
+     return inner
+ [builtins fixtures/paramspec.pyi]
+ 
+-[case testParamSpecArgsAndKwargsStringified]
+-from typing import Callable
+-from typing_extensions import ParamSpec
+-
+-P1 = ParamSpec("P1")
+-
+-def func(callback: Callable[P1, str]) -> Callable[P1, str]:
+-    def inner(*args: "P1.args", **kwargs: "P1.kwargs") -> str:
+-        return "foo"
+-    return inner
+-
+- at func
+-def outer(a: int) -> str:
+-    return ""
+-
+-outer(1)  # OK
+-outer("x")  # E: Argument 1 to "outer" has incompatible type "str"; expected "int"
+-outer(a=1)  # OK
+-outer(b=1)  # E: Unexpected keyword argument "b" for "outer"
+-[builtins fixtures/paramspec.pyi]
+-
+-[case testParamSpecArgsAndKwargsMismatch]
++[case testParamSpecArgsAndKwargsMissmatch]
+ from typing import Callable
+ from typing_extensions import ParamSpec
+ 
+diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test
+index e1b7a86..27b8855 100644
+--- a/test-data/unit/check-typeguard.test
++++ b/test-data/unit/check-typeguard.test
+@@ -9,17 +9,6 @@ def main(a: object) -> None:
+         reveal_type(a)  # N: Revealed type is "builtins.object"
+ [builtins fixtures/tuple.pyi]
+ 
+-[case testTypeGuardStringified]
+-from typing_extensions import TypeGuard
+-class Point: pass
+-def is_point(a: object) -> "TypeGuard[Point]": pass
+-def main(a: object) -> None:
+-    if is_point(a):
+-        reveal_type(a)  # N: Revealed type is "__main__.Point"
+-    else:
+-        reveal_type(a)  # N: Revealed type is "builtins.object"
+-[builtins fixtures/tuple.pyi]
+-
+ [case testTypeGuardTypeArgsNone]
+ from typing_extensions import TypeGuard
+ def foo(a: object) -> TypeGuard:  # E: TypeGuard must have exactly one type argument
+diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test
+index 83467d5..6b96845 100644
+--- a/test-data/unit/check-typeis.test
++++ b/test-data/unit/check-typeis.test
+@@ -9,17 +9,6 @@ def main(a: object) -> None:
+         reveal_type(a)  # N: Revealed type is "builtins.object"
+ [builtins fixtures/tuple.pyi]
+ 
+-[case testTypeIsStringified]
+-from typing_extensions import TypeIs
+-class Point: pass
+-def is_point(a: object) -> "TypeIs[Point]": pass
+-def main(a: object) -> None:
+-    if is_point(a):
+-        reveal_type(a)  # N: Revealed type is "__main__.Point"
+-    else:
+-        reveal_type(a)  # N: Revealed type is "builtins.object"
+-[builtins fixtures/tuple.pyi]
+-
+ [case testTypeIsElif]
+ from typing_extensions import TypeIs
+ from typing import Union


=====================================
debian/patches/0005-An-alternative-fix-for-a-union-like-literal-string-1.patch
=====================================
@@ -0,0 +1,119 @@
+From: Ivan Levkivskyi <levkivskyi at gmail.com>
+Date: Sun, 11 Aug 2024 22:26:32 +0100
+Subject: An alternative fix for a union-like literal string (#17639)
+
+It is unfortunate to add two extra slots to a common type (and I guess
+this is why it was rejected in the original PR), but all other
+alternatives I tried are hacky and/or dangerous. So, this is a price to
+pay for introducing a new type syntax.
+
+---------
+
+Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
+---
+ mypy/fastparse.py                 |  4 +---
+ mypy/typeanal.py                  |  6 +++++-
+ mypy/types.py                     | 16 +++++++++++++---
+ test-data/unit/check-literal.test |  4 ++++
+ 4 files changed, 23 insertions(+), 7 deletions(-)
+
+diff --git a/mypy/fastparse.py b/mypy/fastparse.py
+index 0d93c36..c78a24e 100644
+--- a/mypy/fastparse.py
++++ b/mypy/fastparse.py
+@@ -329,12 +329,10 @@ def parse_type_string(
+     """
+     try:
+         _, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None)
+-        if isinstance(node, UnboundType) and node.original_str_expr is None:
++        if isinstance(node, (UnboundType, UnionType)) and node.original_str_expr is None:
+             node.original_str_expr = expr_string
+             node.original_str_fallback = expr_fallback_name
+             return node
+-        elif isinstance(node, UnionType):
+-            return node
+         else:
+             return RawExpressionType(expr_string, expr_fallback_name, line, column)
+     except (SyntaxError, ValueError):
+diff --git a/mypy/typeanal.py b/mypy/typeanal.py
+index 5613e01..ee6cd87 100644
+--- a/mypy/typeanal.py
++++ b/mypy/typeanal.py
+@@ -1575,7 +1575,11 @@ class TypeAnalyser(SyntheticTypeVisitor[Type], TypeAnalyzerPluginInterface):
+ 
+     def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None:
+         # This UnboundType was originally defined as a string.
+-        if isinstance(arg, UnboundType) and arg.original_str_expr is not None:
++        if (
++            isinstance(arg, ProperType)
++            and isinstance(arg, (UnboundType, UnionType))
++            and arg.original_str_expr is not None
++        ):
+             assert arg.original_str_fallback is not None
+             return [
+                 LiteralType(
+diff --git a/mypy/types.py b/mypy/types.py
+index 7103e53..2881717 100644
+--- a/mypy/types.py
++++ b/mypy/types.py
+@@ -914,7 +914,7 @@ class UnboundType(ProperType):
+ 
+     def __init__(
+         self,
+-        name: str | None,
++        name: str,
+         args: Sequence[Type] | None = None,
+         line: int = -1,
+         column: int = -1,
+@@ -926,7 +926,6 @@ class UnboundType(ProperType):
+         super().__init__(line, column)
+         if not args:
+             args = []
+-        assert name is not None
+         self.name = name
+         self.args = tuple(args)
+         # Should this type be wrapped in an Optional?
+@@ -2849,7 +2848,13 @@ class LiteralType(ProperType):
+ class UnionType(ProperType):
+     """The union type Union[T1, ..., Tn] (at least one type argument)."""
+ 
+-    __slots__ = ("items", "is_evaluated", "uses_pep604_syntax")
++    __slots__ = (
++        "items",
++        "is_evaluated",
++        "uses_pep604_syntax",
++        "original_str_expr",
++        "original_str_fallback",
++    )
+ 
+     def __init__(
+         self,
+@@ -2868,6 +2873,11 @@ class UnionType(ProperType):
+         self.is_evaluated = is_evaluated
+         # uses_pep604_syntax is True if Union uses OR syntax (X | Y)
+         self.uses_pep604_syntax = uses_pep604_syntax
++        # The meaning of these two is the same as for UnboundType. A UnionType can be
++        # return by type parser from a string "A|B", and we need to be able to fall back
++        # to plain string, when such a string appears inside a Literal[...].
++        self.original_str_expr: str | None = None
++        self.original_str_fallback: str | None = None
+ 
+     def can_be_true_default(self) -> bool:
+         return any(item.can_be_true for item in self.items)
+diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
+index 2f5fa1b..8f8aaf6 100644
+--- a/test-data/unit/check-literal.test
++++ b/test-data/unit/check-literal.test
+@@ -12,8 +12,12 @@ reveal_type(g1)  # N: Revealed type is "def (x: Literal['A['])"
+ 
+ def f2(x: 'A B') -> None: pass  # E: Invalid type comment or annotation
+ def g2(x: Literal['A B']) -> None: pass
++def h2(x: 'A|int') -> None: pass  # E: Name "A" is not defined
++def i2(x: Literal['A|B']) -> None: pass
+ reveal_type(f2)  # N: Revealed type is "def (x: Any)"
+ reveal_type(g2)  # N: Revealed type is "def (x: Literal['A B'])"
++reveal_type(h2)  # N: Revealed type is "def (x: Union[Any, builtins.int])"
++reveal_type(i2)  # N: Revealed type is "def (x: Literal['A|B'])"
+ [builtins fixtures/tuple.pyi]
+ [out]
+ 


=====================================
debian/patches/hint-typeshed-package
=====================================
@@ -1,11 +1,20 @@
-Author: Michael R. Crusoe <crusoe at debian.org>
+From: "Michael R. Crusoe" <crusoe at debian.org>
+Date: Wed, 14 Aug 2024 15:44:10 +0200
+Subject: point to python3-typeshed package for missing types
+
 Co-authored-by: Markus Demleitner <msdemlei at fsfe.org>
 Forwarded: not-needed
-Description: point to python3-typeshed package for missing types
+---
+ mypy/modulefinder.py                     | 4 +++-
+ test-data/unit/check-modules.test        | 4 ++--
+ test-data/unit/fine-grained-modules.test | 4 ++--
+ 3 files changed, 7 insertions(+), 5 deletions(-)
 
---- mypy.orig/mypy/modulefinder.py
-+++ mypy/mypy/modulefinder.py
-@@ -83,7 +83,9 @@
+diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py
+index 452cfef..b4c98ed 100644
+--- a/mypy/modulefinder.py
++++ b/mypy/modulefinder.py
+@@ -83,7 +83,9 @@ class ModuleNotFoundReason(Enum):
              notes = [doc_link]
          elif self is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED:
              msg = 'Library stubs not installed for "{module}"'
@@ -16,26 +25,11 @@ Description: point to python3-typeshed package for missing types
              if not daemon:
                  notes.append(
                      '(or run "mypy --install-types" to install all missing stub packages)'
---- mypy.orig/test-data/unit/fine-grained-modules.test
-+++ mypy/test-data/unit/fine-grained-modules.test
-@@ -2199,12 +2199,12 @@
- import jack
- [out]
- a.py:1: error: Library stubs not installed for "requests"
--a.py:1: note: Hint: "python3 -m pip install types-requests"
-+a.py:1: note: Hint: On Debian systems, you can install the python3-typeshed package to provide mypy with stubs for many popular libraries. In virtual Python environments, you can instead run "python3 -m pip install types-requests".
- a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
- ==
- ==
- a.py:1: error: Library stubs not installed for "jack"
--a.py:1: note: Hint: "python3 -m pip install types-JACK-Client"
-+a.py:1: note: Hint: On Debian systems, you can install the python3-typeshed package to provide mypy with stubs for many popular libraries. In virtual Python environments, you can instead run "python3 -m pip install types-JACK-Client".
- a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
- 
- [case testIgnoreErrorsFromTypeshed]
---- mypy.orig/test-data/unit/check-modules.test
-+++ mypy/test-data/unit/check-modules.test
-@@ -3130,7 +3130,7 @@
+diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test
+index 5fd4857..4b4493a 100644
+--- a/test-data/unit/check-modules.test
++++ b/test-data/unit/check-modules.test
+@@ -3130,7 +3130,7 @@ import google.non_existent  # E: Cannot find implementation or library stub for
  from google.non_existent import x
  
  import google.cloud.ndb  # E: Library stubs not installed for "google.cloud.ndb" \
@@ -44,7 +38,7 @@ Description: point to python3-typeshed package for missing types
                           # N: (or run "mypy --install-types" to install all missing stub packages) \
                           # N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
  from google.cloud import ndb
-@@ -3141,7 +3141,7 @@
+@@ -3141,7 +3141,7 @@ from bleach.abc import fgh
  [file bleach/__init__.pyi]
  [out]
  main:1: error: Library stubs not installed for "bleach.xyz"
@@ -53,3 +47,22 @@ Description: point to python3-typeshed package for missing types
  main:1: note: (or run "mypy --install-types" to install all missing stub packages)
  main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
  main:2: error: Library stubs not installed for "bleach.abc"
+diff --git a/test-data/unit/fine-grained-modules.test b/test-data/unit/fine-grained-modules.test
+index f28dbaa..6273fbf 100644
+--- a/test-data/unit/fine-grained-modules.test
++++ b/test-data/unit/fine-grained-modules.test
+@@ -2199,12 +2199,12 @@ import requests
+ import jack
+ [out]
+ a.py:1: error: Library stubs not installed for "requests"
+-a.py:1: note: Hint: "python3 -m pip install types-requests"
++a.py:1: note: Hint: On Debian systems, you can install the python3-typeshed package to provide mypy with stubs for many popular libraries. In virtual Python environments, you can instead run "python3 -m pip install types-requests".
+ a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+ ==
+ ==
+ a.py:1: error: Library stubs not installed for "jack"
+-a.py:1: note: Hint: "python3 -m pip install types-JACK-Client"
++a.py:1: note: Hint: On Debian systems, you can install the python3-typeshed package to provide mypy with stubs for many popular libraries. In virtual Python environments, you can instead run "python3 -m pip install types-JACK-Client".
+ a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
+ 
+ [case testIgnoreErrorsFromTypeshed]


=====================================
debian/patches/intersphinx
=====================================
@@ -4,12 +4,14 @@ Subject: link to local documenatin
 
 Forwarded: not-needed
 ---
- docs/source/conf.py | 19 ++++++++++++++-----
- 1 file changed, 14 insertions(+), 5 deletions(-)
+ docs/source/conf.py | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
 
---- mypy.orig/docs/source/conf.py
-+++ mypy/docs/source/conf.py
-@@ -265,11 +265,11 @@
+diff --git a/docs/source/conf.py b/docs/source/conf.py
+index fa76734..3057f4f 100644
+--- a/docs/source/conf.py
++++ b/docs/source/conf.py
+@@ -265,11 +265,11 @@ texinfo_documents = [
  rst_prolog = ".. |...| unicode:: U+2026   .. ellipsis\n"
  
  intersphinx_mapping = {


=====================================
debian/patches/series
=====================================
@@ -1,3 +1,5 @@
 hint-typeshed-package
 verbose
 intersphinx
+0004-Revert-Fix-Literal-strings-containing-pipe-character.patch
+0005-An-alternative-fix-for-a-union-like-literal-string-1.patch


=====================================
debian/patches/verbose
=====================================
@@ -7,9 +7,11 @@ Forwarded: not-needed
  setup.py | 1 +
  1 file changed, 1 insertion(+)
 
---- mypy.orig/setup.py
-+++ mypy/setup.py
-@@ -172,6 +172,7 @@
+diff --git a/setup.py b/setup.py
+index 160e2b0..553149d 100644
+--- a/setup.py
++++ b/setup.py
+@@ -172,6 +172,7 @@ if USE_MYPYC:
          # Use multi-file compilation mode on windows because without it
          # our Appveyor builds run out of memory sometimes.
          multi_file=sys.platform == "win32" or force_multifile,



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

-- 
View it on GitLab: https://salsa.debian.org/python-team/packages/mypy/-/commit/b032f57dcb58405edbefe60565c0aef3dfea3fe9
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/20240814/7c850ad2/attachment-0001.htm>


More information about the debian-med-commit mailing list