[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