From 1cccb0b9d95ea01e54efe8932b2f12aabf7eec31 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 13 Apr 2022 15:52:18 +0100 Subject: [PATCH 01/79] Typeshed cherry-pick: Drop some literal types from argparse (add_argument) (#7614) (#12576) From python/typeshed#7614. --- mypy/typeshed/stdlib/argparse.pyi | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index ad54660cc45d..0a56aa4f4b59 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -62,6 +62,15 @@ _T = TypeVar("_T") _ActionT = TypeVar("_ActionT", bound=Action) _ArgumentParserT = TypeVar("_ArgumentParserT", bound=ArgumentParser) _N = TypeVar("_N") +# more precisely, Literal["store", "store_const", "store_true", +# "store_false", "append", "append_const", "count", "help", "version", +# "extend"], but using this would make it hard to annotate callers +# that don't use a literal argument +_ActionStr = str +# more precisely, Literal["?", "*", "+", "...", "A...", +# "==SUPPRESS=="], but using this would make it hard to annotate +# callers that don't use a literal argument +_NArgsStr = str ONE_OR_MORE: Literal["+"] OPTIONAL: Literal["?"] @@ -106,11 +115,8 @@ class _ActionsContainer: def add_argument( self, *name_or_flags: str, - action: Literal[ - "store", "store_const", "store_true", "store_false", "append", "append_const", "count", "help", "version", "extend" - ] - | type[Action] = ..., - nargs: int | Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="] | _SUPPRESS_T = ..., + action: _ActionStr | type[Action] = ..., + nargs: int | _NArgsStr | _SUPPRESS_T = ..., const: Any = ..., default: Any = ..., type: Callable[[str], _T] | FileType = ..., From f8ca2332eca0f242a3b1d1b99e795c790b044138 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Wed, 13 Apr 2022 07:56:00 -0700 Subject: [PATCH 02/79] Recognise LiteralString as str (#12559) Linking #12554 Co-authored-by: hauntsaninja <> --- mypy/nodes.py | 7 +++++++ test-data/unit/check-type-aliases.test | 18 ++++++++++++++++++ test-data/unit/fixtures/typing-medium.pyi | 1 + 3 files changed, 26 insertions(+) diff --git a/mypy/nodes.py b/mypy/nodes.py index 7fcf5d85673c..ff9276213ddc 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -123,6 +123,8 @@ def get_column(self) -> int: 'typing.DefaultDict': 'collections.defaultdict', 'typing.Deque': 'collections.deque', 'typing.OrderedDict': 'collections.OrderedDict', + # HACK: a lie in lieu of actual support for PEP 675 + 'typing.LiteralString': 'builtins.str', } # This keeps track of the oldest supported Python version where the corresponding @@ -137,12 +139,15 @@ def get_column(self) -> int: 'typing.DefaultDict': (2, 7), 'typing.Deque': (2, 7), 'typing.OrderedDict': (3, 7), + 'typing.LiteralString': (3, 11), } # This keeps track of aliases in `typing_extensions`, which we treat specially. typing_extensions_aliases: Final = { # See: https://github.com/python/mypy/issues/11528 'typing_extensions.OrderedDict': 'collections.OrderedDict', + # HACK: a lie in lieu of actual support for PEP 675 + 'typing_extensions.LiteralString': 'builtins.str', } reverse_builtin_aliases: Final = { @@ -156,6 +161,8 @@ def get_column(self) -> int: _nongen_builtins.update((name, alias) for alias, name in type_aliases.items()) # Drop OrderedDict from this for backward compatibility del _nongen_builtins['collections.OrderedDict'] +# HACK: consequence of hackily treating LiteralString as an alias for str +del _nongen_builtins['builtins.str'] def get_nongen_builtins(python_version: Tuple[int, int]) -> Dict[str, str]: diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 61d53870d724..111743e9235e 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -752,3 +752,21 @@ from typing_extensions import TypeAlias A: TypeAlias = str [builtins fixtures/bool.pyi] [out] + + +[case testLiteralStringPep675] +# flags: --python-version 3.11 +from typing import LiteralString as tpLS +from typing_extensions import LiteralString as tpxLS + +def f(a: tpLS, b: tpxLS) -> None: + reveal_type(a) # N: Revealed type is "builtins.str" + reveal_type(b) # N: Revealed type is "builtins.str" + +# This isn't the correct behaviour, but should unblock use of LiteralString in typeshed +f("asdf", "asdf") +string: str +f(string, string) + +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/fixtures/typing-medium.pyi b/test-data/unit/fixtures/typing-medium.pyi index 923ede2f0c00..568fe057c4cf 100644 --- a/test-data/unit/fixtures/typing-medium.pyi +++ b/test-data/unit/fixtures/typing-medium.pyi @@ -27,6 +27,7 @@ TypedDict = 0 NoReturn = 0 NewType = 0 TypeAlias = 0 +LiteralString = 0 T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) From 7cc138822cd348be223d298740dbd7194dfa9596 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 13 Apr 2022 16:38:16 +0100 Subject: [PATCH 03/79] Update version to 0.960+dev (#12578) --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index b59c826eab6b..c7b4e30f2420 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -5,7 +5,7 @@ # - Release versions have the form "0.NNN". # - Dev versions have the form "0.NNN+dev" (PLUS sign to conform to PEP 440). # - For 1.0 we'll switch back to 1.2.3 form. -__version__ = '0.950+dev' +__version__ = '0.960+dev' base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From 0c6b29073b2bf7439b9efbb958c403574111ac15 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer <13833860+VincentVanlaer@users.noreply.github.com> Date: Thu, 14 Apr 2022 01:11:00 +0200 Subject: [PATCH 04/79] Change install from git link in README to https (#12581) GitHub has dropped support for the unauthenticated git protocol. See https://github.blog/2021-09-01-improving-git-protocol-security-github/ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98f6d48cd982..c8f4df995f3a 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Mypy can be installed using pip: If you want to run the latest version of the code, you can install from git: - python3 -m pip install -U git+git://github.com/python/mypy.git + python3 -m pip install -U git+https://github.com/python/mypy.git Now you can type-check the [statically typed parts] of a program like this: From 44993e6a18f852a9296786267ecf26ea730b4f31 Mon Sep 17 00:00:00 2001 From: EXPLOSION Date: Thu, 14 Apr 2022 17:49:51 +0900 Subject: [PATCH 05/79] Fix propagated Any constraint (#12548) Fixes #12542. This allows parameters to constrain to Any. --- mypy/constraints.py | 4 ++++ test-data/unit/check-parameter-specification.test | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/mypy/constraints.py b/mypy/constraints.py index 0b2217b21ae0..b7ed1492e5f3 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -407,6 +407,10 @@ def visit_unpack_type(self, template: UnpackType) -> List[Constraint]: raise NotImplementedError def visit_parameters(self, template: Parameters) -> List[Constraint]: + # constraining Any against C[P] turns into infer_against_any([P], Any) + # ... which seems like the only case this can happen. Better to fail loudly. + if isinstance(self.actual, AnyType): + return self.infer_against_any(template.arg_types, self.actual) raise RuntimeError("Parameters cannot be constrained to") # Non-leaf types diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index be296b243019..2242b79d4b64 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1049,3 +1049,17 @@ P = ParamSpec("P") def x(f: Callable[Concatenate[int, Concatenate[int, P]], None]) -> None: ... # E: Nested Concatenates are invalid [builtins fixtures/tuple.pyi] + +[case testPropagatedAnyConstraintsAreOK] +from typing import Any, Callable, Generic, TypeVar +from typing_extensions import ParamSpec + +T = TypeVar("T") +P = ParamSpec("P") + +def callback(func: Callable[[Any], Any]) -> None: ... +class Job(Generic[P]): ... + +@callback +def run_job(job: Job[...]) -> T: ... +[builtins fixtures/tuple.pyi] From 875774769922c11094541eec20bd85bb1b87a70c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 14 Apr 2022 20:53:33 -0700 Subject: [PATCH 06/79] Add support for assert_type (#12584) See python/cpython#30843. The implementation mostly follows that of cast(). It relies on `mypy.sametypes.is_same_type()`. --- mypy/checkexpr.py | 10 +++++++++- mypy/errorcodes.py | 3 +++ mypy/literals.py | 6 +++++- mypy/messages.py | 5 +++++ mypy/mixedtraverser.py | 6 +++++- mypy/nodes.py | 16 ++++++++++++++++ mypy/semanal.py | 23 +++++++++++++++++++++-- mypy/server/astmerge.py | 6 +++++- mypy/server/deps.py | 7 ++++++- mypy/server/subexpr.py | 6 +++++- mypy/strconv.py | 3 +++ mypy/traverser.py | 5 ++++- mypy/treetransform.py | 5 ++++- mypy/types.py | 5 +++++ mypy/visitor.py | 7 +++++++ mypyc/irbuild/expression.py | 5 ++++- mypyc/irbuild/visitor.py | 5 ++++- mypyc/test-data/irbuild-basic.test | 11 +++++++++++ test-data/unit/check-expressions.test | 12 ++++++++++++ test-data/unit/fixtures/typing-full.pyi | 1 + test-data/unit/lib-stub/typing.pyi | 1 + test-data/unit/semanal-errors.test | 9 +++++++++ test-data/unit/semanal-types.test | 11 +++++++++++ test-data/unit/typexport-basic.test | 19 +++++++++++++++++++ 24 files changed, 175 insertions(+), 12 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 45d5818d4eeb..32fa391bb0e2 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -23,7 +23,7 @@ get_proper_types, flatten_nested_unions, LITERAL_TYPE_NAMES, ) from mypy.nodes import ( - NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr, + AssertTypeExpr, NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr, MemberExpr, IntExpr, StrExpr, BytesExpr, UnicodeExpr, FloatExpr, OpExpr, UnaryExpr, IndexExpr, CastExpr, RevealExpr, TypeApplication, ListExpr, TupleExpr, DictExpr, LambdaExpr, SuperExpr, SliceExpr, Context, Expression, @@ -3144,6 +3144,14 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: context=expr) return target_type + def visit_assert_type_expr(self, expr: AssertTypeExpr) -> Type: + source_type = self.accept(expr.expr, type_context=AnyType(TypeOfAny.special_form), + allow_none_return=True, always_allow_any=True) + target_type = expr.type + if not is_same_type(source_type, target_type): + self.msg.assert_type_fail(source_type, target_type, expr) + return source_type + def visit_reveal_expr(self, expr: RevealExpr) -> Type: """Type check a reveal_type expression.""" if expr.kind == REVEAL_TYPE: diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 31f1a2fb73ea..85d6d9dd4159 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -113,6 +113,9 @@ def __str__(self) -> str: REDUNDANT_CAST: Final = ErrorCode( "redundant-cast", "Check that cast changes type of expression", "General" ) +ASSERT_TYPE: Final = ErrorCode( + "assert-type", "Check that assert_type() call succeeds", "General" +) COMPARISON_OVERLAP: Final = ErrorCode( "comparison-overlap", "Check that types in comparisons and 'in' expressions overlap", "General" ) diff --git a/mypy/literals.py b/mypy/literals.py index 00cf5916bec2..b11c07d91a91 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -8,7 +8,8 @@ ConditionalExpr, EllipsisExpr, YieldFromExpr, YieldExpr, RevealExpr, SuperExpr, TypeApplication, LambdaExpr, ListComprehension, SetComprehension, DictionaryComprehension, GeneratorExpr, BackquoteExpr, TypeVarExpr, TypeAliasExpr, NamedTupleExpr, EnumCallExpr, - TypedDictExpr, NewTypeExpr, PromoteExpr, AwaitExpr, TempNode, AssignmentExpr, ParamSpecExpr + TypedDictExpr, NewTypeExpr, PromoteExpr, AwaitExpr, TempNode, AssignmentExpr, ParamSpecExpr, + AssertTypeExpr, ) from mypy.visitor import ExpressionVisitor @@ -175,6 +176,9 @@ def visit_slice_expr(self, e: SliceExpr) -> None: def visit_cast_expr(self, e: CastExpr) -> None: return None + def visit_assert_type_expr(self, e: AssertTypeExpr) -> None: + return None + def visit_conditional_expr(self, e: ConditionalExpr) -> None: return None diff --git a/mypy/messages.py b/mypy/messages.py index 0e9a59ea4016..23ab172f5499 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1213,6 +1213,11 @@ def redundant_cast(self, typ: Type, context: Context) -> None: self.fail('Redundant cast to {}'.format(format_type(typ)), context, code=codes.REDUNDANT_CAST) + def assert_type_fail(self, source_type: Type, target_type: Type, context: Context) -> None: + self.fail(f"Expression is of type {format_type(source_type)}, " + f"not {format_type(target_type)}", context, + code=codes.ASSERT_TYPE) + def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: self.fail("{} becomes {} due to an unfollowed import".format(prefix, format_type(typ)), ctx, code=codes.NO_ANY_UNIMPORTED) diff --git a/mypy/mixedtraverser.py b/mypy/mixedtraverser.py index 57fdb28e0e45..c14648cdf654 100644 --- a/mypy/mixedtraverser.py +++ b/mypy/mixedtraverser.py @@ -1,7 +1,7 @@ from typing import Optional from mypy.nodes import ( - Var, FuncItem, ClassDef, AssignmentStmt, ForStmt, WithStmt, + AssertTypeExpr, Var, FuncItem, ClassDef, AssignmentStmt, ForStmt, WithStmt, CastExpr, TypeApplication, TypeAliasExpr, TypeVarExpr, TypedDictExpr, NamedTupleExpr, PromoteExpr, NewTypeExpr ) @@ -79,6 +79,10 @@ def visit_cast_expr(self, o: CastExpr) -> None: super().visit_cast_expr(o) o.type.accept(self) + def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + super().visit_assert_type_expr(o) + o.type.accept(self) + def visit_type_application(self, o: TypeApplication) -> None: super().visit_type_application(o) for t in o.types: diff --git a/mypy/nodes.py b/mypy/nodes.py index ff9276213ddc..30bb2c6aa10a 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1945,6 +1945,22 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_cast_expr(self) +class AssertTypeExpr(Expression): + """Represents a typing.assert_type(expr, type) call.""" + __slots__ = ('expr', 'type') + + expr: Expression + type: "mypy.types.Type" + + def __init__(self, expr: Expression, typ: 'mypy.types.Type') -> None: + super().__init__() + self.expr = expr + self.type = typ + + def accept(self, visitor: ExpressionVisitor[T]) -> T: + return visitor.visit_assert_type_expr(self) + + class RevealExpr(Expression): """Reveal type expression reveal_type(expr) or reveal_locals() expression.""" diff --git a/mypy/semanal.py b/mypy/semanal.py index 44ece0674732..3ffc20cead77 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -56,7 +56,7 @@ from typing_extensions import Final, TypeAlias as _TypeAlias from mypy.nodes import ( - MypyFile, TypeInfo, Node, AssignmentStmt, FuncDef, OverloadedFuncDef, + AssertTypeExpr, MypyFile, TypeInfo, Node, AssignmentStmt, FuncDef, OverloadedFuncDef, ClassDef, Var, GDEF, FuncItem, Import, Expression, Lvalue, ImportFrom, ImportAll, Block, LDEF, NameExpr, MemberExpr, IndexExpr, TupleExpr, ListExpr, ExpressionStmt, ReturnStmt, @@ -99,7 +99,7 @@ TypeTranslator, TypeOfAny, TypeType, NoneType, PlaceholderType, TPDICT_NAMES, ProperType, get_proper_type, get_proper_types, TypeAliasType, TypeVarLikeType, Parameters, ParamSpecType, PROTOCOL_NAMES, TYPE_ALIAS_NAMES, FINAL_TYPE_NAMES, FINAL_DECORATOR_NAMES, REVEAL_TYPE_NAMES, - is_named_instance, + ASSERT_TYPE_NAMES, is_named_instance, ) from mypy.typeops import function_type, get_type_vars from mypy.type_visitor import TypeQuery @@ -3897,6 +3897,19 @@ def visit_call_expr(self, expr: CallExpr) -> None: expr.analyzed.line = expr.line expr.analyzed.column = expr.column expr.analyzed.accept(self) + elif refers_to_fullname(expr.callee, ASSERT_TYPE_NAMES): + if not self.check_fixed_args(expr, 2, 'assert_type'): + return + # Translate second argument to an unanalyzed type. + try: + target = self.expr_to_unanalyzed_type(expr.args[1]) + except TypeTranslationError: + self.fail('assert_type() type is not a type', expr) + return + expr.analyzed = AssertTypeExpr(expr.args[0], target) + expr.analyzed.line = expr.line + expr.analyzed.column = expr.column + expr.analyzed.accept(self) elif refers_to_fullname(expr.callee, REVEAL_TYPE_NAMES): if not self.check_fixed_args(expr, 1, 'reveal_type'): return @@ -4200,6 +4213,12 @@ def visit_cast_expr(self, expr: CastExpr) -> None: if analyzed is not None: expr.type = analyzed + def visit_assert_type_expr(self, expr: AssertTypeExpr) -> None: + expr.expr.accept(self) + analyzed = self.anal_type(expr.type) + if analyzed is not None: + expr.type = analyzed + def visit_reveal_expr(self, expr: RevealExpr) -> None: if expr.kind == REVEAL_TYPE: if expr.expr is not None: diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index deaf7a6e21b7..d8f1d5a19155 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -51,7 +51,7 @@ MypyFile, SymbolTable, Block, AssignmentStmt, NameExpr, MemberExpr, RefExpr, TypeInfo, FuncDef, ClassDef, NamedTupleExpr, SymbolNode, Var, Statement, SuperExpr, NewTypeExpr, OverloadedFuncDef, LambdaExpr, TypedDictExpr, EnumCallExpr, FuncBase, TypeAliasExpr, CallExpr, - CastExpr, TypeAlias, + CastExpr, TypeAlias, AssertTypeExpr, MDEF ) from mypy.traverser import TraverserVisitor @@ -226,6 +226,10 @@ def visit_cast_expr(self, node: CastExpr) -> None: super().visit_cast_expr(node) self.fixup_type(node.type) + def visit_assert_type_expr(self, node: AssertTypeExpr) -> None: + super().visit_assert_type_expr(node) + self.fixup_type(node.type) + def visit_super_expr(self, node: SuperExpr) -> None: super().visit_super_expr(node) if node.info is not None: diff --git a/mypy/server/deps.py b/mypy/server/deps.py index 646a02434048..da4960ba1934 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -89,7 +89,8 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a ComparisonExpr, GeneratorExpr, DictionaryComprehension, StarExpr, PrintStmt, ForStmt, WithStmt, TupleExpr, OperatorAssignmentStmt, DelStmt, YieldFromExpr, Decorator, Block, TypeInfo, FuncBase, OverloadedFuncDef, RefExpr, SuperExpr, Var, NamedTupleExpr, TypedDictExpr, - LDEF, MDEF, GDEF, TypeAliasExpr, NewTypeExpr, ImportAll, EnumCallExpr, AwaitExpr + LDEF, MDEF, GDEF, TypeAliasExpr, NewTypeExpr, ImportAll, EnumCallExpr, AwaitExpr, + AssertTypeExpr, ) from mypy.operators import ( op_methods, reverse_op_methods, ops_with_inplace_method, unary_op_methods @@ -686,6 +687,10 @@ def visit_cast_expr(self, e: CastExpr) -> None: super().visit_cast_expr(e) self.add_type_dependencies(e.type) + def visit_assert_type_expr(self, e: AssertTypeExpr) -> None: + super().visit_assert_type_expr(e) + self.add_type_dependencies(e.type) + def visit_type_application(self, e: TypeApplication) -> None: super().visit_type_application(e) for typ in e.types: diff --git a/mypy/server/subexpr.py b/mypy/server/subexpr.py index 2fb0ef4ffaf1..4078c4170fcf 100644 --- a/mypy/server/subexpr.py +++ b/mypy/server/subexpr.py @@ -7,7 +7,7 @@ SliceExpr, CastExpr, RevealExpr, UnaryExpr, ListExpr, TupleExpr, DictExpr, SetExpr, IndexExpr, GeneratorExpr, ListComprehension, SetComprehension, DictionaryComprehension, ConditionalExpr, TypeApplication, LambdaExpr, StarExpr, BackquoteExpr, AwaitExpr, - AssignmentExpr, + AssignmentExpr, AssertTypeExpr, ) from mypy.traverser import TraverserVisitor @@ -99,6 +99,10 @@ def visit_cast_expr(self, e: CastExpr) -> None: self.add(e) super().visit_cast_expr(e) + def visit_assert_type_expr(self, e: AssertTypeExpr) -> None: + self.add(e) + super().visit_assert_type_expr(e) + def visit_reveal_expr(self, e: RevealExpr) -> None: self.add(e) super().visit_reveal_expr(e) diff --git a/mypy/strconv.py b/mypy/strconv.py index 22534a44971d..46ac4b10363c 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -431,6 +431,9 @@ def visit_comparison_expr(self, o: 'mypy.nodes.ComparisonExpr') -> str: def visit_cast_expr(self, o: 'mypy.nodes.CastExpr') -> str: return self.dump([o.expr, o.type], o) + def visit_assert_type_expr(self, o: 'mypy.nodes.AssertTypeExpr') -> str: + return self.dump([o.expr, o.type], o) + def visit_reveal_expr(self, o: 'mypy.nodes.RevealExpr') -> str: if o.kind == mypy.nodes.REVEAL_TYPE: return self.dump([o.expr], o) diff --git a/mypy/traverser.py b/mypy/traverser.py index 996f752f4c32..d9681bdd81ba 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -9,7 +9,7 @@ ) from mypy.visitor import NodeVisitor from mypy.nodes import ( - Block, MypyFile, FuncBase, FuncItem, CallExpr, ClassDef, Decorator, FuncDef, + AssertTypeExpr, Block, MypyFile, FuncBase, FuncItem, CallExpr, ClassDef, Decorator, FuncDef, ExpressionStmt, AssignmentStmt, OperatorAssignmentStmt, WhileStmt, ForStmt, ReturnStmt, AssertStmt, DelStmt, IfStmt, RaiseStmt, TryStmt, WithStmt, MatchStmt, NameExpr, MemberExpr, OpExpr, SliceExpr, CastExpr, @@ -205,6 +205,9 @@ def visit_slice_expr(self, o: SliceExpr) -> None: def visit_cast_expr(self, o: CastExpr) -> None: o.expr.accept(self) + def visit_assert_type_expr(self, o: AssertTypeExpr) -> None: + o.expr.accept(self) + def visit_reveal_expr(self, o: RevealExpr) -> None: if o.kind == REVEAL_TYPE: assert o.expr is not None diff --git a/mypy/treetransform.py b/mypy/treetransform.py index cdd4f604be86..62d5f6d72cbc 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -6,7 +6,7 @@ from typing import List, Dict, cast, Optional, Iterable from mypy.nodes import ( - MypyFile, Import, Node, ImportAll, ImportFrom, FuncItem, FuncDef, + AssertTypeExpr, MypyFile, Import, Node, ImportAll, ImportFrom, FuncItem, FuncDef, OverloadedFuncDef, ClassDef, Decorator, Block, Var, OperatorAssignmentStmt, ExpressionStmt, AssignmentStmt, ReturnStmt, RaiseStmt, AssertStmt, DelStmt, BreakStmt, ContinueStmt, @@ -407,6 +407,9 @@ def visit_cast_expr(self, node: CastExpr) -> CastExpr: return CastExpr(self.expr(node.expr), self.type(node.type)) + def visit_assert_type_expr(self, node: AssertTypeExpr) -> AssertTypeExpr: + return AssertTypeExpr(self.expr(node.expr), self.type(node.type)) + def visit_reveal_expr(self, node: RevealExpr) -> RevealExpr: if node.kind == REVEAL_TYPE: assert node.expr is not None diff --git a/mypy/types.py b/mypy/types.py index b42335096198..1d0274f38330 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -132,6 +132,11 @@ 'typing_extensions.reveal_type', ) +ASSERT_TYPE_NAMES: Final = ( + 'typing.assert_type', + 'typing_extensions.assert_type', +) + # Attributes that can optionally be defined in the body of a subclass of # enum.Enum but are removed from the class __dict__ by EnumMeta. ENUM_REMOVED_PROPS: Final = ( diff --git a/mypy/visitor.py b/mypy/visitor.py index 9d3ebb6818b4..7339111c7a05 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -81,6 +81,10 @@ def visit_comparison_expr(self, o: 'mypy.nodes.ComparisonExpr') -> T: def visit_cast_expr(self, o: 'mypy.nodes.CastExpr') -> T: pass + @abstractmethod + def visit_assert_type_expr(self, o: 'mypy.nodes.AssertTypeExpr') -> T: + pass + @abstractmethod def visit_reveal_expr(self, o: 'mypy.nodes.RevealExpr') -> T: pass @@ -523,6 +527,9 @@ def visit_comparison_expr(self, o: 'mypy.nodes.ComparisonExpr') -> T: def visit_cast_expr(self, o: 'mypy.nodes.CastExpr') -> T: pass + def visit_assert_type_expr(self, o: 'mypy.nodes.AssertTypeExpr') -> T: + pass + def visit_reveal_expr(self, o: 'mypy.nodes.RevealExpr') -> T: pass diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 71e497f9e368..1a74d71c3b27 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -11,7 +11,7 @@ ConditionalExpr, ComparisonExpr, IntExpr, FloatExpr, ComplexExpr, StrExpr, BytesExpr, EllipsisExpr, ListExpr, TupleExpr, DictExpr, SetExpr, ListComprehension, SetComprehension, DictionaryComprehension, SliceExpr, GeneratorExpr, CastExpr, StarExpr, - AssignmentExpr, + AssignmentExpr, AssertTypeExpr, Var, RefExpr, MypyFile, TypeInfo, TypeApplication, LDEF, ARG_POS ) from mypy.types import TupleType, Instance, TypeType, ProperType, get_proper_type @@ -203,6 +203,9 @@ def transform_super_expr(builder: IRBuilder, o: SuperExpr) -> Value: def transform_call_expr(builder: IRBuilder, expr: CallExpr) -> Value: if isinstance(expr.analyzed, CastExpr): return translate_cast_expr(builder, expr.analyzed) + elif isinstance(expr.analyzed, AssertTypeExpr): + # Compile to a no-op. + return builder.accept(expr.analyzed.expr) callee = expr.callee if isinstance(callee, IndexExpr) and isinstance(callee.analyzed, TypeApplication): diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index 43cfd457667d..3a1883cca50b 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -6,7 +6,7 @@ from typing_extensions import NoReturn from mypy.nodes import ( - MypyFile, FuncDef, ReturnStmt, AssignmentStmt, OpExpr, + AssertTypeExpr, MypyFile, FuncDef, ReturnStmt, AssignmentStmt, OpExpr, IntExpr, NameExpr, Var, IfStmt, UnaryExpr, ComparisonExpr, WhileStmt, CallExpr, IndexExpr, Block, ListExpr, ExpressionStmt, MemberExpr, ForStmt, BreakStmt, ContinueStmt, ConditionalExpr, OperatorAssignmentStmt, TupleExpr, ClassDef, @@ -327,6 +327,9 @@ def visit_var(self, o: Var) -> None: def visit_cast_expr(self, o: CastExpr) -> Value: assert False, "CastExpr should have been handled in CallExpr" + def visit_assert_type_expr(self, o: AssertTypeExpr) -> Value: + assert False, "AssertTypeExpr should have been handled in CallExpr" + def visit_star_expr(self, o: StarExpr) -> Value: assert False, "should have been handled in Tuple/List/Set/DictExpr or CallExpr" diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index ecce9248a4c8..d3403addecfb 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -876,6 +876,17 @@ L0: o = r3 return 1 +[case testAssertType] +from typing import assert_type +def f(x: int) -> None: + y = assert_type(x, int) +[out] +def f(x): + x, y :: int +L0: + y = x + return 1 + [case testDownCast] from typing import cast, List, Tuple class A: pass diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 46f0cf02a125..84b6105170bd 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1036,6 +1036,18 @@ class B: pass [out] main:3: error: "A" not callable +-- assert_type() + +[case testAssertType] +from typing import assert_type, Any +from typing_extensions import Literal +a: int = 1 +returned = assert_type(a, int) +reveal_type(returned) # N: Revealed type is "builtins.int" +assert_type(a, str) # E: Expression is of type "int", not "str" +assert_type(a, Any) # E: Expression is of type "int", not "Any" +assert_type(a, Literal[1]) # E: Expression is of type "int", not "Literal[1]" +[builtins fixtures/tuple.pyi] -- None return type -- ---------------- diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 739bf703f3e7..66b02638ebc7 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -11,6 +11,7 @@ from abc import abstractmethod, ABCMeta class GenericMeta(type): pass def cast(t, o): ... +def assert_type(o, t): ... overload = 0 Any = 0 Union = 0 diff --git a/test-data/unit/lib-stub/typing.pyi b/test-data/unit/lib-stub/typing.pyi index 57563fc9d2f6..0a1bb42b936c 100644 --- a/test-data/unit/lib-stub/typing.pyi +++ b/test-data/unit/lib-stub/typing.pyi @@ -9,6 +9,7 @@ # the stubs under fixtures/. cast = 0 +assert_type = 0 overload = 0 Any = 0 Union = 0 diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 34ec4f3c5b63..ea937b8678f1 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -839,6 +839,15 @@ cast(str, *None) # E: "cast" must be called with 2 positional arguments cast(str, target=None) # E: "cast" must be called with 2 positional arguments [out] +[case testInvalidAssertType] +from typing import assert_type +assert_type(1, type=int) # E: "assert_type" must be called with 2 positional arguments +assert_type(1, *int) # E: "assert_type" must be called with 2 positional arguments +assert_type() # E: "assert_type" expects 2 arguments +assert_type(1, int, "hello") # E: "assert_type" expects 2 arguments +assert_type(int, 1) # E: Invalid type: try using Literal[1] instead? +assert_type(1, int[int]) # E: "int" expects no type arguments, but 1 given + [case testInvalidAnyCall] from typing import Any Any(str, None) # E: Any(...) is no longer supported. Use cast(Any, ...) instead diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 1a2eca64a9f2..3ce289b52ae2 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -390,6 +390,17 @@ MypyFile:1( IntExpr(1) builtins.int))) +[case testAssertType] +from typing import assert_type +assert_type(1, int) +[out] +MypyFile:1( + ImportFrom:1(typing, [assert_type]) + ExpressionStmt:2( + AssertTypeExpr:2( + IntExpr(1) + builtins.int))) + [case testFunctionTypeVariable] from typing import TypeVar t = TypeVar('t') diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index 7a0115f17e9c..bdefb49e3038 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -101,6 +101,25 @@ NameExpr(8) : B CastExpr(9) : B NameExpr(9) : B +[case testAssertTypeExpr] +## AssertTypeExpr|[a-z] +from typing import Any, assert_type +d = None # type: Any +a = None # type: A +b = None # type: B +class A: pass +class B(A): pass +assert_type(d, Any) +assert_type(a, A) +assert_type(b, B) +[out] +AssertTypeExpr(8) : Any +NameExpr(8) : Any +AssertTypeExpr(9) : A +NameExpr(9) : A +AssertTypeExpr(10) : B +NameExpr(10) : B + [case testArithmeticOps] ## OpExpr import typing From ce858a6a0c85206ff877faf702293a9b67fae750 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 15 Apr 2022 16:45:02 -0700 Subject: [PATCH 07/79] Sync typeshed (#12596) Source commit: https://github.com/python/typeshed/commit/d09689f8115173e3b0b04365f5233164014a7a69 Co-authored-by: hauntsaninja <> --- mypy/typeshed/stdlib/_pydecimal.pyi | 82 +++++++++++++++++++ mypy/typeshed/stdlib/asyncio/streams.pyi | 4 +- mypy/typeshed/stdlib/asyncio/transports.pyi | 3 + mypy/typeshed/stdlib/base64.pyi | 41 +++++----- mypy/typeshed/stdlib/builtins.pyi | 47 ++++++++--- mypy/typeshed/stdlib/calendar.pyi | 87 +++++++++++++++------ mypy/typeshed/stdlib/datetime.pyi | 82 ++++++------------- mypy/typeshed/stdlib/linecache.pyi | 2 +- mypy/typeshed/stdlib/poplib.pyi | 6 +- mypy/typeshed/stdlib/tty.pyi | 2 + mypy/typeshed/stdlib/typing_extensions.pyi | 77 +++++++++++++++--- 11 files changed, 300 insertions(+), 133 deletions(-) diff --git a/mypy/typeshed/stdlib/_pydecimal.pyi b/mypy/typeshed/stdlib/_pydecimal.pyi index 56fbddfffa5c..c15a4a41747e 100644 --- a/mypy/typeshed/stdlib/_pydecimal.pyi +++ b/mypy/typeshed/stdlib/_pydecimal.pyi @@ -1,3 +1,85 @@ +import sys + # This is a slight lie, the implementations aren't exactly identical # However, in all likelihood, the differences are inconsequential from decimal import * + +if sys.version_info >= (3, 7): + __all__ = [ + "Decimal", + "Context", + "DecimalTuple", + "DefaultContext", + "BasicContext", + "ExtendedContext", + "DecimalException", + "Clamped", + "InvalidOperation", + "DivisionByZero", + "Inexact", + "Rounded", + "Subnormal", + "Overflow", + "Underflow", + "FloatOperation", + "DivisionImpossible", + "InvalidContext", + "ConversionSyntax", + "DivisionUndefined", + "ROUND_DOWN", + "ROUND_HALF_UP", + "ROUND_HALF_EVEN", + "ROUND_CEILING", + "ROUND_FLOOR", + "ROUND_UP", + "ROUND_HALF_DOWN", + "ROUND_05UP", + "setcontext", + "getcontext", + "localcontext", + "MAX_PREC", + "MAX_EMAX", + "MIN_EMIN", + "MIN_ETINY", + "HAVE_THREADS", + "HAVE_CONTEXTVAR", + ] +else: + __all__ = [ + "Decimal", + "Context", + "DecimalTuple", + "DefaultContext", + "BasicContext", + "ExtendedContext", + "DecimalException", + "Clamped", + "InvalidOperation", + "DivisionByZero", + "Inexact", + "Rounded", + "Subnormal", + "Overflow", + "Underflow", + "FloatOperation", + "DivisionImpossible", + "InvalidContext", + "ConversionSyntax", + "DivisionUndefined", + "ROUND_DOWN", + "ROUND_HALF_UP", + "ROUND_HALF_EVEN", + "ROUND_CEILING", + "ROUND_FLOOR", + "ROUND_UP", + "ROUND_HALF_DOWN", + "ROUND_05UP", + "setcontext", + "getcontext", + "localcontext", + "MAX_PREC", + "MAX_EMAX", + "MIN_EMIN", + "MIN_ETINY", + "HAVE_THREADS", + ] diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 98349222d5a2..666862cc7b7d 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -159,13 +159,13 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): class StreamWriter: def __init__( self, - transport: transports.BaseTransport, + transport: transports.WriteTransport, protocol: protocols.BaseProtocol, reader: StreamReader | None, loop: events.AbstractEventLoop, ) -> None: ... @property - def transport(self) -> transports.BaseTransport: ... + def transport(self) -> transports.WriteTransport: ... def write(self, data: bytes) -> None: ... def writelines(self, data: Iterable[bytes]) -> None: ... def write_eof(self) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi index c24ded49cfb8..90e54b547a87 100644 --- a/mypy/typeshed/stdlib/asyncio/transports.pyi +++ b/mypy/typeshed/stdlib/asyncio/transports.pyi @@ -27,6 +27,9 @@ class ReadTransport(BaseTransport): class WriteTransport(BaseTransport): def set_write_buffer_limits(self, high: int | None = ..., low: int | None = ...) -> None: ... def get_write_buffer_size(self) -> int: ... + if sys.version_info >= (3, 9): + def get_write_buffer_limits(self) -> tuple[int, int]: ... + def write(self, data: Any) -> None: ... def writelines(self, list_of_data: list[Any]) -> None: ... def write_eof(self) -> None: ... diff --git a/mypy/typeshed/stdlib/base64.pyi b/mypy/typeshed/stdlib/base64.pyi index 70fe64292328..ceed7d018d82 100644 --- a/mypy/typeshed/stdlib/base64.pyi +++ b/mypy/typeshed/stdlib/base64.pyi @@ -1,4 +1,5 @@ import sys +from _typeshed import ReadableBuffer from typing import IO if sys.version_info >= (3, 10): @@ -46,30 +47,30 @@ else: "urlsafe_b64decode", ] -def b64encode(s: bytes, altchars: bytes | None = ...) -> bytes: ... -def b64decode(s: str | bytes, altchars: bytes | None = ..., validate: bool = ...) -> bytes: ... -def standard_b64encode(s: bytes) -> bytes: ... -def standard_b64decode(s: str | bytes) -> bytes: ... -def urlsafe_b64encode(s: bytes) -> bytes: ... -def urlsafe_b64decode(s: str | bytes) -> bytes: ... -def b32encode(s: bytes) -> bytes: ... -def b32decode(s: str | bytes, casefold: bool = ..., map01: bytes | None = ...) -> bytes: ... -def b16encode(s: bytes) -> bytes: ... -def b16decode(s: str | bytes, casefold: bool = ...) -> bytes: ... +def b64encode(s: ReadableBuffer, altchars: ReadableBuffer | None = ...) -> bytes: ... +def b64decode(s: str | ReadableBuffer, altchars: ReadableBuffer | None = ..., validate: bool = ...) -> bytes: ... +def standard_b64encode(s: ReadableBuffer) -> bytes: ... +def standard_b64decode(s: str | ReadableBuffer) -> bytes: ... +def urlsafe_b64encode(s: ReadableBuffer) -> bytes: ... +def urlsafe_b64decode(s: str | ReadableBuffer) -> bytes: ... +def b32encode(s: ReadableBuffer) -> bytes: ... +def b32decode(s: str | ReadableBuffer, casefold: bool = ..., map01: bytes | None = ...) -> bytes: ... +def b16encode(s: ReadableBuffer) -> bytes: ... +def b16decode(s: str | ReadableBuffer, casefold: bool = ...) -> bytes: ... if sys.version_info >= (3, 10): - def b32hexencode(s: bytes) -> bytes: ... - def b32hexdecode(s: str | bytes, casefold: bool = ...) -> bytes: ... + def b32hexencode(s: ReadableBuffer) -> bytes: ... + def b32hexdecode(s: str | ReadableBuffer, casefold: bool = ...) -> bytes: ... -def a85encode(b: bytes, *, foldspaces: bool = ..., wrapcol: int = ..., pad: bool = ..., adobe: bool = ...) -> bytes: ... -def a85decode(b: str | bytes, *, foldspaces: bool = ..., adobe: bool = ..., ignorechars: str | bytes = ...) -> bytes: ... -def b85encode(b: bytes, pad: bool = ...) -> bytes: ... -def b85decode(b: str | bytes) -> bytes: ... +def a85encode(b: ReadableBuffer, *, foldspaces: bool = ..., wrapcol: int = ..., pad: bool = ..., adobe: bool = ...) -> bytes: ... +def a85decode(b: str | ReadableBuffer, *, foldspaces: bool = ..., adobe: bool = ..., ignorechars: str | bytes = ...) -> bytes: ... +def b85encode(b: ReadableBuffer, pad: bool = ...) -> bytes: ... +def b85decode(b: str | ReadableBuffer) -> bytes: ... def decode(input: IO[bytes], output: IO[bytes]) -> None: ... def encode(input: IO[bytes], output: IO[bytes]) -> None: ... -def encodebytes(s: bytes) -> bytes: ... -def decodebytes(s: bytes) -> bytes: ... +def encodebytes(s: ReadableBuffer) -> bytes: ... +def decodebytes(s: ReadableBuffer) -> bytes: ... if sys.version_info < (3, 9): - def encodestring(s: bytes) -> bytes: ... - def decodestring(s: bytes) -> bytes: ... + def encodestring(s: ReadableBuffer) -> bytes: ... + def decodestring(s: ReadableBuffer) -> bytes: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index ce2042bcac63..9c5dfcfef22f 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -52,7 +52,6 @@ from typing import ( SupportsInt, SupportsRound, TypeVar, - Union, overload, ) from typing_extensions import Literal, SupportsIndex, TypeGuard, final @@ -1747,19 +1746,45 @@ if sys.version_info >= (3, 10): class EncodingWarning(Warning): ... if sys.version_info >= (3, 11): - _SplitCondition = Union[type[BaseException], tuple[type[BaseException], ...], Callable[[BaseException], bool]] + _BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True) + _BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException) + _ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True) + _ExceptionT = TypeVar("_ExceptionT", bound=Exception) - class BaseExceptionGroup(BaseException): - def __new__(cls: type[Self], __message: str, __exceptions: Sequence[BaseException]) -> Self: ... + class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]): + def __new__(cls: type[Self], __message: str, __exceptions: Sequence[_BaseExceptionT_co]) -> Self: ... @property def message(self) -> str: ... @property - def exceptions(self) -> tuple[BaseException, ...]: ... - def subgroup(self: Self, __condition: _SplitCondition) -> Self | None: ... - def split(self: Self, __condition: _SplitCondition) -> tuple[Self | None, Self | None]: ... - def derive(self: Self, __excs: Sequence[BaseException]) -> Self: ... + def exceptions(self) -> tuple[_BaseExceptionT_co | BaseExceptionGroup[_BaseExceptionT_co], ...]: ... + @overload + def subgroup( + self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + ) -> BaseExceptionGroup[_BaseExceptionT] | None: ... + @overload + def subgroup(self: Self, __condition: Callable[[_BaseExceptionT_co], bool]) -> Self | None: ... + @overload + def split( + self: Self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...] + ) -> tuple[BaseExceptionGroup[_BaseExceptionT] | None, Self | None]: ... + @overload + def split(self: Self, __condition: Callable[[_BaseExceptionT_co], bool]) -> tuple[Self | None, Self | None]: ... + def derive(self: Self, __excs: Sequence[_BaseExceptionT_co]) -> Self: ... - class ExceptionGroup(BaseExceptionGroup, Exception): - def __new__(cls: type[Self], __message: str, __exceptions: Sequence[Exception]) -> Self: ... + class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): + def __new__(cls: type[Self], __message: str, __exceptions: Sequence[_ExceptionT_co]) -> Self: ... @property - def exceptions(self) -> tuple[Exception, ...]: ... + def exceptions(self) -> tuple[_ExceptionT_co | ExceptionGroup[_ExceptionT_co], ...]: ... + # We accept a narrower type, but that's OK. + @overload # type: ignore[override] + def subgroup( + self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + ) -> ExceptionGroup[_ExceptionT] | None: ... + @overload + def subgroup(self: Self, __condition: Callable[[_ExceptionT_co], bool]) -> Self | None: ... + @overload # type: ignore[override] + def split( + self: Self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...] + ) -> tuple[ExceptionGroup[_ExceptionT] | None, Self | None]: ... + @overload + def split(self: Self, __condition: Callable[[_ExceptionT_co], bool]) -> tuple[Self | None, Self | None]: ... diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index 05ceca4a3ae0..f106eb1213f1 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -4,32 +4,67 @@ from collections.abc import Iterable, Sequence from time import struct_time from typing_extensions import Literal -__all__ = [ - "IllegalMonthError", - "IllegalWeekdayError", - "setfirstweekday", - "firstweekday", - "isleap", - "leapdays", - "weekday", - "monthrange", - "monthcalendar", - "prmonth", - "month", - "prcal", - "calendar", - "timegm", - "month_name", - "month_abbr", - "day_name", - "day_abbr", - "Calendar", - "TextCalendar", - "HTMLCalendar", - "LocaleTextCalendar", - "LocaleHTMLCalendar", - "weekheader", -] +if sys.version_info >= (3, 10): + __all__ = [ + "IllegalMonthError", + "IllegalWeekdayError", + "setfirstweekday", + "firstweekday", + "isleap", + "leapdays", + "weekday", + "monthrange", + "monthcalendar", + "prmonth", + "month", + "prcal", + "calendar", + "timegm", + "month_name", + "month_abbr", + "day_name", + "day_abbr", + "Calendar", + "TextCalendar", + "HTMLCalendar", + "LocaleTextCalendar", + "LocaleHTMLCalendar", + "weekheader", + "FRIDAY", + "MONDAY", + "SATURDAY", + "SUNDAY", + "THURSDAY", + "TUESDAY", + "WEDNESDAY", + ] +else: + __all__ = [ + "IllegalMonthError", + "IllegalWeekdayError", + "setfirstweekday", + "firstweekday", + "isleap", + "leapdays", + "weekday", + "monthrange", + "monthcalendar", + "prmonth", + "month", + "prcal", + "calendar", + "timegm", + "month_name", + "month_abbr", + "day_name", + "day_abbr", + "Calendar", + "TextCalendar", + "HTMLCalendar", + "LocaleTextCalendar", + "LocaleHTMLCalendar", + "weekheader", + ] _LocaleType = tuple[str | None, str | None] diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 18a4d2dd2856..220e07e25fe0 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -65,12 +65,7 @@ class date: def isoformat(self) -> str: ... def timetuple(self) -> struct_time: ... def toordinal(self) -> int: ... - if sys.version_info >= (3, 6): - def replace(self: Self, year: int = ..., month: int = ..., day: int = ...) -> Self: ... - else: - # Prior to Python 3.6, the `replace` method always returned `date`, even in subclasses - def replace(self, year: int = ..., month: int = ..., day: int = ...) -> date: ... - + def replace(self: Self, year: int = ..., month: int = ..., day: int = ...) -> Self: ... def __le__(self, __other: date) -> bool: ... def __lt__(self, __other: date) -> bool: ... def __ge__(self, __other: date) -> bool: ... @@ -144,29 +139,16 @@ class time: def utcoffset(self) -> timedelta | None: ... def tzname(self) -> str | None: ... def dst(self) -> timedelta | None: ... - if sys.version_info >= (3, 6): - def replace( - self: Self, - hour: int = ..., - minute: int = ..., - second: int = ..., - microsecond: int = ..., - tzinfo: _tzinfo | None = ..., - *, - fold: int = ..., - ) -> Self: ... - else: - # Prior to Python 3.6, the `replace` method always returned `time`, even in subclasses - def replace( - self, - hour: int = ..., - minute: int = ..., - second: int = ..., - microsecond: int = ..., - tzinfo: _tzinfo | None = ..., - *, - fold: int = ..., - ) -> time: ... + def replace( + self: Self, + hour: int = ..., + minute: int = ..., + second: int = ..., + microsecond: int = ..., + tzinfo: _tzinfo | None = ..., + *, + fold: int = ..., + ) -> Self: ... _date = date _time = time @@ -278,35 +260,19 @@ class datetime(date): def date(self) -> _date: ... def time(self) -> _time: ... def timetz(self) -> _time: ... - if sys.version_info >= (3, 6): - def replace( - self: Self, - year: int = ..., - month: int = ..., - day: int = ..., - hour: int = ..., - minute: int = ..., - second: int = ..., - microsecond: int = ..., - tzinfo: _tzinfo | None = ..., - *, - fold: int = ..., - ) -> Self: ... - else: - # Prior to Python 3.6, the `replace` method always returned `datetime`, even in subclasses - def replace( - self, - year: int = ..., - month: int = ..., - day: int = ..., - hour: int = ..., - minute: int = ..., - second: int = ..., - microsecond: int = ..., - tzinfo: _tzinfo | None = ..., - *, - fold: int = ..., - ) -> datetime: ... + def replace( + self: Self, + year: int = ..., + month: int = ..., + day: int = ..., + hour: int = ..., + minute: int = ..., + second: int = ..., + microsecond: int = ..., + tzinfo: _tzinfo | None = ..., + *, + fold: int = ..., + ) -> Self: ... if sys.version_info >= (3, 8): def astimezone(self: Self, tz: _tzinfo | None = ...) -> Self: ... else: diff --git a/mypy/typeshed/stdlib/linecache.pyi b/mypy/typeshed/stdlib/linecache.pyi index 6b3761f4a0c9..d72d678b5e18 100644 --- a/mypy/typeshed/stdlib/linecache.pyi +++ b/mypy/typeshed/stdlib/linecache.pyi @@ -7,7 +7,7 @@ else: __all__ = ["getline", "clearcache", "checkcache"] _ModuleGlobals = dict[str, Any] -_ModuleMetadata = tuple[int, float, list[str], str] +_ModuleMetadata = tuple[int, float | None, list[str], str] class _SourceLoader(Protocol): def __call__(self) -> str | None: ... diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index 111e5c4ca76a..6b651e98e41f 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -1,13 +1,9 @@ import socket import ssl -import sys from typing import Any, BinaryIO, NoReturn, Pattern, overload from typing_extensions import Literal -if sys.version_info >= (3, 10): - __all__ = ["POP3", "error_proto", "POP3_SSL"] -else: - __all__ = ["POP3", "error_proto"] +__all__ = ["POP3", "error_proto", "POP3_SSL"] _LongResp = tuple[bytes, list[bytes], int] diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi index 63f594a8fbb1..08c93f6f2e84 100644 --- a/mypy/typeshed/stdlib/tty.pyi +++ b/mypy/typeshed/stdlib/tty.pyi @@ -2,6 +2,8 @@ import sys from typing import IO if sys.platform != "win32": + __all__ = ["setraw", "setcbreak"] + _FD = int | IO[str] # XXX: Undocumented integer constants diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 892218f68283..70f395446b0b 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -30,6 +30,57 @@ from typing import ( # noqa: Y022,Y027 overload as overload, ) +__all__ = [ + "ClassVar", + "Concatenate", + "Final", + "LiteralString", + "ParamSpec", + "Self", + "Type", + "TypeVarTuple", + "Unpack", + "Awaitable", + "AsyncIterator", + "AsyncIterable", + "Coroutine", + "AsyncGenerator", + "AsyncContextManager", + "ChainMap", + "ContextManager", + "Counter", + "Deque", + "DefaultDict", + "OrderedDict", + "TypedDict", + "SupportsIndex", + "Annotated", + "assert_never", + "assert_type", + "dataclass_transform", + "final", + "IntVar", + "is_typeddict", + "Literal", + "NewType", + "overload", + "Protocol", + "reveal_type", + "runtime", + "runtime_checkable", + "Text", + "TypeAlias", + "TypeGuard", + "TYPE_CHECKING", + "Never", + "NoReturn", + "Required", + "NotRequired", + "get_args", + "get_origin", + "get_type_hints", +] + _T = TypeVar("_T") _F = TypeVar("_F", bound=Callable[..., Any]) _TC = TypeVar("_TC", bound=Type[object]) @@ -82,15 +133,14 @@ TypedDict: object OrderedDict = _Alias() -if sys.version_info >= (3, 7): - def get_type_hints( - obj: Callable[..., Any], - globalns: dict[str, Any] | None = ..., - localns: dict[str, Any] | None = ..., - include_extras: bool = ..., - ) -> dict[str, Any]: ... - def get_args(tp: Any) -> tuple[Any, ...]: ... - def get_origin(tp: Any) -> Any | None: ... +def get_type_hints( + obj: Callable[..., Any], + globalns: dict[str, Any] | None = ..., + localns: dict[str, Any] | None = ..., + include_extras: bool = ..., +) -> dict[str, Any]: ... +def get_args(tp: Any) -> tuple[Any, ...]: ... +def get_origin(tp: Any) -> Any | None: ... Annotated: _SpecialForm _AnnotatedAlias: Any # undocumented @@ -137,12 +187,19 @@ else: # New things in 3.11 if sys.version_info >= (3, 11): - from typing import Never as Never, Self as Self, assert_never as assert_never, reveal_type as reveal_type + from typing import ( + Never as Never, + Self as Self, + assert_never as assert_never, + assert_type as assert_type, + reveal_type as reveal_type, + ) else: Self: _SpecialForm Never: _SpecialForm def reveal_type(__obj: _T) -> _T: ... def assert_never(__arg: NoReturn) -> NoReturn: ... + def assert_type(__val: _T, __typ: Any) -> _T: ... # Experimental (hopefully these will be in 3.11) Required: _SpecialForm From 10ba5c1aa14e01be1cfacb322136e22751617d26 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sat, 16 Apr 2022 16:37:25 +0100 Subject: [PATCH 08/79] Docs: Use PEP 585 syntax in "The type of class objects" (#12516) --- docs/source/kinds_of_types.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index dd19d7fc0622..51f54ab0e1e6 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -615,10 +615,11 @@ The type of class objects <484#the-type-of-class-objects>`.) Sometimes you want to talk about class objects that inherit from a -given class. This can be spelled as :py:class:`Type[C] ` where ``C`` is a +given class. This can be spelled as ``type[C]`` (or, on Python 3.8 and lower, +:py:class:`typing.Type[C] `) where ``C`` is a class. In other words, when ``C`` is the name of a class, using ``C`` to annotate an argument declares that the argument is an instance of -``C`` (or of a subclass of ``C``), but using :py:class:`Type[C] ` as an +``C`` (or of a subclass of ``C``), but using ``type[C]`` as an argument annotation declares that the argument is a class object deriving from ``C`` (or ``C`` itself). @@ -649,7 +650,7 @@ you pass it the right class object: # (Here we could write the user object to a database) return user -How would we annotate this function? Without :py:class:`~typing.Type` the best we +How would we annotate this function? Without the ability to parameterize ``type``, the best we could do would be: .. code-block:: python @@ -665,14 +666,14 @@ doesn't see that the ``buyer`` variable has type ``ProUser``: buyer = new_user(ProUser) buyer.pay() # Rejected, not a method on User -However, using :py:class:`~typing.Type` and a type variable with an upper bound (see +However, using the ``type[C]`` syntax and a type variable with an upper bound (see :ref:`type-variable-upper-bound`) we can do better: .. code-block:: python U = TypeVar('U', bound=User) - def new_user(user_class: Type[U]) -> U: + def new_user(user_class: type[U]) -> U: # Same implementation as before Now mypy will infer the correct type of the result when we call @@ -685,12 +686,12 @@ Now mypy will infer the correct type of the result when we call .. note:: - The value corresponding to :py:class:`Type[C] ` must be an actual class + The value corresponding to ``type[C]`` must be an actual class object that's a subtype of ``C``. Its constructor must be compatible with the constructor of ``C``. If ``C`` is a type variable, its upper bound must be a class object. -For more details about ``Type[]`` see :pep:`PEP 484: The type of +For more details about ``type[]`` and :py:class:`typing.Type[] `, see :pep:`PEP 484: The type of class objects <484#the-type-of-class-objects>`. .. _text-and-anystr: From 0df8cf532918f888610a5afd7bb88192712de984 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 16 Apr 2022 13:48:20 -0700 Subject: [PATCH 09/79] Support typing_extensions.overload (#12602) This always existed in typing_extensions, but was an alias for typing.overload. With python/typing#1140, it will actually make a difference at runtime which one you use. Note that this shouldn't change mypy's behaviour, since we alias typing_extensions.overload to typing.overload in typeshed, but this makes the logic less fragile. --- mypy/checker.py | 5 +- mypy/semanal.py | 4 +- mypy/stubgen.py | 23 +++--- mypy/stubtest.py | 3 +- mypy/types.py | 5 ++ test-data/unit/check-overloading.test | 18 +++++ test-data/unit/lib-stub/typing_extensions.pyi | 2 +- test-data/unit/stubgen.test | 79 ++++++++++++++++++- 8 files changed, 122 insertions(+), 17 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index e53e306a7e5d..24f101421ff4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -37,7 +37,8 @@ UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, is_named_instance, union_items, TypeQuery, LiteralType, is_optional, remove_optional, TypeTranslator, StarType, get_proper_type, ProperType, - get_proper_types, is_literal_type, TypeAliasType, TypeGuardedType, ParamSpecType + get_proper_types, is_literal_type, TypeAliasType, TypeGuardedType, ParamSpecType, + OVERLOAD_NAMES, ) from mypy.sametypes import is_same_type from mypy.messages import ( @@ -3981,7 +3982,7 @@ def visit_decorator(self, e: Decorator) -> None: # may be different from the declared signature. sig: Type = self.function_type(e.func) for d in reversed(e.decorators): - if refers_to_fullname(d, 'typing.overload'): + if refers_to_fullname(d, OVERLOAD_NAMES): self.fail(message_registry.MULTIPLE_OVERLOADS_REQUIRED, e) continue dec = self.expr_checker.accept(d) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3ffc20cead77..1ec37309ce8e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -99,7 +99,7 @@ TypeTranslator, TypeOfAny, TypeType, NoneType, PlaceholderType, TPDICT_NAMES, ProperType, get_proper_type, get_proper_types, TypeAliasType, TypeVarLikeType, Parameters, ParamSpecType, PROTOCOL_NAMES, TYPE_ALIAS_NAMES, FINAL_TYPE_NAMES, FINAL_DECORATOR_NAMES, REVEAL_TYPE_NAMES, - ASSERT_TYPE_NAMES, is_named_instance, + ASSERT_TYPE_NAMES, OVERLOAD_NAMES, is_named_instance, ) from mypy.typeops import function_type, get_type_vars from mypy.type_visitor import TypeQuery @@ -835,7 +835,7 @@ def analyze_overload_sigs_and_impl( if isinstance(item, Decorator): callable = function_type(item.func, self.named_type('builtins.function')) assert isinstance(callable, CallableType) - if not any(refers_to_fullname(dec, 'typing.overload') + if not any(refers_to_fullname(dec, OVERLOAD_NAMES) for dec in item.decorators): if i == len(defn.items) - 1 and not self.is_stub_file: # Last item outside a stub is impl diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 5d8e6a57c212..eade0bbdc363 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -54,7 +54,7 @@ from collections import defaultdict from typing import ( - List, Dict, Tuple, Iterable, Mapping, Optional, Set, cast, + List, Dict, Tuple, Iterable, Mapping, Optional, Set, Union, cast, ) from typing_extensions import Final @@ -84,7 +84,7 @@ from mypy.options import Options as MypyOptions from mypy.types import ( Type, TypeStrVisitor, CallableType, UnboundType, NoneType, TupleType, TypeList, Instance, - AnyType, get_proper_type + AnyType, get_proper_type, OVERLOAD_NAMES ) from mypy.visitor import NodeVisitor from mypy.find_sources import create_source_list, InvalidSourceList @@ -93,6 +93,10 @@ from mypy.traverser import all_yield_expressions, has_return_statement, has_yield_expression from mypy.moduleinspect import ModuleInspect +TYPING_MODULE_NAMES: Final = ( + 'typing', + 'typing_extensions', +) # Common ways of naming package containing vendored modules. VENDOR_PACKAGES: Final = [ @@ -768,13 +772,15 @@ def process_name_expr_decorator(self, expr: NameExpr, context: Decorator) -> Tup self.add_decorator('property') self.add_decorator('abc.abstractmethod') is_abstract = True - elif self.refers_to_fullname(name, 'typing.overload'): + elif self.refers_to_fullname(name, OVERLOAD_NAMES): self.add_decorator(name) self.add_typing_import('overload') is_overload = True return is_abstract, is_overload - def refers_to_fullname(self, name: str, fullname: str) -> bool: + def refers_to_fullname(self, name: str, fullname: Union[str, Tuple[str, ...]]) -> bool: + if isinstance(fullname, tuple): + return any(self.refers_to_fullname(name, fname) for fname in fullname) module, short = fullname.rsplit('.', 1) return (self.import_tracker.module_for.get(name) == module and (name == short or @@ -825,8 +831,8 @@ def process_member_expr_decorator(self, expr: MemberExpr, context: Decorator) -> expr.expr.name + '.coroutine', expr.expr.name) elif (isinstance(expr.expr, NameExpr) and - (expr.expr.name == 'typing' or - self.import_tracker.reverse_alias.get(expr.expr.name) == 'typing') and + (expr.expr.name in TYPING_MODULE_NAMES or + self.import_tracker.reverse_alias.get(expr.expr.name) in TYPING_MODULE_NAMES) and expr.name == 'overload'): self.import_tracker.require_name(expr.expr.name) self.add_decorator('%s.%s' % (expr.expr.name, 'overload')) @@ -1060,7 +1066,7 @@ def visit_import_from(self, o: ImportFrom) -> None: and name not in self.referenced_names and (not self._all_ or name in IGNORED_DUNDERS) and not is_private - and module not in ('abc', 'typing', 'asyncio')): + and module not in ('abc', 'asyncio') + TYPING_MODULE_NAMES): # An imported name that is never referenced in the module is assumed to be # exported, unless there is an explicit __all__. Note that we need to special # case 'abc' since some references are deleted during semantic analysis. @@ -1118,8 +1124,7 @@ def get_init(self, lvalue: str, rvalue: Expression, typename = self.print_annotation(annotation) if (isinstance(annotation, UnboundType) and not annotation.args and annotation.name == 'Final' and - self.import_tracker.module_for.get('Final') in ('typing', - 'typing_extensions')): + self.import_tracker.module_for.get('Final') in TYPING_MODULE_NAMES): # Final without type argument is invalid in stubs. final_arg = self.get_str_type_of_node(rvalue) typename += '[{}]'.format(final_arg) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 582c467ee2b0..7fa0f5937f99 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -912,9 +912,8 @@ def apply_decorator_to_funcitem( return None if decorator.fullname in ( "builtins.staticmethod", - "typing.overload", "abc.abstractmethod", - ): + ) or decorator.fullname in mypy.types.OVERLOAD_NAMES: return func if decorator.fullname == "builtins.classmethod": assert func.arguments[0].variable.name in ("cls", "metacls") diff --git a/mypy/types.py b/mypy/types.py index 1d0274f38330..213d8de7d8bb 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -137,6 +137,11 @@ 'typing_extensions.assert_type', ) +OVERLOAD_NAMES: Final = ( + 'typing.overload', + 'typing_extensions.overload', +) + # Attributes that can optionally be defined in the body of a subclass of # enum.Enum but are removed from the class __dict__ by EnumMeta. ENUM_REMOVED_PROPS: Final = ( diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 0376b62ab202..e2a87ea62a92 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -40,6 +40,24 @@ class A: pass class B: pass [builtins fixtures/isinstance.pyi] +[case testTypingExtensionsOverload] +from typing import Any +from typing_extensions import overload +@overload +def f(x: 'A') -> 'B': ... +@overload +def f(x: 'B') -> 'A': ... + +def f(x: Any) -> Any: + pass + +reveal_type(f(A())) # N: Revealed type is "__main__.B" +reveal_type(f(B())) # N: Revealed type is "__main__.A" + +class A: pass +class B: pass +[builtins fixtures/isinstance.pyi] + [case testOverloadNeedsImplementation] from typing import overload, Any @overload # E: An overloaded function outside a stub file must have an implementation diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 7ad334d6a24e..95f45f3b8947 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -1,6 +1,6 @@ from typing import TypeVar, Any, Mapping, Iterator, NoReturn as NoReturn, Dict, Type from typing import TYPE_CHECKING as TYPE_CHECKING -from typing import NewType as NewType +from typing import NewType as NewType, overload as overload import sys diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index cbce46b35605..927cc5617c75 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -2461,6 +2461,50 @@ class A: def f(self, x: Tuple[int, int]) -> int: ... +@overload +def f(x: int, y: int) -> int: ... +@overload +def f(x: Tuple[int, int]) -> int: ... + +[case testOverload_fromTypingExtensionsImport] +from typing import Tuple, Union +from typing_extensions import overload + +class A: + @overload + def f(self, x: int, y: int) -> int: + ... + + @overload + def f(self, x: Tuple[int, int]) -> int: + ... + + def f(self, *args: Union[int, Tuple[int, int]]) -> int: + pass + +@overload +def f(x: int, y: int) -> int: + ... + +@overload +def f(x: Tuple[int, int]) -> int: + ... + +def f(*args: Union[int, Tuple[int, int]]) -> int: + pass + + +[out] +from typing import Tuple +from typing_extensions import overload + +class A: + @overload + def f(self, x: int, y: int) -> int: ... + @overload + def f(self, x: Tuple[int, int]) -> int: ... + + @overload def f(x: int, y: int) -> int: ... @overload @@ -2468,6 +2512,7 @@ def f(x: Tuple[int, int]) -> int: ... [case testOverload_importTyping] import typing +import typing_extensions class A: @typing.overload @@ -2506,9 +2551,21 @@ def f(x: typing.Tuple[int, int]) -> int: def f(*args: typing.Union[int, typing.Tuple[int, int]]) -> int: pass +@typing_extensions.overload +def g(x: int, y: int) -> int: + ... + +@typing_extensions.overload +def g(x: typing.Tuple[int, int]) -> int: + ... + +def g(*args: typing.Union[int, typing.Tuple[int, int]]) -> int: + pass + [out] import typing +import typing_extensions class A: @typing.overload @@ -2527,10 +2584,14 @@ class A: def f(x: int, y: int) -> int: ... @typing.overload def f(x: typing.Tuple[int, int]) -> int: ... - +@typing_extensions.overload +def g(x: int, y: int) -> int: ... +@typing_extensions.overload +def g(x: typing.Tuple[int, int]) -> int: ... [case testOverload_importTypingAs] import typing as t +import typing_extensions as te class A: @t.overload @@ -2570,8 +2631,20 @@ def f(*args: t.Union[int, t.Tuple[int, int]]) -> int: pass +@te.overload +def g(x: int, y: int) -> int: + ... + +@te.overload +def g(x: t.Tuple[int, int]) -> int: + ... + +def g(*args: t.Union[int, t.Tuple[int, int]]) -> int: + pass + [out] import typing as t +import typing_extensions as te class A: @t.overload @@ -2590,6 +2663,10 @@ class A: def f(x: int, y: int) -> int: ... @t.overload def f(x: t.Tuple[int, int]) -> int: ... +@te.overload +def g(x: int, y: int) -> int: ... +@te.overload +def g(x: t.Tuple[int, int]) -> int: ... [case testProtocol_semanal] from typing import Protocol, TypeVar From f501cf649d7976077a7196e3548d773d67340a8c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 17 Apr 2022 18:07:56 -0700 Subject: [PATCH 10/79] Fix type context for assert_type() (#12612) Noticed in python/typeshed#7655 that it was incorrectly inferring list[Any] in all cases. This is because I incorrectly put Any as the type context in the assert_type implementation. Use the current context instead, like for reveal_type(). --- mypy/checkexpr.py | 2 +- test-data/unit/check-expressions.test | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 32fa391bb0e2..7383a2b69610 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3145,7 +3145,7 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: return target_type def visit_assert_type_expr(self, expr: AssertTypeExpr) -> Type: - source_type = self.accept(expr.expr, type_context=AnyType(TypeOfAny.special_form), + source_type = self.accept(expr.expr, type_context=self.type_context[-1], allow_none_return=True, always_allow_any=True) target_type = expr.type if not is_same_type(source_type, target_type): diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 84b6105170bd..fd10b82cc558 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1049,6 +1049,20 @@ assert_type(a, Any) # E: Expression is of type "int", not "Any" assert_type(a, Literal[1]) # E: Expression is of type "int", not "Literal[1]" [builtins fixtures/tuple.pyi] +[case testAssertTypeGeneric] +from typing import assert_type, TypeVar, Generic +from typing_extensions import Literal +T = TypeVar("T") +def f(x: T) -> T: return x +assert_type(f(1), int) +class Gen(Generic[T]): + def __new__(cls, obj: T) -> Gen[T]: ... +assert_type(Gen(1), Gen[int]) +# With type context, it infers Gen[Literal[1]] instead. +y: Gen[Literal[1]] = assert_type(Gen(1), Gen[Literal[1]]) + +[builtins fixtures/tuple.pyi] + -- None return type -- ---------------- From 9cab2964a186d7e71567a1528fcc956f9eecebac Mon Sep 17 00:00:00 2001 From: Hugues Date: Mon, 18 Apr 2022 07:54:02 -0700 Subject: [PATCH 11/79] more enum-related speedups (#12032) As a followup to #9394 address a few more O(n**2) behaviors caused by decomposing enums into unions of literals. --- mypy/meet.py | 30 ++++++++++++++++++++++++ mypy/sametypes.py | 34 ++++++++++++++++++++++----- mypy/subtypes.py | 59 +++++++++++++++++++++++++++++++++++++++-------- mypy/typeops.py | 9 ++++++++ 4 files changed, 116 insertions(+), 16 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index 535e48bc3a23..5ee64416490d 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -64,6 +64,8 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type: if isinstance(declared, UnionType): return make_simplified_union([narrow_declared_type(x, narrowed) for x in declared.relevant_items()]) + if is_enum_overlapping_union(declared, narrowed): + return narrowed elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: @@ -137,6 +139,22 @@ def get_possible_variants(typ: Type) -> List[Type]: return [typ] +def is_enum_overlapping_union(x: ProperType, y: ProperType) -> bool: + """Return True if x is an Enum, and y is an Union with at least one Literal from x""" + return ( + isinstance(x, Instance) and x.type.is_enum and + isinstance(y, UnionType) and + any(isinstance(p, LiteralType) and x.type == p.fallback.type + for p in (get_proper_type(z) for z in y.relevant_items())) + ) + + +def is_literal_in_union(x: ProperType, y: ProperType) -> bool: + """Return True if x is a Literal and y is an Union that includes x""" + return (isinstance(x, LiteralType) and isinstance(y, UnionType) and + any(x == get_proper_type(z) for z in y.items)) + + def is_overlapping_types(left: Type, right: Type, ignore_promotions: bool = False, @@ -198,6 +216,18 @@ def _is_overlapping_types(left: Type, right: Type) -> bool: # # These checks will also handle the NoneType and UninhabitedType cases for us. + # enums are sometimes expanded into an Union of Literals + # when that happens we want to make sure we treat the two as overlapping + # and crucially, we want to do that *fast* in case the enum is large + # so we do it before expanding variants below to avoid O(n**2) behavior + if ( + is_enum_overlapping_union(left, right) + or is_enum_overlapping_union(right, left) + or is_literal_in_union(left, right) + or is_literal_in_union(right, left) + ): + return True + if (is_proper_subtype(left, right, ignore_promotions=ignore_promotions) or is_proper_subtype(right, left, ignore_promotions=ignore_promotions)): return True diff --git a/mypy/sametypes.py b/mypy/sametypes.py index 46798018410d..1c22c32f8b06 100644 --- a/mypy/sametypes.py +++ b/mypy/sametypes.py @@ -1,4 +1,4 @@ -from typing import Sequence +from typing import Sequence, Tuple, Set, List from mypy.types import ( Type, UnboundType, AnyType, NoneType, TupleType, TypedDictType, @@ -6,7 +6,7 @@ Overloaded, PartialType, DeletedType, UninhabitedType, TypeType, LiteralType, ProperType, get_proper_type, TypeAliasType, ParamSpecType, Parameters, UnpackType ) -from mypy.typeops import tuple_fallback, make_simplified_union +from mypy.typeops import tuple_fallback, make_simplified_union, is_simple_literal def is_same_type(left: Type, right: Type) -> bool: @@ -49,6 +49,22 @@ def is_same_types(a1: Sequence[Type], a2: Sequence[Type]) -> bool: return True +def _extract_literals(u: UnionType) -> Tuple[Set[Type], List[Type]]: + """Given a UnionType, separate out its items into a set of simple literals and a remainder list + This is a useful helper to avoid O(n**2) behavior when comparing large unions, which can often + result from large enums in contexts where type narrowing removes a small subset of entries. + """ + lit: Set[Type] = set() + rem: List[Type] = [] + for i in u.relevant_items(): + i = get_proper_type(i) + if is_simple_literal(i): + lit.add(i) + else: + rem.append(i) + return lit, rem + + class SameTypeVisitor(TypeVisitor[bool]): """Visitor for checking whether two types are the 'same' type.""" @@ -153,14 +169,20 @@ def visit_literal_type(self, left: LiteralType) -> bool: def visit_union_type(self, left: UnionType) -> bool: if isinstance(self.right, UnionType): + left_lit, left_rem = _extract_literals(left) + right_lit, right_rem = _extract_literals(self.right) + + if left_lit != right_lit: + return False + # Check that everything in left is in right - for left_item in left.items: - if not any(is_same_type(left_item, right_item) for right_item in self.right.items): + for left_item in left_rem: + if not any(is_same_type(left_item, right_item) for right_item in right_rem): return False # Check that everything in right is in left - for right_item in self.right.items: - if not any(is_same_type(right_item, left_item) for left_item in left.items): + for right_item in right_rem: + if not any(is_same_type(right_item, left_item) for left_item in left_rem): return False return True diff --git a/mypy/subtypes.py b/mypy/subtypes.py index af35821d7ef4..d977a114bf2f 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -535,6 +535,20 @@ def visit_overloaded(self, left: Overloaded) -> bool: return False def visit_union_type(self, left: UnionType) -> bool: + if isinstance(self.right, Instance): + literal_types: Set[Instance] = set() + # avoid redundant check for union of literals + for item in left.relevant_items(): + item = get_proper_type(item) + lit_type = mypy.typeops.simple_literal_type(item) + if lit_type is not None: + if lit_type in literal_types: + continue + literal_types.add(lit_type) + item = lit_type + if not self._is_subtype(item, self.orig_right): + return False + return True return all(self._is_subtype(item, self.orig_right) for item in left.items) def visit_partial_type(self, left: PartialType) -> bool: @@ -1199,6 +1213,27 @@ def report(*args: Any) -> None: return applied +def try_restrict_literal_union(t: UnionType, s: Type) -> Optional[List[Type]]: + """Return the items of t, excluding any occurrence of s, if and only if + - t only contains simple literals + - s is a simple literal + + Otherwise, returns None + """ + ps = get_proper_type(s) + if not mypy.typeops.is_simple_literal(ps): + return None + + new_items: List[Type] = [] + for i in t.relevant_items(): + pi = get_proper_type(i) + if not mypy.typeops.is_simple_literal(pi): + return None + if pi != ps: + new_items.append(i) + return new_items + + def restrict_subtype_away(t: Type, s: Type, *, ignore_promotions: bool = False) -> Type: """Return t minus s for runtime type assertions. @@ -1212,10 +1247,14 @@ def restrict_subtype_away(t: Type, s: Type, *, ignore_promotions: bool = False) s = get_proper_type(s) if isinstance(t, UnionType): - new_items = [restrict_subtype_away(item, s, ignore_promotions=ignore_promotions) - for item in t.relevant_items() - if (isinstance(get_proper_type(item), AnyType) or - not covers_at_runtime(item, s, ignore_promotions))] + new_items = try_restrict_literal_union(t, s) + if new_items is None: + new_items = [ + restrict_subtype_away(item, s, ignore_promotions=ignore_promotions) + for item in t.relevant_items() + if (isinstance(get_proper_type(item), AnyType) or + not covers_at_runtime(item, s, ignore_promotions)) + ] return UnionType.make_union(new_items) elif covers_at_runtime(t, s, ignore_promotions): return UninhabitedType() @@ -1285,11 +1324,11 @@ def _is_proper_subtype(left: Type, right: Type, *, right = get_proper_type(right) if isinstance(right, UnionType) and not isinstance(left, UnionType): - return any([is_proper_subtype(orig_left, item, - ignore_promotions=ignore_promotions, - erase_instances=erase_instances, - keep_erased_types=keep_erased_types) - for item in right.items]) + return any(is_proper_subtype(orig_left, item, + ignore_promotions=ignore_promotions, + erase_instances=erase_instances, + keep_erased_types=keep_erased_types) + for item in right.items) return left.accept(ProperSubtypeVisitor(orig_right, ignore_promotions=ignore_promotions, erase_instances=erase_instances, @@ -1495,7 +1534,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: return False def visit_union_type(self, left: UnionType) -> bool: - return all([self._is_proper_subtype(item, self.orig_right) for item in left.items]) + return all(self._is_proper_subtype(item, self.orig_right) for item in left.items) def visit_partial_type(self, left: PartialType) -> bool: # TODO: What's the right thing to do here? diff --git a/mypy/typeops.py b/mypy/typeops.py index bb5d8c291b12..9eb2c9bee18f 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -318,6 +318,15 @@ def simple_literal_value_key(t: ProperType) -> Optional[Tuple[str, ...]]: return None +def simple_literal_type(t: ProperType) -> Optional[Instance]: + """Extract the underlying fallback Instance type for a simple Literal""" + if isinstance(t, Instance) and t.last_known_value is not None: + t = t.last_known_value + if isinstance(t, LiteralType): + return t.fallback + return None + + def is_simple_literal(t: ProperType) -> bool: """Fast way to check if simple_literal_value_key() would return a non-None value.""" if isinstance(t, LiteralType): From 9e9de71cc7d896829f503f6a35171d906eeef5da Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 18 Apr 2022 21:26:41 +0100 Subject: [PATCH 12/79] stubtest: error if type alias doesn't exist at runtime (#12608) Co-authored-by: hauntsaninja <> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/stubtest.py | 7 +++++-- mypy/test/teststubtest.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 7fa0f5937f99..d3bc40bc27e8 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -955,10 +955,13 @@ def verify_decorator( def verify_typealias( stub: nodes.TypeAlias, runtime: MaybeMissing[Any], object_path: List[str] ) -> Iterator[Error]: + stub_target = mypy.types.get_proper_type(stub.target) if isinstance(runtime, Missing): - # ignore type aliases that don't have a runtime counterpart + yield Error( + object_path, "is not present at runtime", stub, runtime, + stub_desc=f"Type alias for: {stub_target}" + ) return - stub_target = mypy.types.get_proper_type(stub.target) if isinstance(stub_target, mypy.types.Instance): yield from verify(stub_target.type, runtime, object_path) return diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 1cdcd34c666c..869d2e110a66 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -712,6 +712,18 @@ class Y: ... runtime="A = (int, str)", error="A", ) + # Error if an alias isn't present at runtime... + yield Case( + stub="B = str", + runtime="", + error="B" + ) + # ... but only if the alias isn't private + yield Case( + stub="_C = int", + runtime="", + error=None + ) @collect_cases def test_enum(self) -> Iterator[Case]: From cf6a48c865cc5aff23b08e651f41e1bf7b397f98 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 19 Apr 2022 04:36:20 +0200 Subject: [PATCH 13/79] Fix nested overload merging (#12607) Closes #12606 --- mypy/fastparse.py | 69 +++++++++---- test-data/unit/check-overloading.test | 139 ++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 18 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 077d287655fb..0b3322db2af3 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -496,18 +496,9 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: if_overload_name: Optional[str] = None if_block_with_overload: Optional[Block] = None if_unknown_truth_value: Optional[IfStmt] = None - if ( - isinstance(stmt, IfStmt) - and len(stmt.body[0].body) == 1 - and seen_unconditional_func_def is False - and ( - isinstance(stmt.body[0].body[0], (Decorator, OverloadedFuncDef)) - or current_overload_name is not None - and isinstance(stmt.body[0].body[0], FuncDef) - ) - ): + if isinstance(stmt, IfStmt) and seen_unconditional_func_def is False: # Check IfStmt block to determine if function overloads can be merged - if_overload_name = self._check_ifstmt_for_overloads(stmt) + if_overload_name = self._check_ifstmt_for_overloads(stmt, current_overload_name) if if_overload_name is not None: if_block_with_overload, if_unknown_truth_value = \ self._get_executable_if_block_with_overloads(stmt) @@ -553,8 +544,11 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: else: current_overload.append(last_if_overload) last_if_stmt, last_if_overload = None, None - if isinstance(if_block_with_overload.body[0], OverloadedFuncDef): - current_overload.extend(if_block_with_overload.body[0].items) + if isinstance(if_block_with_overload.body[-1], OverloadedFuncDef): + skipped_if_stmts.extend( + cast(List[IfStmt], if_block_with_overload.body[:-1]) + ) + current_overload.extend(if_block_with_overload.body[-1].items) else: current_overload.append( cast(Union[Decorator, FuncDef], if_block_with_overload.body[0]) @@ -600,9 +594,12 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: last_if_stmt = stmt last_if_stmt_overload_name = None if if_block_with_overload is not None: + skipped_if_stmts.extend( + cast(List[IfStmt], if_block_with_overload.body[:-1]) + ) last_if_overload = cast( Union[Decorator, FuncDef, OverloadedFuncDef], - if_block_with_overload.body[0] + if_block_with_overload.body[-1] ) last_if_unknown_truth_value = if_unknown_truth_value else: @@ -620,11 +617,15 @@ def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]: ret.append(current_overload[0]) elif len(current_overload) > 1: ret.append(OverloadedFuncDef(current_overload)) + elif last_if_overload is not None: + ret.append(last_if_overload) elif last_if_stmt is not None: ret.append(last_if_stmt) return ret - def _check_ifstmt_for_overloads(self, stmt: IfStmt) -> Optional[str]: + def _check_ifstmt_for_overloads( + self, stmt: IfStmt, current_overload_name: Optional[str] = None + ) -> Optional[str]: """Check if IfStmt contains only overloads with the same name. Return overload_name if found, None otherwise. """ @@ -632,11 +633,22 @@ def _check_ifstmt_for_overloads(self, stmt: IfStmt) -> Optional[str]: # Multiple overloads have already been merged as OverloadedFuncDef. if not ( len(stmt.body[0].body) == 1 - and isinstance(stmt.body[0].body[0], (Decorator, FuncDef, OverloadedFuncDef)) + and ( + isinstance(stmt.body[0].body[0], (Decorator, OverloadedFuncDef)) + or current_overload_name is not None + and isinstance(stmt.body[0].body[0], FuncDef) + ) + or len(stmt.body[0].body) > 1 + and isinstance(stmt.body[0].body[-1], OverloadedFuncDef) + and all( + self._is_stripped_if_stmt(if_stmt) + for if_stmt in stmt.body[0].body[:-1] + ) ): return None - overload_name = stmt.body[0].body[0].name + overload_name = cast( + Union[Decorator, FuncDef, OverloadedFuncDef], stmt.body[0].body[-1]).name if stmt.else_body is None: return overload_name @@ -649,7 +661,9 @@ def _check_ifstmt_for_overloads(self, stmt: IfStmt) -> Optional[str]: return overload_name if ( isinstance(stmt.else_body.body[0], IfStmt) - and self._check_ifstmt_for_overloads(stmt.else_body.body[0]) == overload_name + and self._check_ifstmt_for_overloads( + stmt.else_body.body[0], current_overload_name + ) == overload_name ): return overload_name @@ -704,6 +718,25 @@ def _strip_contents_from_if_stmt(self, stmt: IfStmt) -> None: else: stmt.else_body.body = [] + def _is_stripped_if_stmt(self, stmt: Statement) -> bool: + """Check stmt to make sure it is a stripped IfStmt. + + See also: _strip_contents_from_if_stmt + """ + if not isinstance(stmt, IfStmt): + return False + + if not (len(stmt.body) == 1 and len(stmt.body[0].body) == 0): + # Body not empty + return False + + if not stmt.else_body or len(stmt.else_body.body) == 0: + # No or empty else_body + return True + + # For elif, IfStmt are stored recursively in else_body + return self._is_stripped_if_stmt(stmt.else_body.body[0]) + def in_method_scope(self) -> bool: return self.class_and_function_stack[-2:] == ['C', 'F'] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index e2a87ea62a92..8259f2754bce 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -6367,3 +6367,142 @@ def g(x: int) -> str: ... def g(x: int = 0) -> int: # E: Overloaded function implementation cannot produce return type of signature 2 return x + +[case testOverloadIfNestedOk] +# flags: --always-true True --always-false False +from typing import overload + +class A: ... +class B: ... +class C: ... +class D: ... + +@overload +def f1(g: A) -> A: ... +if True: + @overload + def f1(g: B) -> B: ... + if True: + @overload + def f1(g: C) -> C: ... + @overload + def f1(g: D) -> D: ... +def f1(g): ... +reveal_type(f1(A())) # N: Revealed type is "__main__.A" +reveal_type(f1(B())) # N: Revealed type is "__main__.B" +reveal_type(f1(C())) # N: Revealed type is "__main__.C" +reveal_type(f1(D())) # N: Revealed type is "__main__.D" + +@overload +def f2(g: A) -> A: ... +if True: + @overload + def f2(g: B) -> B: ... + if True: + @overload + def f2(g: C) -> C: ... + if True: + @overload + def f2(g: D) -> D: ... +def f2(g): ... +reveal_type(f2(A())) # N: Revealed type is "__main__.A" +reveal_type(f2(B())) # N: Revealed type is "__main__.B" +reveal_type(f2(C())) # N: Revealed type is "__main__.C" +reveal_type(f2(D())) # N: Revealed type is "__main__.D" + +@overload +def f3(g: A) -> A: ... +if True: + if True: + @overload + def f3(g: B) -> B: ... + if True: + @overload + def f3(g: C) -> C: ... +def f3(g): ... +reveal_type(f3(A())) # N: Revealed type is "__main__.A" +reveal_type(f3(B())) # N: Revealed type is "__main__.B" +reveal_type(f3(C())) # N: Revealed type is "__main__.C" + +@overload +def f4(g: A) -> A: ... +if True: + if False: + @overload + def f4(g: B) -> B: ... + else: + @overload + def f4(g: C) -> C: ... +def f4(g): ... +reveal_type(f4(A())) # N: Revealed type is "__main__.A" +reveal_type(f4(B())) # E: No overload variant of "f4" matches argument type "B" \ + # N: Possible overload variants: \ + # N: def f4(g: A) -> A \ + # N: def f4(g: C) -> C \ + # N: Revealed type is "Any" +reveal_type(f4(C())) # N: Revealed type is "__main__.C" + +@overload +def f5(g: A) -> A: ... +if True: + if False: + @overload + def f5(g: B) -> B: ... + elif True: + @overload + def f5(g: C) -> C: ... +def f5(g): ... +reveal_type(f5(A())) # N: Revealed type is "__main__.A" +reveal_type(f5(B())) # E: No overload variant of "f5" matches argument type "B" \ + # N: Possible overload variants: \ + # N: def f5(g: A) -> A \ + # N: def f5(g: C) -> C \ + # N: Revealed type is "Any" +reveal_type(f5(C())) # N: Revealed type is "__main__.C" + +[case testOverloadIfNestedFailure] +# flags: --always-true True --always-false False +from typing import overload + +class A: ... +class B: ... +class C: ... +class D: ... + +@overload # E: Single overload definition, multiple required +def f1(g: A) -> A: ... +if True: + @overload # E: Single overload definition, multiple required + def f1(g: B) -> B: ... + if maybe_true: # E: Condition cannot be inferred, unable to merge overloads \ + # E: Name "maybe_true" is not defined + @overload + def f1(g: C) -> C: ... + @overload + def f1(g: D) -> D: ... +def f1(g): ... # E: Name "f1" already defined on line 9 + +@overload # E: Single overload definition, multiple required +def f2(g: A) -> A: ... +if True: + if False: + @overload + def f2(g: B) -> B: ... + elif maybe_true: # E: Name "maybe_true" is not defined + @overload # E: Single overload definition, multiple required + def f2(g: C) -> C: ... +def f2(g): ... # E: Name "f2" already defined on line 21 + +@overload # E: Single overload definition, multiple required +def f3(g: A) -> A: ... +if True: + @overload # E: Single overload definition, multiple required + def f3(g: B) -> B: ... + if True: + pass # Some other node + @overload # E: Name "f3" already defined on line 32 \ + # E: An overloaded function outside a stub file must have an implementation + def f3(g: C) -> C: ... + @overload + def f3(g: D) -> D: ... +def f3(g): ... # E: Name "f3" already defined on line 32 From 20b0b9b460cd11a4755f70ae08823fa6a8f5fbd4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 19 Apr 2022 11:50:25 +0100 Subject: [PATCH 14/79] Use class name as namespace for type variables (#12590) This avoids confusion between type variables of two classes, which can happen at least in some edge cases. Type variables are only the same if both the numeric id and namespace match (plus meta level). Fixes #12588 (though the textual presentation of type variables doesn't take the namespace into consideration yet). --- mypy/checkpattern.py | 3 +-- mypy/semanal.py | 3 ++- mypy/tvar_scope.py | 15 ++++++++++----- mypy/types.py | 16 +++++++++++---- test-data/unit/check-selftype.test | 31 +++++++++++++++++++++++++++++- 5 files changed, 55 insertions(+), 13 deletions(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 0fedec24cc37..e1d4f9fe285e 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -396,8 +396,7 @@ def visit_mapping_pattern(self, o: MappingPattern) -> PatternType: if is_subtype(current_type, mapping) and isinstance(current_type, Instance): mapping_inst = map_instance_to_supertype(current_type, mapping.type) dict_typeinfo = self.chk.lookup_typeinfo("builtins.dict") - dict_type = fill_typevars(dict_typeinfo) - rest_type = expand_type_by_instance(dict_type, mapping_inst) + rest_type = Instance(dict_typeinfo, mapping_inst.args) else: object_type = self.chk.named_type("builtins.object") rest_type = self.chk.named_generic_type("builtins.dict", diff --git a/mypy/semanal.py b/mypy/semanal.py index 1ec37309ce8e..5b6b7ebd78c0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1116,7 +1116,8 @@ def check_decorated_function_is_method(self, decorator: str, def visit_class_def(self, defn: ClassDef) -> None: self.statement = defn self.incomplete_type_stack.append(not defn.info) - with self.tvar_scope_frame(self.tvar_scope.class_frame()): + namespace = self.qualified_name(defn.name) + with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): self.analyze_class(defn) self.incomplete_type_stack.pop() diff --git a/mypy/tvar_scope.py b/mypy/tvar_scope.py index 0d8be7845e52..ac82a7708f0e 100644 --- a/mypy/tvar_scope.py +++ b/mypy/tvar_scope.py @@ -1,5 +1,5 @@ from typing import Optional, Dict, Union -from mypy.types import TypeVarLikeType, TypeVarType, ParamSpecType, ParamSpecFlavor +from mypy.types import TypeVarLikeType, TypeVarType, ParamSpecType, ParamSpecFlavor, TypeVarId from mypy.nodes import ParamSpecExpr, TypeVarExpr, TypeVarLikeExpr, SymbolTableNode @@ -12,7 +12,8 @@ class TypeVarLikeScope: def __init__(self, parent: 'Optional[TypeVarLikeScope]' = None, is_class_scope: bool = False, - prohibited: 'Optional[TypeVarLikeScope]' = None) -> None: + prohibited: 'Optional[TypeVarLikeScope]' = None, + namespace: str = '') -> None: """Initializer for TypeVarLikeScope Parameters: @@ -27,6 +28,7 @@ def __init__(self, self.class_id = 0 self.is_class_scope = is_class_scope self.prohibited = prohibited + self.namespace = namespace if parent is not None: self.func_id = parent.func_id self.class_id = parent.class_id @@ -51,22 +53,25 @@ def method_frame(self) -> 'TypeVarLikeScope': """A new scope frame for binding a method""" return TypeVarLikeScope(self, False, None) - def class_frame(self) -> 'TypeVarLikeScope': + def class_frame(self, namespace: str) -> 'TypeVarLikeScope': """A new scope frame for binding a class. Prohibits *this* class's tvars""" - return TypeVarLikeScope(self.get_function_scope(), True, self) + return TypeVarLikeScope(self.get_function_scope(), True, self, namespace=namespace) def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: if self.is_class_scope: self.class_id += 1 i = self.class_id + namespace = self.namespace else: self.func_id -= 1 i = self.func_id + # TODO: Consider also using namespaces for functions + namespace = '' if isinstance(tvar_expr, TypeVarExpr): tvar_def: TypeVarLikeType = TypeVarType( name, tvar_expr.fullname, - i, + TypeVarId(i, namespace=namespace), values=tvar_expr.values, upper_bound=tvar_expr.upper_bound, variance=tvar_expr.variance, diff --git a/mypy/types.py b/mypy/types.py index 213d8de7d8bb..49e0aa1d85ec 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -426,9 +426,15 @@ class TypeVarId: # Class variable used for allocating fresh ids for metavariables. next_raw_id: ClassVar[int] = 1 - def __init__(self, raw_id: int, meta_level: int = 0) -> None: + # Fullname of class (or potentially function in the future) which + # declares this type variable (not the fullname of the TypeVar + # definition!), or '' + namespace: str + + def __init__(self, raw_id: int, meta_level: int = 0, *, namespace: str = '') -> None: self.raw_id = raw_id self.meta_level = meta_level + self.namespace = namespace @staticmethod def new(meta_level: int) -> 'TypeVarId': @@ -442,7 +448,8 @@ def __repr__(self) -> str: def __eq__(self, other: object) -> bool: if isinstance(other, TypeVarId): return (self.raw_id == other.raw_id and - self.meta_level == other.meta_level) + self.meta_level == other.meta_level and + self.namespace == other.namespace) else: return False @@ -450,7 +457,7 @@ def __ne__(self, other: object) -> bool: return not (self == other) def __hash__(self) -> int: - return hash((self.raw_id, self.meta_level)) + return hash((self.raw_id, self.meta_level, self.namespace)) def is_meta_var(self) -> bool: return self.meta_level > 0 @@ -524,6 +531,7 @@ def serialize(self) -> JsonDict: 'name': self.name, 'fullname': self.fullname, 'id': self.id.raw_id, + 'namespace': self.id.namespace, 'values': [v.serialize() for v in self.values], 'upper_bound': self.upper_bound.serialize(), 'variance': self.variance, @@ -535,7 +543,7 @@ def deserialize(cls, data: JsonDict) -> 'TypeVarType': return TypeVarType( data['name'], data['fullname'], - data['id'], + TypeVarId(data['id'], namespace=data['namespace']), [deserialize_type(v) for v in data['values']], deserialize_type(data['upper_bound']), data['variance'], diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index b59c22dfae06..085c522c3013 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -893,11 +893,14 @@ from typing import Generic, TypeVar, Tuple T = TypeVar('T') S = TypeVar('S') U = TypeVar('U') +V = TypeVar('V') class C(Generic[T]): def magic(self: C[Tuple[S, U]]) -> Tuple[T, S, U]: ... -reveal_type(C[Tuple[int, str]]().magic()) # N: Revealed type is "Tuple[Tuple[builtins.int, builtins.str], builtins.int, builtins.str]" +class D(Generic[V]): + def f(self) -> None: + reveal_type(C[Tuple[V, str]]().magic()) # N: Revealed type is "Tuple[Tuple[V`1, builtins.str], V`1, builtins.str]" [builtins fixtures/tuple.pyi] [case testSelfTypeOnUnion] @@ -1167,3 +1170,29 @@ def build_wrapper_non_gen(descriptor: Descriptor[int]) -> BaseWrapper[str]: def build_sub_wrapper_non_gen(descriptor: Descriptor[int]) -> SubWrapper[str]: return SubWrapper.create_wrapper(descriptor) # E: Argument 1 to "create_wrapper" of "BaseWrapper" has incompatible type "Descriptor[int]"; expected "Descriptor[str]" [builtins fixtures/classmethod.pyi] + +[case testSelfTypeInGenericClassUsedFromAnotherGenericClass1] +from typing import TypeVar, Generic, Iterator, List, Tuple + +_T_co = TypeVar("_T_co", covariant=True) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +S = TypeVar("S") + +class Z(Iterator[_T_co]): + def __new__(cls, + __iter1: List[_T1], + __iter2: List[_T2]) -> Z[Tuple[_T1, _T2]]: ... + def __iter__(self: S) -> S: ... + def __next__(self) -> _T_co: ... + +T = TypeVar('T') + +class C(Generic[T]): + a: List[T] + b: List[str] + + def f(self) -> None: + for x, y in Z(self.a, self.b): + reveal_type((x, y)) # N: Revealed type is "Tuple[T`1, builtins.str]" +[builtins fixtures/tuple.pyi] From cf146f4b5a578597ea5d81a31476fbf5b6a2ef62 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Tue, 19 Apr 2022 21:00:37 +0100 Subject: [PATCH 15/79] Add test case for recently fixed `enumerate` regression (#12627) --- test-data/unit/check-selftype.test | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 085c522c3013..ef3d5289ddfd 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1196,3 +1196,27 @@ class C(Generic[T]): for x, y in Z(self.a, self.b): reveal_type((x, y)) # N: Revealed type is "Tuple[T`1, builtins.str]" [builtins fixtures/tuple.pyi] + +[case testEnumerateReturningSelfFromIter] +from typing import Generic, Iterable, Iterator, TypeVar, Tuple + +T = TypeVar("T") +KT = TypeVar("KT") +VT = TypeVar("VT") +Self = TypeVar("Self") + +class enumerate(Iterator[Tuple[int, T]], Generic[T]): + def __init__(self, iterable: Iterable[T]) -> None: ... + def __iter__(self: Self) -> Self: ... + def __next__(self) -> Tuple[int, T]: ... + +class Dict(Generic[KT, VT]): + def update(self, __m: Iterable[Tuple[KT, VT]]) -> None: ... + +class ThingCollection(Generic[T]): + collection: Iterable[Tuple[float, T]] + index: Dict[int, T] + + def do_thing(self) -> None: + self.index.update((idx, c) for idx, (k, c) in enumerate(self.collection)) +[builtins fixtures/tuple.pyi] From 0bcca591b49cbc9349d55ba4d38b85f9bc222566 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 20 Apr 2022 09:11:30 +0100 Subject: [PATCH 16/79] Add overlapping type variables test case (#12623) This was originally written by @A5rocks in #11657. Related to #12590. --- test-data/unit/check-generics.test | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 9c5f3a332dab..6a89f6fb200c 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2504,3 +2504,23 @@ b: I[I[Any]] reveal_type([a, b]) # N: Revealed type is "builtins.list[__main__.I[__main__.I[Any]]]" reveal_type([b, a]) # N: Revealed type is "builtins.list[__main__.I[__main__.I[Any]]]" [builtins fixtures/list.pyi] + +[case testOverlappingTypeVarIds] +from typing import TypeVar, Generic + +class A: ... +class B: ... + +T = TypeVar("T", bound=A) +V = TypeVar("V", bound=B) +S = TypeVar("S") + +class Whatever(Generic[T]): + def something(self: S) -> S: + return self + +# the "V" here had the same id as "T" and so mypy used to think it could expand one into another. +# this test is here to make sure that doesn't happen! +class WhateverPartTwo(Whatever[A], Generic[V]): + def something(self: S) -> S: + return self From 8b1a8109796c0a32d02de147d3371d381593fbe0 Mon Sep 17 00:00:00 2001 From: Hugues Date: Wed, 20 Apr 2022 04:47:33 -0700 Subject: [PATCH 17/79] make_simplified_union: simpler and faster (#12630) Recent attempts at speedup introduced some convoluted logic that both reduced accuracy and caused performance regressions. Fix this and add more comments to clarify the reasoning behind the optimization. --- mypy/typeops.py | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 9eb2c9bee18f..e9127aee0060 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -410,26 +410,29 @@ def _remove_redundant_union_items(items: List[ProperType], keep_erased: bool) -> # Keep track of the truishness info for deleted subtypes which can be relevant cbt = cbf = False - num_items = len(items) for j, tj in enumerate(items): - if i != j: - # NB: The first check below is an optimization to - # avoid very expensive computations with large - # unions involving literals. We approximate the - # results for unions with many items. This is - # "fine" since simplifying these union items is - # (almost) always optional. - if ( - (num_items < 5 - or is_likely_literal_supertype(item) - or not is_simple_literal(tj)) - and is_proper_subtype(tj, item, keep_erased_types=keep_erased) - and is_redundant_literal_instance(item, tj) # XXX? - ): - # We found a redundant item in the union. - removed.add(j) - cbt = cbt or tj.can_be_true - cbf = cbf or tj.can_be_false + if ( + i == j + # avoid further checks if this item was already marked redundant. + or j in removed + # if the current item is a simple literal then this simplification loop can + # safely skip all other simple literals as two literals will only ever be + # subtypes of each other if they are equal, which is already handled above. + # However, if the current item is not a literal, it might plausibly be a + # supertype of other literals in the union, so we must check them again. + # This is an important optimization as is_proper_subtype is pretty expensive. + or (k is not None and is_simple_literal(tj)) + ): + continue + # actual redundancy checks + if ( + is_redundant_literal_instance(item, tj) # XXX? + and is_proper_subtype(tj, item, keep_erased_types=keep_erased) + ): + # We found a redundant item in the union. + removed.add(j) + cbt = cbt or tj.can_be_true + cbf = cbf or tj.can_be_false # if deleted subtypes had more general truthiness, use that if not item.can_be_true and cbt: items[i] = true_or_false(item) @@ -439,12 +442,6 @@ def _remove_redundant_union_items(items: List[ProperType], keep_erased: bool) -> return [items[i] for i in range(len(items)) if i not in removed] -def is_likely_literal_supertype(t: ProperType) -> bool: - """Is the type likely to cause simplification of literal types in unions?""" - return isinstance(t, Instance) and t.type.fullname in ('builtins.object', - 'builtins.str') - - def _get_type_special_method_bool_ret_type(t: Type) -> Optional[Type]: t = get_proper_type(t) From 50213b520b131fc1dd037dfcc1938beba1f4e177 Mon Sep 17 00:00:00 2001 From: jhance Date: Wed, 20 Apr 2022 12:56:02 -0700 Subject: [PATCH 18/79] Add TypeVarTupleExpr node (#12481) This adds minimal support for a TypeVarTupleExpr node, gated behind the flag to enable incomplete features. It is modeled after paramspec, including the part where we don't support the various arguments that have no behavior defined in PEP646. We also include TypeVarTuple in the typing_extensions stubs for test data and add some very basic semanal tests to verify the basic things work. --- mypy/checkexpr.py | 5 ++- mypy/literals.py | 5 ++- mypy/nodes.py | 33 ++++++++++++++- mypy/semanal.py | 41 ++++++++++++++++++- mypy/strconv.py | 12 ++++++ mypy/test/testtransform.py | 5 ++- mypy/treetransform.py | 7 +++- mypy/visitor.py | 7 ++++ mypyc/irbuild/visitor.py | 5 ++- test-data/unit/lib-stub/typing_extensions.pyi | 1 + test-data/unit/semanal-errors.test | 10 +++++ test-data/unit/semanal-types.test | 10 +++++ 12 files changed, 134 insertions(+), 7 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 7383a2b69610..c6f4d24c1815 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -32,7 +32,7 @@ DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, AwaitExpr, YieldExpr, YieldFromExpr, TypedDictExpr, PromoteExpr, NewTypeExpr, NamedTupleExpr, TypeVarExpr, TypeAliasExpr, BackquoteExpr, EnumCallExpr, TypeAlias, SymbolNode, PlaceholderNode, - ParamSpecExpr, + ParamSpecExpr, TypeVarTupleExpr, ArgKind, ARG_POS, ARG_NAMED, ARG_STAR, ARG_STAR2, LITERAL_TYPE, REVEAL_TYPE, ) from mypy.literals import literal @@ -4186,6 +4186,9 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> Type: def visit_paramspec_expr(self, e: ParamSpecExpr) -> Type: return AnyType(TypeOfAny.special_form) + def visit_type_var_tuple_expr(self, e: TypeVarTupleExpr) -> Type: + return AnyType(TypeOfAny.special_form) + def visit_newtype_expr(self, e: NewTypeExpr) -> Type: return AnyType(TypeOfAny.special_form) diff --git a/mypy/literals.py b/mypy/literals.py index b11c07d91a91..e20e37412ab2 100644 --- a/mypy/literals.py +++ b/mypy/literals.py @@ -9,7 +9,7 @@ TypeApplication, LambdaExpr, ListComprehension, SetComprehension, DictionaryComprehension, GeneratorExpr, BackquoteExpr, TypeVarExpr, TypeAliasExpr, NamedTupleExpr, EnumCallExpr, TypedDictExpr, NewTypeExpr, PromoteExpr, AwaitExpr, TempNode, AssignmentExpr, ParamSpecExpr, - AssertTypeExpr, + AssertTypeExpr, TypeVarTupleExpr, ) from mypy.visitor import ExpressionVisitor @@ -224,6 +224,9 @@ def visit_type_var_expr(self, e: TypeVarExpr) -> None: def visit_paramspec_expr(self, e: ParamSpecExpr) -> None: return None + def visit_type_var_tuple_expr(self, e: TypeVarTupleExpr) -> None: + return None + def visit_type_alias_expr(self, e: TypeAliasExpr) -> None: return None diff --git a/mypy/nodes.py b/mypy/nodes.py index 30bb2c6aa10a..f25d3abab7bc 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2233,7 +2233,10 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: class TypeVarLikeExpr(SymbolNode, Expression): - """Base class for TypeVarExpr and ParamSpecExpr.""" + """Base class for TypeVarExpr, ParamSpecExpr and TypeVarTupleExpr. + + Note that they are constructed by the semantic analyzer. + """ __slots__ = ('_name', '_fullname', 'upper_bound', 'variance') @@ -2339,6 +2342,34 @@ def deserialize(cls, data: JsonDict) -> 'ParamSpecExpr': ) +class TypeVarTupleExpr(TypeVarLikeExpr): + """Type variable tuple expression TypeVarTuple(...).""" + + __slots__ = () + + def accept(self, visitor: ExpressionVisitor[T]) -> T: + return visitor.visit_type_var_tuple_expr(self) + + def serialize(self) -> JsonDict: + return { + '.class': 'TypeVarTupleExpr', + 'name': self._name, + 'fullname': self._fullname, + 'upper_bound': self.upper_bound.serialize(), + 'variance': self.variance, + } + + @classmethod + def deserialize(cls, data: JsonDict) -> 'TypeVarTupleExpr': + assert data['.class'] == 'TypeVarTupleExpr' + return TypeVarTupleExpr( + data['name'], + data['fullname'], + mypy.types.deserialize_type(data['upper_bound']), + data['variance'] + ) + + class TypeAliasExpr(Expression): """Type alias expression (rvalue).""" diff --git a/mypy/semanal.py b/mypy/semanal.py index 5b6b7ebd78c0..985d5217cc08 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -78,7 +78,7 @@ typing_extensions_aliases, EnumCallExpr, RUNTIME_PROTOCOL_DECOS, FakeExpression, Statement, AssignmentExpr, ParamSpecExpr, EllipsisExpr, TypeVarLikeExpr, implicit_module_attrs, - MatchStmt, FuncBase + MatchStmt, FuncBase, TypeVarTupleExpr ) from mypy.patterns import ( AsPattern, OrPattern, ValuePattern, SequencePattern, @@ -2074,6 +2074,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: special_form = True elif self.process_paramspec_declaration(s): special_form = True + elif self.process_typevartuple_declaration(s): + special_form = True # * type constructors elif self.analyze_namedtuple_assign(s): special_form = True @@ -3332,6 +3334,43 @@ def process_paramspec_declaration(self, s: AssignmentStmt) -> bool: self.add_symbol(name, call.analyzed, s) return True + def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: + """Checks if s declares a TypeVarTuple; if yes, store it in symbol table. + + Return True if this looks like a TypeVarTuple (maybe with errors), otherwise return False. + """ + call = self.get_typevarlike_declaration( + s, ("typing_extensions.TypeVarTuple", "typing.TypeVarTuple") + ) + if not call: + return False + + if len(call.args) > 1: + self.fail( + "Only the first argument to TypeVarTuple has defined semantics", + s, + ) + + if not self.options.enable_incomplete_features: + self.fail('"TypeVarTuple" is not supported by mypy yet', s) + return False + + name = self.extract_typevarlike_name(s, call) + if name is None: + return False + + # PEP 646 does not specify the behavior of variance, constraints, or bounds. + if not call.analyzed: + typevartuple_var = TypeVarTupleExpr( + name, self.qualified_name(name), self.object_type(), INVARIANT + ) + typevartuple_var.line = call.line + call.analyzed = typevartuple_var + else: + assert isinstance(call.analyzed, TypeVarTupleExpr) + self.add_symbol(name, call.analyzed, s) + return True + def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance, line: int) -> TypeInfo: diff --git a/mypy/strconv.py b/mypy/strconv.py index 46ac4b10363c..1a08423b4164 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -496,6 +496,18 @@ def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> str: a += ['UpperBound({})'.format(o.upper_bound)] return self.dump(a, o) + def visit_type_var_tuple_expr(self, o: 'mypy.nodes.TypeVarTupleExpr') -> str: + import mypy.types + + a: List[Any] = [] + if o.variance == mypy.nodes.COVARIANT: + a += ['Variance(COVARIANT)'] + if o.variance == mypy.nodes.CONTRAVARIANT: + a += ['Variance(CONTRAVARIANT)'] + if not mypy.types.is_named_instance(o.upper_bound, 'builtins.object'): + a += ['UpperBound({})'.format(o.upper_bound)] + return self.dump(a, o) + def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> str: return 'TypeAliasExpr({})'.format(o.type) diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index d884fe9137ab..e1e3b6ab63ed 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -38,6 +38,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None: options = parse_options(src, testcase, 1) options.use_builtins_fixtures = True options.semantic_analysis_only = True + options.enable_incomplete_features = True options.show_traceback = True result = build.build(sources=[BuildSource('main', None, src)], options=options, @@ -54,8 +55,10 @@ def test_transform(testcase: DataDrivenTestCase) -> None: # path. # TODO the test is not reliable if (not f.path.endswith((os.sep + 'builtins.pyi', + 'typing_extensions.pyi', 'typing.pyi', - 'abc.pyi')) + 'abc.pyi', + 'sys.pyi')) and not os.path.basename(f.path).startswith('_') and not os.path.splitext( os.path.basename(f.path))[0].endswith('_')): diff --git a/mypy/treetransform.py b/mypy/treetransform.py index 62d5f6d72cbc..0bc72274354a 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -20,7 +20,7 @@ YieldFromExpr, NamedTupleExpr, TypedDictExpr, NonlocalDecl, SetComprehension, DictionaryComprehension, ComplexExpr, TypeAliasExpr, EllipsisExpr, YieldExpr, ExecStmt, Argument, BackquoteExpr, AwaitExpr, AssignmentExpr, - OverloadPart, EnumCallExpr, REVEAL_TYPE, GDEF + OverloadPart, EnumCallExpr, REVEAL_TYPE, GDEF, TypeVarTupleExpr ) from mypy.types import Type, FunctionLike, ProperType from mypy.traverser import TraverserVisitor @@ -515,6 +515,11 @@ def visit_paramspec_expr(self, node: ParamSpecExpr) -> ParamSpecExpr: node.name, node.fullname, self.type(node.upper_bound), variance=node.variance ) + def visit_type_var_tuple_expr(self, node: TypeVarTupleExpr) -> TypeVarTupleExpr: + return TypeVarTupleExpr( + node.name, node.fullname, self.type(node.upper_bound), variance=node.variance + ) + def visit_type_alias_expr(self, node: TypeAliasExpr) -> TypeAliasExpr: return TypeAliasExpr(node.node) diff --git a/mypy/visitor.py b/mypy/visitor.py index 7339111c7a05..94fde0b11319 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -165,6 +165,10 @@ def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> T: def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: pass + @abstractmethod + def visit_type_var_tuple_expr(self, o: 'mypy.nodes.TypeVarTupleExpr') -> T: + pass + @abstractmethod def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> T: pass @@ -590,6 +594,9 @@ def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> T: def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> T: pass + def visit_type_var_tuple_expr(self, o: 'mypy.nodes.TypeVarTupleExpr') -> T: + pass + def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> T: pass diff --git a/mypyc/irbuild/visitor.py b/mypyc/irbuild/visitor.py index 3a1883cca50b..15ac08d9c973 100644 --- a/mypyc/irbuild/visitor.py +++ b/mypyc/irbuild/visitor.py @@ -17,7 +17,7 @@ NamedTupleExpr, NewTypeExpr, NonlocalDecl, OverloadedFuncDef, PrintStmt, RaiseStmt, RevealExpr, SetExpr, SliceExpr, StarExpr, SuperExpr, TryStmt, TypeAliasExpr, TypeApplication, TypeVarExpr, TypedDictExpr, UnicodeExpr, WithStmt, YieldFromExpr, YieldExpr, ParamSpecExpr, - MatchStmt + MatchStmt, TypeVarTupleExpr ) from mypyc.ir.ops import Value @@ -315,6 +315,9 @@ def visit_type_var_expr(self, o: TypeVarExpr) -> Value: def visit_paramspec_expr(self, o: ParamSpecExpr) -> Value: assert False, "can't compile analysis-only expressions" + def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr) -> Value: + assert False, "can't compile analysis-only expressions" + def visit_typeddict_expr(self, o: TypedDictExpr) -> Value: assert False, "can't compile analysis-only expressions" diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index 95f45f3b8947..d4c3244cf083 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -29,6 +29,7 @@ TypeAlias: _SpecialForm TypeGuard: _SpecialForm Never: _SpecialForm +TypeVarTuple: _SpecialForm Unpack: _SpecialForm # Fallback type for all typed dicts (does not exist at runtime). diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index ea937b8678f1..5a1c48772190 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1460,3 +1460,13 @@ heterogenous_tuple: Tuple[Unpack[Tuple[int, str]]] homogenous_tuple: Tuple[Unpack[Tuple[int, ...]]] bad: Tuple[Unpack[int]] # E: builtins.int cannot be unpacked (must be tuple or TypeVarTuple) [builtins fixtures/tuple.pyi] + +[case testTypeVarTuple] +from typing_extensions import TypeVarTuple + +TVariadic = TypeVarTuple('TVariadic') +TP = TypeVarTuple('?') # E: String argument 1 "?" to TypeVarTuple(...) does not match variable name "TP" +TP2: int = TypeVarTuple('TP2') # E: Cannot declare the type of a TypeVar or similar construct +TP3 = TypeVarTuple() # E: Too few arguments for TypeVarTuple() +TP4 = TypeVarTuple('TP4', 'TP4') # E: Only the first argument to TypeVarTuple has defined semantics +TP5 = TypeVarTuple(t='TP5') # E: TypeVarTuple() expects a string literal as first argument diff --git a/test-data/unit/semanal-types.test b/test-data/unit/semanal-types.test index 3ce289b52ae2..f2cd737b1a6c 100644 --- a/test-data/unit/semanal-types.test +++ b/test-data/unit/semanal-types.test @@ -1548,3 +1548,13 @@ MypyFile:1( AssignmentStmt:2( NameExpr(P* [__main__.P]) ParamSpecExpr:2())) + +[case testTypeVarTuple] +from typing_extensions import TypeVarTuple +TV = TypeVarTuple("TV") +[out] +MypyFile:1( + ImportFrom:1(typing_extensions, [TypeVarTuple]) + AssignmentStmt:2( + NameExpr(TV* [__main__.TV]) + TypeVarTupleExpr:2())) From c56046c5d963b501b69cb76221189aa4cf5ccbac Mon Sep 17 00:00:00 2001 From: 97littleleaf11 <11172084+97littleleaf11@users.noreply.github.com> Date: Thu, 21 Apr 2022 13:27:38 +0800 Subject: [PATCH 19/79] Fix typos (#12635) Improve docs and fix typos --- mypy/typeops.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index e9127aee0060..dbfeebe42f14 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -107,7 +107,7 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, explicit_type = init_ret_type if is_new else orig_self_type if ( isinstance(explicit_type, (Instance, TupleType)) - # We have to skip protocols, because it can can be a subtype of a return type + # We have to skip protocols, because it can be a subtype of a return type # by accident. Like `Hashable` is a subtype of `object`. See #11799 and isinstance(default_ret_type, Instance) and not default_ret_type.type.is_protocol @@ -354,10 +354,17 @@ def make_simplified_union(items: Sequence[Type], Note: This must NOT be used during semantic analysis, since TypeInfos may not be fully initialized. + The keep_erased flag is used for type inference against union types containing type variables. If set to True, keep all ErasedType items. + + The contract_literals flag indicates whether we need to contract literal types + back into a sum type. Set it to False when called by try_expanding_sum_type_ + to_union(). """ items = get_proper_types(items) + + # Step 1: expand all nested unions while any(isinstance(typ, UnionType) for typ in items): all_items: List[ProperType] = [] for typ in items: @@ -367,10 +374,11 @@ def make_simplified_union(items: Sequence[Type], all_items.append(typ) items = all_items + # Step 2: remove redundant unions simplified_set = _remove_redundant_union_items(items, keep_erased) - # If more than one literal exists in the union, try to simplify - if (contract_literals and sum(isinstance(item, LiteralType) for item in simplified_set) > 1): + # Step 3: If more than one literal exists in the union, try to simplify + if contract_literals and sum(isinstance(item, LiteralType) for item in simplified_set) > 1: simplified_set = try_contracting_literals_in_union(simplified_set) return UnionType.make_union(simplified_set, line, column) @@ -384,7 +392,7 @@ def _remove_redundant_union_items(items: List[ProperType], keep_erased: bool) -> # NB: having a separate fast path for Union of Literal and slow path for other things # would arguably be cleaner, however it breaks down when simplifying the Union of two - # different enum types as try_expanding_enum_to_union works recursively and will + # different enum types as try_expanding_sum_type_to_union works recursively and will # trigger intermediate simplifications that would render the fast path useless for i, item in enumerate(items): if i in removed: @@ -408,7 +416,7 @@ def _remove_redundant_union_items(items: List[ProperType], keep_erased: bool) -> if safe_skip: continue - # Keep track of the truishness info for deleted subtypes which can be relevant + # Keep track of the truthiness info for deleted subtypes which can be relevant cbt = cbf = False for j, tj in enumerate(items): if ( @@ -609,7 +617,7 @@ def try_getting_str_literals(expr: Expression, typ: Type) -> Optional[List[str]] Otherwise, returns None. Specifically, this function is guaranteed to return a list with - one or more strings if one one the following is true: + one or more strings if one of the following is true: 1. 'expr' is a StrExpr 2. 'typ' is a LiteralType containing a string @@ -651,7 +659,7 @@ def try_getting_literals_from_type(typ: Type, target_literal_type: TypingType[T], target_fullname: str) -> Optional[List[T]]: """If the given expression or type corresponds to a Literal or - union of Literals where the underlying values corresponds to the given + union of Literals where the underlying values correspond to the given target type, returns a list of those underlying values. Otherwise, returns None. """ From a16c414809714c2872ee9a063c28cde6d4398021 Mon Sep 17 00:00:00 2001 From: jhance Date: Thu, 21 Apr 2022 01:57:29 -0700 Subject: [PATCH 20/79] Add TypeVarTupleType type node (#12632) This adds the TypeVarTupleType type node and basic semanal/glue. Type checking involving it will be added in a subsequent PR to keep each PR smaller. This PR is mostly consisting of modifying all the visitors, but not all of them are implemented. --- mypy/constraints.py | 5 +++- mypy/erasetype.py | 8 ++++-- mypy/expandtype.py | 5 +++- mypy/fixup.py | 5 +++- mypy/indirection.py | 3 ++ mypy/join.py | 7 ++++- mypy/meet.py | 8 +++++- mypy/sametypes.py | 7 ++++- mypy/semanal_typeargs.py | 9 +++++- mypy/server/astdiff.py | 8 +++++- mypy/server/astmerge.py | 5 +++- mypy/server/deps.py | 9 +++++- mypy/subtypes.py | 20 ++++++++++++- mypy/tvar_scope.py | 17 +++++++++-- mypy/type_visitor.py | 13 ++++++++- mypy/typeanal.py | 30 ++++++++++++++++++-- mypy/types.py | 45 ++++++++++++++++++++++++++++++ mypy/typetraverser.py | 5 +++- test-data/unit/semanal-errors.test | 5 +++- 19 files changed, 193 insertions(+), 21 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index b7ed1492e5f3..06feddc0d3ce 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -8,7 +8,7 @@ TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, TypeVarId, TypeQuery, is_named_instance, TypeOfAny, LiteralType, ProperType, ParamSpecType, get_proper_type, TypeAliasType, is_union_with_any, - UnpackType, callable_with_ellipsis, Parameters, TUPLE_LIKE_INSTANCE_NAMES, + UnpackType, callable_with_ellipsis, Parameters, TUPLE_LIKE_INSTANCE_NAMES, TypeVarTupleType, ) from mypy.maptype import map_instance_to_supertype import mypy.subtypes @@ -403,6 +403,9 @@ def visit_param_spec(self, template: ParamSpecType) -> List[Constraint]: # Can't infer ParamSpecs from component values (only via Callable[P, T]). return [] + def visit_type_var_tuple(self, template: TypeVarTupleType) -> List[Constraint]: + raise NotImplementedError + def visit_unpack_type(self, template: UnpackType) -> List[Constraint]: raise NotImplementedError diff --git a/mypy/erasetype.py b/mypy/erasetype.py index ff0ef6c0784e..21ca5771b32e 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -4,7 +4,8 @@ Type, TypeVisitor, UnboundType, AnyType, NoneType, TypeVarId, Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, TypeTranslator, UninhabitedType, TypeType, TypeOfAny, LiteralType, ProperType, - get_proper_type, get_proper_types, TypeAliasType, ParamSpecType, Parameters, UnpackType + get_proper_type, get_proper_types, TypeAliasType, ParamSpecType, Parameters, UnpackType, + TypeVarTupleType ) from mypy.nodes import ARG_STAR, ARG_STAR2 @@ -62,8 +63,11 @@ def visit_param_spec(self, t: ParamSpecType) -> ProperType: def visit_parameters(self, t: Parameters) -> ProperType: raise RuntimeError("Parameters should have been bound to a class") + def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: + return AnyType(TypeOfAny.special_form) + def visit_unpack_type(self, t: UnpackType) -> ProperType: - raise NotImplementedError + return AnyType(TypeOfAny.special_form) def visit_callable_type(self, t: CallableType) -> ProperType: # We must preserve the fallback type for overload resolution to work. diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 39606c263f6b..eef841c9387e 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -6,7 +6,7 @@ ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, TypeVarId, FunctionLike, TypeVarType, LiteralType, get_proper_type, ProperType, TypeAliasType, ParamSpecType, TypeVarLikeType, Parameters, ParamSpecFlavor, - UnpackType + UnpackType, TypeVarTupleType ) @@ -131,6 +131,9 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: # TODO: should this branch be removed? better not to fail silently return repl + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + raise NotImplementedError + def visit_unpack_type(self, t: UnpackType) -> Type: raise NotImplementedError diff --git a/mypy/fixup.py b/mypy/fixup.py index 302bd38097b3..11f07b1d4655 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -11,7 +11,7 @@ CallableType, Instance, Overloaded, TupleType, TypedDictType, TypeVarType, UnboundType, UnionType, TypeVisitor, LiteralType, TypeType, NOT_READY, TypeAliasType, AnyType, TypeOfAny, ParamSpecType, - Parameters, UnpackType, + Parameters, UnpackType, TypeVarTupleType ) from mypy.visitor import NodeVisitor from mypy.lookup import lookup_fully_qualified @@ -252,6 +252,9 @@ def visit_type_var(self, tvt: TypeVarType) -> None: def visit_param_spec(self, p: ParamSpecType) -> None: p.upper_bound.accept(self) + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + t.upper_bound.accept(self) + def visit_unpack_type(self, u: UnpackType) -> None: u.type.accept(self) diff --git a/mypy/indirection.py b/mypy/indirection.py index 0888c2afad20..56c1f97928f2 100644 --- a/mypy/indirection.py +++ b/mypy/indirection.py @@ -67,6 +67,9 @@ def visit_type_var(self, t: types.TypeVarType) -> Set[str]: def visit_param_spec(self, t: types.ParamSpecType) -> Set[str]: return set() + def visit_type_var_tuple(self, t: types.TypeVarTupleType) -> Set[str]: + return self._visit(t.upper_bound) + def visit_unpack_type(self, t: types.UnpackType) -> Set[str]: return t.type.accept(self) diff --git a/mypy/join.py b/mypy/join.py index 7e8fd5d61491..78f280411622 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -8,7 +8,7 @@ TupleType, TypedDictType, ErasedType, UnionType, FunctionLike, Overloaded, LiteralType, PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, get_proper_type, ProperType, get_proper_types, TypeAliasType, PlaceholderType, ParamSpecType, Parameters, - UnpackType + UnpackType, TypeVarTupleType, ) from mypy.maptype import map_instance_to_supertype from mypy.subtypes import ( @@ -257,6 +257,11 @@ def visit_param_spec(self, t: ParamSpecType) -> ProperType: return t return self.default(self.s) + def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: + if self.s == t: + return t + return self.default(self.s) + def visit_unpack_type(self, t: UnpackType) -> UnpackType: raise NotImplementedError diff --git a/mypy/meet.py b/mypy/meet.py index 5ee64416490d..ad7725182838 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -6,7 +6,7 @@ TupleType, TypedDictType, ErasedType, UnionType, PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, Overloaded, FunctionLike, LiteralType, ProperType, get_proper_type, get_proper_types, TypeAliasType, TypeGuardedType, - ParamSpecType, Parameters, UnpackType, + ParamSpecType, Parameters, UnpackType, TypeVarTupleType, ) from mypy.subtypes import is_equivalent, is_subtype, is_callable_compatible, is_proper_subtype from mypy.erasetype import erase_type @@ -536,6 +536,12 @@ def visit_param_spec(self, t: ParamSpecType) -> ProperType: else: return self.default(self.s) + def visit_type_var_tuple(self, t: TypeVarTupleType) -> ProperType: + if self.s == t: + return self.s + else: + return self.default(self.s) + def visit_unpack_type(self, t: UnpackType) -> ProperType: raise NotImplementedError diff --git a/mypy/sametypes.py b/mypy/sametypes.py index 1c22c32f8b06..4fbc9bfc4801 100644 --- a/mypy/sametypes.py +++ b/mypy/sametypes.py @@ -4,7 +4,8 @@ Type, UnboundType, AnyType, NoneType, TupleType, TypedDictType, UnionType, CallableType, TypeVarType, Instance, TypeVisitor, ErasedType, Overloaded, PartialType, DeletedType, UninhabitedType, TypeType, LiteralType, - ProperType, get_proper_type, TypeAliasType, ParamSpecType, Parameters, UnpackType + ProperType, get_proper_type, TypeAliasType, ParamSpecType, Parameters, + UnpackType, TypeVarTupleType, ) from mypy.typeops import tuple_fallback, make_simplified_union, is_simple_literal @@ -118,6 +119,10 @@ def visit_param_spec(self, left: ParamSpecType) -> bool: return (isinstance(self.right, ParamSpecType) and left.id == self.right.id and left.flavor == self.right.flavor) + def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: + return (isinstance(self.right, TypeVarTupleType) and + left.id == self.right.id) + def visit_unpack_type(self, left: UnpackType) -> bool: return (isinstance(self.right, UnpackType) and is_same_type(left.type, self.right.type)) diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index 0e885f46a7a5..f8e14d28661a 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -10,7 +10,7 @@ from mypy.nodes import TypeInfo, Context, MypyFile, FuncItem, ClassDef, Block, FakeInfo from mypy.types import ( Type, Instance, TypeVarType, AnyType, get_proper_types, TypeAliasType, ParamSpecType, - UnpackType, TupleType, get_proper_type + UnpackType, TupleType, TypeVarTupleType, TypeOfAny, get_proper_type ) from mypy.mixedtraverser import MixedTraverserVisitor from mypy.subtypes import is_subtype @@ -99,8 +99,15 @@ def visit_unpack_type(self, typ: UnpackType) -> None: proper_type = get_proper_type(typ.type) if isinstance(proper_type, TupleType): return + if isinstance(proper_type, TypeVarTupleType): + return if isinstance(proper_type, Instance) and proper_type.type.fullname == "builtins.tuple": return + if isinstance(proper_type, AnyType) and proper_type.type_of_any == TypeOfAny.from_error: + return + + # TODO: Infer something when it can't be unpacked to allow rest of + # typechecking to work. self.fail(message_registry.INVALID_UNPACK.format(proper_type), typ) def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str, diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index 437cb777c8d5..f41a54752fee 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -60,7 +60,7 @@ class level -- these are handled at attribute level (say, 'mod.Cls.method' Type, TypeVisitor, UnboundType, AnyType, NoneType, UninhabitedType, ErasedType, DeletedType, Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded, PartialType, TypeType, LiteralType, TypeAliasType, ParamSpecType, - Parameters, UnpackType, + Parameters, UnpackType, TypeVarTupleType, ) from mypy.util import get_prefix @@ -318,6 +318,12 @@ def visit_param_spec(self, typ: ParamSpecType) -> SnapshotItem: typ.flavor, snapshot_type(typ.upper_bound)) + def visit_type_var_tuple(self, typ: TypeVarTupleType) -> SnapshotItem: + return ('TypeVarTupleType', + typ.id.raw_id, + typ.id.meta_level, + snapshot_type(typ.upper_bound)) + def visit_unpack_type(self, typ: UnpackType) -> SnapshotItem: return ('UnpackType', snapshot_type(typ.type)) diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index d8f1d5a19155..4d684e226b21 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -60,7 +60,7 @@ TupleType, TypeType, TypedDictType, UnboundType, UninhabitedType, UnionType, Overloaded, TypeVarType, TypeList, CallableArgument, EllipsisType, StarType, LiteralType, RawExpressionType, PartialType, PlaceholderType, TypeAliasType, ParamSpecType, Parameters, - UnpackType + UnpackType, TypeVarTupleType, ) from mypy.util import get_prefix, replace_object_state from mypy.typestate import TypeState @@ -416,6 +416,9 @@ def visit_type_var(self, typ: TypeVarType) -> None: def visit_param_spec(self, typ: ParamSpecType) -> None: pass + def visit_type_var_tuple(self, typ: TypeVarTupleType) -> None: + typ.upper_bound.accept(self) + def visit_unpack_type(self, typ: UnpackType) -> None: typ.type.accept(self) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index da4960ba1934..c7623ff26c7f 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -100,7 +100,7 @@ class 'mod.Cls'. This can also refer to an attribute inherited from a Type, Instance, AnyType, NoneType, TypeVisitor, CallableType, DeletedType, PartialType, TupleType, TypeType, TypeVarType, TypedDictType, UnboundType, UninhabitedType, UnionType, FunctionLike, Overloaded, TypeOfAny, LiteralType, ErasedType, get_proper_type, ProperType, - TypeAliasType, ParamSpecType, Parameters, UnpackType + TypeAliasType, ParamSpecType, Parameters, UnpackType, TypeVarTupleType, ) from mypy.server.trigger import make_trigger, make_wildcard_trigger from mypy.util import correct_relative_import @@ -966,6 +966,13 @@ def visit_param_spec(self, typ: ParamSpecType) -> List[str]: triggers.extend(self.get_type_triggers(typ.upper_bound)) return triggers + def visit_type_var_tuple(self, typ: TypeVarTupleType) -> List[str]: + triggers = [] + if typ.fullname: + triggers.append(make_trigger(typ.fullname)) + triggers.extend(self.get_type_triggers(typ.upper_bound)) + return triggers + def visit_unpack_type(self, typ: UnpackType) -> List[str]: return typ.type.accept(self) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index d977a114bf2f..809f457ab2a2 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -8,7 +8,7 @@ Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType, UninhabitedType, TypeType, is_named_instance, FunctionLike, TypeOfAny, LiteralType, get_proper_type, TypeAliasType, ParamSpecType, - Parameters, UnpackType, TUPLE_LIKE_INSTANCE_NAMES, + Parameters, UnpackType, TUPLE_LIKE_INSTANCE_NAMES, TypeVarTupleType, ) import mypy.applytype import mypy.constraints @@ -340,6 +340,15 @@ def visit_param_spec(self, left: ParamSpecType) -> bool: return True return self._is_subtype(left.upper_bound, self.right) + def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: + right = self.right + if ( + isinstance(right, TypeVarTupleType) + and right.id == left.id + ): + return True + return self._is_subtype(left.upper_bound, self.right) + def visit_unpack_type(self, left: UnpackType) -> bool: raise NotImplementedError @@ -1463,6 +1472,15 @@ def visit_param_spec(self, left: ParamSpecType) -> bool: return True return self._is_proper_subtype(left.upper_bound, self.right) + def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: + right = self.right + if ( + isinstance(right, TypeVarTupleType) + and right.id == left.id + ): + return True + return self._is_proper_subtype(left.upper_bound, self.right) + def visit_unpack_type(self, left: UnpackType) -> bool: raise NotImplementedError diff --git a/mypy/tvar_scope.py b/mypy/tvar_scope.py index ac82a7708f0e..c1fe1cd6be35 100644 --- a/mypy/tvar_scope.py +++ b/mypy/tvar_scope.py @@ -1,6 +1,10 @@ from typing import Optional, Dict, Union -from mypy.types import TypeVarLikeType, TypeVarType, ParamSpecType, ParamSpecFlavor, TypeVarId -from mypy.nodes import ParamSpecExpr, TypeVarExpr, TypeVarLikeExpr, SymbolTableNode +from mypy.types import ( + TypeVarLikeType, TypeVarType, ParamSpecType, ParamSpecFlavor, TypeVarId, TypeVarTupleType, +) +from mypy.nodes import ( + ParamSpecExpr, TypeVarExpr, TypeVarLikeExpr, SymbolTableNode, TypeVarTupleExpr, +) class TypeVarLikeScope: @@ -88,6 +92,15 @@ def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: line=tvar_expr.line, column=tvar_expr.column ) + elif isinstance(tvar_expr, TypeVarTupleExpr): + tvar_def = TypeVarTupleType( + name, + tvar_expr.fullname, + i, + upper_bound=tvar_expr.upper_bound, + line=tvar_expr.line, + column=tvar_expr.column + ) else: assert False self.scope[tvar_expr.fullname] = tvar_def diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 05688a1e5071..85701a51f128 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -23,7 +23,8 @@ Parameters, RawExpressionType, Instance, NoneType, TypeType, UnionType, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarLikeType, UnboundType, ErasedType, StarType, EllipsisType, TypeList, CallableArgument, - PlaceholderType, TypeAliasType, ParamSpecType, UnpackType, get_proper_type + PlaceholderType, TypeAliasType, ParamSpecType, UnpackType, TypeVarTupleType, + get_proper_type ) @@ -71,6 +72,10 @@ def visit_param_spec(self, t: ParamSpecType) -> T: def visit_parameters(self, t: Parameters) -> T: pass + @abstractmethod + def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: + pass + @abstractmethod def visit_instance(self, t: Instance) -> T: pass @@ -197,6 +202,9 @@ def visit_param_spec(self, t: ParamSpecType) -> Type: def visit_parameters(self, t: Parameters) -> Type: return t.copy_modified(arg_types=self.translate_types(t.arg_types)) + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + return t + def visit_partial_type(self, t: PartialType) -> Type: return t @@ -315,6 +323,9 @@ def visit_type_var(self, t: TypeVarType) -> T: def visit_param_spec(self, t: ParamSpecType) -> T: return self.strategy([]) + def visit_type_var_tuple(self, t: TypeVarTupleType) -> T: + return self.strategy([]) + def visit_unpack_type(self, t: UnpackType) -> T: return self.query_types([t.type]) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 276e46df03ee..eee8a43c25f3 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -17,7 +17,7 @@ StarType, PartialType, EllipsisType, UninhabitedType, TypeType, CallableArgument, Parameters, TypeQuery, union_items, TypeOfAny, LiteralType, RawExpressionType, PlaceholderType, Overloaded, get_proper_type, TypeAliasType, RequiredType, - TypeVarLikeType, ParamSpecType, ParamSpecFlavor, UnpackType, + TypeVarLikeType, ParamSpecType, ParamSpecFlavor, UnpackType, TypeVarTupleType, callable_with_ellipsis, TYPE_ALIAS_NAMES, FINAL_TYPE_NAMES, LITERAL_TYPE_NAMES, ANNOTATED_TYPE_NAMES, ) @@ -26,7 +26,8 @@ TypeInfo, Context, SymbolTableNode, Var, Expression, get_nongen_builtins, check_arg_names, check_arg_kinds, ArgKind, ARG_POS, ARG_NAMED, ARG_OPT, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2, TypeVarExpr, TypeVarLikeExpr, ParamSpecExpr, - TypeAlias, PlaceholderNode, SYMBOL_FUNCBASE_TYPES, Decorator, MypyFile + TypeAlias, PlaceholderNode, SYMBOL_FUNCBASE_TYPES, Decorator, MypyFile, + TypeVarTupleExpr ) from mypy.typetraverser import TypeTraverserVisitor from mypy.tvar_scope import TypeVarLikeScope @@ -236,6 +237,24 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) tvar_def.name, tvar_def.fullname, tvar_def.id, tvar_def.values, tvar_def.upper_bound, tvar_def.variance, line=t.line, column=t.column, ) + if isinstance(sym.node, TypeVarTupleExpr) and ( + tvar_def is not None and self.defining_alias + ): + self.fail('Can\'t use bound type variable "{}"' + ' to define generic alias'.format(t.name), t) + return AnyType(TypeOfAny.from_error) + if isinstance(sym.node, TypeVarTupleExpr): + if tvar_def is None: + self.fail('TypeVarTuple "{}" is unbound'.format(t.name), t) + return AnyType(TypeOfAny.from_error) + assert isinstance(tvar_def, TypeVarTupleType) + if len(t.args) > 0: + self.fail('Type variable "{}" used with arguments'.format(t.name), t) + # Change the line number + return TypeVarTupleType( + tvar_def.name, tvar_def.fullname, tvar_def.id, + tvar_def.upper_bound, line=t.line, column=t.column, + ) special = self.try_analyze_special_unbound_type(t, fullname) if special is not None: return special @@ -514,7 +533,7 @@ def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTabl # Option 2: # Unbound type variable. Currently these may be still valid, # for example when defining a generic type alias. - unbound_tvar = (isinstance(sym.node, TypeVarExpr) and + unbound_tvar = (isinstance(sym.node, (TypeVarExpr, TypeVarTupleExpr)) and self.tvar_scope.get_binding(sym) is None) if self.allow_unbound_tvars and unbound_tvar: return t @@ -630,6 +649,9 @@ def visit_type_var(self, t: TypeVarType) -> Type: def visit_param_spec(self, t: ParamSpecType) -> Type: return t + def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: + return t + def visit_unpack_type(self, t: UnpackType) -> Type: raise NotImplementedError @@ -1180,6 +1202,8 @@ def anal_var_def(self, var_def: TypeVarLikeType) -> TypeVarLikeType: var_def.variance, var_def.line ) + elif isinstance(var_def, TypeVarTupleType): + raise NotImplementedError else: return var_def diff --git a/mypy/types.py b/mypy/types.py index 49e0aa1d85ec..e43b73f093b8 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -661,6 +661,42 @@ def deserialize(cls, data: JsonDict) -> 'ParamSpecType': ) +class TypeVarTupleType(TypeVarLikeType): + """Type that refers to a TypeVarTuple. + + See PEP646 for more information. + """ + def serialize(self) -> JsonDict: + assert not self.id.is_meta_var() + return {'.class': 'TypeVarTupleType', + 'name': self.name, + 'fullname': self.fullname, + 'id': self.id.raw_id, + 'upper_bound': self.upper_bound.serialize(), + } + + @classmethod + def deserialize(cls, data: JsonDict) -> 'TypeVarTupleType': + assert data['.class'] == 'TypeVarTupleType' + return TypeVarTupleType( + data['name'], + data['fullname'], + data['id'], + deserialize_type(data['upper_bound']), + ) + + def accept(self, visitor: 'TypeVisitor[T]') -> T: + return visitor.visit_type_var_tuple(self) + + def __hash__(self) -> int: + return hash(self.id) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, TypeVarTupleType): + return NotImplemented + return self.id == other.id + + class UnboundType(ProperType): """Instance type that has not been bound during semantic analysis.""" @@ -2657,6 +2693,15 @@ def visit_parameters(self, t: Parameters) -> str: return f'[{s}]' + def visit_type_var_tuple(self, t: TypeVarTupleType) -> str: + if t.name is None: + # Anonymous type variable type (only numeric id). + s = f'`{t.id}' + else: + # Named type variable type. + s = f'{t.name}`{t.id}' + return s + def visit_callable_type(self, t: CallableType) -> str: param_spec = t.param_spec() if param_spec is not None: diff --git a/mypy/typetraverser.py b/mypy/typetraverser.py index 94eeee79be93..7d959c97b66b 100644 --- a/mypy/typetraverser.py +++ b/mypy/typetraverser.py @@ -7,7 +7,7 @@ TypeVarType, LiteralType, Instance, CallableType, TupleType, TypedDictType, UnionType, Overloaded, TypeType, CallableArgument, UnboundType, TypeList, StarType, EllipsisType, PlaceholderType, PartialType, RawExpressionType, TypeAliasType, ParamSpecType, Parameters, - UnpackType + UnpackType, TypeVarTupleType, ) @@ -44,6 +44,9 @@ def visit_param_spec(self, t: ParamSpecType) -> None: def visit_parameters(self, t: Parameters) -> None: self.traverse_types(t.arg_types) + def visit_type_var_tuple(self, t: TypeVarTupleType) -> None: + pass + def visit_literal_type(self, t: LiteralType) -> None: t.fallback.accept(self) diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 5a1c48772190..14bc24f03a7b 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -1462,7 +1462,7 @@ bad: Tuple[Unpack[int]] # E: builtins.int cannot be unpacked (must be tuple or [builtins fixtures/tuple.pyi] [case testTypeVarTuple] -from typing_extensions import TypeVarTuple +from typing_extensions import TypeVarTuple, Unpack TVariadic = TypeVarTuple('TVariadic') TP = TypeVarTuple('?') # E: String argument 1 "?" to TypeVarTuple(...) does not match variable name "TP" @@ -1470,3 +1470,6 @@ TP2: int = TypeVarTuple('TP2') # E: Cannot declare the type of a TypeVar or sim TP3 = TypeVarTuple() # E: Too few arguments for TypeVarTuple() TP4 = TypeVarTuple('TP4', 'TP4') # E: Only the first argument to TypeVarTuple has defined semantics TP5 = TypeVarTuple(t='TP5') # E: TypeVarTuple() expects a string literal as first argument + +x: TVariadic # E: TypeVarTuple "TVariadic" is unbound +y: Unpack[TVariadic] # E: TypeVarTuple "TVariadic" is unbound From 00a981518fe1a5ee0a26cf110fcc8bb9649d487e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 21 Apr 2022 17:28:01 +0100 Subject: [PATCH 21/79] Fix slots and deleted attributes in incremental mode (#12645) They weren't serialized before. Manually written serialization code is a pain to keep up-to-date. --- mypy/nodes.py | 4 ++++ mypyc/ir/class_ir.py | 2 ++ mypyc/test-data/fixtures/ir.py | 2 +- mypyc/test-data/run-multimodule.test | 22 ++++++++++++++++++++++ test-data/unit/check-incremental.test | 25 +++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index f25d3abab7bc..5a27783e97e1 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2907,6 +2907,8 @@ def serialize(self) -> JsonDict: None if self.typeddict_type is None else self.typeddict_type.serialize(), 'flags': get_flags(self, TypeInfo.FLAGS), 'metadata': self.metadata, + 'slots': list(sorted(self.slots)) if self.slots is not None else None, + 'deletable_attributes': self.deletable_attributes, } return data @@ -2944,6 +2946,8 @@ def deserialize(cls, data: JsonDict) -> 'TypeInfo': ti.typeddict_type = (None if data['typeddict_type'] is None else mypy.types.TypedDictType.deserialize(data['typeddict_type'])) ti.metadata = data['metadata'] + ti.slots = set(data['slots']) if data['slots'] is not None else None + ti.deletable_attributes = data['deletable_attributes'] set_flags(ti, data['flags']) return ti diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index ade04f39edcb..d6407610e2bc 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -325,6 +325,7 @@ def serialize(self) -> JsonDict: 'children': [ cir.fullname for cir in self.children ] if self.children is not None else None, + 'deletable': self.deletable, } @classmethod @@ -373,6 +374,7 @@ def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'ClassIR': ir.mro = [ctx.classes[s] for s in data['mro']] ir.base_mro = [ctx.classes[s] for s in data['base_mro']] ir.children = data['children'] and [ctx.classes[s] for s in data['children']] + ir.deletable = data['deletable'] return ir diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index a661067845ac..a6914ccc36e5 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -294,7 +294,7 @@ def next(i: Iterator[T]) -> T: pass def next(i: Iterator[T], default: T) -> T: pass def hash(o: object) -> int: ... def globals() -> Dict[str, Any]: ... -def getattr(obj: object, name: str) -> Any: ... +def getattr(obj: object, name: str, default: Any = None) -> Any: ... def setattr(obj: object, name: str, value: Any) -> None: ... def enumerate(x: Iterable[T]) -> Iterator[Tuple[int, T]]: ... @overload diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 20c9002cdf1d..6ffa166c57a1 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -798,3 +798,25 @@ def foo() -> int: return 10 import native [rechecked native, other_a] + +[case testIncrementalCompilationWithDeletable] +import other_a +[file other_a.py] +from other_b import C +[file other_a.py.2] +from other_b import C +c = C() +print(getattr(c, 'x', None)) +del c.x +print(getattr(c, 'x', None)) +[file other_b.py] +class C: + __deletable__ = ['x'] + def __init__(self) -> None: + self.x = 0 +[file driver.py] +import native +[out] +[out2] +0 +None diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index c990f52e74c8..caba9b73e594 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5633,3 +5633,28 @@ main:5: error: Cannot override final attribute "x" (previously declared in base main:3: error: Cannot override writable attribute "x" with a final one main:4: error: Cannot extend enum with existing members: "FinalEnum" main:5: error: Cannot override final attribute "x" (previously declared in base class "FinalEnum") + +[case testSlotsSerialization] +import a +[file a.py] +from b import C + +class D(C): + pass +[file b.py] +class C: + __slots__ = ('x',) +[file a.py.2] +from b import C + +class D(C): + __slots__ = ('y',) + + def __init__(self) -> None: + self.x = 1 + self.y = 2 + self.z = 3 +[builtins fixtures/tuple.pyi] +[out] +[out2] +tmp/a.py:9: error: Trying to assign name "z" that is not in "__slots__" of type "a.D" From 9477bd8965e4bbfbb754b7fd2076e1dbe1e26432 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Thu, 21 Apr 2022 11:04:01 -0700 Subject: [PATCH 22/79] Update typed_ast types for build (#12638) --- build-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-requirements.txt b/build-requirements.txt index b4d024ee7f38..a46926fb3220 100644 --- a/build-requirements.txt +++ b/build-requirements.txt @@ -1,2 +1,2 @@ -r mypy-requirements.txt -types-typed-ast>=1.4.0,<1.5.0 +types-typed-ast>=1.5.0,<1.6.0 From 6eac2403f68e33e317c9f17b434d322291e60543 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 21 Apr 2022 20:53:30 +0100 Subject: [PATCH 23/79] Add regression test for recently fixed `deque.copy()` bug (#12648) --- test-data/unit/check-selftype.test | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index ef3d5289ddfd..f86e32905752 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -1220,3 +1220,21 @@ class ThingCollection(Generic[T]): def do_thing(self) -> None: self.index.update((idx, c) for idx, (k, c) in enumerate(self.collection)) [builtins fixtures/tuple.pyi] + +[case testDequeReturningSelfFromCopy] +# Tests a bug with generic self types identified in issue #12641 +from typing import Generic, Sequence, TypeVar + +T = TypeVar("T") +Self = TypeVar("Self") + +class deque(Sequence[T]): + def copy(self: Self) -> Self: ... + +class List(Sequence[T]): ... + +class Test(Generic[T]): + def test(self) -> None: + a: deque[List[T]] + # previously this failed with 'Incompatible types in assignment (expression has type "deque[List[List[T]]]", variable has type "deque[List[T]]")' + b: deque[List[T]] = a.copy() From 09dbdf6c1020aac533e0a7e71eeb97f1f1dd00f9 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 22 Apr 2022 05:21:47 +0100 Subject: [PATCH 24/79] Add test case for fixed bug regarding nested generic classes (#12652) Closes #5551 (the bug in that issue was fixed by #12590) --- test-data/unit/check-generics.test | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 6a89f6fb200c..b228e76a32d1 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -1506,6 +1506,22 @@ class Outer(Generic[T]): def g(self) -> None: y: T # E: Invalid type "__main__.T" +[case testGenericClassInsideOtherGenericClass] +from typing import TypeVar, Generic +T = TypeVar("T") +K = TypeVar("K") + +class C(Generic[T]): + def __init__(self, t: T) -> None: ... + class F(Generic[K]): + def __init__(self, k: K) -> None: ... + def foo(self) -> K: ... + +reveal_type(C.F(17).foo()) # N: Revealed type is "builtins.int" +reveal_type(C("").F(17).foo()) # N: Revealed type is "builtins.int" +reveal_type(C.F) # N: Revealed type is "def [K] (k: K`1) -> __main__.C.F[K`1]" +reveal_type(C("").F) # N: Revealed type is "def [K] (k: K`1) -> __main__.C.F[K`1]" + -- Callable subtyping with generic functions -- ----------------------------------------- From 07ea0f612ea2e755cc403b1b11e8ef058a59e4fe Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 22 Apr 2022 12:05:40 +0100 Subject: [PATCH 25/79] Attempt to fix issue with ParamSpec serialization (#12654) I've seen crashes like this, which might be caused by not fixing up some FakeInfos: ``` File "mypy/checkexpr.py", line 3981, in accept File "mypy/nodes.py", line 1753, in accept File "mypy/checkexpr.py", line 288, in visit_call_expr File "mypy/checkexpr.py", line 371, in visit_call_expr_inner File "mypy/checkexpr.py", line 880, in check_call_expr_with_callee_type File "mypy/checkexpr.py", line 940, in check_call File "mypy/checkexpr.py", line 1027, in check_callable_call File "mypy/checkexpr.py", line 1269, in infer_function_type_arguments File "mypy/checkexpr.py", line 1324, in infer_function_type_arguments_pass2 File "mypy/infer.py", line 47, in infer_function_type_arguments File "mypy/constraints.py", line 72, in infer_constraints_for_callable File "mypy/constraints.py", line 108, in infer_constraints File "mypy/constraints.py", line 181, in _infer_constraints File "mypy/types.py", line 1576, in accept File "mypy/constraints.py", line 663, in visit_callable_type File "mypy/constraints.py", line 685, in infer_against_overloaded File "mypy/constraints.py", line 775, in find_matching_overload_item File "mypy/subtypes.py", line 942, in is_callable_compatible File "mypy/subtypes.py", line 1209, in unify_generic_callable File "mypy/applytype.py", line 86, in apply_generic_arguments File "mypy/applytype.py", line 50, in get_target_type File "mypy/subtypes.py", line 97, in is_subtype File "mypy/subtypes.py", line 158, in _is_subtype File "mypy/types.py", line 615, in accept File "mypy/subtypes.py", line 341, in visit_param_spec File "mypy/subtypes.py", line 217, in _is_subtype File "mypy/subtypes.py", line 97, in is_subtype File "mypy/subtypes.py", line 158, in _is_subtype File "mypy/types.py", line 1127, in accept File "mypy/subtypes.py", line 257, in visit_instance AttributeError: attribute 'fallback_to_any' of 'TypeInfo' undefined ``` I don't have a small reproducer to I couldn't add a test case, unfortunately. --- mypy/fixup.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mypy/fixup.py b/mypy/fixup.py index 11f07b1d4655..ec979e4e1927 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -188,11 +188,7 @@ def visit_callable_type(self, ct: CallableType) -> None: if ct.ret_type is not None: ct.ret_type.accept(self) for v in ct.variables: - if isinstance(v, TypeVarType): - if v.values: - for val in v.values: - val.accept(self) - v.upper_bound.accept(self) + v.accept(self) for arg in ct.bound_args: if arg: arg.accept(self) @@ -262,6 +258,8 @@ def visit_parameters(self, p: Parameters) -> None: for argt in p.arg_types: if argt is not None: argt.accept(self) + for var in p.variables: + var.accept(self) def visit_unbound_type(self, o: UnboundType) -> None: for a in o.args: From d1c061689a330a4fa993f3cadf5cd8bc06756698 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Fri, 22 Apr 2022 23:32:58 +0900 Subject: [PATCH 26/79] Fix spelling of "GitHub" in error message (#12655) --- mypy/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/errors.py b/mypy/errors.py index 60abb739ba84..20e43fa810f9 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -877,7 +877,7 @@ def report_internal_error(err: Exception, # Print "INTERNAL ERROR" message. print('{}error: INTERNAL ERROR --'.format(prefix), - 'Please try using mypy master on Github:\n' + 'Please try using mypy master on GitHub:\n' 'https://mypy.readthedocs.io/en/stable/common_issues.html' '#using-a-development-mypy-build', file=stderr) From 40bbfb5f2539f6fc3ea8c9b4de6b62d167bb003f Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 23 Apr 2022 14:45:13 -0700 Subject: [PATCH 27/79] Sync typeshed (#12663) * Sync typeshed Source commit: https://github.com/python/typeshed/commit/5dad506bf20ed5b3ddf945407c31ec6d4abc3c67 * Fix tests Co-authored-by: hauntsaninja <> --- mypy/test/teststubtest.py | 2 +- mypy/typeshed/stdlib/VERSIONS | 1 + mypy/typeshed/stdlib/_ast.pyi | 18 +- mypy/typeshed/stdlib/_bisect.pyi | 3 +- mypy/typeshed/stdlib/_codecs.pyi | 8 +- mypy/typeshed/stdlib/_collections_abc.pyi | 2 +- mypy/typeshed/stdlib/_compression.pyi | 3 +- mypy/typeshed/stdlib/_csv.pyi | 7 +- mypy/typeshed/stdlib/_curses.pyi | 4 +- mypy/typeshed/stdlib/_decimal.pyi | 264 +++++++++++- mypy/typeshed/stdlib/_dummy_thread.pyi | 3 +- mypy/typeshed/stdlib/_dummy_threading.pyi | 8 +- mypy/typeshed/stdlib/_json.pyi | 3 +- mypy/typeshed/stdlib/_operator.pyi | 22 +- mypy/typeshed/stdlib/_osx_support.pyi | 3 +- mypy/typeshed/stdlib/_posixsubprocess.pyi | 2 +- mypy/typeshed/stdlib/_pydecimal.pyi | 2 +- mypy/typeshed/stdlib/_random.pyi | 4 +- mypy/typeshed/stdlib/_sitebuiltins.pyi | 3 +- mypy/typeshed/stdlib/_socket.pyi | 13 +- mypy/typeshed/stdlib/_thread.pyi | 3 +- mypy/typeshed/stdlib/_threading_local.pyi | 9 +- mypy/typeshed/stdlib/_tracemalloc.pyi | 2 +- mypy/typeshed/stdlib/_typeshed/__init__.pyi | 9 +- mypy/typeshed/stdlib/_typeshed/wsgi.pyi | 58 +-- mypy/typeshed/stdlib/_weakref.pyi | 11 +- mypy/typeshed/stdlib/_weakrefset.pyi | 3 +- mypy/typeshed/stdlib/_winapi.pyi | 3 +- mypy/typeshed/stdlib/aifc.pyi | 6 +- mypy/typeshed/stdlib/argparse.pyi | 23 +- mypy/typeshed/stdlib/array.pyi | 15 +- mypy/typeshed/stdlib/ast.pyi | 30 +- mypy/typeshed/stdlib/asyncio/base_events.pyi | 14 +- mypy/typeshed/stdlib/asyncio/base_futures.pyi | 3 +- .../stdlib/asyncio/base_subprocess.pyi | 6 +- mypy/typeshed/stdlib/asyncio/events.pyi | 13 +- .../stdlib/asyncio/format_helpers.pyi | 6 +- mypy/typeshed/stdlib/asyncio/futures.pyi | 3 +- mypy/typeshed/stdlib/asyncio/locks.pyi | 3 +- .../stdlib/asyncio/proactor_events.pyi | 3 +- mypy/typeshed/stdlib/asyncio/runners.pyi | 3 +- mypy/typeshed/stdlib/asyncio/sslproto.pyi | 3 +- mypy/typeshed/stdlib/asyncio/staggered.pyi | 3 +- mypy/typeshed/stdlib/asyncio/streams.pyi | 10 +- mypy/typeshed/stdlib/asyncio/subprocess.pyi | 9 +- mypy/typeshed/stdlib/asyncio/taskgroups.pyi | 3 +- mypy/typeshed/stdlib/asyncio/tasks.pyi | 10 +- mypy/typeshed/stdlib/asyncio/threads.pyi | 3 +- mypy/typeshed/stdlib/asyncio/transports.pyi | 3 +- mypy/typeshed/stdlib/asyncio/trsock.pyi | 12 +- mypy/typeshed/stdlib/asyncio/unix_events.pyi | 3 +- .../stdlib/asyncio/windows_events.pyi | 3 +- .../typeshed/stdlib/asyncio/windows_utils.pyi | 3 +- mypy/typeshed/stdlib/asyncore.pyi | 5 +- mypy/typeshed/stdlib/atexit.pyi | 3 +- mypy/typeshed/stdlib/audioop.pyi | 14 +- mypy/typeshed/stdlib/bdb.pyi | 14 +- mypy/typeshed/stdlib/binascii.pyi | 44 +- mypy/typeshed/stdlib/binhex.pyi | 6 +- mypy/typeshed/stdlib/builtins.pyi | 193 +++++---- mypy/typeshed/stdlib/bz2.pyi | 13 +- mypy/typeshed/stdlib/cProfile.pyi | 7 +- mypy/typeshed/stdlib/calendar.pyi | 4 +- mypy/typeshed/stdlib/cgi.pyi | 4 +- mypy/typeshed/stdlib/cgitb.pyi | 15 +- mypy/typeshed/stdlib/cmath.pyi | 5 +- mypy/typeshed/stdlib/cmd.pyi | 3 +- mypy/typeshed/stdlib/code.pyi | 3 +- mypy/typeshed/stdlib/codecs.pyi | 9 +- mypy/typeshed/stdlib/collections/__init__.pyi | 2 +- .../stdlib/concurrent/futures/_base.pyi | 4 +- .../stdlib/concurrent/futures/process.pyi | 4 +- .../stdlib/concurrent/futures/thread.pyi | 4 +- mypy/typeshed/stdlib/configparser.pyi | 14 +- mypy/typeshed/stdlib/contextlib.pyi | 23 +- mypy/typeshed/stdlib/contextvars.pyi | 3 +- mypy/typeshed/stdlib/copyreg.pyi | 8 +- mypy/typeshed/stdlib/ctypes/__init__.pyi | 12 +- mypy/typeshed/stdlib/ctypes/wintypes.pyi | 101 ++--- mypy/typeshed/stdlib/curses/__init__.pyi | 3 +- mypy/typeshed/stdlib/curses/textpad.pyi | 2 +- mypy/typeshed/stdlib/dataclasses.pyi | 3 +- mypy/typeshed/stdlib/datetime.pyi | 8 +- mypy/typeshed/stdlib/dbm/__init__.pyi | 10 +- mypy/typeshed/stdlib/dbm/dumb.pyi | 7 +- mypy/typeshed/stdlib/dbm/gnu.pyi | 5 +- mypy/typeshed/stdlib/dbm/ndbm.pyi | 5 +- mypy/typeshed/stdlib/decimal.pyi | 264 +----------- mypy/typeshed/stdlib/difflib.pyi | 3 +- mypy/typeshed/stdlib/dis.pyi | 90 ++++- mypy/typeshed/stdlib/distutils/ccompiler.pyi | 6 +- mypy/typeshed/stdlib/distutils/cmd.pyi | 3 +- mypy/typeshed/stdlib/distutils/core.pyi | 3 +- mypy/typeshed/stdlib/distutils/dist.pyi | 3 +- .../stdlib/distutils/fancy_getopt.pyi | 8 +- mypy/typeshed/stdlib/distutils/file_util.pyi | 2 +- mypy/typeshed/stdlib/distutils/filelist.pyi | 3 +- mypy/typeshed/stdlib/distutils/sysconfig.pyi | 2 +- mypy/typeshed/stdlib/doctest.pyi | 14 +- mypy/typeshed/stdlib/email/__init__.pyi | 9 +- .../stdlib/email/_header_value_parser.pyi | 3 +- mypy/typeshed/stdlib/email/charset.pyi | 2 +- mypy/typeshed/stdlib/email/contentmanager.pyi | 3 +- mypy/typeshed/stdlib/email/feedparser.pyi | 6 +- mypy/typeshed/stdlib/email/iterators.pyi | 2 +- mypy/typeshed/stdlib/email/message.pyi | 10 +- mypy/typeshed/stdlib/email/parser.pyi | 9 +- mypy/typeshed/stdlib/email/policy.pyi | 3 +- mypy/typeshed/stdlib/email/utils.pyi | 3 +- mypy/typeshed/stdlib/enum.pyi | 4 +- mypy/typeshed/stdlib/errno.pyi | 2 +- mypy/typeshed/stdlib/filecmp.pyi | 3 +- mypy/typeshed/stdlib/fileinput.pyi | 3 +- mypy/typeshed/stdlib/fnmatch.pyi | 3 +- mypy/typeshed/stdlib/formatter.pyi | 8 +- mypy/typeshed/stdlib/fractions.pyi | 4 +- mypy/typeshed/stdlib/ftplib.pyi | 3 +- mypy/typeshed/stdlib/functools.pyi | 9 +- mypy/typeshed/stdlib/gc.pyi | 7 +- mypy/typeshed/stdlib/genericpath.pyi | 3 +- mypy/typeshed/stdlib/gettext.pyi | 26 +- mypy/typeshed/stdlib/glob.pyi | 3 +- mypy/typeshed/stdlib/graphlib.pyi | 3 +- mypy/typeshed/stdlib/gzip.pyi | 8 +- mypy/typeshed/stdlib/hashlib.pyi | 78 +++- mypy/typeshed/stdlib/heapq.pyi | 3 +- mypy/typeshed/stdlib/hmac.pyi | 8 +- mypy/typeshed/stdlib/http/client.pyi | 6 +- mypy/typeshed/stdlib/http/cookiejar.pyi | 3 +- mypy/typeshed/stdlib/http/cookies.pyi | 6 +- mypy/typeshed/stdlib/http/server.pyi | 3 +- mypy/typeshed/stdlib/imaplib.pyi | 12 +- mypy/typeshed/stdlib/imghdr.pyi | 3 +- mypy/typeshed/stdlib/importlib/__init__.pyi | 2 +- mypy/typeshed/stdlib/importlib/abc.pyi | 7 +- mypy/typeshed/stdlib/importlib/machinery.pyi | 3 +- .../stdlib/importlib/metadata/__init__.pyi | 4 +- .../stdlib/importlib/metadata/_meta.pyi | 3 +- mypy/typeshed/stdlib/importlib/resources.pyi | 8 +- mypy/typeshed/stdlib/importlib/util.pyi | 3 +- mypy/typeshed/stdlib/inspect.pyi | 15 +- mypy/typeshed/stdlib/io.pyi | 3 +- mypy/typeshed/stdlib/ipaddress.pyi | 9 +- mypy/typeshed/stdlib/itertools.pyi | 114 +++++- mypy/typeshed/stdlib/json/__init__.pyi | 3 +- mypy/typeshed/stdlib/json/decoder.pyi | 3 +- mypy/typeshed/stdlib/json/encoder.pyi | 3 +- mypy/typeshed/stdlib/keyword.pyi | 2 +- mypy/typeshed/stdlib/lib2to3/pgen2/driver.pyi | 3 +- .../typeshed/stdlib/lib2to3/pgen2/grammar.pyi | 7 +- mypy/typeshed/stdlib/lib2to3/pgen2/parse.pyi | 6 +- mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi | 3 +- .../stdlib/lib2to3/pgen2/tokenize.pyi | 9 +- mypy/typeshed/stdlib/lib2to3/pytree.pyi | 14 +- mypy/typeshed/stdlib/lib2to3/refactor.pyi | 5 +- mypy/typeshed/stdlib/linecache.pyi | 5 +- mypy/typeshed/stdlib/locale.pyi | 3 +- mypy/typeshed/stdlib/logging/__init__.pyi | 14 +- mypy/typeshed/stdlib/logging/config.pyi | 9 +- mypy/typeshed/stdlib/lzma.pyi | 13 +- mypy/typeshed/stdlib/mailbox.pyi | 7 +- mypy/typeshed/stdlib/mailcap.pyi | 5 +- mypy/typeshed/stdlib/math.pyi | 9 +- mypy/typeshed/stdlib/mimetypes.pyi | 3 +- mypy/typeshed/stdlib/mmap.pyi | 3 +- mypy/typeshed/stdlib/modulefinder.pyi | 3 +- mypy/typeshed/stdlib/msilib/__init__.pyi | 3 +- mypy/typeshed/stdlib/msilib/sequence.pyi | 3 +- .../stdlib/multiprocessing/__init__.pyi | 27 +- .../stdlib/multiprocessing/connection.pyi | 7 +- .../stdlib/multiprocessing/context.pyi | 4 +- .../stdlib/multiprocessing/dummy/__init__.pyi | 3 +- .../multiprocessing/dummy/connection.pyi | 3 +- .../stdlib/multiprocessing/managers.pyi | 15 +- mypy/typeshed/stdlib/multiprocessing/pool.pyi | 3 +- .../stdlib/multiprocessing/shared_memory.pyi | 3 +- .../typeshed/stdlib/multiprocessing/spawn.pyi | 3 +- .../stdlib/multiprocessing/synchronize.pyi | 6 +- mypy/typeshed/stdlib/netrc.pyi | 3 +- mypy/typeshed/stdlib/nntplib.pyi | 10 +- mypy/typeshed/stdlib/optparse.pyi | 3 +- mypy/typeshed/stdlib/os/__init__.pyi | 38 +- mypy/typeshed/stdlib/parser.pyi | 3 +- mypy/typeshed/stdlib/pathlib.pyi | 3 +- mypy/typeshed/stdlib/pdb.pyi | 3 +- mypy/typeshed/stdlib/pickle.pyi | 25 +- mypy/typeshed/stdlib/pickletools.pyi | 6 +- mypy/typeshed/stdlib/pkgutil.pyi | 3 +- mypy/typeshed/stdlib/plistlib.pyi | 3 +- mypy/typeshed/stdlib/poplib.pyi | 7 +- mypy/typeshed/stdlib/posixpath.pyi | 3 +- mypy/typeshed/stdlib/profile.pyi | 7 +- mypy/typeshed/stdlib/pstats.pyi | 9 +- mypy/typeshed/stdlib/pty.pyi | 6 +- mypy/typeshed/stdlib/pydoc.pyi | 13 +- mypy/typeshed/stdlib/pyexpat/__init__.pyi | 7 +- mypy/typeshed/stdlib/random.pyi | 5 +- mypy/typeshed/stdlib/re.pyi | 6 +- mypy/typeshed/stdlib/readline.pyi | 7 +- mypy/typeshed/stdlib/reprlib.pyi | 6 +- mypy/typeshed/stdlib/sched.pyi | 3 +- mypy/typeshed/stdlib/select.pyi | 3 +- mypy/typeshed/stdlib/selectors.pyi | 6 +- mypy/typeshed/stdlib/shlex.pyi | 3 +- mypy/typeshed/stdlib/shutil.pyi | 8 +- mypy/typeshed/stdlib/signal.pyi | 9 +- mypy/typeshed/stdlib/site.pyi | 2 +- mypy/typeshed/stdlib/smtpd.pyi | 3 +- mypy/typeshed/stdlib/smtplib.pyi | 10 +- mypy/typeshed/stdlib/socketserver.pyi | 8 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 377 ++++++++++++++---- mypy/typeshed/stdlib/sre_parse.pyi | 24 +- mypy/typeshed/stdlib/ssl.pyi | 23 +- mypy/typeshed/stdlib/statistics.pyi | 7 +- mypy/typeshed/stdlib/string.pyi | 3 +- mypy/typeshed/stdlib/struct.pyi | 3 +- mypy/typeshed/stdlib/subprocess.pyi | 17 +- mypy/typeshed/stdlib/sunau.pyi | 4 +- mypy/typeshed/stdlib/symtable.pyi | 3 +- mypy/typeshed/stdlib/sys.pyi | 29 +- mypy/typeshed/stdlib/tabnanny.pyi | 2 +- mypy/typeshed/stdlib/tarfile.pyi | 4 +- mypy/typeshed/stdlib/telnetlib.pyi | 3 +- mypy/typeshed/stdlib/tempfile.pyi | 7 +- mypy/typeshed/stdlib/termios.pyi | 3 +- mypy/typeshed/stdlib/textwrap.pyi | 3 +- mypy/typeshed/stdlib/threading.pyi | 8 +- mypy/typeshed/stdlib/time.pyi | 4 +- mypy/typeshed/stdlib/timeit.pyi | 8 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 45 ++- mypy/typeshed/stdlib/tkinter/commondialog.pyi | 3 +- mypy/typeshed/stdlib/tkinter/dialog.pyi | 3 +- mypy/typeshed/stdlib/tkinter/filedialog.pyi | 3 +- mypy/typeshed/stdlib/tkinter/font.pyi | 4 +- mypy/typeshed/stdlib/tkinter/ttk.pyi | 9 +- mypy/typeshed/stdlib/tokenize.pyi | 8 +- mypy/typeshed/stdlib/trace.pyi | 9 +- mypy/typeshed/stdlib/traceback.pyi | 7 +- mypy/typeshed/stdlib/tracemalloc.pyi | 11 +- mypy/typeshed/stdlib/tty.pyi | 3 +- mypy/typeshed/stdlib/turtle.pyi | 16 +- mypy/typeshed/stdlib/types.pyi | 15 +- mypy/typeshed/stdlib/typing.pyi | 16 +- mypy/typeshed/stdlib/typing_extensions.pyi | 35 +- mypy/typeshed/stdlib/unittest/async_case.pyi | 2 +- mypy/typeshed/stdlib/unittest/case.pyi | 19 +- mypy/typeshed/stdlib/unittest/loader.pyi | 8 +- mypy/typeshed/stdlib/unittest/main.pyi | 3 +- mypy/typeshed/stdlib/unittest/mock.pyi | 19 +- mypy/typeshed/stdlib/unittest/result.pyi | 15 +- mypy/typeshed/stdlib/unittest/runner.pyi | 6 +- mypy/typeshed/stdlib/unittest/signals.pyi | 3 +- mypy/typeshed/stdlib/unittest/suite.pyi | 5 +- mypy/typeshed/stdlib/unittest/util.pyi | 6 +- mypy/typeshed/stdlib/urllib/parse.pyi | 6 +- mypy/typeshed/stdlib/urllib/request.pyi | 8 +- mypy/typeshed/stdlib/urllib/response.pyi | 3 +- mypy/typeshed/stdlib/urllib/robotparser.pyi | 3 +- mypy/typeshed/stdlib/uu.pyi | 3 +- mypy/typeshed/stdlib/uuid.pyi | 7 +- mypy/typeshed/stdlib/venv/__init__.pyi | 2 +- mypy/typeshed/stdlib/warnings.pyi | 7 +- mypy/typeshed/stdlib/wave.pyi | 4 +- mypy/typeshed/stdlib/weakref.pyi | 3 +- mypy/typeshed/stdlib/webbrowser.pyi | 2 +- mypy/typeshed/stdlib/winreg.pyi | 4 +- mypy/typeshed/stdlib/wsgiref/handlers.pyi | 13 +- mypy/typeshed/stdlib/wsgiref/headers.pyi | 3 +- .../typeshed/stdlib/wsgiref/simple_server.pyi | 2 +- mypy/typeshed/stdlib/wsgiref/types.pyi | 33 +- mypy/typeshed/stdlib/wsgiref/util.pyi | 6 +- mypy/typeshed/stdlib/wsgiref/validate.pyi | 3 +- mypy/typeshed/stdlib/xdrlib.pyi | 3 +- mypy/typeshed/stdlib/xml/dom/domreg.pyi | 2 +- mypy/typeshed/stdlib/xml/dom/minicompat.pyi | 3 +- mypy/typeshed/stdlib/xml/dom/pulldom.pyi | 11 +- mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi | 10 +- .../stdlib/xml/etree/ElementInclude.pyi | 2 +- .../typeshed/stdlib/xml/etree/ElementPath.pyi | 10 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 26 +- mypy/typeshed/stdlib/xml/sax/__init__.pyi | 3 +- mypy/typeshed/stdlib/xml/sax/saxutils.pyi | 2 +- mypy/typeshed/stdlib/xml/sax/xmlreader.pyi | 2 +- mypy/typeshed/stdlib/xmlrpc/client.pyi | 13 +- mypy/typeshed/stdlib/xmlrpc/server.pyi | 10 +- mypy/typeshed/stdlib/zipapp.pyi | 6 +- mypy/typeshed/stdlib/zipfile.pyi | 13 +- mypy/typeshed/stdlib/zoneinfo/__init__.pyi | 3 +- test-data/unit/plugins/decimal_to_int.py | 2 +- 289 files changed, 2086 insertions(+), 1454 deletions(-) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 869d2e110a66..2983c23d5150 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1174,7 +1174,7 @@ def test_config_file(self) -> None: output = run_stubtest(stub=stub, runtime=runtime, options=[]) assert remove_color_code(output) == ( "error: test_module.temp variable differs from runtime type Literal[5]\n" - "Stub: at line 2\ndecimal.Decimal\nRuntime:\n5\n\n" + "Stub: at line 2\n_decimal.Decimal\nRuntime:\n5\n\n" ) output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) assert output == "" diff --git a/mypy/typeshed/stdlib/VERSIONS b/mypy/typeshed/stdlib/VERSIONS index d3ba459dd9e3..eefc7b895436 100644 --- a/mypy/typeshed/stdlib/VERSIONS +++ b/mypy/typeshed/stdlib/VERSIONS @@ -286,6 +286,7 @@ webbrowser: 2.7- winreg: 3.0- winsound: 2.7- wsgiref: 2.7- +wsgiref.types: 3.11- xdrlib: 2.7- xml: 2.7- xmlrpc: 3.0- diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index f801b5deb0d9..cb13c7452081 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -1,13 +1,13 @@ import sys from typing import Any, ClassVar -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias PyCF_ONLY_AST: Literal[1024] if sys.version_info >= (3, 8): PyCF_TYPE_COMMENTS: Literal[4096] PyCF_ALLOW_TOP_LEVEL_AWAIT: Literal[8192] -_identifier = str +_identifier: TypeAlias = str class AST: if sys.version_info >= (3, 10): @@ -172,6 +172,14 @@ class Try(stmt): orelse: list[stmt] finalbody: list[stmt] +if sys.version_info >= (3, 11): + class TryStar(stmt): + __match_args__ = ("body", "handlers", "orelse", "finalbody") + body: list[stmt] + handlers: list[ExceptHandler] + orelse: list[stmt] + finalbody: list[stmt] + class Assert(stmt): if sys.version_info >= (3, 10): __match_args__ = ("test", "msg") @@ -358,10 +366,10 @@ class Attribute(expr): ctx: expr_context if sys.version_info >= (3, 9): - _SliceT = expr + _SliceT: TypeAlias = expr else: class slice(AST): ... - _SliceT = slice + _SliceT: TypeAlias = slice class Slice(_SliceT): if sys.version_info >= (3, 10): @@ -516,7 +524,7 @@ if sys.version_info >= (3, 10): class pattern(AST): ... # Without the alias, Pyright complains variables named pattern are recursively defined - _pattern = pattern + _pattern: TypeAlias = pattern class match_case(AST): __match_args__ = ("pattern", "guard", "body") diff --git a/mypy/typeshed/stdlib/_bisect.pyi b/mypy/typeshed/stdlib/_bisect.pyi index 5608094ccbd6..d902e1eea7d4 100644 --- a/mypy/typeshed/stdlib/_bisect.pyi +++ b/mypy/typeshed/stdlib/_bisect.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import SupportsRichComparisonT -from typing import Callable, MutableSequence, Sequence, TypeVar, overload +from collections.abc import Callable, MutableSequence, Sequence +from typing import TypeVar, overload _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/_codecs.pyi b/mypy/typeshed/stdlib/_codecs.pyi index 1781a2418ca0..e335f6d5119a 100644 --- a/mypy/typeshed/stdlib/_codecs.pyi +++ b/mypy/typeshed/stdlib/_codecs.pyi @@ -1,13 +1,15 @@ import codecs import sys -from typing import Any, Callable +from collections.abc import Callable +from typing import Any +from typing_extensions import TypeAlias # This type is not exposed; it is defined in unicodeobject.c class _EncodingMap: def size(self) -> int: ... -_MapT = dict[int, int] | _EncodingMap -_Handler = Callable[[Exception], tuple[str, int]] +_MapT: TypeAlias = dict[int, int] | _EncodingMap +_Handler: TypeAlias = Callable[[Exception], tuple[str, int]] def register(__search_function: Callable[[str], Any]) -> None: ... def register_error(__errors: str, __handler: _Handler) -> None: ... diff --git a/mypy/typeshed/stdlib/_collections_abc.pyi b/mypy/typeshed/stdlib/_collections_abc.pyi index bd8d35641b37..8373fe836330 100644 --- a/mypy/typeshed/stdlib/_collections_abc.pyi +++ b/mypy/typeshed/stdlib/_collections_abc.pyi @@ -1,6 +1,6 @@ import sys from types import MappingProxyType -from typing import ( +from typing import ( # noqa: Y027,Y038 AbstractSet as Set, AsyncGenerator as AsyncGenerator, AsyncIterable as AsyncIterable, diff --git a/mypy/typeshed/stdlib/_compression.pyi b/mypy/typeshed/stdlib/_compression.pyi index e71f7d14bd2b..ec3c7fe70856 100644 --- a/mypy/typeshed/stdlib/_compression.pyi +++ b/mypy/typeshed/stdlib/_compression.pyi @@ -1,6 +1,7 @@ from _typeshed import WriteableBuffer +from collections.abc import Callable from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase -from typing import Any, Callable, Protocol +from typing import Any, Protocol BUFFER_SIZE = DEFAULT_BUFFER_SIZE diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index 161a89778de8..ae9031df6e81 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,5 +1,6 @@ -from typing import Any, Iterable, Iterator, Protocol, Union -from typing_extensions import Literal +from collections.abc import Iterable, Iterator +from typing import Any, Protocol, Union +from typing_extensions import Literal, TypeAlias __version__: str @@ -21,7 +22,7 @@ class Dialect: strict: int def __init__(self) -> None: ... -_DialectLike = Union[str, Dialect, type[Dialect]] +_DialectLike: TypeAlias = Union[str, Dialect, type[Dialect]] class _reader(Iterator[list[str]]): dialect: Dialect diff --git a/mypy/typeshed/stdlib/_curses.pyi b/mypy/typeshed/stdlib/_curses.pyi index e193759bdc6e..95a128a32256 100644 --- a/mypy/typeshed/stdlib/_curses.pyi +++ b/mypy/typeshed/stdlib/_curses.pyi @@ -1,10 +1,10 @@ import sys from _typeshed import SupportsRead from typing import IO, Any, NamedTuple, overload -from typing_extensions import final +from typing_extensions import TypeAlias, final if sys.platform != "win32": - _chtype = str | bytes | int + _chtype: TypeAlias = str | bytes | int # ACS codes are only initialized after initscr is called ACS_BBSS: int diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index de49a787283d..fdeca8f7c42f 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -1,3 +1,265 @@ -from decimal import * +import numbers +import sys +from _typeshed import Self +from collections.abc import Container, Sequence +from types import TracebackType +from typing import Any, NamedTuple, Union, overload +from typing_extensions import TypeAlias +_Decimal: TypeAlias = Decimal | int +_DecimalNew: TypeAlias = Union[Decimal, float, str, tuple[int, Sequence[int], int]] +_ComparableNum: TypeAlias = Decimal | float | numbers.Rational + +__version__: str __libmpdec_version__: str + +class DecimalTuple(NamedTuple): + sign: int + digits: tuple[int, ...] + exponent: int + +ROUND_DOWN: str +ROUND_HALF_UP: str +ROUND_HALF_EVEN: str +ROUND_CEILING: str +ROUND_FLOOR: str +ROUND_UP: str +ROUND_HALF_DOWN: str +ROUND_05UP: str + +if sys.version_info >= (3, 7): + HAVE_CONTEXTVAR: bool +HAVE_THREADS: bool +MAX_EMAX: int +MAX_PREC: int +MIN_EMIN: int +MIN_ETINY: int + +class DecimalException(ArithmeticError): ... +class Clamped(DecimalException): ... +class InvalidOperation(DecimalException): ... +class ConversionSyntax(InvalidOperation): ... +class DivisionByZero(DecimalException, ZeroDivisionError): ... +class DivisionImpossible(InvalidOperation): ... +class DivisionUndefined(InvalidOperation, ZeroDivisionError): ... +class Inexact(DecimalException): ... +class InvalidContext(InvalidOperation): ... +class Rounded(DecimalException): ... +class Subnormal(DecimalException): ... +class Overflow(Inexact, Rounded): ... +class Underflow(Inexact, Rounded, Subnormal): ... +class FloatOperation(DecimalException, TypeError): ... + +def setcontext(__context: Context) -> None: ... +def getcontext() -> Context: ... +def localcontext(ctx: Context | None = ...) -> _ContextManager: ... + +class Decimal: + def __new__(cls: type[Self], value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... + @classmethod + def from_float(cls: type[Self], __f: float) -> Self: ... + def __bool__(self) -> bool: ... + def compare(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def __hash__(self) -> int: ... + def as_tuple(self) -> DecimalTuple: ... + def as_integer_ratio(self) -> tuple[int, int]: ... + def to_eng_string(self, context: Context | None = ...) -> str: ... + def __abs__(self) -> Decimal: ... + def __add__(self, __other: _Decimal) -> Decimal: ... + def __divmod__(self, __other: _Decimal) -> tuple[Decimal, Decimal]: ... + def __eq__(self, __other: object) -> bool: ... + def __floordiv__(self, __other: _Decimal) -> Decimal: ... + def __ge__(self, __other: _ComparableNum) -> bool: ... + def __gt__(self, __other: _ComparableNum) -> bool: ... + def __le__(self, __other: _ComparableNum) -> bool: ... + def __lt__(self, __other: _ComparableNum) -> bool: ... + def __mod__(self, __other: _Decimal) -> Decimal: ... + def __mul__(self, __other: _Decimal) -> Decimal: ... + def __neg__(self) -> Decimal: ... + def __pos__(self) -> Decimal: ... + def __pow__(self, __other: _Decimal, __modulo: _Decimal | None = ...) -> Decimal: ... + def __radd__(self, __other: _Decimal) -> Decimal: ... + def __rdivmod__(self, __other: _Decimal) -> tuple[Decimal, Decimal]: ... + def __rfloordiv__(self, __other: _Decimal) -> Decimal: ... + def __rmod__(self, __other: _Decimal) -> Decimal: ... + def __rmul__(self, __other: _Decimal) -> Decimal: ... + def __rsub__(self, __other: _Decimal) -> Decimal: ... + def __rtruediv__(self, __other: _Decimal) -> Decimal: ... + def __sub__(self, __other: _Decimal) -> Decimal: ... + def __truediv__(self, __other: _Decimal) -> Decimal: ... + def remainder_near(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def __float__(self) -> float: ... + def __int__(self) -> int: ... + def __trunc__(self) -> int: ... + @property + def real(self) -> Decimal: ... + @property + def imag(self) -> Decimal: ... + def conjugate(self) -> Decimal: ... + def __complex__(self) -> complex: ... + @overload + def __round__(self) -> int: ... + @overload + def __round__(self, __ndigits: int) -> Decimal: ... + def __floor__(self) -> int: ... + def __ceil__(self) -> int: ... + def fma(self, other: _Decimal, third: _Decimal, context: Context | None = ...) -> Decimal: ... + def __rpow__(self, __other: _Decimal, __context: Context | None = ...) -> Decimal: ... + def normalize(self, context: Context | None = ...) -> Decimal: ... + def quantize(self, exp: _Decimal, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... + def same_quantum(self, other: _Decimal, context: Context | None = ...) -> bool: ... + def to_integral_exact(self, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... + def to_integral_value(self, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... + def to_integral(self, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... + def sqrt(self, context: Context | None = ...) -> Decimal: ... + def max(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def min(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def adjusted(self) -> int: ... + def canonical(self) -> Decimal: ... + def compare_signal(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def compare_total(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def compare_total_mag(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def copy_abs(self) -> Decimal: ... + def copy_negate(self) -> Decimal: ... + def copy_sign(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def exp(self, context: Context | None = ...) -> Decimal: ... + def is_canonical(self) -> bool: ... + def is_finite(self) -> bool: ... + def is_infinite(self) -> bool: ... + def is_nan(self) -> bool: ... + def is_normal(self, context: Context | None = ...) -> bool: ... + def is_qnan(self) -> bool: ... + def is_signed(self) -> bool: ... + def is_snan(self) -> bool: ... + def is_subnormal(self, context: Context | None = ...) -> bool: ... + def is_zero(self) -> bool: ... + def ln(self, context: Context | None = ...) -> Decimal: ... + def log10(self, context: Context | None = ...) -> Decimal: ... + def logb(self, context: Context | None = ...) -> Decimal: ... + def logical_and(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def logical_invert(self, context: Context | None = ...) -> Decimal: ... + def logical_or(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def logical_xor(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def max_mag(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def min_mag(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def next_minus(self, context: Context | None = ...) -> Decimal: ... + def next_plus(self, context: Context | None = ...) -> Decimal: ... + def next_toward(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def number_class(self, context: Context | None = ...) -> str: ... + def radix(self) -> Decimal: ... + def rotate(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def scaleb(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def shift(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... + def __reduce__(self: Self) -> tuple[type[Self], tuple[str]]: ... + def __copy__(self: Self) -> Self: ... + def __deepcopy__(self: Self, __memo: Any) -> Self: ... + def __format__(self, __specifier: str, __context: Context | None = ...) -> str: ... + +class _ContextManager: + new_context: Context + saved_context: Context + def __init__(self, new_context: Context) -> None: ... + def __enter__(self) -> Context: ... + def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... + +_TrapType: TypeAlias = type[DecimalException] + +class Context: + prec: int + rounding: str + Emin: int + Emax: int + capitals: int + clamp: int + traps: dict[_TrapType, bool] + flags: dict[_TrapType, bool] + def __init__( + self, + prec: int | None = ..., + rounding: str | None = ..., + Emin: int | None = ..., + Emax: int | None = ..., + capitals: int | None = ..., + clamp: int | None = ..., + flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., + _ignored_flags: list[_TrapType] | None = ..., + ) -> None: ... + # __setattr__() only allows to set a specific set of attributes, + # already defined above. + def __delattr__(self, __name: str) -> None: ... + def __reduce__(self: Self) -> tuple[type[Self], tuple[Any, ...]]: ... + def clear_flags(self) -> None: ... + def clear_traps(self) -> None: ... + def copy(self) -> Context: ... + def __copy__(self) -> Context: ... + __hash__: Any + def Etiny(self) -> int: ... + def Etop(self) -> int: ... + def create_decimal(self, __num: _DecimalNew = ...) -> Decimal: ... + def create_decimal_from_float(self, __f: float) -> Decimal: ... + def abs(self, __x: _Decimal) -> Decimal: ... + def add(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def canonical(self, __x: Decimal) -> Decimal: ... + def compare(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def compare_signal(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def compare_total(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def compare_total_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def copy_abs(self, __x: _Decimal) -> Decimal: ... + def copy_decimal(self, __x: _Decimal) -> Decimal: ... + def copy_negate(self, __x: _Decimal) -> Decimal: ... + def copy_sign(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def divide(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def divide_int(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def divmod(self, __x: _Decimal, __y: _Decimal) -> tuple[Decimal, Decimal]: ... + def exp(self, __x: _Decimal) -> Decimal: ... + def fma(self, __x: _Decimal, __y: _Decimal, __z: _Decimal) -> Decimal: ... + def is_canonical(self, __x: _Decimal) -> bool: ... + def is_finite(self, __x: _Decimal) -> bool: ... + def is_infinite(self, __x: _Decimal) -> bool: ... + def is_nan(self, __x: _Decimal) -> bool: ... + def is_normal(self, __x: _Decimal) -> bool: ... + def is_qnan(self, __x: _Decimal) -> bool: ... + def is_signed(self, __x: _Decimal) -> bool: ... + def is_snan(self, __x: _Decimal) -> bool: ... + def is_subnormal(self, __x: _Decimal) -> bool: ... + def is_zero(self, __x: _Decimal) -> bool: ... + def ln(self, __x: _Decimal) -> Decimal: ... + def log10(self, __x: _Decimal) -> Decimal: ... + def logb(self, __x: _Decimal) -> Decimal: ... + def logical_and(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def logical_invert(self, __x: _Decimal) -> Decimal: ... + def logical_or(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def logical_xor(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def max(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def max_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def min(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def min_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def minus(self, __x: _Decimal) -> Decimal: ... + def multiply(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def next_minus(self, __x: _Decimal) -> Decimal: ... + def next_plus(self, __x: _Decimal) -> Decimal: ... + def next_toward(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def normalize(self, __x: _Decimal) -> Decimal: ... + def number_class(self, __x: _Decimal) -> str: ... + def plus(self, __x: _Decimal) -> Decimal: ... + def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = ...) -> Decimal: ... + def quantize(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def radix(self) -> Decimal: ... + def remainder(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def remainder_near(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def rotate(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def same_quantum(self, __x: _Decimal, __y: _Decimal) -> bool: ... + def scaleb(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def shift(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def sqrt(self, __x: _Decimal) -> Decimal: ... + def subtract(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... + def to_eng_string(self, __x: _Decimal) -> str: ... + def to_sci_string(self, __x: _Decimal) -> str: ... + def to_integral_exact(self, __x: _Decimal) -> Decimal: ... + def to_integral_value(self, __x: _Decimal) -> Decimal: ... + def to_integral(self, __x: _Decimal) -> Decimal: ... + +DefaultContext: Context +BasicContext: Context +ExtendedContext: Context diff --git a/mypy/typeshed/stdlib/_dummy_thread.pyi b/mypy/typeshed/stdlib/_dummy_thread.pyi index 97ba17ae497d..f257b758eeab 100644 --- a/mypy/typeshed/stdlib/_dummy_thread.pyi +++ b/mypy/typeshed/stdlib/_dummy_thread.pyi @@ -1,5 +1,6 @@ +from collections.abc import Callable from types import TracebackType -from typing import Any, Callable, NoReturn +from typing import Any, NoReturn TIMEOUT_MAX: int error = RuntimeError diff --git a/mypy/typeshed/stdlib/_dummy_threading.pyi b/mypy/typeshed/stdlib/_dummy_threading.pyi index 1cbb8f1ee8c8..2daceaedd4ad 100644 --- a/mypy/typeshed/stdlib/_dummy_threading.pyi +++ b/mypy/typeshed/stdlib/_dummy_threading.pyi @@ -1,11 +1,13 @@ import sys +from collections.abc import Callable, Iterable, Mapping from types import FrameType, TracebackType -from typing import Any, Callable, Iterable, Mapping, TypeVar +from typing import Any, TypeVar +from typing_extensions import TypeAlias # TODO recursive type -_TF = Callable[[FrameType, str, Any], Callable[..., Any] | None] +_TF: TypeAlias = Callable[[FrameType, str, Any], Callable[..., Any] | None] -_PF = Callable[[FrameType, str, Any], None] +_PF: TypeAlias = Callable[[FrameType, str, Any], None] _T = TypeVar("_T") if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/_json.pyi b/mypy/typeshed/stdlib/_json.pyi index 962fa9ec257a..130f7ab92e97 100644 --- a/mypy/typeshed/stdlib/_json.pyi +++ b/mypy/typeshed/stdlib/_json.pyi @@ -1,4 +1,5 @@ -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from typing_extensions import final @final diff --git a/mypy/typeshed/stdlib/_operator.pyi b/mypy/typeshed/stdlib/_operator.pyi index 375d8e4ddfbf..92e04d0f499d 100644 --- a/mypy/typeshed/stdlib/_operator.pyi +++ b/mypy/typeshed/stdlib/_operator.pyi @@ -1,21 +1,7 @@ import sys -from typing import ( - Any, - AnyStr, - Callable, - Container, - Generic, - Iterable, - Mapping, - MutableMapping, - MutableSequence, - Protocol, - Sequence, - SupportsAbs, - TypeVar, - overload, -) -from typing_extensions import ParamSpec, SupportsIndex, final +from collections.abc import Callable, Container, Iterable, Mapping, MutableMapping, MutableSequence, Sequence +from typing import Any, AnyStr, Generic, Protocol, SupportsAbs, TypeVar, overload +from typing_extensions import ParamSpec, SupportsIndex, TypeAlias, final _R = TypeVar("_R") _T = TypeVar("_T") @@ -40,7 +26,7 @@ class _SupportsDunderLE(Protocol): class _SupportsDunderGE(Protocol): def __ge__(self, __other: Any) -> Any: ... -_SupportsComparison = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT +_SupportsComparison: TypeAlias = _SupportsDunderLE | _SupportsDunderGE | _SupportsDunderGT | _SupportsDunderLT class _SupportsInversion(Protocol[_T_co]): def __invert__(self) -> _T_co: ... diff --git a/mypy/typeshed/stdlib/_osx_support.pyi b/mypy/typeshed/stdlib/_osx_support.pyi index cb43fa93bb80..7fd0ee922ca6 100644 --- a/mypy/typeshed/stdlib/_osx_support.pyi +++ b/mypy/typeshed/stdlib/_osx_support.pyi @@ -1,5 +1,6 @@ import sys -from typing import Iterable, Sequence, TypeVar +from collections.abc import Iterable, Sequence +from typing import TypeVar _T = TypeVar("_T") _K = TypeVar("_K") diff --git a/mypy/typeshed/stdlib/_posixsubprocess.pyi b/mypy/typeshed/stdlib/_posixsubprocess.pyi index 5481100cacfc..2d221c4896f6 100644 --- a/mypy/typeshed/stdlib/_posixsubprocess.pyi +++ b/mypy/typeshed/stdlib/_posixsubprocess.pyi @@ -1,5 +1,5 @@ import sys -from typing import Callable, Sequence +from collections.abc import Callable, Sequence if sys.platform != "win32": def cloexec_pipe() -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/_pydecimal.pyi b/mypy/typeshed/stdlib/_pydecimal.pyi index c15a4a41747e..90dbef1dc2e2 100644 --- a/mypy/typeshed/stdlib/_pydecimal.pyi +++ b/mypy/typeshed/stdlib/_pydecimal.pyi @@ -2,7 +2,7 @@ import sys # This is a slight lie, the implementations aren't exactly identical # However, in all likelihood, the differences are inconsequential -from decimal import * +from _decimal import * if sys.version_info >= (3, 7): __all__ = [ diff --git a/mypy/typeshed/stdlib/_random.pyi b/mypy/typeshed/stdlib/_random.pyi index 9aff4b3cb026..c4b235f0cd5b 100644 --- a/mypy/typeshed/stdlib/_random.pyi +++ b/mypy/typeshed/stdlib/_random.pyi @@ -1,5 +1,7 @@ +from typing_extensions import TypeAlias + # Actually Tuple[(int,) * 625] -_State = tuple[int, ...] +_State: TypeAlias = tuple[int, ...] class Random: def __init__(self, seed: object = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/_sitebuiltins.pyi b/mypy/typeshed/stdlib/_sitebuiltins.pyi index a71364b8db65..4a35921e1ef7 100644 --- a/mypy/typeshed/stdlib/_sitebuiltins.pyi +++ b/mypy/typeshed/stdlib/_sitebuiltins.pyi @@ -1,4 +1,5 @@ -from typing import ClassVar, Iterable, NoReturn +from collections.abc import Iterable +from typing import ClassVar, NoReturn from typing_extensions import Literal class Quitter: diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index a8cf16823d4e..2366412050cd 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -2,21 +2,22 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer from collections.abc import Iterable from typing import Any, SupportsInt, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 8): from typing import SupportsIndex - _FD = SupportsIndex + _FD: TypeAlias = SupportsIndex else: - _FD = SupportsInt + _FD: TypeAlias = SupportsInt -_CMSG = tuple[int, int, bytes] -_CMSGArg = tuple[int, int, ReadableBuffer] +_CMSG: TypeAlias = tuple[int, int, bytes] +_CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer] # Addresses can be either tuples of varying lengths (AF_INET, AF_INET6, # AF_NETLINK, AF_TIPC) or strings (AF_UNIX). -_Address = tuple[Any, ...] | str -_RetAddress = Any +_Address: TypeAlias = tuple[Any, ...] | str +_RetAddress: TypeAlias = Any # TODO Most methods allow bytes as address objects # ----- Constants ----- diff --git a/mypy/typeshed/stdlib/_thread.pyi b/mypy/typeshed/stdlib/_thread.pyi index 04abf8dc869c..c5da8ccf3a90 100644 --- a/mypy/typeshed/stdlib/_thread.pyi +++ b/mypy/typeshed/stdlib/_thread.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import structseq +from collections.abc import Callable from threading import Thread from types import TracebackType -from typing import Any, Callable, NoReturn +from typing import Any, NoReturn from typing_extensions import Final, final error = RuntimeError diff --git a/mypy/typeshed/stdlib/_threading_local.pyi b/mypy/typeshed/stdlib/_threading_local.pyi index 2ad77a177c37..def996fba117 100644 --- a/mypy/typeshed/stdlib/_threading_local.pyi +++ b/mypy/typeshed/stdlib/_threading_local.pyi @@ -1,15 +1,16 @@ from typing import Any +from typing_extensions import TypeAlias from weakref import ReferenceType __all__ = ["local"] -localdict = dict[Any, Any] +_localdict: TypeAlias = dict[Any, Any] class _localimpl: key: str - dicts: dict[int, tuple[ReferenceType[Any], localdict]] + dicts: dict[int, tuple[ReferenceType[Any], _localdict]] def __init__(self) -> None: ... - def get_dict(self) -> localdict: ... - def create_dict(self) -> localdict: ... + def get_dict(self) -> _localdict: ... + def create_dict(self) -> _localdict: ... class local: def __getattribute__(self, name: str) -> Any: ... diff --git a/mypy/typeshed/stdlib/_tracemalloc.pyi b/mypy/typeshed/stdlib/_tracemalloc.pyi index fd159dc586cb..6e3c4e59a07a 100644 --- a/mypy/typeshed/stdlib/_tracemalloc.pyi +++ b/mypy/typeshed/stdlib/_tracemalloc.pyi @@ -1,6 +1,6 @@ import sys +from collections.abc import Sequence from tracemalloc import _FrameTupleT, _TraceTupleT -from typing import Sequence def _get_object_traceback(__obj: object) -> Sequence[_FrameTupleT] | None: ... def _get_traces() -> Sequence[_TraceTupleT]: ... diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index b348a329522b..a80ea0702f77 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -6,8 +6,10 @@ import array import ctypes import mmap import sys +from collections.abc import Awaitable, Container, Iterable, Set as AbstractSet from os import PathLike -from typing import AbstractSet, Any, Awaitable, Container, Generic, Iterable, Protocol, TypeVar +from types import TracebackType +from typing import Any, Generic, Protocol, TypeVar, Union from typing_extensions import Final, Literal, TypeAlias, final _KT = TypeVar("_KT") @@ -99,7 +101,7 @@ StrPath: TypeAlias = str | PathLike[str] # stable BytesPath: TypeAlias = bytes | PathLike[bytes] # stable StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] # stable -OpenTextModeUpdating = Literal[ +OpenTextModeUpdating: TypeAlias = Literal[ "r+", "+r", "rt+", @@ -197,6 +199,9 @@ WriteableBuffer: TypeAlias = bytearray | memoryview | array.array[Any] | mmap.mm # Same as _WriteableBuffer, but also includes read-only buffer types (like bytes). ReadableBuffer: TypeAlias = ReadOnlyBuffer | WriteableBuffer # stable +ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType] +OptExcInfo: TypeAlias = Union[ExcInfo, tuple[None, None, None]] + # stable if sys.version_info >= (3, 10): from types import NoneType as NoneType diff --git a/mypy/typeshed/stdlib/_typeshed/wsgi.pyi b/mypy/typeshed/stdlib/_typeshed/wsgi.pyi index 9f036d8f2d33..81ca12910bd9 100644 --- a/mypy/typeshed/stdlib/_typeshed/wsgi.pyi +++ b/mypy/typeshed/stdlib/_typeshed/wsgi.pyi @@ -1,36 +1,44 @@ # Types to support PEP 3333 (WSGI) # +# Obsolete since Python 3.11: Use wsgiref.types instead. +# # See the README.md file in this directory for more information. -from sys import _OptExcInfo -from typing import Any, Callable, Iterable, Protocol +import sys +from _typeshed import OptExcInfo +from collections.abc import Callable, Iterable +from typing import Any, Protocol from typing_extensions import TypeAlias -# stable -class StartResponse(Protocol): - def __call__( - self, status: str, headers: list[tuple[str, str]], exc_info: _OptExcInfo | None = ... - ) -> Callable[[bytes], Any]: ... +class _Readable(Protocol): + def read(self, size: int = ...) -> bytes: ... + # Optional: def close(self) -> object: ... -WSGIEnvironment: TypeAlias = dict[str, Any] # stable -WSGIApplication: TypeAlias = Callable[[WSGIEnvironment, StartResponse], Iterable[bytes]] # stable +if sys.version_info >= (3, 11): + from wsgiref.types import * +else: + # stable + class StartResponse(Protocol): + def __call__( + self, __status: str, __headers: list[tuple[str, str]], __exc_info: OptExcInfo | None = ... + ) -> Callable[[bytes], object]: ... -# WSGI input streams per PEP 3333, stable -class InputStream(Protocol): - def read(self, size: int = ...) -> bytes: ... - def readline(self, size: int = ...) -> bytes: ... - def readlines(self, hint: int = ...) -> list[bytes]: ... - def __iter__(self) -> Iterable[bytes]: ... + WSGIEnvironment: TypeAlias = dict[str, Any] # stable + WSGIApplication: TypeAlias = Callable[[WSGIEnvironment, StartResponse], Iterable[bytes]] # stable -# WSGI error streams per PEP 3333, stable -class ErrorStream(Protocol): - def flush(self) -> None: ... - def write(self, s: str) -> None: ... - def writelines(self, seq: list[str]) -> None: ... + # WSGI input streams per PEP 3333, stable + class InputStream(Protocol): + def read(self, __size: int = ...) -> bytes: ... + def readline(self, __size: int = ...) -> bytes: ... + def readlines(self, __hint: int = ...) -> list[bytes]: ... + def __iter__(self) -> Iterable[bytes]: ... -class _Readable(Protocol): - def read(self, size: int = ...) -> bytes: ... + # WSGI error streams per PEP 3333, stable + class ErrorStream(Protocol): + def flush(self) -> object: ... + def write(self, __s: str) -> object: ... + def writelines(self, __seq: list[str]) -> object: ... -# Optional file wrapper in wsgi.file_wrapper -class FileWrapper(Protocol): - def __call__(self, file: _Readable, block_size: int = ...) -> Iterable[bytes]: ... + # Optional file wrapper in wsgi.file_wrapper + class FileWrapper(Protocol): + def __call__(self, __file: _Readable, __block_size: int = ...) -> Iterable[bytes]: ... diff --git a/mypy/typeshed/stdlib/_weakref.pyi b/mypy/typeshed/stdlib/_weakref.pyi index 00dc2d5114b8..2d3faec1fa68 100644 --- a/mypy/typeshed/stdlib/_weakref.pyi +++ b/mypy/typeshed/stdlib/_weakref.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Callable, Generic, TypeVar, overload +from collections.abc import Callable +from typing import Any, Generic, TypeVar, overload from typing_extensions import final if sys.version_info >= (3, 9): @@ -28,10 +29,10 @@ class ReferenceType(Generic[_T]): ref = ReferenceType def getweakrefcount(__object: Any) -> int: ... -def getweakrefs(object: Any) -> list[Any]: ... -@overload -def proxy(object: _C, callback: Callable[[_C], Any] | None = ...) -> CallableProxyType[_C]: ... +def getweakrefs(__object: Any) -> list[Any]: ... # Return CallableProxyType if object is callable, ProxyType otherwise @overload -def proxy(object: _T, callback: Callable[[_T], Any] | None = ...) -> Any: ... +def proxy(__object: _C, __callback: Callable[[_C], Any] | None = ...) -> CallableProxyType[_C]: ... +@overload +def proxy(__object: _T, __callback: Callable[[_T], Any] | None = ...) -> Any: ... diff --git a/mypy/typeshed/stdlib/_weakrefset.pyi b/mypy/typeshed/stdlib/_weakrefset.pyi index b0c22a5ecc13..382dbdeb6c8a 100644 --- a/mypy/typeshed/stdlib/_weakrefset.pyi +++ b/mypy/typeshed/stdlib/_weakrefset.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import Self -from typing import Any, Generic, Iterable, Iterator, MutableSet, TypeVar +from collections.abc import Iterable, Iterator, MutableSet +from typing import Any, Generic, TypeVar if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/_winapi.pyi b/mypy/typeshed/stdlib/_winapi.pyi index 1e8c51477083..77e7714454e7 100644 --- a/mypy/typeshed/stdlib/_winapi.pyi +++ b/mypy/typeshed/stdlib/_winapi.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, NoReturn, Sequence, overload +from collections.abc import Sequence +from typing import Any, NoReturn, overload from typing_extensions import Literal, final if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/aifc.pyi b/mypy/typeshed/stdlib/aifc.pyi index db3d8d991029..14e824f3d22e 100644 --- a/mypy/typeshed/stdlib/aifc.pyi +++ b/mypy/typeshed/stdlib/aifc.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import Self from types import TracebackType from typing import IO, Any, NamedTuple, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): __all__ = ["Error", "open"] @@ -19,8 +19,8 @@ class _aifc_params(NamedTuple): comptype: bytes compname: bytes -_File = str | IO[bytes] -_Marker = tuple[int, int, bytes] +_File: TypeAlias = str | IO[bytes] +_Marker: TypeAlias = tuple[int, int, bytes] class Aifc_read: def __init__(self, f: _File) -> None: ... diff --git a/mypy/typeshed/stdlib/argparse.pyi b/mypy/typeshed/stdlib/argparse.pyi index 0a56aa4f4b59..759027d3a890 100644 --- a/mypy/typeshed/stdlib/argparse.pyi +++ b/mypy/typeshed/stdlib/argparse.pyi @@ -1,20 +1,7 @@ import sys -from typing import ( - IO, - Any, - Callable, - Generator, - Generic, - Iterable, - NewType, - NoReturn, - Pattern, - Protocol, - Sequence, - TypeVar, - overload, -) -from typing_extensions import Literal +from collections.abc import Callable, Generator, Iterable, Sequence +from typing import IO, Any, Generic, NewType, NoReturn, Pattern, Protocol, TypeVar, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): __all__ = [ @@ -66,11 +53,11 @@ _N = TypeVar("_N") # "store_false", "append", "append_const", "count", "help", "version", # "extend"], but using this would make it hard to annotate callers # that don't use a literal argument -_ActionStr = str +_ActionStr: TypeAlias = str # more precisely, Literal["?", "*", "+", "...", "A...", # "==SUPPRESS=="], but using this would make it hard to annotate # callers that don't use a literal argument -_NArgsStr = str +_NArgsStr: TypeAlias = str ONE_OR_MORE: Literal["+"] OPTIONAL: Literal["?"] diff --git a/mypy/typeshed/stdlib/array.pyi b/mypy/typeshed/stdlib/array.pyi index 69b4d35d9fe9..d69f02d338cf 100644 --- a/mypy/typeshed/stdlib/array.pyi +++ b/mypy/typeshed/stdlib/array.pyi @@ -1,12 +1,15 @@ import sys from _typeshed import Self -from typing import Any, BinaryIO, Generic, Iterable, MutableSequence, TypeVar, overload -from typing_extensions import Literal, SupportsIndex +from collections.abc import Iterable -_IntTypeCode = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"] -_FloatTypeCode = Literal["f", "d"] -_UnicodeTypeCode = Literal["u"] -_TypeCode = _IntTypeCode | _FloatTypeCode | _UnicodeTypeCode +# pytype crashes if array inherits from collections.abc.MutableSequence instead of typing.MutableSequence +from typing import Any, BinaryIO, Generic, MutableSequence, TypeVar, overload # noqa: Y027 +from typing_extensions import Literal, SupportsIndex, TypeAlias + +_IntTypeCode: TypeAlias = Literal["b", "B", "h", "H", "i", "I", "l", "L", "q", "Q"] +_FloatTypeCode: TypeAlias = Literal["f", "d"] +_UnicodeTypeCode: TypeAlias = Literal["u"] +_TypeCode: TypeAlias = _IntTypeCode | _FloatTypeCode | _UnicodeTypeCode _T = TypeVar("_T", int, float, str) diff --git a/mypy/typeshed/stdlib/ast.pyi b/mypy/typeshed/stdlib/ast.pyi index 5a86d6888b2f..199e4f2acb68 100644 --- a/mypy/typeshed/stdlib/ast.pyi +++ b/mypy/typeshed/stdlib/ast.pyi @@ -1,15 +1,7 @@ -# Rename typing to _typing, as not to conflict with typing imported -# from _ast below when loaded in an unorthodox way by the Dropbox -# internal Bazel integration. - -# The same unorthodox Bazel integration causes issues with sys, which -# is imported in both modules. unfortunately we can't just rename sys, -# since mypy only supports version checks with a sys that is named -# sys. import sys -import typing as _typing from _ast import * -from typing import Any, Iterator, TypeVar, overload +from collections.abc import Iterator +from typing import Any, TypeVar, overload from typing_extensions import Literal if sys.version_info >= (3, 8): @@ -166,7 +158,7 @@ if sys.version_info >= (3, 8): mode: Literal["exec"] = ..., *, type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> Module: ... @overload def parse( @@ -175,7 +167,7 @@ if sys.version_info >= (3, 8): mode: Literal["eval"], *, type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> Expression: ... @overload def parse( @@ -184,7 +176,7 @@ if sys.version_info >= (3, 8): mode: Literal["func_type"], *, type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> FunctionType: ... @overload def parse( @@ -193,7 +185,7 @@ if sys.version_info >= (3, 8): mode: Literal["single"], *, type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> Interactive: ... @overload def parse( @@ -201,7 +193,7 @@ if sys.version_info >= (3, 8): *, mode: Literal["eval"], type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> Expression: ... @overload def parse( @@ -209,7 +201,7 @@ if sys.version_info >= (3, 8): *, mode: Literal["func_type"], type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> FunctionType: ... @overload def parse( @@ -217,7 +209,7 @@ if sys.version_info >= (3, 8): *, mode: Literal["single"], type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> Interactive: ... @overload def parse( @@ -226,7 +218,7 @@ if sys.version_info >= (3, 8): mode: str = ..., *, type_comments: bool = ..., - feature_version: None | int | _typing.Tuple[int, int] = ..., + feature_version: None | int | tuple[int, int] = ..., ) -> AST: ... else: @@ -260,7 +252,7 @@ def fix_missing_locations(node: _T) -> _T: ... def get_docstring(node: AST, clean: bool = ...) -> str | None: ... def increment_lineno(node: _T, n: int = ...) -> _T: ... def iter_child_nodes(node: AST) -> Iterator[AST]: ... -def iter_fields(node: AST) -> Iterator[_typing.Tuple[str, Any]]: ... +def iter_fields(node: AST) -> Iterator[tuple[str, Any]]: ... def literal_eval(node_or_string: str | AST) -> Any: ... if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/asyncio/base_events.pyi b/mypy/typeshed/stdlib/asyncio/base_events.pyi index 71e4487baf99..7742651fea2a 100644 --- a/mypy/typeshed/stdlib/asyncio/base_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_events.pyi @@ -6,10 +6,10 @@ from asyncio.futures import Future from asyncio.protocols import BaseProtocol from asyncio.tasks import Task from asyncio.transports import BaseTransport, ReadTransport, SubprocessTransport, WriteTransport -from collections.abc import Iterable +from collections.abc import Awaitable, Callable, Coroutine, Generator, Iterable, Sequence from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket -from typing import IO, Any, Awaitable, Callable, Coroutine, Generator, Sequence, TypeVar, overload -from typing_extensions import Literal +from typing import IO, Any, TypeVar, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 7): from contextvars import Context @@ -23,10 +23,10 @@ else: _T = TypeVar("_T") _ProtocolT = TypeVar("_ProtocolT", bound=BaseProtocol) -_Context = dict[str, Any] -_ExceptionHandler = Callable[[AbstractEventLoop, _Context], Any] -_ProtocolFactory = Callable[[], BaseProtocol] -_SSLContext = bool | None | ssl.SSLContext +_Context: TypeAlias = dict[str, Any] +_ExceptionHandler: TypeAlias = Callable[[AbstractEventLoop, _Context], Any] +_ProtocolFactory: TypeAlias = Callable[[], BaseProtocol] +_SSLContext: TypeAlias = bool | None | ssl.SSLContext class Server(AbstractServer): if sys.version_info >= (3, 7): diff --git a/mypy/typeshed/stdlib/asyncio/base_futures.pyi b/mypy/typeshed/stdlib/asyncio/base_futures.pyi index 1b7fe4671ca8..8a973d1618f4 100644 --- a/mypy/typeshed/stdlib/asyncio/base_futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_futures.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import Any from typing_extensions import Literal if sys.version_info >= (3, 7): diff --git a/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi b/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi index 21c56bde1eac..963cfa93de28 100644 --- a/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/base_subprocess.pyi @@ -1,10 +1,12 @@ import subprocess from collections import deque -from typing import IO, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import IO, Any +from typing_extensions import TypeAlias from . import events, futures, protocols, transports -_File = int | IO[Any] | None +_File: TypeAlias = int | IO[Any] | None class BaseSubprocessTransport(transports.SubprocessTransport): diff --git a/mypy/typeshed/stdlib/asyncio/events.pyi b/mypy/typeshed/stdlib/asyncio/events.pyi index cc0391f92bc4..ae566234160b 100644 --- a/mypy/typeshed/stdlib/asyncio/events.pyi +++ b/mypy/typeshed/stdlib/asyncio/events.pyi @@ -2,9 +2,10 @@ import ssl import sys from _typeshed import FileDescriptorLike, Self from abc import ABCMeta, abstractmethod +from collections.abc import Awaitable, Callable, Coroutine, Generator, Sequence from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket -from typing import IO, Any, Awaitable, Callable, Coroutine, Generator, Sequence, TypeVar, overload -from typing_extensions import Literal +from typing import IO, Any, TypeVar, overload +from typing_extensions import Literal, TypeAlias from .base_events import Server from .futures import Future @@ -75,10 +76,10 @@ else: _T = TypeVar("_T") _ProtocolT = TypeVar("_ProtocolT", bound=BaseProtocol) -_Context = dict[str, Any] -_ExceptionHandler = Callable[[AbstractEventLoop, _Context], Any] -_ProtocolFactory = Callable[[], BaseProtocol] -_SSLContext = bool | None | ssl.SSLContext +_Context: TypeAlias = dict[str, Any] +_ExceptionHandler: TypeAlias = Callable[[AbstractEventLoop, _Context], Any] +_ProtocolFactory: TypeAlias = Callable[[], BaseProtocol] +_SSLContext: TypeAlias = bool | None | ssl.SSLContext class Handle: _cancelled: bool diff --git a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi index 5e483a578c3e..4e2ef8d3f274 100644 --- a/mypy/typeshed/stdlib/asyncio/format_helpers.pyi +++ b/mypy/typeshed/stdlib/asyncio/format_helpers.pyi @@ -1,12 +1,14 @@ import functools import traceback +from collections.abc import Iterable from types import FrameType, FunctionType -from typing import Any, Iterable, overload +from typing import Any, overload +from typing_extensions import TypeAlias class _HasWrapper: __wrapper__: _HasWrapper | FunctionType -_FuncType = FunctionType | _HasWrapper | functools.partial[Any] | functools.partialmethod[Any] +_FuncType: TypeAlias = FunctionType | _HasWrapper | functools.partial[Any] | functools.partialmethod[Any] @overload def _get_function_source(func: _FuncType) -> tuple[str, int]: ... diff --git a/mypy/typeshed/stdlib/asyncio/futures.pyi b/mypy/typeshed/stdlib/asyncio/futures.pyi index 2b0b93a09638..692d263f673b 100644 --- a/mypy/typeshed/stdlib/asyncio/futures.pyi +++ b/mypy/typeshed/stdlib/asyncio/futures.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import Self +from collections.abc import Awaitable, Callable, Generator, Iterable from concurrent.futures._base import Error, Future as _ConcurrentFuture -from typing import Any, Awaitable, Callable, Generator, Iterable, TypeVar +from typing import Any, TypeVar from typing_extensions import Literal, TypeGuard from .events import AbstractEventLoop diff --git a/mypy/typeshed/stdlib/asyncio/locks.pyi b/mypy/typeshed/stdlib/asyncio/locks.pyi index 01b1c5b4591d..2758e0c46919 100644 --- a/mypy/typeshed/stdlib/asyncio/locks.pyi +++ b/mypy/typeshed/stdlib/asyncio/locks.pyi @@ -1,7 +1,8 @@ import sys from collections import deque +from collections.abc import Callable, Generator from types import TracebackType -from typing import Any, Callable, Generator, TypeVar +from typing import Any, TypeVar from typing_extensions import Literal from .events import AbstractEventLoop diff --git a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi index 4ffb40160420..21247401c9ba 100644 --- a/mypy/typeshed/stdlib/asyncio/proactor_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/proactor_events.pyi @@ -1,6 +1,7 @@ import sys +from collections.abc import Mapping from socket import socket -from typing import Any, Mapping, Protocol +from typing import Any, Protocol from typing_extensions import Literal from . import base_events, constants, events, futures, streams, transports diff --git a/mypy/typeshed/stdlib/asyncio/runners.pyi b/mypy/typeshed/stdlib/asyncio/runners.pyi index 7e799dd22fd8..32cd839f2f79 100644 --- a/mypy/typeshed/stdlib/asyncio/runners.pyi +++ b/mypy/typeshed/stdlib/asyncio/runners.pyi @@ -1,5 +1,6 @@ import sys -from typing import Awaitable, TypeVar +from collections.abc import Awaitable +from typing import TypeVar __all__ = ("run",) _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/asyncio/sslproto.pyi b/mypy/typeshed/stdlib/asyncio/sslproto.pyi index 4ecd7a11dd56..619e329bfb43 100644 --- a/mypy/typeshed/stdlib/asyncio/sslproto.pyi +++ b/mypy/typeshed/stdlib/asyncio/sslproto.pyi @@ -1,7 +1,8 @@ import ssl import sys from collections import deque -from typing import Any, Callable, ClassVar +from collections.abc import Callable +from typing import Any, ClassVar from typing_extensions import Literal from . import constants, events, futures, protocols, transports diff --git a/mypy/typeshed/stdlib/asyncio/staggered.pyi b/mypy/typeshed/stdlib/asyncio/staggered.pyi index fc4bfad76984..610d6f70b614 100644 --- a/mypy/typeshed/stdlib/asyncio/staggered.pyi +++ b/mypy/typeshed/stdlib/asyncio/staggered.pyi @@ -1,4 +1,5 @@ -from typing import Any, Awaitable, Callable, Iterable +from collections.abc import Awaitable, Callable, Iterable +from typing import Any from . import events diff --git a/mypy/typeshed/stdlib/asyncio/streams.pyi b/mypy/typeshed/stdlib/asyncio/streams.pyi index 666862cc7b7d..14a6d2c4d8fe 100644 --- a/mypy/typeshed/stdlib/asyncio/streams.pyi +++ b/mypy/typeshed/stdlib/asyncio/streams.pyi @@ -1,6 +1,8 @@ import sys from _typeshed import Self, StrPath -from typing import Any, AsyncIterator, Awaitable, Callable, Iterable, Sequence +from collections.abc import AsyncIterator, Awaitable, Callable, Iterable, Sequence +from typing import Any +from typing_extensions import TypeAlias from . import events, protocols, transports from .base_events import Server @@ -64,7 +66,7 @@ else: "start_unix_server", ] -_ClientConnectedCallback = Callable[[StreamReader, StreamWriter], Awaitable[None] | None] +_ClientConnectedCallback: TypeAlias = Callable[[StreamReader, StreamWriter], Awaitable[None] | None] if sys.version_info < (3, 8): class IncompleteReadError(EOFError): @@ -118,9 +120,9 @@ else: if sys.platform != "win32": if sys.version_info >= (3, 7): - _PathType = StrPath + _PathType: TypeAlias = StrPath else: - _PathType = str + _PathType: TypeAlias = str if sys.version_info >= (3, 10): async def open_unix_connection( path: _PathType | None = ..., *, limit: int = ..., **kwds: Any diff --git a/mypy/typeshed/stdlib/asyncio/subprocess.pyi b/mypy/typeshed/stdlib/asyncio/subprocess.pyi index 3a617d6fb628..55093a3ebd9f 100644 --- a/mypy/typeshed/stdlib/asyncio/subprocess.pyi +++ b/mypy/typeshed/stdlib/asyncio/subprocess.pyi @@ -2,8 +2,9 @@ import subprocess import sys from _typeshed import StrOrBytesPath from asyncio import events, protocols, streams, transports -from typing import IO, Any, Callable -from typing_extensions import Literal +from collections.abc import Callable +from typing import IO, Any +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 7): __all__ = ("create_subprocess_exec", "create_subprocess_shell") @@ -11,9 +12,9 @@ else: __all__ = ["create_subprocess_exec", "create_subprocess_shell"] if sys.version_info >= (3, 8): - _ExecArg = StrOrBytesPath + _ExecArg: TypeAlias = StrOrBytesPath else: - _ExecArg = str | bytes + _ExecArg: TypeAlias = str | bytes PIPE: int STDOUT: int diff --git a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi index ef04614c3c15..58e3d41e53f1 100644 --- a/mypy/typeshed/stdlib/asyncio/taskgroups.pyi +++ b/mypy/typeshed/stdlib/asyncio/taskgroups.pyi @@ -1,8 +1,9 @@ # This only exists in 3.11+. See VERSIONS. from _typeshed import Self +from collections.abc import Coroutine, Generator from types import TracebackType -from typing import Any, Coroutine, Generator, TypeVar +from typing import Any, TypeVar from .tasks import Task diff --git a/mypy/typeshed/stdlib/asyncio/tasks.pyi b/mypy/typeshed/stdlib/asyncio/tasks.pyi index 885688065ddc..4a8e565afb2f 100644 --- a/mypy/typeshed/stdlib/asyncio/tasks.pyi +++ b/mypy/typeshed/stdlib/asyncio/tasks.pyi @@ -1,9 +1,9 @@ import concurrent.futures import sys -from collections.abc import Awaitable, Generator, Iterable, Iterator +from collections.abc import Awaitable, Coroutine, Generator, Iterable, Iterator from types import FrameType -from typing import Any, Coroutine, Generic, TextIO, TypeVar, overload -from typing_extensions import Literal +from typing import Any, Generic, TextIO, TypeVar, overload +from typing_extensions import Literal, TypeAlias from .events import AbstractEventLoop from .futures import Future @@ -56,8 +56,8 @@ _T3 = TypeVar("_T3") _T4 = TypeVar("_T4") _T5 = TypeVar("_T5") _FT = TypeVar("_FT", bound=Future[Any]) -_FutureT = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] -_TaskYieldType = Future[object] | None +_FutureT: TypeAlias = Future[_T] | Generator[Any, None, _T] | Awaitable[_T] +_TaskYieldType: TypeAlias = Future[object] | None FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION diff --git a/mypy/typeshed/stdlib/asyncio/threads.pyi b/mypy/typeshed/stdlib/asyncio/threads.pyi index ac3a5c56b222..88c4fddcaa3f 100644 --- a/mypy/typeshed/stdlib/asyncio/threads.pyi +++ b/mypy/typeshed/stdlib/asyncio/threads.pyi @@ -1,4 +1,5 @@ -from typing import Callable, TypeVar +from collections.abc import Callable +from typing import TypeVar from typing_extensions import ParamSpec __all__ = ("to_thread",) diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi index 90e54b547a87..a8cd753c2af8 100644 --- a/mypy/typeshed/stdlib/asyncio/transports.pyi +++ b/mypy/typeshed/stdlib/asyncio/transports.pyi @@ -1,8 +1,9 @@ import sys from asyncio.events import AbstractEventLoop from asyncio.protocols import BaseProtocol +from collections.abc import Mapping from socket import _Address -from typing import Any, Mapping +from typing import Any if sys.version_info >= (3, 7): __all__ = ("BaseTransport", "ReadTransport", "WriteTransport", "Transport", "DatagramTransport", "SubprocessTransport") diff --git a/mypy/typeshed/stdlib/asyncio/trsock.pyi b/mypy/typeshed/stdlib/asyncio/trsock.pyi index 3fe1e19d1311..20df2a78a5ab 100644 --- a/mypy/typeshed/stdlib/asyncio/trsock.pyi +++ b/mypy/typeshed/stdlib/asyncio/trsock.pyi @@ -1,14 +1,16 @@ import socket import sys from builtins import type as Type # alias to avoid name clashes with property named "type" +from collections.abc import Iterable from types import TracebackType -from typing import Any, BinaryIO, Iterable, NoReturn, overload +from typing import Any, BinaryIO, NoReturn, overload +from typing_extensions import TypeAlias # These are based in socket, maybe move them out into _typeshed.pyi or such -_Address = tuple[Any, ...] | str -_RetAddress = Any -_WriteBuffer = bytearray | memoryview -_CMSG = tuple[int, int, bytes] +_Address: TypeAlias = tuple[Any, ...] | str +_RetAddress: TypeAlias = Any +_WriteBuffer: TypeAlias = bytearray | memoryview +_CMSG: TypeAlias = tuple[int, int, bytes] class TransportSocket: def __init__(self, sock: socket.socket) -> None: ... diff --git a/mypy/typeshed/stdlib/asyncio/unix_events.pyi b/mypy/typeshed/stdlib/asyncio/unix_events.pyi index 78d87dc4e9e9..ca28ee04125a 100644 --- a/mypy/typeshed/stdlib/asyncio/unix_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/unix_events.pyi @@ -2,8 +2,9 @@ import sys import types from _typeshed import Self from abc import ABCMeta, abstractmethod +from collections.abc import Callable from socket import socket -from typing import Any, Callable +from typing import Any from typing_extensions import Literal from .base_events import Server diff --git a/mypy/typeshed/stdlib/asyncio/windows_events.pyi b/mypy/typeshed/stdlib/asyncio/windows_events.pyi index 1e4d286386c8..d33210bc1297 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_events.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_events.pyi @@ -1,7 +1,8 @@ import socket import sys from _typeshed import WriteableBuffer -from typing import IO, Any, Callable, ClassVar, NoReturn +from collections.abc import Callable +from typing import IO, Any, ClassVar, NoReturn from typing_extensions import Literal from . import events, futures, proactor_events, selector_events, streams, windows_utils diff --git a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi index 1156f905659e..0a79635b3d4e 100644 --- a/mypy/typeshed/stdlib/asyncio/windows_utils.pyi +++ b/mypy/typeshed/stdlib/asyncio/windows_utils.pyi @@ -1,8 +1,9 @@ import subprocess import sys from _typeshed import Self +from collections.abc import Callable from types import TracebackType -from typing import Any, AnyStr, Callable, Protocol +from typing import Any, AnyStr, Protocol from typing_extensions import Literal if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/asyncore.pyi b/mypy/typeshed/stdlib/asyncore.pyi index 8f77e0e45c44..a4a774282343 100644 --- a/mypy/typeshed/stdlib/asyncore.pyi +++ b/mypy/typeshed/stdlib/asyncore.pyi @@ -2,10 +2,11 @@ import sys from _typeshed import FileDescriptorLike from socket import socket from typing import Any, overload +from typing_extensions import TypeAlias # cyclic dependence with asynchat -_maptype = dict[int, Any] -_socket = socket +_maptype: TypeAlias = dict[int, Any] +_socket: TypeAlias = socket socket_map: _maptype # undocumented diff --git a/mypy/typeshed/stdlib/atexit.pyi b/mypy/typeshed/stdlib/atexit.pyi index ba0c7dfaf6b1..095ab5f9b26d 100644 --- a/mypy/typeshed/stdlib/atexit.pyi +++ b/mypy/typeshed/stdlib/atexit.pyi @@ -1,4 +1,5 @@ -from typing import Any, Callable, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar from typing_extensions import ParamSpec _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/audioop.pyi b/mypy/typeshed/stdlib/audioop.pyi index b08731b85b0b..62b54ced9127 100644 --- a/mypy/typeshed/stdlib/audioop.pyi +++ b/mypy/typeshed/stdlib/audioop.pyi @@ -1,10 +1,12 @@ -AdpcmState = tuple[int, int] -RatecvState = tuple[int, tuple[tuple[int, int], ...]] +from typing_extensions import TypeAlias + +_AdpcmState: TypeAlias = tuple[int, int] +_RatecvState: TypeAlias = tuple[int, tuple[tuple[int, int], ...]] class error(Exception): ... def add(__fragment1: bytes, __fragment2: bytes, __width: int) -> bytes: ... -def adpcm2lin(__fragment: bytes, __width: int, __state: AdpcmState | None) -> tuple[bytes, AdpcmState]: ... +def adpcm2lin(__fragment: bytes, __width: int, __state: _AdpcmState | None) -> tuple[bytes, _AdpcmState]: ... def alaw2lin(__fragment: bytes, __width: int) -> bytes: ... def avg(__fragment: bytes, __width: int) -> int: ... def avgpp(__fragment: bytes, __width: int) -> int: ... @@ -15,7 +17,7 @@ def findfactor(__fragment: bytes, __reference: bytes) -> float: ... def findfit(__fragment: bytes, __reference: bytes) -> tuple[int, float]: ... def findmax(__fragment: bytes, __length: int) -> int: ... def getsample(__fragment: bytes, __width: int, __index: int) -> int: ... -def lin2adpcm(__fragment: bytes, __width: int, __state: AdpcmState | None) -> tuple[bytes, AdpcmState]: ... +def lin2adpcm(__fragment: bytes, __width: int, __state: _AdpcmState | None) -> tuple[bytes, _AdpcmState]: ... def lin2alaw(__fragment: bytes, __width: int) -> bytes: ... def lin2lin(__fragment: bytes, __width: int, __newwidth: int) -> bytes: ... def lin2ulaw(__fragment: bytes, __width: int) -> bytes: ... @@ -29,10 +31,10 @@ def ratecv( __nchannels: int, __inrate: int, __outrate: int, - __state: RatecvState | None, + __state: _RatecvState | None, __weightA: int = ..., __weightB: int = ..., -) -> tuple[bytes, RatecvState]: ... +) -> tuple[bytes, _RatecvState]: ... def reverse(__fragment: bytes, __width: int) -> bytes: ... def rms(__fragment: bytes, __width: int) -> int: ... def tomono(__fragment: bytes, __width: int, __lfactor: float, __rfactor: float) -> bytes: ... diff --git a/mypy/typeshed/stdlib/bdb.pyi b/mypy/typeshed/stdlib/bdb.pyi index 8f61433e0cb8..bbd0f20af6c8 100644 --- a/mypy/typeshed/stdlib/bdb.pyi +++ b/mypy/typeshed/stdlib/bdb.pyi @@ -1,20 +1,20 @@ +from _typeshed import ExcInfo +from collections.abc import Callable, Iterable, Mapping from types import CodeType, FrameType, TracebackType -from typing import IO, Any, Callable, Iterable, Mapping, SupportsInt, TypeVar -from typing_extensions import Literal, ParamSpec +from typing import IO, Any, SupportsInt, TypeVar +from typing_extensions import Literal, ParamSpec, TypeAlias __all__ = ["BdbQuit", "Bdb", "Breakpoint"] _T = TypeVar("_T") _P = ParamSpec("_P") -_TraceDispatch = Callable[[FrameType, str, Any], Any] # TODO: Recursive type -_ExcInfo = tuple[type[BaseException], BaseException, FrameType] +_TraceDispatch: TypeAlias = Callable[[FrameType, str, Any], Any] # TODO: Recursive type GENERATOR_AND_COROUTINE_FLAGS: Literal[672] class BdbQuit(Exception): ... class Bdb: - skip: set[str] | None breaks: dict[str, list[int]] fncache: dict[str, str] @@ -31,7 +31,7 @@ class Bdb: def dispatch_line(self, frame: FrameType) -> _TraceDispatch: ... def dispatch_call(self, frame: FrameType, arg: None) -> _TraceDispatch: ... def dispatch_return(self, frame: FrameType, arg: Any) -> _TraceDispatch: ... - def dispatch_exception(self, frame: FrameType, arg: _ExcInfo) -> _TraceDispatch: ... + def dispatch_exception(self, frame: FrameType, arg: ExcInfo) -> _TraceDispatch: ... def is_skipped_module(self, module_name: str) -> bool: ... def stop_here(self, frame: FrameType) -> bool: ... def break_here(self, frame: FrameType) -> bool: ... @@ -40,7 +40,7 @@ class Bdb: def user_call(self, frame: FrameType, argument_list: None) -> None: ... def user_line(self, frame: FrameType) -> None: ... def user_return(self, frame: FrameType, return_value: Any) -> None: ... - def user_exception(self, frame: FrameType, exc_info: _ExcInfo) -> None: ... + def user_exception(self, frame: FrameType, exc_info: ExcInfo) -> None: ... def set_until(self, frame: FrameType, lineno: int | None = ...) -> None: ... def set_step(self) -> None: ... def set_next(self, frame: FrameType) -> None: ... diff --git a/mypy/typeshed/stdlib/binascii.pyi b/mypy/typeshed/stdlib/binascii.pyi index 317bb9979b92..53f72ad6a88f 100644 --- a/mypy/typeshed/stdlib/binascii.pyi +++ b/mypy/typeshed/stdlib/binascii.pyi @@ -1,36 +1,44 @@ import sys +from _typeshed import ReadableBuffer +from typing_extensions import TypeAlias -def a2b_uu(__data: str | bytes) -> bytes: ... +# Many functions in binascii accept buffer objects +# or ASCII-only strings. +_AsciiBuffer: TypeAlias = str | ReadableBuffer + +def a2b_uu(__data: _AsciiBuffer) -> bytes: ... if sys.version_info >= (3, 7): - def b2a_uu(__data: bytes, *, backtick: bool = ...) -> bytes: ... + def b2a_uu(__data: ReadableBuffer, *, backtick: bool = ...) -> bytes: ... else: - def b2a_uu(__data: bytes) -> bytes: ... + def b2a_uu(__data: ReadableBuffer) -> bytes: ... -def a2b_base64(__data: str | bytes) -> bytes: ... -def b2a_base64(__data: bytes, *, newline: bool = ...) -> bytes: ... -def a2b_qp(data: str | bytes, header: bool = ...) -> bytes: ... -def b2a_qp(data: bytes, quotetabs: bool = ..., istext: bool = ..., header: bool = ...) -> bytes: ... +def a2b_base64(__data: _AsciiBuffer) -> bytes: ... +def b2a_base64(__data: ReadableBuffer, *, newline: bool = ...) -> bytes: ... +def a2b_qp(data: _AsciiBuffer, header: bool = ...) -> bytes: ... +def b2a_qp(data: ReadableBuffer, quotetabs: bool = ..., istext: bool = ..., header: bool = ...) -> bytes: ... if sys.version_info < (3, 11): - def a2b_hqx(__data: str | bytes) -> bytes: ... - def rledecode_hqx(__data: bytes) -> bytes: ... - def rlecode_hqx(__data: bytes) -> bytes: ... - def b2a_hqx(__data: bytes) -> bytes: ... + def a2b_hqx(__data: _AsciiBuffer) -> bytes: ... + def rledecode_hqx(__data: ReadableBuffer) -> bytes: ... + def rlecode_hqx(__data: ReadableBuffer) -> bytes: ... + def b2a_hqx(__data: ReadableBuffer) -> bytes: ... -def crc_hqx(__data: bytes, __crc: int) -> int: ... -def crc32(__data: bytes, __crc: int = ...) -> int: ... -def b2a_hex(__data: bytes) -> bytes: ... +def crc_hqx(__data: ReadableBuffer, __crc: int) -> int: ... +def crc32(__data: ReadableBuffer, __crc: int = ...) -> int: ... if sys.version_info >= (3, 8): - def hexlify(data: bytes, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... + # sep must be str or bytes, not bytearray or any other buffer + def b2a_hex(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... + def hexlify(data: ReadableBuffer, sep: str | bytes = ..., bytes_per_sep: int = ...) -> bytes: ... else: - def hexlify(__data: bytes) -> bytes: ... + def b2a_hex(__data: ReadableBuffer) -> bytes: ... + def hexlify(__data: ReadableBuffer) -> bytes: ... -def a2b_hex(__hexstr: str | bytes) -> bytes: ... -def unhexlify(__hexstr: str | bytes) -> bytes: ... +def a2b_hex(__hexstr: _AsciiBuffer) -> bytes: ... +def unhexlify(__hexstr: _AsciiBuffer) -> bytes: ... class Error(ValueError): ... class Incomplete(Exception): ... diff --git a/mypy/typeshed/stdlib/binhex.pyi b/mypy/typeshed/stdlib/binhex.pyi index db002503c75f..27aa379f134d 100644 --- a/mypy/typeshed/stdlib/binhex.pyi +++ b/mypy/typeshed/stdlib/binhex.pyi @@ -1,5 +1,5 @@ from typing import IO, Any -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["binhex", "hexbin", "Error"] @@ -15,8 +15,8 @@ class FInfo: Creator: str Flags: int -_FileInfoTuple = tuple[str, FInfo, int, int] -_FileHandleUnion = str | IO[bytes] +_FileInfoTuple: TypeAlias = tuple[str, FInfo, int, int] +_FileHandleUnion: TypeAlias = str | IO[bytes] def getfileinfo(name: str) -> _FileInfoTuple: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index 9c5dfcfef22f..bf1e6cde2c08 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -22,29 +22,24 @@ from _typeshed import ( SupportsTrunc, SupportsWrite, ) -from collections.abc import Callable +from collections.abc import Awaitable, Callable, Iterable, Iterator, MutableSet, Reversible, Set as AbstractSet, Sized from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from types import CodeType, TracebackType, _Cell -from typing import ( + +# mypy crashes if any of {ByteString, Sequence, MutableSequence, Mapping, MutableMapping} are imported from collections.abc in builtins.pyi +from typing import ( # noqa: Y027 IO, - AbstractSet, Any, - Awaitable, BinaryIO, ByteString, ClassVar, Generic, - Iterable, - Iterator, Mapping, MutableMapping, MutableSequence, - MutableSet, NoReturn, Protocol, - Reversible, Sequence, - Sized, SupportsAbs, SupportsBytes, SupportsComplex, @@ -54,7 +49,7 @@ from typing import ( TypeVar, overload, ) -from typing_extensions import Literal, SupportsIndex, TypeGuard, final +from typing_extensions import Literal, SupportsIndex, TypeAlias, TypeGuard, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -194,12 +189,12 @@ class super: @overload def __init__(self) -> None: ... -_PositiveInteger = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] -_NegativeInteger = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20] +_PositiveInteger: TypeAlias = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] +_NegativeInteger: TypeAlias = Literal[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20] class int: @overload - def __new__(cls: type[Self], __x: str | bytes | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> Self: ... + def __new__(cls: type[Self], __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> Self: ... @overload def __new__(cls: type[Self], __x: str | bytes | bytearray, base: SupportsIndex) -> Self: ... if sys.version_info >= (3, 8): @@ -218,15 +213,29 @@ class int: if sys.version_info >= (3, 10): def bit_count(self) -> int: ... - def to_bytes(self, length: SupportsIndex, byteorder: Literal["little", "big"], *, signed: bool = ...) -> bytes: ... - @classmethod - def from_bytes( - cls: type[Self], - bytes: Iterable[SupportsIndex] | SupportsBytes, # TODO buffer object argument - byteorder: Literal["little", "big"], - *, - signed: bool = ..., - ) -> Self: ... + if sys.version_info >= (3, 11): + def to_bytes( + self, length: SupportsIndex = ..., byteorder: Literal["little", "big"] = ..., *, signed: bool = ... + ) -> bytes: ... + @classmethod + def from_bytes( + cls: type[Self], + bytes: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer, + byteorder: Literal["little", "big"] = ..., + *, + signed: bool = ..., + ) -> Self: ... + else: + def to_bytes(self, length: SupportsIndex, byteorder: Literal["little", "big"], *, signed: bool = ...) -> bytes: ... + @classmethod + def from_bytes( + cls: type[Self], + bytes: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer, + byteorder: Literal["little", "big"], + *, + signed: bool = ..., + ) -> Self: ... + def __add__(self, __x: int) -> int: ... def __sub__(self, __x: int) -> int: ... def __mul__(self, __x: int) -> int: ... @@ -288,7 +297,7 @@ class int: def __index__(self) -> int: ... class float: - def __new__(cls: type[Self], x: SupportsFloat | SupportsIndex | str | bytes | bytearray = ...) -> Self: ... + def __new__(cls: type[Self], x: SupportsFloat | SupportsIndex | str | ReadableBuffer = ...) -> Self: ... def as_integer_ratio(self) -> tuple[int, int]: ... def hex(self) -> str: ... def is_integer(self) -> bool: ... @@ -382,7 +391,7 @@ class str(Sequence[str]): @overload def __new__(cls: type[Self], object: object = ...) -> Self: ... @overload - def __new__(cls: type[Self], object: bytes, encoding: str = ..., errors: str = ...) -> Self: ... + def __new__(cls: type[Self], object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ... def capitalize(self) -> str: ... def casefold(self) -> str: ... def center(self, __width: SupportsIndex, __fillchar: str = ...) -> str: ... @@ -479,11 +488,14 @@ class bytes(ByteString): def capitalize(self) -> bytes: ... def center(self, __width: SupportsIndex, __fillchar: bytes = ...) -> bytes: ... def count( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... def decode(self, encoding: str = ..., errors: str = ...) -> str: ... def endswith( - self, __suffix: bytes | tuple[bytes, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, + __suffix: ReadableBuffer | tuple[ReadableBuffer, ...], + __start: SupportsIndex | None = ..., + __end: SupportsIndex | None = ..., ) -> bool: ... if sys.version_info >= (3, 8): def expandtabs(self, tabsize: SupportsIndex = ...) -> bytes: ... @@ -491,7 +503,7 @@ class bytes(ByteString): def expandtabs(self, tabsize: int = ...) -> bytes: ... def find( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... if sys.version_info >= (3, 8): def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... @@ -499,7 +511,7 @@ class bytes(ByteString): def hex(self) -> str: ... def index( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... def isalnum(self) -> bool: ... def isalpha(self) -> bool: ... @@ -511,41 +523,44 @@ class bytes(ByteString): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - def join(self, __iterable_of_bytes: Iterable[ByteString | memoryview]) -> bytes: ... - def ljust(self, __width: SupportsIndex, __fillchar: bytes = ...) -> bytes: ... + def join(self, __iterable_of_bytes: Iterable[ReadableBuffer]) -> bytes: ... + def ljust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = ...) -> bytes: ... def lower(self) -> bytes: ... - def lstrip(self, __bytes: bytes | None = ...) -> bytes: ... - def partition(self, __sep: bytes) -> tuple[bytes, bytes, bytes]: ... - def replace(self, __old: bytes, __new: bytes, __count: SupportsIndex = ...) -> bytes: ... + def lstrip(self, __bytes: ReadableBuffer | None = ...) -> bytes: ... + def partition(self, __sep: ReadableBuffer) -> tuple[bytes, bytes, bytes]: ... + def replace(self, __old: ReadableBuffer, __new: ReadableBuffer, __count: SupportsIndex = ...) -> bytes: ... if sys.version_info >= (3, 9): - def removeprefix(self, __prefix: bytes) -> bytes: ... - def removesuffix(self, __suffix: bytes) -> bytes: ... + def removeprefix(self, __prefix: ReadableBuffer) -> bytes: ... + def removesuffix(self, __suffix: ReadableBuffer) -> bytes: ... def rfind( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... def rindex( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... - def rjust(self, __width: SupportsIndex, __fillchar: bytes = ...) -> bytes: ... - def rpartition(self, __sep: bytes) -> tuple[bytes, bytes, bytes]: ... - def rsplit(self, sep: bytes | None = ..., maxsplit: SupportsIndex = ...) -> list[bytes]: ... - def rstrip(self, __bytes: bytes | None = ...) -> bytes: ... - def split(self, sep: bytes | None = ..., maxsplit: SupportsIndex = ...) -> list[bytes]: ... + def rjust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = ...) -> bytes: ... + def rpartition(self, __sep: ReadableBuffer) -> tuple[bytes, bytes, bytes]: ... + def rsplit(self, sep: ReadableBuffer | None = ..., maxsplit: SupportsIndex = ...) -> list[bytes]: ... + def rstrip(self, __bytes: ReadableBuffer | None = ...) -> bytes: ... + def split(self, sep: ReadableBuffer | None = ..., maxsplit: SupportsIndex = ...) -> list[bytes]: ... def splitlines(self, keepends: bool = ...) -> list[bytes]: ... def startswith( - self, __prefix: bytes | tuple[bytes, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, + __prefix: ReadableBuffer | tuple[ReadableBuffer, ...], + __start: SupportsIndex | None = ..., + __end: SupportsIndex | None = ..., ) -> bool: ... - def strip(self, __bytes: bytes | None = ...) -> bytes: ... + def strip(self, __bytes: ReadableBuffer | None = ...) -> bytes: ... def swapcase(self) -> bytes: ... def title(self) -> bytes: ... - def translate(self, __table: bytes | None, delete: bytes = ...) -> bytes: ... + def translate(self, __table: ReadableBuffer | None, delete: bytes = ...) -> bytes: ... def upper(self) -> bytes: ... def zfill(self, __width: SupportsIndex) -> bytes: ... @classmethod def fromhex(cls: type[Self], __s: str) -> Self: ... @staticmethod - def maketrans(__frm: bytes, __to: bytes) -> bytes: ... + def maketrans(__frm: ReadableBuffer, __to: ReadableBuffer) -> bytes: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... def __hash__(self) -> int: ... @@ -553,7 +568,7 @@ class bytes(ByteString): def __getitem__(self, __i: SupportsIndex) -> int: ... @overload def __getitem__(self, __s: slice) -> bytes: ... - def __add__(self, __s: bytes) -> bytes: ... + def __add__(self, __s: ReadableBuffer) -> bytes: ... def __mul__(self, __n: SupportsIndex) -> bytes: ... def __rmul__(self, __n: SupportsIndex) -> bytes: ... def __mod__(self, __value: Any) -> bytes: ... @@ -582,12 +597,15 @@ class bytearray(MutableSequence[int], ByteString): def capitalize(self) -> bytearray: ... def center(self, __width: SupportsIndex, __fillchar: bytes = ...) -> bytearray: ... def count( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... def copy(self) -> bytearray: ... def decode(self, encoding: str = ..., errors: str = ...) -> str: ... def endswith( - self, __suffix: bytes | tuple[bytes, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, + __suffix: ReadableBuffer | tuple[ReadableBuffer, ...], + __start: SupportsIndex | None = ..., + __end: SupportsIndex | None = ..., ) -> bool: ... if sys.version_info >= (3, 8): def expandtabs(self, tabsize: SupportsIndex = ...) -> bytearray: ... @@ -596,7 +614,7 @@ class bytearray(MutableSequence[int], ByteString): def extend(self, __iterable_of_ints: Iterable[SupportsIndex]) -> None: ... def find( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... if sys.version_info >= (3, 8): def hex(self, sep: str | bytes = ..., bytes_per_sep: SupportsIndex = ...) -> str: ... @@ -604,7 +622,7 @@ class bytearray(MutableSequence[int], ByteString): def hex(self) -> str: ... def index( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... def insert(self, __index: SupportsIndex, __item: SupportsIndex) -> None: ... def isalnum(self) -> bool: ... @@ -617,43 +635,46 @@ class bytearray(MutableSequence[int], ByteString): def isspace(self) -> bool: ... def istitle(self) -> bool: ... def isupper(self) -> bool: ... - def join(self, __iterable_of_bytes: Iterable[ByteString | memoryview]) -> bytearray: ... - def ljust(self, __width: SupportsIndex, __fillchar: bytes = ...) -> bytearray: ... + def join(self, __iterable_of_bytes: Iterable[ReadableBuffer]) -> bytearray: ... + def ljust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = ...) -> bytearray: ... def lower(self) -> bytearray: ... - def lstrip(self, __bytes: bytes | None = ...) -> bytearray: ... - def partition(self, __sep: bytes) -> tuple[bytearray, bytearray, bytearray]: ... + def lstrip(self, __bytes: ReadableBuffer | None = ...) -> bytearray: ... + def partition(self, __sep: ReadableBuffer) -> tuple[bytearray, bytearray, bytearray]: ... def pop(self, __index: int = ...) -> int: ... def remove(self, __value: int) -> None: ... if sys.version_info >= (3, 9): - def removeprefix(self, __prefix: bytes) -> bytearray: ... - def removesuffix(self, __suffix: bytes) -> bytearray: ... + def removeprefix(self, __prefix: ReadableBuffer) -> bytearray: ... + def removesuffix(self, __suffix: ReadableBuffer) -> bytearray: ... - def replace(self, __old: bytes, __new: bytes, __count: SupportsIndex = ...) -> bytearray: ... + def replace(self, __old: ReadableBuffer, __new: ReadableBuffer, __count: SupportsIndex = ...) -> bytearray: ... def rfind( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... def rindex( - self, __sub: bytes | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, __sub: ReadableBuffer | SupportsIndex, __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... ) -> int: ... - def rjust(self, __width: SupportsIndex, __fillchar: bytes = ...) -> bytearray: ... - def rpartition(self, __sep: bytes) -> tuple[bytearray, bytearray, bytearray]: ... - def rsplit(self, sep: bytes | None = ..., maxsplit: SupportsIndex = ...) -> list[bytearray]: ... - def rstrip(self, __bytes: bytes | None = ...) -> bytearray: ... - def split(self, sep: bytes | None = ..., maxsplit: SupportsIndex = ...) -> list[bytearray]: ... + def rjust(self, __width: SupportsIndex, __fillchar: bytes | bytearray = ...) -> bytearray: ... + def rpartition(self, __sep: ReadableBuffer) -> tuple[bytearray, bytearray, bytearray]: ... + def rsplit(self, sep: ReadableBuffer | None = ..., maxsplit: SupportsIndex = ...) -> list[bytearray]: ... + def rstrip(self, __bytes: ReadableBuffer | None = ...) -> bytearray: ... + def split(self, sep: ReadableBuffer | None = ..., maxsplit: SupportsIndex = ...) -> list[bytearray]: ... def splitlines(self, keepends: bool = ...) -> list[bytearray]: ... def startswith( - self, __prefix: bytes | tuple[bytes, ...], __start: SupportsIndex | None = ..., __end: SupportsIndex | None = ... + self, + __prefix: ReadableBuffer | tuple[ReadableBuffer, ...], + __start: SupportsIndex | None = ..., + __end: SupportsIndex | None = ..., ) -> bool: ... - def strip(self, __bytes: bytes | None = ...) -> bytearray: ... + def strip(self, __bytes: ReadableBuffer | None = ...) -> bytearray: ... def swapcase(self) -> bytearray: ... def title(self) -> bytearray: ... - def translate(self, __table: bytes | None, delete: bytes = ...) -> bytearray: ... + def translate(self, __table: ReadableBuffer | None, delete: bytes = ...) -> bytearray: ... def upper(self) -> bytearray: ... def zfill(self, __width: SupportsIndex) -> bytearray: ... @classmethod def fromhex(cls: type[Self], __string: str) -> Self: ... @staticmethod - def maketrans(__frm: bytes, __to: bytes) -> bytes: ... + def maketrans(__frm: ReadableBuffer, __to: ReadableBuffer) -> bytes: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[int]: ... __hash__: ClassVar[None] # type: ignore[assignment] @@ -666,14 +687,15 @@ class bytearray(MutableSequence[int], ByteString): @overload def __setitem__(self, __s: slice, __x: Iterable[SupportsIndex] | bytes) -> None: ... def __delitem__(self, __i: SupportsIndex | slice) -> None: ... - def __add__(self, __s: bytes) -> bytearray: ... - def __iadd__(self: Self, __s: Iterable[int]) -> Self: ... + def __add__(self, __s: ReadableBuffer) -> bytearray: ... + # The superclass wants us to accept Iterable[int], but that fails at runtime. + def __iadd__(self: Self, __s: ReadableBuffer) -> Self: ... # type: ignore[override] def __mul__(self, __n: SupportsIndex) -> bytearray: ... def __rmul__(self, __n: SupportsIndex) -> bytearray: ... def __imul__(self: Self, __n: SupportsIndex) -> Self: ... def __mod__(self, __value: Any) -> bytes: ... # Incompatible with Sequence.__contains__ - def __contains__(self, __o: SupportsIndex | bytes) -> bool: ... # type: ignore[override] + def __contains__(self, __o: SupportsIndex | ReadableBuffer) -> bool: ... # type: ignore[override] def __eq__(self, __x: object) -> bool: ... def __ne__(self, __x: object) -> bool: ... def __lt__(self, __x: bytes) -> bool: ... @@ -722,7 +744,7 @@ class memoryview(Sized, Sequence[int]): def __iter__(self) -> Iterator[int]: ... def __len__(self) -> int: ... @overload - def __setitem__(self, __s: slice, __o: bytes) -> None: ... + def __setitem__(self, __s: slice, __o: ReadableBuffer) -> None: ... @overload def __setitem__(self, __i: SupportsIndex, __o: SupportsIndex) -> None: ... if sys.version_info >= (3, 8): @@ -1086,8 +1108,8 @@ if sys.version_info >= (3, 10): # TODO: `compile` has a more precise return type in reality; work on a way of expressing that? if sys.version_info >= (3, 8): def compile( - source: str | bytes | AST, - filename: str | bytes | _PathLike[Any], + source: str | ReadableBuffer | AST, + filename: str | ReadableBuffer | _PathLike[Any], mode: str, flags: int = ..., dont_inherit: int = ..., @@ -1098,8 +1120,8 @@ if sys.version_info >= (3, 8): else: def compile( - source: str | bytes | AST, - filename: str | bytes | _PathLike[Any], + source: str | ReadableBuffer | AST, + filename: str | ReadableBuffer | _PathLike[Any], mode: str, flags: int = ..., dont_inherit: int = ..., @@ -1118,12 +1140,12 @@ def divmod(__x: _T_contra, __y: SupportsRDivMod[_T_contra, _T_co]) -> _T_co: ... # The `globals` argument to `eval` has to be `dict[str, Any]` rather than `dict[str, object]` due to invariance. # (The `globals` argument has to be a "real dict", rather than any old mapping, unlike the `locals` argument.) def eval( - __source: str | bytes | CodeType, __globals: dict[str, Any] | None = ..., __locals: Mapping[str, object] | None = ... + __source: str | ReadableBuffer | CodeType, __globals: dict[str, Any] | None = ..., __locals: Mapping[str, object] | None = ... ) -> Any: ... # Comment above regarding `eval` applies to `exec` as well def exec( - __source: str | bytes | CodeType, __globals: dict[str, Any] | None = ..., __locals: Mapping[str, object] | None = ... + __source: str | ReadableBuffer | CodeType, __globals: dict[str, Any] | None = ..., __locals: Mapping[str, object] | None = ... ) -> None: ... def exit(code: object = ...) -> NoReturn: ... @@ -1262,8 +1284,8 @@ def next(__i: SupportsNext[_T]) -> _T: ... def next(__i: SupportsNext[_T], __default: _VT) -> _T | _VT: ... def oct(__number: int | SupportsIndex) -> str: ... -_OpenFile = StrOrBytesPath | int -_Opener = Callable[[str, int], int] +_OpenFile = StrOrBytesPath | int # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed +_Opener: TypeAlias = Callable[[str, int], int] # Text mode: always returns a TextIOWrapper @overload @@ -1351,7 +1373,7 @@ def open( closefd: bool = ..., opener: _Opener | None = ..., ) -> IO[Any]: ... -def ord(__c: str | bytes) -> int: ... +def ord(__c: str | bytes | bytearray) -> int: ... class _SupportsWriteAndFlush(SupportsWrite[_T_contra], Protocol[_T_contra]): def flush(self) -> None: ... @@ -1381,7 +1403,9 @@ class _SupportsPow3NoneOnly(Protocol[_E, _T_co]): class _SupportsPow3(Protocol[_E, _M, _T_co]): def __pow__(self, __other: _E, __modulo: _M) -> _T_co: ... -_SupportsSomeKindOfPow = _SupportsPow2[Any, Any] | _SupportsPow3NoneOnly[Any, Any] | _SupportsPow3[Any, Any, Any] +_SupportsSomeKindOfPow = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed + _SupportsPow2[Any, Any] | _SupportsPow3NoneOnly[Any, Any] | _SupportsPow3[Any, Any, Any] +) if sys.version_info >= (3, 8): @overload @@ -1712,7 +1736,7 @@ class UnicodeDecodeError(UnicodeError): start: int end: int reason: str - def __init__(self, __encoding: str, __object: bytes, __start: int, __end: int, __reason: str) -> None: ... + def __init__(self, __encoding: str, __object: ReadableBuffer, __start: int, __end: int, __reason: str) -> None: ... class UnicodeEncodeError(UnicodeError): encoding: str @@ -1770,6 +1794,7 @@ if sys.version_info >= (3, 11): @overload def split(self: Self, __condition: Callable[[_BaseExceptionT_co], bool]) -> tuple[Self | None, Self | None]: ... def derive(self: Self, __excs: Sequence[_BaseExceptionT_co]) -> Self: ... + def __class_getitem__(cls, __item: Any) -> GenericAlias: ... class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception): def __new__(cls: type[Self], __message: str, __exceptions: Sequence[_ExceptionT_co]) -> Self: ... diff --git a/mypy/typeshed/stdlib/bz2.pyi b/mypy/typeshed/stdlib/bz2.pyi index f1467acadd10..cea317e28037 100644 --- a/mypy/typeshed/stdlib/bz2.pyi +++ b/mypy/typeshed/stdlib/bz2.pyi @@ -2,8 +2,9 @@ import _compression import sys from _compression import BaseStream from _typeshed import ReadableBuffer, Self, StrOrBytesPath, WriteableBuffer -from typing import IO, Any, Iterable, Protocol, TextIO, overload -from typing_extensions import Literal, SupportsIndex, final +from collections.abc import Iterable +from typing import IO, Any, Protocol, TextIO, overload +from typing_extensions import Literal, SupportsIndex, TypeAlias, final __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "open", "compress", "decompress"] @@ -21,10 +22,10 @@ class _WritableFileobj(Protocol): def compress(data: bytes, compresslevel: int = ...) -> bytes: ... def decompress(data: bytes) -> bytes: ... -_ReadBinaryMode = Literal["", "r", "rb"] -_WriteBinaryMode = Literal["w", "wb", "x", "xb", "a", "ab"] -_ReadTextMode = Literal["rt"] -_WriteTextMode = Literal["wt", "xt", "at"] +_ReadBinaryMode: TypeAlias = Literal["", "r", "rb"] +_WriteBinaryMode: TypeAlias = Literal["w", "wb", "x", "xb", "a", "ab"] +_ReadTextMode: TypeAlias = Literal["rt"] +_WriteTextMode: TypeAlias = Literal["wt", "xt", "at"] @overload def open( diff --git a/mypy/typeshed/stdlib/cProfile.pyi b/mypy/typeshed/stdlib/cProfile.pyi index 6f15e461e007..6e21fc92ade5 100644 --- a/mypy/typeshed/stdlib/cProfile.pyi +++ b/mypy/typeshed/stdlib/cProfile.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import Self, StrOrBytesPath +from collections.abc import Callable from types import CodeType -from typing import Any, Callable, TypeVar -from typing_extensions import ParamSpec +from typing import Any, TypeVar +from typing_extensions import ParamSpec, TypeAlias __all__ = ["run", "runctx", "Profile"] @@ -13,7 +14,7 @@ def runctx( _T = TypeVar("_T") _P = ParamSpec("_P") -_Label = tuple[str, int, str] +_Label: TypeAlias = tuple[str, int, str] class Profile: stats: dict[_Label, tuple[int, int, int, int, dict[_Label, tuple[int, int, int, int]]]] # undocumented diff --git a/mypy/typeshed/stdlib/calendar.pyi b/mypy/typeshed/stdlib/calendar.pyi index f106eb1213f1..c7e0a6b4606f 100644 --- a/mypy/typeshed/stdlib/calendar.pyi +++ b/mypy/typeshed/stdlib/calendar.pyi @@ -2,7 +2,7 @@ import datetime import sys from collections.abc import Iterable, Sequence from time import struct_time -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 10): __all__ = [ @@ -66,7 +66,7 @@ else: "weekheader", ] -_LocaleType = tuple[str | None, str | None] +_LocaleType: TypeAlias = tuple[str | None, str | None] class IllegalMonthError(ValueError): def __init__(self, month: int) -> None: ... diff --git a/mypy/typeshed/stdlib/cgi.pyi b/mypy/typeshed/stdlib/cgi.pyi index 0bd4e515ce51..5e7bebc2a7f8 100644 --- a/mypy/typeshed/stdlib/cgi.pyi +++ b/mypy/typeshed/stdlib/cgi.pyi @@ -1,6 +1,6 @@ import sys from _typeshed import Self, SupportsGetItem, SupportsItemAccess -from builtins import type as _type +from builtins import list as _list, type as _type from collections.abc import Iterable, Iterator, Mapping from types import TracebackType from typing import IO, Any, Protocol @@ -87,8 +87,6 @@ class MiniFieldStorage: value: Any def __init__(self, name: Any, value: Any) -> None: ... -_list = list - class FieldStorage: FieldStorageClass: _type | None keep_blank_values: int diff --git a/mypy/typeshed/stdlib/cgitb.pyi b/mypy/typeshed/stdlib/cgitb.pyi index 2db108ce75ec..ea5a8341bc5e 100644 --- a/mypy/typeshed/stdlib/cgitb.pyi +++ b/mypy/typeshed/stdlib/cgitb.pyi @@ -1,8 +1,7 @@ -from _typeshed import StrOrBytesPath +from _typeshed import OptExcInfo, StrOrBytesPath +from collections.abc import Callable from types import FrameType, TracebackType -from typing import IO, Any, Callable - -_ExcInfo = tuple[type[BaseException] | None, BaseException | None, TracebackType | None] +from typing import IO, Any __UNDEF__: object # undocumented sentinel @@ -14,8 +13,8 @@ def lookup(name: str, frame: FrameType, locals: dict[str, Any]) -> tuple[str | N def scanvars( reader: Callable[[], bytes], frame: FrameType, locals: dict[str, Any] ) -> list[tuple[str, str | None, Any]]: ... # undocumented -def html(einfo: _ExcInfo, context: int = ...) -> str: ... -def text(einfo: _ExcInfo, context: int = ...) -> str: ... +def html(einfo: OptExcInfo, context: int = ...) -> str: ... +def text(einfo: OptExcInfo, context: int = ...) -> str: ... class Hook: # undocumented def __init__( @@ -27,7 +26,7 @@ class Hook: # undocumented format: str = ..., ) -> None: ... def __call__(self, etype: type[BaseException] | None, evalue: BaseException | None, etb: TracebackType | None) -> None: ... - def handle(self, info: _ExcInfo | None = ...) -> None: ... + def handle(self, info: OptExcInfo | None = ...) -> None: ... -def handler(info: _ExcInfo | None = ...) -> None: ... +def handler(info: OptExcInfo | None = ...) -> None: ... def enable(display: int = ..., logdir: StrOrBytesPath | None = ..., context: int = ..., format: str = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/cmath.pyi b/mypy/typeshed/stdlib/cmath.pyi index ee51861fdaa0..30ada5d5b5ef 100644 --- a/mypy/typeshed/stdlib/cmath.pyi +++ b/mypy/typeshed/stdlib/cmath.pyi @@ -1,5 +1,6 @@ import sys from typing import SupportsComplex, SupportsFloat +from typing_extensions import TypeAlias if sys.version_info >= (3, 8): from typing import SupportsIndex @@ -13,9 +14,9 @@ nanj: complex tau: float if sys.version_info >= (3, 8): - _C = SupportsFloat | SupportsComplex | SupportsIndex | complex + _C: TypeAlias = SupportsFloat | SupportsComplex | SupportsIndex | complex else: - _C = SupportsFloat | SupportsComplex | complex + _C: TypeAlias = SupportsFloat | SupportsComplex | complex def acos(__z: _C) -> complex: ... def acosh(__z: _C) -> complex: ... diff --git a/mypy/typeshed/stdlib/cmd.pyi b/mypy/typeshed/stdlib/cmd.pyi index d1166db0f507..ddefff2edf05 100644 --- a/mypy/typeshed/stdlib/cmd.pyi +++ b/mypy/typeshed/stdlib/cmd.pyi @@ -1,4 +1,5 @@ -from typing import IO, Any, Callable +from collections.abc import Callable +from typing import IO, Any from typing_extensions import Literal __all__ = ["Cmd"] diff --git a/mypy/typeshed/stdlib/code.pyi b/mypy/typeshed/stdlib/code.pyi index 185c15853b82..59318aa353e2 100644 --- a/mypy/typeshed/stdlib/code.pyi +++ b/mypy/typeshed/stdlib/code.pyi @@ -1,6 +1,7 @@ from codeop import CommandCompiler +from collections.abc import Callable, Mapping from types import CodeType -from typing import Any, Callable, Mapping +from typing import Any __all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", "compile_command"] diff --git a/mypy/typeshed/stdlib/codecs.pyi b/mypy/typeshed/stdlib/codecs.pyi index 8fa93961d9d0..bba7703c7d33 100644 --- a/mypy/typeshed/stdlib/codecs.pyi +++ b/mypy/typeshed/stdlib/codecs.pyi @@ -2,8 +2,9 @@ import sys import types from _typeshed import Self from abc import abstractmethod -from typing import IO, Any, BinaryIO, Callable, Generator, Iterable, Iterator, Protocol, TextIO, overload -from typing_extensions import Literal +from collections.abc import Callable, Generator, Iterable, Iterator +from typing import IO, Any, BinaryIO, Protocol, TextIO, overload +from typing_extensions import Literal, TypeAlias __all__ = [ "register", @@ -83,7 +84,7 @@ class _IncrementalDecoder(Protocol): # The type ignore on `encode` and `decode` is to avoid issues with overlapping overloads, for more details, see #300 # https://docs.python.org/3/library/codecs.html#binary-transforms -_BytesToBytesEncoding = Literal[ +_BytesToBytesEncoding: TypeAlias = Literal[ "base64", "base_64", "base64_codec", @@ -102,7 +103,7 @@ _BytesToBytesEncoding = Literal[ "zlib_codec", ] # https://docs.python.org/3/library/codecs.html#text-transforms -_StrToStrEncoding = Literal["rot13", "rot_13"] +_StrToStrEncoding: TypeAlias = Literal["rot13", "rot_13"] @overload def encode(obj: bytes, encoding: _BytesToBytesEncoding, errors: str = ...) -> bytes: ... diff --git a/mypy/typeshed/stdlib/collections/__init__.pyi b/mypy/typeshed/stdlib/collections/__init__.pyi index e534c13c026b..2e88c0d8f474 100644 --- a/mypy/typeshed/stdlib/collections/__init__.pyi +++ b/mypy/typeshed/stdlib/collections/__init__.pyi @@ -8,7 +8,7 @@ if sys.version_info >= (3, 9): from types import GenericAlias if sys.version_info >= (3, 10): - from typing import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Reversible, Sequence + from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, MutableSequence, Reversible, Sequence else: from _collections_abc import * diff --git a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi index 4967d01f90dc..5b756d87d118 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/_base.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/_base.pyi @@ -2,10 +2,10 @@ import sys import threading from _typeshed import Self from abc import abstractmethod -from collections.abc import Container, Iterable, Iterator, Sequence +from collections.abc import Callable, Container, Iterable, Iterator, Sequence from logging import Logger from types import TracebackType -from typing import Any, Callable, Generic, Protocol, TypeVar, overload +from typing import Any, Generic, Protocol, TypeVar, overload from typing_extensions import Literal, ParamSpec, SupportsIndex if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/concurrent/futures/process.pyi b/mypy/typeshed/stdlib/concurrent/futures/process.pyi index 0c6c403949ad..4cfa8276897f 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/process.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/process.pyi @@ -1,11 +1,11 @@ import sys -from collections.abc import Generator, Iterable, Mapping, MutableMapping, MutableSequence +from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping, MutableSequence from multiprocessing.connection import Connection from multiprocessing.context import BaseContext, Process from multiprocessing.queues import Queue, SimpleQueue from threading import Lock, Semaphore, Thread from types import TracebackType -from typing import Any, Callable, Generic, TypeVar +from typing import Any, Generic, TypeVar from weakref import ref from ._base import Executor, Future diff --git a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi index 46ca681c54fc..e10254531788 100644 --- a/mypy/typeshed/stdlib/concurrent/futures/thread.pyi +++ b/mypy/typeshed/stdlib/concurrent/futures/thread.pyi @@ -1,8 +1,8 @@ import queue import sys -from collections.abc import Iterable, Mapping, Set as AbstractSet +from collections.abc import Callable, Iterable, Mapping, Set as AbstractSet from threading import Lock, Semaphore, Thread -from typing import Any, Callable, Generic, TypeVar +from typing import Any, Generic, TypeVar from weakref import ref from ._base import Executor, Future diff --git a/mypy/typeshed/stdlib/configparser.pyi b/mypy/typeshed/stdlib/configparser.pyi index 55df2ce58de7..5ffac353ab31 100644 --- a/mypy/typeshed/stdlib/configparser.pyi +++ b/mypy/typeshed/stdlib/configparser.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import StrOrBytesPath, StrPath, SupportsWrite from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence from typing import Any, ClassVar, Pattern, TypeVar, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = [ "NoSectionError", @@ -29,16 +29,16 @@ __all__ = [ ] # Internal type aliases -_section = Mapping[str, str] -_parser = MutableMapping[str, _section] -_converter = Callable[[str], Any] -_converters = dict[str, _converter] +_section: TypeAlias = Mapping[str, str] +_parser: TypeAlias = MutableMapping[str, _section] +_converter: TypeAlias = Callable[[str], Any] +_converters: TypeAlias = dict[str, _converter] _T = TypeVar("_T") if sys.version_info >= (3, 7): - _Path = StrOrBytesPath + _Path: TypeAlias = StrOrBytesPath else: - _Path = StrPath + _Path: TypeAlias = StrPath DEFAULTSECT: Literal["DEFAULT"] MAX_INTERPOLATION_DEPTH: Literal[10] diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index a60f8811e0ac..c93eeac3b44f 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -1,22 +1,9 @@ import sys from _typeshed import Self, StrOrBytesPath +from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Generator, Iterator from types import TracebackType -from typing import ( # noqa: Y027 - IO, - Any, - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - ContextManager, - Generator, - Generic, - Iterator, - Protocol, - TypeVar, - overload, -) -from typing_extensions import ParamSpec +from typing import IO, Any, ContextManager, Generic, Protocol, TypeVar, overload # noqa: Y027 +from typing_extensions import ParamSpec, TypeAlias if sys.version_info >= (3, 11): __all__ = [ @@ -90,7 +77,7 @@ _T_io = TypeVar("_T_io", bound=IO[str] | None) _F = TypeVar("_F", bound=Callable[..., Any]) _P = ParamSpec("_P") -_ExitFunc = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] +_ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] _CM_EF = TypeVar("_CM_EF", AbstractContextManager[Any], _ExitFunc) class ContextDecorator: @@ -189,7 +176,7 @@ class ExitStack(AbstractContextManager[ExitStack]): ) -> bool: ... if sys.version_info >= (3, 7): - _ExitCoroFunc = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], Awaitable[bool]] + _ExitCoroFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], Awaitable[bool]] _ACM_EF = TypeVar("_ACM_EF", AbstractAsyncContextManager[Any], _ExitCoroFunc) class AsyncExitStack(AbstractAsyncContextManager[AsyncExitStack]): diff --git a/mypy/typeshed/stdlib/contextvars.pyi b/mypy/typeshed/stdlib/contextvars.pyi index 6b5661dd69eb..341cd8491caf 100644 --- a/mypy/typeshed/stdlib/contextvars.pyi +++ b/mypy/typeshed/stdlib/contextvars.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Callable, ClassVar, Generic, Iterator, Mapping, TypeVar, overload +from collections.abc import Callable, Iterator, Mapping +from typing import Any, ClassVar, Generic, TypeVar, overload from typing_extensions import ParamSpec, final if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/copyreg.pyi b/mypy/typeshed/stdlib/copyreg.pyi index 4844a8028c5c..4403550b587e 100644 --- a/mypy/typeshed/stdlib/copyreg.pyi +++ b/mypy/typeshed/stdlib/copyreg.pyi @@ -1,7 +1,9 @@ -from typing import Any, Callable, Hashable, SupportsInt, TypeVar, Union +from collections.abc import Callable, Hashable +from typing import Any, SupportsInt, TypeVar, Union +from typing_extensions import TypeAlias _T = TypeVar("_T") -_Reduce = Union[tuple[Callable[..., _T], tuple[Any, ...]], tuple[Callable[..., _T], tuple[Any, ...], Any | None]] +_Reduce: TypeAlias = Union[tuple[Callable[..., _T], tuple[Any, ...]], tuple[Callable[..., _T], tuple[Any, ...], Any | None]] __all__ = ["pickle", "constructor", "add_extension", "remove_extension", "clear_extension_cache"] @@ -15,5 +17,5 @@ def add_extension(module: Hashable, name: Hashable, code: SupportsInt) -> None: def remove_extension(module: Hashable, name: Hashable, code: int) -> None: ... def clear_extension_cache() -> None: ... -_DispatchTableType = dict[type, Callable[[Any], str | _Reduce[Any]]] # imported by multiprocessing.reduction +_DispatchTableType: TypeAlias = dict[type, Callable[[Any], str | _Reduce[Any]]] # imported by multiprocessing.reduction dispatch_table: _DispatchTableType # undocumented diff --git a/mypy/typeshed/stdlib/ctypes/__init__.pyi b/mypy/typeshed/stdlib/ctypes/__init__.pyi index 4a03886e8dd2..53a382ec0e71 100644 --- a/mypy/typeshed/stdlib/ctypes/__init__.pyi +++ b/mypy/typeshed/stdlib/ctypes/__init__.pyi @@ -1,7 +1,9 @@ import sys from _typeshed import ReadableBuffer, Self, WriteableBuffer from abc import abstractmethod -from typing import Any, Callable, ClassVar, Generic, Iterable, Iterator, Mapping, Sequence, TypeVar, Union as _UnionT, overload +from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence +from typing import Any, ClassVar, Generic, TypeVar, Union as _UnionT, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -84,8 +86,8 @@ class _CData(metaclass=_CDataMeta): class _CanCastTo(_CData): ... class _PointerLike(_CanCastTo): ... -_ECT = Callable[[type[_CData] | None, _FuncPointer, tuple[_CData, ...]], _CData] -_PF = _UnionT[tuple[int], tuple[int, str], tuple[int, str, Any]] +_ECT: TypeAlias = Callable[[type[_CData] | None, _FuncPointer, tuple[_CData, ...]], _CData] +_PF: TypeAlias = _UnionT[tuple[int], tuple[int, str], tuple[int, str, Any]] class _FuncPointer(_PointerLike, _CData): restype: type[_CData] | Callable[[int], Any] | None @@ -121,12 +123,12 @@ class _CArgObject: ... # Any type that can be implicitly converted to c_void_p when passed as a C function argument. # (bytes is not included here, see below.) -_CVoidPLike = _PointerLike | Array[Any] | _CArgObject | int +_CVoidPLike: TypeAlias = _PointerLike | Array[Any] | _CArgObject | int # Same as above, but including types known to be read-only (i. e. bytes). # This distinction is not strictly necessary (ctypes doesn't differentiate between const # and non-const pointers), but it catches errors like memmove(b'foo', buf, 4) # when memmove(buf, b'foo', 4) was intended. -_CVoidConstPLike = _CVoidPLike | bytes +_CVoidConstPLike: TypeAlias = _CVoidPLike | bytes def addressof(obj: _CData) -> int: ... def alignment(obj_or_type: _CData | type[_CData]) -> int: ... diff --git a/mypy/typeshed/stdlib/ctypes/wintypes.pyi b/mypy/typeshed/stdlib/ctypes/wintypes.pyi index c178a9bdf936..9536114b786a 100644 --- a/mypy/typeshed/stdlib/ctypes/wintypes.pyi +++ b/mypy/typeshed/stdlib/ctypes/wintypes.pyi @@ -20,6 +20,7 @@ from ctypes import ( c_wchar_p, pointer, ) +from typing_extensions import TypeAlias BYTE = c_byte WORD = c_ushort @@ -182,53 +183,53 @@ class WIN32_FIND_DATAW(Structure): # These pointer type definitions use pointer[...] instead of POINTER(...), to allow them # to be used in type annotations. -PBOOL = pointer[BOOL] -LPBOOL = pointer[BOOL] -PBOOLEAN = pointer[BOOLEAN] -PBYTE = pointer[BYTE] -LPBYTE = pointer[BYTE] -PCHAR = pointer[CHAR] -LPCOLORREF = pointer[COLORREF] -PDWORD = pointer[DWORD] -LPDWORD = pointer[DWORD] -PFILETIME = pointer[FILETIME] -LPFILETIME = pointer[FILETIME] -PFLOAT = pointer[FLOAT] -PHANDLE = pointer[HANDLE] -LPHANDLE = pointer[HANDLE] -PHKEY = pointer[HKEY] -LPHKL = pointer[HKL] -PINT = pointer[INT] -LPINT = pointer[INT] -PLARGE_INTEGER = pointer[LARGE_INTEGER] -PLCID = pointer[LCID] -PLONG = pointer[LONG] -LPLONG = pointer[LONG] -PMSG = pointer[MSG] -LPMSG = pointer[MSG] -PPOINT = pointer[POINT] -LPPOINT = pointer[POINT] -PPOINTL = pointer[POINTL] -PRECT = pointer[RECT] -LPRECT = pointer[RECT] -PRECTL = pointer[RECTL] -LPRECTL = pointer[RECTL] -LPSC_HANDLE = pointer[SC_HANDLE] -PSHORT = pointer[SHORT] -PSIZE = pointer[SIZE] -LPSIZE = pointer[SIZE] -PSIZEL = pointer[SIZEL] -LPSIZEL = pointer[SIZEL] -PSMALL_RECT = pointer[SMALL_RECT] -PUINT = pointer[UINT] -LPUINT = pointer[UINT] -PULARGE_INTEGER = pointer[ULARGE_INTEGER] -PULONG = pointer[ULONG] -PUSHORT = pointer[USHORT] -PWCHAR = pointer[WCHAR] -PWIN32_FIND_DATAA = pointer[WIN32_FIND_DATAA] -LPWIN32_FIND_DATAA = pointer[WIN32_FIND_DATAA] -PWIN32_FIND_DATAW = pointer[WIN32_FIND_DATAW] -LPWIN32_FIND_DATAW = pointer[WIN32_FIND_DATAW] -PWORD = pointer[WORD] -LPWORD = pointer[WORD] +PBOOL: TypeAlias = pointer[BOOL] +LPBOOL: TypeAlias = pointer[BOOL] +PBOOLEAN: TypeAlias = pointer[BOOLEAN] +PBYTE: TypeAlias = pointer[BYTE] +LPBYTE: TypeAlias = pointer[BYTE] +PCHAR: TypeAlias = pointer[CHAR] +LPCOLORREF: TypeAlias = pointer[COLORREF] +PDWORD: TypeAlias = pointer[DWORD] +LPDWORD: TypeAlias = pointer[DWORD] +PFILETIME: TypeAlias = pointer[FILETIME] +LPFILETIME: TypeAlias = pointer[FILETIME] +PFLOAT: TypeAlias = pointer[FLOAT] +PHANDLE: TypeAlias = pointer[HANDLE] +LPHANDLE: TypeAlias = pointer[HANDLE] +PHKEY: TypeAlias = pointer[HKEY] +LPHKL: TypeAlias = pointer[HKL] +PINT: TypeAlias = pointer[INT] +LPINT: TypeAlias = pointer[INT] +PLARGE_INTEGER: TypeAlias = pointer[LARGE_INTEGER] +PLCID: TypeAlias = pointer[LCID] +PLONG: TypeAlias = pointer[LONG] +LPLONG: TypeAlias = pointer[LONG] +PMSG: TypeAlias = pointer[MSG] +LPMSG: TypeAlias = pointer[MSG] +PPOINT: TypeAlias = pointer[POINT] +LPPOINT: TypeAlias = pointer[POINT] +PPOINTL: TypeAlias = pointer[POINTL] +PRECT: TypeAlias = pointer[RECT] +LPRECT: TypeAlias = pointer[RECT] +PRECTL: TypeAlias = pointer[RECTL] +LPRECTL: TypeAlias = pointer[RECTL] +LPSC_HANDLE: TypeAlias = pointer[SC_HANDLE] +PSHORT: TypeAlias = pointer[SHORT] +PSIZE: TypeAlias = pointer[SIZE] +LPSIZE: TypeAlias = pointer[SIZE] +PSIZEL: TypeAlias = pointer[SIZEL] +LPSIZEL: TypeAlias = pointer[SIZEL] +PSMALL_RECT: TypeAlias = pointer[SMALL_RECT] +PUINT: TypeAlias = pointer[UINT] +LPUINT: TypeAlias = pointer[UINT] +PULARGE_INTEGER: TypeAlias = pointer[ULARGE_INTEGER] +PULONG: TypeAlias = pointer[ULONG] +PUSHORT: TypeAlias = pointer[USHORT] +PWCHAR: TypeAlias = pointer[WCHAR] +PWIN32_FIND_DATAA: TypeAlias = pointer[WIN32_FIND_DATAA] +LPWIN32_FIND_DATAA: TypeAlias = pointer[WIN32_FIND_DATAA] +PWIN32_FIND_DATAW: TypeAlias = pointer[WIN32_FIND_DATAW] +LPWIN32_FIND_DATAW: TypeAlias = pointer[WIN32_FIND_DATAW] +PWORD: TypeAlias = pointer[WORD] +LPWORD: TypeAlias = pointer[WORD] diff --git a/mypy/typeshed/stdlib/curses/__init__.pyi b/mypy/typeshed/stdlib/curses/__init__.pyi index ee74c13b6b50..f80ed442ea9c 100644 --- a/mypy/typeshed/stdlib/curses/__init__.pyi +++ b/mypy/typeshed/stdlib/curses/__init__.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Callable, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar if sys.platform != "win32": from _curses import * diff --git a/mypy/typeshed/stdlib/curses/textpad.pyi b/mypy/typeshed/stdlib/curses/textpad.pyi index b8a9c843f402..ad9983431fc7 100644 --- a/mypy/typeshed/stdlib/curses/textpad.pyi +++ b/mypy/typeshed/stdlib/curses/textpad.pyi @@ -1,5 +1,5 @@ import sys -from typing import Callable +from collections.abc import Callable if sys.platform != "win32": from _curses import _CursesWindow diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index d82e9a2bb526..f58c6f9f1460 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -2,7 +2,8 @@ import enum import sys import types from builtins import type as Type # alias to avoid name clashes with fields named "type" -from typing import Any, Callable, Generic, Iterable, Mapping, Protocol, TypeVar, overload +from collections.abc import Callable, Iterable, Mapping +from typing import Any, Generic, Protocol, TypeVar, overload from typing_extensions import Literal if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 220e07e25fe0..113c679743fd 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -2,7 +2,7 @@ import sys from _typeshed import Self from time import struct_time from typing import ClassVar, NamedTuple, NoReturn, SupportsAbs, TypeVar, overload -from typing_extensions import Literal, final +from typing_extensions import Literal, TypeAlias, final if sys.version_info >= (3, 9): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR") @@ -19,7 +19,7 @@ class tzinfo: def fromutc(self, __dt: datetime) -> datetime: ... # Alias required to avoid name conflicts with date(time).tzinfo. -_tzinfo = tzinfo +_tzinfo: TypeAlias = tzinfo @final class timezone(tzinfo): @@ -150,8 +150,8 @@ class time: fold: int = ..., ) -> Self: ... -_date = date -_time = time +_date: TypeAlias = date +_time: TypeAlias = time class timedelta(SupportsAbs[timedelta]): min: ClassVar[timedelta] diff --git a/mypy/typeshed/stdlib/dbm/__init__.pyi b/mypy/typeshed/stdlib/dbm/__init__.pyi index d43a2415caff..9e99f0d5e74c 100644 --- a/mypy/typeshed/stdlib/dbm/__init__.pyi +++ b/mypy/typeshed/stdlib/dbm/__init__.pyi @@ -1,13 +1,13 @@ from _typeshed import Self +from collections.abc import Iterator, MutableMapping from types import TracebackType -from typing import Iterator, MutableMapping -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["open", "whichdb", "error"] -_KeyType = str | bytes -_ValueType = str | bytes -_TFlags = Literal[ +_KeyType: TypeAlias = str | bytes +_ValueType: TypeAlias = str | bytes +_TFlags: TypeAlias = Literal[ "r", "w", "c", diff --git a/mypy/typeshed/stdlib/dbm/dumb.pyi b/mypy/typeshed/stdlib/dbm/dumb.pyi index 5af95ace72ad..4fd199f19728 100644 --- a/mypy/typeshed/stdlib/dbm/dumb.pyi +++ b/mypy/typeshed/stdlib/dbm/dumb.pyi @@ -1,11 +1,12 @@ from _typeshed import Self +from collections.abc import Iterator, MutableMapping from types import TracebackType -from typing import Iterator, MutableMapping +from typing_extensions import TypeAlias __all__ = ["error", "open"] -_KeyType = str | bytes -_ValueType = str | bytes +_KeyType: TypeAlias = str | bytes +_ValueType: TypeAlias = str | bytes error = OSError diff --git a/mypy/typeshed/stdlib/dbm/gnu.pyi b/mypy/typeshed/stdlib/dbm/gnu.pyi index 9a603228a691..561206c4e0be 100644 --- a/mypy/typeshed/stdlib/dbm/gnu.pyi +++ b/mypy/typeshed/stdlib/dbm/gnu.pyi @@ -2,11 +2,12 @@ import sys from _typeshed import Self from types import TracebackType from typing import TypeVar, overload +from typing_extensions import TypeAlias if sys.platform != "win32": _T = TypeVar("_T") - _KeyType = str | bytes - _ValueType = str | bytes + _KeyType: TypeAlias = str | bytes + _ValueType: TypeAlias = str | bytes open_flags: str diff --git a/mypy/typeshed/stdlib/dbm/ndbm.pyi b/mypy/typeshed/stdlib/dbm/ndbm.pyi index 8405bec2bcf2..f1032bf3cae7 100644 --- a/mypy/typeshed/stdlib/dbm/ndbm.pyi +++ b/mypy/typeshed/stdlib/dbm/ndbm.pyi @@ -2,11 +2,12 @@ import sys from _typeshed import Self from types import TracebackType from typing import TypeVar, overload +from typing_extensions import TypeAlias if sys.platform != "win32": _T = TypeVar("_T") - _KeyType = str | bytes - _ValueType = str | bytes + _KeyType: TypeAlias = str | bytes + _ValueType: TypeAlias = str | bytes class error(OSError): ... library: str diff --git a/mypy/typeshed/stdlib/decimal.pyi b/mypy/typeshed/stdlib/decimal.pyi index 819ed1641448..35fc4405f11b 100644 --- a/mypy/typeshed/stdlib/decimal.pyi +++ b/mypy/typeshed/stdlib/decimal.pyi @@ -1,262 +1,2 @@ -import numbers -import sys -from _typeshed import Self -from types import TracebackType -from typing import Any, Container, NamedTuple, Sequence, Union, overload - -_Decimal = Decimal | int -_DecimalNew = Union[Decimal, float, str, tuple[int, Sequence[int], int]] -_ComparableNum = Decimal | float | numbers.Rational - -__libmpdec_version__: str - -class DecimalTuple(NamedTuple): - sign: int - digits: tuple[int, ...] - exponent: int - -ROUND_DOWN: str -ROUND_HALF_UP: str -ROUND_HALF_EVEN: str -ROUND_CEILING: str -ROUND_FLOOR: str -ROUND_UP: str -ROUND_HALF_DOWN: str -ROUND_05UP: str - -if sys.version_info >= (3, 7): - HAVE_CONTEXTVAR: bool -HAVE_THREADS: bool -MAX_EMAX: int -MAX_PREC: int -MIN_EMIN: int -MIN_ETINY: int - -class DecimalException(ArithmeticError): ... -class Clamped(DecimalException): ... -class InvalidOperation(DecimalException): ... -class ConversionSyntax(InvalidOperation): ... -class DivisionByZero(DecimalException, ZeroDivisionError): ... -class DivisionImpossible(InvalidOperation): ... -class DivisionUndefined(InvalidOperation, ZeroDivisionError): ... -class Inexact(DecimalException): ... -class InvalidContext(InvalidOperation): ... -class Rounded(DecimalException): ... -class Subnormal(DecimalException): ... -class Overflow(Inexact, Rounded): ... -class Underflow(Inexact, Rounded, Subnormal): ... -class FloatOperation(DecimalException, TypeError): ... - -def setcontext(__context: Context) -> None: ... -def getcontext() -> Context: ... -def localcontext(ctx: Context | None = ...) -> _ContextManager: ... - -class Decimal: - def __new__(cls: type[Self], value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... - @classmethod - def from_float(cls: type[Self], __f: float) -> Self: ... - def __bool__(self) -> bool: ... - def compare(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def __hash__(self) -> int: ... - def as_tuple(self) -> DecimalTuple: ... - def as_integer_ratio(self) -> tuple[int, int]: ... - def to_eng_string(self, context: Context | None = ...) -> str: ... - def __abs__(self) -> Decimal: ... - def __add__(self, __other: _Decimal) -> Decimal: ... - def __divmod__(self, __other: _Decimal) -> tuple[Decimal, Decimal]: ... - def __eq__(self, __other: object) -> bool: ... - def __floordiv__(self, __other: _Decimal) -> Decimal: ... - def __ge__(self, __other: _ComparableNum) -> bool: ... - def __gt__(self, __other: _ComparableNum) -> bool: ... - def __le__(self, __other: _ComparableNum) -> bool: ... - def __lt__(self, __other: _ComparableNum) -> bool: ... - def __mod__(self, __other: _Decimal) -> Decimal: ... - def __mul__(self, __other: _Decimal) -> Decimal: ... - def __neg__(self) -> Decimal: ... - def __pos__(self) -> Decimal: ... - def __pow__(self, __other: _Decimal, __modulo: _Decimal | None = ...) -> Decimal: ... - def __radd__(self, __other: _Decimal) -> Decimal: ... - def __rdivmod__(self, __other: _Decimal) -> tuple[Decimal, Decimal]: ... - def __rfloordiv__(self, __other: _Decimal) -> Decimal: ... - def __rmod__(self, __other: _Decimal) -> Decimal: ... - def __rmul__(self, __other: _Decimal) -> Decimal: ... - def __rsub__(self, __other: _Decimal) -> Decimal: ... - def __rtruediv__(self, __other: _Decimal) -> Decimal: ... - def __sub__(self, __other: _Decimal) -> Decimal: ... - def __truediv__(self, __other: _Decimal) -> Decimal: ... - def remainder_near(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def __float__(self) -> float: ... - def __int__(self) -> int: ... - def __trunc__(self) -> int: ... - @property - def real(self) -> Decimal: ... - @property - def imag(self) -> Decimal: ... - def conjugate(self) -> Decimal: ... - def __complex__(self) -> complex: ... - @overload - def __round__(self) -> int: ... - @overload - def __round__(self, __ndigits: int) -> Decimal: ... - def __floor__(self) -> int: ... - def __ceil__(self) -> int: ... - def fma(self, other: _Decimal, third: _Decimal, context: Context | None = ...) -> Decimal: ... - def __rpow__(self, __other: _Decimal, __context: Context | None = ...) -> Decimal: ... - def normalize(self, context: Context | None = ...) -> Decimal: ... - def quantize(self, exp: _Decimal, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... - def same_quantum(self, other: _Decimal, context: Context | None = ...) -> bool: ... - def to_integral_exact(self, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... - def to_integral_value(self, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... - def to_integral(self, rounding: str | None = ..., context: Context | None = ...) -> Decimal: ... - def sqrt(self, context: Context | None = ...) -> Decimal: ... - def max(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def min(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def adjusted(self) -> int: ... - def canonical(self) -> Decimal: ... - def compare_signal(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def compare_total(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def compare_total_mag(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def copy_abs(self) -> Decimal: ... - def copy_negate(self) -> Decimal: ... - def copy_sign(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def exp(self, context: Context | None = ...) -> Decimal: ... - def is_canonical(self) -> bool: ... - def is_finite(self) -> bool: ... - def is_infinite(self) -> bool: ... - def is_nan(self) -> bool: ... - def is_normal(self, context: Context | None = ...) -> bool: ... - def is_qnan(self) -> bool: ... - def is_signed(self) -> bool: ... - def is_snan(self) -> bool: ... - def is_subnormal(self, context: Context | None = ...) -> bool: ... - def is_zero(self) -> bool: ... - def ln(self, context: Context | None = ...) -> Decimal: ... - def log10(self, context: Context | None = ...) -> Decimal: ... - def logb(self, context: Context | None = ...) -> Decimal: ... - def logical_and(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def logical_invert(self, context: Context | None = ...) -> Decimal: ... - def logical_or(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def logical_xor(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def max_mag(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def min_mag(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def next_minus(self, context: Context | None = ...) -> Decimal: ... - def next_plus(self, context: Context | None = ...) -> Decimal: ... - def next_toward(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def number_class(self, context: Context | None = ...) -> str: ... - def radix(self) -> Decimal: ... - def rotate(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def scaleb(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def shift(self, other: _Decimal, context: Context | None = ...) -> Decimal: ... - def __reduce__(self: Self) -> tuple[type[Self], tuple[str]]: ... - def __copy__(self: Self) -> Self: ... - def __deepcopy__(self: Self, __memo: Any) -> Self: ... - def __format__(self, __specifier: str, __context: Context | None = ...) -> str: ... - -class _ContextManager: - new_context: Context - saved_context: Context - def __init__(self, new_context: Context) -> None: ... - def __enter__(self) -> Context: ... - def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ... - -_TrapType = type[DecimalException] - -class Context: - prec: int - rounding: str - Emin: int - Emax: int - capitals: int - clamp: int - traps: dict[_TrapType, bool] - flags: dict[_TrapType, bool] - def __init__( - self, - prec: int | None = ..., - rounding: str | None = ..., - Emin: int | None = ..., - Emax: int | None = ..., - capitals: int | None = ..., - clamp: int | None = ..., - flags: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - traps: None | dict[_TrapType, bool] | Container[_TrapType] = ..., - _ignored_flags: list[_TrapType] | None = ..., - ) -> None: ... - # __setattr__() only allows to set a specific set of attributes, - # already defined above. - def __delattr__(self, __name: str) -> None: ... - def __reduce__(self: Self) -> tuple[type[Self], tuple[Any, ...]]: ... - def clear_flags(self) -> None: ... - def clear_traps(self) -> None: ... - def copy(self) -> Context: ... - def __copy__(self) -> Context: ... - __hash__: Any - def Etiny(self) -> int: ... - def Etop(self) -> int: ... - def create_decimal(self, __num: _DecimalNew = ...) -> Decimal: ... - def create_decimal_from_float(self, __f: float) -> Decimal: ... - def abs(self, __x: _Decimal) -> Decimal: ... - def add(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def canonical(self, __x: Decimal) -> Decimal: ... - def compare(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def compare_signal(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def compare_total(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def compare_total_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def copy_abs(self, __x: _Decimal) -> Decimal: ... - def copy_decimal(self, __x: _Decimal) -> Decimal: ... - def copy_negate(self, __x: _Decimal) -> Decimal: ... - def copy_sign(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def divide(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def divide_int(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def divmod(self, __x: _Decimal, __y: _Decimal) -> tuple[Decimal, Decimal]: ... - def exp(self, __x: _Decimal) -> Decimal: ... - def fma(self, __x: _Decimal, __y: _Decimal, __z: _Decimal) -> Decimal: ... - def is_canonical(self, __x: _Decimal) -> bool: ... - def is_finite(self, __x: _Decimal) -> bool: ... - def is_infinite(self, __x: _Decimal) -> bool: ... - def is_nan(self, __x: _Decimal) -> bool: ... - def is_normal(self, __x: _Decimal) -> bool: ... - def is_qnan(self, __x: _Decimal) -> bool: ... - def is_signed(self, __x: _Decimal) -> bool: ... - def is_snan(self, __x: _Decimal) -> bool: ... - def is_subnormal(self, __x: _Decimal) -> bool: ... - def is_zero(self, __x: _Decimal) -> bool: ... - def ln(self, __x: _Decimal) -> Decimal: ... - def log10(self, __x: _Decimal) -> Decimal: ... - def logb(self, __x: _Decimal) -> Decimal: ... - def logical_and(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def logical_invert(self, __x: _Decimal) -> Decimal: ... - def logical_or(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def logical_xor(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def max(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def max_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def min(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def min_mag(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def minus(self, __x: _Decimal) -> Decimal: ... - def multiply(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def next_minus(self, __x: _Decimal) -> Decimal: ... - def next_plus(self, __x: _Decimal) -> Decimal: ... - def next_toward(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def normalize(self, __x: _Decimal) -> Decimal: ... - def number_class(self, __x: _Decimal) -> str: ... - def plus(self, __x: _Decimal) -> Decimal: ... - def power(self, a: _Decimal, b: _Decimal, modulo: _Decimal | None = ...) -> Decimal: ... - def quantize(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def radix(self) -> Decimal: ... - def remainder(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def remainder_near(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def rotate(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def same_quantum(self, __x: _Decimal, __y: _Decimal) -> bool: ... - def scaleb(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def shift(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def sqrt(self, __x: _Decimal) -> Decimal: ... - def subtract(self, __x: _Decimal, __y: _Decimal) -> Decimal: ... - def to_eng_string(self, __x: _Decimal) -> str: ... - def to_sci_string(self, __x: _Decimal) -> str: ... - def to_integral_exact(self, __x: _Decimal) -> Decimal: ... - def to_integral_value(self, __x: _Decimal) -> Decimal: ... - def to_integral(self, __x: _Decimal) -> Decimal: ... - -DefaultContext: Context -BasicContext: Context -ExtendedContext: Context +from _decimal import * +from _decimal import __libmpdec_version__ as __libmpdec_version__, __version__ as __version__ diff --git a/mypy/typeshed/stdlib/difflib.pyi b/mypy/typeshed/stdlib/difflib.pyi index a572430155e9..87e3768034bf 100644 --- a/mypy/typeshed/stdlib/difflib.pyi +++ b/mypy/typeshed/stdlib/difflib.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, AnyStr, Callable, Generic, Iterable, Iterator, NamedTuple, Sequence, TypeVar, overload +from collections.abc import Callable, Iterable, Iterator, Sequence +from typing import Any, AnyStr, Generic, NamedTuple, TypeVar, overload if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/dis.pyi b/mypy/typeshed/stdlib/dis.pyi index 910458c08e00..9a99d4498668 100644 --- a/mypy/typeshed/stdlib/dis.pyi +++ b/mypy/typeshed/stdlib/dis.pyi @@ -1,8 +1,10 @@ import sys import types from _typeshed import Self +from collections.abc import Callable, Iterator from opcode import * # `dis` re-exports it as a part of public API -from typing import IO, Any, Callable, Iterator, NamedTuple +from typing import IO, Any, NamedTuple +from typing_extensions import TypeAlias __all__ = [ "code_info", @@ -34,28 +36,63 @@ __all__ = [ # Strictly this should not have to include Callable, but mypy doesn't use FunctionType # for functions (python/mypy#3171) -_HaveCodeType = types.MethodType | types.FunctionType | types.CodeType | type | Callable[..., Any] -_HaveCodeOrStringType = _HaveCodeType | str | bytes +_HaveCodeType: TypeAlias = types.MethodType | types.FunctionType | types.CodeType | type | Callable[..., Any] +_HaveCodeOrStringType: TypeAlias = _HaveCodeType | str | bytes -class Instruction(NamedTuple): - opname: str - opcode: int - arg: int | None - argval: Any - argrepr: str - offset: int - starts_line: int | None - is_jump_target: bool +if sys.version_info >= (3, 11): + class Positions(NamedTuple): + lineno: int | None = ... + end_lineno: int | None = ... + col_offset: int | None = ... + end_col_offset: int | None = ... + +if sys.version_info >= (3, 11): + class Instruction(NamedTuple): + opname: str + opcode: int + arg: int | None + argval: Any + argrepr: str + offset: int + starts_line: int | None + is_jump_target: bool + positions: Positions | None = ... + +else: + class Instruction(NamedTuple): + opname: str + opcode: int + arg: int | None + argval: Any + argrepr: str + offset: int + starts_line: int | None + is_jump_target: bool class Bytecode: codeobj: types.CodeType first_line: int - def __init__(self, x: _HaveCodeOrStringType, *, first_line: int | None = ..., current_offset: int | None = ...) -> None: ... + if sys.version_info >= (3, 11): + def __init__( + self, + x: _HaveCodeOrStringType, + *, + first_line: int | None = ..., + current_offset: int | None = ..., + show_caches: bool = ..., + ) -> None: ... + @classmethod + def from_traceback(cls: type[Self], tb: types.TracebackType, *, show_caches: bool = ...) -> Self: ... + else: + def __init__( + self, x: _HaveCodeOrStringType, *, first_line: int | None = ..., current_offset: int | None = ... + ) -> None: ... + @classmethod + def from_traceback(cls: type[Self], tb: types.TracebackType) -> Self: ... + def __iter__(self) -> Iterator[Instruction]: ... def info(self) -> str: ... def dis(self) -> str: ... - @classmethod - def from_traceback(cls: type[Self], tb: types.TracebackType) -> Self: ... COMPILER_FLAG_NAMES: dict[int, str] @@ -64,14 +101,27 @@ def findlinestarts(code: _HaveCodeType) -> Iterator[tuple[int, int]]: ... def pretty_flags(flags: int) -> str: ... def code_info(x: _HaveCodeOrStringType) -> str: ... -if sys.version_info >= (3, 7): +if sys.version_info >= (3, 11): + def dis( + x: _HaveCodeOrStringType | None = ..., *, file: IO[str] | None = ..., depth: int | None = ..., show_caches: bool = ... + ) -> None: ... + +elif sys.version_info >= (3, 7): def dis(x: _HaveCodeOrStringType | None = ..., *, file: IO[str] | None = ..., depth: int | None = ...) -> None: ... else: def dis(x: _HaveCodeOrStringType | None = ..., *, file: IO[str] | None = ...) -> None: ... -def distb(tb: types.TracebackType | None = ..., *, file: IO[str] | None = ...) -> None: ... -def disassemble(co: _HaveCodeType, lasti: int = ..., *, file: IO[str] | None = ...) -> None: ... -def disco(co: _HaveCodeType, lasti: int = ..., *, file: IO[str] | None = ...) -> None: ... +if sys.version_info >= (3, 11): + def disassemble(co: _HaveCodeType, lasti: int = ..., *, file: IO[str] | None = ..., show_caches: bool = ...) -> None: ... + def disco(co: _HaveCodeType, lasti: int = ..., *, file: IO[str] | None = ..., show_caches: bool = ...) -> None: ... + def distb(tb: types.TracebackType | None = ..., *, file: IO[str] | None = ..., show_caches: bool = ...) -> None: ... + def get_instructions(x: _HaveCodeType, *, first_line: int | None = ..., show_caches: bool = ...) -> Iterator[Instruction]: ... + +else: + def disassemble(co: _HaveCodeType, lasti: int = ..., *, file: IO[str] | None = ...) -> None: ... + def disco(co: _HaveCodeType, lasti: int = ..., *, file: IO[str] | None = ...) -> None: ... + def distb(tb: types.TracebackType | None = ..., *, file: IO[str] | None = ...) -> None: ... + def get_instructions(x: _HaveCodeType, *, first_line: int | None = ...) -> Iterator[Instruction]: ... + def show_code(co: _HaveCodeType, *, file: IO[str] | None = ...) -> None: ... -def get_instructions(x: _HaveCodeType, *, first_line: int | None = ...) -> Iterator[Instruction]: ... diff --git a/mypy/typeshed/stdlib/distutils/ccompiler.pyi b/mypy/typeshed/stdlib/distutils/ccompiler.pyi index 4cdc62ce3bae..ed823f7c070f 100644 --- a/mypy/typeshed/stdlib/distutils/ccompiler.pyi +++ b/mypy/typeshed/stdlib/distutils/ccompiler.pyi @@ -1,6 +1,8 @@ -from typing import Any, Callable, Union +from collections.abc import Callable +from typing import Any, Union +from typing_extensions import TypeAlias -_Macro = Union[tuple[str], tuple[str, str | None]] +_Macro: TypeAlias = Union[tuple[str], tuple[str, str | None]] def gen_lib_options( compiler: CCompiler, library_dirs: list[str], runtime_library_dirs: list[str], libraries: list[str] diff --git a/mypy/typeshed/stdlib/distutils/cmd.pyi b/mypy/typeshed/stdlib/distutils/cmd.pyi index 96a048c93f41..8163ae78fd8f 100644 --- a/mypy/typeshed/stdlib/distutils/cmd.pyi +++ b/mypy/typeshed/stdlib/distutils/cmd.pyi @@ -1,6 +1,7 @@ from abc import abstractmethod +from collections.abc import Callable, Iterable from distutils.dist import Distribution -from typing import Any, Callable, Iterable +from typing import Any class Command: sub_commands: list[tuple[str, Callable[[Command], bool] | None]] diff --git a/mypy/typeshed/stdlib/distutils/core.pyi b/mypy/typeshed/stdlib/distutils/core.pyi index 6564c9a86ded..199a4d70a953 100644 --- a/mypy/typeshed/stdlib/distutils/core.pyi +++ b/mypy/typeshed/stdlib/distutils/core.pyi @@ -1,7 +1,8 @@ +from collections.abc import Mapping from distutils.cmd import Command as Command from distutils.dist import Distribution as Distribution from distutils.extension import Extension as Extension -from typing import Any, Mapping +from typing import Any def setup( *, diff --git a/mypy/typeshed/stdlib/distutils/dist.pyi b/mypy/typeshed/stdlib/distutils/dist.pyi index c5b3afe7cc3b..ef47e4e4d15a 100644 --- a/mypy/typeshed/stdlib/distutils/dist.pyi +++ b/mypy/typeshed/stdlib/distutils/dist.pyi @@ -1,6 +1,7 @@ from _typeshed import StrOrBytesPath, SupportsWrite +from collections.abc import Iterable, Mapping from distutils.cmd import Command -from typing import IO, Any, Iterable, Mapping +from typing import IO, Any class DistributionMetadata: def __init__(self, path: int | StrOrBytesPath | None = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi index c2a5bd4c20a1..6a7124bd15ad 100644 --- a/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi +++ b/mypy/typeshed/stdlib/distutils/fancy_getopt.pyi @@ -1,7 +1,9 @@ -from typing import Any, Iterable, Mapping, overload +from collections.abc import Iterable, Mapping +from typing import Any, overload +from typing_extensions import TypeAlias -_Option = tuple[str, str | None, str] -_GR = tuple[list[str], OptionDummy] +_Option: TypeAlias = tuple[str, str | None, str] +_GR: TypeAlias = tuple[list[str], OptionDummy] def fancy_getopt( options: list[_Option], negative_opt: Mapping[_Option, _Option], object: Any, args: list[str] | None diff --git a/mypy/typeshed/stdlib/distutils/file_util.pyi b/mypy/typeshed/stdlib/distutils/file_util.pyi index a7f24105a678..b3127841bce8 100644 --- a/mypy/typeshed/stdlib/distutils/file_util.pyi +++ b/mypy/typeshed/stdlib/distutils/file_util.pyi @@ -1,4 +1,4 @@ -from typing import Sequence +from collections.abc import Sequence def copy_file( src: str, diff --git a/mypy/typeshed/stdlib/distutils/filelist.pyi b/mypy/typeshed/stdlib/distutils/filelist.pyi index 2b436938f13e..361cb13f0c47 100644 --- a/mypy/typeshed/stdlib/distutils/filelist.pyi +++ b/mypy/typeshed/stdlib/distutils/filelist.pyi @@ -1,4 +1,5 @@ -from typing import Iterable, Pattern, overload +from collections.abc import Iterable +from typing import Pattern, overload from typing_extensions import Literal # class is entirely undocumented diff --git a/mypy/typeshed/stdlib/distutils/sysconfig.pyi b/mypy/typeshed/stdlib/distutils/sysconfig.pyi index 3f579be40882..bf7db9c8f06b 100644 --- a/mypy/typeshed/stdlib/distutils/sysconfig.pyi +++ b/mypy/typeshed/stdlib/distutils/sysconfig.pyi @@ -1,5 +1,5 @@ +from collections.abc import Mapping from distutils.ccompiler import CCompiler -from typing import Mapping PREFIX: str EXEC_PREFIX: str diff --git a/mypy/typeshed/stdlib/doctest.pyi b/mypy/typeshed/stdlib/doctest.pyi index 651e1b298aaf..c767436c2be8 100644 --- a/mypy/typeshed/stdlib/doctest.pyi +++ b/mypy/typeshed/stdlib/doctest.pyi @@ -1,6 +1,9 @@ import types import unittest -from typing import Any, Callable, NamedTuple +from _typeshed import ExcInfo +from collections.abc import Callable +from typing import Any, NamedTuple +from typing_extensions import TypeAlias __all__ = [ "register_optionflag", @@ -123,8 +126,7 @@ class DocTestFinder: extraglobs: dict[str, Any] | None = ..., ) -> list[DocTest]: ... -_Out = Callable[[str], Any] -_ExcInfo = tuple[type[BaseException], BaseException, types.TracebackType] +_Out: TypeAlias = Callable[[str], Any] class DocTestRunner: DIVIDER: str @@ -137,7 +139,7 @@ class DocTestRunner: def report_start(self, out: _Out, test: DocTest, example: Example) -> None: ... def report_success(self, out: _Out, test: DocTest, example: Example, got: str) -> None: ... def report_failure(self, out: _Out, test: DocTest, example: Example, got: str) -> None: ... - def report_unexpected_exception(self, out: _Out, test: DocTest, example: Example, exc_info: _ExcInfo) -> None: ... + def report_unexpected_exception(self, out: _Out, test: DocTest, example: Example, exc_info: ExcInfo) -> None: ... def run( self, test: DocTest, compileflags: int | None = ..., out: _Out | None = ..., clear_globs: bool = ... ) -> TestResults: ... @@ -157,8 +159,8 @@ class DocTestFailure(Exception): class UnexpectedException(Exception): test: DocTest example: Example - exc_info: _ExcInfo - def __init__(self, test: DocTest, example: Example, exc_info: _ExcInfo) -> None: ... + exc_info: ExcInfo + def __init__(self, test: DocTest, example: Example, exc_info: ExcInfo) -> None: ... class DebugRunner(DocTestRunner): ... diff --git a/mypy/typeshed/stdlib/email/__init__.pyi b/mypy/typeshed/stdlib/email/__init__.pyi index 49e18ccb8c2e..78368a2cf4a0 100644 --- a/mypy/typeshed/stdlib/email/__init__.pyi +++ b/mypy/typeshed/stdlib/email/__init__.pyi @@ -1,11 +1,12 @@ +from collections.abc import Callable from email.message import Message from email.policy import Policy -from typing import IO, Callable, TypeVar, Union +from typing import IO, Union +from typing_extensions import TypeAlias # Definitions imported by multiple submodules in typeshed -_MessageT = TypeVar("_MessageT", bound=Message) # noqa: Y018 -_ParamType = Union[str, tuple[str | None, str | None, str]] -_ParamsType = Union[str, None, tuple[str, str | None, str]] +_ParamType: TypeAlias = Union[str, tuple[str | None, str | None, str]] +_ParamsType: TypeAlias = Union[str, None, tuple[str, str | None, str]] def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... def message_from_bytes(s: bytes, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ... diff --git a/mypy/typeshed/stdlib/email/_header_value_parser.pyi b/mypy/typeshed/stdlib/email/_header_value_parser.pyi index 749b6454a8f8..abe6ec63abb1 100644 --- a/mypy/typeshed/stdlib/email/_header_value_parser.pyi +++ b/mypy/typeshed/stdlib/email/_header_value_parser.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import Self +from collections.abc import Iterable, Iterator from email.errors import HeaderParseError, MessageDefect from email.policy import Policy -from typing import Any, Iterable, Iterator, Pattern +from typing import Any, Pattern from typing_extensions import Final WSP: Final[set[str]] diff --git a/mypy/typeshed/stdlib/email/charset.pyi b/mypy/typeshed/stdlib/email/charset.pyi index fd3de9ceace2..236908537f83 100644 --- a/mypy/typeshed/stdlib/email/charset.pyi +++ b/mypy/typeshed/stdlib/email/charset.pyi @@ -1,4 +1,4 @@ -from typing import Iterator +from collections.abc import Iterator __all__ = ["Charset", "add_alias", "add_charset", "add_codec"] diff --git a/mypy/typeshed/stdlib/email/contentmanager.pyi b/mypy/typeshed/stdlib/email/contentmanager.pyi index 68fe99c8c09f..3ac665eaa7bf 100644 --- a/mypy/typeshed/stdlib/email/contentmanager.pyi +++ b/mypy/typeshed/stdlib/email/contentmanager.pyi @@ -1,5 +1,6 @@ +from collections.abc import Callable from email.message import Message -from typing import Any, Callable +from typing import Any class ContentManager: def __init__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/email/feedparser.pyi b/mypy/typeshed/stdlib/email/feedparser.pyi index b1c7e0f7d127..c535c353daad 100644 --- a/mypy/typeshed/stdlib/email/feedparser.pyi +++ b/mypy/typeshed/stdlib/email/feedparser.pyi @@ -1,10 +1,12 @@ -from email import _MessageT +from collections.abc import Callable from email.message import Message from email.policy import Policy -from typing import Callable, Generic, overload +from typing import Generic, TypeVar, overload __all__ = ["FeedParser", "BytesFeedParser"] +_MessageT = TypeVar("_MessageT", bound=Message) + class FeedParser(Generic[_MessageT]): @overload def __init__(self: FeedParser[Message], _factory: None = ..., *, policy: Policy = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/email/iterators.pyi b/mypy/typeshed/stdlib/email/iterators.pyi index ad5517712d25..29068819ac15 100644 --- a/mypy/typeshed/stdlib/email/iterators.pyi +++ b/mypy/typeshed/stdlib/email/iterators.pyi @@ -1,6 +1,6 @@ from _typeshed import SupportsWrite +from collections.abc import Iterator from email.message import Message -from typing import Iterator __all__ = ["body_line_iterator", "typed_subpart_iterator", "walk"] diff --git a/mypy/typeshed/stdlib/email/message.pyi b/mypy/typeshed/stdlib/email/message.pyi index 97de1cf5daf6..6544f8fc2385 100644 --- a/mypy/typeshed/stdlib/email/message.pyi +++ b/mypy/typeshed/stdlib/email/message.pyi @@ -1,17 +1,19 @@ +from collections.abc import Generator, Iterator, Sequence from email import _ParamsType, _ParamType from email.charset import Charset from email.contentmanager import ContentManager from email.errors import MessageDefect from email.policy import Policy -from typing import Any, Generator, Iterator, Sequence, TypeVar +from typing import Any, TypeVar +from typing_extensions import TypeAlias __all__ = ["Message", "EmailMessage"] _T = TypeVar("_T") -_PayloadType = list[Message] | str | bytes -_CharsetType = Charset | str | None -_HeaderType = Any +_PayloadType: TypeAlias = list[Message] | str | bytes +_CharsetType: TypeAlias = Charset | str | None +_HeaderType: TypeAlias = Any class Message: policy: Policy # undocumented diff --git a/mypy/typeshed/stdlib/email/parser.pyi b/mypy/typeshed/stdlib/email/parser.pyi index 1846bdcc36b2..dcd346c1b46d 100644 --- a/mypy/typeshed/stdlib/email/parser.pyi +++ b/mypy/typeshed/stdlib/email/parser.pyi @@ -1,14 +1,11 @@ -import email.feedparser -from email import _MessageT +from collections.abc import Callable +from email.feedparser import BytesFeedParser as BytesFeedParser, FeedParser as FeedParser from email.message import Message from email.policy import Policy -from typing import BinaryIO, Callable, TextIO +from typing import BinaryIO, TextIO __all__ = ["Parser", "HeaderParser", "BytesParser", "BytesHeaderParser", "FeedParser", "BytesFeedParser"] -FeedParser = email.feedparser.FeedParser[_MessageT] -BytesFeedParser = email.feedparser.BytesFeedParser[_MessageT] - class Parser: def __init__(self, _class: Callable[[], Message] | None = ..., *, policy: Policy = ...) -> None: ... def parse(self, fp: TextIO, headersonly: bool = ...) -> Message: ... diff --git a/mypy/typeshed/stdlib/email/policy.pyi b/mypy/typeshed/stdlib/email/policy.pyi index d4ebb1fd5e37..4df3c1e48b07 100644 --- a/mypy/typeshed/stdlib/email/policy.pyi +++ b/mypy/typeshed/stdlib/email/policy.pyi @@ -1,9 +1,10 @@ from abc import ABCMeta, abstractmethod +from collections.abc import Callable from email.contentmanager import ContentManager from email.errors import MessageDefect from email.header import Header from email.message import Message -from typing import Any, Callable +from typing import Any __all__ = ["Compat32", "compat32", "Policy", "EmailPolicy", "default", "strict", "SMTP", "HTTP"] diff --git a/mypy/typeshed/stdlib/email/utils.pyi b/mypy/typeshed/stdlib/email/utils.pyi index aeffb0ef1981..480c5f79549d 100644 --- a/mypy/typeshed/stdlib/email/utils.pyi +++ b/mypy/typeshed/stdlib/email/utils.pyi @@ -3,6 +3,7 @@ import sys from email import _ParamType from email.charset import Charset from typing import overload +from typing_extensions import TypeAlias __all__ = [ "collapse_rfc2231_value", @@ -22,7 +23,7 @@ __all__ = [ "unquote", ] -_PDTZ = tuple[int, int, int, int, int, int, int, int, int, int | None] +_PDTZ: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int | None] def quote(str: str) -> str: ... def unquote(str: str) -> str: ... diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index f49bcd7a00e0..a7c84c5b1c0d 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -5,7 +5,7 @@ from abc import ABCMeta from builtins import property as _builtins_property from collections.abc import Iterable, Iterator, Mapping from typing import Any, TypeVar, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 11): __all__ = [ @@ -52,7 +52,7 @@ _EnumerationT = TypeVar("_EnumerationT", bound=type[Enum]) # # >>> Enum('Foo', names={'RED': 1, 'YELLOW': 2}) # -_EnumNames = str | Iterable[str] | Iterable[Iterable[str | Any]] | Mapping[str, Any] +_EnumNames: TypeAlias = str | Iterable[str] | Iterable[Iterable[str | Any]] | Mapping[str, Any] class _EnumDict(dict[str, Any]): def __init__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/errno.pyi b/mypy/typeshed/stdlib/errno.pyi index 12410af23e71..9ef1fe6e7618 100644 --- a/mypy/typeshed/stdlib/errno.pyi +++ b/mypy/typeshed/stdlib/errno.pyi @@ -1,4 +1,4 @@ -from typing import Mapping +from collections.abc import Mapping errorcode: Mapping[int, str] diff --git a/mypy/typeshed/stdlib/filecmp.pyi b/mypy/typeshed/stdlib/filecmp.pyi index 7c606af40791..a6747dd504a3 100644 --- a/mypy/typeshed/stdlib/filecmp.pyi +++ b/mypy/typeshed/stdlib/filecmp.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import StrOrBytesPath +from collections.abc import Callable, Iterable, Sequence from os import PathLike -from typing import Any, AnyStr, Callable, Generic, Iterable, Sequence +from typing import Any, AnyStr, Generic from typing_extensions import Literal if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/fileinput.pyi b/mypy/typeshed/stdlib/fileinput.pyi index a3b62948ca20..0ef8c14ddaac 100644 --- a/mypy/typeshed/stdlib/fileinput.pyi +++ b/mypy/typeshed/stdlib/fileinput.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import Self, StrOrBytesPath +from collections.abc import Callable, Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Callable, Generic, Iterable, Iterator +from typing import IO, Any, AnyStr, Generic __all__ = [ "input", diff --git a/mypy/typeshed/stdlib/fnmatch.pyi b/mypy/typeshed/stdlib/fnmatch.pyi index 8351fce59ebb..7051c999c430 100644 --- a/mypy/typeshed/stdlib/fnmatch.pyi +++ b/mypy/typeshed/stdlib/fnmatch.pyi @@ -1,4 +1,5 @@ -from typing import AnyStr, Iterable +from collections.abc import Iterable +from typing import AnyStr __all__ = ["filter", "fnmatch", "fnmatchcase", "translate"] diff --git a/mypy/typeshed/stdlib/formatter.pyi b/mypy/typeshed/stdlib/formatter.pyi index f5d8348d08a1..0aac0a5f918c 100644 --- a/mypy/typeshed/stdlib/formatter.pyi +++ b/mypy/typeshed/stdlib/formatter.pyi @@ -1,8 +1,10 @@ -from typing import IO, Any, Iterable +from collections.abc import Iterable +from typing import IO, Any +from typing_extensions import TypeAlias AS_IS: None -_FontType = tuple[str, bool, bool, bool] -_StylesType = tuple[Any, ...] +_FontType: TypeAlias = tuple[str, bool, bool, bool] +_StylesType: TypeAlias = tuple[Any, ...] class NullFormatter: writer: NullWriter | None diff --git a/mypy/typeshed/stdlib/fractions.pyi b/mypy/typeshed/stdlib/fractions.pyi index 00989fb1fc02..0d787a011f5b 100644 --- a/mypy/typeshed/stdlib/fractions.pyi +++ b/mypy/typeshed/stdlib/fractions.pyi @@ -3,9 +3,9 @@ from _typeshed import Self from decimal import Decimal from numbers import Integral, Rational, Real from typing import Any, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias -_ComparableNum = int | float | Decimal | Real +_ComparableNum: TypeAlias = int | float | Decimal | Real if sys.version_info >= (3, 9): __all__ = ["Fraction"] diff --git a/mypy/typeshed/stdlib/ftplib.pyi b/mypy/typeshed/stdlib/ftplib.pyi index 4a5dad0dd14f..925ad5884700 100644 --- a/mypy/typeshed/stdlib/ftplib.pyi +++ b/mypy/typeshed/stdlib/ftplib.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import Self, SupportsRead, SupportsReadline +from collections.abc import Callable, Iterable, Iterator from socket import socket from ssl import SSLContext from types import TracebackType -from typing import Any, Callable, Iterable, Iterator, TextIO +from typing import Any, TextIO from typing_extensions import Literal __all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto", "all_errors", "FTP_TLS"] diff --git a/mypy/typeshed/stdlib/functools.pyi b/mypy/typeshed/stdlib/functools.pyi index 741a53ed82a2..44feeed63f6a 100644 --- a/mypy/typeshed/stdlib/functools.pyi +++ b/mypy/typeshed/stdlib/functools.pyi @@ -1,8 +1,9 @@ import sys import types from _typeshed import Self, SupportsAllComparisons, SupportsItems -from typing import Any, Callable, Generic, Hashable, Iterable, NamedTuple, Sequence, Sized, TypeVar, overload -from typing_extensions import Literal, final +from collections.abc import Callable, Hashable, Iterable, Sequence, Sized +from typing import Any, Generic, NamedTuple, TypeVar, overload +from typing_extensions import Literal, TypeAlias, final if sys.version_info >= (3, 9): from types import GenericAlias @@ -54,7 +55,7 @@ else: "singledispatch", ] -_AnyCallable = Callable[..., Any] +_AnyCallable: TypeAlias = Callable[..., Any] _T = TypeVar("_T") _S = TypeVar("_S") @@ -111,7 +112,7 @@ class partial(Generic[_T]): def __class_getitem__(cls, item: Any) -> GenericAlias: ... # With protocols, this could change into a generic protocol that defines __get__ and returns _T -_Descriptor = Any +_Descriptor: TypeAlias = Any class partialmethod(Generic[_T]): func: Callable[..., _T] | _Descriptor diff --git a/mypy/typeshed/stdlib/gc.pyi b/mypy/typeshed/stdlib/gc.pyi index 7c15e0f5b0a2..98b92e109f82 100644 --- a/mypy/typeshed/stdlib/gc.pyi +++ b/mypy/typeshed/stdlib/gc.pyi @@ -1,6 +1,7 @@ import sys -from typing import Any, Callable -from typing_extensions import Literal +from collections.abc import Callable +from typing import Any +from typing_extensions import Literal, TypeAlias DEBUG_COLLECTABLE: Literal[2] DEBUG_LEAK: Literal[38] @@ -8,7 +9,7 @@ DEBUG_SAVEALL: Literal[32] DEBUG_STATS: Literal[1] DEBUG_UNCOLLECTABLE: Literal[4] -_CallbackType = Callable[[Literal["start", "stop"], dict[str, int]], object] +_CallbackType: TypeAlias = Callable[[Literal["start", "stop"], dict[str, int]], object] callbacks: list[_CallbackType] garbage: list[Any] diff --git a/mypy/typeshed/stdlib/genericpath.pyi b/mypy/typeshed/stdlib/genericpath.pyi index 3abedda262ea..984d0c3cf51e 100644 --- a/mypy/typeshed/stdlib/genericpath.pyi +++ b/mypy/typeshed/stdlib/genericpath.pyi @@ -1,6 +1,7 @@ import os from _typeshed import BytesPath, StrOrBytesPath, StrPath, SupportsRichComparisonT -from typing import Sequence, overload +from collections.abc import Sequence +from typing import overload from typing_extensions import Literal __all__ = [ diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi index 1f3ef67ab0f3..d02dda5350b7 100644 --- a/mypy/typeshed/stdlib/gettext.pyi +++ b/mypy/typeshed/stdlib/gettext.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import StrPath -from typing import IO, Any, Container, Iterable, Sequence, TypeVar, overload -from typing_extensions import Literal +from collections.abc import Container, Iterable, Sequence +from typing import Any, Protocol, TypeVar, overload +from typing_extensions import Final, Literal if sys.version_info >= (3, 11): __all__ = [ @@ -67,9 +68,14 @@ else: "ngettext", ] +class _TranslationsReader(Protocol): + def read(self) -> bytes: ... + # optional: + # name: str + class NullTranslations: - def __init__(self, fp: IO[str] | None = ...) -> None: ... - def _parse(self, fp: IO[str]) -> None: ... + def __init__(self, fp: _TranslationsReader | None = ...) -> None: ... + def _parse(self, fp: _TranslationsReader) -> None: ... def add_fallback(self, fallback: NullTranslations) -> None: ... def gettext(self, message: str) -> str: ... def lgettext(self, message: str) -> str: ... @@ -79,18 +85,18 @@ class NullTranslations: def pgettext(self, context: str, message: str) -> str: ... def npgettext(self, context: str, msgid1: str, msgid2: str, n: int) -> str: ... - def info(self) -> Any: ... - def charset(self) -> Any: ... + def info(self) -> dict[str, str]: ... + def charset(self) -> str | None: ... if sys.version_info < (3, 11): - def output_charset(self) -> Any: ... + def output_charset(self) -> str | None: ... def set_output_charset(self, charset: str) -> None: ... def install(self, names: Container[str] | None = ...) -> None: ... class GNUTranslations(NullTranslations): - LE_MAGIC: int - BE_MAGIC: int - CONTEXT: str + LE_MAGIC: Final[int] + BE_MAGIC: Final[int] + CONTEXT: Final[str] VERSIONS: Sequence[int] @overload # ignores incompatible overloads diff --git a/mypy/typeshed/stdlib/glob.pyi b/mypy/typeshed/stdlib/glob.pyi index ced0ceceb205..c63563d19f58 100644 --- a/mypy/typeshed/stdlib/glob.pyi +++ b/mypy/typeshed/stdlib/glob.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import StrOrBytesPath -from typing import AnyStr, Iterator +from collections.abc import Iterator +from typing import AnyStr __all__ = ["escape", "glob", "iglob"] diff --git a/mypy/typeshed/stdlib/graphlib.pyi b/mypy/typeshed/stdlib/graphlib.pyi index cae2a07e95c6..2fca402bf906 100644 --- a/mypy/typeshed/stdlib/graphlib.pyi +++ b/mypy/typeshed/stdlib/graphlib.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import SupportsItems -from typing import Any, Generic, Iterable, TypeVar +from collections.abc import Iterable +from typing import Any, Generic, TypeVar __all__ = ["TopologicalSorter", "CycleError"] diff --git a/mypy/typeshed/stdlib/gzip.pyi b/mypy/typeshed/stdlib/gzip.pyi index 7347949ae865..abf12925aea2 100644 --- a/mypy/typeshed/stdlib/gzip.pyi +++ b/mypy/typeshed/stdlib/gzip.pyi @@ -4,16 +4,16 @@ import zlib from _typeshed import ReadableBuffer, StrOrBytesPath from io import FileIO from typing import Any, Protocol, TextIO, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 8): __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] else: __all__ = ["GzipFile", "open", "compress", "decompress"] -_ReadBinaryMode = Literal["r", "rb"] -_WriteBinaryMode = Literal["a", "ab", "w", "wb", "x", "xb"] -_OpenTextMode = Literal["rt", "at", "wt", "xt"] +_ReadBinaryMode: TypeAlias = Literal["r", "rb"] +_WriteBinaryMode: TypeAlias = Literal["a", "ab", "w", "wb", "x", "xb"] +_OpenTextMode: TypeAlias = Literal["rt", "at", "wt", "xt"] READ: Literal[1] WRITE: Literal[2] diff --git a/mypy/typeshed/stdlib/hashlib.pyi b/mypy/typeshed/stdlib/hashlib.pyi index 4332153d281c..2a417364b171 100644 --- a/mypy/typeshed/stdlib/hashlib.pyi +++ b/mypy/typeshed/stdlib/hashlib.pyi @@ -1,28 +1,52 @@ import sys from _typeshed import ReadableBuffer, Self -from typing import AbstractSet +from collections.abc import Callable, Set as AbstractSet +from typing import Protocol from typing_extensions import final -__all__ = ( - "md5", - "sha1", - "sha224", - "sha256", - "sha384", - "sha512", - "blake2b", - "blake2s", - "sha3_224", - "sha3_256", - "sha3_384", - "sha3_512", - "shake_128", - "shake_256", - "new", - "algorithms_guaranteed", - "algorithms_available", - "pbkdf2_hmac", -) +if sys.version_info >= (3, 11): + __all__ = ( + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "blake2b", + "blake2s", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + "shake_128", + "shake_256", + "new", + "algorithms_guaranteed", + "algorithms_available", + "pbkdf2_hmac", + "file_digest", + ) +else: + __all__ = ( + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "blake2b", + "blake2s", + "sha3_224", + "sha3_256", + "sha3_384", + "sha3_512", + "shake_128", + "shake_256", + "new", + "algorithms_guaranteed", + "algorithms_available", + "pbkdf2_hmac", + ) class _Hash: @property @@ -143,3 +167,15 @@ class _BlakeHash(_Hash): blake2b = _BlakeHash blake2s = _BlakeHash + +if sys.version_info >= (3, 11): + class _BytesIOLike(Protocol): + def getbuffer(self) -> ReadableBuffer: ... + + class _FileDigestFileObj(Protocol): + def readinto(self, __buf: bytearray) -> int: ... + def readable(self) -> bool: ... + + def file_digest( + __fileobj: _BytesIOLike | _FileDigestFileObj, __digest: str | Callable[[], _Hash], *, _bufsize: int = ... + ) -> _Hash: ... diff --git a/mypy/typeshed/stdlib/heapq.pyi b/mypy/typeshed/stdlib/heapq.pyi index a7a787d44e62..f07afc7af706 100644 --- a/mypy/typeshed/stdlib/heapq.pyi +++ b/mypy/typeshed/stdlib/heapq.pyi @@ -1,6 +1,7 @@ from _heapq import * from _typeshed import SupportsRichComparison -from typing import Any, Callable, Iterable, TypeVar +from collections.abc import Callable, Iterable +from typing import Any, TypeVar __all__ = ["heappush", "heappop", "heapify", "heapreplace", "merge", "nlargest", "nsmallest", "heappushpop"] diff --git a/mypy/typeshed/stdlib/hmac.pyi b/mypy/typeshed/stdlib/hmac.pyi index 6d355147f51e..858e46a71b68 100644 --- a/mypy/typeshed/stdlib/hmac.pyi +++ b/mypy/typeshed/stdlib/hmac.pyi @@ -1,11 +1,13 @@ import sys from _typeshed import ReadableBuffer +from collections.abc import Callable from types import ModuleType -from typing import Any, AnyStr, Callable, overload +from typing import Any, AnyStr, overload +from typing_extensions import TypeAlias # TODO more precise type for object of hashlib -_Hash = Any -_DigestMod = str | Callable[[], _Hash] | ModuleType +_Hash: TypeAlias = Any +_DigestMod: TypeAlias = str | Callable[[], _Hash] | ModuleType trans_5C: bytes trans_36: bytes diff --git a/mypy/typeshed/stdlib/http/client.pyi b/mypy/typeshed/stdlib/http/client.pyi index 801a195c9fb3..235b6d6b4951 100644 --- a/mypy/typeshed/stdlib/http/client.pyi +++ b/mypy/typeshed/stdlib/http/client.pyi @@ -4,8 +4,10 @@ import ssl import sys import types from _typeshed import Self, WriteableBuffer +from collections.abc import Callable, Iterable, Iterator, Mapping from socket import socket -from typing import IO, Any, BinaryIO, Callable, Iterable, Iterator, Mapping, Protocol, TypeVar, overload +from typing import IO, Any, BinaryIO, Protocol, TypeVar, overload +from typing_extensions import TypeAlias __all__ = [ "HTTPResponse", @@ -29,7 +31,7 @@ __all__ = [ "HTTPSConnection", ] -_DataType = bytes | IO[Any] | Iterable[bytes] | str +_DataType: TypeAlias = bytes | IO[Any] | Iterable[bytes] | str _T = TypeVar("_T") HTTP_PORT: int diff --git a/mypy/typeshed/stdlib/http/cookiejar.pyi b/mypy/typeshed/stdlib/http/cookiejar.pyi index 4fb1c38c6ab8..83da7faaf0fc 100644 --- a/mypy/typeshed/stdlib/http/cookiejar.pyi +++ b/mypy/typeshed/stdlib/http/cookiejar.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import StrPath +from collections.abc import Iterable, Iterator, Sequence from http.client import HTTPResponse -from typing import ClassVar, Iterable, Iterator, Pattern, Sequence, TypeVar, overload +from typing import ClassVar, Pattern, TypeVar, overload from urllib.request import Request __all__ = [ diff --git a/mypy/typeshed/stdlib/http/cookies.pyi b/mypy/typeshed/stdlib/http/cookies.pyi index 2cc05961a303..e5aa2c1609db 100644 --- a/mypy/typeshed/stdlib/http/cookies.pyi +++ b/mypy/typeshed/stdlib/http/cookies.pyi @@ -1,12 +1,14 @@ import sys -from typing import Any, Generic, Iterable, Mapping, TypeVar, overload +from collections.abc import Iterable, Mapping +from typing import Any, Generic, TypeVar, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias __all__ = ["CookieError", "BaseCookie", "SimpleCookie"] -_DataType = str | Mapping[str, str | Morsel[Any]] +_DataType: TypeAlias = str | Mapping[str, str | Morsel[Any]] _T = TypeVar("_T") @overload diff --git a/mypy/typeshed/stdlib/http/server.pyi b/mypy/typeshed/stdlib/http/server.pyi index 53159b65ec14..ad314cec1541 100644 --- a/mypy/typeshed/stdlib/http/server.pyi +++ b/mypy/typeshed/stdlib/http/server.pyi @@ -3,7 +3,8 @@ import io import socketserver import sys from _typeshed import StrPath, SupportsRead, SupportsWrite -from typing import Any, AnyStr, BinaryIO, ClassVar, Mapping, Sequence +from collections.abc import Mapping, Sequence +from typing import Any, AnyStr, BinaryIO, ClassVar if sys.version_info >= (3, 7): __all__ = ["HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler"] diff --git a/mypy/typeshed/stdlib/imaplib.pyi b/mypy/typeshed/stdlib/imaplib.pyi index ab6490cea570..eef1c1957769 100644 --- a/mypy/typeshed/stdlib/imaplib.pyi +++ b/mypy/typeshed/stdlib/imaplib.pyi @@ -2,21 +2,21 @@ import subprocess import sys import time from _typeshed import Self +from builtins import list as _list # conflicts with a method named "list" +from collections.abc import Callable from socket import socket as _socket from ssl import SSLContext, SSLSocket from types import TracebackType -from typing import IO, Any, Callable, Pattern -from typing_extensions import Literal +from typing import IO, Any, Pattern +from typing_extensions import Literal, TypeAlias __all__ = ["IMAP4", "IMAP4_stream", "Internaldate2tuple", "Int2AP", "ParseFlags", "Time2Internaldate", "IMAP4_SSL"] # TODO: Commands should use their actual return types, not this type alias. # E.g. Tuple[Literal["OK"], List[bytes]] -_CommandResults = tuple[str, list[Any]] +_CommandResults: TypeAlias = tuple[str, list[Any]] -_AnyResponseData = list[None] | list[bytes | tuple[bytes, bytes]] - -_list = list # conflicts with a method named "list" +_AnyResponseData: TypeAlias = list[None] | list[bytes | tuple[bytes, bytes]] class IMAP4: error: type[Exception] diff --git a/mypy/typeshed/stdlib/imghdr.pyi b/mypy/typeshed/stdlib/imghdr.pyi index af046e899326..5f439779a69c 100644 --- a/mypy/typeshed/stdlib/imghdr.pyi +++ b/mypy/typeshed/stdlib/imghdr.pyi @@ -1,5 +1,6 @@ from _typeshed import StrPath -from typing import Any, BinaryIO, Callable, Protocol, overload +from collections.abc import Callable +from typing import Any, BinaryIO, Protocol, overload __all__ = ["what"] diff --git a/mypy/typeshed/stdlib/importlib/__init__.pyi b/mypy/typeshed/stdlib/importlib/__init__.pyi index 0e99786775b0..42401a00bdeb 100644 --- a/mypy/typeshed/stdlib/importlib/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/__init__.pyi @@ -1,6 +1,6 @@ +from collections.abc import Mapping, Sequence from importlib.abc import Loader from types import ModuleType -from typing import Mapping, Sequence __all__ = ["__import__", "import_module", "invalidate_caches", "reload"] diff --git a/mypy/typeshed/stdlib/importlib/abc.pyi b/mypy/typeshed/stdlib/importlib/abc.pyi index 877f8ff1a65c..63fd02f7b3d5 100644 --- a/mypy/typeshed/stdlib/importlib/abc.pyi +++ b/mypy/typeshed/stdlib/importlib/abc.pyi @@ -10,12 +10,13 @@ from _typeshed import ( StrPath, ) from abc import ABCMeta, abstractmethod +from collections.abc import Iterator, Mapping, Sequence from importlib.machinery import ModuleSpec from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper -from typing import IO, Any, BinaryIO, Iterator, Mapping, NoReturn, Protocol, Sequence, overload, runtime_checkable -from typing_extensions import Literal +from typing import IO, Any, BinaryIO, NoReturn, Protocol, overload, runtime_checkable +from typing_extensions import Literal, TypeAlias -_Path = bytes | str +_Path: TypeAlias = bytes | str class Finder(metaclass=ABCMeta): ... diff --git a/mypy/typeshed/stdlib/importlib/machinery.pyi b/mypy/typeshed/stdlib/importlib/machinery.pyi index f15ac48af5c5..09abdc6f34fd 100644 --- a/mypy/typeshed/stdlib/importlib/machinery.pyi +++ b/mypy/typeshed/stdlib/importlib/machinery.pyi @@ -1,7 +1,8 @@ import importlib.abc import sys import types -from typing import Any, Callable, Iterable, Sequence +from collections.abc import Callable, Iterable, Sequence +from typing import Any if sys.version_info >= (3, 8): from importlib.metadata import DistributionFinder, PathDistribution diff --git a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi index 52c5547a6128..1f32f0770b37 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/__init__.pyi @@ -2,12 +2,12 @@ import abc import pathlib import sys from _typeshed import Self, StrPath -from collections.abc import Mapping +from collections.abc import Iterable, Mapping from email.message import Message from importlib.abc import MetaPathFinder from os import PathLike from pathlib import Path -from typing import Any, ClassVar, Iterable, NamedTuple, Pattern, overload +from typing import Any, ClassVar, NamedTuple, Pattern, overload if sys.version_info >= (3, 10): __all__ = [ diff --git a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi index a5e5733396d7..a1101df0d5ce 100644 --- a/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi +++ b/mypy/typeshed/stdlib/importlib/metadata/_meta.pyi @@ -1,4 +1,5 @@ -from typing import Any, Iterator, Protocol, TypeVar +from collections.abc import Iterator +from typing import Any, Protocol, TypeVar _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/importlib/resources.pyi b/mypy/typeshed/stdlib/importlib/resources.pyi index e6e3035e5dac..04d7e8dcc967 100644 --- a/mypy/typeshed/stdlib/importlib/resources.pyi +++ b/mypy/typeshed/stdlib/importlib/resources.pyi @@ -1,9 +1,11 @@ import os import sys +from collections.abc import Iterator from contextlib import AbstractContextManager from pathlib import Path from types import ModuleType -from typing import Any, BinaryIO, Iterator, TextIO +from typing import Any, BinaryIO, TextIO +from typing_extensions import TypeAlias if sys.version_info >= (3, 10): __all__ = [ @@ -37,8 +39,8 @@ elif sys.version_info >= (3, 9): else: __all__ = ["Package", "Resource", "contents", "is_resource", "open_binary", "open_text", "path", "read_binary", "read_text"] -Package = str | ModuleType -Resource = str | os.PathLike[Any] +Package: TypeAlias = str | ModuleType +Resource: TypeAlias = str | os.PathLike[Any] def open_binary(package: Package, resource: Resource) -> BinaryIO: ... def open_text(package: Package, resource: Resource, encoding: str = ..., errors: str = ...) -> TextIO: ... diff --git a/mypy/typeshed/stdlib/importlib/util.pyi b/mypy/typeshed/stdlib/importlib/util.pyi index c759d7def5be..2546c2c7882f 100644 --- a/mypy/typeshed/stdlib/importlib/util.pyi +++ b/mypy/typeshed/stdlib/importlib/util.pyi @@ -3,7 +3,8 @@ import importlib.machinery import sys import types from _typeshed import StrOrBytesPath -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from typing_extensions import ParamSpec _P = ParamSpec("_P") diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index f87cd0e55acb..7ca9c9bb3fc4 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -3,7 +3,7 @@ import sys import types from _typeshed import Self from collections import OrderedDict -from collections.abc import Awaitable, Callable, Generator, Mapping, Sequence, Set as AbstractSet +from collections.abc import Awaitable, Callable, Coroutine, Generator, Mapping, Sequence, Set as AbstractSet from types import ( AsyncGeneratorType, BuiltinFunctionType, @@ -19,6 +19,7 @@ from types import ( ModuleType, TracebackType, ) +from typing_extensions import TypeAlias if sys.version_info >= (3, 7): from types import ( @@ -29,7 +30,7 @@ if sys.version_info >= (3, 7): MethodWrapperType, ) -from typing import Any, ClassVar, Coroutine, NamedTuple, Protocol, TypeVar, Union +from typing import Any, ClassVar, NamedTuple, Protocol, TypeVar, Union from typing_extensions import Literal, ParamSpec, TypeGuard if sys.version_info >= (3, 11): @@ -165,8 +166,8 @@ TPFLAGS_IS_ABSTRACT: Literal[1048576] modulesbyfile: dict[str, Any] -_GetMembersPredicate = Callable[[Any], bool] -_GetMembersReturn = list[tuple[str, Any]] +_GetMembersPredicate: TypeAlias = Callable[[Any], bool] +_GetMembersReturn: TypeAlias = list[tuple[str, Any]] def getmembers(object: object, predicate: _GetMembersPredicate | None = ...) -> _GetMembersReturn: ... @@ -242,7 +243,9 @@ def isdatadescriptor(object: object) -> TypeGuard[_SupportsSet[Any, Any] | _Supp # # Retrieving source code # -_SourceObjectType = Union[ModuleType, type[Any], MethodType, FunctionType, TracebackType, FrameType, CodeType, Callable[..., Any]] +_SourceObjectType: TypeAlias = Union[ + ModuleType, type[Any], MethodType, FunctionType, TracebackType, FrameType, CodeType, Callable[..., Any] +] def findsource(object: _SourceObjectType) -> tuple[list[str], int]: ... def getabsfile(object: _SourceObjectType, _filename: str | None = ...) -> str: ... @@ -511,7 +514,7 @@ def getcoroutinelocals(coroutine: Coroutine[Any, Any, Any]) -> dict[str, Any]: . # Create private type alias to avoid conflict with symbol of same # name created in Attribute class. -_Object = object +_Object: TypeAlias = object class Attribute(NamedTuple): name: str diff --git a/mypy/typeshed/stdlib/io.pyi b/mypy/typeshed/stdlib/io.pyi index ef4789251cc3..327fd0d94a4d 100644 --- a/mypy/typeshed/stdlib/io.pyi +++ b/mypy/typeshed/stdlib/io.pyi @@ -2,9 +2,10 @@ import builtins import codecs import sys from _typeshed import ReadableBuffer, Self, StrOrBytesPath, WriteableBuffer +from collections.abc import Callable, Iterable, Iterator from os import _Opener from types import TracebackType -from typing import IO, Any, BinaryIO, Callable, Iterable, Iterator, TextIO +from typing import IO, Any, BinaryIO, TextIO from typing_extensions import Literal if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/ipaddress.pyi b/mypy/typeshed/stdlib/ipaddress.pyi index d777cef74597..1fdc6c57d8a8 100644 --- a/mypy/typeshed/stdlib/ipaddress.pyi +++ b/mypy/typeshed/stdlib/ipaddress.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import Self -from typing import Any, Container, Generic, Iterable, Iterator, SupportsInt, TypeVar, overload -from typing_extensions import Literal +from collections.abc import Container, Iterable, Iterator +from typing import Any, Generic, SupportsInt, TypeVar, overload +from typing_extensions import Literal, TypeAlias # Undocumented length constants IPV4LENGTH: Literal[32] @@ -10,8 +11,8 @@ IPV6LENGTH: Literal[128] _A = TypeVar("_A", IPv4Address, IPv6Address) _N = TypeVar("_N", IPv4Network, IPv6Network) -_RawIPAddress = int | str | bytes | IPv4Address | IPv6Address -_RawNetworkPart = IPv4Network | IPv6Network | IPv4Interface | IPv6Interface +_RawIPAddress: TypeAlias = int | str | bytes | IPv4Address | IPv6Address +_RawNetworkPart: TypeAlias = IPv4Network | IPv6Network | IPv4Interface | IPv6Interface def ip_address(address: _RawIPAddress) -> IPv4Address | IPv6Address: ... def ip_network(address: _RawIPAddress | _RawNetworkPart, strict: bool = ...) -> IPv4Network | IPv6Network: ... diff --git a/mypy/typeshed/stdlib/itertools.pyi b/mypy/typeshed/stdlib/itertools.pyi index bec03b1876ac..43a0e8038d69 100644 --- a/mypy/typeshed/stdlib/itertools.pyi +++ b/mypy/typeshed/stdlib/itertools.pyi @@ -1,7 +1,8 @@ import sys -from _typeshed import Self, _T_co -from typing import Any, Callable, Generic, Iterable, Iterator, SupportsComplex, SupportsFloat, SupportsInt, TypeVar, overload -from typing_extensions import Literal, SupportsIndex +from _typeshed import Self +from collections.abc import Callable, Iterable, Iterator +from typing import Any, Generic, SupportsComplex, SupportsFloat, SupportsInt, TypeVar, overload +from typing_extensions import Literal, SupportsIndex, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -9,9 +10,17 @@ if sys.version_info >= (3, 9): _T = TypeVar("_T") _S = TypeVar("_S") _N = TypeVar("_N", int, float, SupportsFloat, SupportsInt, SupportsIndex, SupportsComplex) -_Step = int | float | SupportsFloat | SupportsInt | SupportsIndex | SupportsComplex +_T_co = TypeVar("_T_co", covariant=True) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_T4 = TypeVar("_T4") +_T5 = TypeVar("_T5") +_T6 = TypeVar("_T6") + +_Step: TypeAlias = int | float | SupportsFloat | SupportsInt | SupportsIndex | SupportsComplex -Predicate = Callable[[_T], object] +_Predicate: TypeAlias = Callable[[_T], object] # Technically count can take anything that implements a number protocol and has an add method # but we can't enforce the add method @@ -67,18 +76,15 @@ class compress(Iterator[_T], Generic[_T]): def __next__(self) -> _T: ... class dropwhile(Iterator[_T], Generic[_T]): - def __init__(self, __predicate: Predicate[_T], __iterable: Iterable[_T]) -> None: ... + def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... def __iter__(self: Self) -> Self: ... def __next__(self) -> _T: ... class filterfalse(Iterator[_T], Generic[_T]): - def __init__(self, __predicate: Predicate[_T] | None, __iterable: Iterable[_T]) -> None: ... + def __init__(self, __predicate: _Predicate[_T] | None, __iterable: Iterable[_T]) -> None: ... def __iter__(self: Self) -> Self: ... def __next__(self) -> _T: ... -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") - class groupby(Iterator[tuple[_T, Iterator[_S]]], Generic[_T, _S]): @overload def __new__(cls, iterable: Iterable[_T1], key: None = ...) -> groupby[_T1, _T1]: ... @@ -101,21 +107,91 @@ class starmap(Iterator[_T], Generic[_T]): def __next__(self) -> _T: ... class takewhile(Iterator[_T], Generic[_T]): - def __init__(self, __predicate: Predicate[_T], __iterable: Iterable[_T]) -> None: ... + def __init__(self, __predicate: _Predicate[_T], __iterable: Iterable[_T]) -> None: ... def __iter__(self: Self) -> Self: ... def __next__(self) -> _T: ... def tee(__iterable: Iterable[_T], __n: int = ...) -> tuple[Iterator[_T], ...]: ... -class zip_longest(Iterator[Any]): - def __init__(self, *p: Iterable[Any], fillvalue: Any = ...) -> None: ... +class zip_longest(Iterator[_T_co], Generic[_T_co]): + # one iterable (fillvalue doesn't matter) + @overload + def __new__(cls, __iter1: Iterable[_T1], *, fillvalue: object = ...) -> zip_longest[tuple[_T1]]: ... + # two iterables + @overload + # In the overloads without fillvalue, all of the tuple members could theoretically be None, + # but we return Any instead to avoid false positives for code where we know one of the iterables + # is longer. + def __new__(cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2]) -> zip_longest[tuple[_T1 | Any, _T2 | Any]]: ... + @overload + def __new__( + cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], *, fillvalue: _T + ) -> zip_longest[tuple[_T1 | _T, _T2 | _T]]: ... + # three iterables + @overload + def __new__( + cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] + ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any]]: ... + @overload + def __new__( + cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], *, fillvalue: _T + ) -> zip_longest[tuple[_T1 | _T, _T2 | _T, _T3 | _T]]: ... + # four iterables + @overload + def __new__( + cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4] + ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any]]: ... + @overload + def __new__( + cls, __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3], __iter4: Iterable[_T4], *, fillvalue: _T + ) -> zip_longest[tuple[_T1 | _T, _T2 | _T, _T3 | _T, _T4 | _T]]: ... + # five iterables + @overload + def __new__( + cls, + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + __iter3: Iterable[_T3], + __iter4: Iterable[_T4], + __iter5: Iterable[_T5], + ) -> zip_longest[tuple[_T1 | Any, _T2 | Any, _T3 | Any, _T4 | Any, _T5 | Any]]: ... + @overload + def __new__( + cls, + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + __iter3: Iterable[_T3], + __iter4: Iterable[_T4], + __iter5: Iterable[_T5], + *, + fillvalue: _T, + ) -> zip_longest[tuple[_T1 | _T, _T2 | _T, _T3 | _T, _T4 | _T, _T5 | _T]]: ... + # six or more iterables + @overload + def __new__( + cls, + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + __iter4: Iterable[_T], + __iter5: Iterable[_T], + __iter6: Iterable[_T], + *iterables: Iterable[_T], + ) -> zip_longest[tuple[_T | Any, ...]]: ... + @overload + def __new__( + cls, + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + __iter4: Iterable[_T], + __iter5: Iterable[_T], + __iter6: Iterable[_T], + *iterables: Iterable[_T], + fillvalue: _T, + ) -> zip_longest[tuple[_T, ...]]: ... def __iter__(self: Self) -> Self: ... - def __next__(self) -> Any: ... - -_T3 = TypeVar("_T3") -_T4 = TypeVar("_T4") -_T5 = TypeVar("_T5") -_T6 = TypeVar("_T6") + def __next__(self) -> _T_co: ... class product(Iterator[_T_co], Generic[_T_co]): @overload diff --git a/mypy/typeshed/stdlib/json/__init__.pyi b/mypy/typeshed/stdlib/json/__init__.pyi index 8e1a36756398..2fd87622e1fe 100644 --- a/mypy/typeshed/stdlib/json/__init__.pyi +++ b/mypy/typeshed/stdlib/json/__init__.pyi @@ -1,5 +1,6 @@ from _typeshed import SupportsRead -from typing import IO, Any, Callable +from collections.abc import Callable +from typing import IO, Any from .decoder import JSONDecodeError as JSONDecodeError, JSONDecoder as JSONDecoder from .encoder import JSONEncoder as JSONEncoder diff --git a/mypy/typeshed/stdlib/json/decoder.pyi b/mypy/typeshed/stdlib/json/decoder.pyi index 866836758545..2060cf17dd05 100644 --- a/mypy/typeshed/stdlib/json/decoder.pyi +++ b/mypy/typeshed/stdlib/json/decoder.pyi @@ -1,4 +1,5 @@ -from typing import Any, Callable +from collections.abc import Callable +from typing import Any __all__ = ["JSONDecoder", "JSONDecodeError"] diff --git a/mypy/typeshed/stdlib/json/encoder.pyi b/mypy/typeshed/stdlib/json/encoder.pyi index 6dd74896e5a0..ecd1fa78ad99 100644 --- a/mypy/typeshed/stdlib/json/encoder.pyi +++ b/mypy/typeshed/stdlib/json/encoder.pyi @@ -1,4 +1,5 @@ -from typing import Any, Callable, Iterator, Pattern +from collections.abc import Callable, Iterator +from typing import Any, Pattern ESCAPE: Pattern[str] ESCAPE_ASCII: Pattern[str] diff --git a/mypy/typeshed/stdlib/keyword.pyi b/mypy/typeshed/stdlib/keyword.pyi index e9a9877d57da..c17c58012fd1 100644 --- a/mypy/typeshed/stdlib/keyword.pyi +++ b/mypy/typeshed/stdlib/keyword.pyi @@ -1,5 +1,5 @@ import sys -from typing import Sequence +from collections.abc import Sequence if sys.version_info >= (3, 9): __all__ = ["iskeyword", "issoftkeyword", "kwlist", "softkwlist"] diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/driver.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/driver.pyi index 4ecba031942c..61ec90b4d582 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/driver.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/driver.pyi @@ -1,8 +1,9 @@ from _typeshed import StrPath +from collections.abc import Iterable from lib2to3.pgen2.grammar import Grammar from lib2to3.pytree import _NL, _Convert from logging import Logger -from typing import IO, Any, Iterable +from typing import IO, Any __all__ = ["Driver", "load_grammar"] diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/grammar.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/grammar.pyi index b5836e1b90c5..4d298ec6972c 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/grammar.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/grammar.pyi @@ -1,8 +1,9 @@ from _typeshed import Self, StrPath +from typing_extensions import TypeAlias -_Label = tuple[int, str | None] -_DFA = list[list[tuple[int, int]]] -_DFAS = tuple[_DFA, dict[int, int]] +_Label: TypeAlias = tuple[int, str | None] +_DFA: TypeAlias = list[list[tuple[int, int]]] +_DFAS: TypeAlias = tuple[_DFA, dict[int, int]] class Grammar: symbol2number: dict[str, int] diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/parse.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/parse.pyi index e776ed1e5a61..14d6004d3423 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/parse.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/parse.pyi @@ -1,8 +1,10 @@ +from collections.abc import Sequence from lib2to3.pgen2.grammar import _DFAS, Grammar from lib2to3.pytree import _NL, _Convert, _RawNode -from typing import Any, Sequence +from typing import Any +from typing_extensions import TypeAlias -_Context = Sequence[Any] +_Context: TypeAlias = Sequence[Any] class ParseError(Exception): msg: str diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi index 2628e1223fb4..e3ea07432d70 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/pgen.pyi @@ -1,7 +1,8 @@ from _typeshed import StrPath +from collections.abc import Iterable, Iterator from lib2to3.pgen2 import grammar from lib2to3.pgen2.tokenize import _TokenInfo -from typing import IO, Any, Iterable, Iterator, NoReturn +from typing import IO, Any, NoReturn class PgenGrammar(grammar.Grammar): ... diff --git a/mypy/typeshed/stdlib/lib2to3/pgen2/tokenize.pyi b/mypy/typeshed/stdlib/lib2to3/pgen2/tokenize.pyi index c1b5a91df9e6..a998ad5fe49e 100644 --- a/mypy/typeshed/stdlib/lib2to3/pgen2/tokenize.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pgen2/tokenize.pyi @@ -1,6 +1,7 @@ import sys +from collections.abc import Callable, Iterable, Iterator from lib2to3.pgen2.token import * -from typing import Callable, Iterable, Iterator +from typing_extensions import TypeAlias if sys.version_info >= (3, 8): __all__ = [ @@ -146,9 +147,9 @@ else: "untokenize", ] -_Coord = tuple[int, int] -_TokenEater = Callable[[int, str, _Coord, _Coord, str], None] -_TokenInfo = tuple[int, str, _Coord, _Coord, str] +_Coord: TypeAlias = tuple[int, int] +_TokenEater: TypeAlias = Callable[[int, str, _Coord, _Coord, str], None] +_TokenInfo: TypeAlias = tuple[int, str, _Coord, _Coord, str] class TokenError(Exception): ... class StopTokenizing(Exception): ... diff --git a/mypy/typeshed/stdlib/lib2to3/pytree.pyi b/mypy/typeshed/stdlib/lib2to3/pytree.pyi index 68e5d8ba1323..fa0cb9e34f75 100644 --- a/mypy/typeshed/stdlib/lib2to3/pytree.pyi +++ b/mypy/typeshed/stdlib/lib2to3/pytree.pyi @@ -1,12 +1,14 @@ from _typeshed import Self +from collections.abc import Callable, Iterator from lib2to3.pgen2.grammar import Grammar -from typing import Any, Callable, Iterator +from typing import Any +from typing_extensions import TypeAlias -_NL = Node | Leaf -_Context = tuple[str, int, int] -_Results = dict[str, _NL] -_RawNode = tuple[int, str, _Context, list[_NL] | None] -_Convert = Callable[[Grammar, _RawNode], Any] +_NL: TypeAlias = Node | Leaf +_Context: TypeAlias = tuple[str, int, int] +_Results: TypeAlias = dict[str, _NL] +_RawNode: TypeAlias = tuple[int, str, _Context, list[_NL] | None] +_Convert: TypeAlias = Callable[[Grammar, _RawNode], Any] HUGE: int diff --git a/mypy/typeshed/stdlib/lib2to3/refactor.pyi b/mypy/typeshed/stdlib/lib2to3/refactor.pyi index 6687092d862c..3aaea0e519d9 100644 --- a/mypy/typeshed/stdlib/lib2to3/refactor.pyi +++ b/mypy/typeshed/stdlib/lib2to3/refactor.pyi @@ -1,11 +1,12 @@ from collections.abc import Container, Generator, Iterable, Mapping from logging import Logger from typing import Any, ClassVar, NoReturn +from typing_extensions import TypeAlias from .pgen2.grammar import Grammar -_Driver = Any # really lib2to3.driver.Driver -_BottomMatcher = Any # really lib2to3.btm_matcher.BottomMatcher +_Driver: TypeAlias = Any # really lib2to3.driver.Driver +_BottomMatcher: TypeAlias = Any # really lib2to3.btm_matcher.BottomMatcher def get_all_fix_names(fixer_pkg: str, remove_prefix: bool = ...) -> list[str]: ... def get_fixers_from_package(pkg_name: str) -> list[str]: ... diff --git a/mypy/typeshed/stdlib/linecache.pyi b/mypy/typeshed/stdlib/linecache.pyi index d72d678b5e18..df54fd80aea7 100644 --- a/mypy/typeshed/stdlib/linecache.pyi +++ b/mypy/typeshed/stdlib/linecache.pyi @@ -1,13 +1,14 @@ import sys from typing import Any, Protocol +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): __all__ = ["getline", "clearcache", "checkcache", "lazycache"] else: __all__ = ["getline", "clearcache", "checkcache"] -_ModuleGlobals = dict[str, Any] -_ModuleMetadata = tuple[int, float | None, list[str], str] +_ModuleGlobals: TypeAlias = dict[str, Any] +_ModuleMetadata: TypeAlias = tuple[int, float | None, list[str], str] class _SourceLoader(Protocol): def __call__(self) -> str | None: ... diff --git a/mypy/typeshed/stdlib/locale.pyi b/mypy/typeshed/stdlib/locale.pyi index e5227beca149..959054e847a8 100644 --- a/mypy/typeshed/stdlib/locale.pyi +++ b/mypy/typeshed/stdlib/locale.pyi @@ -1,5 +1,6 @@ import sys from _typeshed import StrPath +from collections.abc import Callable, Iterable, Mapping __all__ = [ "getlocale", @@ -32,7 +33,7 @@ __all__ = [ # as a type annotation or type alias. from builtins import str as _str from decimal import Decimal -from typing import Any, Callable, Iterable, Mapping +from typing import Any CODESET: int D_T_FMT: int diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index 8de4d0d88260..edb15061a588 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -7,7 +7,7 @@ from string import Template from time import struct_time from types import FrameType, TracebackType from typing import Any, ClassVar, Generic, Pattern, TextIO, TypeVar, Union, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = [ "BASIC_FORMAT", @@ -54,12 +54,12 @@ __all__ = [ "raiseExceptions", ] -_SysExcInfoType = Union[tuple[type[BaseException], BaseException, TracebackType | None], tuple[None, None, None]] -_ExcInfoType = None | bool | _SysExcInfoType | BaseException -_ArgsType = tuple[object, ...] | Mapping[str, object] -_FilterType = Filter | Callable[[LogRecord], int] -_Level = int | str -_FormatStyle = Literal["%", "{", "$"] +_SysExcInfoType: TypeAlias = Union[tuple[type[BaseException], BaseException, TracebackType | None], tuple[None, None, None]] +_ExcInfoType: TypeAlias = None | bool | _SysExcInfoType | BaseException +_ArgsType: TypeAlias = tuple[object, ...] | Mapping[str, object] +_FilterType: TypeAlias = Filter | Callable[[LogRecord], int] +_Level: TypeAlias = int | str +_FormatStyle: TypeAlias = Literal["%", "{", "$"] raiseExceptions: bool logThreads: bool diff --git a/mypy/typeshed/stdlib/logging/config.pyi b/mypy/typeshed/stdlib/logging/config.pyi index 8ee9e7b339b5..5993ba97df4b 100644 --- a/mypy/typeshed/stdlib/logging/config.pyi +++ b/mypy/typeshed/stdlib/logging/config.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import StrOrBytesPath, StrPath -from collections.abc import Callable +from collections.abc import Callable, Sequence from configparser import RawConfigParser from threading import Thread -from typing import IO, Any, Pattern, Sequence +from typing import IO, Any, Pattern +from typing_extensions import TypeAlias from . import _Level @@ -13,9 +14,9 @@ else: from typing_extensions import Literal, TypedDict if sys.version_info >= (3, 7): - _Path = StrOrBytesPath + _Path: TypeAlias = StrOrBytesPath else: - _Path = StrPath + _Path: TypeAlias = StrPath DEFAULT_LOGGING_CONFIG_PORT: int RESET_ERROR: int # undocumented diff --git a/mypy/typeshed/stdlib/lzma.pyi b/mypy/typeshed/stdlib/lzma.pyi index 45bf24b3ef6d..d4c7977b8d0a 100644 --- a/mypy/typeshed/stdlib/lzma.pyi +++ b/mypy/typeshed/stdlib/lzma.pyi @@ -1,7 +1,8 @@ import io from _typeshed import ReadableBuffer, Self, StrOrBytesPath -from typing import IO, Any, Mapping, Sequence, TextIO, overload -from typing_extensions import Literal, final +from collections.abc import Mapping, Sequence +from typing import IO, Any, TextIO, overload +from typing_extensions import Literal, TypeAlias, final __all__ = [ "CHECK_NONE", @@ -42,12 +43,12 @@ __all__ = [ "is_check_supported", ] -_OpenBinaryWritingMode = Literal["w", "wb", "x", "xb", "a", "ab"] -_OpenTextWritingMode = Literal["wt", "xt", "at"] +_OpenBinaryWritingMode: TypeAlias = Literal["w", "wb", "x", "xb", "a", "ab"] +_OpenTextWritingMode: TypeAlias = Literal["wt", "xt", "at"] -_PathOrFile = StrOrBytesPath | IO[bytes] +_PathOrFile: TypeAlias = StrOrBytesPath | IO[bytes] -_FilterChain = Sequence[Mapping[str, Any]] +_FilterChain: TypeAlias = Sequence[Mapping[str, Any]] FORMAT_AUTO: Literal[0] FORMAT_XZ: Literal[1] diff --git a/mypy/typeshed/stdlib/mailbox.pyi b/mypy/typeshed/stdlib/mailbox.pyi index 143891d3240b..64183cd0b3a4 100644 --- a/mypy/typeshed/stdlib/mailbox.pyi +++ b/mypy/typeshed/stdlib/mailbox.pyi @@ -2,9 +2,10 @@ import email.message import sys from _typeshed import Self, StrOrBytesPath from abc import ABCMeta, abstractmethod +from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from types import TracebackType -from typing import IO, Any, AnyStr, Callable, Generic, Iterable, Iterator, Mapping, Protocol, Sequence, TypeVar, overload -from typing_extensions import Literal +from typing import IO, Any, AnyStr, Generic, Protocol, TypeVar, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -31,7 +32,7 @@ __all__ = [ _T = TypeVar("_T") _MessageT = TypeVar("_MessageT", bound=Message) -_MessageData = email.message.Message | bytes | str | IO[str] | IO[bytes] +_MessageData: TypeAlias = email.message.Message | bytes | str | IO[str] | IO[bytes] class _HasIteritems(Protocol): def iteritems(self) -> Iterator[tuple[str, _MessageData]]: ... diff --git a/mypy/typeshed/stdlib/mailcap.pyi b/mypy/typeshed/stdlib/mailcap.pyi index 232ab99c314d..e1637ad6e7be 100644 --- a/mypy/typeshed/stdlib/mailcap.pyi +++ b/mypy/typeshed/stdlib/mailcap.pyi @@ -1,6 +1,7 @@ -from typing import Mapping, Sequence +from collections.abc import Mapping, Sequence +from typing_extensions import TypeAlias -_Cap = dict[str, str | int] +_Cap: TypeAlias = dict[str, str | int] __all__ = ["getcaps", "findmatch"] diff --git a/mypy/typeshed/stdlib/math.pyi b/mypy/typeshed/stdlib/math.pyi index e4ab311990be..ada510d629ed 100644 --- a/mypy/typeshed/stdlib/math.pyi +++ b/mypy/typeshed/stdlib/math.pyi @@ -1,12 +1,13 @@ import sys from _typeshed import SupportsTrunc -from typing import Iterable, SupportsFloat, overload -from typing_extensions import SupportsIndex +from collections.abc import Iterable +from typing import SupportsFloat, overload +from typing_extensions import SupportsIndex, TypeAlias if sys.version_info >= (3, 8): - _SupportsFloatOrIndex = SupportsFloat | SupportsIndex + _SupportsFloatOrIndex: TypeAlias = SupportsFloat | SupportsIndex else: - _SupportsFloatOrIndex = SupportsFloat + _SupportsFloatOrIndex: TypeAlias = SupportsFloat e: float pi: float diff --git a/mypy/typeshed/stdlib/mimetypes.pyi b/mypy/typeshed/stdlib/mimetypes.pyi index e51b7cdf37bd..c2b6ff20281a 100644 --- a/mypy/typeshed/stdlib/mimetypes.pyi +++ b/mypy/typeshed/stdlib/mimetypes.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import StrPath -from typing import IO, Sequence +from collections.abc import Sequence +from typing import IO __all__ = [ "knownfiles", diff --git a/mypy/typeshed/stdlib/mmap.pyi b/mypy/typeshed/stdlib/mmap.pyi index 744888172479..96bb01a271fc 100644 --- a/mypy/typeshed/stdlib/mmap.pyi +++ b/mypy/typeshed/stdlib/mmap.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ReadableBuffer, Self -from typing import Iterable, Iterator, NoReturn, Sized, overload +from collections.abc import Iterable, Iterator, Sized +from typing import NoReturn, overload ACCESS_DEFAULT: int ACCESS_READ: int diff --git a/mypy/typeshed/stdlib/modulefinder.pyi b/mypy/typeshed/stdlib/modulefinder.pyi index 9efe032cfd29..cd01e0e1381f 100644 --- a/mypy/typeshed/stdlib/modulefinder.pyi +++ b/mypy/typeshed/stdlib/modulefinder.pyi @@ -1,6 +1,7 @@ import sys +from collections.abc import Container, Iterable, Iterator, Sequence from types import CodeType -from typing import IO, Any, Container, Iterable, Iterator, Sequence +from typing import IO, Any LOAD_CONST: int # undocumented IMPORT_NAME: int # undocumented diff --git a/mypy/typeshed/stdlib/msilib/__init__.pyi b/mypy/typeshed/stdlib/msilib/__init__.pyi index c512489be34d..968efbec7a6c 100644 --- a/mypy/typeshed/stdlib/msilib/__init__.pyi +++ b/mypy/typeshed/stdlib/msilib/__init__.pyi @@ -1,6 +1,7 @@ import sys +from collections.abc import Container, Iterable, Sequence from types import ModuleType -from typing import Any, Container, Iterable, Sequence +from typing import Any from typing_extensions import Literal if sys.platform == "win32": diff --git a/mypy/typeshed/stdlib/msilib/sequence.pyi b/mypy/typeshed/stdlib/msilib/sequence.pyi index 30346aba3367..9cc1e0eaec01 100644 --- a/mypy/typeshed/stdlib/msilib/sequence.pyi +++ b/mypy/typeshed/stdlib/msilib/sequence.pyi @@ -1,8 +1,9 @@ import sys +from typing_extensions import TypeAlias if sys.platform == "win32": - _SequenceType = list[tuple[str, str | None, int]] + _SequenceType: TypeAlias = list[tuple[str, str | None, int]] AdminExecuteSequence: _SequenceType AdminUISequence: _SequenceType diff --git a/mypy/typeshed/stdlib/multiprocessing/__init__.pyi b/mypy/typeshed/stdlib/multiprocessing/__init__.pyi index 3a8a382b8a27..87ceda10573d 100644 --- a/mypy/typeshed/stdlib/multiprocessing/__init__.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/__init__.pyi @@ -20,8 +20,8 @@ from multiprocessing.process import active_children as active_children, current_ # multiprocessing.queues or the aliases defined below. See #4266 for discussion. from multiprocessing.queues import JoinableQueue as JoinableQueue, Queue as Queue, SimpleQueue as SimpleQueue from multiprocessing.spawn import freeze_support as freeze_support -from typing import Any, overload -from typing_extensions import Literal +from typing import Any, TypeVar, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 8): from multiprocessing.process import parent_process as parent_process @@ -118,23 +118,24 @@ else: # from multiprocessing import _LockType # lock: _LockType = Lock() -_QueueType = Queue -_SimpleQueueType = SimpleQueue -_JoinableQueueType = JoinableQueue -_BarrierType = synchronize.Barrier -_BoundedSemaphoreType = synchronize.BoundedSemaphore -_ConditionType = synchronize.Condition -_EventType = synchronize.Event -_LockType = synchronize.Lock -_RLockType = synchronize.RLock -_SemaphoreType = synchronize.Semaphore +_T = TypeVar("_T") +_QueueType: TypeAlias = Queue[_T] +_SimpleQueueType: TypeAlias = SimpleQueue[_T] +_JoinableQueueType: TypeAlias = JoinableQueue[_T] +_BarrierType: TypeAlias = synchronize.Barrier +_BoundedSemaphoreType: TypeAlias = synchronize.BoundedSemaphore +_ConditionType: TypeAlias = synchronize.Condition +_EventType: TypeAlias = synchronize.Event +_LockType: TypeAlias = synchronize.Lock +_RLockType: TypeAlias = synchronize.RLock +_SemaphoreType: TypeAlias = synchronize.Semaphore # N.B. The functions below are generated at runtime by partially applying # multiprocessing.context.BaseContext's methods, so the two signatures should # be identical (modulo self). # Synchronization primitives -_LockLike = synchronize.Lock | synchronize.RLock +_LockLike: TypeAlias = synchronize.Lock | synchronize.RLock RawValue = context._default_context.RawValue RawArray = context._default_context.RawArray Value = context._default_context.Value diff --git a/mypy/typeshed/stdlib/multiprocessing/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/connection.pyi index 5db6fa4cda7e..7b227a697abe 100644 --- a/mypy/typeshed/stdlib/multiprocessing/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/connection.pyi @@ -2,13 +2,14 @@ import socket import sys import types from _typeshed import Self -from typing import Any, Iterable, Union -from typing_extensions import SupportsIndex +from collections.abc import Iterable +from typing import Any, Union +from typing_extensions import SupportsIndex, TypeAlias __all__ = ["Client", "Listener", "Pipe", "wait"] # https://docs.python.org/3/library/multiprocessing.html#address-formats -_Address = Union[str, tuple[str, int]] +_Address: TypeAlias = Union[str, tuple[str, int]] class _ConnectionBase: def __init__(self, handle: SupportsIndex, readable: bool = ..., writable: bool = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/context.pyi b/mypy/typeshed/stdlib/multiprocessing/context.pyi index 315918a04b98..d618d1028112 100644 --- a/mypy/typeshed/stdlib/multiprocessing/context.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/context.pyi @@ -9,14 +9,14 @@ from multiprocessing.pool import Pool as _Pool from multiprocessing.process import BaseProcess from multiprocessing.sharedctypes import SynchronizedArray, SynchronizedBase from typing import Any, ClassVar, TypeVar, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 8): __all__ = () else: __all__: list[str] = [] -_LockLike = synchronize.Lock | synchronize.RLock +_LockLike: TypeAlias = synchronize.Lock | synchronize.RLock _CT = TypeVar("_CT", bound=_CData) class ProcessError(Exception): ... diff --git a/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi b/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi index 48f42999866a..bbddfd16ded7 100644 --- a/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/dummy/__init__.pyi @@ -1,8 +1,9 @@ import array import threading import weakref +from collections.abc import Callable, Iterable, Mapping, Sequence from queue import Queue as Queue -from typing import Any, Callable, Iterable, Mapping, Sequence +from typing import Any from typing_extensions import Literal from .connection import Pipe as Pipe diff --git a/mypy/typeshed/stdlib/multiprocessing/dummy/connection.pyi b/mypy/typeshed/stdlib/multiprocessing/dummy/connection.pyi index c61617cd91d4..fd909d0d32e1 100644 --- a/mypy/typeshed/stdlib/multiprocessing/dummy/connection.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/dummy/connection.pyi @@ -2,12 +2,13 @@ from _typeshed import Self from queue import Queue from types import TracebackType from typing import Any, Union +from typing_extensions import TypeAlias __all__ = ["Client", "Listener", "Pipe"] families: list[None] -_Address = Union[str, tuple[str, int]] +_Address: TypeAlias = Union[str, tuple[str, int]] class Connection: _in: Any diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index a0f76b636c4b..234660cc4f80 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -2,19 +2,20 @@ import queue import sys import threading from _typeshed import Self +from builtins import dict as _dict, list as _list # Conflicts with method names +from collections.abc import Callable, Iterable, Mapping, Sequence from types import TracebackType -from typing import Any, AnyStr, Callable, Generic, Iterable, Mapping, Sequence, TypeVar +from typing import Any, AnyStr, Generic, TypeVar +from typing_extensions import TypeAlias from .connection import Connection from .context import BaseContext if sys.version_info >= (3, 8): - from .shared_memory import _SLT, ShareableList, SharedMemory + from .shared_memory import _SLT, ShareableList as _ShareableList, SharedMemory as _SharedMemory __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token", "SharedMemoryManager"] - _SharedMemory = SharedMemory - _ShareableList = ShareableList else: __all__ = ["BaseManager", "SyncManager", "BaseProxy", "Token"] @@ -30,7 +31,7 @@ class Namespace: def __getattr__(self, __name: str) -> Any: ... def __setattr__(self, __name: str, __value: Any) -> None: ... -_Namespace = Namespace +_Namespace: TypeAlias = Namespace class Token: typeid: str | bytes | None @@ -100,10 +101,6 @@ class BaseManager: self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None ) -> None: ... -# Conflicts with method names -_dict = dict -_list = list - class SyncManager(BaseManager): def BoundedSemaphore(self, value: Any = ...) -> threading.BoundedSemaphore: ... def Condition(self, lock: Any = ...) -> threading.Condition: ... diff --git a/mypy/typeshed/stdlib/multiprocessing/pool.pyi b/mypy/typeshed/stdlib/multiprocessing/pool.pyi index 5e38e0161834..c0d01e98dfae 100644 --- a/mypy/typeshed/stdlib/multiprocessing/pool.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/pool.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import Self +from collections.abc import Callable, Iterable, Iterator, Mapping from types import TracebackType -from typing import Any, Callable, Generic, Iterable, Iterator, Mapping, TypeVar +from typing import Any, Generic, TypeVar from typing_extensions import Literal if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi index a4c4fd071c5f..76ccedaf478e 100644 --- a/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/shared_memory.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import Self -from typing import Any, Generic, Iterable, TypeVar +from collections.abc import Iterable +from typing import Any, Generic, TypeVar if sys.version_info >= (3, 9): from types import GenericAlias diff --git a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi index 4f981ea467c4..50570ff3717b 100644 --- a/mypy/typeshed/stdlib/multiprocessing/spawn.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/spawn.pyi @@ -1,5 +1,6 @@ +from collections.abc import Mapping, Sequence from types import ModuleType -from typing import Any, Mapping, Sequence +from typing import Any __all__ = [ "_main", diff --git a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi index 0cfc815b2d82..e93d6c58b5cf 100644 --- a/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/synchronize.pyi @@ -1,13 +1,15 @@ import sys import threading +from collections.abc import Callable from contextlib import AbstractContextManager from multiprocessing.context import BaseContext from types import TracebackType -from typing import Any, Callable +from typing import Any +from typing_extensions import TypeAlias __all__ = ["Lock", "RLock", "Semaphore", "BoundedSemaphore", "Condition", "Event"] -_LockLike = Lock | RLock +_LockLike: TypeAlias = Lock | RLock class Barrier(threading.Barrier): def __init__( diff --git a/mypy/typeshed/stdlib/netrc.pyi b/mypy/typeshed/stdlib/netrc.pyi index 45f6cfbeda7b..803c78073348 100644 --- a/mypy/typeshed/stdlib/netrc.pyi +++ b/mypy/typeshed/stdlib/netrc.pyi @@ -1,4 +1,5 @@ from _typeshed import StrOrBytesPath +from typing_extensions import TypeAlias __all__ = ["netrc", "NetrcParseError"] @@ -9,7 +10,7 @@ class NetrcParseError(Exception): def __init__(self, msg: str, filename: StrOrBytesPath | None = ..., lineno: int | None = ...) -> None: ... # (login, account, password) tuple -_NetrcTuple = tuple[str, str | None, str | None] +_NetrcTuple: TypeAlias = tuple[str, str | None, str | None] class netrc: hosts: dict[str, _NetrcTuple] diff --git a/mypy/typeshed/stdlib/nntplib.pyi b/mypy/typeshed/stdlib/nntplib.pyi index cc48cb83ae4c..aa5bcba5726c 100644 --- a/mypy/typeshed/stdlib/nntplib.pyi +++ b/mypy/typeshed/stdlib/nntplib.pyi @@ -3,8 +3,10 @@ import socket import ssl import sys from _typeshed import Self -from typing import IO, Any, Iterable, NamedTuple -from typing_extensions import Literal +from builtins import list as _list # conflicts with a method named "list" +from collections.abc import Iterable +from typing import IO, Any, NamedTuple +from typing_extensions import Literal, TypeAlias __all__ = [ "NNTP", @@ -18,7 +20,7 @@ __all__ = [ "NNTP_SSL", ] -_File = IO[bytes] | bytes | str | None +_File: TypeAlias = IO[bytes] | bytes | str | None class NNTPError(Exception): response: str @@ -45,8 +47,6 @@ class ArticleInfo(NamedTuple): def decode_header(header_str: str) -> str: ... -_list = list # conflicts with a method named "list" - class NNTP: encoding: str errors: str diff --git a/mypy/typeshed/stdlib/optparse.pyi b/mypy/typeshed/stdlib/optparse.pyi index 7aedf583e556..1e7428f59a95 100644 --- a/mypy/typeshed/stdlib/optparse.pyi +++ b/mypy/typeshed/stdlib/optparse.pyi @@ -1,5 +1,6 @@ from abc import abstractmethod -from typing import IO, Any, AnyStr, Callable, Iterable, Mapping, Sequence, overload +from collections.abc import Callable, Iterable, Mapping, Sequence +from typing import IO, Any, AnyStr, overload __all__ = [ "Option", diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 2ef781bbe288..76c114591d32 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -14,28 +14,12 @@ from _typeshed import ( ) from abc import abstractmethod from builtins import OSError +from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping, Sequence from contextlib import AbstractContextManager from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper as _TextIOWrapper from subprocess import Popen -from typing import ( - IO, - Any, - AnyStr, - BinaryIO, - Callable, - Generic, - Iterable, - Iterator, - Mapping, - MutableMapping, - NoReturn, - Protocol, - Sequence, - TypeVar, - overload, - runtime_checkable, -) -from typing_extensions import Final, Literal, final +from typing import IO, Any, AnyStr, BinaryIO, Generic, NoReturn, Protocol, TypeVar, overload, runtime_checkable +from typing_extensions import Final, Literal, TypeAlias, final from . import path as _path @@ -211,7 +195,7 @@ R_OK: int W_OK: int X_OK: int -_EnvironCodeFunc = Callable[[AnyStr], AnyStr] +_EnvironCodeFunc: TypeAlias = Callable[[AnyStr], AnyStr] class _Environ(MutableMapping[AnyStr, AnyStr], Generic[AnyStr]): encodekey: _EnvironCodeFunc[AnyStr] @@ -383,7 +367,7 @@ def listdir(path: BytesPath) -> list[bytes]: ... @overload def listdir(path: int) -> list[str]: ... -_FdOrAnyPath = int | StrOrBytesPath +_FdOrAnyPath: TypeAlias = int | StrOrBytesPath @final class DirEntry(Generic[AnyStr]): @@ -404,9 +388,9 @@ class DirEntry(Generic[AnyStr]): def __class_getitem__(cls, item: Any) -> GenericAlias: ... if sys.version_info >= (3, 7): - _StatVfsTuple = tuple[int, int, int, int, int, int, int, int, int, int, int] + _StatVfsTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int] else: - _StatVfsTuple = tuple[int, int, int, int, int, int, int, int, int, int] + _StatVfsTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int] @final class statvfs_result(structseq[int], _StatVfsTuple): @@ -527,7 +511,7 @@ def putenv(__name: bytes | str, __value: bytes | str) -> None: ... if sys.platform != "win32" or sys.version_info >= (3, 9): def unsetenv(__name: bytes | str) -> None: ... -_Opener = Callable[[str, int], int] +_Opener: TypeAlias = Callable[[str, int], int] @overload def fdopen( @@ -787,7 +771,7 @@ def utime( follow_symlinks: bool = ..., ) -> None: ... -_OnError = Callable[[OSError], Any] +_OnError: TypeAlias = Callable[[OSError], Any] def walk( top: AnyStr | PathLike[AnyStr], topdown: bool = ..., onerror: _OnError | None = ..., followlinks: bool = ... @@ -845,7 +829,7 @@ def execlpe(file: StrOrBytesPath, __arg0: StrOrBytesPath, *args: Any) -> NoRetur # Not separating out PathLike[str] and PathLike[bytes] here because it doesn't make much difference # in practice, and doing so would explode the number of combinations in this already long union. # All these combinations are necessary due to list being invariant. -_ExecVArgs = ( +_ExecVArgs: TypeAlias = ( tuple[StrOrBytesPath, ...] | list[bytes] | list[str] @@ -855,7 +839,7 @@ _ExecVArgs = ( | list[str | PathLike[Any]] | list[bytes | str | PathLike[Any]] ) -_ExecEnv = Mapping[bytes, bytes | str] | Mapping[str, bytes | str] +_ExecEnv: TypeAlias = Mapping[bytes, bytes | str] | Mapping[str, bytes | str] def execv(__path: StrOrBytesPath, __argv: _ExecVArgs) -> NoReturn: ... def execve(path: _FdOrAnyPath, argv: _ExecVArgs, env: _ExecEnv) -> NoReturn: ... diff --git a/mypy/typeshed/stdlib/parser.pyi b/mypy/typeshed/stdlib/parser.pyi index 95e770b57256..cce8594eac58 100644 --- a/mypy/typeshed/stdlib/parser.pyi +++ b/mypy/typeshed/stdlib/parser.pyi @@ -1,6 +1,7 @@ from _typeshed import StrOrBytesPath +from collections.abc import Sequence from types import CodeType -from typing import Any, Sequence +from typing import Any from typing_extensions import final def expr(source: str) -> STType: ... diff --git a/mypy/typeshed/stdlib/pathlib.pyi b/mypy/typeshed/stdlib/pathlib.pyi index 45917ce59f8f..65aead6cb4de 100644 --- a/mypy/typeshed/stdlib/pathlib.pyi +++ b/mypy/typeshed/stdlib/pathlib.pyi @@ -8,10 +8,11 @@ from _typeshed import ( Self, StrPath, ) +from collections.abc import Generator, Sequence from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper from os import PathLike, stat_result from types import TracebackType -from typing import IO, Any, BinaryIO, Generator, Sequence, overload +from typing import IO, Any, BinaryIO, overload from typing_extensions import Literal if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/pdb.pyi b/mypy/typeshed/stdlib/pdb.pyi index c1cba5e8d23b..e787b4a4c416 100644 --- a/mypy/typeshed/stdlib/pdb.pyi +++ b/mypy/typeshed/stdlib/pdb.pyi @@ -3,9 +3,10 @@ import sys from _typeshed import Self from bdb import Bdb from cmd import Cmd +from collections.abc import Callable, Iterable, Mapping, Sequence from inspect import _SourceObjectType from types import CodeType, FrameType, TracebackType -from typing import IO, Any, Callable, ClassVar, Iterable, Mapping, Sequence, TypeVar +from typing import IO, Any, ClassVar, TypeVar from typing_extensions import ParamSpec __all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", "post_mortem", "help"] diff --git a/mypy/typeshed/stdlib/pickle.pyi b/mypy/typeshed/stdlib/pickle.pyi index 26ee94ca2682..d58cf8ed9d50 100644 --- a/mypy/typeshed/stdlib/pickle.pyi +++ b/mypy/typeshed/stdlib/pickle.pyi @@ -1,6 +1,8 @@ import sys -from typing import Any, Callable, ClassVar, Iterable, Iterator, Mapping, Protocol, Union -from typing_extensions import final +from _typeshed import ReadableBuffer +from collections.abc import Callable, Iterable, Iterator, Mapping +from typing import Any, ClassVar, Protocol, SupportsBytes, Union +from typing_extensions import SupportsIndex, TypeAlias, final if sys.version_info >= (3, 8): __all__ = [ @@ -182,14 +184,12 @@ class _WritableFileobj(Protocol): def write(self, __b: bytes) -> Any: ... if sys.version_info >= (3, 8): - # TODO: holistic design for buffer interface (typing.Buffer?) @final class PickleBuffer: - # buffer must be a buffer-providing object - def __init__(self, buffer: Any) -> None: ... + def __init__(self, buffer: ReadableBuffer) -> None: ... def raw(self) -> memoryview: ... def release(self) -> None: ... - _BufferCallback = Callable[[PickleBuffer], Any] | None + _BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None def dump( obj: Any, file: _WritableFileobj, @@ -210,20 +210,25 @@ if sys.version_info >= (3, 8): buffers: Iterable[Any] | None = ..., ) -> Any: ... def loads( - __data: bytes, *, fix_imports: bool = ..., encoding: str = ..., errors: str = ..., buffers: Iterable[Any] | None = ... + __data: ReadableBuffer, + *, + fix_imports: bool = ..., + encoding: str = ..., + errors: str = ..., + buffers: Iterable[Any] | None = ..., ) -> Any: ... else: def dump(obj: Any, file: _WritableFileobj, protocol: int | None = ..., *, fix_imports: bool = ...) -> None: ... def dumps(obj: Any, protocol: int | None = ..., *, fix_imports: bool = ...) -> bytes: ... def load(file: _ReadableFileobj, *, fix_imports: bool = ..., encoding: str = ..., errors: str = ...) -> Any: ... - def loads(data: bytes, *, fix_imports: bool = ..., encoding: str = ..., errors: str = ...) -> Any: ... + def loads(data: ReadableBuffer, *, fix_imports: bool = ..., encoding: str = ..., errors: str = ...) -> Any: ... class PickleError(Exception): ... class PicklingError(PickleError): ... class UnpicklingError(PickleError): ... -_reducedtype = Union[ +_reducedtype: TypeAlias = Union[ str, tuple[Callable[..., Any], tuple[Any, ...]], tuple[Callable[..., Any], tuple[Any, ...], Any], @@ -358,7 +363,7 @@ if sys.version_info >= (3, 8): READONLY_BUFFER: bytes def encode_long(x: int) -> bytes: ... # undocumented -def decode_long(data: bytes) -> int: ... # undocumented +def decode_long(data: Iterable[SupportsIndex] | SupportsBytes | ReadableBuffer) -> int: ... # undocumented # pure-Python implementations _Pickler = Pickler # undocumented diff --git a/mypy/typeshed/stdlib/pickletools.pyi b/mypy/typeshed/stdlib/pickletools.pyi index 7b79ddcff347..c78848464237 100644 --- a/mypy/typeshed/stdlib/pickletools.pyi +++ b/mypy/typeshed/stdlib/pickletools.pyi @@ -1,8 +1,10 @@ -from typing import IO, Any, Callable, Iterator, MutableMapping +from collections.abc import Callable, Iterator, MutableMapping +from typing import IO, Any +from typing_extensions import TypeAlias __all__ = ["dis", "genops", "optimize"] -_Reader = Callable[[IO[bytes]], Any] +_Reader: TypeAlias = Callable[[IO[bytes]], Any] bytes_types: tuple[type[Any], ...] UP_TO_NEWLINE: int diff --git a/mypy/typeshed/stdlib/pkgutil.pyi b/mypy/typeshed/stdlib/pkgutil.pyi index 7c27f6702a7e..5a93a9f86812 100644 --- a/mypy/typeshed/stdlib/pkgutil.pyi +++ b/mypy/typeshed/stdlib/pkgutil.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import SupportsRead +from collections.abc import Callable, Iterable, Iterator from importlib.abc import Loader, MetaPathFinder, PathEntryFinder -from typing import IO, Any, Callable, Iterable, Iterator, NamedTuple, TypeVar +from typing import IO, Any, NamedTuple, TypeVar __all__ = [ "get_importer", diff --git a/mypy/typeshed/stdlib/plistlib.pyi b/mypy/typeshed/stdlib/plistlib.pyi index 67d1611de828..de5fe1b75ca0 100644 --- a/mypy/typeshed/stdlib/plistlib.pyi +++ b/mypy/typeshed/stdlib/plistlib.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import Self +from collections.abc import Mapping, MutableMapping from datetime import datetime from enum import Enum -from typing import IO, Any, Mapping, MutableMapping +from typing import IO, Any if sys.version_info >= (3, 9): __all__ = ["InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps", "UID"] diff --git a/mypy/typeshed/stdlib/poplib.pyi b/mypy/typeshed/stdlib/poplib.pyi index 6b651e98e41f..487a7266694c 100644 --- a/mypy/typeshed/stdlib/poplib.pyi +++ b/mypy/typeshed/stdlib/poplib.pyi @@ -1,11 +1,12 @@ import socket import ssl +from builtins import list as _list # conflicts with a method named "list" from typing import Any, BinaryIO, NoReturn, Pattern, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias __all__ = ["POP3", "error_proto", "POP3_SSL"] -_LongResp = tuple[bytes, list[bytes], int] +_LongResp: TypeAlias = tuple[bytes, list[bytes], int] class error_proto(Exception): ... @@ -16,8 +17,6 @@ LF: Literal[b"\n"] CRLF: Literal[b"\r\n"] HAVE_SSL: bool -_list = list # conflicts with a method named "list" - class POP3: encoding: str host: str diff --git a/mypy/typeshed/stdlib/posixpath.pyi b/mypy/typeshed/stdlib/posixpath.pyi index c72ba8a99bdd..4cec7c770ea3 100644 --- a/mypy/typeshed/stdlib/posixpath.pyi +++ b/mypy/typeshed/stdlib/posixpath.pyi @@ -1,5 +1,6 @@ import sys from _typeshed import BytesPath, StrOrBytesPath, StrPath +from collections.abc import Sequence from genericpath import ( commonprefix as commonprefix, exists as exists, @@ -14,7 +15,7 @@ from genericpath import ( samestat as samestat, ) from os import PathLike -from typing import AnyStr, Sequence, overload +from typing import AnyStr, overload __all__ = [ "normcase", diff --git a/mypy/typeshed/stdlib/profile.pyi b/mypy/typeshed/stdlib/profile.pyi index 982bcabad401..4b3f832d3224 100644 --- a/mypy/typeshed/stdlib/profile.pyi +++ b/mypy/typeshed/stdlib/profile.pyi @@ -1,6 +1,7 @@ from _typeshed import Self, StrOrBytesPath -from typing import Any, Callable, TypeVar -from typing_extensions import ParamSpec +from collections.abc import Callable +from typing import Any, TypeVar +from typing_extensions import ParamSpec, TypeAlias __all__ = ["run", "runctx", "Profile"] @@ -11,7 +12,7 @@ def runctx( _T = TypeVar("_T") _P = ParamSpec("_P") -_Label = tuple[str, int, str] +_Label: TypeAlias = tuple[str, int, str] class Profile: bias: int diff --git a/mypy/typeshed/stdlib/pstats.pyi b/mypy/typeshed/stdlib/pstats.pyi index a7b8bebe4066..7868512e5ab9 100644 --- a/mypy/typeshed/stdlib/pstats.pyi +++ b/mypy/typeshed/stdlib/pstats.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import Self, StrOrBytesPath +from collections.abc import Iterable from cProfile import Profile as _cProfile from profile import Profile -from typing import IO, Any, Iterable, overload -from typing_extensions import Literal +from typing import IO, Any, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): __all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"] @@ -12,7 +13,7 @@ elif sys.version_info >= (3, 7): else: __all__ = ["Stats"] -_Selector = str | float | int +_Selector: TypeAlias = str | float | int if sys.version_info >= (3, 7): from enum import Enum @@ -45,7 +46,7 @@ if sys.version_info >= (3, 9): total_tt: float func_profiles: dict[str, FunctionProfile] -_SortArgDict = dict[str, tuple[tuple[tuple[int, int], ...], str]] +_SortArgDict: TypeAlias = dict[str, tuple[tuple[tuple[int, int], ...], str]] class Stats: sort_arg_dict_default: _SortArgDict diff --git a/mypy/typeshed/stdlib/pty.pyi b/mypy/typeshed/stdlib/pty.pyi index 73c6ddfbd0c4..a6a2d8fabb69 100644 --- a/mypy/typeshed/stdlib/pty.pyi +++ b/mypy/typeshed/stdlib/pty.pyi @@ -1,10 +1,10 @@ import sys -from typing import Callable, Iterable -from typing_extensions import Literal +from collections.abc import Callable, Iterable +from typing_extensions import Literal, TypeAlias if sys.platform != "win32": __all__ = ["openpty", "fork", "spawn"] - _Reader = Callable[[int], bytes] + _Reader: TypeAlias = Callable[[int], bytes] STDIN_FILENO: Literal[0] STDOUT_FILENO: Literal[1] diff --git a/mypy/typeshed/stdlib/pydoc.pyi b/mypy/typeshed/stdlib/pydoc.pyi index b4fa66c60155..6e5d3e818f83 100644 --- a/mypy/typeshed/stdlib/pydoc.pyi +++ b/mypy/typeshed/stdlib/pydoc.pyi @@ -1,14 +1,13 @@ -from _typeshed import SupportsWrite +from _typeshed import OptExcInfo, SupportsWrite from abc import abstractmethod +from builtins import list as _list # "list" conflicts with method name +from collections.abc import Callable, Container, Mapping, MutableMapping from reprlib import Repr from types import MethodType, ModuleType, TracebackType -from typing import IO, Any, AnyStr, Callable, Container, Mapping, MutableMapping, NoReturn, TypeVar +from typing import IO, Any, AnyStr, NoReturn, TypeVar __all__ = ["help"] -# the return type of sys.exc_info(), used by ErrorDuringImport.__init__ -_Exc_Info = tuple[type[BaseException] | None, BaseException | None, TracebackType | None] - _T = TypeVar("_T") __author__: str @@ -36,7 +35,7 @@ class ErrorDuringImport(Exception): exc: type[BaseException] | None value: BaseException | None tb: TracebackType | None - def __init__(self, filename: str, exc_info: _Exc_Info) -> None: ... + def __init__(self, filename: str, exc_info: OptExcInfo) -> None: ... def importfile(path: str) -> ModuleType: ... def safeimport(path: str, forceload: bool = ..., cache: MutableMapping[str, ModuleType] = ...) -> ModuleType: ... @@ -195,8 +194,6 @@ def doc(thing: str | object, title: str = ..., forceload: bool = ..., output: Su def writedoc(thing: str | object, forceload: bool = ...) -> None: ... def writedocs(dir: str, pkgpath: str = ..., done: Any | None = ...) -> None: ... -_list = list # "list" conflicts with method name - class Helper: keywords: dict[str, str | tuple[str, str]] symbols: dict[str, str] diff --git a/mypy/typeshed/stdlib/pyexpat/__init__.pyi b/mypy/typeshed/stdlib/pyexpat/__init__.pyi index 24c93965b21f..2fe76a3d61b6 100644 --- a/mypy/typeshed/stdlib/pyexpat/__init__.pyi +++ b/mypy/typeshed/stdlib/pyexpat/__init__.pyi @@ -1,8 +1,9 @@ import pyexpat.errors as errors import pyexpat.model as model from _typeshed import SupportsRead -from typing import Any, Callable -from typing_extensions import final +from collections.abc import Callable +from typing import Any +from typing_extensions import TypeAlias, final EXPAT_VERSION: str # undocumented version_info: tuple[int, int, int] # undocumented @@ -20,7 +21,7 @@ XML_PARAM_ENTITY_PARSING_NEVER: int XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE: int XML_PARAM_ENTITY_PARSING_ALWAYS: int -_Model = tuple[int, int, str | None, tuple[Any, ...]] +_Model: TypeAlias = tuple[int, int, str | None, tuple[Any, ...]] @final class XMLParserType: diff --git a/mypy/typeshed/stdlib/random.pyi b/mypy/typeshed/stdlib/random.pyi index 612a54cb95b0..255436dc377d 100644 --- a/mypy/typeshed/stdlib/random.pyi +++ b/mypy/typeshed/stdlib/random.pyi @@ -89,7 +89,10 @@ class Random(_random.Random): cum_weights: Sequence[float | Fraction] | None = ..., k: int = ..., ) -> list[_T]: ... - def shuffle(self, x: MutableSequence[Any], random: Callable[[], float] | None = ...) -> None: ... + if sys.version_info >= (3, 11): + def shuffle(self, x: MutableSequence[Any]) -> None: ... + else: + def shuffle(self, x: MutableSequence[Any], random: Callable[[], float] | None = ...) -> None: ... if sys.version_info >= (3, 11): def sample(self, population: Sequence[_T], k: int, *, counts: Iterable[int] | None = ...) -> list[_T]: ... elif sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index 57dab8eb820f..ff2a55fb4e61 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -1,8 +1,10 @@ import enum import sre_compile import sys +from collections.abc import Callable, Iterator from sre_constants import error as error -from typing import Any, AnyStr, Callable, Iterator, overload +from typing import Any, AnyStr, overload +from typing_extensions import TypeAlias # ----- re variables and constants ----- if sys.version_info >= (3, 7): @@ -147,7 +149,7 @@ T = RegexFlag.T TEMPLATE = RegexFlag.TEMPLATE if sys.version_info >= (3, 11): NOFLAG = RegexFlag.NOFLAG -_FlagsType = int | RegexFlag +_FlagsType: TypeAlias = int | RegexFlag if sys.version_info < (3, 7): # undocumented diff --git a/mypy/typeshed/stdlib/readline.pyi b/mypy/typeshed/stdlib/readline.pyi index df08a3cc25ff..ceca2e32f221 100644 --- a/mypy/typeshed/stdlib/readline.pyi +++ b/mypy/typeshed/stdlib/readline.pyi @@ -1,10 +1,11 @@ import sys from _typeshed import StrOrBytesPath -from typing import Callable, Sequence +from collections.abc import Callable, Sequence +from typing_extensions import TypeAlias if sys.platform != "win32": - _Completer = Callable[[str, int], str | None] - _CompDisp = Callable[[str, Sequence[str], int], None] + _Completer: TypeAlias = Callable[[str, int], str | None] + _CompDisp: TypeAlias = Callable[[str, Sequence[str], int], None] def parse_and_bind(__string: str) -> None: ... def read_init_file(__filename: StrOrBytesPath | None = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/reprlib.pyi b/mypy/typeshed/stdlib/reprlib.pyi index 2d114a7c4f26..d5554344c494 100644 --- a/mypy/typeshed/stdlib/reprlib.pyi +++ b/mypy/typeshed/stdlib/reprlib.pyi @@ -1,10 +1,12 @@ from array import array from collections import deque -from typing import Any, Callable +from collections.abc import Callable +from typing import Any +from typing_extensions import TypeAlias __all__ = ["Repr", "repr", "recursive_repr"] -_ReprFunc = Callable[[Any], str] +_ReprFunc: TypeAlias = Callable[[Any], str] def recursive_repr(fillvalue: str = ...) -> Callable[[_ReprFunc], _ReprFunc]: ... diff --git a/mypy/typeshed/stdlib/sched.pyi b/mypy/typeshed/stdlib/sched.pyi index dff781b0c176..709d6f47ff65 100644 --- a/mypy/typeshed/stdlib/sched.pyi +++ b/mypy/typeshed/stdlib/sched.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Callable, NamedTuple +from collections.abc import Callable +from typing import Any, NamedTuple __all__ = ["scheduler"] diff --git a/mypy/typeshed/stdlib/select.pyi b/mypy/typeshed/stdlib/select.pyi index d4a3656e110e..7cfea9ea0fc1 100644 --- a/mypy/typeshed/stdlib/select.pyi +++ b/mypy/typeshed/stdlib/select.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import FileDescriptorLike, Self +from collections.abc import Iterable from types import TracebackType -from typing import Any, Iterable +from typing import Any from typing_extensions import final if sys.platform != "win32": diff --git a/mypy/typeshed/stdlib/selectors.pyi b/mypy/typeshed/stdlib/selectors.pyi index 23a94a29a74d..95dfaa41a5c0 100644 --- a/mypy/typeshed/stdlib/selectors.pyi +++ b/mypy/typeshed/stdlib/selectors.pyi @@ -1,9 +1,11 @@ import sys from _typeshed import FileDescriptor, FileDescriptorLike, Self from abc import ABCMeta, abstractmethod -from typing import Any, Mapping, NamedTuple +from collections.abc import Mapping +from typing import Any, NamedTuple +from typing_extensions import TypeAlias -_EventMask = int +_EventMask: TypeAlias = int EVENT_READ: _EventMask EVENT_WRITE: _EventMask diff --git a/mypy/typeshed/stdlib/shlex.pyi b/mypy/typeshed/stdlib/shlex.pyi index 6385011575a4..fe0f80ba26c1 100644 --- a/mypy/typeshed/stdlib/shlex.pyi +++ b/mypy/typeshed/stdlib/shlex.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import Self -from typing import Iterable, TextIO +from collections.abc import Iterable +from typing import TextIO if sys.version_info >= (3, 8): __all__ = ["shlex", "split", "quote", "join"] diff --git a/mypy/typeshed/stdlib/shutil.pyi b/mypy/typeshed/stdlib/shutil.pyi index 5fa5f6669f1b..b367a46fe572 100644 --- a/mypy/typeshed/stdlib/shutil.pyi +++ b/mypy/typeshed/stdlib/shutil.pyi @@ -1,7 +1,9 @@ import os import sys from _typeshed import BytesPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite -from typing import Any, AnyStr, Callable, Iterable, NamedTuple, Sequence, TypeVar, overload +from collections.abc import Callable, Iterable, Sequence +from typing import Any, AnyStr, NamedTuple, TypeVar, overload +from typing_extensions import TypeAlias __all__ = [ "copyfileobj", @@ -36,7 +38,7 @@ _StrOrBytesPathT = TypeVar("_StrOrBytesPathT", bound=StrOrBytesPath) _StrPathT = TypeVar("_StrPathT", bound=StrPath) # Return value of some functions that may either return a path-like object that was passed in or # a string -_PathReturn = Any +_PathReturn: TypeAlias = Any class Error(OSError): ... class SameFileError(Error): ... @@ -82,7 +84,7 @@ else: def rmtree(path: StrOrBytesPath, ignore_errors: bool = ..., onerror: Callable[[Any, Any, Any], Any] | None = ...) -> None: ... -_CopyFn = Callable[[str, str], None] | Callable[[StrPath, StrPath], None] +_CopyFn: TypeAlias = Callable[[str, str], None] | Callable[[StrPath, StrPath], None] # N.B. shutil.move appears to take bytes arguments, however, # this does not work when dst is (or is within) an existing directory. diff --git a/mypy/typeshed/stdlib/signal.pyi b/mypy/typeshed/stdlib/signal.pyi index 2defe7995991..893d9d45e4b1 100644 --- a/mypy/typeshed/stdlib/signal.pyi +++ b/mypy/typeshed/stdlib/signal.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import structseq +from collections.abc import Callable, Iterable from enum import IntEnum from types import FrameType -from typing import Any, Callable, Iterable, Union -from typing_extensions import Final, final +from typing import Any, Union +from typing_extensions import Final, TypeAlias, final NSIG: int @@ -60,8 +61,8 @@ class Handlers(IntEnum): SIG_DFL: Handlers SIG_IGN: Handlers -_SIGNUM = int | Signals -_HANDLER = Union[Callable[[int, FrameType | None], Any], int, Handlers, None] +_SIGNUM: TypeAlias = int | Signals +_HANDLER: TypeAlias = Union[Callable[[int, FrameType | None], Any], int, Handlers, None] def default_int_handler(__signalnum: int, __frame: FrameType | None) -> None: ... diff --git a/mypy/typeshed/stdlib/site.pyi b/mypy/typeshed/stdlib/site.pyi index a73d188a7e5c..53199db0eaf3 100644 --- a/mypy/typeshed/stdlib/site.pyi +++ b/mypy/typeshed/stdlib/site.pyi @@ -1,5 +1,5 @@ from _typeshed import StrPath -from typing import Iterable +from collections.abc import Iterable PREFIXES: list[str] ENABLE_USER_SITE: bool | None diff --git a/mypy/typeshed/stdlib/smtpd.pyi b/mypy/typeshed/stdlib/smtpd.pyi index 037f62a8d6e1..fc5a1cb62b16 100644 --- a/mypy/typeshed/stdlib/smtpd.pyi +++ b/mypy/typeshed/stdlib/smtpd.pyi @@ -4,13 +4,14 @@ import socket import sys from collections import defaultdict from typing import Any +from typing_extensions import TypeAlias if sys.version_info >= (3, 11): __all__ = ["SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy"] else: __all__ = ["SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy", "MailmanProxy"] -_Address = tuple[str, int] # (host, port) +_Address: TypeAlias = tuple[str, int] # (host, port) class SMTPChannel(asynchat.async_chat): COMMAND: int diff --git a/mypy/typeshed/stdlib/smtplib.pyi b/mypy/typeshed/stdlib/smtplib.pyi index 3136667dcd11..d90c744c504a 100644 --- a/mypy/typeshed/stdlib/smtplib.pyi +++ b/mypy/typeshed/stdlib/smtplib.pyi @@ -1,10 +1,12 @@ import sys from _typeshed import Self +from collections.abc import Sequence from email.message import Message as _Message from socket import socket from ssl import SSLContext from types import TracebackType -from typing import Any, Pattern, Protocol, Sequence, overload +from typing import Any, Pattern, Protocol, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 7): __all__ = [ @@ -40,10 +42,10 @@ else: "SMTP_SSL", ] -_Reply = tuple[int, bytes] -_SendErrs = dict[str, _Reply] +_Reply: TypeAlias = tuple[int, bytes] +_SendErrs: TypeAlias = dict[str, _Reply] # Should match source_address for socket.create_connection -_SourceAddress = tuple[bytearray | bytes | str, int] +_SourceAddress: TypeAlias = tuple[bytearray | bytes | str, int] SMTP_PORT: int SMTP_SSL_PORT: int diff --git a/mypy/typeshed/stdlib/socketserver.pyi b/mypy/typeshed/stdlib/socketserver.pyi index 9bdd8ccfe31f..20ff5daa718e 100644 --- a/mypy/typeshed/stdlib/socketserver.pyi +++ b/mypy/typeshed/stdlib/socketserver.pyi @@ -1,8 +1,10 @@ import sys import types from _typeshed import Self +from collections.abc import Callable from socket import socket as _socket -from typing import Any, BinaryIO, Callable, ClassVar, Union +from typing import Any, BinaryIO, ClassVar, Union +from typing_extensions import TypeAlias if sys.platform == "win32": __all__ = [ @@ -36,8 +38,8 @@ else: "ThreadingUnixDatagramServer", ] -_RequestType = Union[_socket, tuple[bytes, _socket]] -_AddressType = Union[tuple[str, int], str] +_RequestType: TypeAlias = Union[_socket, tuple[bytes, _socket]] +_AddressType: TypeAlias = Union[tuple[str, int], str] # This can possibly be generic at some point: class BaseServer: diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 05e5a176d8ff..87e843c5fb26 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -1,11 +1,23 @@ +import sqlite3 import sys -from _typeshed import Self, StrOrBytesPath +from _typeshed import ReadableBuffer, Self, StrOrBytesPath, SupportsLenAndGetItem +from collections.abc import Callable, Generator, Iterable, Iterator, Mapping from datetime import date, datetime, time from types import TracebackType -from typing import Any, Callable, Generator, Iterable, Iterator, Protocol, TypeVar -from typing_extensions import Literal, final +from typing import Any, Generic, Protocol, TypeVar, overload +from typing_extensions import Literal, SupportsIndex, TypeAlias, final _T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_CursorT = TypeVar("_CursorT", bound=Cursor) +_SqliteData: TypeAlias = str | ReadableBuffer | int | float | None +# Data that is passed through adapters can be of any type accepted by an adapter. +_AdaptedInputData: TypeAlias = _SqliteData | Any +# The Mapping must really be a dict, but making it invariant is too annoying. +_Parameters: TypeAlias = SupportsLenAndGetItem[_AdaptedInputData] | Mapping[str, _AdaptedInputData] +_SqliteOutputData: TypeAlias = str | bytes | int | float | None +_Adapter: TypeAlias = Callable[[_T], _SqliteData] +_Converter: TypeAlias = Callable[[bytes], Any] paramstyle: str threadsafety: int @@ -80,43 +92,144 @@ if sys.version_info >= (3, 7): SQLITE_SELECT: int SQLITE_TRANSACTION: int SQLITE_UPDATE: int -adapters: Any -converters: Any +adapters: dict[tuple[type[Any], type[Any]], _Adapter[Any]] +converters: dict[str, _Converter] sqlite_version: str version: str -# TODO: adapt needs to get probed -def adapt(obj, protocol, alternate): ... +if sys.version_info >= (3, 11): + SQLITE_ABORT: int + SQLITE_ABORT_ROLLBACK: int + SQLITE_AUTH: int + SQLITE_AUTH_USER: int + SQLITE_BUSY: int + SQLITE_BUSY_RECOVERY: int + SQLITE_BUSY_SNAPSHOT: int + SQLITE_BUSY_TIMEOUT: int + SQLITE_CANTOPEN: int + SQLITE_CANTOPEN_CONVPATH: int + SQLITE_CANTOPEN_DIRTYWAL: int + SQLITE_CANTOPEN_FULLPATH: int + SQLITE_CANTOPEN_ISDIR: int + SQLITE_CANTOPEN_NOTEMPDIR: int + SQLITE_CANTOPEN_SYMLINK: int + SQLITE_CONSTRAINT: int + SQLITE_CONSTRAINT_CHECK: int + SQLITE_CONSTRAINT_COMMITHOOK: int + SQLITE_CONSTRAINT_FOREIGNKEY: int + SQLITE_CONSTRAINT_FUNCTION: int + SQLITE_CONSTRAINT_NOTNULL: int + SQLITE_CONSTRAINT_PINNED: int + SQLITE_CONSTRAINT_PRIMARYKEY: int + SQLITE_CONSTRAINT_ROWID: int + SQLITE_CONSTRAINT_TRIGGER: int + SQLITE_CONSTRAINT_UNIQUE: int + SQLITE_CONSTRAINT_VTAB: int + SQLITE_CORRUPT: int + SQLITE_CORRUPT_INDEX: int + SQLITE_CORRUPT_SEQUENCE: int + SQLITE_CORRUPT_VTAB: int + SQLITE_EMPTY: int + SQLITE_ERROR: int + SQLITE_ERROR_MISSING_COLLSEQ: int + SQLITE_ERROR_RETRY: int + SQLITE_ERROR_SNAPSHOT: int + SQLITE_FORMAT: int + SQLITE_FULL: int + SQLITE_INTERNAL: int + SQLITE_INTERRUPT: int + SQLITE_IOERR: int + SQLITE_IOERR_ACCESS: int + SQLITE_IOERR_AUTH: int + SQLITE_IOERR_BEGIN_ATOMIC: int + SQLITE_IOERR_BLOCKED: int + SQLITE_IOERR_CHECKRESERVEDLOCK: int + SQLITE_IOERR_CLOSE: int + SQLITE_IOERR_COMMIT_ATOMIC: int + SQLITE_IOERR_CONVPATH: int + SQLITE_IOERR_CORRUPTFS: int + SQLITE_IOERR_DATA: int + SQLITE_IOERR_DELETE: int + SQLITE_IOERR_DELETE_NOENT: int + SQLITE_IOERR_DIR_CLOSE: int + SQLITE_IOERR_DIR_FSYNC: int + SQLITE_IOERR_FSTAT: int + SQLITE_IOERR_FSYNC: int + SQLITE_IOERR_GETTEMPPATH: int + SQLITE_IOERR_LOCK: int + SQLITE_IOERR_MMAP: int + SQLITE_IOERR_NOMEM: int + SQLITE_IOERR_RDLOCK: int + SQLITE_IOERR_READ: int + SQLITE_IOERR_ROLLBACK_ATOMIC: int + SQLITE_IOERR_SEEK: int + SQLITE_IOERR_SHMLOCK: int + SQLITE_IOERR_SHMMAP: int + SQLITE_IOERR_SHMOPEN: int + SQLITE_IOERR_SHMSIZE: int + SQLITE_IOERR_SHORT_READ: int + SQLITE_IOERR_TRUNCATE: int + SQLITE_IOERR_UNLOCK: int + SQLITE_IOERR_VNODE: int + SQLITE_IOERR_WRITE: int + SQLITE_LOCKED: int + SQLITE_LOCKED_SHAREDCACHE: int + SQLITE_LOCKED_VTAB: int + SQLITE_MISMATCH: int + SQLITE_MISUSE: int + SQLITE_NOLFS: int + SQLITE_NOMEM: int + SQLITE_NOTADB: int + SQLITE_NOTFOUND: int + SQLITE_NOTICE: int + SQLITE_NOTICE_RECOVER_ROLLBACK: int + SQLITE_NOTICE_RECOVER_WAL: int + SQLITE_OK_LOAD_PERMANENTLY: int + SQLITE_OK_SYMLINK: int + SQLITE_PERM: int + SQLITE_PROTOCOL: int + SQLITE_RANGE: int + SQLITE_READONLY: int + SQLITE_READONLY_CANTINIT: int + SQLITE_READONLY_CANTLOCK: int + SQLITE_READONLY_DBMOVED: int + SQLITE_READONLY_DIRECTORY: int + SQLITE_READONLY_RECOVERY: int + SQLITE_READONLY_ROLLBACK: int + SQLITE_ROW: int + SQLITE_SCHEMA: int + SQLITE_TOOBIG: int + SQLITE_WARNING: int + SQLITE_WARNING_AUTOINDEX: int + +# Can take or return anything depending on what's in the registry. +@overload +def adapt(__obj: Any, __proto: Any) -> Any: ... +@overload +def adapt(__obj: Any, __proto: Any, __alt: _T) -> Any | _T: ... def complete_statement(statement: str) -> bool: ... if sys.version_info >= (3, 7): - def connect( - database: StrOrBytesPath, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - ) -> Connection: ... - + _DatabaseArg: TypeAlias = StrOrBytesPath else: - def connect( - database: bytes | str, - timeout: float = ..., - detect_types: int = ..., - isolation_level: str | None = ..., - check_same_thread: bool = ..., - factory: type[Connection] | None = ..., - cached_statements: int = ..., - uri: bool = ..., - ) -> Connection: ... + _DatabaseArg: TypeAlias = bytes | str +def connect( + database: _DatabaseArg, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., +) -> Connection: ... def enable_callback_tracebacks(__enable: bool) -> None: ... + +# takes a pos-or-keyword argument because there is a C wrapper def enable_shared_cache(enable: int) -> None: ... -def register_adapter(__type: type[_T], __caster: Callable[[_T], int | float | str | bytes]) -> None: ... -def register_converter(__name: str, __converter: Callable[[bytes], Any]) -> None: ... +def register_adapter(__type: type[_T], __caster: _Adapter[_T]) -> None: ... +def register_converter(__name: str, __converter: _Converter) -> None: ... if sys.version_info < (3, 8): class Cache: @@ -125,40 +238,104 @@ if sys.version_info < (3, 8): def get(self, *args, **kwargs) -> None: ... class _AggregateProtocol(Protocol): - def step(self, value: int) -> None: ... + def step(self, __value: int) -> object: ... def finalize(self) -> int: ... +class _SingleParamWindowAggregateClass(Protocol): + def step(self, __param: Any) -> object: ... + def inverse(self, __param: Any) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +class _AnyParamWindowAggregateClass(Protocol): + def step(self, *args: Any) -> object: ... + def inverse(self, *args: Any) -> object: ... + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + +class _WindowAggregateClass(Protocol): + step: Callable[..., object] + inverse: Callable[..., object] + def value(self) -> _SqliteData: ... + def finalize(self) -> _SqliteData: ... + class Connection: - DataError: Any - DatabaseError: Any - Error: Any - IntegrityError: Any - InterfaceError: Any - InternalError: Any - NotSupportedError: Any - OperationalError: Any - ProgrammingError: Any - Warning: Any - in_transaction: Any - isolation_level: Any + @property + def DataError(self) -> type[sqlite3.DataError]: ... + @property + def DatabaseError(self) -> type[sqlite3.DatabaseError]: ... + @property + def Error(self) -> type[sqlite3.Error]: ... + @property + def IntegrityError(self) -> type[sqlite3.IntegrityError]: ... + @property + def InterfaceError(self) -> type[sqlite3.InterfaceError]: ... + @property + def InternalError(self) -> type[sqlite3.InternalError]: ... + @property + def NotSupportedError(self) -> type[sqlite3.NotSupportedError]: ... + @property + def OperationalError(self) -> type[sqlite3.OperationalError]: ... + @property + def ProgrammingError(self) -> type[sqlite3.ProgrammingError]: ... + @property + def Warning(self) -> type[sqlite3.Warning]: ... + @property + def in_transaction(self) -> bool: ... + isolation_level: str | None # one of '', 'DEFERRED', 'IMMEDIATE' or 'EXCLUSIVE' + @property + def total_changes(self) -> int: ... row_factory: Any text_factory: Any - total_changes: Any - def __init__(self, *args: Any, **kwargs: Any) -> None: ... + def __init__( + self, + database: _DatabaseArg, + timeout: float = ..., + detect_types: int = ..., + isolation_level: str | None = ..., + check_same_thread: bool = ..., + factory: type[Connection] | None = ..., + cached_statements: int = ..., + uri: bool = ..., + ) -> None: ... def close(self) -> None: ... + if sys.version_info >= (3, 11): + def blobopen(self, __table: str, __column: str, __row: int, *, readonly: bool = ..., name: str = ...) -> Blob: ... + def commit(self) -> None: ... def create_aggregate(self, name: str, n_arg: int, aggregate_class: Callable[[], _AggregateProtocol]) -> None: ... - def create_collation(self, __name: str, __callback: Any) -> None: ... + if sys.version_info >= (3, 11): + # num_params determines how many params will be passed to the aggregate class. We provide an overload + # for the case where num_params = 1, which is expected to be the common case. + @overload + def create_window_function( + self, __name: str, __num_params: Literal[1], __aggregate_class: Callable[[], _SingleParamWindowAggregateClass] | None + ) -> None: ... + # And for num_params = -1, which means the aggregate must accept any number of parameters. + @overload + def create_window_function( + self, __name: str, __num_params: Literal[-1], __aggregate_class: Callable[[], _AnyParamWindowAggregateClass] | None + ) -> None: ... + @overload + def create_window_function( + self, __name: str, __num_params: int, __aggregate_class: Callable[[], _WindowAggregateClass] | None + ) -> None: ... + + def create_collation(self, __name: str, __callback: Callable[[str, str], int | SupportsIndex] | None) -> None: ... if sys.version_info >= (3, 8): - def create_function(self, name: str, narg: int, func: Any, *, deterministic: bool = ...) -> None: ... + def create_function( + self, name: str, narg: int, func: Callable[..., _SqliteData], *, deterministic: bool = ... + ) -> None: ... else: - def create_function(self, name: str, num_params: int, func: Any) -> None: ... + def create_function(self, name: str, num_params: int, func: Callable[..., _SqliteData]) -> None: ... - def cursor(self, cursorClass: type | None = ...) -> Cursor: ... - def execute(self, sql: str, parameters: Iterable[Any] = ...) -> Cursor: ... - # TODO: please check in executemany() if seq_of_parameters type is possible like this - def executemany(self, __sql: str, __parameters: Iterable[Iterable[Any]]) -> Cursor: ... - def executescript(self, __sql_script: bytes | str) -> Cursor: ... + @overload + def cursor(self, cursorClass: None = ...) -> Cursor: ... + @overload + def cursor(self, cursorClass: Callable[[], _CursorT]) -> _CursorT: ... + def execute(self, sql: str, parameters: _Parameters = ...) -> Cursor: ... + def executemany(self, __sql: str, __parameters: Iterable[_Parameters]) -> Cursor: ... + def executescript(self, __sql_script: str) -> Cursor: ... def interrupt(self) -> None: ... def iterdump(self) -> Generator[str, None, None]: ... def rollback(self) -> None: ... @@ -169,8 +346,8 @@ class Connection: def set_trace_callback(self, trace_callback: Callable[[str], object] | None) -> None: ... # enable_load_extension and load_extension is not available on python distributions compiled # without sqlite3 loadable extension support. see footnotes https://docs.python.org/3/library/sqlite3.html#f1 - def enable_load_extension(self, enabled: bool) -> None: ... - def load_extension(self, path: str) -> None: ... + def enable_load_extension(self, __enabled: bool) -> None: ... + def load_extension(self, __name: str) -> None: ... if sys.version_info >= (3, 7): def backup( self, @@ -181,30 +358,38 @@ class Connection: name: str = ..., sleep: float = ..., ) -> None: ... + if sys.version_info >= (3, 11): + def setlimit(self, __category: int, __limit: int) -> int: ... + def getlimit(self, __category: int) -> int: ... + def serialize(self, *, name: str = ...) -> bytes: ... + def deserialize(self, __data: ReadableBuffer, *, name: str = ...) -> None: ... - def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + def __call__(self, __sql: str) -> _Statement: ... def __enter__(self: Self) -> Self: ... def __exit__( self, __type: type[BaseException] | None, __value: BaseException | None, __traceback: TracebackType | None ) -> Literal[False]: ... class Cursor(Iterator[Any]): - arraysize: Any - connection: Any - description: Any - lastrowid: Any - row_factory: Any - rowcount: int - # TODO: Cursor class accepts exactly 1 argument - # required type is sqlite3.Connection (which is imported as _Connection) - # however, the name of the __init__ variable is unknown - def __init__(self, *args: Any, **kwargs: Any) -> None: ... + arraysize: int + @property + def connection(self) -> Connection: ... + @property + def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | None: ... + @property + def lastrowid(self) -> int | None: ... + row_factory: Callable[[Cursor, Row[Any]], object] | None + @property + def rowcount(self) -> int: ... + def __init__(self, __cursor: Connection) -> None: ... def close(self) -> None: ... - def execute(self, __sql: str, __parameters: Iterable[Any] = ...) -> Cursor: ... - def executemany(self, __sql: str, __seq_of_parameters: Iterable[Iterable[Any]]) -> Cursor: ... - def executescript(self, __sql_script: bytes | str) -> Cursor: ... + def execute(self: Self, __sql: str, __parameters: _Parameters = ...) -> Self: ... + def executemany(self: Self, __sql: str, __seq_of_parameters: Iterable[_Parameters]) -> Self: ... + def executescript(self, __sql_script: str) -> Cursor: ... def fetchall(self) -> list[Any]: ... def fetchmany(self, size: int | None = ...) -> list[Any]: ... + # Returns either a row (as created by the row_factory) or None, but + # putting None in the return annotation causes annoying false positives. def fetchone(self) -> Any: ... def setinputsizes(self, __sizes: object) -> None: ... # does nothing def setoutputsize(self, __size: object, __column: object = ...) -> None: ... # does nothing @@ -225,31 +410,53 @@ class InternalError(DatabaseError): ... class NotSupportedError(DatabaseError): ... class OperationalError(DatabaseError): ... -OptimizedUnicode = str +if sys.version_info < (3, 10): + OptimizedUnicode = str @final class PrepareProtocol: - def __init__(self, *args: Any, **kwargs: Any) -> None: ... + def __init__(self, *args: object, **kwargs: object) -> None: ... class ProgrammingError(DatabaseError): ... -class Row: - def __init__(self, *args: Any, **kwargs: Any) -> None: ... - def keys(self): ... - def __eq__(self, __other): ... - def __ge__(self, __other): ... - def __getitem__(self, __index): ... - def __gt__(self, __other): ... - def __hash__(self): ... - def __iter__(self): ... - def __le__(self, __other): ... - def __len__(self): ... - def __lt__(self, __other): ... - def __ne__(self, __other): ... +class Row(Generic[_T_co]): + def __init__(self, __cursor: Cursor, __data: tuple[_T_co, ...]) -> None: ... + def keys(self) -> list[str]: ... + @overload + def __getitem__(self, __index: int | str) -> _T_co: ... + @overload + def __getitem__(self, __index: slice) -> tuple[_T_co, ...]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def __len__(self) -> int: ... + # These return NotImplemented for anything that is not a Row. + def __eq__(self, __other: object) -> bool: ... + def __ge__(self, __other: object) -> bool: ... + def __gt__(self, __other: object) -> bool: ... + def __le__(self, __other: object) -> bool: ... + def __lt__(self, __other: object) -> bool: ... + def __ne__(self, __other: object) -> bool: ... -if sys.version_info < (3, 8): +if sys.version_info >= (3, 8): + @final + class _Statement: ... + +else: @final class Statement: def __init__(self, *args, **kwargs): ... + _Statement: TypeAlias = Statement class Warning(Exception): ... + +if sys.version_info >= (3, 11): + class Blob: + def close(self) -> None: ... + def read(self, __length: int = ...) -> bytes: ... + def write(self, __data: bytes) -> None: ... + def tell(self) -> int: ... + # whence must be one of os.SEEK_SET, os.SEEK_CUR, os.SEEK_END + def seek(self, __offset: int, __whence: int = ...) -> None: ... + def __len__(self) -> int: ... + def __enter__(self: Self) -> Self: ... + def __exit__(self, __typ: object, __val: object, __tb: object) -> Literal[False]: ... diff --git a/mypy/typeshed/stdlib/sre_parse.pyi b/mypy/typeshed/stdlib/sre_parse.pyi index 05e71c255967..0958e73f5176 100644 --- a/mypy/typeshed/stdlib/sre_parse.pyi +++ b/mypy/typeshed/stdlib/sre_parse.pyi @@ -1,7 +1,9 @@ import sys +from collections.abc import Iterable from sre_constants import * from sre_constants import _NamedIntConstant as _NIC, error as _Error -from typing import Any, Iterable, Match, Pattern as _Pattern, overload +from typing import Any, Match, Pattern as _Pattern, overload +from typing_extensions import TypeAlias SPECIAL_CHARS: str REPEAT_CHARS: str @@ -33,16 +35,16 @@ class _State: def checklookbehindgroup(self, gid: int, source: Tokenizer) -> None: ... if sys.version_info >= (3, 8): - State = _State + State: TypeAlias = _State else: - Pattern = _State + Pattern: TypeAlias = _State -_OpSubpatternType = tuple[int | None, int, int, SubPattern] -_OpGroupRefExistsType = tuple[int, SubPattern, SubPattern] -_OpInType = list[tuple[_NIC, int]] -_OpBranchType = tuple[None, list[SubPattern]] -_AvType = _OpInType | _OpBranchType | Iterable[SubPattern] | _OpGroupRefExistsType | _OpSubpatternType -_CodeType = tuple[_NIC, _AvType] +_OpSubpatternType: TypeAlias = tuple[int | None, int, int, SubPattern] +_OpGroupRefExistsType: TypeAlias = tuple[int, SubPattern, SubPattern] +_OpInType: TypeAlias = list[tuple[_NIC, int]] +_OpBranchType: TypeAlias = tuple[None, list[SubPattern]] +_AvType: TypeAlias = _OpInType | _OpBranchType | Iterable[SubPattern] | _OpGroupRefExistsType | _OpSubpatternType +_CodeType: TypeAlias = tuple[_NIC, _AvType] class SubPattern: data: list[_CodeType] @@ -87,8 +89,8 @@ class Tokenizer: def fix_flags(src: str | bytes, flags: int) -> int: ... -_TemplateType = tuple[list[tuple[int, int]], list[str | None]] -_TemplateByteType = tuple[list[tuple[int, int]], list[bytes | None]] +_TemplateType: TypeAlias = tuple[list[tuple[int, int]], list[str | None]] +_TemplateByteType: TypeAlias = tuple[list[tuple[int, int]], list[bytes | None]] if sys.version_info >= (3, 8): def parse(str: str, flags: int = ..., state: State | None = ...) -> SubPattern: ... @overload diff --git a/mypy/typeshed/stdlib/ssl.pyi b/mypy/typeshed/stdlib/ssl.pyi index b7fe6914db0e..8445435fa346 100644 --- a/mypy/typeshed/stdlib/ssl.pyi +++ b/mypy/typeshed/stdlib/ssl.pyi @@ -2,17 +2,18 @@ import enum import socket import sys from _typeshed import ReadableBuffer, Self, StrOrBytesPath, WriteableBuffer -from typing import Any, Callable, Iterable, NamedTuple, Union, overload -from typing_extensions import Literal, TypedDict, final - -_PCTRTT = tuple[tuple[str, str], ...] -_PCTRTTT = tuple[_PCTRTT, ...] -_PeerCertRetDictType = dict[str, str | _PCTRTTT | _PCTRTT] -_PeerCertRetType = _PeerCertRetDictType | bytes | None -_EnumRetType = list[tuple[bytes, str, set[str] | bool]] -_PasswordType = Union[Callable[[], str | bytes], str, bytes] - -_SrvnmeCbType = Callable[[SSLSocket | SSLObject, str | None, SSLSocket], int | None] +from collections.abc import Callable, Iterable +from typing import Any, NamedTuple, Union, overload +from typing_extensions import Literal, TypeAlias, TypedDict, final + +_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] +_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] +_PeerCertRetDictType: TypeAlias = dict[str, str | _PCTRTTT | _PCTRTT] +_PeerCertRetType: TypeAlias = _PeerCertRetDictType | bytes | None +_EnumRetType: TypeAlias = list[tuple[bytes, str, set[str] | bool]] +_PasswordType: TypeAlias = Union[Callable[[], str | bytes], str, bytes] + +_SrvnmeCbType: TypeAlias = Callable[[SSLSocket | SSLObject, str | None, SSLSocket], int | None] class _Cipher(TypedDict): aead: bool diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index 446a778794f1..540ccfcaaa8c 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import Self, SupportsRichComparisonT +from collections.abc import Hashable, Iterable, Sequence from decimal import Decimal from fractions import Fraction -from typing import Any, Hashable, Iterable, NamedTuple, Sequence, SupportsFloat, TypeVar -from typing_extensions import Literal +from typing import Any, NamedTuple, SupportsFloat, TypeVar +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 10): __all__ = [ @@ -65,7 +66,7 @@ else: ] # Most functions in this module accept homogeneous collections of one of these types -_Number = float | Decimal | Fraction +_Number: TypeAlias = float | Decimal | Fraction _NumberT = TypeVar("_NumberT", float, Decimal, Fraction) # Used in mode, multimode diff --git a/mypy/typeshed/stdlib/string.pyi b/mypy/typeshed/stdlib/string.pyi index 535f38545132..4404bde8bc4f 100644 --- a/mypy/typeshed/stdlib/string.pyi +++ b/mypy/typeshed/stdlib/string.pyi @@ -1,6 +1,7 @@ import sys +from collections.abc import Iterable, Mapping, Sequence from re import RegexFlag -from typing import Any, Iterable, Mapping, Sequence +from typing import Any if sys.version_info >= (3, 8): from re import Pattern diff --git a/mypy/typeshed/stdlib/struct.pyi b/mypy/typeshed/stdlib/struct.pyi index 1f6c45a23c0a..59c66ad2f167 100644 --- a/mypy/typeshed/stdlib/struct.pyi +++ b/mypy/typeshed/stdlib/struct.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import ReadableBuffer, WriteableBuffer -from typing import Any, Iterator +from collections.abc import Iterator +from typing import Any __all__ = ["calcsize", "pack", "pack_into", "unpack", "unpack_from", "iter_unpack", "Struct", "error"] diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index ced2e708f8ff..98bbf7d33f90 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import Self, StrOrBytesPath +from collections.abc import Callable, Iterable, Mapping, Sequence from types import TracebackType -from typing import IO, Any, AnyStr, Callable, Generic, Iterable, Mapping, Sequence, TypeVar, overload -from typing_extensions import Literal +from typing import IO, Any, AnyStr, Generic, TypeVar, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -102,18 +103,18 @@ else: # reveal_type(x) # bytes, based on the overloads # except TimeoutError as e: # reveal_type(e.cmd) # Any, but morally is _CMD -_FILE = None | int | IO[Any] -_TXT = bytes | str +_FILE: TypeAlias = None | int | IO[Any] +_TXT: TypeAlias = bytes | str if sys.version_info >= (3, 8): - _CMD = StrOrBytesPath | Sequence[StrOrBytesPath] + _CMD: TypeAlias = StrOrBytesPath | Sequence[StrOrBytesPath] else: # Python 3.6 doesn't support _CMD being a single PathLike. # See: https://bugs.python.org/issue31961 - _CMD = _TXT | Sequence[StrOrBytesPath] + _CMD: TypeAlias = _TXT | Sequence[StrOrBytesPath] if sys.platform == "win32": - _ENV = Mapping[str, str] + _ENV: TypeAlias = Mapping[str, str] else: - _ENV = Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] + _ENV: TypeAlias = Mapping[bytes, StrOrBytesPath] | Mapping[str, StrOrBytesPath] _T = TypeVar("_T") diff --git a/mypy/typeshed/stdlib/sunau.pyi b/mypy/typeshed/stdlib/sunau.pyi index 73aa8999caa1..5b21cb03d4a3 100644 --- a/mypy/typeshed/stdlib/sunau.pyi +++ b/mypy/typeshed/stdlib/sunau.pyi @@ -1,9 +1,9 @@ import sys from _typeshed import Self from typing import IO, Any, NamedTuple, NoReturn, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias -_File = str | IO[bytes] +_File: TypeAlias = str | IO[bytes] class Error(Exception): ... diff --git a/mypy/typeshed/stdlib/symtable.pyi b/mypy/typeshed/stdlib/symtable.pyi index 91e95270901e..7a95fe5e445f 100644 --- a/mypy/typeshed/stdlib/symtable.pyi +++ b/mypy/typeshed/stdlib/symtable.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"] diff --git a/mypy/typeshed/stdlib/sys.pyi b/mypy/typeshed/stdlib/sys.pyi index 4fca35a2c82d..92b806e04420 100644 --- a/mypy/typeshed/stdlib/sys.pyi +++ b/mypy/typeshed/stdlib/sys.pyi @@ -1,18 +1,17 @@ import sys -from _typeshed import structseq +from _typeshed import OptExcInfo, structseq from builtins import object as _object +from collections.abc import AsyncGenerator, Callable, Coroutine, Sequence from importlib.abc import PathEntryFinder from importlib.machinery import ModuleSpec from io import TextIOWrapper from types import FrameType, ModuleType, TracebackType -from typing import Any, AsyncGenerator, Callable, Coroutine, NoReturn, Protocol, Sequence, TextIO, TypeVar, Union, overload -from typing_extensions import Literal, final +from typing import Any, NoReturn, Protocol, TextIO, TypeVar, overload +from typing_extensions import Literal, TypeAlias, final _T = TypeVar("_T") -# The following type alias are stub-only and do not exist during runtime -_ExcInfo = tuple[type[BaseException], BaseException, TracebackType] -_OptExcInfo = Union[_ExcInfo, tuple[None, None, None]] +_OptExcInfo: TypeAlias = OptExcInfo # TODO: obsolete, remove fall 2022 or later # Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder` class _MetaPathFinder(Protocol): @@ -76,16 +75,16 @@ _xoptions: dict[Any, Any] # Type alias used as a mixin for structseq classes that cannot be instantiated at runtime # This can't be represented in the type system, so we just use `structseq[Any]` -_uninstantiable_structseq = structseq[Any] +_uninstantiable_structseq: TypeAlias = structseq[Any] flags: _flags if sys.version_info >= (3, 10): - _FlagTuple = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int, int] + _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int, int] elif sys.version_info >= (3, 7): - _FlagTuple = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int] + _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, bool, int] else: - _FlagTuple = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int] + _FlagTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int, int, int, int, int] @final class _flags(_uninstantiable_structseq, _FlagTuple): @@ -217,7 +216,7 @@ def _getframe(__depth: int = ...) -> FrameType: ... def _debugmallocstats() -> None: ... def __displayhook__(__value: object) -> None: ... def __excepthook__(__exctype: type[BaseException], __value: BaseException, __traceback: TracebackType | None) -> None: ... -def exc_info() -> _OptExcInfo: ... +def exc_info() -> OptExcInfo: ... # sys.exit() accepts an optional argument of anything printable def exit(__status: object = ...) -> NoReturn: ... @@ -237,12 +236,12 @@ def getsizeof(obj: object) -> int: ... def getsizeof(obj: object, default: int) -> int: ... def getswitchinterval() -> float: ... -_ProfileFunc = Callable[[FrameType, str, Any], Any] +_ProfileFunc: TypeAlias = Callable[[FrameType, str, Any], Any] def getprofile() -> _ProfileFunc | None: ... def setprofile(profilefunc: _ProfileFunc | None) -> None: ... -_TraceFunc = Callable[[FrameType, str, Any], Callable[[FrameType, str, Any], Any] | None] +_TraceFunc: TypeAlias = Callable[[FrameType, str, Any], Callable[[FrameType, str, Any], Any] | None] def gettrace() -> _TraceFunc | None: ... def settrace(tracefunc: _TraceFunc | None) -> None: ... @@ -309,7 +308,7 @@ if sys.version_info >= (3, 8): def addaudithook(hook: Callable[[str, tuple[Any, ...]], Any]) -> None: ... def audit(__event: str, *args: Any) -> None: ... -_AsyncgenHook = Callable[[AsyncGenerator[Any, Any]], None] | None +_AsyncgenHook: TypeAlias = Callable[[AsyncGenerator[Any, Any]], None] | None @final class _asyncgen_hooks(structseq[_AsyncgenHook], tuple[_AsyncgenHook, _AsyncgenHook]): @@ -330,6 +329,6 @@ if sys.version_info >= (3, 7): def set_coroutine_origin_tracking_depth(depth: int) -> None: ... if sys.version_info < (3, 8): - _CoroWrapper = Callable[[Coroutine[Any, Any, Any]], Any] + _CoroWrapper: TypeAlias = Callable[[Coroutine[Any, Any, Any]], Any] def set_coroutine_wrapper(__wrapper: _CoroWrapper) -> None: ... def get_coroutine_wrapper() -> _CoroWrapper: ... diff --git a/mypy/typeshed/stdlib/tabnanny.pyi b/mypy/typeshed/stdlib/tabnanny.pyi index 020100031c14..8a8592f44124 100644 --- a/mypy/typeshed/stdlib/tabnanny.pyi +++ b/mypy/typeshed/stdlib/tabnanny.pyi @@ -1,5 +1,5 @@ from _typeshed import StrOrBytesPath -from typing import Iterable +from collections.abc import Iterable __all__ = ["check", "NannyNag", "process_tokens"] diff --git a/mypy/typeshed/stdlib/tarfile.pyi b/mypy/typeshed/stdlib/tarfile.pyi index 2837c46e1d21..364bcad0683f 100644 --- a/mypy/typeshed/stdlib/tarfile.pyi +++ b/mypy/typeshed/stdlib/tarfile.pyi @@ -2,7 +2,7 @@ import bz2 import io import sys from _typeshed import Self, StrOrBytesPath, StrPath -from builtins import type as Type # alias to avoid name clashes with fields named "type" +from builtins import list as _list, type as Type # aliases to avoid name clashes with fields named "type" or "list" from collections.abc import Callable, Iterable, Iterator, Mapping from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj from types import TracebackType @@ -109,8 +109,6 @@ def open( class ExFileObject(io.BufferedReader): def __init__(self, tarfile: TarFile, tarinfo: TarInfo) -> None: ... -_list = list # conflicts with method name - class TarFile: OPEN_METH: Mapping[str, str] name: StrOrBytesPath | None diff --git a/mypy/typeshed/stdlib/telnetlib.pyi b/mypy/typeshed/stdlib/telnetlib.pyi index 359be6e49781..8edbd155f61c 100644 --- a/mypy/typeshed/stdlib/telnetlib.pyi +++ b/mypy/typeshed/stdlib/telnetlib.pyi @@ -1,7 +1,8 @@ import socket from _typeshed import Self +from collections.abc import Callable, Sequence from types import TracebackType -from typing import Any, Callable, Match, Pattern, Sequence +from typing import Any, Match, Pattern __all__ = ["Telnet"] diff --git a/mypy/typeshed/stdlib/tempfile.pyi b/mypy/typeshed/stdlib/tempfile.pyi index 19a4dbee2ee7..4f2b4a545ff7 100644 --- a/mypy/typeshed/stdlib/tempfile.pyi +++ b/mypy/typeshed/stdlib/tempfile.pyi @@ -1,9 +1,10 @@ import os import sys from _typeshed import Self +from collections.abc import Iterable, Iterator from types import TracebackType -from typing import IO, Any, AnyStr, Generic, Iterable, Iterator, overload -from typing_extensions import Literal +from typing import IO, Any, AnyStr, Generic, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -29,7 +30,7 @@ TMP_MAX: int tempdir: str | None template: str -_DirT = AnyStr | os.PathLike[AnyStr] +_DirT: TypeAlias = AnyStr | os.PathLike[AnyStr] if sys.version_info >= (3, 8): @overload diff --git a/mypy/typeshed/stdlib/termios.pyi b/mypy/typeshed/stdlib/termios.pyi index c6a90df31b59..b2423304b930 100644 --- a/mypy/typeshed/stdlib/termios.pyi +++ b/mypy/typeshed/stdlib/termios.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import FileDescriptorLike from typing import Any +from typing_extensions import TypeAlias if sys.platform != "win32": - _Attr = list[int | list[bytes | int]] + _Attr: TypeAlias = list[int | list[bytes | int]] # TODO constants not really documented B0: int diff --git a/mypy/typeshed/stdlib/textwrap.pyi b/mypy/typeshed/stdlib/textwrap.pyi index aeb1d87141be..5a61dfedb380 100644 --- a/mypy/typeshed/stdlib/textwrap.pyi +++ b/mypy/typeshed/stdlib/textwrap.pyi @@ -1,4 +1,5 @@ -from typing import Callable, Pattern +from collections.abc import Callable +from typing import Pattern __all__ = ["TextWrapper", "wrap", "fill", "dedent", "indent", "shorten"] diff --git a/mypy/typeshed/stdlib/threading.pyi b/mypy/typeshed/stdlib/threading.pyi index c3fa57fafa7c..231018ca731a 100644 --- a/mypy/typeshed/stdlib/threading.pyi +++ b/mypy/typeshed/stdlib/threading.pyi @@ -1,11 +1,13 @@ import sys +from collections.abc import Callable, Iterable, Mapping from types import FrameType, TracebackType -from typing import Any, Callable, Iterable, Mapping, TypeVar +from typing import Any, TypeVar +from typing_extensions import TypeAlias # TODO recursive type -_TF = Callable[[FrameType, str, Any], Callable[..., Any] | None] +_TF: TypeAlias = Callable[[FrameType, str, Any], Callable[..., Any] | None] -_PF = Callable[[FrameType, str, Any], None] +_PF: TypeAlias = Callable[[FrameType, str, Any], None] _T = TypeVar("_T") if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/time.pyi b/mypy/typeshed/stdlib/time.pyi index 25f8d7056cd8..cceb7c8ca874 100644 --- a/mypy/typeshed/stdlib/time.pyi +++ b/mypy/typeshed/stdlib/time.pyi @@ -1,9 +1,9 @@ import sys from _typeshed import structseq from typing import Any, Protocol -from typing_extensions import Final, Literal, final +from typing_extensions import Final, Literal, TypeAlias, final -_TimeTuple = tuple[int, int, int, int, int, int, int, int, int] +_TimeTuple: TypeAlias = tuple[int, int, int, int, int, int, int, int, int] altzone: int daylight: int diff --git a/mypy/typeshed/stdlib/timeit.pyi b/mypy/typeshed/stdlib/timeit.pyi index bfaea728ff42..076b2c54f991 100644 --- a/mypy/typeshed/stdlib/timeit.pyi +++ b/mypy/typeshed/stdlib/timeit.pyi @@ -1,9 +1,11 @@ -from typing import IO, Any, Callable, Sequence +from collections.abc import Callable, Sequence +from typing import IO, Any +from typing_extensions import TypeAlias __all__ = ["Timer", "timeit", "repeat", "default_timer"] -_Timer = Callable[[], float] -_Stmt = str | Callable[[], Any] +_Timer: TypeAlias = Callable[[], float] +_Stmt: TypeAlias = str | Callable[[], Any] default_timer: _Timer diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 2a6172ba46a3..66c52107067d 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -1,12 +1,13 @@ import _tkinter import sys from _typeshed import StrOrBytesPath +from collections.abc import Callable, Mapping, Sequence from enum import Enum from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Callable, Generic, Mapping, Protocol, Sequence, TypeVar, Union, overload -from typing_extensions import Literal, TypedDict +from typing import Any, Generic, Protocol, TypeVar, Union, overload +from typing_extensions import Literal, TypeAlias, TypedDict if sys.version_info >= (3, 9): __all__ = [ @@ -171,29 +172,31 @@ EXCEPTION = _tkinter.EXCEPTION # Some widgets have an option named -compound that accepts different values # than the _Compound defined here. Many other options have similar things. -_Anchor = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] # manual page: Tk_GetAnchor -_Bitmap = str # manual page: Tk_GetBitmap -_ButtonCommand = str | Callable[[], Any] # accepts string of tcl code, return value is returned from Button.invoke() -_CanvasItemId = int -_Color = str # typically '#rrggbb', '#rgb' or color names. -_Compound = Literal["top", "left", "center", "right", "bottom", "none"] # -compound in manual page named 'options' -_Cursor = Union[str, tuple[str], tuple[str, str], tuple[str, str, str], tuple[str, str, str, str]] # manual page: Tk_GetCursor -_EntryValidateCommand = ( +_Anchor: TypeAlias = Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] # manual page: Tk_GetAnchor +_Bitmap: TypeAlias = str # manual page: Tk_GetBitmap +_ButtonCommand: TypeAlias = str | Callable[[], Any] # accepts string of tcl code, return value is returned from Button.invoke() +_CanvasItemId: TypeAlias = int +_Color: TypeAlias = str # typically '#rrggbb', '#rgb' or color names. +_Compound: TypeAlias = Literal["top", "left", "center", "right", "bottom", "none"] # -compound in manual page named 'options' +_Cursor: TypeAlias = Union[ + str, tuple[str], tuple[str, str], tuple[str, str, str], tuple[str, str, str, str] +] # manual page: Tk_GetCursor +_EntryValidateCommand: TypeAlias = ( str | list[str] | tuple[str, ...] | Callable[[], bool] ) # example when it's sequence: entry['invalidcommand'] = [entry.register(print), '%P'] -_GridIndex = int | str | Literal["all"] -_ImageSpec = _Image | str # str can be from e.g. tkinter.image_names() -_Padding = Union[ +_GridIndex: TypeAlias = int | str | Literal["all"] +_ImageSpec: TypeAlias = _Image | str # str can be from e.g. tkinter.image_names() +_Padding: TypeAlias = Union[ _ScreenUnits, tuple[_ScreenUnits], tuple[_ScreenUnits, _ScreenUnits], tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits], tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits], ] -_Relief = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] # manual page: Tk_GetRelief -_ScreenUnits = str | float # Often the right type instead of int. Manual page: Tk_GetPixels -_XYScrollCommand = str | Callable[[float, float], Any] # -xscrollcommand and -yscrollcommand in 'options' manual page -_TakeFocusValue = Union[int, Literal[""], Callable[[str], bool | None]] # -takefocus in manual page named 'options' +_Relief: TypeAlias = Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] # manual page: Tk_GetRelief +_ScreenUnits: TypeAlias = str | float # Often the right type instead of int. Manual page: Tk_GetPixels +_XYScrollCommand: TypeAlias = str | Callable[[float, float], Any] # -xscrollcommand and -yscrollcommand in 'options' manual page +_TakeFocusValue: TypeAlias = Union[int, Literal[""], Callable[[str], bool | None]] # -takefocus in manual page named 'options' class EventType(str, Enum): Activate: str @@ -263,7 +266,7 @@ class Event(Generic[_W_co]): def NoDefaultRoot() -> None: ... -_TraceMode = Literal["array", "read", "write", "unset"] +_TraceMode: TypeAlias = Literal["array", "read", "write", "unset"] class Variable: def __init__(self, master: Misc | None = ..., value: Any | None = ..., name: str | None = ...) -> None: ... @@ -1696,7 +1699,7 @@ class Checkbutton(Widget): def select(self) -> None: ... def toggle(self) -> None: ... -_EntryIndex = str | int # "INDICES" in manual page +_EntryIndex: TypeAlias = str | int # "INDICES" in manual page class Entry(Widget, XView): def __init__( @@ -2054,7 +2057,7 @@ class Listbox(Widget, XView, YView): def itemconfigure(self, index, cnf: Any | None = ..., **kw): ... itemconfig: Any -_MenuIndex = str | int +_MenuIndex: TypeAlias = str | int class Menu(Widget): def __init__( @@ -2740,7 +2743,7 @@ class Scrollbar(Widget): def get(self) -> tuple[float, float, float, float] | tuple[float, float]: ... def set(self, first: float, last: float) -> None: ... -_TextIndex = _tkinter.Tcl_Obj | str | float | Misc +_TextIndex: TypeAlias = _tkinter.Tcl_Obj | str | float | Misc class Text(Widget, XView, YView): def __init__( diff --git a/mypy/typeshed/stdlib/tkinter/commondialog.pyi b/mypy/typeshed/stdlib/tkinter/commondialog.pyi index 1c5fb0f53706..faebcc33955e 100644 --- a/mypy/typeshed/stdlib/tkinter/commondialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/commondialog.pyi @@ -1,5 +1,6 @@ import sys -from typing import Any, ClassVar, Mapping +from collections.abc import Mapping +from typing import Any, ClassVar if sys.version_info >= (3, 9): __all__ = ["Dialog"] diff --git a/mypy/typeshed/stdlib/tkinter/dialog.pyi b/mypy/typeshed/stdlib/tkinter/dialog.pyi index f9c8487c44a9..9ced7a208808 100644 --- a/mypy/typeshed/stdlib/tkinter/dialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/dialog.pyi @@ -1,6 +1,7 @@ import sys +from collections.abc import Mapping from tkinter import Widget -from typing import Any, Mapping +from typing import Any if sys.version_info >= (3, 9): __all__ = ["Dialog"] diff --git a/mypy/typeshed/stdlib/tkinter/filedialog.pyi b/mypy/typeshed/stdlib/tkinter/filedialog.pyi index dc0e01a6d1d1..2815289e81c3 100644 --- a/mypy/typeshed/stdlib/tkinter/filedialog.pyi +++ b/mypy/typeshed/stdlib/tkinter/filedialog.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import StrOrBytesPath +from collections.abc import Iterable from tkinter import Button, Entry, Frame, Listbox, Misc, Scrollbar, StringVar, Toplevel, commondialog -from typing import IO, Any, ClassVar, Iterable +from typing import IO, Any, ClassVar from typing_extensions import Literal if sys.version_info >= (3, 9): diff --git a/mypy/typeshed/stdlib/tkinter/font.pyi b/mypy/typeshed/stdlib/tkinter/font.pyi index 4b0101b32788..dff84e9fac78 100644 --- a/mypy/typeshed/stdlib/tkinter/font.pyi +++ b/mypy/typeshed/stdlib/tkinter/font.pyi @@ -2,7 +2,7 @@ import _tkinter import sys import tkinter from typing import Any, overload -from typing_extensions import Literal, TypedDict +from typing_extensions import Literal, TypeAlias, TypedDict if sys.version_info >= (3, 9): __all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", "nametofont", "Font", "families", "names"] @@ -12,7 +12,7 @@ ROMAN: Literal["roman"] BOLD: Literal["bold"] ITALIC: Literal["italic"] -_FontDescription = ( +_FontDescription: TypeAlias = ( str # "Helvetica 12" | Font # A font object constructed in Python | list[Any] # ("Helvetica", 12, BOLD) diff --git a/mypy/typeshed/stdlib/tkinter/ttk.pyi b/mypy/typeshed/stdlib/tkinter/ttk.pyi index c48b5cd7aa0d..7ca8f9b800ce 100644 --- a/mypy/typeshed/stdlib/tkinter/ttk.pyi +++ b/mypy/typeshed/stdlib/tkinter/ttk.pyi @@ -1,9 +1,10 @@ import _tkinter import sys import tkinter +from collections.abc import Callable from tkinter.font import _FontDescription -from typing import Any, Callable, overload -from typing_extensions import Literal, TypedDict +from typing import Any, overload +from typing_extensions import Literal, TypeAlias, TypedDict if sys.version_info >= (3, 7): __all__ = [ @@ -65,7 +66,7 @@ def tclobjs_to_py(adict: dict[Any, Any]) -> dict[Any, Any]: ... def setup_master(master: Any | None = ...): ... # from ttk_widget (aka ttk::widget) manual page, differs from tkinter._Compound -_TtkCompound = Literal["text", "image", tkinter._Compound] +_TtkCompound: TypeAlias = Literal["text", "image", tkinter._Compound] class Style: master: Any @@ -972,7 +973,7 @@ class _TreeviewColumnDict(TypedDict): anchor: tkinter._Anchor id: str -_TreeviewColumnId = int | str # manual page: "COLUMN IDENTIFIERS" +_TreeviewColumnId: TypeAlias = int | str # manual page: "COLUMN IDENTIFIERS" class Treeview(Widget, tkinter.XView, tkinter.YView): def __init__( diff --git a/mypy/typeshed/stdlib/tokenize.pyi b/mypy/typeshed/stdlib/tokenize.pyi index 7b17e8de6153..dea83263b550 100644 --- a/mypy/typeshed/stdlib/tokenize.pyi +++ b/mypy/typeshed/stdlib/tokenize.pyi @@ -1,8 +1,10 @@ import sys from _typeshed import StrOrBytesPath from builtins import open as _builtin_open +from collections.abc import Callable, Generator, Iterable, Sequence from token import * -from typing import Any, Callable, Generator, Iterable, NamedTuple, Pattern, Sequence, TextIO +from typing import Any, NamedTuple, Pattern, TextIO +from typing_extensions import TypeAlias if sys.version_info >= (3, 10): __all__ = [ @@ -317,7 +319,7 @@ if sys.version_info < (3, 7): cookie_re: Pattern[str] blank_re: Pattern[bytes] -_Position = tuple[int, int] +_Position: TypeAlias = tuple[int, int] class _TokenInfo(NamedTuple): type: int @@ -331,7 +333,7 @@ class TokenInfo(_TokenInfo): def exact_type(self) -> int: ... # Backwards compatible tokens can be sequences of a shorter length too -_Token = TokenInfo | Sequence[int | str | _Position] +_Token: TypeAlias = TokenInfo | Sequence[int | str | _Position] class TokenError(Exception): ... class StopTokenizing(Exception): ... # undocumented diff --git a/mypy/typeshed/stdlib/trace.pyi b/mypy/typeshed/stdlib/trace.pyi index 6128064478bc..3640cb11a878 100644 --- a/mypy/typeshed/stdlib/trace.pyi +++ b/mypy/typeshed/stdlib/trace.pyi @@ -1,15 +1,16 @@ import sys import types from _typeshed import StrPath -from typing import Any, Callable, Mapping, Sequence, TypeVar -from typing_extensions import ParamSpec +from collections.abc import Callable, Mapping, Sequence +from typing import Any, TypeVar +from typing_extensions import ParamSpec, TypeAlias __all__ = ["Trace", "CoverageResults"] _T = TypeVar("_T") _P = ParamSpec("_P") -_localtrace = Callable[[types.FrameType, str, Any], Callable[..., Any]] -_fileModuleFunction = tuple[str, str | None, str] +_localtrace: TypeAlias = Callable[[types.FrameType, str, Any], Callable[..., Any]] +_fileModuleFunction: TypeAlias = tuple[str, str | None, str] class CoverageResults: def __init__( diff --git a/mypy/typeshed/stdlib/traceback.pyi b/mypy/typeshed/stdlib/traceback.pyi index 859bfef64622..5c4d323a2d9f 100644 --- a/mypy/typeshed/stdlib/traceback.pyi +++ b/mypy/typeshed/stdlib/traceback.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import Self, SupportsWrite +from collections.abc import Generator, Iterable, Iterator, Mapping from types import FrameType, TracebackType -from typing import IO, Any, Generator, Iterable, Iterator, Mapping, overload -from typing_extensions import Literal +from typing import IO, Any, overload +from typing_extensions import Literal, TypeAlias __all__ = [ "extract_stack", @@ -26,7 +27,7 @@ __all__ = [ "walk_tb", ] -_PT = tuple[str, int, str, str | None] +_PT: TypeAlias = tuple[str, int, str, str | None] def print_tb(tb: TracebackType | None, limit: int | None = ..., file: IO[str] | None = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/tracemalloc.pyi b/mypy/typeshed/stdlib/tracemalloc.pyi index e2e6800cbab4..193a4acc7c2d 100644 --- a/mypy/typeshed/stdlib/tracemalloc.pyi +++ b/mypy/typeshed/stdlib/tracemalloc.pyi @@ -1,7 +1,8 @@ import sys from _tracemalloc import * -from typing import Any, Sequence, Union, overload -from typing_extensions import SupportsIndex +from collections.abc import Sequence +from typing import Any, Union, overload +from typing_extensions import SupportsIndex, TypeAlias def get_object_traceback(obj: object) -> Traceback | None: ... def take_snapshot() -> Snapshot: ... @@ -41,7 +42,7 @@ class StatisticDiff: def __init__(self, traceback: Traceback, size: int, size_diff: int, count: int, count_diff: int) -> None: ... def __eq__(self, other: object) -> bool: ... -_FrameTupleT = tuple[str, int] +_FrameTupleT: TypeAlias = tuple[str, int] class Frame: @property @@ -61,9 +62,9 @@ class Frame: def __le__(self, other: Frame, NotImplemented: Any = ...) -> bool: ... if sys.version_info >= (3, 9): - _TraceTupleT = Union[tuple[int, int, Sequence[_FrameTupleT], int | None], tuple[int, int, Sequence[_FrameTupleT]]] + _TraceTupleT: TypeAlias = Union[tuple[int, int, Sequence[_FrameTupleT], int | None], tuple[int, int, Sequence[_FrameTupleT]]] else: - _TraceTupleT = tuple[int, int, Sequence[_FrameTupleT]] + _TraceTupleT: TypeAlias = tuple[int, int, Sequence[_FrameTupleT]] class Trace: @property diff --git a/mypy/typeshed/stdlib/tty.pyi b/mypy/typeshed/stdlib/tty.pyi index 08c93f6f2e84..8edae9ec2deb 100644 --- a/mypy/typeshed/stdlib/tty.pyi +++ b/mypy/typeshed/stdlib/tty.pyi @@ -1,10 +1,11 @@ import sys from typing import IO +from typing_extensions import TypeAlias if sys.platform != "win32": __all__ = ["setraw", "setcbreak"] - _FD = int | IO[str] + _FD: TypeAlias = int | IO[str] # XXX: Undocumented integer constants IFLAG: int diff --git a/mypy/typeshed/stdlib/turtle.pyi b/mypy/typeshed/stdlib/turtle.pyi index 2ff965465be6..3e91a5eb0ebf 100644 --- a/mypy/typeshed/stdlib/turtle.pyi +++ b/mypy/typeshed/stdlib/turtle.pyi @@ -1,6 +1,8 @@ from _typeshed import Self +from collections.abc import Callable, Sequence from tkinter import Canvas, Frame, Misc, PhotoImage, Scrollbar -from typing import Any, Callable, ClassVar, Sequence, Union, overload +from typing import Any, ClassVar, Union, overload +from typing_extensions import TypeAlias __all__ = [ "ScrolledCanvas", @@ -131,18 +133,18 @@ __all__ = [ # alias we use for return types. Really, these two aliases should be the # same, but as per the "no union returns" typeshed policy, we'll return # Any instead. -_Color = Union[str, tuple[float, float, float]] -_AnyColor = Any +_Color: TypeAlias = Union[str, tuple[float, float, float]] +_AnyColor: TypeAlias = Any # TODO: Replace this with a TypedDict once it becomes standardized. -_PenState = dict[str, Any] +_PenState: TypeAlias = dict[str, Any] -_Speed = str | float -_PolygonCoords = Sequence[tuple[float, float]] +_Speed: TypeAlias = str | float +_PolygonCoords: TypeAlias = Sequence[tuple[float, float]] # TODO: Type this more accurately # Vec2D is actually a custom subclass of 'tuple'. -Vec2D = tuple[float, float] +Vec2D: TypeAlias = tuple[float, float] # Does not actually inherit from Canvas, but dynamically gets all methods of Canvas class ScrolledCanvas(Canvas, Frame): # type: ignore[misc] diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 9b2fa1f4a2aa..872ed57a7c76 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -1,26 +1,23 @@ import sys from _typeshed import SupportsKeysAndGetItem -from importlib.abc import _LoaderProtocol -from importlib.machinery import ModuleSpec -from typing import ( - Any, +from collections.abc import ( AsyncGenerator, Awaitable, Callable, - ClassVar, Coroutine, Generator, - Generic, ItemsView, Iterable, Iterator, KeysView, - Mapping, MutableSequence, - TypeVar, ValuesView, - overload, ) +from importlib.abc import _LoaderProtocol +from importlib.machinery import ModuleSpec + +# pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping +from typing import Any, ClassVar, Generic, Mapping, TypeVar, overload # noqa: Y027 from typing_extensions import Literal, ParamSpec, final if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 4ad3ab3da274..28b588d79c9b 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -22,9 +22,12 @@ if sys.version_info >= (3, 11): "ForwardRef", "Generic", "Literal", + "LiteralString", + "NotRequired", "Optional", "ParamSpec", "Protocol", + "Required", "Tuple", "Type", "TypeVar", @@ -84,9 +87,11 @@ if sys.version_info >= (3, 11): "assert_never", "assert_type", "cast", + "clear_overloads", "final", "get_args", "get_origin", + "get_overloads", "get_type_hints", "is_typeddict", "Never", @@ -528,6 +533,9 @@ if sys.version_info >= (3, 11): Self: _SpecialForm Never: _SpecialForm = ... Unpack: _SpecialForm + Required: _SpecialForm + NotRequired: _SpecialForm + LiteralString: _SpecialForm class TypeVarTuple: __name__: str @@ -1138,7 +1146,7 @@ class Pattern(Generic[AnyStr]): # Functions if sys.version_info >= (3, 7): - _get_type_hints_obj_allowed_types = ( + _get_type_hints_obj_allowed_types = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed object | Callable[..., Any] | FunctionType @@ -1150,7 +1158,9 @@ if sys.version_info >= (3, 7): | MethodDescriptorType ) else: - _get_type_hints_obj_allowed_types = object | Callable[..., Any] | FunctionType | BuiltinFunctionType | MethodType | ModuleType + _get_type_hints_obj_allowed_types = ( # noqa: Y026 # TODO: Use TypeAlias once mypy bugs are fixed + object | Callable[..., Any] | FunctionType | BuiltinFunctionType | MethodType | ModuleType + ) if sys.version_info >= (3, 9): def get_type_hints( @@ -1180,6 +1190,8 @@ if sys.version_info >= (3, 11): def reveal_type(__obj: _T) -> _T: ... def assert_never(__arg: Never) -> Never: ... def assert_type(__val: _T, __typ: Any) -> _T: ... + def clear_overloads() -> None: ... + def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... # Type constructors diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 70f395446b0b..1c75ec38e75c 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -22,6 +22,7 @@ from typing import ( # noqa: Y022,Y027 Mapping, NewType as NewType, NoReturn as NoReturn, + Sequence, Text as Text, Type as Type, TypeVar, @@ -76,8 +77,10 @@ __all__ = [ "NoReturn", "Required", "NotRequired", + "clear_overloads", "get_args", "get_origin", + "get_overloads", "get_type_hints", ] @@ -188,10 +191,17 @@ else: # New things in 3.11 if sys.version_info >= (3, 11): from typing import ( + LiteralString as LiteralString, Never as Never, + NotRequired as NotRequired, + Required as Required, Self as Self, + TypeVarTuple as TypeVarTuple, + Unpack as Unpack, assert_never as assert_never, assert_type as assert_type, + clear_overloads as clear_overloads, + get_overloads as get_overloads, reveal_type as reveal_type, ) else: @@ -200,23 +210,26 @@ else: def reveal_type(__obj: _T) -> _T: ... def assert_never(__arg: NoReturn) -> NoReturn: ... def assert_type(__val: _T, __typ: Any) -> _T: ... + def clear_overloads() -> None: ... + def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... -# Experimental (hopefully these will be in 3.11) -Required: _SpecialForm -NotRequired: _SpecialForm -LiteralString: _SpecialForm -Unpack: _SpecialForm + Required: _SpecialForm + NotRequired: _SpecialForm + LiteralString: _SpecialForm + Unpack: _SpecialForm -@final -class TypeVarTuple: - __name__: str - def __init__(self, name: str) -> None: ... - def __iter__(self) -> Any: ... # Unpack[Self] + @final + class TypeVarTuple: + __name__: str + def __init__(self, name: str) -> None: ... + def __iter__(self) -> Any: ... # Unpack[Self] +# Experimental (hopefully these will be in 3.11) def dataclass_transform( *, eq_default: bool = ..., order_default: bool = ..., kw_only_default: bool = ..., - field_descriptors: tuple[type[Any] | Callable[..., Any], ...] = ..., + field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = ..., + **kwargs: object, ) -> Callable[[_T], _T]: ... diff --git a/mypy/typeshed/stdlib/unittest/async_case.pyi b/mypy/typeshed/stdlib/unittest/async_case.pyi index 0442ec444559..55407ec3f1c8 100644 --- a/mypy/typeshed/stdlib/unittest/async_case.pyi +++ b/mypy/typeshed/stdlib/unittest/async_case.pyi @@ -1,4 +1,4 @@ -from typing import Awaitable, Callable +from collections.abc import Awaitable, Callable from typing_extensions import ParamSpec from .case import TestCase diff --git a/mypy/typeshed/stdlib/unittest/case.pyi b/mypy/typeshed/stdlib/unittest/case.pyi index 87441920fe50..578bd6d6f271 100644 --- a/mypy/typeshed/stdlib/unittest/case.pyi +++ b/mypy/typeshed/stdlib/unittest/case.pyi @@ -3,25 +3,10 @@ import logging import sys import unittest.result from _typeshed import Self -from collections.abc import Set as AbstractSet +from collections.abc import Callable, Container, Iterable, Mapping, Sequence, Set as AbstractSet from contextlib import AbstractContextManager from types import TracebackType -from typing import ( - Any, - AnyStr, - Callable, - ClassVar, - Container, - Generic, - Iterable, - Mapping, - NamedTuple, - NoReturn, - Pattern, - Sequence, - TypeVar, - overload, -) +from typing import Any, AnyStr, ClassVar, Generic, NamedTuple, NoReturn, Pattern, TypeVar, overload from typing_extensions import ParamSpec from warnings import WarningMessage diff --git a/mypy/typeshed/stdlib/unittest/loader.pyi b/mypy/typeshed/stdlib/unittest/loader.pyi index 8b3c82233cec..76c60f4803f5 100644 --- a/mypy/typeshed/stdlib/unittest/loader.pyi +++ b/mypy/typeshed/stdlib/unittest/loader.pyi @@ -2,11 +2,13 @@ import sys import unittest.case import unittest.result import unittest.suite +from collections.abc import Callable, Sequence from types import ModuleType -from typing import Any, Callable, Pattern, Sequence +from typing import Any, Pattern +from typing_extensions import TypeAlias -_SortComparisonMethod = Callable[[str, str], int] -_SuiteClass = Callable[[list[unittest.case.TestCase]], unittest.suite.TestSuite] +_SortComparisonMethod: TypeAlias = Callable[[str, str], int] +_SuiteClass: TypeAlias = Callable[[list[unittest.case.TestCase]], unittest.suite.TestSuite] VALID_MODULE_NAME: Pattern[str] diff --git a/mypy/typeshed/stdlib/unittest/main.pyi b/mypy/typeshed/stdlib/unittest/main.pyi index d3c0ca722f4d..31853676b011 100644 --- a/mypy/typeshed/stdlib/unittest/main.pyi +++ b/mypy/typeshed/stdlib/unittest/main.pyi @@ -3,8 +3,9 @@ import unittest.case import unittest.loader import unittest.result import unittest.suite +from collections.abc import Iterable from types import ModuleType -from typing import Any, Iterable, Protocol +from typing import Any, Protocol MAIN_EXAMPLES: str MODULE_EXAMPLES: str diff --git a/mypy/typeshed/stdlib/unittest/mock.pyi b/mypy/typeshed/stdlib/unittest/mock.pyi index f5cd4218cea0..400bdaac3b41 100644 --- a/mypy/typeshed/stdlib/unittest/mock.pyi +++ b/mypy/typeshed/stdlib/unittest/mock.pyi @@ -1,9 +1,10 @@ import sys from _typeshed import Self +from collections.abc import Awaitable, Callable, Iterable, Mapping, Sequence from contextlib import _GeneratorContextManager from types import TracebackType -from typing import Any, Awaitable, Callable, Generic, Iterable, Mapping, Sequence, TypeVar, overload -from typing_extensions import Literal +from typing import Any, Generic, TypeVar, overload +from typing_extensions import Literal, TypeAlias _T = TypeVar("_T") _TT = TypeVar("_TT", bound=type[Any]) @@ -77,9 +78,9 @@ class _Sentinel: sentinel: Any DEFAULT: Any -_ArgsKwargs = tuple[tuple[Any, ...], Mapping[str, Any]] -_NameArgsKwargs = tuple[str, tuple[Any, ...], Mapping[str, Any]] -_CallValue = str | tuple[Any, ...] | Mapping[str, Any] | _ArgsKwargs | _NameArgsKwargs +_ArgsKwargs: TypeAlias = tuple[tuple[Any, ...], Mapping[str, Any]] +_NameArgsKwargs: TypeAlias = tuple[str, tuple[Any, ...], Mapping[str, Any]] +_CallValue: TypeAlias = str | tuple[Any, ...] | Mapping[str, Any] | _ArgsKwargs | _NameArgsKwargs class _Call(tuple[Any, ...]): def __new__( @@ -283,9 +284,9 @@ class _patch_dict: stop: Any if sys.version_info >= (3, 8): - _Mock = MagicMock | AsyncMock + _Mock: TypeAlias = MagicMock | AsyncMock else: - _Mock = MagicMock + _Mock: TypeAlias = MagicMock class _patcher: TEST_PREFIX: str @@ -296,7 +297,7 @@ class _patcher: @overload def __call__( # type: ignore[misc] self, - target: Any, + target: str, new: _T, spec: Any | None = ..., create: bool = ..., @@ -308,7 +309,7 @@ class _patcher: @overload def __call__( self, - target: Any, + target: str, *, spec: Any | None = ..., create: bool = ..., diff --git a/mypy/typeshed/stdlib/unittest/result.pyi b/mypy/typeshed/stdlib/unittest/result.pyi index 1c79f8ab648c..5dfec13cb52c 100644 --- a/mypy/typeshed/stdlib/unittest/result.pyi +++ b/mypy/typeshed/stdlib/unittest/result.pyi @@ -1,8 +1,7 @@ import unittest.case -from types import TracebackType -from typing import Any, Callable, TextIO, TypeVar, Union - -_SysExcInfoType = Union[tuple[type[BaseException], BaseException, TracebackType], tuple[None, None, None]] +from _typeshed import OptExcInfo +from collections.abc import Callable +from typing import Any, TextIO, TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) @@ -31,10 +30,10 @@ class TestResult: def stopTest(self, test: unittest.case.TestCase) -> None: ... def startTestRun(self) -> None: ... def stopTestRun(self) -> None: ... - def addError(self, test: unittest.case.TestCase, err: _SysExcInfoType) -> None: ... - def addFailure(self, test: unittest.case.TestCase, err: _SysExcInfoType) -> None: ... + def addError(self, test: unittest.case.TestCase, err: OptExcInfo) -> None: ... + def addFailure(self, test: unittest.case.TestCase, err: OptExcInfo) -> None: ... def addSuccess(self, test: unittest.case.TestCase) -> None: ... def addSkip(self, test: unittest.case.TestCase, reason: str) -> None: ... - def addExpectedFailure(self, test: unittest.case.TestCase, err: _SysExcInfoType) -> None: ... + def addExpectedFailure(self, test: unittest.case.TestCase, err: OptExcInfo) -> None: ... def addUnexpectedSuccess(self, test: unittest.case.TestCase) -> None: ... - def addSubTest(self, test: unittest.case.TestCase, subtest: unittest.case.TestCase, err: _SysExcInfoType | None) -> None: ... + def addSubTest(self, test: unittest.case.TestCase, subtest: unittest.case.TestCase, err: OptExcInfo | None) -> None: ... diff --git a/mypy/typeshed/stdlib/unittest/runner.pyi b/mypy/typeshed/stdlib/unittest/runner.pyi index 479a9f2c304c..1f1b89bc1bee 100644 --- a/mypy/typeshed/stdlib/unittest/runner.pyi +++ b/mypy/typeshed/stdlib/unittest/runner.pyi @@ -1,9 +1,11 @@ import unittest.case import unittest.result import unittest.suite -from typing import Callable, Iterable, TextIO +from collections.abc import Callable, Iterable +from typing import TextIO +from typing_extensions import TypeAlias -_ResultClassType = Callable[[TextIO, bool, int], unittest.result.TestResult] +_ResultClassType: TypeAlias = Callable[[TextIO, bool, int], unittest.result.TestResult] class TextTestResult(unittest.result.TestResult): descriptions: bool # undocumented diff --git a/mypy/typeshed/stdlib/unittest/signals.pyi b/mypy/typeshed/stdlib/unittest/signals.pyi index e6f5f95e1eb1..89e108d926a6 100644 --- a/mypy/typeshed/stdlib/unittest/signals.pyi +++ b/mypy/typeshed/stdlib/unittest/signals.pyi @@ -1,5 +1,6 @@ import unittest.result -from typing import Callable, TypeVar, overload +from collections.abc import Callable +from typing import TypeVar, overload from typing_extensions import ParamSpec _P = ParamSpec("_P") diff --git a/mypy/typeshed/stdlib/unittest/suite.pyi b/mypy/typeshed/stdlib/unittest/suite.pyi index ca483b06ac9f..26bef658f1cd 100644 --- a/mypy/typeshed/stdlib/unittest/suite.pyi +++ b/mypy/typeshed/stdlib/unittest/suite.pyi @@ -1,8 +1,9 @@ import unittest.case import unittest.result -from typing import Iterable, Iterator +from collections.abc import Iterable, Iterator +from typing_extensions import TypeAlias -_TestType = unittest.case.TestCase | TestSuite +_TestType: TypeAlias = unittest.case.TestCase | TestSuite class BaseTestSuite(Iterable[_TestType]): _tests: list[unittest.case.TestCase] diff --git a/mypy/typeshed/stdlib/unittest/util.pyi b/mypy/typeshed/stdlib/unittest/util.pyi index 30ab6061b100..f62c728760ff 100644 --- a/mypy/typeshed/stdlib/unittest/util.pyi +++ b/mypy/typeshed/stdlib/unittest/util.pyi @@ -1,7 +1,9 @@ -from typing import Any, Sequence, TypeVar +from collections.abc import Sequence +from typing import Any, TypeVar +from typing_extensions import TypeAlias _T = TypeVar("_T") -_Mismatch = tuple[_T, _T, int] +_Mismatch: TypeAlias = tuple[_T, _T, int] _MAX_LENGTH: int _PLACEHOLDER_LEN: int diff --git a/mypy/typeshed/stdlib/urllib/parse.pyi b/mypy/typeshed/stdlib/urllib/parse.pyi index a45e23dd9084..c6a6836e6e95 100644 --- a/mypy/typeshed/stdlib/urllib/parse.pyi +++ b/mypy/typeshed/stdlib/urllib/parse.pyi @@ -1,5 +1,7 @@ import sys -from typing import Any, AnyStr, Callable, Generic, Mapping, NamedTuple, Sequence, overload +from collections.abc import Callable, Mapping, Sequence +from typing import Any, AnyStr, Generic, NamedTuple, overload +from typing_extensions import TypeAlias if sys.version_info >= (3, 9): from types import GenericAlias @@ -28,7 +30,7 @@ __all__ = [ "SplitResultBytes", ] -_Str = bytes | str +_Str: TypeAlias = bytes | str uses_relative: list[str] uses_netloc: list[str] diff --git a/mypy/typeshed/stdlib/urllib/request.pyi b/mypy/typeshed/stdlib/urllib/request.pyi index 265ef2196715..5e6dde01480a 100644 --- a/mypy/typeshed/stdlib/urllib/request.pyi +++ b/mypy/typeshed/stdlib/urllib/request.pyi @@ -1,10 +1,12 @@ import ssl import sys from _typeshed import StrOrBytesPath, SupportsRead +from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence from email.message import Message from http.client import HTTPMessage, HTTPResponse, _HTTPConnectionProtocol from http.cookiejar import CookieJar -from typing import IO, Any, Callable, ClassVar, Iterable, Mapping, MutableMapping, NoReturn, Pattern, Sequence, TypeVar, overload +from typing import IO, Any, ClassVar, NoReturn, Pattern, TypeVar, overload +from typing_extensions import TypeAlias from urllib.error import HTTPError from urllib.response import addclosehook, addinfourl @@ -46,8 +48,8 @@ __all__ = [ ] _T = TypeVar("_T") -_UrlopenRet = Any -_DataType = bytes | SupportsRead[bytes] | Iterable[bytes] | None +_UrlopenRet: TypeAlias = Any +_DataType: TypeAlias = bytes | SupportsRead[bytes] | Iterable[bytes] | None def urlopen( url: str | Request, diff --git a/mypy/typeshed/stdlib/urllib/response.pyi b/mypy/typeshed/stdlib/urllib/response.pyi index 2efec0d47d44..8c9a600f3c48 100644 --- a/mypy/typeshed/stdlib/urllib/response.pyi +++ b/mypy/typeshed/stdlib/urllib/response.pyi @@ -1,8 +1,9 @@ import sys from _typeshed import Self +from collections.abc import Callable, Iterable from email.message import Message from types import TracebackType -from typing import IO, Any, BinaryIO, Callable, Iterable +from typing import IO, Any, BinaryIO __all__ = ["addbase", "addclosehook", "addinfo", "addinfourl"] diff --git a/mypy/typeshed/stdlib/urllib/robotparser.pyi b/mypy/typeshed/stdlib/urllib/robotparser.pyi index d1d69546db42..795cf83fcecd 100644 --- a/mypy/typeshed/stdlib/urllib/robotparser.pyi +++ b/mypy/typeshed/stdlib/urllib/robotparser.pyi @@ -1,5 +1,6 @@ import sys -from typing import Iterable, NamedTuple +from collections.abc import Iterable +from typing import NamedTuple __all__ = ["RobotFileParser"] diff --git a/mypy/typeshed/stdlib/uu.pyi b/mypy/typeshed/stdlib/uu.pyi index d75df67a1f76..4ebb12be8858 100644 --- a/mypy/typeshed/stdlib/uu.pyi +++ b/mypy/typeshed/stdlib/uu.pyi @@ -1,9 +1,10 @@ import sys from typing import BinaryIO +from typing_extensions import TypeAlias __all__ = ["Error", "encode", "decode"] -_File = str | BinaryIO +_File: TypeAlias = str | BinaryIO class Error(Exception): ... diff --git a/mypy/typeshed/stdlib/uuid.pyi b/mypy/typeshed/stdlib/uuid.pyi index 4d46e89beddd..fd7f1334e52a 100644 --- a/mypy/typeshed/stdlib/uuid.pyi +++ b/mypy/typeshed/stdlib/uuid.pyi @@ -1,9 +1,10 @@ import sys +from typing_extensions import TypeAlias # Because UUID has properties called int and bytes we need to rename these temporarily. -_Int = int -_Bytes = bytes -_FieldsType = tuple[int, int, int, int, int, int] +_Int: TypeAlias = int +_Bytes: TypeAlias = bytes +_FieldsType: TypeAlias = tuple[int, int, int, int, int, int] if sys.version_info >= (3, 7): from enum import Enum diff --git a/mypy/typeshed/stdlib/venv/__init__.pyi b/mypy/typeshed/stdlib/venv/__init__.pyi index 815490a205ab..6afe328ac90d 100644 --- a/mypy/typeshed/stdlib/venv/__init__.pyi +++ b/mypy/typeshed/stdlib/venv/__init__.pyi @@ -1,7 +1,7 @@ +from collections.abc import Sequence import sys from _typeshed import StrOrBytesPath from types import SimpleNamespace -from typing import Sequence if sys.version_info >= (3, 9): CORE_VENV_DEPS: tuple[str, ...] diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index 1799d69f5ba6..bd7afb2d7cba 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -1,7 +1,8 @@ from _warnings import warn as warn, warn_explicit as warn_explicit +from collections.abc import Sequence from types import ModuleType, TracebackType -from typing import Any, Sequence, TextIO, overload -from typing_extensions import Literal +from typing import Any, TextIO, overload +from typing_extensions import Literal, TypeAlias __all__ = [ "warn", @@ -14,7 +15,7 @@ __all__ = [ "catch_warnings", ] -_ActionKind = Literal["default", "error", "ignore", "always", "module", "once"] +_ActionKind: TypeAlias = Literal["default", "error", "ignore", "always", "module", "once"] filters: Sequence[tuple[str, str | None, type[Warning], str | None, int]] # undocumented, do not mutate diff --git a/mypy/typeshed/stdlib/wave.pyi b/mypy/typeshed/stdlib/wave.pyi index de20c6c4f5d4..689282f69ee7 100644 --- a/mypy/typeshed/stdlib/wave.pyi +++ b/mypy/typeshed/stdlib/wave.pyi @@ -1,14 +1,14 @@ import sys from _typeshed import ReadableBuffer, Self from typing import IO, Any, BinaryIO, NamedTuple, NoReturn, overload -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 9): __all__ = ["open", "Error", "Wave_read", "Wave_write"] else: __all__ = ["open", "openfp", "Error", "Wave_read", "Wave_write"] -_File = str | IO[bytes] +_File: TypeAlias = str | IO[bytes] class Error(Exception): ... diff --git a/mypy/typeshed/stdlib/weakref.pyi b/mypy/typeshed/stdlib/weakref.pyi index 820488529143..03dd89c8e210 100644 --- a/mypy/typeshed/stdlib/weakref.pyi +++ b/mypy/typeshed/stdlib/weakref.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import Self, SupportsKeysAndGetItem from _weakrefset import WeakSet as WeakSet -from typing import Any, Callable, Generic, Iterable, Iterator, Mapping, MutableMapping, TypeVar, overload +from collections.abc import Callable, Iterable, Iterator, Mapping, MutableMapping +from typing import Any, Generic, TypeVar, overload from typing_extensions import ParamSpec from _weakref import ( diff --git a/mypy/typeshed/stdlib/webbrowser.pyi b/mypy/typeshed/stdlib/webbrowser.pyi index ce8fca262d2d..a5b04a262596 100644 --- a/mypy/typeshed/stdlib/webbrowser.pyi +++ b/mypy/typeshed/stdlib/webbrowser.pyi @@ -1,6 +1,6 @@ import sys from abc import abstractmethod -from typing import Callable, Sequence +from collections.abc import Callable, Sequence from typing_extensions import Literal __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] diff --git a/mypy/typeshed/stdlib/winreg.pyi b/mypy/typeshed/stdlib/winreg.pyi index 5dc7e4363d6f..2cc42318f1a4 100644 --- a/mypy/typeshed/stdlib/winreg.pyi +++ b/mypy/typeshed/stdlib/winreg.pyi @@ -2,10 +2,10 @@ import sys from _typeshed import Self from types import TracebackType from typing import Any -from typing_extensions import Literal, final +from typing_extensions import Literal, TypeAlias, final if sys.platform == "win32": - _KeyType = HKEYType | int + _KeyType: TypeAlias = HKEYType | int def CloseKey(__hkey: _KeyType) -> None: ... def ConnectRegistry(__computer_name: str | None, __key: _KeyType) -> HKEYType: ... def CreateKey(__key: _KeyType, __sub_key: str | None) -> HKEYType: ... diff --git a/mypy/typeshed/stdlib/wsgiref/handlers.pyi b/mypy/typeshed/stdlib/wsgiref/handlers.pyi index 9e2153788cac..655fba668598 100644 --- a/mypy/typeshed/stdlib/wsgiref/handlers.pyi +++ b/mypy/typeshed/stdlib/wsgiref/handlers.pyi @@ -1,15 +1,14 @@ +from _typeshed import OptExcInfo +from _typeshed.wsgi import ErrorStream, InputStream, StartResponse, WSGIApplication, WSGIEnvironment from abc import abstractmethod -from types import TracebackType -from typing import IO, Callable, MutableMapping +from collections.abc import Callable, MutableMapping +from typing import IO from .headers import Headers -from .types import ErrorStream, InputStream, StartResponse, WSGIApplication, WSGIEnvironment from .util import FileWrapper __all__ = ["BaseHandler", "SimpleHandler", "BaseCGIHandler", "CGIHandler", "IISCGIHandler", "read_environ"] -_exc_info = tuple[type[BaseException] | None, BaseException | None, TracebackType | None] - def format_date_time(timestamp: float | None) -> str: ... # undocumented def read_environ() -> dict[str, str]: ... @@ -39,7 +38,7 @@ class BaseHandler: def set_content_length(self) -> None: ... def cleanup_headers(self) -> None: ... def start_response( - self, status: str, headers: list[tuple[str, str]], exc_info: _exc_info | None = ... + self, status: str, headers: list[tuple[str, str]], exc_info: OptExcInfo | None = ... ) -> Callable[[bytes], None]: ... def send_preamble(self) -> None: ... def write(self, data: bytes) -> None: ... @@ -49,7 +48,7 @@ class BaseHandler: def send_headers(self) -> None: ... def result_is_file(self) -> bool: ... def client_is_modern(self) -> bool: ... - def log_exception(self, exc_info: _exc_info) -> None: ... + def log_exception(self, exc_info: OptExcInfo) -> None: ... def handle_error(self) -> None: ... def error_output(self, environ: WSGIEnvironment, start_response: StartResponse) -> list[bytes]: ... @abstractmethod diff --git a/mypy/typeshed/stdlib/wsgiref/headers.pyi b/mypy/typeshed/stdlib/wsgiref/headers.pyi index b62124a2a936..cde0227a7830 100644 --- a/mypy/typeshed/stdlib/wsgiref/headers.pyi +++ b/mypy/typeshed/stdlib/wsgiref/headers.pyi @@ -1,6 +1,7 @@ from typing import Pattern, overload +from typing_extensions import TypeAlias -_HeaderList = list[tuple[str, str]] +_HeaderList: TypeAlias = list[tuple[str, str]] tspecials: Pattern[str] # undocumented diff --git a/mypy/typeshed/stdlib/wsgiref/simple_server.pyi b/mypy/typeshed/stdlib/wsgiref/simple_server.pyi index 389d30c22db2..1dc84e9fbebe 100644 --- a/mypy/typeshed/stdlib/wsgiref/simple_server.pyi +++ b/mypy/typeshed/stdlib/wsgiref/simple_server.pyi @@ -1,8 +1,8 @@ +from _typeshed.wsgi import ErrorStream, StartResponse, WSGIApplication, WSGIEnvironment from http.server import BaseHTTPRequestHandler, HTTPServer from typing import TypeVar, overload from .handlers import SimpleHandler -from .types import ErrorStream, StartResponse, WSGIApplication, WSGIEnvironment __all__ = ["WSGIServer", "WSGIRequestHandler", "demo_app", "make_server"] diff --git a/mypy/typeshed/stdlib/wsgiref/types.pyi b/mypy/typeshed/stdlib/wsgiref/types.pyi index c272ae67c391..b8ece8d57a9d 100644 --- a/mypy/typeshed/stdlib/wsgiref/types.pyi +++ b/mypy/typeshed/stdlib/wsgiref/types.pyi @@ -1,3 +1,32 @@ -# Obsolete, use _typeshed.wsgi directly. +from collections.abc import Callable, Iterable +from sys import _OptExcInfo +from typing import Any, Protocol +from typing_extensions import TypeAlias -from _typeshed.wsgi import * +__all__ = ["StartResponse", "WSGIEnvironment", "WSGIApplication", "InputStream", "ErrorStream", "FileWrapper"] + +class StartResponse(Protocol): + def __call__( + self, __status: str, __headers: list[tuple[str, str]], __exc_info: _OptExcInfo | None = ... + ) -> Callable[[bytes], object]: ... + +WSGIEnvironment: TypeAlias = dict[str, Any] +WSGIApplication: TypeAlias = Callable[[WSGIEnvironment, StartResponse], Iterable[bytes]] + +class InputStream(Protocol): + def read(self, __size: int = ...) -> bytes: ... + def readline(self, __size: int = ...) -> bytes: ... + def readlines(self, __hint: int = ...) -> list[bytes]: ... + def __iter__(self) -> Iterable[bytes]: ... + +class ErrorStream(Protocol): + def flush(self) -> object: ... + def write(self, __s: str) -> object: ... + def writelines(self, __seq: list[str]) -> object: ... + +class _Readable(Protocol): + def read(self, __size: int = ...) -> bytes: ... + # Optional: def close(self) -> object: ... + +class FileWrapper(Protocol): + def __call__(self, __file: _Readable, __block_size: int = ...) -> Iterable[bytes]: ... diff --git a/mypy/typeshed/stdlib/wsgiref/util.pyi b/mypy/typeshed/stdlib/wsgiref/util.pyi index f2c3135df786..36e5c1e69676 100644 --- a/mypy/typeshed/stdlib/wsgiref/util.pyi +++ b/mypy/typeshed/stdlib/wsgiref/util.pyi @@ -1,7 +1,7 @@ import sys -from typing import IO, Any, Callable - -from .types import WSGIEnvironment +from _typeshed.wsgi import WSGIEnvironment +from collections.abc import Callable +from typing import IO, Any __all__ = ["FileWrapper", "guess_scheme", "application_uri", "request_uri", "shift_path_info", "setup_testing_defaults"] diff --git a/mypy/typeshed/stdlib/wsgiref/validate.pyi b/mypy/typeshed/stdlib/wsgiref/validate.pyi index 35491756c288..ada2283a6af0 100644 --- a/mypy/typeshed/stdlib/wsgiref/validate.pyi +++ b/mypy/typeshed/stdlib/wsgiref/validate.pyi @@ -1,5 +1,6 @@ from _typeshed.wsgi import ErrorStream, InputStream, WSGIApplication -from typing import Any, Callable, Iterable, Iterator, NoReturn +from collections.abc import Callable, Iterable, Iterator +from typing import Any, NoReturn __all__ = ["validator"] diff --git a/mypy/typeshed/stdlib/xdrlib.pyi b/mypy/typeshed/stdlib/xdrlib.pyi index e9716e29014d..e6b78d5542be 100644 --- a/mypy/typeshed/stdlib/xdrlib.pyi +++ b/mypy/typeshed/stdlib/xdrlib.pyi @@ -1,4 +1,5 @@ -from typing import Callable, Sequence, TypeVar +from collections.abc import Callable, Sequence +from typing import TypeVar __all__ = ["Error", "Packer", "Unpacker", "ConversionError"] diff --git a/mypy/typeshed/stdlib/xml/dom/domreg.pyi b/mypy/typeshed/stdlib/xml/dom/domreg.pyi index b9e2dd9eb263..5a276ae5f561 100644 --- a/mypy/typeshed/stdlib/xml/dom/domreg.pyi +++ b/mypy/typeshed/stdlib/xml/dom/domreg.pyi @@ -1,5 +1,5 @@ from _typeshed.xml import DOMImplementation -from typing import Callable, Iterable +from collections.abc import Callable, Iterable well_known_implementations: dict[str, str] registered: dict[str, Callable[[], DOMImplementation]] diff --git a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi index 411401d11ccd..4507b3d98ee7 100644 --- a/mypy/typeshed/stdlib/xml/dom/minicompat.pyi +++ b/mypy/typeshed/stdlib/xml/dom/minicompat.pyi @@ -1,4 +1,5 @@ -from typing import Any, Iterable, TypeVar +from collections.abc import Iterable +from typing import Any, TypeVar __all__ = ["NodeList", "EmptyNodeList", "StringTypes", "defproperty"] diff --git a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi index b2bec64fd1dc..07f220ddd347 100644 --- a/mypy/typeshed/stdlib/xml/dom/pulldom.pyi +++ b/mypy/typeshed/stdlib/xml/dom/pulldom.pyi @@ -1,7 +1,8 @@ import sys from _typeshed import SupportsRead -from typing import Any, Sequence -from typing_extensions import Literal +from collections.abc import Sequence +from typing import Any +from typing_extensions import Literal, TypeAlias from xml.dom.minidom import Document, DOMImplementation, Element, Text from xml.sax.handler import ContentHandler from xml.sax.xmlreader import XMLReader @@ -15,10 +16,10 @@ PROCESSING_INSTRUCTION: Literal["PROCESSING_INSTRUCTION"] IGNORABLE_WHITESPACE: Literal["IGNORABLE_WHITESPACE"] CHARACTERS: Literal["CHARACTERS"] -_DocumentFactory = DOMImplementation | None -_Node = Document | Element | Text +_DocumentFactory: TypeAlias = DOMImplementation | None +_Node: TypeAlias = Document | Element | Text -_Event = tuple[ +_Event: TypeAlias = tuple[ Literal[ Literal["START_ELEMENT"], Literal["END_ELEMENT"], diff --git a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi index ec47ec134e08..f6afd8aa2786 100644 --- a/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi +++ b/mypy/typeshed/stdlib/xml/dom/xmlbuilder.pyi @@ -1,5 +1,5 @@ from typing import Any, NoReturn -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from urllib.request import OpenerDirector from xml.dom.expatbuilder import ExpatBuilder, ExpatBuilderNS from xml.dom.minidom import Node @@ -18,13 +18,13 @@ __all__ = ["DOMBuilder", "DOMEntityResolver", "DOMInputSource"] # probably the same as `Options.errorHandler`? # Maybe `xml.sax.handler.ErrorHandler`? -_DOMBuilderErrorHandlerType = Any | None +_DOMBuilderErrorHandlerType: TypeAlias = Any | None # probably some kind of IO... -_DOMInputSourceCharacterStreamType = Any | None +_DOMInputSourceCharacterStreamType: TypeAlias = Any | None # probably a string?? -_DOMInputSourceStringDataType = Any | None +_DOMInputSourceStringDataType: TypeAlias = Any | None # probably a string?? -_DOMInputSourceEncodingType = Any | None +_DOMInputSourceEncodingType: TypeAlias = Any | None class Options: namespaces: int diff --git a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi index 5cd85cc21753..7bb78d0628ce 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementInclude.pyi @@ -1,5 +1,5 @@ import sys -from typing import Callable +from collections.abc import Callable from xml.etree.ElementTree import Element XINCLUDE: str diff --git a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi index 5a2dd69c1bee..e5b8223aab34 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementPath.pyi @@ -1,11 +1,13 @@ -from typing import Callable, Generator, Pattern, TypeVar +from collections.abc import Callable, Generator +from typing import Pattern, TypeVar +from typing_extensions import TypeAlias from xml.etree.ElementTree import Element xpath_tokenizer_re: Pattern[str] -_token = tuple[str, str] -_next = Callable[[], _token] -_callback = Callable[[_SelectorContext, list[Element]], Generator[Element, None, None]] +_token: TypeAlias = tuple[str, str] +_next: TypeAlias = Callable[[], _token] +_callback: TypeAlias = Callable[[_SelectorContext, list[Element]], Generator[Element, None, None]] def xpath_tokenizer(pattern: str, namespaces: dict[str, str] | None = ...) -> Generator[_token, None, None]: ... def get_parent_map(context: _SelectorContext) -> dict[Element, Element]: ... diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 7b50b4279021..414530b0a34c 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -1,20 +1,8 @@ import sys from _typeshed import FileDescriptor, StrOrBytesPath, SupportsRead, SupportsWrite -from typing import ( - Any, - Callable, - Generator, - ItemsView, - Iterable, - Iterator, - KeysView, - Mapping, - MutableSequence, - Sequence, - TypeVar, - overload, -) -from typing_extensions import Literal, SupportsIndex, TypeGuard +from collections.abc import Callable, Generator, ItemsView, Iterable, Iterator, KeysView, Mapping, MutableSequence, Sequence +from typing import Any, TypeVar, overload +from typing_extensions import Literal, SupportsIndex, TypeAlias, TypeGuard if sys.version_info >= (3, 9): __all__ = [ @@ -101,9 +89,9 @@ else: ] _T = TypeVar("_T") -_FileRead = StrOrBytesPath | FileDescriptor | SupportsRead[bytes] | SupportsRead[str] -_FileWriteC14N = StrOrBytesPath | FileDescriptor | SupportsWrite[bytes] -_FileWrite = _FileWriteC14N | SupportsWrite[str] +_FileRead: TypeAlias = StrOrBytesPath | FileDescriptor | SupportsRead[bytes] | SupportsRead[str] +_FileWriteC14N: TypeAlias = StrOrBytesPath | FileDescriptor | SupportsWrite[bytes] +_FileWrite: TypeAlias = _FileWriteC14N | SupportsWrite[str] VERSION: str @@ -352,7 +340,7 @@ def fromstringlist(sequence: Sequence[str | bytes], parser: XMLParser | None = . # TreeBuilder is called by client code (they could pass strs, bytes or whatever); # but we don't want to use a too-broad type, or it would be too hard to write # elementfactories. -_ElementFactory = Callable[[Any, dict[Any, Any]], Element] +_ElementFactory: TypeAlias = Callable[[Any, dict[Any, Any]], Element] class TreeBuilder: if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/xml/sax/__init__.pyi b/mypy/typeshed/stdlib/xml/sax/__init__.pyi index 418164aa887f..22a2764a699f 100644 --- a/mypy/typeshed/stdlib/xml/sax/__init__.pyi +++ b/mypy/typeshed/stdlib/xml/sax/__init__.pyi @@ -1,6 +1,7 @@ import sys from _typeshed import SupportsRead, _T_co -from typing import Any, Iterable, NoReturn, Protocol +from collections.abc import Iterable +from typing import Any, NoReturn, Protocol from xml.sax.handler import ContentHandler, ErrorHandler from xml.sax.xmlreader import Locator, XMLReader diff --git a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi index c7304f4b5261..1361949d0c3e 100644 --- a/mypy/typeshed/stdlib/xml/sax/saxutils.pyi +++ b/mypy/typeshed/stdlib/xml/sax/saxutils.pyi @@ -1,7 +1,7 @@ from _typeshed import SupportsWrite from codecs import StreamReaderWriter, StreamWriter +from collections.abc import Mapping from io import RawIOBase, TextIOBase -from typing import Mapping from xml.sax import handler, xmlreader def escape(data: str, entities: Mapping[str, str] = ...) -> str: ... diff --git a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi index 684e9cef1f42..d7d4db5b0a16 100644 --- a/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi +++ b/mypy/typeshed/stdlib/xml/sax/xmlreader.pyi @@ -1,4 +1,4 @@ -from typing import Mapping +from collections.abc import Mapping class XMLReader: def __init__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/xmlrpc/client.pyi b/mypy/typeshed/stdlib/xmlrpc/client.pyi index a59be37f9e81..d4e82d5e40da 100644 --- a/mypy/typeshed/stdlib/xmlrpc/client.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/client.pyi @@ -3,21 +3,22 @@ import http.client import sys import time from _typeshed import Self, SupportsRead, SupportsWrite +from collections.abc import Callable, Iterable, Mapping from datetime import datetime from io import BytesIO from types import TracebackType -from typing import Any, Callable, Iterable, Mapping, Protocol, Union, overload -from typing_extensions import Literal +from typing import Any, Protocol, Union, overload +from typing_extensions import Literal, TypeAlias class _SupportsTimeTuple(Protocol): def timetuple(self) -> time.struct_time: ... -_DateTimeComparable = DateTime | datetime | str | _SupportsTimeTuple -_Marshallable = ( +_DateTimeComparable: TypeAlias = DateTime | datetime | str | _SupportsTimeTuple +_Marshallable: TypeAlias = ( bool | int | float | str | bytes | None | tuple[Any, ...] | list[Any] | dict[Any, Any] | datetime | DateTime | Binary ) -_XMLDate = int | datetime | tuple[int, ...] | time.struct_time -_HostType = Union[tuple[str, dict[str, str]], str] +_XMLDate: TypeAlias = int | datetime | tuple[int, ...] | time.struct_time +_HostType: TypeAlias = Union[tuple[str, dict[str, str]], str] def escape(s: str) -> str: ... # undocumented diff --git a/mypy/typeshed/stdlib/xmlrpc/server.pyi b/mypy/typeshed/stdlib/xmlrpc/server.pyi index 2ed0b03c7151..371f1821b29d 100644 --- a/mypy/typeshed/stdlib/xmlrpc/server.pyi +++ b/mypy/typeshed/stdlib/xmlrpc/server.pyi @@ -2,12 +2,14 @@ import http.server import pydoc import socketserver import sys +from collections.abc import Callable, Iterable, Mapping from datetime import datetime -from typing import Any, Callable, Iterable, Mapping, Pattern, Protocol +from typing import Any, Pattern, Protocol +from typing_extensions import TypeAlias from xmlrpc.client import Fault # TODO: Recursive type on tuple, list, dict -_Marshallable = None | bool | int | float | str | bytes | tuple[Any, ...] | list[Any] | dict[Any, Any] | datetime +_Marshallable: TypeAlias = None | bool | int | float | str | bytes | tuple[Any, ...] | list[Any] | dict[Any, Any] | datetime # The dispatch accepts anywhere from 0 to N arguments, no easy way to allow this in mypy class _DispatchArity0(Protocol): @@ -30,7 +32,9 @@ class _DispatchArity4(Protocol): class _DispatchArityN(Protocol): def __call__(self, *args: _Marshallable) -> _Marshallable: ... -_DispatchProtocol = _DispatchArity0 | _DispatchArity1 | _DispatchArity2 | _DispatchArity3 | _DispatchArity4 | _DispatchArityN +_DispatchProtocol: TypeAlias = ( + _DispatchArity0 | _DispatchArity1 | _DispatchArity2 | _DispatchArity3 | _DispatchArity4 | _DispatchArityN +) def resolve_dotted_attribute(obj: Any, attr: str, allow_dotted_names: bool = ...) -> Any: ... # undocumented def list_public_methods(obj: Any) -> list[str]: ... # undocumented diff --git a/mypy/typeshed/stdlib/zipapp.pyi b/mypy/typeshed/stdlib/zipapp.pyi index c3cf8321dbc4..d42640141620 100644 --- a/mypy/typeshed/stdlib/zipapp.pyi +++ b/mypy/typeshed/stdlib/zipapp.pyi @@ -1,10 +1,12 @@ import sys +from collections.abc import Callable from pathlib import Path -from typing import BinaryIO, Callable +from typing import BinaryIO +from typing_extensions import TypeAlias __all__ = ["ZipAppError", "create_archive", "get_interpreter"] -_Path = str | Path | BinaryIO +_Path: TypeAlias = str | Path | BinaryIO class ZipAppError(ValueError): ... diff --git a/mypy/typeshed/stdlib/zipfile.pyi b/mypy/typeshed/stdlib/zipfile.pyi index b837528d7dfa..d5255b15c3c0 100644 --- a/mypy/typeshed/stdlib/zipfile.pyi +++ b/mypy/typeshed/stdlib/zipfile.pyi @@ -1,10 +1,11 @@ import io import sys from _typeshed import Self, StrOrBytesPath, StrPath +from collections.abc import Callable, Iterable, Iterator, Sequence from os import PathLike from types import TracebackType -from typing import IO, Any, Callable, Iterable, Iterator, Protocol, Sequence, overload -from typing_extensions import Literal +from typing import IO, Any, Protocol, overload +from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 8): __all__ = [ @@ -38,10 +39,10 @@ else: "LargeZipFile", ] -_DateTuple = tuple[int, int, int, int, int, int] -_ReadWriteMode = Literal["r", "w"] -_ReadWriteBinaryMode = Literal["r", "w", "rb", "wb"] -_ZipFileMode = Literal["r", "w", "x", "a"] +_DateTuple: TypeAlias = tuple[int, int, int, int, int, int] +_ReadWriteMode: TypeAlias = Literal["r", "w"] +_ReadWriteBinaryMode: TypeAlias = Literal["r", "w", "rb", "wb"] +_ZipFileMode: TypeAlias = Literal["r", "w", "x", "a"] class BadZipFile(Exception): ... diff --git a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi index d766eab6b7ef..0e898cb29740 100644 --- a/mypy/typeshed/stdlib/zoneinfo/__init__.pyi +++ b/mypy/typeshed/stdlib/zoneinfo/__init__.pyi @@ -1,6 +1,7 @@ from _typeshed import Self, StrPath +from collections.abc import Iterable, Sequence from datetime import tzinfo -from typing import Any, Iterable, Protocol, Sequence +from typing import Any, Protocol __all__ = ["ZoneInfo", "reset_tzpath", "available_timezones", "TZPATH", "ZoneInfoNotFoundError", "InvalidTZPathWarning"] diff --git a/test-data/unit/plugins/decimal_to_int.py b/test-data/unit/plugins/decimal_to_int.py index 21fd3b518b7d..597010f9a6d1 100644 --- a/test-data/unit/plugins/decimal_to_int.py +++ b/test-data/unit/plugins/decimal_to_int.py @@ -4,7 +4,7 @@ class MyPlugin(Plugin): def get_type_analyze_hook(self, fullname): - if fullname == "decimal.Decimal": + if fullname in ("decimal.Decimal", "_decimal.Decimal"): return decimal_to_int_hook return None From cb6581a8f1f41bf784eebb6a1d2c8a32fb9b43f0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 25 Apr 2022 19:12:28 +0100 Subject: [PATCH 28/79] Fix types of inherited attributes in generic dataclasses (#12656) Fixes #12633. --- mypy/plugins/dataclasses.py | 13 ++++--- test-data/unit/check-dataclasses.test | 51 +++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 18caed8bbb8e..5827a21e8ccf 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -19,6 +19,7 @@ get_proper_type, AnyType, TypeOfAny, ) from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state # The set of decorators that generate dataclasses. dataclass_makers: Final = { @@ -101,10 +102,8 @@ def deserialize( def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None: """Expands type vars in the context of a subtype when an attribute is inherited from a generic super type.""" - if not isinstance(self.type, TypeVarType): - return - - self.type = map_type_from_supertype(self.type, sub_type, self.info) + if self.type is not None: + self.type = map_type_from_supertype(self.type, sub_type, self.info) class DataclassTransformer: @@ -402,7 +401,11 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: name: str = data["name"] if name not in known_attrs: attr = DataclassAttribute.deserialize(info, data, ctx.api) - attr.expand_typevar_from_subtype(ctx.cls.info) + # TODO: We shouldn't be performing type operations during the main + # semantic analysis pass, since some TypeInfo attributes might + # still be in flux. This should be performed in a later phase. + with state.strict_optional_set(ctx.api.options.strict_optional): + attr.expand_typevar_from_subtype(ctx.cls.info) known_attrs.add(name) super_attrs.append(attr) elif all_attrs: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 594df103841c..bce1ee24a31a 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1568,3 +1568,54 @@ class B: class Derived(A, B): pass [builtins fixtures/dataclasses.pyi] + +[case testDataclassGenericInheritance2] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Any, Callable, Generic, TypeVar, List + +T = TypeVar("T") +S = TypeVar("S") + +@dataclass +class Parent(Generic[T]): + f: Callable[[T], Any] + +@dataclass +class Child(Parent[T]): ... + +class A: ... +def func(obj: A) -> bool: ... + +reveal_type(Child[A](func).f) # N: Revealed type is "def (__main__.A) -> Any" + +@dataclass +class Parent2(Generic[T]): + a: List[T] + +@dataclass +class Child2(Generic[T, S], Parent2[S]): + b: List[T] + +reveal_type(Child2([A()], [1]).a) # N: Revealed type is "builtins.list[__main__.A]" +reveal_type(Child2[int, A]([A()], [1]).b) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/dataclasses.pyi] + +[case testDataclassInheritOptionalType] +# flags: --python-version 3.7 --strict-optional +from dataclasses import dataclass +from typing import Any, Callable, Generic, TypeVar, List, Optional + +T = TypeVar("T") + +@dataclass +class Parent(Generic[T]): + x: Optional[str] +@dataclass +class Child(Parent): + y: Optional[int] +Child(x=1, y=1) # E: Argument "x" to "Child" has incompatible type "int"; expected "Optional[str]" +Child(x='', y='') # E: Argument "y" to "Child" has incompatible type "str"; expected "Optional[int]" +Child(x='', y=1) +Child(x=None, y=None) +[builtins fixtures/dataclasses.pyi] From ee78afe325800f9266ccc0d143b49b8c0b29d8a3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 27 Apr 2022 14:58:45 +0100 Subject: [PATCH 29/79] Various documentation updates (mostly about error codes) (#12680) Document some additional error codes and some general doc updates for the 0.950 release. --- docs/source/command_line.rst | 10 ++++---- docs/source/error_code_list.rst | 37 +++++++++++++++++++++++++++++ docs/source/error_code_list2.rst | 38 ++++++++++++++++++++++++++---- docs/source/error_codes.rst | 21 ++++++++++++----- docs/source/installed_packages.rst | 8 +++++++ 5 files changed, 100 insertions(+), 14 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 36c13910c21a..43c5444ed7be 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -616,6 +616,7 @@ of the above sections. .. option:: --disable-error-code This flag allows disabling one or multiple error codes globally. + See :ref:`error-codes` for more information. .. code-block:: python @@ -623,20 +624,21 @@ of the above sections. x = 'a string' x.trim() # error: "str" has no attribute "trim" [attr-defined] - # --disable-error-code attr-defined + # When using --disable-error-code attr-defined x = 'a string' x.trim() .. option:: --enable-error-code This flag allows enabling one or multiple error codes globally. + See :ref:`error-codes` for more information. - Note: This flag will override disabled error codes from the --disable-error-code - flag + Note: This flag will override disabled error codes from the + :option:`--disable-error-code ` flag. .. code-block:: python - # --disable-error-code attr-defined + # When using --disable-error-code attr-defined x = 'a string' x.trim() diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index e655cae3c45d..5c1f0bedb980 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -679,6 +679,43 @@ implementation. def func(value): pass # actual implementation +Check that coroutine return value is used [unused-coroutine] +------------------------------------------------------------ + +Mypy ensures that return values of async def functions are not +ignored, as this is usually a programming error, as the coroutine +won't be executed at the call site. + +.. code-block:: python + + async def f() -> None: + ... + + async def g() -> None: + f() # Error: missing await + await f() # OK + +You can work around this error by assigning the result to a temporary, +otherwise unused variable: + +.. code-block:: python + + _ = f() # No error + +Check types in assert_type [assert-type] +---------------------------------------- + +The inferred type for an expression passed to ``assert_type`` must match +the provided type. + +.. code-block:: python + + from typing_extensions import assert_type + + assert_type([1], list[int]) # OK + + assert_type([1], list[str]) # Error + Report syntax errors [syntax] ----------------------------- diff --git a/docs/source/error_code_list2.rst b/docs/source/error_code_list2.rst index c55643ad6181..3938669edafc 100644 --- a/docs/source/error_code_list2.rst +++ b/docs/source/error_code_list2.rst @@ -200,7 +200,7 @@ mypy generates an error if it thinks that an expression is redundant. .. code-block:: python - # mypy: enable-error-code redundant-expr + # Use "mypy --enable-error-code redundant-expr ..." def example(x: int) -> None: # Error: Left operand of "and" is always true [redundant-expr] @@ -222,7 +222,7 @@ since unless implemented by a sub-type, the expression will always evaluate to t .. code-block:: python - # mypy: enable-error-code truthy-bool + # Use "mypy --enable-error-code truthy-bool ..." class Foo: pass @@ -237,7 +237,8 @@ This check might falsely imply an error. For example, ``Iterable`` does not impl .. code-block:: python - # mypy: enable-error-code truthy-bool + # Use "mypy -enable-error-code truthy-bool ..." + from typing import Iterable def transform(items: Iterable[int]) -> Iterable[int]: @@ -270,7 +271,7 @@ Example: .. code-block:: python - # mypy: enable-error-code ignore-without-code + # Use "mypy --enable-error-code ignore-without-code ..." class Foo: def __init__(self, name: str) -> None: @@ -288,3 +289,32 @@ Example: # This line warns correctly about the typo in the attribute name # Error: "Foo" has no attribute "nme"; maybe "name"? f.nme = 42 # type: ignore[assignment] + +Check that awaitable return value is used [unused-awaitable] +------------------------------------------------------------ + +If you use :option:`--enable-error-code unused-awaitable `, +mypy generates an error if you don't use a returned value that defines ``__await__``. + +Example: + +.. code-block:: python + + # Use "mypy --enable-error-code unused-awaitable ..." + + import asyncio + + async def f() -> int: ... + + async def g() -> None: + # Error: Value of type "Task[int]" must be used + # Are you missing an await? + asyncio.create_task(f()) + +You can assign the value to a temporary, otherwise unused to variable to +silence the error: + +.. code-block:: python + + async def g() -> None: + _ = asyncio.create_task(f()) # No error diff --git a/docs/source/error_codes.rst b/docs/source/error_codes.rst index 08d56c59fba2..bed73abc379f 100644 --- a/docs/source/error_codes.rst +++ b/docs/source/error_codes.rst @@ -24,7 +24,7 @@ Displaying error codes ---------------------- Error codes are not displayed by default. Use :option:`--show-error-codes ` -or config `show_error_codes = True` to display error codes. Error codes are shown inside square brackets: +or config ``show_error_codes = True`` to display error codes. Error codes are shown inside square brackets: .. code-block:: text @@ -46,11 +46,8 @@ line. This can be used even if you have not configured mypy to show error codes. Currently it's only possible to disable arbitrary error codes on individual lines using this comment. -.. note:: - - There are command-line flags and config file settings for enabling - certain optional error codes, such as :option:`--disallow-untyped-defs `, - which enables the ``no-untyped-def`` error code. +You can also use :option:`--disable-error-code ` +to disable specific error codes globally. This example shows how to ignore an error about an imported name mypy thinks is undefined: @@ -60,3 +57,15 @@ thinks is undefined: # 'foo' is defined in 'foolib', even though mypy can't see the # definition. from foolib import foo # type: ignore[attr-defined] + + +Enabling specific error codes +----------------------------- + +There are command-line flags and config file settings for enabling +certain optional error codes, such as :option:`--disallow-untyped-defs `, +which enables the ``no-untyped-def`` error code. + +You can use :option:`--enable-error-code ` to +enable specific error codes that don't have a dedicated command-line +flag or config file setting. diff --git a/docs/source/installed_packages.rst b/docs/source/installed_packages.rst index 8db113e4ba9e..8e5bff04ea72 100644 --- a/docs/source/installed_packages.rst +++ b/docs/source/installed_packages.rst @@ -25,6 +25,14 @@ you can create such packages. :pep:`561` specifies how a package can declare that it supports type checking. +.. note:: + + New versions of stub packages often use type system features not + supported by older, and even fairly recent mypy versions. If you + pin to an older version of mypy (using ``requirements.txt``, for + example), it is recommended that you also pin the versions of all + your stub package dependencies. + Using installed packages with mypy (PEP 561) ******************************************** From 3460717a11576d84b5f83cd62282480416c240b2 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:03:22 -0600 Subject: [PATCH 30/79] Remove most mentions of type comments from docs (#12683) --- docs/source/cheat_sheet_py3.rst | 9 +---- docs/source/class_basics.rst | 13 ------- docs/source/command_line.rst | 4 +- docs/source/dynamic_typing.rst | 2 +- docs/source/generics.rst | 6 +-- docs/source/getting_started.rst | 3 -- docs/source/kinds_of_types.rst | 19 ++-------- docs/source/more_types.rst | 8 ++-- .../source/type_inference_and_annotations.rst | 38 ------------------- 9 files changed, 14 insertions(+), 88 deletions(-) diff --git a/docs/source/cheat_sheet_py3.rst b/docs/source/cheat_sheet_py3.rst index e14cde7d50df..b4847932db50 100644 --- a/docs/source/cheat_sheet_py3.rst +++ b/docs/source/cheat_sheet_py3.rst @@ -25,10 +25,6 @@ and we use it in most examples. # This is how you declare the type of a variable type in Python 3.6 age: int = 1 - # In Python 3.5 and earlier you can use a type comment instead - # (equivalent to the previous definition) - age = 1 # type: int - # You don't need to initialize a variable to annotate it a: int # Ok (no value at runtime until assigned) @@ -45,7 +41,7 @@ Built-in types .. code-block:: python - + from typing import List, Set, Dict, Tuple, Optional # For simple built-in types, just use the name of the type @@ -65,9 +61,6 @@ Built-in types x: List[int] = [1] x: Set[int] = {6, 7} - # Same as above, but with type comment syntax (Python 3.5 and earlier) - x = [1] # type: List[int] - # For mappings, we need the types of both keys and values x: dict[str, float] = {"field": 2.0} # Python 3.9+ x: Dict[str, float] = {"field": 2.0} diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index 3c12b4b06d9b..a7f57afee4e7 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -42,19 +42,6 @@ As in Python generally, a variable defined in the class body can be used as a class or an instance variable. (As discussed in the next section, you can override this with a :py:data:`~typing.ClassVar` annotation.) -Type comments work as well, if you need to support Python versions earlier -than 3.6: - -.. code-block:: python - - class A: - x = None # type: list[int] # Declare attribute 'x' of type list[int] - -Note that attribute definitions in the class body that use a type comment -are special: a ``None`` value is valid as the initializer, even though -the declared type is not optional. This should be used sparingly, as this can -result in ``None``-related runtime errors that mypy can't detect. - Similarly, you can give explicit types to instance variables defined in a method: diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 43c5444ed7be..1a35d81a7ee9 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -548,11 +548,11 @@ of the above sections. from typing import Optional a = None # Need type annotation here if using --local-partial-types - b = None # type: Optional[int] + b: Optional[int] = None class Foo: bar = None # Need type annotation here if using --local-partial-types - baz = None # type: Optional[int] + baz: Optional[int] = None def __init__(self) -> None: self.bar = 1 diff --git a/docs/source/dynamic_typing.rst b/docs/source/dynamic_typing.rst index add445009666..390bc52d9e2c 100644 --- a/docs/source/dynamic_typing.rst +++ b/docs/source/dynamic_typing.rst @@ -77,7 +77,7 @@ operations: o.foo() # Error! o + 2 # Error! open(o) # Error! - n = 1 # type: int + n: int = 1 n = o # Error! You can use different :ref:`type narrowing ` diff --git a/docs/source/generics.rst b/docs/source/generics.rst index 7730bd0e5c10..bf34c286d841 100644 --- a/docs/source/generics.rst +++ b/docs/source/generics.rst @@ -295,8 +295,8 @@ In this way, for example, you can typecheck chaining of setter methods: self.width = w return self - circle = Circle().set_scale(0.5).set_radius(2.7) # type: Circle - square = Square().set_scale(0.5).set_width(3.2) # type: Square + circle: Circle = Circle().set_scale(0.5).set_radius(2.7) + square: Square = Square().set_scale(0.5).set_width(3.2) Without using generic ``self``, the last two lines could not be type-checked properly. @@ -310,7 +310,7 @@ For class methods, you can also define generic ``cls``, using :py:class:`Type[T] T = TypeVar('T', bound='Friend') class Friend: - other = None # type: Friend + other: "Friend" = None @classmethod def make_pair(cls: Type[T]) -> tuple[T, T]: diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 124edd650d27..ee102ad8b639 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -317,9 +317,6 @@ syntax like so: # If you're using Python 3.6+ my_global_dict: Dict[int, float] = {} - # If you want compatibility with even older versions of Python - my_global_dict = {} # type: Dict[int, float] - .. _stubs-intro: Library stubs and typeshed diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index 51f54ab0e1e6..806dc571d45a 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -347,23 +347,13 @@ This also works for attributes defined within methods: def __init__(self) -> None: self.count: Optional[int] = None -As a special case, you can use a non-optional type when initializing an -attribute to ``None`` inside a class body *and* using a type comment, -since when using a type comment, an initializer is syntactically required, -and ``None`` is used as a dummy, placeholder initializer: +This is not a problem when using variable annotations, since no initial +value is needed: .. code-block:: python class Container: - items = None # type: list[str] # OK (only with type comment) - -This is not a problem when using variable annotations, since no initializer -is needed: - -.. code-block:: python - - class Container: - items: list[str] # No initializer + items: list[str] # No initial value Mypy generally uses the first assignment to a variable to infer the type of the variable. However, if you assign both a ``None`` @@ -421,9 +411,6 @@ the runtime with some limitations (see :ref:`runtime_troubles`). t2: int | None # equivalent to Optional[int] - # Usable in type comments - t3 = 42 # type: int | str - .. _no_strict_optional: Disabling strict optional checking diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index dd688cab7e17..c04cefe31191 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -120,7 +120,7 @@ implicitly casting from ``UserId`` where ``int`` is expected. Examples: name_by_id(42) # Fails type check name_by_id(UserId(42)) # OK - num = UserId(5) + 1 # type: int + num: int = UserId(5) + 1 :py:func:`NewType ` accepts exactly two arguments. The first argument must be a string literal containing the name of the new type and must equal the name of the variable to which the new @@ -985,7 +985,7 @@ dictionary value depends on the key: Movie = TypedDict('Movie', {'name': str, 'year': int}) - movie = {'name': 'Blade Runner', 'year': 1982} # type: Movie + movie: Movie = {'name': 'Blade Runner', 'year': 1982} ``Movie`` is a ``TypedDict`` type with two items: ``'name'`` (with type ``str``) and ``'year'`` (with type ``int``). Note that we used an explicit type @@ -1080,7 +1080,7 @@ keys. This will be flagged as an error: .. code-block:: python # Error: 'year' missing - toy_story = {'name': 'Toy Story'} # type: Movie + toy_story: Movie = {'name': 'Toy Story'} Sometimes you want to allow keys to be left out when creating a ``TypedDict`` object. You can provide the ``total=False`` argument to @@ -1090,7 +1090,7 @@ Sometimes you want to allow keys to be left out when creating a GuiOptions = TypedDict( 'GuiOptions', {'language': str, 'color': str}, total=False) - options = {} # type: GuiOptions # Okay + options: GuiOptions = {} # Okay options['language'] = 'en' You may need to use :py:meth:`~dict.get` to access items of a partial (non-total) diff --git a/docs/source/type_inference_and_annotations.rst b/docs/source/type_inference_and_annotations.rst index 8150f88e579e..040961d40a40 100644 --- a/docs/source/type_inference_and_annotations.rst +++ b/docs/source/type_inference_and_annotations.rst @@ -44,23 +44,6 @@ type: x: Union[int, str] = 1.1 # Error! -The variable annotation syntax is available starting from Python 3.6. -In earlier Python versions, you can use a special comment after an -assignment statement to declare the type of a variable: - -.. code-block:: python - - x = 1 # type: Union[int, str] - -We'll use both syntax variants in examples. The syntax variants are -mostly interchangeable, but the variable annotation syntax allows -defining the type of a variable without initialization, which is not -possible with the comment syntax: - -.. code-block:: python - - x: str # Declare type of 'x' without initialization - .. note:: The best way to think about this is that the type annotation sets the @@ -182,27 +165,6 @@ Working around the issue is easy by adding a type annotation: a: list[int] = [] # OK foo(a) -Declaring multiple variable types at a time -******************************************* - -You can declare more than a single variable at a time, but only with -a type comment. In order to nicely work with multiple assignment, you -must give each variable a type separately: - -.. code-block:: python - - i, found = 0, False # type: int, bool - -You can optionally use parentheses around the types, assignment targets -and assigned expression: - -.. code-block:: python - - i, found = 0, False # type: (int, bool) # OK - (i, found) = 0, False # type: int, bool # OK - i, found = (0, False) # type: int, bool # OK - (i, found) = (0, False) # type: (int, bool) # OK - Starred expressions ******************* From a3abd3644c5f2c84d2fa65fb621327a6c6a0e695 Mon Sep 17 00:00:00 2001 From: Hugues Date: Fri, 29 Apr 2022 07:21:22 -0700 Subject: [PATCH 31/79] errors: speedup for large error counts and improve error filtering (#12631) * errors: speedup for large error counts We have a legacy codebase with many errors across many files ``` Found 7995 errors in 2218 files (checked 21364 source files) ``` For historical reasons, it hasn't been practical to fix all of these yet, and we've been slowly chipping at them over time. Profiling shows that `is_blockers` is the biggest single hotspot, taking roughly 1min, and `total_errors` account for another 11s. Instead of computing those values on read by iterating over all errors, update auxiliary variables appropriately every time a new error is recorded. * errors: unify and optimize error filtering Instead of maintaining two separate mechanism to filter out errors (boolean flag in MessageBuilder and explicit copy of MessageBuilder/Errors) expand ErrorWatcher to support all relevant usage patterns and update all usages accordingly. This is both cleaner and more robust than the previous approach, and should also offer a slight performance improvement by reducing allocations. --- mypy/checker.py | 51 +++++------ mypy/checkexpr.py | 188 +++++++++++++++++++---------------------- mypy/checkmember.py | 17 ++-- mypy/checkpattern.py | 77 ++++++++--------- mypy/checkstrformat.py | 4 +- mypy/errors.py | 105 ++++++++++++++++++++--- mypy/messages.py | 75 +++++----------- 7 files changed, 277 insertions(+), 240 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 24f101421ff4..002f28d4db6c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2196,7 +2196,7 @@ def is_raising_or_empty(self, s: Statement) -> bool: if isinstance(s.expr, EllipsisExpr): return True elif isinstance(s.expr, CallExpr): - with self.expr_checker.msg.disable_errors(): + with self.expr_checker.msg.filter_errors(): typ = get_proper_type(self.expr_checker.accept( s.expr, allow_none_return=True, always_allow_any=True)) @@ -3377,7 +3377,7 @@ def check_member_assignment(self, instance_type: Type, attribute_type: Type, # For non-overloaded setters, the result should be type-checked like a regular assignment. # Hence, we first only try to infer the type by using the rvalue as type context. type_context = rvalue - with self.msg.disable_errors(): + with self.msg.filter_errors(): _, inferred_dunder_set_type = self.expr_checker.check_call( dunder_set_type, [TempNode(instance_type, context=context), type_context], @@ -4312,9 +4312,6 @@ def _make_fake_typeinfo_and_full_name( ) return info_, full_name_ - old_msg = self.msg - new_msg = old_msg.clean_copy() - self.msg = new_msg base_classes = _get_base_classes(instances) # We use the pretty_names_list for error messages but can't # use it for the real name that goes into the symbol table @@ -4322,24 +4319,22 @@ def _make_fake_typeinfo_and_full_name( pretty_names_list = pretty_seq(format_type_distinctly(*base_classes, bare=True), "and") try: info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) - self.check_multiple_inheritance(info) - if new_msg.is_errors(): + with self.msg.filter_errors() as local_errors: + self.check_multiple_inheritance(info) + if local_errors.has_new_errors(): # "class A(B, C)" unsafe, now check "class A(C, B)": - new_msg = new_msg.clean_copy() - self.msg = new_msg base_classes = _get_base_classes(instances[::-1]) info, full_name = _make_fake_typeinfo_and_full_name(base_classes, curr_module) - self.check_multiple_inheritance(info) + with self.msg.filter_errors() as local_errors: + self.check_multiple_inheritance(info) info.is_intersection = True except MroError: if self.should_report_unreachable_issues(): - old_msg.impossible_intersection( + self.msg.impossible_intersection( pretty_names_list, "inconsistent method resolution order", ctx) return None - finally: - self.msg = old_msg - if new_msg.is_errors(): + if local_errors.has_new_errors(): if self.should_report_unreachable_issues(): self.msg.impossible_intersection( pretty_names_list, "incompatible method signatures", ctx) @@ -4974,20 +4969,20 @@ def refine_parent_types(self, member_name = expr.name def replay_lookup(new_parent_type: ProperType) -> Optional[Type]: - msg_copy = self.msg.clean_copy() - member_type = analyze_member_access( - name=member_name, - typ=new_parent_type, - context=parent_expr, - is_lvalue=False, - is_super=False, - is_operator=False, - msg=msg_copy, - original_type=new_parent_type, - chk=self, - in_literal_context=False, - ) - if msg_copy.is_errors(): + with self.msg.filter_errors() as w: + member_type = analyze_member_access( + name=member_name, + typ=new_parent_type, + context=parent_expr, + is_lvalue=False, + is_super=False, + is_operator=False, + msg=self.msg, + original_type=new_parent_type, + chk=self, + in_literal_context=False, + ) + if w.has_new_errors(): return None else: return member_type diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c6f4d24c1815..f6aef2e361cc 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1,6 +1,6 @@ """Expression type checker. This file is conceptually part of TypeChecker.""" -from mypy.backports import OrderedDict, nullcontext +from mypy.backports import OrderedDict from contextlib import contextmanager import itertools from typing import ( @@ -8,7 +8,7 @@ ) from typing_extensions import ClassVar, Final, overload, TypeAlias as _TypeAlias -from mypy.errors import report_internal_error +from mypy.errors import report_internal_error, ErrorWatcher from mypy.typeanal import ( has_any_from_unimported_type, check_for_explicit_any, set_any_tvars, expand_type_alias, make_optional_type, @@ -887,7 +887,7 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str res: List[Type] = [] for typ in object_type.relevant_items(): # Member access errors are already reported when visiting the member expression. - with self.msg.disable_errors(): + with self.msg.filter_errors(): item = analyze_member_access(member, typ, e, False, False, False, self.msg, original_type=object_type, chk=self.chk, in_literal_context=self.is_literal_context(), @@ -1244,7 +1244,7 @@ def infer_function_type_arguments(self, callee_type: CallableType, # due to partial available context information at this time, but # these errors can be safely ignored as the arguments will be # inferred again later. - with self.msg.disable_errors(): + with self.msg.filter_errors(): arg_types = self.infer_arg_types_in_context( callee_type, args, arg_kinds, formal_to_actual) @@ -1594,13 +1594,13 @@ def check_overload_call(self, unioned_result: Optional[Tuple[Type, Type]] = None union_interrupted = False # did we try all union combinations? if any(self.real_union(arg) for arg in arg_types): - unioned_errors = arg_messages.clean_copy() try: - unioned_return = self.union_overload_result(plausible_targets, args, - arg_types, arg_kinds, arg_names, - callable_name, object_type, - context, - arg_messages=unioned_errors) + with arg_messages.filter_errors(): + unioned_return = self.union_overload_result(plausible_targets, args, + arg_types, arg_kinds, arg_names, + callable_name, object_type, + context, + arg_messages=arg_messages) except TooManyUnions: union_interrupted = True else: @@ -1751,29 +1751,18 @@ def infer_overload_return_type(self, args_contain_any = any(map(has_any_type, arg_types)) for typ in plausible_targets: - overload_messages = self.msg.clean_copy() - prev_messages = self.msg assert self.msg is self.chk.msg - self.msg = overload_messages - self.chk.msg = overload_messages - try: - # Passing `overload_messages` as the `arg_messages` parameter doesn't - # seem to reliably catch all possible errors. - # TODO: Figure out why + with self.msg.filter_errors() as w: ret_type, infer_type = self.check_call( callee=typ, args=args, arg_kinds=arg_kinds, arg_names=arg_names, context=context, - arg_messages=overload_messages, + arg_messages=self.msg, callable_name=callable_name, object_type=object_type) - finally: - self.chk.msg = prev_messages - self.msg = prev_messages - - is_match = not overload_messages.is_errors() + is_match = not w.has_new_errors() if is_match: # Return early if possible; otherwise record info so we can # check for ambiguity due to 'Any' below. @@ -2254,15 +2243,15 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: # Keep track of whether we get type check errors (these won't be reported, they # are just to verify whether something is valid typing wise). - local_errors = self.msg.clean_copy() - _, method_type = self.check_method_call_by_name( - method='__contains__', - base_type=right_type, - args=[left], - arg_kinds=[ARG_POS], - context=e, - local_errors=local_errors, - ) + with self.msg.filter_errors(save_filtered_errors=True) as local_errors: + _, method_type = self.check_method_call_by_name( + method='__contains__', + base_type=right_type, + args=[left], + arg_kinds=[ARG_POS], + context=e, + ) + sub_result = self.bool_type() # Container item type for strict type overlap checks. Note: we need to only # check for nominal type, because a usual "Unsupported operands for in" @@ -2272,7 +2261,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: if isinstance(right_type, PartialType): # We don't really know if this is an error or not, so just shut up. pass - elif (local_errors.is_errors() and + elif (local_errors.has_new_errors() and # is_valid_var_arg is True for any Iterable self.is_valid_var_arg(right_type)): _, itertype = self.chk.analyze_iterable_item_type(right) @@ -2285,20 +2274,22 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: if not is_subtype(left_type, itertype): self.msg.unsupported_operand_types('in', left_type, right_type, e) # Only show dangerous overlap if there are no other errors. - elif (not local_errors.is_errors() and cont_type and + elif (not local_errors.has_new_errors() and cont_type and self.dangerous_comparison(left_type, cont_type, original_container=right_type)): self.msg.dangerous_comparison(left_type, cont_type, 'container', e) else: - self.msg.add_errors(local_errors) + self.msg.add_errors(local_errors.filtered_errors()) elif operator in operators.op_methods: method = self.get_operator_method(operator) - err_count = self.msg.errors.total_errors() - sub_result, method_type = self.check_op(method, left_type, right, e, - allow_reverse=True) + + with ErrorWatcher(self.msg.errors) as w: + sub_result, method_type = self.check_op(method, left_type, right, e, + allow_reverse=True) + # Only show dangerous overlap if there are no other errors. See # testCustomEqCheckStrictEquality for an example. - if self.msg.errors.total_errors() == err_count and operator in ('==', '!='): + if not w.has_new_errors() and operator in ('==', '!='): right_type = self.accept(right) # We suppress the error if there is a custom __eq__() method on either # side. User defined (or even standard library) classes can define this @@ -2525,24 +2516,20 @@ def lookup_operator(op_name: str, base_type: Type) -> Optional[Type]: if not self.has_member(base_type, op_name): return None - local_errors = msg.clean_copy() - - member = analyze_member_access( - name=op_name, - typ=base_type, - is_lvalue=False, - is_super=False, - is_operator=True, - original_type=base_type, - context=context, - msg=local_errors, - chk=self.chk, - in_literal_context=self.is_literal_context() - ) - if local_errors.is_errors(): - return None - else: - return member + with self.msg.filter_errors() as w: + member = analyze_member_access( + name=op_name, + typ=base_type, + is_lvalue=False, + is_super=False, + is_operator=True, + original_type=base_type, + context=context, + msg=self.msg, + chk=self.chk, + in_literal_context=self.is_literal_context() + ) + return None if w.has_new_errors() else member def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]: """Returns the name of the class that contains the actual definition of attr_name. @@ -2656,11 +2643,11 @@ def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]: errors = [] results = [] for method, obj, arg in variants: - local_errors = msg.clean_copy() - result = self.check_method_call( - op_name, obj, method, [arg], [ARG_POS], context, local_errors) - if local_errors.is_errors(): - errors.append(local_errors) + with self.msg.filter_errors(save_filtered_errors=True) as local_errors: + result = self.check_method_call( + op_name, obj, method, [arg], [ARG_POS], context) + if local_errors.has_new_errors(): + errors.append(local_errors.filtered_errors()) results.append(result) else: return result @@ -2678,12 +2665,12 @@ def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]: # call the __op__ method (even though it's missing). if not variants: - local_errors = msg.clean_copy() - result = self.check_method_call_by_name( - op_name, left_type, [right_expr], [ARG_POS], context, local_errors) + with self.msg.filter_errors(save_filtered_errors=True) as local_errors: + result = self.check_method_call_by_name( + op_name, left_type, [right_expr], [ARG_POS], context) - if local_errors.is_errors(): - errors.append(local_errors) + if local_errors.has_new_errors(): + errors.append(local_errors.filtered_errors()) results.append(result) else: # In theory, we should never enter this case, but it seems @@ -2724,23 +2711,23 @@ def check_op(self, method: str, base_type: Type, # Step 1: We first try leaving the right arguments alone and destructure # just the left ones. (Mypy can sometimes perform some more precise inference # if we leave the right operands a union -- see testOperatorWithEmptyListAndSum.) - msg = self.msg.clean_copy() all_results = [] all_inferred = [] - for left_possible_type in left_variants: - result, inferred = self.check_op_reversible( - op_name=method, - left_type=left_possible_type, - left_expr=TempNode(left_possible_type, context=context), - right_type=right_type, - right_expr=arg, - context=context, - msg=msg) - all_results.append(result) - all_inferred.append(inferred) + with self.msg.filter_errors() as local_errors: + for left_possible_type in left_variants: + result, inferred = self.check_op_reversible( + op_name=method, + left_type=left_possible_type, + left_expr=TempNode(left_possible_type, context=context), + right_type=right_type, + right_expr=arg, + context=context, + msg=self.msg) + all_results.append(result) + all_inferred.append(inferred) - if not msg.is_errors(): + if not local_errors.has_new_errors(): results_final = make_simplified_union(all_results) inferred_final = make_simplified_union(all_inferred) return results_final, inferred_final @@ -2765,27 +2752,30 @@ def check_op(self, method: str, base_type: Type, handle_type_alias_type=True) ] - msg = self.msg.clean_copy() all_results = [] all_inferred = [] - for left_possible_type in left_variants: - for right_possible_type, right_expr in right_variants: - result, inferred = self.check_op_reversible( - op_name=method, - left_type=left_possible_type, - left_expr=TempNode(left_possible_type, context=context), - right_type=right_possible_type, - right_expr=right_expr, - context=context, - msg=msg) - all_results.append(result) - all_inferred.append(inferred) - - if msg.is_errors(): - self.msg.add_errors(msg) + with self.msg.filter_errors(save_filtered_errors=True) as local_errors: + for left_possible_type in left_variants: + for right_possible_type, right_expr in right_variants: + result, inferred = self.check_op_reversible( + op_name=method, + left_type=left_possible_type, + left_expr=TempNode(left_possible_type, context=context), + right_type=right_possible_type, + right_expr=right_expr, + context=context, + msg=self.msg) + all_results.append(result) + all_inferred.append(inferred) + + if local_errors.has_new_errors(): + self.msg.add_errors(local_errors.filtered_errors()) # Point any notes to the same location as an existing message. - recent_context = msg.most_recent_context() + err = local_errors.filtered_errors()[-1] + recent_context = TempNode(NoneType()) + recent_context.line = err.line + recent_context.column = err.column if len(left_variants) >= 2 and len(right_variants) >= 2: self.msg.warn_both_operands_are_from_unions(recent_context) elif len(left_variants) >= 2: @@ -2864,7 +2854,7 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: # If right_map is None then we know mypy considers the right branch # to be unreachable and therefore any errors found in the right branch # should be suppressed. - with (self.msg.disable_errors() if right_map is None else nullcontext()): + with self.msg.filter_errors(filter_errors=right_map is None): right_type = self.analyze_cond_branch(right_map, e.right, expanded_left_type) if left_map is None and right_map is None: diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 16bd0e074c3f..2659ad18ed6e 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -273,13 +273,11 @@ def analyze_type_type_member_access(name: str, # Similar to analyze_type_callable_attribute_access. item = None fallback = mx.named_type('builtins.type') - ignore_messages = mx.msg.copy() - ignore_messages.disable_errors().__enter__() if isinstance(typ.item, Instance): item = typ.item elif isinstance(typ.item, AnyType): - mx = mx.copy_modified(messages=ignore_messages) - return _analyze_member_access(name, fallback, mx, override_info) + with mx.msg.filter_errors(): + return _analyze_member_access(name, fallback, mx, override_info) elif isinstance(typ.item, TypeVarType): upper_bound = get_proper_type(typ.item.upper_bound) if isinstance(upper_bound, Instance): @@ -287,8 +285,8 @@ def analyze_type_type_member_access(name: str, elif isinstance(upper_bound, TupleType): item = tuple_fallback(upper_bound) elif isinstance(upper_bound, AnyType): - mx = mx.copy_modified(messages=ignore_messages) - return _analyze_member_access(name, fallback, mx, override_info) + with mx.msg.filter_errors(): + return _analyze_member_access(name, fallback, mx, override_info) elif isinstance(typ.item, TupleType): item = tuple_fallback(typ.item) elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj(): @@ -297,6 +295,7 @@ def analyze_type_type_member_access(name: str, # Access member on metaclass object via Type[Type[C]] if isinstance(typ.item.item, Instance): item = typ.item.item.type.metaclass_type + ignore_messages = False if item and not mx.is_operator: # See comment above for why operators are skipped result = analyze_class_attribute_access(item, name, mx, override_info) @@ -305,10 +304,12 @@ def analyze_type_type_member_access(name: str, return result else: # We don't want errors on metaclass lookup for classes with Any fallback - mx = mx.copy_modified(messages=ignore_messages) + ignore_messages = True if item is not None: fallback = item.type.metaclass_type or fallback - return _analyze_member_access(name, fallback, mx, override_info) + + with mx.msg.filter_errors(filter_errors=ignore_messages): + return _analyze_member_access(name, fallback, mx, override_info) def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> Type: diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index e1d4f9fe285e..84b9acae1aa2 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -416,29 +416,29 @@ def get_mapping_item_type(self, mapping_type: Type, key: Expression ) -> Optional[Type]: - local_errors = self.msg.clean_copy() - local_errors.disable_count = 0 mapping_type = get_proper_type(mapping_type) if isinstance(mapping_type, TypedDictType): - result: Optional[Type] = self.chk.expr_checker.visit_typeddict_index_expr( - mapping_type, key, local_errors=local_errors) + with self.msg.filter_errors() as local_errors: + result: Optional[Type] = self.chk.expr_checker.visit_typeddict_index_expr( + mapping_type, key) + has_local_errors = local_errors.has_new_errors() # If we can't determine the type statically fall back to treating it as a normal # mapping - if local_errors.is_errors(): - local_errors = self.msg.clean_copy() - local_errors.disable_count = 0 + if has_local_errors: + with self.msg.filter_errors() as local_errors: + result = self.get_simple_mapping_item_type(pattern, + mapping_type, + key, + self.msg) + + if local_errors.has_new_errors(): + result = None + else: + with self.msg.filter_errors(): result = self.get_simple_mapping_item_type(pattern, mapping_type, key, - local_errors) - - if local_errors.is_errors(): - result = None - else: - result = self.get_simple_mapping_item_type(pattern, - mapping_type, - key, - local_errors) + self.msg) return result def get_simple_mapping_item_type(self, @@ -507,14 +507,14 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: pattern_type.captures) captures = pattern_type.captures else: - local_errors = self.msg.clean_copy() - match_args_type = analyze_member_access("__match_args__", typ, o, - False, False, False, - local_errors, - original_type=typ, - chk=self.chk) - - if local_errors.is_errors(): + with self.msg.filter_errors() as local_errors: + match_args_type = analyze_member_access("__match_args__", typ, o, + False, False, False, + self.msg, + original_type=typ, + chk=self.chk) + has_local_errors = local_errors.has_new_errors() + if has_local_errors: self.msg.fail(message_registry.MISSING_MATCH_ARGS.format(typ), o) return self.early_non_match() @@ -561,20 +561,21 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: can_match = True for keyword, pattern in keyword_pairs: key_type: Optional[Type] = None - local_errors = self.msg.clean_copy() - if keyword is not None: - key_type = analyze_member_access(keyword, - narrowed_type, - pattern, - False, - False, - False, - local_errors, - original_type=new_type, - chk=self.chk) - else: - key_type = AnyType(TypeOfAny.from_error) - if local_errors.is_errors() or key_type is None: + with self.msg.filter_errors() as local_errors: + if keyword is not None: + key_type = analyze_member_access(keyword, + narrowed_type, + pattern, + False, + False, + False, + self.msg, + original_type=new_type, + chk=self.chk) + else: + key_type = AnyType(TypeOfAny.from_error) + has_local_errors = local_errors.has_new_errors() + if has_local_errors or key_type is None: key_type = AnyType(TypeOfAny.from_error) self.msg.fail(message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format(typ, keyword), pattern) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index dcb711150870..995f3073ba79 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -17,6 +17,7 @@ ) from typing_extensions import Final, TYPE_CHECKING, TypeAlias as _TypeAlias +from mypy.errors import Errors from mypy.types import ( Type, AnyType, TupleType, Instance, UnionType, TypeOfAny, get_proper_type, TypeVarType, LiteralType, get_proper_types @@ -512,8 +513,7 @@ def apply_field_accessors(self, spec: ConversionSpecifier, repl: Expression, return repl assert spec.field - # This is a bit of a dirty trick, but it looks like this is the simplest way. - temp_errors = self.msg.clean_copy().errors + temp_errors = Errors() dummy = DUMMY_FIELD_NAME + spec.field[len(spec.key):] temp_ast: Node = parse( dummy, fnam="", module=None, options=self.chk.options, errors=temp_errors diff --git a/mypy/errors.py b/mypy/errors.py index 20e43fa810f9..6dc0a21e7904 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -4,8 +4,8 @@ from mypy.backports import OrderedDict from collections import defaultdict -from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable -from typing_extensions import Final +from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable, Union +from typing_extensions import Final, Literal from mypy.scope import Scope from mypy.options import Options @@ -122,6 +122,58 @@ def __init__(self, Optional[ErrorCode]] +class ErrorWatcher: + """Context manager that can be used to keep track of new errors recorded + around a given operation. + + Errors maintain a stack of such watchers. The handler is called starting + at the top of the stack, and is propagated down the stack unless filtered + out by one of the ErrorWatcher instances. + """ + def __init__(self, errors: 'Errors', *, + filter_errors: Union[bool, Callable[[str, ErrorInfo], bool]] = False, + save_filtered_errors: bool = False): + self.errors = errors + self._has_new_errors = False + self._filter = filter_errors + self._filtered: Optional[List[ErrorInfo]] = [] if save_filtered_errors else None + + def __enter__(self) -> 'ErrorWatcher': + self.errors._watchers.append(self) + return self + + def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> Literal[False]: + assert self == self.errors._watchers.pop() + return False + + def on_error(self, file: str, info: ErrorInfo) -> bool: + """Handler called when a new error is recorded. + + The default implementation just sets the has_new_errors flag + + Return True to filter out the error, preventing it from being seen by other + ErrorWatcher further down the stack and from being recorded by Errors + """ + self._has_new_errors = True + if isinstance(self._filter, bool): + should_filter = self._filter + elif callable(self._filter): + should_filter = self._filter(file, info) + else: + raise AssertionError("invalid error filter: {}".format(type(self._filter))) + if should_filter and self._filtered is not None: + self._filtered.append(info) + + return should_filter + + def has_new_errors(self) -> bool: + return self._has_new_errors + + def filtered_errors(self) -> List[ErrorInfo]: + assert self._filtered is not None + return self._filtered + + class Errors: """Container for compile errors. @@ -134,6 +186,9 @@ class Errors: # files were processed. error_info_map: Dict[str, List[ErrorInfo]] + # optimization for legacy codebases with many files with errors + has_blockers: Set[str] + # Files that we have reported the errors for flushed_files: Set[str] @@ -178,6 +233,8 @@ class Errors: # in some cases to avoid reporting huge numbers of errors. seen_import_error = False + _watchers: List[ErrorWatcher] = [] + def __init__(self, show_error_context: bool = False, show_column_numbers: bool = False, @@ -209,6 +266,7 @@ def initialize(self) -> None: self.used_ignored_lines = defaultdict(lambda: defaultdict(list)) self.ignored_files = set() self.only_once_messages = set() + self.has_blockers = set() self.scope = None self.target_module = None self.seen_import_error = False @@ -234,9 +292,6 @@ def copy(self) -> 'Errors': new.seen_import_error = self.seen_import_error return new - def total_errors(self) -> int: - return sum(len(errs) for errs in self.error_info_map.values()) - def set_ignore_prefix(self, prefix: str) -> None: """Set path prefix that will be removed from all paths.""" prefix = os.path.normpath(prefix) @@ -354,14 +409,39 @@ def report(self, def _add_error_info(self, file: str, info: ErrorInfo) -> None: assert file not in self.flushed_files + # process the stack of ErrorWatchers before modifying any internal state + # in case we need to filter out the error entirely + if self._filter_error(file, info): + return if file not in self.error_info_map: self.error_info_map[file] = [] self.error_info_map[file].append(info) + if info.blocker: + self.has_blockers.add(file) if info.code is IMPORT: self.seen_import_error = True + def _filter_error(self, file: str, info: ErrorInfo) -> bool: + """ + process ErrorWatcher stack from top to bottom, + stopping early if error needs to be filtered out + """ + i = len(self._watchers) + while i > 0: + i -= 1 + w = self._watchers[i] + if w.on_error(file, info): + return True + return False + def add_error_info(self, info: ErrorInfo) -> None: file, line, end_line = info.origin + # process the stack of ErrorWatchers before modifying any internal state + # in case we need to filter out the error entirely + # NB: we need to do this both here and in _add_error_info, otherwise we + # might incorrectly update the sets of ignored or only_once messages + if self._filter_error(file, info): + return if not info.blocker: # Blockers cannot be ignored if file in self.ignored_lines: # It's okay if end_line is *before* line. @@ -476,12 +556,16 @@ def clear_errors_in_targets(self, path: str, targets: Set[str]) -> None: """Remove errors in specific fine-grained targets within a file.""" if path in self.error_info_map: new_errors = [] + has_blocker = False for info in self.error_info_map[path]: if info.target not in targets: new_errors.append(info) + has_blocker |= info.blocker elif info.only_once: self.only_once_messages.remove(info.message) self.error_info_map[path] = new_errors + if not has_blocker and path in self.has_blockers: + self.has_blockers.remove(path) def generate_unused_ignore_errors(self, file: str) -> None: ignored_lines = self.ignored_lines[file] @@ -551,19 +635,14 @@ def is_errors(self) -> bool: """Are there any generated messages?""" return bool(self.error_info_map) - def is_real_errors(self) -> bool: - """Are there any generated errors (not just notes, for example)?""" - return any(info.severity == 'error' - for infos in self.error_info_map.values() for info in infos) - def is_blockers(self) -> bool: """Are the any errors that are blockers?""" - return any(err for errs in self.error_info_map.values() for err in errs if err.blocker) + return bool(self.has_blockers) def blocker_module(self) -> Optional[str]: """Return the module with a blocking error, or None if not possible.""" - for errs in self.error_info_map.values(): - for err in errs: + for path in self.has_blockers: + for err in self.error_info_map[path]: if err.blocker: return err.module return None diff --git a/mypy/messages.py b/mypy/messages.py index 23ab172f5499..d499a574d527 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -21,7 +21,7 @@ from typing_extensions import Final from mypy.erasetype import erase_type -from mypy.errors import Errors +from mypy.errors import Errors, ErrorWatcher, ErrorInfo from mypy.types import ( Type, CallableType, Instance, TypeVarType, TupleType, TypedDictType, LiteralType, UnionType, NoneType, AnyType, Overloaded, FunctionLike, DeletedType, TypeType, @@ -33,7 +33,7 @@ TypeInfo, Context, MypyFile, FuncDef, reverse_builtin_aliases, ArgKind, ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2, ReturnStmt, NameExpr, Var, CONTRAVARIANT, COVARIANT, SymbolNode, - CallExpr, IndexExpr, StrExpr, SymbolTable, TempNode, SYMBOL_FUNCBASE_TYPES + CallExpr, IndexExpr, StrExpr, SymbolTable, SYMBOL_FUNCBASE_TYPES ) from mypy.operators import op_methods, op_methods_to_symbols from mypy.subtypes import ( @@ -106,66 +106,38 @@ class MessageBuilder: modules: Dict[str, MypyFile] - # Number of times errors have been disabled. - disable_count = 0 - # Hack to deduplicate error messages from union types - disable_type_names_count = 0 + _disable_type_names: List[bool] def __init__(self, errors: Errors, modules: Dict[str, MypyFile]) -> None: self.errors = errors self.modules = modules - self.disable_count = 0 - self.disable_type_names_count = 0 + self._disable_type_names = [] # # Helpers # - def copy(self) -> 'MessageBuilder': - new = MessageBuilder(self.errors.copy(), self.modules) - new.disable_count = self.disable_count - new.disable_type_names_count = self.disable_type_names_count - return new - - def clean_copy(self) -> 'MessageBuilder': - errors = self.errors.copy() - errors.error_info_map = OrderedDict() - return MessageBuilder(errors, self.modules) + def filter_errors(self, *, filter_errors: bool = True, + save_filtered_errors: bool = False) -> ErrorWatcher: + return ErrorWatcher(self.errors, filter_errors=filter_errors, + save_filtered_errors=save_filtered_errors) - def add_errors(self, messages: 'MessageBuilder') -> None: + def add_errors(self, errors: List[ErrorInfo]) -> None: """Add errors in messages to this builder.""" - if self.disable_count <= 0: - for errs in messages.errors.error_info_map.values(): - for info in errs: - self.errors.add_error_info(info) - - @contextmanager - def disable_errors(self) -> Iterator[None]: - self.disable_count += 1 - try: - yield - finally: - self.disable_count -= 1 + for info in errors: + self.errors.add_error_info(info) @contextmanager def disable_type_names(self) -> Iterator[None]: - self.disable_type_names_count += 1 + self._disable_type_names.append(True) try: yield finally: - self.disable_type_names_count -= 1 - - def is_errors(self) -> bool: - return self.errors.is_errors() + self._disable_type_names.pop() - def most_recent_context(self) -> Context: - """Return a dummy context matching the most recent generated error in current file.""" - line, column = self.errors.most_recent_error_location() - node = TempNode(NoneType()) - node.line = line - node.column = column - return node + def are_type_names_disabled(self) -> bool: + return len(self._disable_type_names) > 0 and self._disable_type_names[-1] def report(self, msg: str, @@ -184,12 +156,11 @@ def report(self, end_line = context.end_line else: end_line = None - if self.disable_count <= 0: - self.errors.report(context.get_line() if context else -1, - context.get_column() if context else -1, - msg, severity=severity, file=file, offset=offset, - origin_line=origin.get_line() if origin else None, - end_line=end_line, code=code, allow_dups=allow_dups) + self.errors.report(context.get_line() if context else -1, + context.get_column() if context else -1, + msg, severity=severity, file=file, offset=offset, + origin_line=origin.get_line() if origin else None, + end_line=end_line, code=code, allow_dups=allow_dups) def fail(self, msg: str, @@ -309,7 +280,7 @@ def has_no_attr(self, extra = ' (not iterable)' elif member == '__aiter__': extra = ' (not async iterable)' - if not self.disable_type_names_count: + if not self.are_type_names_disabled(): failed = False if isinstance(original_type, Instance) and original_type.type.names: alternatives = set(original_type.type.names.keys()) @@ -391,7 +362,7 @@ def unsupported_operand_types(self, else: right_str = format_type(right_type) - if self.disable_type_names_count: + if self.are_type_names_disabled(): msg = 'Unsupported operand types for {} (likely involving Union)'.format(op) else: msg = 'Unsupported operand types for {} ({} and {})'.format( @@ -400,7 +371,7 @@ def unsupported_operand_types(self, def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None: - if self.disable_type_names_count: + if self.are_type_names_disabled(): msg = 'Unsupported left operand type for {} (some union)'.format(op) else: msg = 'Unsupported left operand type for {} ({})'.format( From d64efcd555fc7782592c213472cfa77bfcc2f11c Mon Sep 17 00:00:00 2001 From: jhance Date: Fri, 29 Apr 2022 08:24:55 -0700 Subject: [PATCH 32/79] Implement basic typevartuple constraints/inference (#12688) Implements basic inference for TypeVarTuple along with various check tests verifying uses of TypeVarTuple with functions works reasonably. --- mypy/applytype.py | 5 ++- mypy/constraints.py | 21 +++++++++ mypy/expandtype.py | 57 +++++++++++++++++++++++-- mypy/subtypes.py | 8 +++- mypy/test/testcheck.py | 2 + mypy/typeanal.py | 2 - mypy/types.py | 6 +++ test-data/unit/check-typevar-tuple.test | 29 +++++++++++++ 8 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 test-data/unit/check-typevar-tuple.test diff --git a/mypy/applytype.py b/mypy/applytype.py index a967d834f1a2..51a10c7084cf 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -5,7 +5,8 @@ from mypy.expandtype import expand_type from mypy.types import ( Type, TypeVarId, TypeVarType, CallableType, AnyType, PartialType, get_proper_types, - TypeVarLikeType, ProperType, ParamSpecType, Parameters, get_proper_type + TypeVarLikeType, ProperType, ParamSpecType, Parameters, get_proper_type, + TypeVarTupleType, ) from mypy.nodes import Context @@ -20,6 +21,8 @@ def get_target_type( ) -> Optional[Type]: if isinstance(tvar, ParamSpecType): return type + if isinstance(tvar, TypeVarTupleType): + return type assert isinstance(tvar, TypeVarType) values = get_proper_types(tvar.values) if values: diff --git a/mypy/constraints.py b/mypy/constraints.py index 06feddc0d3ce..1d9ca8b138ed 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -694,6 +694,27 @@ def infer_against_overloaded(self, overloaded: Overloaded, def visit_tuple_type(self, template: TupleType) -> List[Constraint]: actual = self.actual + # TODO: Support other items in the tuple besides Unpack + # TODO: Support subclasses of Tuple + is_varlength_tuple = ( + isinstance(actual, Instance) + and actual.type.fullname == "builtins.tuple" + ) + if len(template.items) == 1: + item = get_proper_type(template.items[0]) + if isinstance(item, UnpackType): + unpacked_type = get_proper_type(item.type) + if isinstance(unpacked_type, TypeVarTupleType): + if ( + isinstance(actual, (TupleType, AnyType)) + or is_varlength_tuple + ): + return [Constraint( + type_var=unpacked_type.id, + op=self.direction, + target=actual, + )] + if isinstance(actual, TupleType) and len(actual.items) == len(template.items): res: List[Constraint] = [] for i in range(len(template.items)): diff --git a/mypy/expandtype.py b/mypy/expandtype.py index eef841c9387e..78b36f288757 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -1,4 +1,4 @@ -from typing import Dict, Iterable, List, TypeVar, Mapping, cast +from typing import Dict, Iterable, List, TypeVar, Mapping, cast, Union, Optional from mypy.types import ( Type, Instance, CallableType, TypeVisitor, UnboundType, AnyType, @@ -45,6 +45,8 @@ def freshen_function_type_vars(callee: F) -> F: # TODO(PEP612): fix for ParamSpecType if isinstance(v, TypeVarType): tv: TypeVarLikeType = TypeVarType.new_unification_variable(v) + elif isinstance(v, TypeVarTupleType): + tv = TypeVarTupleType.new_unification_variable(v) else: assert isinstance(v, ParamSpecType) tv = ParamSpecType.new_unification_variable(v) @@ -135,7 +137,36 @@ def visit_type_var_tuple(self, t: TypeVarTupleType) -> Type: raise NotImplementedError def visit_unpack_type(self, t: UnpackType) -> Type: - raise NotImplementedError + # It is impossible to reasonally implement visit_unpack_type, because + # unpacking inherently expands to something more like a list of types. + # + # Relevant sections that can call unpack should call expand_unpack() + # instead. + assert False, "Mypy bug: unpacking must happen at a higher level" + + def expand_unpack(self, t: UnpackType) -> Optional[Union[List[Type], Instance, AnyType]]: + """May return either a list of types to unpack to, any, or a single + variable length tuple. The latter may not be valid in all contexts. + """ + proper_typ = get_proper_type(t.type) + if isinstance(proper_typ, TypeVarTupleType): + repl = get_proper_type(self.variables.get(proper_typ.id, t)) + if isinstance(repl, TupleType): + return repl.items + elif isinstance(repl, Instance) and repl.type.fullname == "builtins.tuple": + return repl + elif isinstance(repl, AnyType): + # tuple[Any, ...] would be better, but we don't have + # the type info to construct that type here. + return repl + elif isinstance(repl, TypeVarTupleType): + return [UnpackType(typ=repl)] + elif isinstance(repl, UninhabitedType): + return None + else: + raise NotImplementedError("Invalid type to expand: {}".format(repl)) + else: + raise NotImplementedError def visit_parameters(self, t: Parameters) -> Type: return t.copy_modified(arg_types=self.expand_types(t.arg_types)) @@ -179,7 +210,27 @@ def visit_overloaded(self, t: Overloaded) -> Type: return Overloaded(items) def visit_tuple_type(self, t: TupleType) -> Type: - return t.copy_modified(items=self.expand_types(t.items)) + items = [] + for item in t.items: + proper_item = get_proper_type(item) + if isinstance(proper_item, UnpackType): + unpacked_items = self.expand_unpack(proper_item) + if unpacked_items is None: + # TODO: better error, something like tuple of unknown? + return UninhabitedType() + elif isinstance(unpacked_items, Instance): + if len(t.items) == 1: + return unpacked_items + else: + assert False, "Invalid unpack of variable length tuple" + elif isinstance(unpacked_items, AnyType): + return unpacked_items + else: + items.extend(unpacked_items) + else: + items.append(proper_item.accept(self)) + + return t.copy_modified(items=items) def visit_typeddict_type(self, t: TypedDictType) -> Type: return t.copy_modified(item_types=self.expand_types(t.items.values())) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 809f457ab2a2..8bb592702157 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -350,7 +350,9 @@ def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: return self._is_subtype(left.upper_bound, self.right) def visit_unpack_type(self, left: UnpackType) -> bool: - raise NotImplementedError + if isinstance(self.right, UnpackType): + return self._is_subtype(left.type, self.right.type) + return False def visit_parameters(self, left: Parameters) -> bool: right = self.right @@ -1482,7 +1484,9 @@ def visit_type_var_tuple(self, left: TypeVarTupleType) -> bool: return self._is_proper_subtype(left.upper_bound, self.right) def visit_unpack_type(self, left: UnpackType) -> bool: - raise NotImplementedError + if isinstance(self.right, UnpackType): + return self._is_proper_subtype(left.type, self.right.type) + return False def visit_parameters(self, left: Parameters) -> bool: right = self.right diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 355a400168f6..4f6c82877775 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -91,6 +91,7 @@ 'check-errorcodes.test', 'check-annotated.test', 'check-parameter-specification.test', + 'check-typevar-tuple.test', 'check-generic-alias.test', 'check-typeguard.test', 'check-functools.test', @@ -164,6 +165,7 @@ def run_case_once(self, testcase: DataDrivenTestCase, # Parse options after moving files (in case mypy.ini is being moved). options = parse_options(original_program_text, testcase, incremental_step) options.use_builtins_fixtures = True + options.enable_incomplete_features = True options.show_traceback = True # Enable some options automatically based on test file name. diff --git a/mypy/typeanal.py b/mypy/typeanal.py index eee8a43c25f3..119fbd3fbf79 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1202,8 +1202,6 @@ def anal_var_def(self, var_def: TypeVarLikeType) -> TypeVarLikeType: var_def.variance, var_def.line ) - elif isinstance(var_def, TypeVarTupleType): - raise NotImplementedError else: return var_def diff --git a/mypy/types.py b/mypy/types.py index e43b73f093b8..69776e4188ad 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -696,6 +696,12 @@ def __eq__(self, other: object) -> bool: return NotImplemented return self.id == other.id + @staticmethod + def new_unification_variable(old: 'TypeVarTupleType') -> 'TypeVarTupleType': + new_id = TypeVarId.new(meta_level=1) + return TypeVarTupleType(old.name, old.fullname, new_id, old.upper_bound, + line=old.line, column=old.column) + class UnboundType(ProperType): """Instance type that has not been bound during semantic analysis.""" diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test new file mode 100644 index 000000000000..ed11e5b53263 --- /dev/null +++ b/test-data/unit/check-typevar-tuple.test @@ -0,0 +1,29 @@ +[case testTypeVarTupleBasic] +from typing import Any, Tuple +from typing_extensions import Unpack, TypeVarTuple + +Ts = TypeVarTuple("Ts") + +def f(a: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: + return a + +any: Any +args: Tuple[int, str] = (1, 'x') +args2: Tuple[bool, str] = (False, 'y') +args3: Tuple[int, str, bool] = (2, 'z', True) +varargs: Tuple[int, ...] = (1, 2, 3) + +reveal_type(f(args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" + +reveal_type(f(varargs)) # N: Revealed type is "builtins.tuple[builtins.int, ...]" + +f(0) # E: Argument 1 to "f" has incompatible type "int"; expected + +def g(a: Tuple[Unpack[Ts]], b: Tuple[Unpack[Ts]]) -> Tuple[Unpack[Ts]]: + return a + +reveal_type(g(args, args)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(g(args, args2)) # N: Revealed type is "Tuple[builtins.int, builtins.str]" +reveal_type(g(args, args3)) # N: Revealed type is "builtins.tuple[builtins.object, ...]" +reveal_type(g(any, any)) # N: Revealed type is "Any" +[builtins fixtures/tuple.pyi] From d48d548ca5945897c4cbfa9929c0e22e6a4ec9c8 Mon Sep 17 00:00:00 2001 From: Hugues Date: Fri, 29 Apr 2022 08:27:11 -0700 Subject: [PATCH 33/79] introduce per-file timing-stats (#12639) When profiling mypy over a large codebase, it can be useful to know which files are slowest to typecheck. Gather per-file timing stats and expose them through a new (hidden) command line switch --- mypy/build.py | 32 +++++++++++++++++++++++++++++++- mypy/main.py | 3 +++ mypy/options.py | 1 + mypy/test/data.py | 10 ++++++---- mypy/test/helpers.py | 15 ++++++++++++--- mypy/util.py | 10 ++++++++++ test-data/unit/cmdline.test | 12 ++++++++++++ 7 files changed, 75 insertions(+), 8 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index f7a9e9e05e1d..f084e632417a 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -36,7 +36,8 @@ from mypy.errors import Errors, CompileError, ErrorInfo, report_internal_error from mypy.util import ( DecodeError, decode_python_encoding, is_sub_path, get_mypy_comments, module_prefix, - read_py_file, hash_digest, is_typeshed_file, is_stub_package_file, get_top_two_prefixes + read_py_file, hash_digest, is_typeshed_file, is_stub_package_file, get_top_two_prefixes, + time_ref, time_spent_us ) if TYPE_CHECKING: from mypy.report import Reports # Avoid unconditional slow import @@ -256,6 +257,8 @@ def _build(sources: List[BuildSource], graph = dispatch(sources, manager, stdout) if not options.fine_grained_incremental: TypeState.reset_all_subtype_caches() + if options.timing_stats is not None: + dump_timing_stats(options.timing_stats, graph) return BuildResult(manager, graph) finally: t0 = time.time() @@ -1808,6 +1811,9 @@ class State: fine_grained_deps_loaded = False + # Cumulative time spent on this file, in microseconds (for profiling stats) + time_spent_us: int = 0 + def __init__(self, id: Optional[str], path: Optional[str], @@ -2034,6 +2040,8 @@ def parse_file(self) -> None: else: manager.log("Using cached AST for %s (%s)" % (self.xpath, self.id)) + t0 = time_ref() + with self.wrap_context(): source = self.source self.source = None # We won't need it again. @@ -2079,6 +2087,8 @@ def parse_file(self) -> None: self.tree.ignored_lines, self.ignore_all or self.options.ignore_errors) + self.time_spent_us += time_spent_us(t0) + if not cached: # Make a copy of any errors produced during parse time so that # fine-grained mode can repeat them when the module is @@ -2113,6 +2123,9 @@ def semantic_analysis_pass1(self) -> None: """ options = self.options assert self.tree is not None + + t0 = time_ref() + # Do the first pass of semantic analysis: analyze the reachability # of blocks and import statements. We must do this before # processing imports, since this may mark some import statements as @@ -2131,6 +2144,7 @@ def semantic_analysis_pass1(self) -> None: if options.allow_redefinition: # Perform more renaming across the AST to allow variable redefinitions self.tree.accept(VariableRenameVisitor()) + self.time_spent_us += time_spent_us(t0) def add_dependency(self, dep: str) -> None: if dep not in self.dependencies_set: @@ -2188,8 +2202,10 @@ def compute_dependencies(self) -> None: def type_check_first_pass(self) -> None: if self.options.semantic_analysis_only: return + t0 = time_ref() with self.wrap_context(): self.type_checker().check_first_pass() + self.time_spent_us += time_spent_us(t0) def type_checker(self) -> TypeChecker: if not self._type_checker: @@ -2207,14 +2223,17 @@ def type_map(self) -> Dict[Expression, Type]: def type_check_second_pass(self) -> bool: if self.options.semantic_analysis_only: return False + t0 = time_ref() with self.wrap_context(): return self.type_checker().check_second_pass() + self.time_spent_us += time_spent_us(t0) def finish_passes(self) -> None: assert self.tree is not None, "Internal error: method must be called on parsed file only" manager = self.manager if self.options.semantic_analysis_only: return + t0 = time_ref() with self.wrap_context(): # Some tests (and tools) want to look at the set of all types. options = manager.options @@ -2237,6 +2256,7 @@ def finish_passes(self) -> None: self.free_state() if not manager.options.fine_grained_incremental and not manager.options.preserve_asts: free_tree(self.tree) + self.time_spent_us += time_spent_us(t0) def free_state(self) -> None: if self._type_checker: @@ -2771,6 +2791,16 @@ def dumps(self) -> str: json.dumps(self.deps)) +def dump_timing_stats(path: str, graph: Graph) -> None: + """ + Dump timing stats for each file in the given graph + """ + with open(path, 'w') as f: + for k in sorted(graph.keys()): + v = graph[k] + f.write('{} {}\n'.format(v.id, v.time_spent_us)) + + def dump_graph(graph: Graph, stdout: Optional[TextIO] = None) -> None: """Dump the graph as a JSON string to stdout. diff --git a/mypy/main.py b/mypy/main.py index c4548ea9b625..7c7a5993d5c5 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -835,6 +835,9 @@ def add_invertible_flag(flag: str, parser.add_argument( '--dump-build-stats', action='store_true', help=argparse.SUPPRESS) + # dump timing stats for each processed file into the given output file + parser.add_argument( + '--timing-stats', dest='timing_stats', help=argparse.SUPPRESS) # --debug-cache will disable any cache-related compressions/optimizations, # which will make the cache writing process output pretty-printed JSON (which # is easier to debug). diff --git a/mypy/options.py b/mypy/options.py index 8e56d67bbeb8..4cb2434958f4 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -263,6 +263,7 @@ def __init__(self) -> None: self.dump_inference_stats = False self.dump_build_stats = False self.enable_incomplete_features = False + self.timing_stats: Optional[str] = None # -- test options -- # Stop after the semantic analysis phase diff --git a/mypy/test/data.py b/mypy/test/data.py index e886b11ffa8e..e18ac142d15e 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -10,7 +10,7 @@ import sys import pytest -from typing import List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union +from typing import List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union, Pattern from mypy.test.config import test_data_prefix, test_temp_dir, PREFIX @@ -44,7 +44,7 @@ def parse_test_case(case: 'DataDrivenTestCase') -> None: normalize_output = True files: List[Tuple[str, str]] = [] # path and contents - output_files: List[Tuple[str, str]] = [] # path and contents for output files + output_files: List[Tuple[str, Union[str, Pattern[str]]]] = [] # output path and contents output: List[str] = [] # Regular output errors output2: Dict[int, List[str]] = {} # Output errors for incremental, runs 2+ deleted_paths: Dict[int, Set[str]] = {} # from run number of paths @@ -57,13 +57,15 @@ def parse_test_case(case: 'DataDrivenTestCase') -> None: # optionally followed by lines of text. item = first_item = test_items[0] for item in test_items[1:]: - if item.id == 'file' or item.id == 'outfile': + if item.id in {'file', 'outfile', 'outfile-re'}: # Record an extra file needed for the test case. assert item.arg is not None contents = expand_variables('\n'.join(item.data)) file_entry = (join(base_path, item.arg), contents) if item.id == 'file': files.append(file_entry) + elif item.id == 'outfile-re': + output_files.append((file_entry[0], re.compile(file_entry[1].rstrip(), re.S))) else: output_files.append(file_entry) elif item.id in ('builtins', 'builtins_py2'): @@ -220,7 +222,7 @@ class DataDrivenTestCase(pytest.Item): # Extra attributes used by some tests. last_line: int - output_files: List[Tuple[str, str]] # Path and contents for output files + output_files: List[Tuple[str, Union[str, Pattern[str]]]] # Path and contents for output files deleted_paths: Dict[int, Set[str]] # Mapping run number -> paths triggered: List[str] # Active triggers (one line per incremental step) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index f9f117634c21..5046c46eaa43 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -5,7 +5,7 @@ import shutil import contextlib -from typing import List, Iterable, Dict, Tuple, Callable, Any, Iterator, Union +from typing import List, Iterable, Dict, Tuple, Callable, Any, Iterator, Union, Pattern from mypy import defaults import mypy.api as api @@ -458,8 +458,17 @@ def check_test_output_files(testcase: DataDrivenTestCase, 'Expected file {} was not produced by test case{}'.format( path, ' on step %d' % step if testcase.output2 else '')) with open(path, 'r', encoding='utf8') as output_file: - actual_output_content = output_file.read().splitlines() - normalized_output = normalize_file_output(actual_output_content, + actual_output_content = output_file.read() + + if isinstance(expected_content, Pattern): + if expected_content.fullmatch(actual_output_content) is not None: + continue + raise AssertionError( + 'Output file {} did not match its expected output pattern\n---\n{}\n---'.format( + path, actual_output_content) + ) + + normalized_output = normalize_file_output(actual_output_content.splitlines(), os.path.abspath(test_temp_dir)) # We always normalize things like timestamp, but only handle operating-system # specific things if requested. diff --git a/mypy/util.py b/mypy/util.py index c02e5dcfc9b4..1a3628458c48 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -8,6 +8,7 @@ import hashlib import io import shutil +import time from typing import ( TypeVar, List, Tuple, Optional, Dict, Sequence, Iterable, Container, IO, Callable @@ -763,3 +764,12 @@ def is_stub_package_file(file: str) -> bool: def unnamed_function(name: Optional[str]) -> bool: return name is not None and name == "_" + + +# TODO: replace with uses of perf_counter_ns when support for py3.6 is dropped +# (or when mypy properly handles alternate definitions based on python version check +time_ref = time.perf_counter + + +def time_spent_us(t0: float) -> int: + return int((time.perf_counter() - t0) * 1e6) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 1a038b9fac09..7fc517643342 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1373,3 +1373,15 @@ exclude = (?x)( 1() [out] c/cpkg.py:1: error: "int" not callable + + +[case testCmdlineTimingStats] +# cmd: mypy --timing-stats timing.txt . +[file b/__init__.py] +[file b/c.py] +class C: pass +[outfile-re timing.txt] +.* +b \d+ +b\.c \d+ +.* From c6cf7cd3dac90dce0be5bf888f530f2eee1534e7 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 29 Apr 2022 09:41:29 -0600 Subject: [PATCH 34/79] Use tuple[object, ...] and dict[str, object] as upper bounds for ParamSpec.args and ParamSpec.kwargs (#12668) Mypy thought that a variable annotated with P.args is not iterable, and that a variable annotated with P.kwargs does not have a .pop() method. Fixes #12386. --- mypy/checkmember.py | 2 +- mypy/semanal_shared.py | 50 +++++++++++- mypy/typeanal.py | 30 ++++---- .../unit/check-parameter-specification.test | 75 +++++++++++------- test-data/unit/fixtures/paramspec.pyi | 76 +++++++++++++++++++ 5 files changed, 186 insertions(+), 47 deletions(-) create mode 100644 test-data/unit/fixtures/paramspec.pyi diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2659ad18ed6e..04b64e9ba7fe 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -164,7 +164,7 @@ def _analyze_member_access(name: str, return analyze_typeddict_access(name, typ, mx, override_info) elif isinstance(typ, NoneType): return analyze_none_member_access(name, typ, mx) - elif isinstance(typ, TypeVarType): + elif isinstance(typ, TypeVarLikeType): return _analyze_member_access(name, typ.upper_bound, mx, override_info) elif isinstance(typ, DeletedType): mx.msg.deleted_as_rvalue(typ, mx.context) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 85a6779ac9f3..72a89150bb64 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -2,8 +2,8 @@ from abc import abstractmethod -from typing import Optional, List, Callable -from typing_extensions import Final +from typing import Optional, List, Callable, Union +from typing_extensions import Final, Protocol from mypy_extensions import trait from mypy.nodes import ( @@ -11,7 +11,8 @@ SymbolNode, SymbolTable ) from mypy.types import ( - Type, FunctionLike, Instance, TupleType, TPDICT_FB_NAMES, ProperType, get_proper_type + Type, FunctionLike, Instance, TupleType, TPDICT_FB_NAMES, ProperType, get_proper_type, + ParamSpecType, ParamSpecFlavor, Parameters, TypeVarId ) from mypy.tvar_scope import TypeVarLikeScope from mypy.errorcodes import ErrorCode @@ -212,3 +213,46 @@ def calculate_tuple_fallback(typ: TupleType) -> None: fallback = typ.partial_fallback assert fallback.type.fullname == 'builtins.tuple' fallback.args = (join.join_type_list(list(typ.items)),) + fallback.args[1:] + + +class _NamedTypeCallback(Protocol): + def __call__( + self, fully_qualified_name: str, args: Optional[List[Type]] = None + ) -> Instance: ... + + +def paramspec_args( + name: str, fullname: str, id: Union[TypeVarId, int], *, + named_type_func: _NamedTypeCallback, line: int = -1, column: int = -1, + prefix: Optional[Parameters] = None +) -> ParamSpecType: + return ParamSpecType( + name, + fullname, + id, + flavor=ParamSpecFlavor.ARGS, + upper_bound=named_type_func('builtins.tuple', [named_type_func('builtins.object')]), + line=line, + column=column, + prefix=prefix + ) + + +def paramspec_kwargs( + name: str, fullname: str, id: Union[TypeVarId, int], *, + named_type_func: _NamedTypeCallback, line: int = -1, column: int = -1, + prefix: Optional[Parameters] = None +) -> ParamSpecType: + return ParamSpecType( + name, + fullname, + id, + flavor=ParamSpecFlavor.KWARGS, + upper_bound=named_type_func( + 'builtins.dict', + [named_type_func('builtins.str'), named_type_func('builtins.object')] + ), + line=line, + column=column, + prefix=prefix + ) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 119fbd3fbf79..84d9758b9a57 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -33,7 +33,7 @@ from mypy.tvar_scope import TypeVarLikeScope from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.plugin import Plugin, TypeAnalyzerPluginInterface, AnalyzeTypeContext -from mypy.semanal_shared import SemanticAnalyzerCoreInterface +from mypy.semanal_shared import SemanticAnalyzerCoreInterface, paramspec_args, paramspec_kwargs from mypy.errorcodes import ErrorCode from mypy import nodes, message_registry, errorcodes as codes @@ -711,13 +711,13 @@ def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type: tvar_def = self.tvar_scope.get_binding(sym) if isinstance(tvar_def, ParamSpecType): if kind == ARG_STAR: - flavor = ParamSpecFlavor.ARGS + make_paramspec = paramspec_args elif kind == ARG_STAR2: - flavor = ParamSpecFlavor.KWARGS + make_paramspec = paramspec_kwargs else: assert False, kind - return ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, flavor, - upper_bound=self.named_type('builtins.object'), + return make_paramspec(tvar_def.name, tvar_def.fullname, tvar_def.id, + named_type_func=self.named_type, line=t.line, column=t.column) return self.anal_type(t, nested=nested) @@ -855,13 +855,11 @@ def analyze_callable_args_for_paramspec( if not isinstance(tvar_def, ParamSpecType): return None - # TODO: Use tuple[...] or Mapping[..] instead? - obj = self.named_type('builtins.object') return CallableType( - [ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.ARGS, - upper_bound=obj), - ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.KWARGS, - upper_bound=obj)], + [paramspec_args(tvar_def.name, tvar_def.fullname, tvar_def.id, + named_type_func=self.named_type), + paramspec_kwargs(tvar_def.name, tvar_def.fullname, tvar_def.id, + named_type_func=self.named_type)], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=ret_type, @@ -891,18 +889,16 @@ def analyze_callable_args_for_concatenate( if not isinstance(tvar_def, ParamSpecType): return None - # TODO: Use tuple[...] or Mapping[..] instead? - obj = self.named_type('builtins.object') # ick, CallableType should take ParamSpecType prefix = tvar_def.prefix # we don't set the prefix here as generic arguments will get updated at some point # in the future. CallableType.param_spec() accounts for this. return CallableType( [*prefix.arg_types, - ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.ARGS, - upper_bound=obj), - ParamSpecType(tvar_def.name, tvar_def.fullname, tvar_def.id, ParamSpecFlavor.KWARGS, - upper_bound=obj)], + paramspec_args(tvar_def.name, tvar_def.fullname, tvar_def.id, + named_type_func=self.named_type), + paramspec_kwargs(tvar_def.name, tvar_def.fullname, tvar_def.id, + named_type_func=self.named_type)], [*prefix.arg_kinds, nodes.ARG_STAR, nodes.ARG_STAR2], [*prefix.arg_names, None, None], ret_type=ret_type, diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 2242b79d4b64..28b08aa7122f 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -11,7 +11,7 @@ P2 = ParamSpec("P2", contravariant=True) # E: Only the first argument to ParamS P3 = ParamSpec("P3", bound=int) # E: Only the first argument to ParamSpec has defined semantics P4 = ParamSpec("P4", int, str) # E: Only the first argument to ParamSpec has defined semantics P5 = ParamSpec("P5", covariant=True, bound=int) # E: Only the first argument to ParamSpec has defined semantics -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecLocations] from typing import Callable, List @@ -35,7 +35,7 @@ def foo5(x: Callable[[int, str], P]) -> None: ... # E: Invalid location for Par def foo6(x: Callable[[P], int]) -> None: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecContextManagerLike] from typing import Callable, List, Iterator, TypeVar @@ -51,7 +51,7 @@ def whatever(x: int) -> Iterator[int]: reveal_type(whatever) # N: Revealed type is "def (x: builtins.int) -> builtins.list[builtins.int]" reveal_type(whatever(217)) # N: Revealed type is "builtins.list[builtins.int]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testInvalidParamSpecType] # flags: --python-version 3.10 @@ -70,7 +70,7 @@ P = ParamSpec('P') def f(x: Callable[P, int]) -> None: ... reveal_type(f) # N: Revealed type is "def [P] (x: def (*P.args, **P.kwargs) -> builtins.int)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecSimpleFunction] from typing import Callable, TypeVar @@ -83,7 +83,7 @@ def changes_return_type_to_str(x: Callable[P, int]) -> Callable[P, str]: ... def returns_int(a: str, b: bool) -> int: ... reveal_type(changes_return_type_to_str(returns_int)) # N: Revealed type is "def (a: builtins.str, b: builtins.bool) -> builtins.str" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecSimpleClass] from typing import Callable, TypeVar, Generic @@ -199,7 +199,7 @@ g: Any reveal_type(f(g)) # N: Revealed type is "def (*Any, **Any) -> builtins.str" f(g)(1, 3, x=1, y=2) -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecDecoratorImplementation] from typing import Callable, Any, TypeVar, List @@ -556,7 +556,7 @@ a: Callable[[int, bytes], str] b: Callable[[str, bytes], str] reveal_type(f(a, b)) # N: Revealed type is "def (builtins.bytes) -> builtins.str" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecConcatenateInReturn] from typing_extensions import ParamSpec, Concatenate @@ -569,7 +569,7 @@ def f(i: Callable[Concatenate[int, P], str]) -> Callable[Concatenate[int, P], st n: Callable[[int, bytes], str] reveal_type(f(n)) # N: Revealed type is "def (builtins.int, builtins.bytes) -> builtins.str" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecConcatenateNamedArgs] # flags: --strict-concatenate @@ -592,7 +592,7 @@ def f2(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: # reason for rejection: f2(lambda x: 42)(42, x=42) -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [out] main:10: error: invalid syntax [out version>=3.8] @@ -619,7 +619,7 @@ def f2(c: Callable[P, R]) -> Callable[Concatenate[int, P], R]: # reason for rejection: f2(lambda x: 42)(42, x=42) -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [out] main:9: error: invalid syntax [out version>=3.8] @@ -640,7 +640,7 @@ n = f(a) reveal_type(n) # N: Revealed type is "def (builtins.int)" reveal_type(n(42)) # N: Revealed type is "None" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testCallablesAsParameters] # credits to https://github.com/microsoft/pyright/issues/2705 @@ -658,7 +658,7 @@ def test(a: int, /, b: str) -> str: ... abc = Foo(test) reveal_type(abc) bar(abc) -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [out] main:11: error: invalid syntax [out version>=3.8] @@ -677,7 +677,7 @@ n: Foo[[int]] def f(x: int) -> None: ... n.foo(f) -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecLiteralsTypeApplication] from typing_extensions import ParamSpec @@ -709,7 +709,7 @@ Z[bytes, str](lambda one: None) # E: Cannot infer type of lambda \ # E: Argument 1 to "Z" has incompatible type "Callable[[Any], None]"; expected "Callable[[bytes, str], None]" Z[bytes, str](f2) # E: Argument 1 to "Z" has incompatible type "Callable[[bytes, int], None]"; expected "Callable[[bytes, str], None]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecLiteralEllipsis] from typing_extensions import ParamSpec @@ -740,7 +740,7 @@ n = Z(f1) n = Z(f2) n = Z(f3) -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecApplyConcatenateTwice] from typing_extensions import ParamSpec, Concatenate @@ -770,7 +770,7 @@ def f(c: C[P]) -> None: reveal_type(p1) # N: Revealed type is "__main__.C[[builtins.str, **P`-1]]" p2 = p1.add_str() reveal_type(p2) # N: Revealed type is "__main__.C[[builtins.int, builtins.str, **P`-1]]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecLiteralJoin] from typing import Generic, Callable, Union @@ -788,7 +788,7 @@ def func( ) -> None: job = action if isinstance(action, Job) else Job(action) reveal_type(job) # N: Revealed type is "__main__.Job[[builtins.int]]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testApplyParamSpecToParamSpecLiterals] from typing import TypeVar, Generic, Callable @@ -818,7 +818,7 @@ def func2(job: Job[..., None]) -> None: run_job(job, "Hello", 42) run_job(job, 42, msg="Hello") run_job(job, x=42, msg="Hello") -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testExpandNonBareParamSpecAgainstCallable] from typing import Callable, TypeVar, Any @@ -850,7 +850,7 @@ reveal_type(A().func(f, 42)) # N: Revealed type is "builtins.int" # TODO: this should reveal `int` reveal_type(A().func(lambda x: x + x, 42)) # N: Revealed type is "Any" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecConstraintOnOtherParamSpec] from typing import Callable, TypeVar, Any, Generic @@ -880,7 +880,7 @@ reveal_type(A().func(Job(lambda x: x))) # N: Revealed type is "__main__.Job[[x: def f(x: int, y: int) -> None: ... reveal_type(A().func(Job(f))) # N: Revealed type is "__main__.Job[[x: builtins.int, y: builtins.int], None]" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testConstraintBetweenParamSpecFunctions1] from typing import Callable, TypeVar, Any, Generic @@ -898,7 +898,7 @@ def func(__action: Job[_P]) -> Callable[_P, None]: ... reveal_type(func) # N: Revealed type is "def [_P] (__main__.Job[_P`-1]) -> def (*_P.args, **_P.kwargs)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testConstraintBetweenParamSpecFunctions2] from typing import Callable, TypeVar, Any, Generic @@ -916,7 +916,7 @@ def func(__action: Job[_P]) -> Callable[_P, None]: ... reveal_type(func) # N: Revealed type is "def [_P] (__main__.Job[_P`-1]) -> def (*_P.args, **_P.kwargs)" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testConstraintsBetweenConcatenatePrefixes] from typing import Any, Callable, Generic, TypeVar @@ -937,7 +937,7 @@ def adds_await() -> Callable[ ... return decorator # we want `_T` and `_P` to refer to the same things. -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testParamSpecVariance] from typing import Callable, Generic @@ -995,7 +995,7 @@ a3: Callable[[int], None] a3 = f3 # E: Incompatible types in assignment (expression has type "Callable[[bool], None]", variable has type "Callable[[int], None]") a3 = f2 a3 = f1 -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testDecoratingClassesThatUseParamSpec] from typing import Generic, TypeVar, Callable, Any @@ -1039,7 +1039,7 @@ reveal_type(j) # N: Revealed type is "__main__.Job[[x: _T`-1]]" jf = j.into_callable() reveal_type(jf) # N: Revealed type is "def [_T] (x: _T`-1)" reveal_type(jf(1)) # N: Revealed type is "None" -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testStackedConcatenateIsIllegal] from typing_extensions import Concatenate, ParamSpec @@ -1048,7 +1048,7 @@ from typing import Callable P = ParamSpec("P") def x(f: Callable[Concatenate[int, Concatenate[int, P]], None]) -> None: ... # E: Nested Concatenates are invalid -[builtins fixtures/tuple.pyi] +[builtins fixtures/paramspec.pyi] [case testPropagatedAnyConstraintsAreOK] from typing import Any, Callable, Generic, TypeVar @@ -1063,3 +1063,26 @@ class Job(Generic[P]): ... @callback def run_job(job: Job[...]) -> T: ... [builtins fixtures/tuple.pyi] + +[case testTupleAndDictOperationsOnParamSpecArgsAndKwargs] +from typing import Callable +from typing_extensions import ParamSpec + +P = ParamSpec('P') + +def func(callback: Callable[P, str]) -> Callable[P, str]: + def inner(*args: P.args, **kwargs: P.kwargs) -> str: + reveal_type(args[5]) # N: Revealed type is "builtins.object" + for a in args: + reveal_type(a) # N: Revealed type is "builtins.object" + b = 'foo' in args + reveal_type(b) # N: Revealed type is "builtins.bool" + reveal_type(args.count(42)) # N: Revealed type is "builtins.int" + reveal_type(len(args)) # N: Revealed type is "builtins.int" + for c, d in kwargs.items(): + reveal_type(c) # N: Revealed type is "builtins.str" + reveal_type(d) # N: Revealed type is "builtins.object" + kwargs.pop('bar') + return 'baz' + return inner +[builtins fixtures/paramspec.pyi] diff --git a/test-data/unit/fixtures/paramspec.pyi b/test-data/unit/fixtures/paramspec.pyi new file mode 100644 index 000000000000..0686924aad6f --- /dev/null +++ b/test-data/unit/fixtures/paramspec.pyi @@ -0,0 +1,76 @@ +# builtins stub for paramspec-related test cases + +from typing import ( + Sequence, Generic, TypeVar, Iterable, Iterator, Tuple, Mapping, Optional, Union, Type, overload, + Protocol +) + +T = TypeVar("T") +T_co = TypeVar('T_co', covariant=True) +KT = TypeVar("KT") +VT = TypeVar("VT") + +class object: + def __init__(self) -> None: ... + +class function: ... +class ellipsis: ... + +class type: + def __init__(self, *a: object) -> None: ... + def __call__(self, *a: object) -> object: ... + +class list(Sequence[T], Generic[T]): + @overload + def __getitem__(self, i: int) -> T: ... + @overload + def __getitem__(self, s: slice) -> list[T]: ... + def __contains__(self, item: object) -> bool: ... + def __iter__(self) -> Iterator[T]: ... + +class int: + def __neg__(self) -> 'int': ... + +class bool(int): ... +class float: ... +class slice: ... +class str: ... +class bytes: ... + +class tuple(Sequence[T_co], Generic[T_co]): + def __new__(cls: Type[T], iterable: Iterable[T_co] = ...) -> T: ... + def __iter__(self) -> Iterator[T_co]: ... + def __contains__(self, item: object) -> bool: ... + def __getitem__(self, x: int) -> T_co: ... + def __mul__(self, n: int) -> Tuple[T_co, ...]: ... + def __rmul__(self, n: int) -> Tuple[T_co, ...]: ... + def __add__(self, x: Tuple[T_co, ...]) -> Tuple[T_co, ...]: ... + def __len__(self) -> int: ... + def count(self, obj: object) -> int: ... + +class _ItemsView(Iterable[Tuple[KT, VT]]): ... + +class dict(Mapping[KT, VT]): + @overload + def __init__(self, **kwargs: VT) -> None: ... + @overload + def __init__(self, arg: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None: ... + def __getitem__(self, key: KT) -> VT: ... + def __setitem__(self, k: KT, v: VT) -> None: ... + def __iter__(self) -> Iterator[KT]: ... + def __contains__(self, item: object) -> int: ... + def update(self, a: Mapping[KT, VT]) -> None: ... + @overload + def get(self, k: KT) -> Optional[VT]: ... + @overload + def get(self, k: KT, default: Union[KT, T]) -> Union[VT, T]: ... + def __len__(self) -> int: ... + def pop(self, k: KT) -> VT: ... + def items(self) -> _ItemsView[KT, VT]: ... + +def isinstance(x: object, t: type) -> bool: ... + +class _Sized(Protocol): + def __len__(self) -> int: ... + +def len(x: _Sized) -> int: ... From ce6d2433fdb9254dac5523825ab8fb310eace371 Mon Sep 17 00:00:00 2001 From: Hugues Date: Sat, 30 Apr 2022 05:50:44 -0700 Subject: [PATCH 35/79] cleanups after recent refactor of error filtering (#12699) --- mypy/checkexpr.py | 197 +++++++++++++++++-------------------------- mypy/checkpattern.py | 12 +-- mypy/errors.py | 22 ----- 3 files changed, 82 insertions(+), 149 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index f6aef2e361cc..89f2cd19a2a7 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -87,7 +87,6 @@ Optional[Type], Context, Context, - MessageBuilder, ], None, ] @@ -908,7 +907,6 @@ def check_call(self, context: Context, arg_names: Optional[Sequence[Optional[str]]] = None, callable_node: Optional[Expression] = None, - arg_messages: Optional[MessageBuilder] = None, callable_name: Optional[str] = None, object_type: Optional[Type] = None) -> Tuple[Type, Type]: """Type check a call. @@ -926,27 +924,23 @@ def check_call(self, arg_names: names of arguments (optional) callable_node: associate the inferred callable type to this node, if specified - arg_messages: utility for generating messages, can be swapped to suppress errors, - by default uses 'self.msg' to show errors callable_name: Fully-qualified name of the function/method to call, or None if unavailable (examples: 'builtins.open', 'typing.Mapping.get') object_type: If callable_name refers to a method, the type of the object on which the method is being called """ - arg_messages = arg_messages or self.msg callee = get_proper_type(callee) if isinstance(callee, CallableType): return self.check_callable_call(callee, args, arg_kinds, context, arg_names, - callable_node, arg_messages, callable_name, - object_type) + callable_node, callable_name, object_type) elif isinstance(callee, Overloaded): return self.check_overload_call(callee, args, arg_kinds, arg_names, callable_name, - object_type, context, arg_messages) + object_type, context) elif isinstance(callee, AnyType) or not self.chk.in_checked_function(): return self.check_any_type_call(args, callee) elif isinstance(callee, UnionType): - return self.check_union_call(callee, args, arg_kinds, arg_names, context, arg_messages) + return self.check_union_call(callee, args, arg_kinds, arg_names, context) elif isinstance(callee, Instance): call_function = analyze_member_access('__call__', callee, context, is_lvalue=False, is_super=False, is_operator=True, msg=self.msg, @@ -957,7 +951,7 @@ def check_call(self, call_function = self.transform_callee_type( callable_name, call_function, args, arg_kinds, context, arg_names, callee) result = self.check_call(call_function, args, arg_kinds, context, arg_names, - callable_node, arg_messages, callable_name, callee) + callable_node, callable_name, callee) if callable_node: # check_call() stored "call_function" as the type, which is incorrect. # Override the type. @@ -965,14 +959,14 @@ def check_call(self, return result elif isinstance(callee, TypeVarType): return self.check_call(callee.upper_bound, args, arg_kinds, context, arg_names, - callable_node, arg_messages) + callable_node) elif isinstance(callee, TypeType): item = self.analyze_type_type_callee(callee.item, context) return self.check_call(item, args, arg_kinds, context, arg_names, - callable_node, arg_messages) + callable_node) elif isinstance(callee, TupleType): return self.check_call(tuple_fallback(callee), args, arg_kinds, context, - arg_names, callable_node, arg_messages, callable_name, + arg_names, callable_node, callable_name, object_type) else: return self.msg.not_callable(callee, context), AnyType(TypeOfAny.from_error) @@ -984,7 +978,6 @@ def check_callable_call(self, context: Context, arg_names: Optional[Sequence[Optional[str]]], callable_node: Optional[Expression], - arg_messages: MessageBuilder, callable_name: Optional[str], object_type: Optional[Type]) -> Tuple[Type, Type]: """Type check a call that targets a callable value. @@ -1050,10 +1043,10 @@ def check_callable_call(self, callee, args, arg_kinds, formal_to_actual) self.check_argument_count(callee, arg_types, arg_kinds, - arg_names, formal_to_actual, context, self.msg) + arg_names, formal_to_actual, context) self.check_argument_types(arg_types, arg_kinds, args, callee, formal_to_actual, context, - messages=arg_messages, object_type=object_type) + object_type=object_type) if (callee.is_type_obj() and (len(arg_types) == 1) and is_equivalent(callee.ret_type, self.named_type('builtins.type'))): @@ -1379,8 +1372,7 @@ def check_argument_count(self, actual_kinds: List[ArgKind], actual_names: Optional[Sequence[Optional[str]]], formal_to_actual: List[List[int]], - context: Optional[Context], - messages: Optional[MessageBuilder]) -> bool: + context: Optional[Context]) -> bool: """Check that there is a value for all required arguments to a function. Also check that there are no duplicate values for arguments. Report found errors @@ -1388,9 +1380,7 @@ def check_argument_count(self, Return False if there were any errors. Otherwise return True """ - if messages: - assert context, "Internal error: messages given without context" - elif context is None: + if context is None: # Avoid "is None" checks context = TempNode(AnyType(TypeOfAny.special_form)) @@ -1402,32 +1392,29 @@ def check_argument_count(self, all_actuals.extend(actuals) ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments( - callee, actual_types, actual_kinds, actual_names, all_actuals, context, messages) + callee, actual_types, actual_kinds, actual_names, all_actuals, context) # Check for too many or few values for formals. for i, kind in enumerate(callee.arg_kinds): if kind.is_required() and not formal_to_actual[i] and not is_unexpected_arg_error: # No actual for a mandatory formal - if messages: - if kind.is_positional(): - messages.too_few_arguments(callee, context, actual_names) - else: - argname = callee.arg_names[i] or "?" - messages.missing_named_argument(callee, context, argname) + if kind.is_positional(): + self.msg.too_few_arguments(callee, context, actual_names) + else: + argname = callee.arg_names[i] or "?" + self.msg.missing_named_argument(callee, context, argname) ok = False elif not kind.is_star() and is_duplicate_mapping( formal_to_actual[i], actual_types, actual_kinds): if (self.chk.in_checked_function() or isinstance(get_proper_type(actual_types[formal_to_actual[i][0]]), TupleType)): - if messages: - messages.duplicate_argument_value(callee, i, context) + self.msg.duplicate_argument_value(callee, i, context) ok = False elif (kind.is_named() and formal_to_actual[i] and actual_kinds[formal_to_actual[i][0]] not in [nodes.ARG_NAMED, nodes.ARG_STAR2]): # Positional argument when expecting a keyword argument. - if messages: - messages.too_many_positional_arguments(callee, context) + self.msg.too_many_positional_arguments(callee, context) ok = False return ok @@ -1437,8 +1424,7 @@ def check_for_extra_actual_arguments(self, actual_kinds: List[ArgKind], actual_names: Optional[Sequence[Optional[str]]], all_actuals: List[int], - context: Context, - messages: Optional[MessageBuilder]) -> Tuple[bool, bool]: + context: Context) -> Tuple[bool, bool]: """Check for extra actual arguments. Return tuple (was everything ok, @@ -1459,15 +1445,13 @@ def check_for_extra_actual_arguments(self, # Extra actual: not matched by a formal argument. ok = False if kind != nodes.ARG_NAMED: - if messages: - messages.too_many_arguments(callee, context) + self.msg.too_many_arguments(callee, context) else: - if messages: - assert actual_names, "Internal error: named kinds without names given" - act_name = actual_names[i] - assert act_name is not None - act_type = actual_types[i] - messages.unexpected_keyword_argument(callee, act_name, act_type, context) + assert actual_names, "Internal error: named kinds without names given" + act_name = actual_names[i] + assert act_name is not None + act_type = actual_types[i] + self.msg.unexpected_keyword_argument(callee, act_name, act_type, context) is_unexpected_arg_error = True elif ((kind == nodes.ARG_STAR and nodes.ARG_STAR not in callee.arg_kinds) or kind == nodes.ARG_STAR2): @@ -1475,14 +1459,13 @@ def check_for_extra_actual_arguments(self, if isinstance(actual_type, (TupleType, TypedDictType)): if all_actuals.count(i) < len(actual_type.items): # Too many tuple/dict items as some did not match. - if messages: - if (kind != nodes.ARG_STAR2 - or not isinstance(actual_type, TypedDictType)): - messages.too_many_arguments(callee, context) - else: - messages.too_many_arguments_from_typed_dict(callee, actual_type, - context) - is_unexpected_arg_error = True + if (kind != nodes.ARG_STAR2 + or not isinstance(actual_type, TypedDictType)): + self.msg.too_many_arguments(callee, context) + else: + self.msg.too_many_arguments_from_typed_dict(callee, actual_type, + context) + is_unexpected_arg_error = True ok = False # *args/**kwargs can be applied even if the function takes a fixed # number of positional arguments. This may succeed at runtime. @@ -1496,7 +1479,6 @@ def check_argument_types(self, callee: CallableType, formal_to_actual: List[List[int]], context: Context, - messages: Optional[MessageBuilder] = None, check_arg: Optional[ArgChecker] = None, object_type: Optional[Type] = None) -> None: """Check argument types against a callable type. @@ -1505,7 +1487,6 @@ def check_argument_types(self, The check_call docstring describes some of the arguments. """ - messages = messages or self.msg check_arg = check_arg or self.check_arg # Keep track of consumed tuple *arg items. mapper = ArgTypeExpander(self.argument_infer_context()) @@ -1518,17 +1499,17 @@ def check_argument_types(self, # Check that a *arg is valid as varargs. if (actual_kind == nodes.ARG_STAR and not self.is_valid_var_arg(actual_type)): - messages.invalid_var_arg(actual_type, context) + self.msg.invalid_var_arg(actual_type, context) if (actual_kind == nodes.ARG_STAR2 and not self.is_valid_keyword_var_arg(actual_type)): is_mapping = is_subtype(actual_type, self.chk.named_type('typing.Mapping')) - messages.invalid_keyword_var_arg(actual_type, is_mapping, context) + self.msg.invalid_keyword_var_arg(actual_type, is_mapping, context) expanded_actual = mapper.expand_actual_type( actual_type, actual_kind, callee.arg_names[i], callee.arg_kinds[i]) check_arg(expanded_actual, actual_type, arg_kinds[actual], callee.arg_types[i], - actual + 1, i + 1, callee, object_type, args[actual], context, messages) + actual + 1, i + 1, callee, object_type, args[actual], context) def check_arg(self, caller_type: Type, @@ -1540,15 +1521,14 @@ def check_arg(self, callee: CallableType, object_type: Optional[Type], context: Context, - outer_context: Context, - messages: MessageBuilder) -> None: + outer_context: Context) -> None: """Check the type of a single argument in a call.""" caller_type = get_proper_type(caller_type) original_caller_type = get_proper_type(original_caller_type) callee_type = get_proper_type(callee_type) if isinstance(caller_type, DeletedType): - messages.deleted_as_rvalue(caller_type, context) + self.msg.deleted_as_rvalue(caller_type, context) # Only non-abstract non-protocol class can be given where Type[...] is expected... elif (isinstance(caller_type, CallableType) and isinstance(callee_type, TypeType) and caller_type.is_type_obj() and @@ -1559,7 +1539,7 @@ def check_arg(self, elif not is_subtype(caller_type, callee_type, options=self.chk.options): if self.chk.should_suppress_optional_error([caller_type, callee_type]): return - code = messages.incompatible_argument(n, + code = self.msg.incompatible_argument(n, m, callee, original_caller_type, @@ -1567,7 +1547,7 @@ def check_arg(self, object_type=object_type, context=context, outer_context=outer_context) - messages.incompatible_argument_note(original_caller_type, callee_type, context, + self.msg.incompatible_argument_note(original_caller_type, callee_type, context, code=code) def check_overload_call(self, @@ -1577,8 +1557,7 @@ def check_overload_call(self, arg_names: Optional[Sequence[Optional[str]]], callable_name: Optional[str], object_type: Optional[Type], - context: Context, - arg_messages: MessageBuilder) -> Tuple[Type, Type]: + context: Context) -> Tuple[Type, Type]: """Checks a call to an overloaded function.""" arg_types = self.infer_arg_types_in_empty_context(args) # Step 1: Filter call targets to remove ones where the argument counts don't match @@ -1595,12 +1574,11 @@ def check_overload_call(self, union_interrupted = False # did we try all union combinations? if any(self.real_union(arg) for arg in arg_types): try: - with arg_messages.filter_errors(): + with self.msg.filter_errors(): unioned_return = self.union_overload_result(plausible_targets, args, arg_types, arg_kinds, arg_names, callable_name, object_type, - context, - arg_messages=arg_messages) + context) except TooManyUnions: union_interrupted = True else: @@ -1620,7 +1598,7 @@ def check_overload_call(self, # Step 3: We try checking each branch one-by-one. inferred_result = self.infer_overload_return_type(plausible_targets, args, arg_types, arg_kinds, arg_names, callable_name, - object_type, context, arg_messages) + object_type, context) # If any of checks succeed, stop early. if inferred_result is not None and unioned_result is not None: # Both unioned and direct checks succeeded, choose the more precise type. @@ -1664,11 +1642,10 @@ def check_overload_call(self, code = None else: code = codes.OPERATOR - arg_messages.no_variant_matches_arguments( + self.msg.no_variant_matches_arguments( callee, arg_types, context, code=code) result = self.check_call(target, args, arg_kinds, context, arg_names, - arg_messages=arg_messages, callable_name=callable_name, object_type=object_type) if union_interrupted: @@ -1712,14 +1689,15 @@ def has_shape(typ: Type) -> bool: typ.arg_kinds, typ.arg_names, lambda i: arg_types[i]) - if self.check_argument_count(typ, arg_types, arg_kinds, arg_names, - formal_to_actual, None, None): - if args_have_var_arg and typ.is_var_arg: - star_matches.append(typ) - elif args_have_kw_arg and typ.is_kw_arg: - star_matches.append(typ) - else: - matches.append(typ) + with self.msg.filter_errors(): + if self.check_argument_count(typ, arg_types, arg_kinds, arg_names, + formal_to_actual, None): + if args_have_var_arg and typ.is_var_arg: + star_matches.append(typ) + elif args_have_kw_arg and typ.is_kw_arg: + star_matches.append(typ) + else: + matches.append(typ) return star_matches + matches @@ -1732,7 +1710,6 @@ def infer_overload_return_type(self, callable_name: Optional[str], object_type: Optional[Type], context: Context, - arg_messages: Optional[MessageBuilder] = None, ) -> Optional[Tuple[Type, Type]]: """Attempts to find the first matching callable from the given list. @@ -1744,7 +1721,6 @@ def infer_overload_return_type(self, Assumes all of the given targets have argument counts compatible with the caller. """ - arg_messages = self.msg if arg_messages is None else arg_messages matches: List[CallableType] = [] return_types: List[Type] = [] inferred_types: List[Type] = [] @@ -1759,7 +1735,6 @@ def infer_overload_return_type(self, arg_kinds=arg_kinds, arg_names=arg_names, context=context, - arg_messages=self.msg, callable_name=callable_name, object_type=object_type) is_match = not w.has_new_errors() @@ -1788,7 +1763,6 @@ def infer_overload_return_type(self, arg_kinds=arg_kinds, arg_names=arg_names, context=context, - arg_messages=arg_messages, callable_name=callable_name, object_type=object_type) else: @@ -1822,7 +1796,6 @@ def union_overload_result(self, callable_name: Optional[str], object_type: Optional[Type], context: Context, - arg_messages: Optional[MessageBuilder] = None, level: int = 0 ) -> Optional[List[Tuple[Type, Type]]]: """Accepts a list of overload signatures and attempts to match calls by destructuring @@ -1847,7 +1820,7 @@ def union_overload_result(self, with self.type_overrides_set(args, arg_types): res = self.infer_overload_return_type(plausible_targets, args, arg_types, arg_kinds, arg_names, callable_name, - object_type, context, arg_messages) + object_type, context) if res is not None: return [res] return None @@ -1857,7 +1830,7 @@ def union_overload_result(self, with self.type_overrides_set(args, arg_types): direct = self.infer_overload_return_type(plausible_targets, args, arg_types, arg_kinds, arg_names, callable_name, - object_type, context, arg_messages) + object_type, context) if direct is not None and not isinstance(get_proper_type(direct[0]), (UnionType, AnyType)): # We only return non-unions soon, to avoid greedy match. @@ -1873,7 +1846,7 @@ def union_overload_result(self, new_arg_types[idx] = item sub_result = self.union_overload_result(plausible_targets, args, new_arg_types, arg_kinds, arg_names, callable_name, - object_type, context, arg_messages, + object_type, context, level + 1) if sub_result is not None: res_items.extend(sub_result) @@ -2003,10 +1976,11 @@ def erased_signature_similarity(self, callee.arg_names, lambda i: arg_types[i]) - if not self.check_argument_count(callee, arg_types, arg_kinds, arg_names, - formal_to_actual, None, None): - # Too few or many arguments -> no match. - return False + with self.msg.filter_errors(): + if not self.check_argument_count(callee, arg_types, arg_kinds, arg_names, + formal_to_actual, None): + # Too few or many arguments -> no match. + return False def check_arg(caller_type: Type, original_ccaller_type: Type, @@ -2017,8 +1991,7 @@ def check_arg(caller_type: Type, callee: CallableType, object_type: Optional[Type], context: Context, - outer_context: Context, - messages: MessageBuilder) -> None: + outer_context: Context) -> None: if not arg_approximate_similarity(caller_type, callee_type): # No match -- exit early since none of the remaining work can change # the result. @@ -2052,8 +2025,7 @@ def check_union_call(self, args: List[Expression], arg_kinds: List[ArgKind], arg_names: Optional[Sequence[Optional[str]]], - context: Context, - arg_messages: MessageBuilder) -> Tuple[Type, Type]: + context: Context) -> Tuple[Type, Type]: with self.msg.disable_type_names(): results = [ self.check_call( @@ -2062,7 +2034,6 @@ def check_union_call(self, arg_kinds, context, arg_names, - arg_messages=arg_messages, ) for subtype in callee.relevant_items() ] @@ -2421,7 +2392,6 @@ def check_method_call_by_name(self, args: List[Expression], arg_kinds: List[ArgKind], context: Context, - local_errors: Optional[MessageBuilder] = None, original_type: Optional[Type] = None ) -> Tuple[Type, Type]: """Type check a call to a named method on an object. @@ -2429,21 +2399,20 @@ def check_method_call_by_name(self, Return tuple (result type, inferred method type). The 'original_type' is used for error messages. """ - local_errors = local_errors or self.msg original_type = original_type or base_type # Unions are special-cased to allow plugins to act on each element of the union. base_type = get_proper_type(base_type) if isinstance(base_type, UnionType): return self.check_union_method_call_by_name(method, base_type, args, arg_kinds, - context, local_errors, original_type) + context, original_type) method_type = analyze_member_access(method, base_type, context, False, False, True, - local_errors, original_type=original_type, + self.msg, original_type=original_type, chk=self.chk, in_literal_context=self.is_literal_context()) return self.check_method_call( - method, base_type, method_type, args, arg_kinds, context, local_errors) + method, base_type, method_type, args, arg_kinds, context) def check_union_method_call_by_name(self, method: str, @@ -2451,7 +2420,6 @@ def check_union_method_call_by_name(self, args: List[Expression], arg_kinds: List[ArgKind], context: Context, - local_errors: MessageBuilder, original_type: Optional[Type] = None ) -> Tuple[Type, Type]: """Type check a call to a named method on an object with union type. @@ -2465,10 +2433,10 @@ def check_union_method_call_by_name(self, for typ in base_type.relevant_items(): # Format error messages consistently with # mypy.checkmember.analyze_union_member_access(). - with local_errors.disable_type_names(): + with self.msg.disable_type_names(): item, meth_item = self.check_method_call_by_name( method, typ, args, arg_kinds, - context, local_errors, original_type, + context, original_type, ) res.append(item) meth_res.append(meth_item) @@ -2480,8 +2448,7 @@ def check_method_call(self, method_type: Type, args: List[Expression], arg_kinds: List[ArgKind], - context: Context, - local_errors: Optional[MessageBuilder] = None) -> Tuple[Type, Type]: + context: Context) -> Tuple[Type, Type]: """Type check a call to a method with the given name and type on an object. Return tuple (result type, inferred method type). @@ -2494,8 +2461,7 @@ def check_method_call(self, callable_name, method_type, args, arg_kinds, context, object_type=object_type) return self.check_call(method_type, args, arg_kinds, - context, arg_messages=local_errors, - callable_name=callable_name, object_type=base_type) + context, callable_name=callable_name, object_type=base_type) def check_op_reversible(self, op_name: str, @@ -2503,8 +2469,7 @@ def check_op_reversible(self, left_expr: Expression, right_type: Type, right_expr: Expression, - context: Context, - msg: MessageBuilder) -> Tuple[Type, Type]: + context: Context) -> Tuple[Type, Type]: def lookup_operator(op_name: str, base_type: Type) -> Optional[Type]: """Looks up the given operator and returns the corresponding type, if it exists.""" @@ -2683,7 +2648,7 @@ def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]: # TODO: Remove this extra case return result - msg.add_errors(errors[0]) + self.msg.add_errors(errors[0]) if len(results) == 1: return results[0] else: @@ -2722,8 +2687,7 @@ def check_op(self, method: str, base_type: Type, left_expr=TempNode(left_possible_type, context=context), right_type=right_type, right_expr=arg, - context=context, - msg=self.msg) + context=context) all_results.append(result) all_inferred.append(inferred) @@ -2764,8 +2728,7 @@ def check_op(self, method: str, base_type: Type, left_expr=TempNode(left_possible_type, context=context), right_type=right_possible_type, right_expr=right_expr, - context=context, - msg=self.msg) + context=context) all_results.append(result) all_inferred.append(inferred) @@ -2798,7 +2761,6 @@ def check_op(self, method: str, base_type: Type, args=[arg], arg_kinds=[ARG_POS], context=context, - local_errors=self.msg, ) def get_reverse_op_method(self, method: str) -> str: @@ -3073,9 +3035,7 @@ def nonliteral_tuple_index_helper(self, left_type: TupleType, index: Expression) def visit_typeddict_index_expr(self, td_type: TypedDictType, index: Expression, - local_errors: Optional[MessageBuilder] = None ) -> Type: - local_errors = local_errors or self.msg if isinstance(index, (StrExpr, UnicodeExpr)): key_names = [index.value] else: @@ -3095,14 +3055,14 @@ def visit_typeddict_index_expr(self, td_type: TypedDictType, and key_type.fallback.type.fullname != 'builtins.bytes'): key_names.append(key_type.value) else: - local_errors.typeddict_key_must_be_string_literal(td_type, index) + self.msg.typeddict_key_must_be_string_literal(td_type, index) return AnyType(TypeOfAny.from_error) value_types = [] for key_name in key_names: value_type = td_type.items.get(key_name) if value_type is None: - local_errors.typeddict_key_not_found(td_type, key_name, index) + self.msg.typeddict_key_not_found(td_type, key_name, index) return AnyType(TypeOfAny.from_error) else: value_types.append(value_type) @@ -3827,8 +3787,7 @@ def check_generator_or_comprehension(self, gen: GeneratorExpr, self.chk.named_type('builtins.function'), name=id_for_messages, variables=[tv]) - return self.check_call(constructor, - [gen.left_expr], [nodes.ARG_POS], gen)[0] + return self.check_call(constructor, [gen.left_expr], [nodes.ARG_POS], gen)[0] def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type: """Type check a dictionary comprehension.""" diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 84b9acae1aa2..7836da019257 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -428,8 +428,7 @@ def get_mapping_item_type(self, with self.msg.filter_errors() as local_errors: result = self.get_simple_mapping_item_type(pattern, mapping_type, - key, - self.msg) + key) if local_errors.has_new_errors(): result = None @@ -437,22 +436,19 @@ def get_mapping_item_type(self, with self.msg.filter_errors(): result = self.get_simple_mapping_item_type(pattern, mapping_type, - key, - self.msg) + key) return result def get_simple_mapping_item_type(self, pattern: MappingPattern, mapping_type: Type, - key: Expression, - local_errors: MessageBuilder + key: Expression ) -> Type: result, _ = self.chk.expr_checker.check_method_call_by_name('__getitem__', mapping_type, [key], [ARG_POS], - pattern, - local_errors=local_errors) + pattern) return result def visit_class_pattern(self, o: ClassPattern) -> PatternType: diff --git a/mypy/errors.py b/mypy/errors.py index 6dc0a21e7904..87a59e3ca9ed 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -274,24 +274,6 @@ def initialize(self) -> None: def reset(self) -> None: self.initialize() - def copy(self) -> 'Errors': - new = Errors(self.show_error_context, - self.show_column_numbers, - self.show_error_codes, - self.pretty, - self.read_source, - self.show_absolute_path, - self.enabled_error_codes, - self.disabled_error_codes, - self.many_errors_threshold) - new.file = self.file - new.import_ctx = self.import_ctx[:] - new.function_or_member = self.function_or_member[:] - new.target_module = self.target_module - new.scope = self.scope - new.seen_import_error = self.seen_import_error - return new - def set_ignore_prefix(self, prefix: str) -> None: """Set path prefix that will be removed from all paths.""" prefix = os.path.normpath(prefix) @@ -651,10 +633,6 @@ def is_errors_for_file(self, file: str) -> bool: """Are there any errors for the given file?""" return file in self.error_info_map - def most_recent_error_location(self) -> Tuple[int, int]: - info = self.error_info_map[self.file][-1] - return info.line, info.column - def raise_error(self, use_stdout: bool = True) -> None: """Raise a CompileError with the generated messages. From 33dbbd2cde45fe688a716f5d10e51d1bd6a0b096 Mon Sep 17 00:00:00 2001 From: eggplants Date: Sun, 1 May 2022 13:32:35 +0900 Subject: [PATCH 36/79] fix: dead link in readme (#12702) `introduction.rst` has moved into `index.rst` in #12348. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8f4df995f3a..1be59f0b0027 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ number = input("What is your favourite number?") print("It is", number + 1) # error: Unsupported operand types for + ("str" and "int") ``` -See [the documentation](https://mypy.readthedocs.io/en/stable/introduction.html) for more examples. +See [the documentation](https://mypy.readthedocs.io/en/stable/index.html) for more examples. In particular, see: - [type hints cheat sheet](https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html) From dd6d78615455fdd9895cff1691f2de6bb2893a48 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 30 Apr 2022 23:08:25 -0600 Subject: [PATCH 37/79] Use callback protocol instead of mypy_extensions (#12701) --- mypy/typeanal.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 84d9758b9a57..41225a6061f3 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -6,8 +6,7 @@ from mypy.backports import OrderedDict from typing import Callable, List, Optional, Set, Tuple, Iterator, TypeVar, Iterable, Sequence -from typing_extensions import Final -from mypy_extensions import DefaultNamedArg +from typing_extensions import Final, Protocol from mypy.messages import MessageBuilder, quote_type_string, format_type_bare from mypy.options import Options @@ -1241,8 +1240,15 @@ def set_allow_param_spec_literals(self, to: bool) -> Iterator[None]: TypeVarLikeList = List[Tuple[str, TypeVarLikeExpr]] -# Mypyc doesn't support callback protocols yet. -MsgCallback = Callable[[str, Context, DefaultNamedArg(Optional[ErrorCode], 'code')], None] + +class MsgCallback(Protocol): + def __call__( + self, + __msg: str, + __ctx: Context, + *, + code: Optional[ErrorCode] = None + ) -> None: ... def get_omitted_any(disallow_any: bool, fail: MsgCallback, note: MsgCallback, From a56ebec8bdc1f22c7bf801653f61c9963dd3fc90 Mon Sep 17 00:00:00 2001 From: Hugues Date: Sat, 30 Apr 2022 22:55:17 -0700 Subject: [PATCH 38/79] checkexpr: speedup argument count check (#12703) Replace List with Dict to avoid O(n**2) behavior in `check_for_extra_actual_arguments` This manifests for instance when initializing a set with `set([...])` instead of a set literal `{...}`. For large sets of literals (~80k entries) this change bring the typechecking of a single set initializer from over 1 min down to under 1s. --- mypy/checkexpr.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 89f2cd19a2a7..bd69c1427dce 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1386,10 +1386,11 @@ def check_argument_count(self, # TODO(jukka): We could return as soon as we find an error if messages is None. - # Collect list of all actual arguments matched to formal arguments. - all_actuals: List[int] = [] + # Collect dict of all actual arguments matched to formal arguments, with occurrence count + all_actuals: Dict[int, int] = {} for actuals in formal_to_actual: - all_actuals.extend(actuals) + for a in actuals: + all_actuals[a] = all_actuals.get(a, 0) + 1 ok, is_unexpected_arg_error = self.check_for_extra_actual_arguments( callee, actual_types, actual_kinds, actual_names, all_actuals, context) @@ -1423,7 +1424,7 @@ def check_for_extra_actual_arguments(self, actual_types: List[Type], actual_kinds: List[ArgKind], actual_names: Optional[Sequence[Optional[str]]], - all_actuals: List[int], + all_actuals: Dict[int, int], context: Context) -> Tuple[bool, bool]: """Check for extra actual arguments. @@ -1457,7 +1458,7 @@ def check_for_extra_actual_arguments(self, or kind == nodes.ARG_STAR2): actual_type = get_proper_type(actual_types[i]) if isinstance(actual_type, (TupleType, TypedDictType)): - if all_actuals.count(i) < len(actual_type.items): + if all_actuals.get(i, 0) < len(actual_type.items): # Too many tuple/dict items as some did not match. if (kind != nodes.ARG_STAR2 or not isinstance(actual_type, TypedDictType)): From 1662fe8142b6b115bb8da255b1a02bff9e8b7868 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 May 2022 13:58:19 -0600 Subject: [PATCH 39/79] mypy/test: run pyupgrade (#12710) Re-attempt of #10741 Ran: `pyupgrade --py36-plus $(fd -e py) --keep-runtime-typing` I mostly only needed to change things where NamedTuple comments got dropped. Co-authored-by: hauntsaninja <> --- mypy/test/data.py | 27 +++++++++++++++----------- mypy/test/helpers.py | 12 ++++++------ mypy/test/test_find_sources.py | 12 ++++++------ mypy/test/testargs.py | 10 +++++----- mypy/test/testcheck.py | 2 +- mypy/test/testcmdline.py | 4 ++-- mypy/test/testdeps.py | 2 +- mypy/test/testfinegrained.py | 4 ++-- mypy/test/testgraph.py | 2 +- mypy/test/testmerge.py | 10 +++++----- mypy/test/testpep561.py | 12 ++++++------ mypy/test/testpythoneval.py | 4 ++-- mypy/test/testsemanal.py | 8 ++++---- mypy/test/teststubgen.py | 2 +- mypy/test/teststubtest.py | 35 +++++++++++++++++----------------- mypy/test/testsubtypes.py | 4 ++-- mypy/test/testtypegen.py | 2 +- mypy/test/testtypes.py | 18 ++++++++--------- 18 files changed, 87 insertions(+), 83 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index e18ac142d15e..9f1c6a1aa24c 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -16,14 +16,19 @@ root_dir = os.path.normpath(PREFIX) + # File modify/create operation: copy module contents from source_path. -UpdateFile = NamedTuple('UpdateFile', [('module', str), - ('content', str), - ('target_path', str)]) +class UpdateFile(NamedTuple): + module: str + content: str + target_path: str + # File delete operation: delete module file. -DeleteFile = NamedTuple('DeleteFile', [('module', str), - ('path', str)]) +class DeleteFile(NamedTuple): + module: str + path: str + FileOperation = Union[UpdateFile, DeleteFile] @@ -100,9 +105,9 @@ def parse_test_case(case: 'DataDrivenTestCase') -> None: # File/directory to delete during a multi-step test case assert item.arg is not None m = re.match(r'(.*)\.([0-9]+)$', item.arg) - assert m, 'Invalid delete section: {}'.format(item.arg) + assert m, f'Invalid delete section: {item.arg}' num = int(m.group(2)) - assert num >= 2, "Can't delete during step {}".format(num) + assert num >= 2, f"Can't delete during step {num}" full = join(base_path, m.group(1)) deleted_paths.setdefault(num, set()).add(full) elif re.match(r'out[0-9]*$', item.id): @@ -271,7 +276,7 @@ def runtest(self) -> None: if save_dir: assert self.tmpdir is not None target_dir = os.path.join(save_dir, os.path.basename(self.tmpdir.name)) - print("Copying data from test {} to {}".format(self.name, target_dir)) + print(f"Copying data from test {self.name} to {target_dir}") if not os.path.isabs(target_dir): assert self.old_cwd target_dir = os.path.join(self.old_cwd, target_dir) @@ -339,7 +344,7 @@ def repr_failure(self, excinfo: Any, style: Optional[Any] = None) -> str: self.parent._prunetraceback(excinfo) excrepr = excinfo.getrepr(style='short') - return "data: {}:{}:\n{}".format(self.file, self.line, excrepr) + return f"data: {self.file}:{self.line}:\n{excrepr}" def find_steps(self) -> List[List[FileOperation]]: """Return a list of descriptions of file operations for each incremental step. @@ -493,7 +498,7 @@ def expand_errors(input: List[str], output: List[str], fnam: str) -> None: message = message.replace('\\#', '#') # adds back escaped # character if col is None: output.append( - '{}:{}: {}: {}'.format(fnam, i + 1, severity, message)) + f'{fnam}:{i + 1}: {severity}: {message}') else: output.append('{}:{}:{}: {}: {}'.format( fnam, i + 1, col, severity, message)) @@ -625,7 +630,7 @@ def collect(self) -> Iterator['DataFileCollector']: suite: DataSuite = self.obj assert os.path.isdir(suite.data_prefix), \ - 'Test data prefix ({}) not set correctly'.format(suite.data_prefix) + f'Test data prefix ({suite.data_prefix}) not set correctly' for data_file in suite.files: yield DataFileCollector.from_parent(parent=self, name=data_file) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 5046c46eaa43..af1fefe67ffd 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -79,7 +79,7 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str], if i >= len(actual) or expected[i] != actual[i]: if first_diff < 0: first_diff = i - sys.stderr.write(' {:<45} (diff)'.format(expected[i])) + sys.stderr.write(f' {expected[i]:<45} (diff)') else: e = expected[i] sys.stderr.write(' ' + e[:width]) @@ -96,7 +96,7 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str], for j in range(num_skip_start, len(actual) - num_skip_end): if j >= len(expected) or expected[j] != actual[j]: - sys.stderr.write(' {:<45} (diff)'.format(actual[j])) + sys.stderr.write(f' {actual[j]:<45} (diff)') else: a = actual[j] sys.stderr.write(' ' + a[:width]) @@ -217,8 +217,8 @@ def show_align_message(s1: str, s2: str) -> None: extra = '...' # Write a chunk of both lines, aligned. - sys.stderr.write(' E: {}{}\n'.format(s1[:maxw], extra)) - sys.stderr.write(' A: {}{}\n'.format(s2[:maxw], extra)) + sys.stderr.write(f' E: {s1[:maxw]}{extra}\n') + sys.stderr.write(f' A: {s2[:maxw]}{extra}\n') # Write an indicator character under the different columns. sys.stderr.write(' ') for j in range(min(maxw, max(len(s1), len(s2)))): @@ -370,7 +370,7 @@ def parse_options(program_text: str, testcase: DataDrivenTestCase, options = Options() flags = re.search('# flags: (.*)$', program_text, flags=re.MULTILINE) if incremental_step > 1: - flags2 = re.search('# flags{}: (.*)$'.format(incremental_step), program_text, + flags2 = re.search(f'# flags{incremental_step}: (.*)$', program_text, flags=re.MULTILINE) if flags2: flags = flags2 @@ -457,7 +457,7 @@ def check_test_output_files(testcase: DataDrivenTestCase, raise AssertionError( 'Expected file {} was not produced by test case{}'.format( path, ' on step %d' % step if testcase.output2 else '')) - with open(path, 'r', encoding='utf8') as output_file: + with open(path, encoding='utf8') as output_file: actual_output_content = output_file.read() if isinstance(expected_content, Pattern): diff --git a/mypy/test/test_find_sources.py b/mypy/test/test_find_sources.py index 53da9c384bd2..e9e7432327e7 100644 --- a/mypy/test/test_find_sources.py +++ b/mypy/test/test_find_sources.py @@ -26,7 +26,7 @@ def isdir(self, dir: str) -> bool: def listdir(self, dir: str) -> List[str]: if not dir.endswith(os.sep): dir += os.sep - return list(set(f[len(dir):].split(os.sep)[0] for f in self.files if f.startswith(dir))) + return list({f[len(dir):].split(os.sep)[0] for f in self.files if f.startswith(dir)}) def init_under_package_root(self, file: str) -> bool: return False @@ -278,15 +278,15 @@ def test_find_sources_exclude(self) -> None: # default for excluded_dir in ["site-packages", ".whatever", "node_modules", ".x/.z"]: - fscache = FakeFSCache({"/dir/a.py", "/dir/venv/{}/b.py".format(excluded_dir)}) + fscache = FakeFSCache({"/dir/a.py", f"/dir/venv/{excluded_dir}/b.py"}) assert find_sources(["/"], options, fscache) == [("a", "/dir")] with pytest.raises(InvalidSourceList): find_sources(["/dir/venv/"], options, fscache) - assert find_sources(["/dir/venv/{}".format(excluded_dir)], options, fscache) == [ - ("b", "/dir/venv/{}".format(excluded_dir)) + assert find_sources([f"/dir/venv/{excluded_dir}"], options, fscache) == [ + ("b", f"/dir/venv/{excluded_dir}") ] - assert find_sources(["/dir/venv/{}/b.py".format(excluded_dir)], options, fscache) == [ - ("b", "/dir/venv/{}".format(excluded_dir)) + assert find_sources([f"/dir/venv/{excluded_dir}/b.py"], options, fscache) == [ + ("b", f"/dir/venv/{excluded_dir}") ] files = { diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index f26e897fbb10..8d74207f353f 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -27,25 +27,25 @@ def test_executable_inference(self) -> None: base = ['file.py'] # dummy file # test inference given one (infer the other) - matching_version = base + ['--python-version={}'.format(sys_ver_str)] + matching_version = base + [f'--python-version={sys_ver_str}'] _, options = process_options(matching_version) assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable - matching_version = base + ['--python-executable={}'.format(sys.executable)] + matching_version = base + [f'--python-executable={sys.executable}'] _, options = process_options(matching_version) assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable # test inference given both - matching_version = base + ['--python-version={}'.format(sys_ver_str), - '--python-executable={}'.format(sys.executable)] + matching_version = base + [f'--python-version={sys_ver_str}', + f'--python-executable={sys.executable}'] _, options = process_options(matching_version) assert options.python_version == sys.version_info[:2] assert options.python_executable == sys.executable # test that --no-site-packages will disable executable inference - matching_version = base + ['--python-version={}'.format(sys_ver_str), + matching_version = base + [f'--python-version={sys_ver_str}', '--no-site-packages'] _, options = process_options(matching_version) assert options.python_version == sys.version_info[:2] diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 4f6c82877775..2f74e543d469 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -328,7 +328,7 @@ def parse_module(self, """ m = re.search('# cmd: mypy -m ([a-zA-Z0-9_. ]+)$', program_text, flags=re.MULTILINE) if incremental_step > 1: - alt_regex = '# cmd{}: mypy -m ([a-zA-Z0-9_. ]+)$'.format(incremental_step) + alt_regex = f'# cmd{incremental_step}: mypy -m ([a-zA-Z0-9_. ]+)$' alt_m = re.search(alt_regex, program_text, flags=re.MULTILINE) if alt_m is not None: # Optionally return a different command if in a later step diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 9fafb1f36cae..d58d10087c80 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -46,7 +46,7 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None: program_path = os.path.join(test_temp_dir, program) with open(program_path, 'w', encoding='utf8') as file: for s in testcase.input: - file.write('{}\n'.format(s)) + file.write(f'{s}\n') args = parse_args(testcase.input[0]) custom_cwd = parse_cwd(testcase.input[1]) if len(testcase.input) > 1 else None args.append('--show-traceback') @@ -94,7 +94,7 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None: out = normalize_error_messages(err + out) obvious_result = 1 if out else 0 if obvious_result != result: - out.append('== Return code: {}'.format(result)) + out.append(f'== Return code: {result}') expected_out = testcase.output if step == 1 else testcase.output2[step] # Strip "tmp/" out of the test so that # E: works... expected_out = [s.replace("tmp" + os.sep, "") for s in expected_out] diff --git a/mypy/test/testdeps.py b/mypy/test/testdeps.py index 0b6f4958db75..d3d184be7f01 100644 --- a/mypy/test/testdeps.py +++ b/mypy/test/testdeps.py @@ -67,7 +67,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: if source.startswith((' Li def get_suggest(self, program_text: str, incremental_step: int) -> List[Tuple[str, str]]: step_bit = '1?' if incremental_step == 1 else str(incremental_step) - regex = '# suggest{}: (--[a-zA-Z0-9_\\-./=?^ ]+ )*([a-zA-Z0-9_.:/?^ ]+)$'.format(step_bit) + regex = f'# suggest{step_bit}: (--[a-zA-Z0-9_\\-./=?^ ]+ )*([a-zA-Z0-9_.:/?^ ]+)$' m = re.findall(regex, program_text, flags=re.MULTILINE) return m diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index d713828ca44c..7d32db2b1c1c 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -29,7 +29,7 @@ def test_topsort(self) -> None: def test_scc(self) -> None: vertices = {"A", "B", "C", "D"} edges: Dict[str, List[str]] = {"A": ["B", "C"], "B": ["C"], "C": ["B", "D"], "D": []} - sccs = set(frozenset(x) for x in strongly_connected_components(vertices, edges)) + sccs = {frozenset(x) for x in strongly_connected_components(vertices, edges)} assert_equal(sccs, {frozenset({'A'}), frozenset({'B', 'C'}), diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 92562d10134d..2e2f6b67d34a 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -164,11 +164,11 @@ def dump_symbol_tables(self, modules: Dict[str, MypyFile]) -> List[str]: return a def dump_symbol_table(self, module_id: str, symtable: SymbolTable) -> List[str]: - a = ['{}:'.format(module_id)] + a = [f'{module_id}:'] for name in sorted(symtable): if name.startswith('__'): continue - a.append(' {}: {}'.format(name, self.format_symbol_table_node(symtable[name]))) + a.append(f' {name}: {self.format_symbol_table_node(symtable[name])}') return a def format_symbol_table_node(self, node: SymbolTableNode) -> str: @@ -180,11 +180,11 @@ def format_symbol_table_node(self, node: SymbolTableNode) -> str: s = '{}<{}>'.format(str(type(node.node).__name__), self.id_mapper.id(node.node)) else: - s = '? ({})'.format(type(node.node)) + s = f'? ({type(node.node)})' if (isinstance(node.node, Var) and node.node.type and not node.node.fullname.startswith('typing.')): typestr = self.format_type(node.node.type) - s += '({})'.format(typestr) + s += f'({typestr})' return s def dump_typeinfos(self, modules: Dict[str, MypyFile]) -> List[str]: @@ -226,7 +226,7 @@ def dump_types(self, manager: FineGrainedBuildManager) -> List[str]: for node in get_subexpressions(tree) if node in all_types} if type_map: - a.append('## {}'.format(module_id)) + a.append(f'## {module_id}') for expr in sorted(type_map, key=lambda n: (n.line, short_type(n), str(n) + str(type_map[n]))): typ = type_map[expr] diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index d30d94c49bee..a49c7e8e5874 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -43,7 +43,7 @@ def virtualenv( proc = subprocess.run([sys.executable, '-m', 'virtualenv', - '-p{}'.format(python_executable), + f'-p{python_executable}', venv_dir], cwd=os.getcwd(), stdout=PIPE, stderr=PIPE) if proc.returncode != 0: err = proc.stdout.decode('utf-8') + proc.stderr.decode('utf-8') @@ -118,12 +118,12 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: program = testcase.name + '.py' with open(program, 'w', encoding='utf-8') as f: for s in testcase.input: - f.write('{}\n'.format(s)) + f.write(f'{s}\n') cmd_line.append(program) cmd_line.extend(['--no-error-summary']) if python_executable != sys.executable: - cmd_line.append('--python-executable={}'.format(python_executable)) + cmd_line.append(f'--python-executable={python_executable}') steps = testcase.find_steps() if steps != [[]]: @@ -144,7 +144,7 @@ def test_pep561(testcase: DataDrivenTestCase) -> None: # Normalize paths so that the output is the same on Windows and Linux/macOS. line = line.replace(test_temp_dir + os.sep, test_temp_dir + '/') output.append(line.rstrip("\r\n")) - iter_count = '' if i == 0 else ' on iteration {}'.format(i + 1) + iter_count = '' if i == 0 else f' on iteration {i + 1}' expected = testcase.output if i == 0 else testcase.output2.get(i + 1, []) assert_string_arrays_equal(expected, output, @@ -189,14 +189,14 @@ def test_mypy_path_is_respected() -> None: mypy_config_path = os.path.join(temp_dir, 'mypy.ini') with open(mypy_config_path, 'w') as mypy_file: mypy_file.write('[mypy]\n') - mypy_file.write('mypy_path = ./{}\n'.format(packages)) + mypy_file.write(f'mypy_path = ./{packages}\n') with virtualenv() as venv: venv_dir, python_executable = venv cmd_line_args = [] if python_executable != sys.executable: - cmd_line_args.append('--python-executable={}'.format(python_executable)) + cmd_line_args.append(f'--python-executable={python_executable}') cmd_line_args.extend(['--config-file', mypy_config_path, '--package', pkg_name]) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 61e6d7fb839f..8002b7410ee8 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -82,8 +82,8 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None mypy_cmdline.append(program_path) with open(program_path, 'w', encoding='utf8') as file: for s in testcase.input: - file.write('{}\n'.format(s)) - mypy_cmdline.append('--cache-dir={}'.format(cache_dir)) + file.write(f'{s}\n') + mypy_cmdline.append(f'--cache-dir={cache_dir}') output = [] # Type check the program. out, err, returncode = api.run(mypy_cmdline) diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index cbe0fb230d7a..a58dc4a3960a 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -126,7 +126,7 @@ def test_semanal_error(testcase: DataDrivenTestCase) -> None: options=get_semanal_options(src, testcase), alt_lib_path=test_temp_dir) a = res.errors - assert a, 'No errors reported in {}, line {}'.format(testcase.file, testcase.line) + assert a, f'No errors reported in {testcase.file}, line {testcase.line}' except CompileError as e: # Verify that there was a compile error and that the error messages # are equivalent. @@ -135,7 +135,7 @@ def test_semanal_error(testcase: DataDrivenTestCase) -> None: a = normalize_error_messages(a) assert_string_arrays_equal( testcase.output, a, - 'Invalid compiler output ({}, line {})'.format(testcase.file, testcase.line)) + f'Invalid compiler output ({testcase.file}, line {testcase.line})') # SymbolNode table export test cases @@ -158,7 +158,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: raise CompileError(a) for f in sorted(result.files.keys()): if f not in ('builtins', 'typing', 'abc'): - a.append('{}:'.format(f)) + a.append(f'{f}:') for s in str(result.files[f].names).split('\n'): a.append(' ' + s) except CompileError as e: @@ -212,6 +212,6 @@ def __str__(self) -> str: not x.startswith('typing.') and not x.startswith('abc.')): ti = ('\n' + ' ').join(str(y).split('\n')) - a.append(' {} : {}'.format(x, ti)) + a.append(f' {x} : {ti}') a[-1] += ')' return '\n'.join(a) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 625ec3f30685..97e3d98597cb 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -627,7 +627,7 @@ def add_file(self, path: str, result: List[str], header: bool) -> None: result.append('<%s was not generated>' % path.replace('\\', '/')) return if header: - result.append('# {}'.format(path[4:])) + result.append(f'# {path[4:]}') with open(path, encoding='utf8') as file: result.extend(file.read().splitlines()) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index 2983c23d5150..a17edbea24aa 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -103,14 +103,14 @@ def run_stubtest( f.write(stubtest_builtins_stub) with open("typing.pyi", "w") as f: f.write(stubtest_typing_stub) - with open("{}.pyi".format(TEST_MODULE_NAME), "w") as f: + with open(f"{TEST_MODULE_NAME}.pyi", "w") as f: f.write(stub) - with open("{}.py".format(TEST_MODULE_NAME), "w") as f: + with open(f"{TEST_MODULE_NAME}.py", "w") as f: f.write(runtime) if config_file: - with open("{}_config.ini".format(TEST_MODULE_NAME), "w") as f: + with open(f"{TEST_MODULE_NAME}_config.ini", "w") as f: f.write(config_file) - options = options + ["--mypy-config-file", "{}_config.ini".format(TEST_MODULE_NAME)] + options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"] output = io.StringIO() with contextlib.redirect_stdout(output): test_stubs( @@ -829,17 +829,16 @@ def test_dunders(self) -> Iterator[Case]: runtime="class B:\n def __call__(self, c, dx): pass", error="B.__call__", ) - if sys.version_info >= (3, 6): - yield Case( - stub=( - "class C:\n" - " def __init_subclass__(\n" - " cls, e: int = ..., **kwargs: int\n" - " ) -> None: ...\n" - ), - runtime="class C:\n def __init_subclass__(cls, e=1, **kwargs): pass", - error=None, - ) + yield Case( + stub=( + "class C:\n" + " def __init_subclass__(\n" + " cls, e: int = ..., **kwargs: int\n" + " ) -> None: ...\n" + ), + runtime="class C:\n def __init_subclass__(cls, e=1, **kwargs): pass", + error=None, + ) if sys.version_info >= (3, 9): yield Case( stub="class D:\n def __class_getitem__(cls, type: type) -> type: ...", @@ -1072,7 +1071,7 @@ def test_allowlist(self) -> None: allowlist = tempfile.NamedTemporaryFile(mode="w+", delete=False) try: with allowlist: - allowlist.write("{}.bad # comment\n# comment".format(TEST_MODULE_NAME)) + allowlist.write(f"{TEST_MODULE_NAME}.bad # comment\n# comment") output = run_stubtest( stub="def bad(number: int, text: str) -> None: ...", @@ -1083,7 +1082,7 @@ def test_allowlist(self) -> None: # test unused entry detection output = run_stubtest(stub="", runtime="", options=["--allowlist", allowlist.name]) - assert output == "note: unused allowlist entry {}.bad\n".format(TEST_MODULE_NAME) + assert output == f"note: unused allowlist entry {TEST_MODULE_NAME}.bad\n" output = run_stubtest( stub="", @@ -1094,7 +1093,7 @@ def test_allowlist(self) -> None: # test regex matching with open(allowlist.name, mode="w+") as f: - f.write("{}.b.*\n".format(TEST_MODULE_NAME)) + f.write(f"{TEST_MODULE_NAME}.b.*\n") f.write("(unused_missing)?\n") f.write("unused.*\n") diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 5776d67a5184..3bfa3e174cfd 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -188,10 +188,10 @@ def test_type_callable_subtyping(self) -> None: # * generic function types def assert_subtype(self, s: Type, t: Type) -> None: - assert is_subtype(s, t), '{} not subtype of {}'.format(s, t) + assert is_subtype(s, t), f'{s} not subtype of {t}' def assert_not_subtype(self, s: Type, t: Type) -> None: - assert not is_subtype(s, t), '{} subtype of {}'.format(s, t) + assert not is_subtype(s, t), f'{s} subtype of {t}' def assert_strict_subtype(self, s: Type, t: Type) -> None: self.assert_subtype(s, t) diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index a10035a8eab5..c652b38ba6fe 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -63,7 +63,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: str(n) + str(map[n]))): ts = str(map[key]).replace('*', '') # Remove erased tags ts = ts.replace('__main__.', '') - a.append('{}({}) : {}'.format(short_type(key), key.line, ts)) + a.append(f'{short_type(key)}({key.line}) : {ts}') except CompileError as e: a = e.messages assert_string_arrays_equal( diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 7458dfa9c6bf..08469a60aba7 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -866,9 +866,9 @@ def assert_simple_join(self, s: Type, t: Type, join: Type) -> None: actual = str(result) expected = str(join) assert_equal(actual, expected, - 'join({}, {}) == {{}} ({{}} expected)'.format(s, t)) - assert is_subtype(s, result), '{} not subtype of {}'.format(s, result) - assert is_subtype(t, result), '{} not subtype of {}'.format(t, result) + f'join({s}, {t}) == {{}} ({{}} expected)') + assert is_subtype(s, result), f'{s} not subtype of {result}' + assert is_subtype(t, result), f'{t} not subtype of {result}' def tuple(self, *a: Type) -> TupleType: return TupleType(list(a), self.fx.std_tuple) @@ -1081,9 +1081,9 @@ def assert_simple_meet(self, s: Type, t: Type, meet: Type) -> None: actual = str(result) expected = str(meet) assert_equal(actual, expected, - 'meet({}, {}) == {{}} ({{}} expected)'.format(s, t)) - assert is_subtype(result, s), '{} not subtype of {}'.format(result, s) - assert is_subtype(result, t), '{} not subtype of {}'.format(result, t) + f'meet({s}, {t}) == {{}} ({{}} expected)') + assert is_subtype(result, s), f'{result} not subtype of {s}' + assert is_subtype(result, t), f'{result} not subtype of {t}' def tuple(self, *a: Type) -> TupleType: return TupleType(list(a), self.fx.std_tuple) @@ -1132,14 +1132,14 @@ def assert_not_same(self, s: Type, t: Type, strict: bool = True) -> None: def assert_simple_is_same(self, s: Type, t: Type, expected: bool, strict: bool) -> None: actual = is_same_type(s, t) assert_equal(actual, expected, - 'is_same_type({}, {}) is {{}} ({{}} expected)'.format(s, t)) + f'is_same_type({s}, {t}) is {{}} ({{}} expected)') if strict: actual2 = (s == t) assert_equal(actual2, expected, - '({} == {}) is {{}} ({{}} expected)'.format(s, t)) + f'({s} == {t}) is {{}} ({{}} expected)') assert_equal(hash(s) == hash(t), expected, - '(hash({}) == hash({}) is {{}} ({{}} expected)'.format(s, t)) + f'(hash({s}) == hash({t}) is {{}} ({{}} expected)') class RemoveLastKnownValueSuite(Suite): From 57e57fa3e8281b0906daf0e0b2e5ee23e19d32bf Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 May 2022 14:31:43 -0600 Subject: [PATCH 40/79] misc: run pyupgrade (#12709) Re-attempt of #10741 Ran: `pyupgrade --py36-plus $(fd -e py) --keep-runtime-typing` I mostly only needed to change things where NamedTuple comments got dropped. Co-authored-by: hauntsaninja <> --- misc/actions_stubs.py | 12 +++++----- misc/analyze_cache.py | 14 ++++++------ misc/apply-cache-diff.py | 2 +- misc/fix_annotate.py | 3 +-- misc/incremental_checker.py | 44 ++++++++++++++++++------------------- misc/perf_checker.py | 8 +++---- misc/sync-typeshed.py | 2 +- misc/test_case_to_actual.py | 2 +- misc/touch_checker.py | 32 +++++++++++++-------------- misc/variadics.py | 10 ++++----- runtests.py | 4 ++-- scripts/find_type.py | 2 +- setup.py | 2 +- 13 files changed, 68 insertions(+), 69 deletions(-) diff --git a/misc/actions_stubs.py b/misc/actions_stubs.py index 978af7187ffe..0d52a882463d 100644 --- a/misc/actions_stubs.py +++ b/misc/actions_stubs.py @@ -15,15 +15,15 @@ def apply_all(func: Any, directory: str, extension: str, to_extension: str='', exclude: Tuple[str]=('',), recursive: bool=True, debug: bool=False) -> None: excluded = [x+extension for x in exclude] if exclude else [] - for p, d, files in os.walk(os.path.join(base_path,directory)): + for p, d, files in os.walk(os.path.join(base_path, directory)): for f in files: - if "{}".format(f) in excluded: + if f in excluded: continue - inner_path = os.path.join(p,f) + inner_path = os.path.join(p, f) if not inner_path.endswith(extension): continue if to_extension: - new_path = "{}{}".format(inner_path[:-len(extension)],to_extension) + new_path = f"{inner_path[:-len(extension)]}{to_extension}" func(inner_path,new_path) else: func(inner_path) @@ -91,9 +91,9 @@ def main(action: str, directory: str, extension: str, to_extension: str, rec = "[Recursively] " if not_recursive else '' if not extension.startswith('.'): - extension = ".{}".format(extension) + extension = f".{extension}" if not to_extension.startswith('.'): - to_extension = ".{}".format(to_extension) + to_extension = f".{to_extension}" if directory.endswith('/'): directory = directory[:-1] if action == 'cp': diff --git a/misc/analyze_cache.py b/misc/analyze_cache.py index 334526a93742..5f2048b5c11c 100644 --- a/misc/analyze_cache.py +++ b/misc/analyze_cache.py @@ -37,10 +37,10 @@ def extract(chunks: Iterable[JsonDict]) -> Iterable[JsonDict]: def load_json(data_path: str, meta_path: str) -> CacheData: - with open(data_path, 'r') as ds: + with open(data_path) as ds: data_json = json.load(ds) - with open(meta_path, 'r') as ms: + with open(meta_path) as ms: meta_json = json.load(ms) data_size = os.path.getsize(data_path) @@ -66,7 +66,7 @@ def pluck(name: str, chunks: Iterable[JsonDict]) -> Iterable[JsonDict]: def report_counter(counter: Counter, amount: Optional[int] = None) -> None: for name, count in counter.most_common(amount): - print(' {: <8} {}'.format(count, name)) + print(f' {count: <8} {name}') print() @@ -138,7 +138,7 @@ def main() -> None: class_chunks = list(extract_classes(json_chunks)) total_size = sum(chunk.total_size for chunk in json_chunks) - print("Total cache size: {:.3f} megabytes".format(total_size / (1024 * 1024))) + print(f"Total cache size: {total_size / (1024 * 1024):.3f} megabytes") print() class_name_counter = Counter(chunk[".class"] for chunk in class_chunks) @@ -154,15 +154,15 @@ def main() -> None: build = chunk break original = json.dumps(build.data, sort_keys=True) - print("Size of build.data.json, in kilobytes: {:.3f}".format(len(original) / 1024)) + print(f"Size of build.data.json, in kilobytes: {len(original) / 1024:.3f}") build.data = compress(build.data) compressed = json.dumps(build.data, sort_keys=True) - print("Size of compressed build.data.json, in kilobytes: {:.3f}".format(len(compressed) / 1024)) + print(f"Size of compressed build.data.json, in kilobytes: {len(compressed) / 1024:.3f}") build.data = decompress(build.data) decompressed = json.dumps(build.data, sort_keys=True) - print("Size of decompressed build.data.json, in kilobytes: {:.3f}".format(len(decompressed) / 1024)) + print(f"Size of decompressed build.data.json, in kilobytes: {len(decompressed) / 1024:.3f}") print("Lossless conversion back", original == decompressed) diff --git a/misc/apply-cache-diff.py b/misc/apply-cache-diff.py index 543ece9981ab..a9e13a1af9a5 100644 --- a/misc/apply-cache-diff.py +++ b/misc/apply-cache-diff.py @@ -24,7 +24,7 @@ def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: def apply_diff(cache_dir: str, diff_file: str, sqlite: bool = False) -> None: cache = make_cache(cache_dir, sqlite) - with open(diff_file, "r") as f: + with open(diff_file) as f: diff = json.load(f) old_deps = json.loads(cache.read("@deps.meta.json")) diff --git a/misc/fix_annotate.py b/misc/fix_annotate.py index 0b552bf51d7a..4c34e0381703 100644 --- a/misc/fix_annotate.py +++ b/misc/fix_annotate.py @@ -27,7 +27,6 @@ def foo(self, bar, baz=12): Finally, it knows that __init__() is supposed to return None. """ -from __future__ import print_function import os import re @@ -90,7 +89,7 @@ def transform(self, node, results): # Insert '# type: {annot}' comment. # For reference, see lib2to3/fixes/fix_tuple_params.py in stdlib. if len(children) >= 2 and children[1].type == token.INDENT: - children[1].prefix = '%s# type: %s\n%s' % (children[1].value, annot, children[1].prefix) + children[1].prefix = '{}# type: {}\n{}'.format(children[1].value, annot, children[1].prefix) children[1].changed() if FixAnnotate.counter is not None: FixAnnotate.counter -= 1 diff --git a/misc/incremental_checker.py b/misc/incremental_checker.py index 0c659bee7023..8eea983ff599 100755 --- a/misc/incremental_checker.py +++ b/misc/incremental_checker.py @@ -92,10 +92,10 @@ def ensure_environment_is_ready(mypy_path: str, temp_repo_path: str, mypy_cache_ def initialize_repo(repo_url: str, temp_repo_path: str, branch: str) -> None: - print("Cloning repo {0} to {1}".format(repo_url, temp_repo_path)) + print(f"Cloning repo {repo_url} to {temp_repo_path}") execute(["git", "clone", repo_url, temp_repo_path]) if branch is not None: - print("Checking out branch {}".format(branch)) + print(f"Checking out branch {branch}") execute(["git", "-C", temp_repo_path, "checkout", branch]) @@ -110,13 +110,13 @@ def get_commits(repo_folder_path: str, commit_range: str) -> List[Tuple[str, str def get_commits_starting_at(repo_folder_path: str, start_commit: str) -> List[Tuple[str, str]]: - print("Fetching commits starting at {0}".format(start_commit)) - return get_commits(repo_folder_path, '{0}^..HEAD'.format(start_commit)) + print(f"Fetching commits starting at {start_commit}") + return get_commits(repo_folder_path, f'{start_commit}^..HEAD') def get_nth_commit(repo_folder_path: str, n: int) -> Tuple[str, str]: - print("Fetching last {} commits (or all, if there are fewer commits than n)".format(n)) - return get_commits(repo_folder_path, '-{}'.format(n))[0] + print(f"Fetching last {n} commits (or all, if there are fewer commits than n)") + return get_commits(repo_folder_path, f'-{n}')[0] def run_mypy(target_file_path: Optional[str], @@ -187,7 +187,7 @@ def stop_daemon() -> None: def load_cache(incremental_cache_path: str = CACHE_PATH) -> JsonDict: if os.path.exists(incremental_cache_path): - with open(incremental_cache_path, 'r') as stream: + with open(incremental_cache_path) as stream: return json.load(stream) else: return {} @@ -213,17 +213,17 @@ def set_expected(commits: List[Tuple[str, str]], skip evaluating that commit and move on to the next.""" for commit_id, message in commits: if commit_id in cache: - print('Skipping commit (already cached): {0}: "{1}"'.format(commit_id, message)) + print(f'Skipping commit (already cached): {commit_id}: "{message}"') else: - print('Caching expected output for commit {0}: "{1}"'.format(commit_id, message)) + print(f'Caching expected output for commit {commit_id}: "{message}"') execute(["git", "-C", temp_repo_path, "checkout", commit_id]) runtime, output, stats = run_mypy(target_file_path, mypy_cache_path, mypy_script, incremental=False) cache[commit_id] = {'runtime': runtime, 'output': output} if output == "": - print(" Clean output ({:.3f} sec)".format(runtime)) + print(f" Clean output ({runtime:.3f} sec)") else: - print(" Output ({:.3f} sec)".format(runtime)) + print(f" Output ({runtime:.3f} sec)") print_offset(output, 8) print() @@ -246,7 +246,7 @@ def test_incremental(commits: List[Tuple[str, str]], commits = [commits[0]] + commits overall_stats = {} # type: Dict[str, float] for commit_id, message in commits: - print('Now testing commit {0}: "{1}"'.format(commit_id, message)) + print(f'Now testing commit {commit_id}: "{message}"') execute(["git", "-C", temp_repo_path, "checkout", commit_id]) runtime, output, stats = run_mypy(target_file_path, mypy_cache_path, mypy_script, incremental=True, daemon=daemon) @@ -255,18 +255,18 @@ def test_incremental(commits: List[Tuple[str, str]], expected_output = cache[commit_id]['output'] # type: str if output != expected_output: print(" Output does not match expected result!") - print(" Expected output ({:.3f} sec):".format(expected_runtime)) + print(f" Expected output ({expected_runtime:.3f} sec):") print_offset(expected_output, 8) - print(" Actual output: ({:.3f} sec):".format(runtime)) + print(f" Actual output: ({runtime:.3f} sec):") print_offset(output, 8) if exit_on_error: break else: print(" Output matches expected result!") - print(" Incremental: {:.3f} sec".format(runtime)) - print(" Original: {:.3f} sec".format(expected_runtime)) + print(f" Incremental: {runtime:.3f} sec") + print(f" Original: {expected_runtime:.3f} sec") if relevant_stats: - print(" Stats: {}".format(relevant_stats)) + print(f" Stats: {relevant_stats}") if overall_stats: print("Overall stats:", overall_stats) @@ -324,7 +324,7 @@ def test_repo(target_repo_url: str, temp_repo_path: str, elif range_type == "commit": start_commit = range_start else: - raise RuntimeError("Invalid option: {}".format(range_type)) + raise RuntimeError(f"Invalid option: {range_type}") commits = get_commits_starting_at(temp_repo_path, start_commit) if params.limit: commits = commits[:params.limit] @@ -419,10 +419,10 @@ def main() -> None: # The path to store the mypy incremental mode cache data mypy_cache_path = os.path.abspath(os.path.join(mypy_path, "misc", ".mypy_cache")) - print("Assuming mypy is located at {0}".format(mypy_path)) - print("Temp repo will be cloned at {0}".format(temp_repo_path)) - print("Testing file/dir located at {0}".format(target_file_path)) - print("Using cache data located at {0}".format(incremental_cache_path)) + print(f"Assuming mypy is located at {mypy_path}") + print(f"Temp repo will be cloned at {temp_repo_path}") + print(f"Testing file/dir located at {target_file_path}") + print(f"Using cache data located at {incremental_cache_path}") print() test_repo(params.repo_url, temp_repo_path, target_file_path, diff --git a/misc/perf_checker.py b/misc/perf_checker.py index e55f8ccd38fe..38a80c148187 100644 --- a/misc/perf_checker.py +++ b/misc/perf_checker.py @@ -58,10 +58,10 @@ def trial(num_trials: int, command: Command) -> List[float]: def report(name: str, times: List[float]) -> None: - print("{}:".format(name)) - print(" Times: {}".format(times)) - print(" Mean: {}".format(statistics.mean(times))) - print(" Stdev: {}".format(statistics.stdev(times))) + print(f"{name}:") + print(f" Times: {times}") + print(f" Mean: {statistics.mean(times)}") + print(f" Stdev: {statistics.stdev(times)}") print() diff --git a/misc/sync-typeshed.py b/misc/sync-typeshed.py index 93cbd951e0f6..8f4bba8487b3 100644 --- a/misc/sync-typeshed.py +++ b/misc/sync-typeshed.py @@ -77,7 +77,7 @@ def main() -> None: if not args.typeshed_dir: # Clone typeshed repo if no directory given. with tempfile.TemporaryDirectory() as tempdir: - print('Cloning typeshed in {}...'.format(tempdir)) + print(f'Cloning typeshed in {tempdir}...') subprocess.run(['git', 'clone', 'https://github.com/python/typeshed.git'], check=True, cwd=tempdir) repo = os.path.join(tempdir, 'typeshed') diff --git a/misc/test_case_to_actual.py b/misc/test_case_to_actual.py index 9a91bb1fa07d..ccf631286802 100644 --- a/misc/test_case_to_actual.py +++ b/misc/test_case_to_actual.py @@ -62,7 +62,7 @@ def main() -> None: return test_file_path, root_path = sys.argv[1], sys.argv[2] - with open(test_file_path, 'r') as stream: + with open(test_file_path) as stream: chunks = produce_chunks(iter(stream)) write_tree(root_path, chunks) diff --git a/misc/touch_checker.py b/misc/touch_checker.py index c44afe492255..d12c2e816614 100644 --- a/misc/touch_checker.py +++ b/misc/touch_checker.py @@ -67,7 +67,7 @@ def make_change_wrappers(filename: str) -> Tuple[Command, Command]: def setup() -> None: nonlocal copy - with open(filename, 'r') as stream: + with open(filename) as stream: copy = stream.read() with open(filename, 'a') as stream: stream.write('\n\nfoo = 3') @@ -102,48 +102,48 @@ def main() -> None: lambda: None, lambda: execute(["python3", "-m", "mypy", "mypy"]), lambda: None) - print("Baseline: {}".format(baseline)) + print(f"Baseline: {baseline}") cold = test( lambda: delete_folder(".mypy_cache"), lambda: execute(["python3", "-m", "mypy", "-i", "mypy"]), lambda: None) - print("Cold cache: {}".format(cold)) + print(f"Cold cache: {cold}") warm = test( lambda: None, lambda: execute(["python3", "-m", "mypy", "-i", "mypy"]), lambda: None) - print("Warm cache: {}".format(warm)) + print(f"Warm cache: {warm}") print() deltas = [] for filename in glob.iglob("mypy/**/*.py", recursive=True): - print("{} {}".format(verb, filename)) + print(f"{verb} {filename}") setup, teardown = make_wrappers(filename) delta = test( setup, lambda: execute(["python3", "-m", "mypy", "-i", "mypy"]), teardown) - print(" Time: {}".format(delta)) + print(f" Time: {delta}") deltas.append(delta) print() print("Initial:") - print(" Baseline: {}".format(baseline)) - print(" Cold cache: {}".format(cold)) - print(" Warm cache: {}".format(warm)) + print(f" Baseline: {baseline}") + print(f" Cold cache: {cold}") + print(f" Warm cache: {warm}") print() print("Aggregate:") - print(" Times: {}".format(deltas)) - print(" Mean: {}".format(statistics.mean(deltas))) - print(" Median: {}".format(statistics.median(deltas))) - print(" Stdev: {}".format(statistics.stdev(deltas))) - print(" Min: {}".format(min(deltas))) - print(" Max: {}".format(max(deltas))) - print(" Total: {}".format(sum(deltas))) + print(f" Times: {deltas}") + print(f" Mean: {statistics.mean(deltas)}") + print(f" Median: {statistics.median(deltas)}") + print(f" Stdev: {statistics.stdev(deltas)}") + print(f" Min: {min(deltas)}") + print(f" Max: {max(deltas)}") + print(f" Total: {sum(deltas)}") print() if __name__ == '__main__': diff --git a/misc/variadics.py b/misc/variadics.py index 920028853a4f..3ffc2a967829 100644 --- a/misc/variadics.py +++ b/misc/variadics.py @@ -8,8 +8,8 @@ def prelude(limit: int, bound: str) -> None: print('from typing import Callable, Iterable, Iterator, Tuple, TypeVar, overload') - print('Ts = TypeVar(\'Ts\', bound={bound})'.format(bound=bound)) - print('R = TypeVar(\'R\')') + print(f"Ts = TypeVar('Ts', bound={bound})") + print("R = TypeVar('R')") for i in range(LIMIT): print('T{i} = TypeVar(\'T{i}\', bound={bound})'.format(i=i+1, bound=bound)) @@ -19,8 +19,8 @@ def expand_template(template: str, limit: int = LIMIT) -> None: print() for i in range(lower, limit): - tvs = ', '.join('T{i}'.format(i=j+1) for j in range(i)) - args = ', '.join(arg_template.format(i=j+1, Ts='T{}'.format(j+1)) + tvs = ', '.join(f'T{j+1}' for j in range(i)) + args = ', '.join(arg_template.format(i=j+1, Ts=f'T{j+1}') for j in range(i)) print('@overload') s = template.format(Ts=tvs, argsTs=args) @@ -49,6 +49,6 @@ def main(): expand_template('def make_check({argsTs}) -> Callable[[{Ts}], bool]: ...') expand_template('def my_map(f: Callable[[{Ts}], R], {argsTs}) -> Iterator[R]: ...', 'arg{i}: Iterable[{Ts}]') - + main() diff --git a/runtests.py b/runtests.py index 62437e1ab011..871a214ef0c1 100755 --- a/runtests.py +++ b/runtests.py @@ -84,7 +84,7 @@ def run_cmd(name: str) -> int: status = 0 cmd = cmds[name] - print('run %s: %s' % (name, cmd)) + print(f'run {name}: {cmd}') proc = subprocess.run(cmd, stderr=subprocess.STDOUT) if proc.returncode: print('\nFAILED: %s' % name) @@ -105,7 +105,7 @@ def start_background_cmd(name: str) -> Popen: def wait_background_cmd(name: str, proc: Popen) -> int: output = proc.communicate()[0] status = proc.returncode - print('run %s: %s' % (name, cmds[name])) + print(f'run {name}: {cmds[name]}') if status: print(output.decode().rstrip()) print('\nFAILED: %s' % name) diff --git a/scripts/find_type.py b/scripts/find_type.py index f488fca9f0ee..757c2a40fd15 100755 --- a/scripts/find_type.py +++ b/scripts/find_type.py @@ -66,7 +66,7 @@ def main(): start_col = int(start_col_str) end_line = int(end_line_str) end_col = int(end_col_str) - with open(filename, 'r') as f: + with open(filename) as f: lines = f.readlines() lines[end_line - 1] = update_line(lines[end_line - 1], REVEAL_TYPE_END, end_col) # insert after end_col lines[start_line - 1] = update_line(lines[start_line - 1], REVEAL_TYPE_START, start_col) diff --git a/setup.py b/setup.py index c2e46675a26b..bba99fec8259 100644 --- a/setup.py +++ b/setup.py @@ -58,7 +58,7 @@ def pin_version(self): path = os.path.join(self.build_lib, 'mypy') self.mkpath(path) with open(os.path.join(path, 'version.py'), 'w') as stream: - stream.write('__version__ = "{}"\n'.format(version)) + stream.write(f'__version__ = "{version}"\n') def run(self): self.execute(self.pin_version, ()) From 7a2ad43431c182cf01a4b9bf73a3bea8e3fac9aa Mon Sep 17 00:00:00 2001 From: Hugues Date: Sun, 1 May 2022 13:59:22 -0700 Subject: [PATCH 41/79] checkexpr: speedup typechecking of container literals with tuple entries (#12706) `fast_dict_type` and `fast_container_type` only allowed Instance but not Tuple of Instances which was mostly an oversight, as opposed to an intentional omission. For #9427 --- mypy/checkexpr.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index bd69c1427dce..cea44c6c17db 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -118,6 +118,16 @@ class TooManyUnions(Exception): """ +def allow_fast_container_literal(t: ProperType) -> bool: + return ( + isinstance(t, Instance) + or ( + isinstance(t, TupleType) + and all(allow_fast_container_literal(get_proper_type(it)) for it in t.items) + ) + ) + + def extract_refexpr_names(expr: RefExpr) -> Set[str]: """Recursively extracts all module references from a reference expression. @@ -3265,7 +3275,7 @@ def fast_container_type( Limitations: - no active type context - no star expressions - - the joined type of all entries must be an Instance type + - the joined type of all entries must be an Instance or Tuple type """ ctx = self.type_context[-1] if ctx: @@ -3277,7 +3287,7 @@ def fast_container_type( return None values.append(self.accept(item)) vt = join.join_type_list(values) - if not isinstance(vt, Instance): + if not allow_fast_container_literal(vt): return None return self.chk.named_generic_type(container_fullname, [vt]) @@ -3377,7 +3387,7 @@ def fast_dict_type(self, e: DictExpr) -> Optional[Type]: Limitations: - no active type context - only supported star expressions are other dict instances - - the joined types of all keys and values must be Instance types + - the joined types of all keys and values must be Instance or Tuple types """ ctx = self.type_context[-1] if ctx: @@ -3401,7 +3411,7 @@ def fast_dict_type(self, e: DictExpr) -> Optional[Type]: values.append(self.accept(value)) kt = join.join_type_list(keys) vt = join.join_type_list(values) - if not (isinstance(kt, Instance) and isinstance(vt, Instance)): + if not (allow_fast_container_literal(kt) and allow_fast_container_literal(vt)): return None if stargs and (stargs[0] != kt or stargs[1] != vt): return None From 9a2f729856de6d16c9b59432e6ef4d2790033d4d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 1 May 2022 15:01:02 -0600 Subject: [PATCH 42/79] Fix missing NoReturn annotations and incorrect try placements (#12705) --- mypy/build.py | 4 ++-- mypy/dmypy/client.py | 3 ++- mypy/dmypy_server.py | 6 +++--- mypy/errors.py | 7 ++++--- mypy/main.py | 2 +- mypy/semanal.py | 2 ++ 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index f084e632417a..2eaabbe75411 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -24,7 +24,7 @@ from typing import (AbstractSet, Any, Dict, Iterable, Iterator, List, Sequence, Mapping, NamedTuple, Optional, Set, Tuple, TypeVar, Union, Callable, TextIO) -from typing_extensions import ClassVar, Final, TYPE_CHECKING, TypeAlias as _TypeAlias +from typing_extensions import ClassVar, NoReturn, Final, TYPE_CHECKING, TypeAlias as _TypeAlias from mypy_extensions import TypedDict from mypy.nodes import MypyFile, ImportBase, Import, ImportFrom, ImportAll, SymbolTable @@ -398,7 +398,7 @@ def load_plugins_from_config( if line == -1: line = 1 # We need to pick some line number that doesn't look too confusing - def plugin_error(message: str) -> None: + def plugin_error(message: str) -> NoReturn: errors.report(line, 0, message) errors.raise_error(use_stdout=False) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 3629372d1a85..56ec804ad7a7 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -14,6 +14,7 @@ import traceback from typing import Any, Callable, Dict, Mapping, Optional, Tuple, List +from typing_extensions import NoReturn from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive from mypy.ipc import IPCClient, IPCException @@ -161,7 +162,7 @@ def main(argv: List[str]) -> None: sys.exit(2) -def fail(msg: str) -> None: +def fail(msg: str) -> NoReturn: print(msg, file=sys.stderr) sys.exit(2) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index a8a7dd82f665..04e25091484e 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -209,8 +209,8 @@ def _response_metadata(self) -> Dict[str, str]: def serve(self) -> None: """Serve requests, synchronously (no thread or fork).""" command = None + server = IPCServer(CONNECTION_NAME, self.timeout) try: - server = IPCServer(CONNECTION_NAME, self.timeout) with open(self.status_file, 'w') as f: json.dump({'pid': os.getpid(), 'connection_name': server.connection_name}, f) f.write('\n') # I like my JSON with a trailing newline @@ -298,11 +298,11 @@ def cmd_stop(self) -> Dict[str, object]: def cmd_run(self, version: str, args: Sequence[str], is_tty: bool, terminal_width: int) -> Dict[str, object]: """Check a list of files, triggering a restart if needed.""" + stderr = io.StringIO() + stdout = io.StringIO() try: # Process options can exit on improper arguments, so we need to catch that and # capture stderr so the client can report it - stderr = io.StringIO() - stdout = io.StringIO() with redirect_stderr(stderr): with redirect_stdout(stdout): sources, options = mypy.main.process_options( diff --git a/mypy/errors.py b/mypy/errors.py index 87a59e3ca9ed..590b02865fb5 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -1,11 +1,12 @@ import os.path import sys import traceback + from mypy.backports import OrderedDict from collections import defaultdict from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable, Union -from typing_extensions import Final, Literal +from typing_extensions import Final, Literal, NoReturn from mypy.scope import Scope from mypy.options import Options @@ -633,7 +634,7 @@ def is_errors_for_file(self, file: str) -> bool: """Are there any errors for the given file?""" return file in self.error_info_map - def raise_error(self, use_stdout: bool = True) -> None: + def raise_error(self, use_stdout: bool = True) -> NoReturn: """Raise a CompileError with the generated messages. Render the messages suitable for displaying. @@ -908,7 +909,7 @@ def report_internal_error(err: Exception, options: Options, stdout: Optional[TextIO] = None, stderr: Optional[TextIO] = None, - ) -> None: + ) -> NoReturn: """Report internal error and exit. This optionally starts pdb or shows a traceback. diff --git a/mypy/main.py b/mypy/main.py index 7c7a5993d5c5..a126122c18ed 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1138,7 +1138,7 @@ def maybe_write_junit_xml(td: float, serious: bool, messages: List[str], options td, serious, messages, options.junit_xml, py_version, options.platform) -def fail(msg: str, stderr: TextIO, options: Options) -> None: +def fail(msg: str, stderr: TextIO, options: Options) -> NoReturn: """Fail with a serious error.""" stderr.write('%s\n' % msg) maybe_write_junit_xml(0.0, serious=True, messages=[msg], options=options) diff --git a/mypy/semanal.py b/mypy/semanal.py index 985d5217cc08..8b0148958984 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4573,6 +4573,8 @@ def lookup_qualified(self, name: str, ctx: Context, assert isinstance(node.target, ProperType) if isinstance(node.target, Instance): nextsym = node.target.type.get(part) + else: + nextsym = None else: if isinstance(node, Var): typ = get_proper_type(node.type) From 3c46b818237aa8e0e06f33a74a752921346f5c5a Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 May 2022 15:47:29 -0600 Subject: [PATCH 43/79] Mention no_type_check in docs (#12713) Co-authored by: @chrisbeardy --- docs/source/type_inference_and_annotations.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/source/type_inference_and_annotations.rst b/docs/source/type_inference_and_annotations.rst index 040961d40a40..47a29a6abf95 100644 --- a/docs/source/type_inference_and_annotations.rst +++ b/docs/source/type_inference_and_annotations.rst @@ -251,3 +251,12 @@ Similarly, you can also ignore all mypy checks in a file, by adding a # This is a test file, skipping type checking in it. import unittest ... + +Finally, adding a ``@typing.no_type_check`` decorator to a class, method or +function has the effect of ignoring that class, method or function. + +.. code-block:: python + + @typing.no_type_check + def foo() -> str: + return 12345 # No error! From fc335cb16315964b923eb1927e3aad1516891c28 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 May 2022 15:52:34 -0600 Subject: [PATCH 44/79] mypy: run pyupgrade (#12711) Re-attempt of #10741 Ran: `pyupgrade --py36-plus $(fd -e py) --keep-runtime-typing` I mostly only needed to change things where NamedTuple comments got dropped. Notably, I omitted changes to pyinfo.py --- mypy/binder.py | 2 +- mypy/build.py | 152 +++++++++++++-------------- mypy/checker.py | 69 ++++++------- mypy/checkexpr.py | 10 +- mypy/checkmember.py | 6 +- mypy/checkpattern.py | 13 +-- mypy/checkstrformat.py | 2 +- mypy/config_parser.py | 30 +++--- mypy/constraints.py | 6 +- mypy/dmypy_os.py | 2 +- mypy/dmypy_server.py | 16 +-- mypy/errorcodes.py | 2 +- mypy/errors.py | 34 +++--- mypy/expandtype.py | 2 +- mypy/fastparse.py | 12 +-- mypy/fastparse2.py | 8 +- mypy/find_sources.py | 4 +- mypy/fixup.py | 4 +- mypy/gclogger.py | 2 +- mypy/ipc.py | 22 ++-- mypy/join.py | 2 +- mypy/lookup.py | 8 +- mypy/main.py | 20 ++-- mypy/meet.py | 6 +- mypy/memprofile.py | 2 +- mypy/messages.py | 182 ++++++++++++++++----------------- mypy/metastore.py | 4 +- mypy/modulefinder.py | 28 ++--- mypy/mro.py | 4 +- mypy/nodes.py | 26 ++--- mypy/operators.py | 4 +- mypy/options.py | 2 +- mypy/plugin.py | 139 ++++++++++++------------- mypy/plugins/attrs.py | 4 +- mypy/plugins/common.py | 2 +- mypy/plugins/default.py | 12 +-- mypy/plugins/enums.py | 12 +-- mypy/plugins/functools.py | 4 +- mypy/plugins/singledispatch.py | 23 +++-- mypy/report.py | 13 ++- mypy/semanal.py | 54 +++++----- mypy/semanal_classprop.py | 8 +- mypy/semanal_enum.py | 4 +- mypy/semanal_namedtuple.py | 14 +-- mypy/semanal_shared.py | 2 +- mypy/semanal_typeargs.py | 4 +- mypy/semanal_typeddict.py | 6 +- mypy/server/astdiff.py | 8 +- mypy/server/deps.py | 12 +-- mypy/server/mergecheck.py | 7 +- mypy/server/objgraph.py | 3 +- mypy/server/trigger.py | 2 +- mypy/server/update.py | 24 +++-- mypy/strconv.py | 46 ++++----- mypy/stubdoc.py | 11 +- mypy/stubgen.py | 64 ++++++------ mypy/stubgenc.py | 20 ++-- mypy/stubtest.py | 54 +++++----- mypy/stubutil.py | 14 ++- mypy/subtypes.py | 4 +- mypy/suggestions.py | 40 ++++---- mypy/tvar_scope.py | 4 +- mypy/typeanal.py | 32 +++--- mypy/typeops.py | 2 +- mypy/types.py | 86 ++++++++-------- mypy/typestate.py | 2 +- 66 files changed, 709 insertions(+), 713 deletions(-) diff --git a/mypy/binder.py b/mypy/binder.py index 2f83ffb095fc..1dffb55a54ac 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -189,7 +189,7 @@ def update_from_options(self, frames: List[Frame]) -> bool: frames = [f for f in frames if not f.unreachable] changed = False - keys = set(key for f in frames for key in f.types) + keys = {key for f in frames for key in f.types} for key in keys: current_value = self._get(key) diff --git a/mypy/build.py b/mypy/build.py index 2eaabbe75411..107a291ad582 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -299,30 +299,30 @@ def normpath(path: str, options: Options) -> str: return os.path.abspath(path) -CacheMeta = NamedTuple('CacheMeta', - [('id', str), - ('path', str), - ('mtime', int), - ('size', int), - ('hash', str), - ('dependencies', List[str]), # names of imported modules - ('data_mtime', int), # mtime of data_json - ('data_json', str), # path of .data.json - ('suppressed', List[str]), # dependencies that weren't imported - ('options', Optional[Dict[str, object]]), # build options - # dep_prios and dep_lines are in parallel with - # dependencies + suppressed. - ('dep_prios', List[int]), - ('dep_lines', List[int]), - ('interface_hash', str), # hash representing the public interface - ('version_id', str), # mypy version for cache invalidation - ('ignore_all', bool), # if errors were ignored - ('plugin_data', Any), # config data from plugins - ]) +class CacheMeta(NamedTuple): + id: str + path: str + mtime: int + size: int + hash: str + dependencies: List[str] # names of imported modules + data_mtime: int # mtime of data_json + data_json: str # path of .data.json + suppressed: List[str] # dependencies that weren't imported + options: Optional[Dict[str, object]] # build options + # dep_prios and dep_lines are in parallel with dependencies + suppressed + dep_prios: List[int] + dep_lines: List[int] + interface_hash: str # hash representing the public interface + version_id: str # mypy version for cache invalidation + ignore_all: bool # if errors were ignored + plugin_data: Any # config data from plugins + # NOTE: dependencies + suppressed == all reachable imports; # suppressed contains those reachable imports that were prevented by # silent mode or simply not found. + # Metadata for the fine-grained dependencies file associated with a module. FgDepMeta = TypedDict('FgDepMeta', {'path': str, 'mtime': int}) @@ -413,7 +413,7 @@ def plugin_error(message: str) -> NoReturn: # Plugin paths can be relative to the config file location. plugin_path = os.path.join(os.path.dirname(options.config_file), plugin_path) if not os.path.isfile(plugin_path): - plugin_error('Can\'t find plugin "{}"'.format(plugin_path)) + plugin_error(f'Can\'t find plugin "{plugin_path}"') # Use an absolute path to avoid populating the cache entry # for 'tmp' during tests, since it will be different in # different tests. @@ -423,14 +423,14 @@ def plugin_error(message: str) -> NoReturn: sys.path.insert(0, plugin_dir) elif re.search(r'[\\/]', plugin_path): fnam = os.path.basename(plugin_path) - plugin_error('Plugin "{}" does not have a .py extension'.format(fnam)) + plugin_error(f'Plugin "{fnam}" does not have a .py extension') else: module_name = plugin_path try: module = importlib.import_module(module_name) except Exception as exc: - plugin_error('Error importing plugin "{}": {}'.format(plugin_path, exc)) + plugin_error(f'Error importing plugin "{plugin_path}": {exc}') finally: if plugin_dir is not None: assert sys.path[0] == plugin_dir @@ -443,7 +443,7 @@ def plugin_error(message: str) -> NoReturn: try: plugin_type = getattr(module, func_name)(__version__) except Exception: - print('Error calling the plugin(version) entry point of {}\n'.format(plugin_path), + print(f'Error calling the plugin(version) entry point of {plugin_path}\n', file=stdout) raise # Propagate to display traceback @@ -459,7 +459,7 @@ def plugin_error(message: str) -> NoReturn: custom_plugins.append(plugin_type(options)) snapshot[module_name] = take_module_snapshot(module) except Exception: - print('Error constructing plugin instance of {}\n'.format(plugin_type.__name__), + print(f'Error constructing plugin instance of {plugin_type.__name__}\n', file=stdout) raise # Propagate to display traceback @@ -503,7 +503,7 @@ def take_module_snapshot(module: types.ModuleType) -> str: else: digest = 'unknown' ver = getattr(module, '__version__', 'none') - return '{}:{}'.format(ver, digest) + return f'{ver}:{digest}' def find_config_file_line_number(path: str, section: str, setting_name: str) -> int: @@ -520,7 +520,7 @@ def find_config_file_line_number(path: str, section: str, setting_name: str) -> if line.startswith('[') and line.endswith(']'): current_section = line[1:-1].strip() in_desired_section = (current_section == section) - elif in_desired_section and re.match(r'{}\s*='.format(setting_name), line): + elif in_desired_section and re.match(fr'{setting_name}\s*=', line): results.append(i + 1) if len(results) == 1: return results[0] @@ -918,7 +918,7 @@ def write_deps_cache(rdeps: Dict[str, Dict[str, Set[str]]], assert deps_json manager.log("Writing deps cache", deps_json) if not manager.metastore.write(deps_json, deps_to_json(rdeps[id])): - manager.log("Error writing fine-grained deps JSON file {}".format(deps_json)) + manager.log(f"Error writing fine-grained deps JSON file {deps_json}") error = True else: fg_deps_meta[id] = {'path': deps_json, 'mtime': manager.getmtime(deps_json)} @@ -938,7 +938,7 @@ def write_deps_cache(rdeps: Dict[str, Dict[str, Set[str]]], meta = {'snapshot': meta_snapshot, 'deps_meta': fg_deps_meta} if not metastore.write(DEPS_META_FILE, json.dumps(meta)): - manager.log("Error writing fine-grained deps meta JSON file {}".format(DEPS_META_FILE)) + manager.log(f"Error writing fine-grained deps meta JSON file {DEPS_META_FILE}") error = True if error: @@ -1037,14 +1037,14 @@ def read_quickstart_file(options: Options, # just ignore it. raw_quickstart: Dict[str, Any] = {} try: - with open(options.quickstart_file, "r") as f: + with open(options.quickstart_file) as f: raw_quickstart = json.load(f) quickstart = {} for file, (x, y, z) in raw_quickstart.items(): quickstart[file] = (x, y, z) except Exception as e: - print("Warning: Failed to load quickstart file: {}\n".format(str(e)), file=stdout) + print(f"Warning: Failed to load quickstart file: {str(e)}\n", file=stdout) return quickstart @@ -1095,7 +1095,7 @@ def _load_json_file(file: str, manager: BuildManager, t0 = time.time() try: data = manager.metastore.read(file) - except IOError: + except OSError: manager.log(log_error + file) return None manager.add_stats(metastore_read_time=time.time() - t0) @@ -1221,11 +1221,11 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache """ # TODO: May need to take more build options into account meta_json, data_json, _ = get_cache_names(id, path, manager.options) - manager.trace('Looking for {} at {}'.format(id, meta_json)) + manager.trace(f'Looking for {id} at {meta_json}') t0 = time.time() meta = _load_json_file(meta_json, manager, - log_success='Meta {} '.format(id), - log_error='Could not load cache for {}: '.format(id)) + log_success=f'Meta {id} ', + log_error=f'Could not load cache for {id}: ') t1 = time.time() if meta is None: return None @@ -1243,7 +1243,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache if (m.id != id or m.mtime is None or m.size is None or m.dependencies is None or m.data_mtime is None): - manager.log('Metadata abandoned for {}: attributes are missing'.format(id)) + manager.log(f'Metadata abandoned for {id}: attributes are missing') return None # Ignore cache if generated by an older mypy version. @@ -1251,7 +1251,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache or m.options is None or len(m.dependencies) + len(m.suppressed) != len(m.dep_prios) or len(m.dependencies) + len(m.suppressed) != len(m.dep_lines)): - manager.log('Metadata abandoned for {}: new attributes are missing'.format(id)) + manager.log(f'Metadata abandoned for {id}: new attributes are missing') return None # Ignore cache if (relevant) options aren't the same. @@ -1265,7 +1265,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache # Older versions included debug_cache, but it's silly to compare it. del cached_options['debug_cache'] if cached_options != current_options: - manager.log('Metadata abandoned for {}: options differ'.format(id)) + manager.log(f'Metadata abandoned for {id}: options differ') if manager.options.verbosity >= 2: for key in sorted(set(cached_options) | set(current_options)): if cached_options.get(key) != current_options.get(key): @@ -1275,7 +1275,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache if manager.old_plugins_snapshot and manager.plugins_snapshot: # Check if plugins are still the same. if manager.plugins_snapshot != manager.old_plugins_snapshot: - manager.log('Metadata abandoned for {}: plugins differ'.format(id)) + manager.log(f'Metadata abandoned for {id}: plugins differ') return None # So that plugins can return data with tuples in it without # things silently always invalidating modules, we round-trip @@ -1284,7 +1284,7 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> Optional[Cache manager.plugin.report_config_data(ReportConfigContext(id, path, is_check=True)) )) if m.plugin_data != plugin_data: - manager.log('Metadata abandoned for {}: plugin configuration differs'.format(id)) + manager.log(f'Metadata abandoned for {id}: plugin configuration differs') return None manager.add_stats(fresh_metas=1) @@ -1306,11 +1306,11 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], # we use cache data file mtime to propagate information about changes in the dependencies. if meta is None: - manager.log('Metadata not found for {}'.format(id)) + manager.log(f'Metadata not found for {id}') return None if meta.ignore_all and not ignore_all: - manager.log('Metadata abandoned for {}: errors were previously ignored'.format(id)) + manager.log(f'Metadata abandoned for {id}: errors were previously ignored') return None t0 = time.time() @@ -1321,10 +1321,10 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], try: data_mtime = manager.getmtime(meta.data_json) except OSError: - manager.log('Metadata abandoned for {}: failed to stat data_json'.format(id)) + manager.log(f'Metadata abandoned for {id}: failed to stat data_json') return None if data_mtime != meta.data_mtime: - manager.log('Metadata abandoned for {}: data cache is modified'.format(id)) + manager.log(f'Metadata abandoned for {id}: data cache is modified') return None if bazel: @@ -1335,7 +1335,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], except OSError: return None if not (stat.S_ISREG(st.st_mode) or stat.S_ISDIR(st.st_mode)): - manager.log('Metadata abandoned for {}: file {} does not exist'.format(id, path)) + manager.log(f'Metadata abandoned for {id}: file {path} does not exist') return None manager.add_stats(validate_stat_time=time.time() - t0) @@ -1358,7 +1358,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], size = st.st_size # Bazel ensures the cache is valid. if size != meta.size and not bazel and not fine_grained_cache: - manager.log('Metadata abandoned for {}: file {} has different size'.format(id, path)) + manager.log(f'Metadata abandoned for {id}: file {path} has different size') return None # Bazel ensures the cache is valid. @@ -1371,7 +1371,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], # the file is up to date even though the mtime is wrong, without needing to hash it. qmtime, qsize, qhash = manager.quickstart_state[path] if int(qmtime) == mtime and qsize == size and qhash == meta.hash: - manager.log('Metadata fresh (by quickstart) for {}: file {}'.format(id, path)) + manager.log(f'Metadata fresh (by quickstart) for {id}: file {path}') meta = meta._replace(mtime=mtime, path=path) return meta @@ -1387,7 +1387,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], manager.add_stats(validate_hash_time=time.time() - t0) if source_hash != meta.hash: if fine_grained_cache: - manager.log('Using stale metadata for {}: file {}'.format(id, path)) + manager.log(f'Using stale metadata for {id}: file {path}') return meta else: manager.log('Metadata abandoned for {}: file {} has different hash'.format( @@ -1430,7 +1430,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str], return meta # It's a match on (id, path, size, hash, mtime). - manager.log('Metadata fresh for {}: file {}'.format(id, path)) + manager.log(f'Metadata fresh for {id}: file {path}') return meta @@ -1504,7 +1504,7 @@ def write_cache(id: str, path: str, tree: MypyFile, try: st = manager.get_stat(path) except OSError as err: - manager.log("Cannot get stat for {}: {}".format(path, err)) + manager.log(f"Cannot get stat for {path}: {err}") # Remove apparently-invalid cache files. # (This is purely an optimization.) for filename in [data_json, meta_json]: @@ -1518,13 +1518,13 @@ def write_cache(id: str, path: str, tree: MypyFile, # Write data cache file, if applicable # Note that for Bazel we don't record the data file's mtime. if old_interface_hash == interface_hash: - manager.trace("Interface for {} is unchanged".format(id)) + manager.trace(f"Interface for {id} is unchanged") else: - manager.trace("Interface for {} has changed".format(id)) + manager.trace(f"Interface for {id} has changed") if not metastore.write(data_json, data_str): # Most likely the error is the replace() call # (see https://github.com/python/mypy/issues/3215). - manager.log("Error writing data JSON file {}".format(data_json)) + manager.log(f"Error writing data JSON file {data_json}") # Let's continue without writing the meta file. Analysis: # If the replace failed, we've changed nothing except left # behind an extraneous temporary file; if the replace @@ -1538,7 +1538,7 @@ def write_cache(id: str, path: str, tree: MypyFile, try: data_mtime = manager.getmtime(data_json) except OSError: - manager.log("Error in os.stat({!r}), skipping cache write".format(data_json)) + manager.log(f"Error in os.stat({data_json!r}), skipping cache write") return interface_hash, None mtime = 0 if bazel else int(st.st_mtime) @@ -1573,7 +1573,7 @@ def write_cache(id: str, path: str, tree: MypyFile, # Most likely the error is the replace() call # (see https://github.com/python/mypy/issues/3215). # The next run will simply find the cache entry out of date. - manager.log("Error writing meta JSON file {}".format(meta_json)) + manager.log(f"Error writing meta JSON file {meta_json}") return interface_hash, cache_meta_from_dict(meta, data_json) @@ -1597,7 +1597,7 @@ def delete_cache(id: str, path: str, manager: BuildManager) -> None: manager.metastore.remove(filename) except OSError as e: if e.errno != errno.ENOENT: - manager.log("Error deleting cache file {}: {}".format(filename, e.strerror)) + manager.log(f"Error deleting cache file {filename}: {e.strerror}") """Dependency manager. @@ -1908,7 +1908,7 @@ def __init__(self, # know about modules that have cache information and defer # handling new modules until the fine-grained update. if manager.use_fine_grained_cache(): - manager.log("Deferring module to fine-grained update %s (%s)" % (path, id)) + manager.log(f"Deferring module to fine-grained update {path} ({id})") raise ModuleNotFound # Parse the file (and then some) to get the dependencies. @@ -2036,9 +2036,9 @@ def parse_file(self) -> None: cached = self.id in manager.ast_cache modules = manager.modules if not cached: - manager.log("Parsing %s (%s)" % (self.xpath, self.id)) + manager.log(f"Parsing {self.xpath} ({self.id})") else: - manager.log("Using cached AST for %s (%s)" % (self.xpath, self.id)) + manager.log(f"Using cached AST for {self.xpath} ({self.id})") t0 = time_ref() @@ -2051,7 +2051,7 @@ def parse_file(self) -> None: source = decode_python_encoding(manager.fscache.read(path), manager.options.python_version) self.source_hash = manager.fscache.hash_digest(path) - except IOError as ioerr: + except OSError as ioerr: # ioerr.strerror differs for os.stat failures between Windows and # other systems, but os.strerror(ioerr.errno) does not, so we use that. # (We want the error messages to be platform-independent so that the @@ -2062,9 +2062,9 @@ def parse_file(self) -> None: module_with_blocker=self.id) from ioerr except (UnicodeDecodeError, DecodeError) as decodeerr: if self.path.endswith('.pyd'): - err = "mypy: stubgen does not support .pyd files: '{}'".format(self.path) + err = f"mypy: stubgen does not support .pyd files: '{self.path}'" else: - err = "mypy: can't decode file '{}': {}".format(self.path, str(decodeerr)) + err = f"mypy: can't decode file '{self.path}': {str(decodeerr)}" raise CompileError([err], module_with_blocker=self.id) from decodeerr elif self.path and self.manager.fscache.isdir(self.path): source = '' @@ -2333,16 +2333,16 @@ def write_cache(self) -> None: dep_lines = self.dependency_lines() assert self.source_hash is not None assert len(set(self.dependencies)) == len(self.dependencies), ( - "Duplicates in dependencies list for {} ({})".format(self.id, self.dependencies)) + f"Duplicates in dependencies list for {self.id} ({self.dependencies})") new_interface_hash, self.meta = write_cache( self.id, self.path, self.tree, list(self.dependencies), list(self.suppressed), dep_prios, dep_lines, self.interface_hash, self.source_hash, self.ignore_all, self.manager) if new_interface_hash == self.interface_hash: - self.manager.log("Cached module {} has same interface".format(self.id)) + self.manager.log(f"Cached module {self.id} has same interface") else: - self.manager.log("Cached module {} has changed interface".format(self.id)) + self.manager.log(f"Cached module {self.id} has changed interface") self.mark_interface_stale() self.interface_hash = new_interface_hash @@ -2466,11 +2466,11 @@ def find_module_and_diagnose(manager: BuildManager, pass elif follow_imports == 'silent': # Still import it, but silence non-blocker errors. - manager.log("Silencing %s (%s)" % (result, id)) + manager.log(f"Silencing {result} ({id})") elif follow_imports == 'skip' or follow_imports == 'error': # In 'error' mode, produce special error messages. if id not in manager.missing_modules: - manager.log("Skipping %s (%s)" % (result, id)) + manager.log(f"Skipping {result} ({id})") if follow_imports == 'error': if ancestor_for: skipping_ancestor(manager, id, result, ancestor_for) @@ -2489,7 +2489,7 @@ def find_module_and_diagnose(manager: BuildManager, and not options.use_builtins_fixtures and not options.custom_typeshed_dir): raise CompileError([ - 'mypy: "%s" shadows library module "%s"' % (os.path.relpath(result), id), + f'mypy: "{os.path.relpath(result)}" shadows library module "{id}"', 'note: A user-defined top-level module with name "%s" is not supported' % id ]) return (result, follow_imports) @@ -2632,7 +2632,7 @@ def skipping_module(manager: BuildManager, line: int, caller_state: Optional[Sta manager.errors.set_import_context(caller_state.import_context) manager.errors.set_file(caller_state.xpath, caller_state.id) manager.errors.report(line, 0, - 'Import of "%s" ignored' % (id,), + f'Import of "{id}" ignored', severity='error') manager.errors.report(line, 0, "(Using --follow-imports=error, module not passed on command line)", @@ -2648,7 +2648,7 @@ def skipping_ancestor(manager: BuildManager, id: str, path: str, ancestor_for: ' # so we'd need to cache the decision. manager.errors.set_import_context([]) manager.errors.set_file(ancestor_for.xpath, ancestor_for.id) - manager.errors.report(-1, -1, 'Ancestor package "%s" ignored' % (id,), + manager.errors.report(-1, -1, f'Ancestor package "{id}" ignored', severity='error', only_once=True) manager.errors.report(-1, -1, "(Using --follow-imports=error, submodule passed on command line)", @@ -2784,7 +2784,7 @@ def __init__(self, index: int, scc: List[str]) -> None: def dumps(self) -> str: """Convert to JSON string.""" total_size = sum(self.sizes.values()) - return "[%s, %s, %s,\n %s,\n %s]" % (json.dumps(self.node_id), + return "[{}, {}, {},\n {},\n {}]".format(json.dumps(self.node_id), json.dumps(total_size), json.dumps(self.scc), json.dumps(self.sizes), @@ -2798,7 +2798,7 @@ def dump_timing_stats(path: str, graph: Graph) -> None: with open(path, 'w') as f: for k in sorted(graph.keys()): v = graph[k] - f.write('{} {}\n'.format(v.id, v.time_spent_us)) + f.write(f'{v.id} {v.time_spent_us}\n') def dump_graph(graph: Graph, stdout: Optional[TextIO] = None) -> None: @@ -2873,7 +2873,7 @@ def load_graph(sources: List[BuildSource], manager: BuildManager, manager.errors.set_file(st.xpath, st.id) manager.errors.report( -1, -1, - 'Duplicate module named "%s" (also at "%s")' % (st.id, graph[st.id].xpath), + f'Duplicate module named "{st.id}" (also at "{graph[st.id].xpath}")', blocker=True, ) manager.errors.report( @@ -3081,11 +3081,11 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: scc_str = " ".join(scc) if fresh: - manager.trace("Queuing %s SCC (%s)" % (fresh_msg, scc_str)) + manager.trace(f"Queuing {fresh_msg} SCC ({scc_str})") fresh_scc_queue.append(scc) else: if len(fresh_scc_queue) > 0: - manager.log("Processing {} queued fresh SCCs".format(len(fresh_scc_queue))) + manager.log(f"Processing {len(fresh_scc_queue)} queued fresh SCCs") # Defer processing fresh SCCs until we actually run into a stale SCC # and need the earlier modules to be loaded. # @@ -3105,7 +3105,7 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: fresh_scc_queue = [] size = len(scc) if size == 1: - manager.log("Processing SCC singleton (%s) as %s" % (scc_str, fresh_msg)) + manager.log(f"Processing SCC singleton ({scc_str}) as {fresh_msg}") else: manager.log("Processing SCC of size %d (%s) as %s" % (size, scc_str, fresh_msg)) process_stale_scc(graph, scc, manager) diff --git a/mypy/checker.py b/mypy/checker.py index 002f28d4db6c..d0820e483d65 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -97,26 +97,23 @@ DeferredNodeType: _TypeAlias = Union[FuncDef, LambdaExpr, OverloadedFuncDef, Decorator] FineGrainedDeferredNodeType: _TypeAlias = Union[FuncDef, MypyFile, OverloadedFuncDef] + # A node which is postponed to be processed during the next pass. # In normal mode one can defer functions and methods (also decorated and/or overloaded) # and lambda expressions. Nested functions can't be deferred -- only top-level functions # and methods of classes not defined within a function can be deferred. -DeferredNode = NamedTuple( - 'DeferredNode', - [ - ('node', DeferredNodeType), - ('active_typeinfo', Optional[TypeInfo]), # And its TypeInfo (for semantic analysis - # self type handling) - ]) +class DeferredNode(NamedTuple): + node: DeferredNodeType + # And its TypeInfo (for semantic analysis self type handling + active_typeinfo: Optional[TypeInfo] + # Same as above, but for fine-grained mode targets. Only top-level functions/methods # and module top levels are allowed as such. -FineGrainedDeferredNode = NamedTuple( - 'FineGrainedDeferredNode', - [ - ('node', FineGrainedDeferredNodeType), - ('active_typeinfo', Optional[TypeInfo]), - ]) +class FineGrainedDeferredNode(NamedTuple): + node: FineGrainedDeferredNodeType + active_typeinfo: Optional[TypeInfo] + # Data structure returned by find_isinstance_check representing # information learned from the truth or falsehood of a condition. The @@ -131,25 +128,23 @@ # (such as two references to the same variable). TODO: it would # probably be better to have the dict keyed by the nodes' literal_hash # field instead. - TypeMap: _TypeAlias = Optional[Dict[Expression, Type]] + # An object that represents either a precise type or a type with an upper bound; # it is important for correct type inference with isinstance. -TypeRange = NamedTuple( - 'TypeRange', - [ - ('item', Type), - ('is_upper_bound', bool), # False => precise type - ]) +class TypeRange(NamedTuple): + item: Type + is_upper_bound: bool # False => precise type + # Keeps track of partial types in a single scope. In fine-grained incremental # mode partial types initially defined at the top level cannot be completed in # a function, and we use the 'is_function' attribute to enforce this. -PartialTypeScope = NamedTuple('PartialTypeScope', [('map', Dict[Var, Context]), - ('is_function', bool), - ('is_local', bool), - ]) +class PartialTypeScope(NamedTuple): + map: Dict[Var, Context] + is_function: bool + is_local: bool class TypeChecker(NodeVisitor[None], CheckerPluginInterface): @@ -891,7 +886,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) self.msg.unimported_type_becomes_any("Return type", ret_type, fdef) for idx, arg_type in enumerate(fdef.type.arg_types): if has_any_from_unimported_type(arg_type): - prefix = "Argument {} to \"{}\"".format(idx + 1, fdef.name) + prefix = f"Argument {idx + 1} to \"{fdef.name}\"" self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) check_for_explicit_any(fdef.type, self.options, self.is_typeshed_stub, self.msg, context=fdef) @@ -1062,9 +1057,9 @@ def check_default_args(self, item: FuncItem, body_is_trivial: bool) -> None: name = arg.variable.name msg = 'Incompatible default for ' if name.startswith('__tuple_arg_'): - msg += "tuple argument {}".format(name[12:]) + msg += f"tuple argument {name[12:]}" else: - msg += 'argument "{}"'.format(name) + msg += f'argument "{name}"' self.check_simple_assignment( arg.variable.type, arg.initializer, @@ -1964,7 +1959,7 @@ def check_enum_bases(self, defn: ClassDef) -> None: continue elif enum_base is not None: self.fail( - 'No base classes are allowed after "{}"'.format(enum_base), + f'No base classes are allowed after "{enum_base}"', defn, ) break @@ -3308,8 +3303,8 @@ def check_simple_assignment(self, lvalue_type: Optional[Type], rvalue: Expressio self.msg.deleted_as_lvalue(lvalue_type, context) elif lvalue_type: self.check_subtype(rvalue_type, lvalue_type, context, msg, - '{} has type'.format(rvalue_name), - '{} has type'.format(lvalue_name), code=code) + f'{rvalue_name} has type', + f'{lvalue_name} has type', code=code) return rvalue_type def check_member_assignment(self, instance_type: Type, attribute_type: Type, @@ -3717,7 +3712,7 @@ def _type_check_raise_python2(self, e: Expression, s: RaiseStmt, typ: ProperType expected_type = TypeType(exc_type) self.check_subtype( typ.items[0], expected_type, s, - 'Argument 1 must be "{}" subtype'.format(expected_type), + f'Argument 1 must be "{expected_type}" subtype', ) # Typecheck `traceback` part: @@ -3732,7 +3727,7 @@ def _type_check_raise_python2(self, e: Expression, s: RaiseStmt, typ: ProperType ]) self.check_subtype( typ.items[2], traceback_type, s, - 'Argument 3 must be "{}" subtype'.format(traceback_type), + f'Argument 3 must be "{traceback_type}" subtype', ) else: expected_type_items = [ @@ -4302,7 +4297,7 @@ def _make_fake_typeinfo_and_full_name( curr_module_: MypyFile, ) -> Tuple[TypeInfo, str]: names_list = pretty_seq([x.type.name for x in base_classes_], "and") - short_name = ''.format(names_list) + short_name = f'' full_name_ = gen_unique_name(short_name, curr_module_.names) cdef, info_ = self.make_fake_typeinfo( curr_module_.fullname, @@ -4354,7 +4349,7 @@ def intersect_instance_callable(self, typ: Instance, callable_type: CallableType # have a valid fullname and a corresponding entry in a symbol table. We generate # a unique name inside the symbol table of the current module. cur_module = cast(MypyFile, self.scope.stack[0]) - gen_name = gen_unique_name("".format(typ.type.name), + gen_name = gen_unique_name(f"", cur_module.names) # Synthesize a fake TypeInfo @@ -5367,7 +5362,7 @@ def lookup(self, name: str) -> SymbolTableNode: table = cast(MypyFile, b.node).names if name in table: return table[name] - raise KeyError('Failed lookup: {}'.format(name)) + raise KeyError(f'Failed lookup: {name}') def lookup_qualified(self, name: str) -> SymbolTableNode: if '.' not in name: @@ -5891,7 +5886,7 @@ def and_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap: # arbitrarily give precedence to m2. (In the future, we could use # an intersection type.) result = m2.copy() - m2_keys = set(literal_hash(n2) for n2 in m2) + m2_keys = {literal_hash(n2) for n2 in m2} for n1 in m1: if literal_hash(n1) not in m2_keys: result[n1] = m1[n1] @@ -6561,7 +6556,7 @@ def is_static(func: Union[FuncBase, Decorator]) -> bool: return is_static(func.func) elif isinstance(func, FuncBase): return func.is_static - assert False, "Unexpected func type: {}".format(type(func)) + assert False, f"Unexpected func type: {type(func)}" def is_subtype_no_promote(left: Type, right: Type) -> bool: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index cea44c6c17db..ed6fd73acfa5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -156,7 +156,7 @@ def extract_refexpr_names(expr: RefExpr) -> Set[str]: else: break else: - raise AssertionError("Unknown RefExpr subclass: {}".format(type(expr))) + raise AssertionError(f"Unknown RefExpr subclass: {type(expr)}") return output @@ -437,7 +437,7 @@ def method_fullname(self, object_type: Type, method_name: str) -> Optional[str]: type_name = tuple_fallback(object_type).type.fullname if type_name is not None: - return '{}.{}'.format(type_name, method_name) + return f'{type_name}.{method_name}' else: return None @@ -592,7 +592,7 @@ def check_typeddict_call_with_kwargs(self, callee: TypedDictType, self.chk.check_simple_assignment( lvalue_type=item_expected_type, rvalue=item_value, context=item_value, msg=message_registry.INCOMPATIBLE_TYPES, - lvalue_name='TypedDict item "{}"'.format(item_name), + lvalue_name=f'TypedDict item "{item_name}"', rvalue_name='expression', code=codes.TYPEDDICT_ITEM) @@ -2199,7 +2199,7 @@ def visit_op_expr(self, e: OpExpr) -> Type: e.method_type = method_type return result else: - raise RuntimeError('Unknown operator {}'.format(e.op)) + raise RuntimeError(f'Unknown operator {e.op}') def visit_comparison_expr(self, e: ComparisonExpr) -> Type: """Type check a comparison expression. @@ -2296,7 +2296,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: self.msg.dangerous_comparison(left_type, right_type, 'identity', e) method_type = None else: - raise RuntimeError('Unknown comparison operator {}'.format(operator)) + raise RuntimeError(f'Unknown comparison operator {operator}') e.method_types.append(method_type) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 04b64e9ba7fe..29d2728c2174 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -263,7 +263,7 @@ def analyze_type_callable_member_access(name: str, # Look up from the 'type' type. return _analyze_member_access(name, typ.fallback, mx) else: - assert False, 'Unexpected type {}'.format(repr(ret_type)) + assert False, f'Unexpected type {ret_type!r}' def analyze_type_type_member_access(name: str, @@ -410,7 +410,7 @@ def analyze_member_var_access(name: str, result = getattr_type # Call the attribute hook before returning. - fullname = '{}.{}'.format(method.info.fullname, name) + fullname = f'{method.info.fullname}.{name}' hook = mx.chk.plugin.get_attribute_hook(fullname) if hook: result = hook(AttributeContext(get_proper_type(mx.original_type), @@ -607,7 +607,7 @@ def analyze_var(name: str, mx.not_ready_callback(var.name, mx.context) # Implicit 'Any' type. result = AnyType(TypeOfAny.special_form) - fullname = '{}.{}'.format(var.info.fullname, name) + fullname = f'{var.info.fullname}.{name}' hook = mx.chk.plugin.get_attribute_hook(fullname) if result and not mx.is_lvalue and not implicit: result = analyze_descriptor_access(result, mx) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 7836da019257..6a8a0196306c 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -53,13 +53,10 @@ # For every Pattern a PatternType can be calculated. This requires recursively calculating # the PatternTypes of the sub-patterns first. # Using the data in the PatternType the match subject and captured names can be narrowed/inferred. -PatternType = NamedTuple( - 'PatternType', - [ - ('type', Type), # The type the match subject can be narrowed to - ('rest_type', Type), # The remaining type if the pattern didn't match - ('captures', Dict[Expression, Type]), # The variables captured by the pattern - ]) +class PatternType(NamedTuple): + type: Type # The type the match subject can be narrowed to + rest_type: Type # The remaining type if the pattern didn't match + captures: Dict[Expression, Type] # The variables captured by the pattern class PatternChecker(PatternVisitor[PatternType]): @@ -628,7 +625,7 @@ def update_type_map(self, ) -> None: # Calculating this would not be needed if TypeMap directly used literal hashes instead of # expressions, as suggested in the TODO above it's definition - already_captured = set(literal_hash(expr) for expr in original_type_map) + already_captured = {literal_hash(expr) for expr in original_type_map} for expr, typ in extra_type_map.items(): if literal_hash(expr) in already_captured: node = get_var(expr) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 995f3073ba79..589a1fd6ac40 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -519,7 +519,7 @@ def apply_field_accessors(self, spec: ConversionSpecifier, repl: Expression, dummy, fnam="", module=None, options=self.chk.options, errors=temp_errors ) if temp_errors.is_errors(): - self.msg.fail('Syntax error in format specifier "{}"'.format(spec.field), + self.msg.fail(f'Syntax error in format specifier "{spec.field}"', ctx, code=codes.STRING_FORMATTING) return TempNode(AnyType(TypeOfAny.from_error)) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 36358f9df79a..a9ba8535a5d6 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -28,20 +28,20 @@ def parse_version(v: str) -> Tuple[int, int]: m = re.match(r'\A(\d)\.(\d+)\Z', v) if not m: raise argparse.ArgumentTypeError( - "Invalid python version '{}' (expected format: 'x.y')".format(v)) + f"Invalid python version '{v}' (expected format: 'x.y')") major, minor = int(m.group(1)), int(m.group(2)) if major == 2: if minor != 7: raise argparse.ArgumentTypeError( - "Python 2.{} is not supported (must be 2.7)".format(minor)) + f"Python 2.{minor} is not supported (must be 2.7)") elif major == 3: if minor < defaults.PYTHON3_VERSION_MIN[1]: raise argparse.ArgumentTypeError( - "Python 3.{0} is not supported (must be {1}.{2} or higher)".format(minor, + "Python 3.{} is not supported (must be {}.{} or higher)".format(minor, *defaults.PYTHON3_VERSION_MIN)) else: raise argparse.ArgumentTypeError( - "Python major version '{}' out of range (must be 2 or 3)".format(major)) + f"Python major version '{major}' out of range (must be 2 or 3)") return major, minor @@ -105,7 +105,7 @@ def check_follow_imports(choice: str) -> str: raise argparse.ArgumentTypeError( "invalid choice '{}' (choose from {})".format( choice, - ', '.join("'{}'".format(x) for x in choices))) + ', '.join(f"'{x}'" for x in choices))) return choice @@ -196,7 +196,7 @@ def parse_config_file(options: Options, set_strict_flags: Callable[[], None], parser = config_parser config_types = ini_config_types except (tomllib.TOMLDecodeError, configparser.Error, ConfigTOMLValueError) as err: - print("%s: %s" % (config_file, err), file=stderr) + print(f"{config_file}: {err}", file=stderr) else: if config_file in defaults.SHARED_CONFIG_FILES and 'mypy' not in parser: continue @@ -214,7 +214,7 @@ def parse_config_file(options: Options, set_strict_flags: Callable[[], None], print("%s: No [mypy] section in config file" % file_read, file=stderr) else: section = parser['mypy'] - prefix = '%s: [%s]: ' % (file_read, 'mypy') + prefix = '{}: [{}]: '.format(file_read, 'mypy') updates, report_dirs = parse_section( prefix, options, set_strict_flags, section, config_types, stderr) for k, v in updates.items(): @@ -258,7 +258,7 @@ def get_prefix(file_read: str, name: str) -> str: else: module_name_str = name - return '%s: [%s]: ' % (file_read, module_name_str) + return f'{file_read}: [{module_name_str}]: ' def is_toml(filename: str) -> bool: @@ -369,7 +369,7 @@ def parse_section(prefix: str, template: Options, if report_type in defaults.REPORTER_NAMES: report_dirs[report_type] = str(section[key]) else: - print("%sUnrecognized report type: %s" % (prefix, key), + print(f"{prefix}Unrecognized report type: {key}", file=stderr) continue if key.startswith('x_'): @@ -386,7 +386,7 @@ def parse_section(prefix: str, template: Options, elif key == 'strict': pass # Special handling below else: - print("%sUnrecognized option: %s = %s" % (prefix, key, section[key]), + print(f"{prefix}Unrecognized option: {key} = {section[key]}", file=stderr) if invert: dv = getattr(template, options_key, None) @@ -404,19 +404,19 @@ def parse_section(prefix: str, template: Options, v = not v elif callable(ct): if invert: - print("%sCan not invert non-boolean key %s" % (prefix, options_key), + print(f"{prefix}Can not invert non-boolean key {options_key}", file=stderr) continue try: v = ct(section.get(key)) except argparse.ArgumentTypeError as err: - print("%s%s: %s" % (prefix, key, err), file=stderr) + print(f"{prefix}{key}: {err}", file=stderr) continue else: - print("%sDon't know what type %s should have" % (prefix, key), file=stderr) + print(f"{prefix}Don't know what type {key} should have", file=stderr) continue except ValueError as err: - print("%s%s: %s" % (prefix, key, err), file=stderr) + print(f"{prefix}{key}: {err}", file=stderr) continue if key == 'strict': if v: @@ -493,7 +493,7 @@ def mypy_comments_to_config_map(line: str, name = entry value = None else: - name, value = [x.strip() for x in entry.split('=', 1)] + name, value = (x.strip() for x in entry.split('=', 1)) name = name.replace('-', '_') if value is None: diff --git a/mypy/constraints.py b/mypy/constraints.py index 1d9ca8b138ed..9a6d87575bdc 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -45,7 +45,7 @@ def __repr__(self) -> str: op_str = '<:' if self.op == SUPERTYPE_OF: op_str = ':>' - return '{} {} {}'.format(self.type_var, op_str, self.target) + return f'{self.type_var} {op_str} {self.target}' def infer_constraints_for_callable( @@ -748,7 +748,7 @@ def visit_union_type(self, template: UnionType) -> List[Constraint]: " (should have been handled in infer_constraints)") def visit_type_alias_type(self, template: TypeAliasType) -> List[Constraint]: - assert False, "This should be never called, got {}".format(template) + assert False, f"This should be never called, got {template}" def infer_against_any(self, types: Iterable[Type], any_type: AnyType) -> List[Constraint]: res: List[Constraint] = [] @@ -791,7 +791,7 @@ def neg_op(op: int) -> int: elif op == SUPERTYPE_OF: return SUBTYPE_OF else: - raise ValueError('Invalid operator {}'.format(op)) + raise ValueError(f'Invalid operator {op}') def find_matching_overload_item(overloaded: Overloaded, template: CallableType) -> CallableType: diff --git a/mypy/dmypy_os.py b/mypy/dmypy_os.py index 3168f7566a27..1405e0a309e9 100644 --- a/mypy/dmypy_os.py +++ b/mypy/dmypy_os.py @@ -38,6 +38,6 @@ def alive(pid: int) -> bool: def kill(pid: int) -> None: """Kill the process.""" if sys.platform == 'win32': - subprocess.check_output("taskkill /pid {pid} /f /t".format(pid=pid)) + subprocess.check_output(f"taskkill /pid {pid} /f /t") else: os.kill(pid, signal.SIGKILL) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 04e25091484e..de03d6400525 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -56,7 +56,7 @@ def daemonize(options: Options, """ command = [sys.executable, '-m', 'mypy.dmypy', '--status-file', status_file, 'daemon'] pickled_options = pickle.dumps((options.snapshot(), timeout, log_file)) - command.append('--options-data="{}"'.format(base64.b64encode(pickled_options).decode())) + command.append(f'--options-data="{base64.b64encode(pickled_options).decode()}"') info = STARTUPINFO() info.dwFlags = 0x1 # STARTF_USESHOWWINDOW aka use wShowWindow's value info.wShowWindow = 0 # SW_HIDE aka make the window invisible @@ -200,7 +200,7 @@ def __init__(self, options: Options, self.formatter = FancyFormatter(sys.stdout, sys.stderr, options.show_error_codes) def _response_metadata(self) -> Dict[str, str]: - py_version = '{}_{}'.format(self.options.python_version[0], self.options.python_version[1]) + py_version = f'{self.options.python_version[0]}_{self.options.python_version[1]}' return { 'platform': self.options.platform, 'python_version': py_version, @@ -367,7 +367,7 @@ def cmd_recheck(self, sources = sources + added_sources # Make a copy! t1 = time.time() manager = self.fine_grained_manager.manager - manager.log("fine-grained increment: cmd_recheck: {:.3f}s".format(t1 - t0)) + manager.log(f"fine-grained increment: cmd_recheck: {t1 - t0:.3f}s") if not self.following_imports(): messages = self.fine_grained_increment(sources, remove, update) else: @@ -525,10 +525,10 @@ def fine_grained_increment(self, manager.search_paths) manager.search_paths = compute_search_paths(sources, manager.options, manager.data_dir) t1 = time.time() - manager.log("fine-grained increment: find_changed: {:.3f}s".format(t1 - t0)) + manager.log(f"fine-grained increment: find_changed: {t1 - t0:.3f}s") messages = self.fine_grained_manager.update(changed, removed) t2 = time.time() - manager.log("fine-grained increment: update: {:.3f}s".format(t2 - t1)) + manager.log(f"fine-grained increment: update: {t2 - t1:.3f}s") manager.add_stats( find_changes_time=t1 - t0, fg_update_time=t2 - t1, @@ -555,7 +555,7 @@ def fine_grained_increment_follow_imports(self, sources: List[BuildSource]) -> L manager.search_paths = compute_search_paths(sources, manager.options, manager.data_dir) t1 = time.time() - manager.log("fine-grained increment: find_changed: {:.3f}s".format(t1 - t0)) + manager.log(f"fine-grained increment: find_changed: {t1 - t0:.3f}s") seen = {source.module for source in sources} @@ -646,7 +646,7 @@ def refresh_file(module: str, path: str) -> List[str]: t5 = time.time() - manager.log("fine-grained increment: update: {:.3f}s".format(t5 - t1)) + manager.log(f"fine-grained increment: update: {t5 - t1:.3f}s") manager.add_stats( find_changes_time=t1 - t0, fg_update_time=t2 - t1, @@ -902,7 +902,7 @@ def get_meminfo() -> Dict[str, Any]: def find_all_sources_in_build(graph: mypy.build.Graph, extra: Sequence[BuildSource] = ()) -> List[BuildSource]: result = list(extra) - seen = set(source.module for source in result) + seen = {source.module for source in result} for module, state in graph.items(): if module not in seen: result.append(BuildSource(state.path, module)) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 85d6d9dd4159..e237e818edae 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -25,7 +25,7 @@ def __init__(self, code: str, error_codes[code] = self def __str__(self) -> str: - return ''.format(self.code) + return f'' ATTR_DEFINED: Final = ErrorCode("attr-defined", "Check that attribute exists", "General") diff --git a/mypy/errors.py b/mypy/errors.py index 590b02865fb5..0ad56b079ecc 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -161,7 +161,7 @@ def on_error(self, file: str, info: ErrorInfo) -> bool: elif callable(self._filter): should_filter = self._filter(file, info) else: - raise AssertionError("invalid error filter: {}".format(type(self._filter))) + raise AssertionError(f"invalid error filter: {type(self._filter)}") if should_filter and self._filtered is not None: self._filtered.append(info) @@ -661,18 +661,18 @@ def format_messages(self, error_info: List[ErrorInfo], s = '' if file is not None: if self.show_column_numbers and line >= 0 and column >= 0: - srcloc = '{}:{}:{}'.format(file, line, 1 + column) + srcloc = f'{file}:{line}:{1 + column}' elif line >= 0: - srcloc = '{}:{}'.format(file, line) + srcloc = f'{file}:{line}' else: srcloc = file - s = '{}: {}: {}'.format(srcloc, severity, message) + s = f'{srcloc}: {severity}: {message}' else: s = message if self.show_error_codes and code and severity != 'note': # If note has an error code, it is related to a previous error. Avoid # displaying duplicate error codes. - s = '{} [{}]'.format(s, code.code) + s = f'{s} [{code.code}]' a.append(s) if self.pretty: # Add source code fragment and a location marker. @@ -723,10 +723,12 @@ def targets(self) -> Set[str]: """Return a set of all targets that contain errors.""" # TODO: Make sure that either target is always defined or that not being defined # is okay for fine-grained incremental checking. - return set(info.target - for errs in self.error_info_map.values() - for info in errs - if info.target) + return { + info.target + for errs in self.error_info_map.values() + for info in errs + if info.target + } def render_messages(self, errors: List[ErrorInfo]) -> List[ErrorTuple]: @@ -792,7 +794,7 @@ def render_messages(self, result.append((file, -1, -1, 'note', 'At top level:', e.allow_dups, None)) else: result.append((file, -1, -1, 'note', - 'In class "{}":'.format(e.type), e.allow_dups, None)) + f'In class "{e.type}":', e.allow_dups, None)) if isinstance(e.message, ErrorMessage): result.append( @@ -927,14 +929,14 @@ def report_internal_error(err: Exception, # Compute file:line prefix for official-looking error messages. if file: if line: - prefix = '{}:{}: '.format(file, line) + prefix = f'{file}:{line}: ' else: - prefix = '{}: '.format(file) + prefix = f'{file}: ' else: prefix = '' # Print "INTERNAL ERROR" message. - print('{}error: INTERNAL ERROR --'.format(prefix), + print(f'{prefix}error: INTERNAL ERROR --', 'Please try using mypy master on GitHub:\n' 'https://mypy.readthedocs.io/en/stable/common_issues.html' '#using-a-development-mypy-build', @@ -946,7 +948,7 @@ def report_internal_error(err: Exception, print('If this issue continues with mypy master, ' 'please report a bug at https://github.com/python/mypy/issues', file=stderr) - print('version: {}'.format(mypy_version), + print(f'version: {mypy_version}', file=stderr) # If requested, drop into pdb. This overrides show_tb. @@ -969,8 +971,8 @@ def report_internal_error(err: Exception, print('Traceback (most recent call last):') for s in traceback.format_list(tb + tb2): print(s.rstrip('\n')) - print('{}: {}'.format(type(err).__name__, err), file=stdout) - print('{}: note: use --pdb to drop into pdb'.format(prefix), file=stderr) + print(f'{type(err).__name__}: {err}', file=stdout) + print(f'{prefix}: note: use --pdb to drop into pdb', file=stderr) # Exit. The caller has nothing more to say. # We use exit code 2 to signal that this is no ordinary error. diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 78b36f288757..985114a53051 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -164,7 +164,7 @@ def expand_unpack(self, t: UnpackType) -> Optional[Union[List[Type], Instance, A elif isinstance(repl, UninhabitedType): return None else: - raise NotImplementedError("Invalid type to expand: {}".format(repl)) + raise NotImplementedError(f"Invalid type to expand: {repl}") else: raise NotImplementedError diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 0b3322db2af3..e4e8f4a7888d 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -250,7 +250,7 @@ def parse_type_comment(type_comment: str, except SyntaxError: if errors is not None: stripped_type = type_comment.split("#", 2)[0].strip() - err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) + err_msg = f'{TYPE_COMMENT_SYNTAX_ERROR} "{stripped_type}"' errors.report(line, column, err_msg, blocker=True, code=codes.SYNTAX) return None, None else: @@ -833,7 +833,7 @@ def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: stripped_type = n.type_comment.split("#", 2)[0].strip() - err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) + err_msg = f'{TYPE_COMMENT_SYNTAX_ERROR} "{stripped_type}"' self.fail(err_msg, lineno, n.col_offset) if n.type_comment and n.type_comment[0] not in ["(", "#"]: self.note('Suggestion: wrap argument types in parentheses', @@ -1751,7 +1751,7 @@ def visit_Call(self, e: Call) -> Type: typ = converted else: self.fail( - 'Unexpected argument "{}" for argument constructor'.format(k.arg), + f'Unexpected argument "{k.arg}" for argument constructor', value.lineno, value.col_offset) return CallableArgument(typ, name, constructor, e.lineno, e.col_offset) @@ -1840,7 +1840,7 @@ def numeric_type(self, value: object, n: AST) -> Type: # RawExpressionType so we just pass in 'None' for now. We'll report the # appropriate error at a later stage. numeric_value = None - type_name = 'builtins.{}'.format(type(value).__name__) + type_name = f'builtins.{type(value).__name__}' return RawExpressionType( numeric_value, type_name, @@ -1939,7 +1939,7 @@ def visit_Attribute(self, n: Attribute) -> Type: before_dot = self.visit(n.value) if isinstance(before_dot, UnboundType) and not before_dot.args: - return UnboundType("{}.{}".format(before_dot.name, n.attr), line=self.line) + return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line) else: return self.invalid_type(n) @@ -1959,5 +1959,5 @@ def stringify_name(n: AST) -> Optional[str]: elif isinstance(n, Attribute): sv = stringify_name(n.value) if sv is not None: - return "{}.{}".format(sv, n.attr) + return f"{sv}.{n.attr}" return None # Can't do it. diff --git a/mypy/fastparse2.py b/mypy/fastparse2.py index 0e8faa957d67..e42a1e3c52c5 100644 --- a/mypy/fastparse2.py +++ b/mypy/fastparse2.py @@ -408,7 +408,7 @@ def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: stripped_type = type_comment.split("#", 2)[0].strip() - err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) + err_msg = f'{TYPE_COMMENT_SYNTAX_ERROR} "{stripped_type}"' self.fail(err_msg, lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) @@ -545,14 +545,14 @@ def convert_arg(self, index: int, arg: ast27.expr, line: int, if isinstance(arg, Name): v = arg.id elif isinstance(arg, ast27_Tuple): - v = '__tuple_arg_{}'.format(index + 1) + v = f'__tuple_arg_{index + 1}' rvalue = NameExpr(v) rvalue.set_line(line) assignment = AssignmentStmt([self.visit(arg)], rvalue) assignment.set_line(line) decompose_stmts.append(assignment) else: - raise RuntimeError("'{}' is not a valid argument.".format(ast27.dump(arg))) + raise RuntimeError(f"'{ast27.dump(arg)}' is not a valid argument.") return Var(v) def get_type(self, @@ -578,7 +578,7 @@ def stringify_name(self, n: AST) -> str: if isinstance(n, Name): return n.id elif isinstance(n, Attribute): - return "{}.{}".format(self.stringify_name(n.value), n.attr) + return f"{self.stringify_name(n.value)}.{n.attr}" else: assert False, "can't stringify " + str(type(n)) diff --git a/mypy/find_sources.py b/mypy/find_sources.py index 25da5b4aa842..64e975f86833 100644 --- a/mypy/find_sources.py +++ b/mypy/find_sources.py @@ -38,7 +38,7 @@ def create_source_list(paths: Sequence[str], options: Options, sub_sources = finder.find_sources_in_dir(path) if not sub_sources and not allow_empty_dir: raise InvalidSourceList( - "There are no .py[i] files in directory '{}'".format(path) + f"There are no .py[i] files in directory '{path}'" ) sources.extend(sub_sources) else: @@ -185,7 +185,7 @@ def _crawl_up_helper(self, dir: str) -> Optional[Tuple[str, str]]: if not name.isidentifier(): # in most cases the directory name is invalid, we'll just stop crawling upwards # but if there's an __init__.py in the directory, something is messed up - raise InvalidSourceList("{} is not a valid Python package name".format(name)) + raise InvalidSourceList(f"{name} is not a valid Python package name") # we're definitely a package, so we always return a non-None value mod_prefix, base_dir = self.crawl_up_dir(parent) return module_join(mod_prefix, name), base_dir diff --git a/mypy/fixup.py b/mypy/fixup.py index ec979e4e1927..1f04c2b181fa 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -81,7 +81,7 @@ def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None: assert stnode.node is not None, (table_fullname + "." + key, cross_ref) value.node = stnode.node elif not self.allow_missing: - assert False, "Could not find cross-ref %s" % (cross_ref,) + assert False, f"Could not find cross-ref {cross_ref}" else: # We have a missing crossref in allow missing mode, need to put something value.node = missing_info(self.modules) @@ -92,7 +92,7 @@ def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None: elif value.node is not None: value.node.accept(self) else: - assert False, 'Unexpected empty node %r: %s' % (key, value) + assert False, f'Unexpected empty node {key!r}: {value}' def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: diff --git a/mypy/gclogger.py b/mypy/gclogger.py index 1f36225461de..b8d7980f5f43 100644 --- a/mypy/gclogger.py +++ b/mypy/gclogger.py @@ -29,7 +29,7 @@ def gc_callback(self, phase: str, info: Mapping[str, int]) -> None: self.gc_collected += info['collected'] self.gc_uncollectable += info['uncollectable'] else: - assert False, "Unrecognized gc phase (%r)" % (phase,) + assert False, f"Unrecognized gc phase ({phase!r})" def __exit__(self, *args: object) -> None: while self.gc_callback in gc.callbacks: diff --git a/mypy/ipc.py b/mypy/ipc.py index f48ac18075d8..bf8bfa43b62a 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -59,7 +59,7 @@ def read(self, size: int = 100000) -> bytes: timeout = int(self.timeout * 1000) if self.timeout else _winapi.INFINITE res = _winapi.WaitForSingleObject(ov.event, timeout) if res != _winapi.WAIT_OBJECT_0: - raise IPCException("Bad result from I/O wait: {}".format(res)) + raise IPCException(f"Bad result from I/O wait: {res}") except BaseException: ov.cancel() raise @@ -96,17 +96,17 @@ def write(self, data: bytes) -> None: timeout = int(self.timeout * 1000) if self.timeout else _winapi.INFINITE res = _winapi.WaitForSingleObject(ov.event, timeout) if res != _winapi.WAIT_OBJECT_0: - raise IPCException("Bad result from I/O wait: {}".format(res)) + raise IPCException(f"Bad result from I/O wait: {res}") elif err != 0: - raise IPCException("Failed writing to pipe with error: {}".format(err)) + raise IPCException(f"Failed writing to pipe with error: {err}") except BaseException: ov.cancel() raise bytes_written, err = ov.GetOverlappedResult(True) assert err == 0, err assert bytes_written == len(data) - except WindowsError as e: - raise IPCException("Failed to write with error: {}".format(e.winerror)) from e + except OSError as e: + raise IPCException(f"Failed to write with error: {e.winerror}") from e else: self.connection.sendall(data) self.connection.shutdown(socket.SHUT_WR) @@ -129,8 +129,8 @@ def __init__(self, name: str, timeout: Optional[float]) -> None: try: _winapi.WaitNamedPipe(self.name, timeout) except FileNotFoundError as e: - raise IPCException("The NamedPipe at {} was not found.".format(self.name)) from e - except WindowsError as e: + raise IPCException(f"The NamedPipe at {self.name} was not found.") from e + except OSError as e: if e.winerror == _winapi.ERROR_SEM_TIMEOUT: raise IPCException("Timed out waiting for connection.") from e else: @@ -145,7 +145,7 @@ def __init__(self, name: str, timeout: Optional[float]) -> None: _winapi.FILE_FLAG_OVERLAPPED, _winapi.NULL, ) - except WindowsError as e: + except OSError as e: if e.winerror == _winapi.ERROR_PIPE_BUSY: raise IPCException("The connection is busy.") from e else: @@ -179,7 +179,7 @@ def __init__(self, name: str, timeout: Optional[float] = None) -> None: name = r'\\.\pipe\{}-{}.pipe'.format( name, base64.urlsafe_b64encode(os.urandom(6)).decode()) else: - name = '{}.sock'.format(name) + name = f'{name}.sock' super().__init__(name, timeout) if sys.platform == 'win32': self.connection = _winapi.CreateNamedPipe(self.name, @@ -198,7 +198,7 @@ def __init__(self, name: str, timeout: Optional[float] = None) -> None: ) if self.connection == -1: # INVALID_HANDLE_VALUE err = _winapi.GetLastError() - raise IPCException('Invalid handle to pipe: {}'.format(err)) + raise IPCException(f'Invalid handle to pipe: {err}') else: self.sock_directory = tempfile.mkdtemp() sockfile = os.path.join(self.sock_directory, self.name) @@ -216,7 +216,7 @@ def __enter__(self) -> 'IPCServer': ov = _winapi.ConnectNamedPipe(self.connection, overlapped=True) # TODO: remove once typeshed supports Literal types assert isinstance(ov, _winapi.Overlapped) - except WindowsError as e: + except OSError as e: # Don't raise if the client already exists, or the client already connected if e.winerror not in (_winapi.ERROR_PIPE_CONNECTED, _winapi.ERROR_NO_DATA): raise diff --git a/mypy/join.py b/mypy/join.py index 78f280411622..94d0afc434f9 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -451,7 +451,7 @@ def visit_type_type(self, t: TypeType) -> ProperType: return self.default(self.s) def visit_type_alias_type(self, t: TypeAliasType) -> ProperType: - assert False, "This should be never called, got {}".format(t) + assert False, f"This should be never called, got {t}" def join(self, s: Type, t: Type) -> ProperType: return join_types(s, t) diff --git a/mypy/lookup.py b/mypy/lookup.py index fcb2f1607393..8a8350080bc2 100644 --- a/mypy/lookup.py +++ b/mypy/lookup.py @@ -26,7 +26,7 @@ def lookup_fully_qualified(name: str, modules: Dict[str, MypyFile], *, while True: if '.' not in head: if raise_on_missing: - assert '.' in head, "Cannot find module for %s" % (name,) + assert '.' in head, f"Cannot find module for {name}" return None head, tail = head.rsplit('.', maxsplit=1) rest.append(tail) @@ -38,13 +38,13 @@ def lookup_fully_qualified(name: str, modules: Dict[str, MypyFile], *, if not rest: # Looks like a module, don't use this to avoid confusions. if raise_on_missing: - assert rest, "Cannot find %s, got a module symbol" % (name,) + assert rest, f"Cannot find {name}, got a module symbol" return None while True: key = rest.pop() if key not in names: if raise_on_missing: - assert key in names, "Cannot find component %r for %r" % (key, name) + assert key in names, f"Cannot find component {key!r} for {name!r}" return None stnode = names[key] if not rest: @@ -54,6 +54,6 @@ def lookup_fully_qualified(name: str, modules: Dict[str, MypyFile], *, # or a Var made up for a missing module. if not isinstance(node, TypeInfo): if raise_on_missing: - assert node, "Cannot find %s" % (name,) + assert node, f"Cannot find {name}" return None names = node.names diff --git a/mypy/main.py b/mypy/main.py index a126122c18ed..6b0238295314 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -36,7 +36,7 @@ def stat_proxy(path: str) -> os.stat_result: try: st = orig_stat(path) except os.error as err: - print("stat(%r) -> %s" % (path, err)) + print(f"stat({path!r}) -> {err}") raise else: print("stat(%r) -> (st_mode=%o, st_mtime=%d, st_size=%d)" % @@ -231,11 +231,11 @@ def invert_flag_name(flag: str) -> str: if len(split) == 2: prefix, rest = split if prefix in flag_prefix_map: - return '--{}-{}'.format(flag_prefix_map[prefix], rest) + return f'--{flag_prefix_map[prefix]}-{rest}' elif prefix == 'no': - return '--{}'.format(rest) + return f'--{rest}' - return '--no-{}'.format(flag[2:]) + return f'--no-{flag[2:]}' class PythonExecutableInferenceError(Exception): @@ -248,9 +248,9 @@ def python_executable_prefix(v: str) -> List[str]: # is the `py` launcher, which can be passed a version e.g. `py -3.8`, and it will # execute an installed Python 3.8 interpreter. See also: # https://docs.python.org/3/using/windows.html#python-launcher-for-windows - return ['py', '-{}'.format(v)] + return ['py', f'-{v}'] else: - return ['python{}'.format(v)] + return [f'python{v}'] def _python_executable_from_version(python_version: Tuple[int, int]) -> str: @@ -460,7 +460,7 @@ def add_invertible_flag(flag: str, group = parser if help is not argparse.SUPPRESS: - help += " (inverse: {})".format(inverse) + help += f" (inverse: {inverse})" arg = group.add_argument(flag, action='store_false' if default else 'store_true', @@ -1050,11 +1050,11 @@ def set_strict_flags() -> None: cache = FindModuleCache(search_paths, fscache, options) for p in special_opts.packages: if os.sep in p or os.altsep and os.altsep in p: - fail("Package name '{}' cannot have a slash in it.".format(p), + fail(f"Package name '{p}' cannot have a slash in it.", stderr, options) p_targets = cache.find_modules_recursive(p) if not p_targets: - fail("Can't find package '{}'".format(p), stderr, options) + fail(f"Can't find package '{p}'", stderr, options) targets.extend(p_targets) for m in special_opts.modules: targets.append(BuildSource(None, m, None)) @@ -1133,7 +1133,7 @@ def process_cache_map(parser: argparse.ArgumentParser, def maybe_write_junit_xml(td: float, serious: bool, messages: List[str], options: Options) -> None: if options.junit_xml: - py_version = '{}_{}'.format(options.python_version[0], options.python_version[1]) + py_version = f'{options.python_version[0]}_{options.python_version[1]}' util.write_junit_xml( td, serious, messages, options.junit_xml, py_version, options.platform) diff --git a/mypy/meet.py b/mypy/meet.py index ad7725182838..2602f0c1abd8 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -444,8 +444,8 @@ def are_tuples_overlapping(left: Type, right: Type, *, left, right = get_proper_types((left, right)) left = adjust_tuple(left, right) or left right = adjust_tuple(right, left) or right - assert isinstance(left, TupleType), 'Type {} is not a tuple'.format(left) - assert isinstance(right, TupleType), 'Type {} is not a tuple'.format(right) + assert isinstance(left, TupleType), f'Type {left} is not a tuple' + assert isinstance(right, TupleType), f'Type {right} is not a tuple' if len(left.items) != len(right.items): return False return all(is_overlapping_types(l, r, @@ -712,7 +712,7 @@ def visit_type_type(self, t: TypeType) -> ProperType: return self.default(self.s) def visit_type_alias_type(self, t: TypeAliasType) -> ProperType: - assert False, "This should be never called, got {}".format(t) + assert False, f"This should be never called, got {t}" def meet(self, s: Type, t: Type) -> ProperType: return meet_types(s, t) diff --git a/mypy/memprofile.py b/mypy/memprofile.py index 5052d0418994..7494fed75750 100644 --- a/mypy/memprofile.py +++ b/mypy/memprofile.py @@ -94,7 +94,7 @@ def find_recursive_objects(objs: List[object]) -> None: We use this since gc.get_objects() does not return objects without pointers in them such as strings. """ - seen = set(id(o) for o in objs) + seen = {id(o) for o in objs} def visit(o: object) -> None: if id(o) not in seen: diff --git a/mypy/messages.py b/mypy/messages.py index d499a574d527..801f84c2580b 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -231,7 +231,7 @@ def has_no_attr(self, if (isinstance(original_type, Instance) and original_type.type.has_readable_member(member)): - self.fail('Member "{}" is not assignable'.format(member), context) + self.fail(f'Member "{member}" is not assignable', context) elif member == '__contains__': self.fail('Unsupported right operand type for in ({})'.format( format_type(original_type)), context, code=codes.OPERATOR) @@ -363,7 +363,7 @@ def unsupported_operand_types(self, right_str = format_type(right_type) if self.are_type_names_disabled(): - msg = 'Unsupported operand types for {} (likely involving Union)'.format(op) + msg = f'Unsupported operand types for {op} (likely involving Union)' else: msg = 'Unsupported operand types for {} ({} and {})'.format( op, left_str, right_str) @@ -372,7 +372,7 @@ def unsupported_operand_types(self, def unsupported_left_operand(self, op: str, typ: Type, context: Context) -> None: if self.are_type_names_disabled(): - msg = 'Unsupported left operand type for {} (some union)'.format(op) + msg = f'Unsupported left operand type for {op} (some union)' else: msg = 'Unsupported left operand type for {} ({})'.format( op, format_type(typ)) @@ -384,7 +384,7 @@ def not_callable(self, typ: Type, context: Context) -> Type: def untyped_function_call(self, callee: CallableType, context: Context) -> Type: name = callable_name(callee) or '(unknown)' - self.fail('Call to untyped function {} in typed context'.format(name), context, + self.fail(f'Call to untyped function {name} in typed context', context, code=codes.NO_UNTYPED_CALL) return AnyType(TypeOfAny.from_error) @@ -421,7 +421,7 @@ def incompatible_argument(self, for method, op in op_methods_to_symbols.items(): for variant in method, '__r' + method[2:]: # FIX: do not rely on textual formatting - if name.startswith('"{}" of'.format(variant)): + if name.startswith(f'"{variant}" of'): if op == 'in' or variant != method: # Reversed order of base/argument. self.unsupported_operand_types(op, arg_type, base, @@ -455,7 +455,7 @@ def incompatible_argument(self, context, code=codes.ASSIGNMENT) return codes.ASSIGNMENT - target = 'to {} '.format(name) + target = f'to {name} ' msg = '' code = codes.MISC @@ -535,7 +535,7 @@ def incompatible_argument(self, if isinstance(outer_context, CallExpr) and len(outer_context.arg_names) >= n: arg_name = outer_context.arg_names[n - 1] if arg_name is not None: - arg_label = '"{}"'.format(arg_name) + arg_label = f'"{arg_name}"' if (arg_kind == ARG_STAR2 and isinstance(arg_type, TypedDictType) and m <= len(callee.arg_names) @@ -547,7 +547,7 @@ def incompatible_argument(self, arg_type.items[arg_name], expected_type, bare=True) - arg_label = '"{}"'.format(arg_name) + arg_label = f'"{arg_name}"' if isinstance(outer_context, IndexExpr) and isinstance(outer_context.index, StrExpr): msg = 'Value of "{}" has incompatible type {}; expected {}' .format( outer_context.index.value, quote_type_string(arg_type_str), @@ -642,7 +642,7 @@ def too_few_arguments(self, callee: CallableType, context: Context, callee_name = callable_name(callee) if callee_name is not None and diff and all(d is not None for d in diff): args = '", "'.join(cast(List[str], diff)) - msg += ' "{}" in call to {}'.format(args, callee_name) + msg += f' "{args}" in call to {callee_name}' else: msg = 'Too few arguments' + for_function(callee) @@ -651,7 +651,7 @@ def too_few_arguments(self, callee: CallableType, context: Context, self.fail(msg, context, code=codes.CALL_ARG) def missing_named_argument(self, callee: CallableType, context: Context, name: str) -> None: - msg = 'Missing named argument "{}"'.format(name) + for_function(callee) + msg = f'Missing named argument "{name}"' + for_function(callee) self.fail(msg, context, code=codes.CALL_ARG) def too_many_arguments(self, callee: CallableType, context: Context) -> None: @@ -666,7 +666,7 @@ def too_many_arguments_from_typed_dict(self, # Try to determine the name of the extra argument. for key in arg_type.items: if key not in callee.arg_names: - msg = 'Extra argument "{}" from **args'.format(key) + for_function(callee) + msg = f'Extra argument "{key}" from **args' + for_function(callee) break else: self.too_many_arguments(callee, context) @@ -692,7 +692,7 @@ def maybe_note_about_special_args(self, callee: CallableType, context: Context) def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: Type, context: Context) -> None: - msg = 'Unexpected keyword argument "{}"'.format(name) + for_function(callee) + msg = f'Unexpected keyword argument "{name}"' + for_function(callee) # Suggest intended keyword, look for type match else fallback on any match. matching_type_args = [] not_matching_type_args = [] @@ -715,7 +715,7 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: fname = callable_name(callee) if not fname: # an alias to function with a different name fname = 'Called function' - self.note('{} defined here'.format(fname), callee.definition, + self.note(f'{fname} defined here', callee.definition, file=module.path, origin=context, code=codes.CALL_ARG) def duplicate_argument_value(self, callee: CallableType, index: int, @@ -731,7 +731,7 @@ def does_not_return_value(self, callee_type: Optional[Type], context: Context) - if isinstance(callee_type, FunctionLike): name = callable_name(callee_type) if name is not None: - self.fail('{} does not return a value'.format(capitalize(name)), context, + self.fail(f'{capitalize(name)} does not return a value', context, code=codes.FUNC_RETURNS_VALUE) else: self.fail('Function does not return a value', context, code=codes.FUNC_RETURNS_VALUE) @@ -744,8 +744,8 @@ def deleted_as_rvalue(self, typ: DeletedType, context: Context) -> None: if typ.source is None: s = "" else: - s = ' "{}"'.format(typ.source) - self.fail('Trying to read deleted variable{}'.format(s), context) + s = f' "{typ.source}"' + self.fail(f'Trying to read deleted variable{s}', context) def deleted_as_lvalue(self, typ: DeletedType, context: Context) -> None: """Report an error about using an deleted type as an lvalue. @@ -756,8 +756,8 @@ def deleted_as_lvalue(self, typ: DeletedType, context: Context) -> None: if typ.source is None: s = "" else: - s = ' "{}"'.format(typ.source) - self.fail('Assignment to variable{} outside except: block'.format(s), context) + s = f' "{typ.source}"' + self.fail(f'Assignment to variable{s} outside except: block', context) def no_variant_matches_arguments(self, overload: Overloaded, @@ -768,13 +768,13 @@ def no_variant_matches_arguments(self, code = code or codes.CALL_OVERLOAD name = callable_name(overload) if name: - name_str = ' of {}'.format(name) + name_str = f' of {name}' else: name_str = '' arg_types_str = ', '.join(format_type(arg) for arg in arg_types) num_args = len(arg_types) if num_args == 0: - self.fail('All overload variants{} require at least one argument'.format(name_str), + self.fail(f'All overload variants{name_str} require at least one argument', context, code=code) elif num_args == 1: self.fail('No overload variant{} matches argument type {}' @@ -784,7 +784,7 @@ def no_variant_matches_arguments(self, .format(name_str, arg_types_str), context, code=code) self.note( - 'Possible overload variant{}:'.format(plural_s(len(overload.items))), + f'Possible overload variant{plural_s(len(overload.items))}:', context, code=code) for item in overload.items: self.note(pretty_callable(item), context, offset=4, code=code) @@ -793,7 +793,7 @@ def wrong_number_values_to_unpack(self, provided: int, expected: int, context: Context) -> None: if provided < expected: if provided == 1: - self.fail('Need more than 1 value to unpack ({} expected)'.format(expected), + self.fail(f'Need more than 1 value to unpack ({expected} expected)', context) else: self.fail('Need more than {} values to unpack ({} expected)'.format( @@ -806,11 +806,11 @@ def unpacking_strings_disallowed(self, context: Context) -> None: self.fail("Unpacking a string is disallowed", context) def type_not_iterable(self, type: Type, context: Context) -> None: - self.fail('{} object is not iterable'.format(format_type(type)), context) + self.fail(f'{format_type(type)} object is not iterable', context) def incompatible_operator_assignment(self, op: str, context: Context) -> None: - self.fail('Result type of {} incompatible in assignment'.format(op), + self.fail(f'Result type of {op} incompatible in assignment', context) def overload_signature_incompatible_with_supertype( @@ -919,9 +919,9 @@ def return_type_incompatible_with_supertype( def override_target(self, name: str, name_in_super: str, supertype: str) -> str: - target = 'supertype "{}"'.format(supertype) + target = f'supertype "{supertype}"' if name_in_super != name: - target = '"{}" of {}'.format(name_in_super, target) + target = f'"{name_in_super}" of {target}' return target def incompatible_type_application(self, expected_arg_count: int, @@ -941,7 +941,7 @@ def could_not_infer_type_arguments(self, callee_type: CallableType, n: int, context: Context) -> None: callee_name = callable_name(callee_type) if callee_name is not None and n > 0: - self.fail('Cannot infer type argument {} of {}'.format(n, callee_name), context) + self.fail(f'Cannot infer type argument {n} of {callee_name}', context) else: self.fail('Cannot infer function type argument', context) @@ -954,11 +954,11 @@ def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) self.fail('Keywords must be strings', context) else: self.fail( - 'Argument after ** must be a mapping, not {}'.format(format_type(typ)), + f'Argument after ** must be a mapping, not {format_type(typ)}', context, code=codes.ARG_TYPE) def undefined_in_superclass(self, member: str, context: Context) -> None: - self.fail('"{}" undefined in superclass'.format(member), context) + self.fail(f'"{member}" undefined in superclass', context) def first_argument_for_super_must_be_type(self, actual: Type, context: Context) -> None: actual = get_proper_type(actual) @@ -968,7 +968,7 @@ def first_argument_for_super_must_be_type(self, actual: Type, context: Context) type_str = 'a non-type instance' else: type_str = format_type(actual) - self.fail('Argument 1 for "super" must be a type object; got {}'.format(type_str), context, + self.fail(f'Argument 1 for "super" must be a type object; got {type_str}', context, code=codes.ARG_TYPE) def too_few_string_formatting_arguments(self, context: Context) -> None: @@ -1010,7 +1010,7 @@ def cannot_determine_type(self, name: str, context: Context) -> None: self.fail('Cannot determine type of "%s"' % name, context, code=codes.HAS_TYPE) def cannot_determine_type_in_base(self, name: str, base: str, context: Context) -> None: - self.fail('Cannot determine type of "%s" in base class "%s"' % (name, base), context) + self.fail(f'Cannot determine type of "{name}" in base class "{base}"', context) def no_formal_self(self, name: str, item: CallableType, context: Context) -> None: self.fail('Attribute function "%s" with type %s does not accept self argument' @@ -1050,7 +1050,7 @@ def cant_assign_to_classvar(self, name: str, context: Context) -> None: self.fail('Cannot assign to class variable "%s" via instance' % name, context) def final_cant_override_writable(self, name: str, ctx: Context) -> None: - self.fail('Cannot override writable attribute "{}" with a final one'.format(name), ctx) + self.fail(f'Cannot override writable attribute "{name}" with a final one', ctx) def cant_override_final(self, name: str, base_name: str, ctx: Context) -> None: self.fail('Cannot override final attribute "{}"' @@ -1062,7 +1062,7 @@ def cant_assign_to_final(self, name: str, attr_assign: bool, ctx: Context) -> No Pass `attr_assign=True` if the assignment assigns to an attribute. """ kind = "attribute" if attr_assign else "name" - self.fail('Cannot assign to final {} "{}"'.format(kind, unmangle(name)), ctx) + self.fail(f'Cannot assign to final {kind} "{unmangle(name)}"', ctx) def protocol_members_cant_be_final(self, ctx: Context) -> None: self.fail("Protocol member cannot be final", ctx) @@ -1095,7 +1095,7 @@ def dangerous_comparison(self, left: Type, right: Type, kind: str, ctx: Context) def overload_inconsistently_applies_decorator(self, decorator: str, context: Context) -> None: self.fail( - 'Overload does not consistently use the "@{}" '.format(decorator) + f'Overload does not consistently use the "@{decorator}" ' + 'decorator on all function signatures.', context) @@ -1113,7 +1113,7 @@ def overloaded_signature_will_never_match(self, index1: int, index2: int, context) def overloaded_signatures_typevar_specific(self, index: int, context: Context) -> None: - self.fail('Overloaded function implementation cannot satisfy signature {} '.format(index) + + self.fail(f'Overloaded function implementation cannot satisfy signature {index} ' + 'due to inconsistencies in how they use type variables', context) def overloaded_signatures_arg_specific(self, index: int, context: Context) -> None: @@ -1128,7 +1128,7 @@ def warn_both_operands_are_from_unions(self, context: Context) -> None: self.note('Both left and right operands are unions', context, code=codes.OPERATOR) def warn_operand_was_from_union(self, side: str, original: Type, context: Context) -> None: - self.note('{} operand is of type {}'.format(side, format_type(original)), context, + self.note(f'{side} operand is of type {format_type(original)}', context, code=codes.OPERATOR) def operator_method_signatures_overlap( @@ -1152,19 +1152,19 @@ def signatures_incompatible(self, method: str, other_method: str, def yield_from_invalid_operand_type(self, expr: Type, context: Context) -> Type: text = format_type(expr) if format_type(expr) != 'object' else expr - self.fail('"yield from" can\'t be applied to {}'.format(text), context) + self.fail(f'"yield from" can\'t be applied to {text}', context) return AnyType(TypeOfAny.from_error) def invalid_signature(self, func_type: Type, context: Context) -> None: - self.fail('Invalid signature {}'.format(format_type(func_type)), context) + self.fail(f'Invalid signature {format_type(func_type)}', context) def invalid_signature_for_special_method( self, func_type: Type, context: Context, method_name: str) -> None: - self.fail('Invalid signature {} for "{}"'.format(format_type(func_type), method_name), + self.fail(f'Invalid signature {format_type(func_type)} for "{method_name}"', context) def reveal_type(self, typ: Type, context: Context) -> None: - self.note('Revealed type is "{}"'.format(typ), context) + self.note(f'Revealed type is "{typ}"', context) def reveal_locals(self, type_map: Dict[str, Optional[Type]], context: Context) -> None: # To ensure that the output is predictable on Python < 3.6, @@ -1173,15 +1173,15 @@ def reveal_locals(self, type_map: Dict[str, Optional[Type]], context: Context) - if sorted_locals: self.note("Revealed local types are:", context) for k, v in sorted_locals.items(): - self.note(' {}: {}'.format(k, v), context) + self.note(f' {k}: {v}', context) else: self.note("There are no locals to reveal", context) def unsupported_type_type(self, item: Type, context: Context) -> None: - self.fail('Cannot instantiate type "Type[{}]"'.format(format_type_bare(item)), context) + self.fail(f'Cannot instantiate type "Type[{format_type_bare(item)}]"', context) def redundant_cast(self, typ: Type, context: Context) -> None: - self.fail('Redundant cast to {}'.format(format_type(typ)), context, + self.fail(f'Redundant cast to {format_type(typ)}', context, code=codes.REDUNDANT_CAST) def assert_type_fail(self, source_type: Type, target_type: Type, context: Context) -> None: @@ -1190,7 +1190,7 @@ def assert_type_fail(self, source_type: Type, target_type: Type, context: Contex code=codes.ASSERT_TYPE) def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> None: - self.fail("{} becomes {} due to an unfollowed import".format(prefix, format_type(typ)), + self.fail(f"{prefix} becomes {format_type(typ)} due to an unfollowed import", ctx, code=codes.NO_ANY_UNIMPORTED) def need_annotation_for_var(self, node: SymbolNode, context: Context, @@ -1204,18 +1204,18 @@ def need_annotation_for_var(self, node: SymbolNode, context: Context, alias = alias.split('.')[-1] type_dec = '' if alias == 'Dict': - type_dec = '{}, {}'.format(type_dec, type_dec) + type_dec = f'{type_dec}, {type_dec}' if has_variable_annotations: - hint = ' (hint: "{}: {}[{}] = ...")'.format(node.name, alias, type_dec) + hint = f' (hint: "{node.name}: {alias}[{type_dec}] = ...")' else: - hint = ' (hint: "{} = ... # type: {}[{}]")'.format(node.name, alias, type_dec) + hint = f' (hint: "{node.name} = ... # type: {alias}[{type_dec}]")' if has_variable_annotations: needed = 'annotation' else: needed = 'comment' - self.fail('Need type {} for "{}"{}'.format(needed, unmangle(node.name), hint), context, + self.fail(f'Need type {needed} for "{unmangle(node.name)}"{hint}', context, code=codes.VAR_ANNOTATED) def explicit_any(self, ctx: Context) -> None: @@ -1249,12 +1249,12 @@ def unexpected_typeddict_keys( return found = format_key_list(actual_keys, short=True) if not expected_keys: - self.fail('Unexpected TypedDict {}'.format(found), context) + self.fail(f'Unexpected TypedDict {found}', context) return expected = format_key_list(expected_keys) if actual_keys and actual_set < expected_set: - found = 'only {}'.format(found) - self.fail('Expected {} but found {}'.format(expected, found), context, + found = f'only {found}' + self.fail(f'Expected {expected} but found {found}', context, code=codes.TYPEDDICT_ITEM) def typeddict_key_must_be_string_literal( @@ -1295,7 +1295,7 @@ def typeddict_key_cannot_be_deleted( item_name: str, context: Context) -> None: if typ.is_anonymous(): - self.fail('TypedDict key "{}" cannot be deleted'.format(item_name), + self.fail(f'TypedDict key "{item_name}" cannot be deleted', context) else: self.fail('Key "{}" of TypedDict {} cannot be deleted'.format( @@ -1318,7 +1318,7 @@ def disallowed_any_type(self, typ: Type, context: Context) -> None: if isinstance(typ, AnyType): message = 'Expression has type "Any"' else: - message = 'Expression type contains "Any" (has type {})'.format(format_type(typ)) + message = f'Expression type contains "Any" (has type {format_type(typ)})' self.fail(message, context) def incorrectly_returning_any(self, typ: Type, context: Context) -> None: @@ -1347,7 +1347,7 @@ def untyped_decorated_function(self, typ: Type, context: Context) -> None: format_type(typ)), context) def typed_function_untyped_decorator(self, func_name: str, context: Context) -> None: - self.fail('Untyped decorator makes function "{}" untyped'.format(func_name), context) + self.fail(f'Untyped decorator makes function "{func_name}" untyped', context) def bad_proto_variance(self, actual: int, tvar_name: str, expected: int, context: Context) -> None: @@ -1367,7 +1367,7 @@ def concrete_only_call(self, typ: Type, context: Context) -> None: def cannot_use_function_with_type( self, method_name: str, type_name: str, context: Context) -> None: - self.fail("Cannot use {}() with {} type".format(method_name, type_name), context) + self.fail(f"Cannot use {method_name}() with {type_name} type", context) def report_non_method_protocol(self, tp: TypeInfo, members: List[str], context: Context) -> None: @@ -1396,14 +1396,14 @@ def redundant_left_operand(self, op_name: str, context: Context) -> None: it does not change the truth value of the entire condition as a whole. 'op_name' should either be the string "and" or the string "or". """ - self.redundant_expr('Left operand of "{}"'.format(op_name), op_name == 'and', context) + self.redundant_expr(f'Left operand of "{op_name}"', op_name == 'and', context) def unreachable_right_operand(self, op_name: str, context: Context) -> None: """Indicates that the right operand of a boolean expression is redundant: it does not change the truth value of the entire condition as a whole. 'op_name' should either be the string "and" or the string "or". """ - self.fail('Right operand of "{}" is never evaluated'.format(op_name), + self.fail(f'Right operand of "{op_name}" is never evaluated', context, code=codes.UNREACHABLE) def redundant_condition_in_comprehension(self, truthiness: bool, context: Context) -> None: @@ -1413,7 +1413,7 @@ def redundant_condition_in_if(self, truthiness: bool, context: Context) -> None: self.redundant_expr("If condition", truthiness, context) def redundant_expr(self, description: str, truthiness: bool, context: Context) -> None: - self.fail("{} is always {}".format(description, str(truthiness).lower()), + self.fail(f"{description} is always {str(truthiness).lower()}", context, code=codes.REDUNDANT_EXPR) def impossible_intersection(self, @@ -1628,9 +1628,9 @@ def generate_incompatible_tuple_error(self, .format(str(i), format_type(rhs_t), format_type(lhs_t))) error_cnt += 1 - error_msg = msg + ' ({} tuple items are incompatible'.format(str(error_cnt)) + error_msg = msg + f' ({str(error_cnt)} tuple items are incompatible' if error_cnt - 3 > 0: - error_msg += '; {} items are omitted)'.format(str(error_cnt - 3)) + error_msg += f'; {str(error_cnt - 3)} items are omitted)' else: error_msg += ')' self.fail(error_msg, context, code=code) @@ -1638,7 +1638,7 @@ def generate_incompatible_tuple_error(self, self.note(note, context, code=code) def add_fixture_note(self, fullname: str, ctx: Context) -> None: - self.note('Maybe your test fixture does not define "{}"?'.format(fullname), ctx) + self.note(f'Maybe your test fixture does not define "{fullname}"?', ctx) if fullname in SUGGESTED_TEST_FIXTURES: self.note( 'Consider adding [builtins fixtures/{}] to your test description'.format( @@ -1653,7 +1653,7 @@ def quote_type_string(type_string: str) -> str: # Messages are easier to read if these aren't quoted. We use a # regex to match strings with variable contents. return type_string - return '"{}"'.format(type_string) + return f'"{type_string}"' def format_callable_args(arg_types: List[Type], arg_kinds: List[ArgKind], @@ -1701,7 +1701,7 @@ def format_list(types: Sequence[Type]) -> str: def format_literal_value(typ: LiteralType) -> str: if typ.is_enum_literal(): underlying_type = format(typ.fallback) - return '{}.{}'.format(underlying_type, typ.value) + return f'{underlying_type}.{typ.value}' else: return typ.value_repr() @@ -1723,14 +1723,14 @@ def format_literal_value(typ: LiteralType) -> str: return base_str elif itype.type.fullname == 'builtins.tuple': item_type_str = format(itype.args[0]) - return 'Tuple[{}, ...]'.format(item_type_str) + return f'Tuple[{item_type_str}, ...]' elif itype.type.fullname in reverse_builtin_aliases: alias = reverse_builtin_aliases[itype.type.fullname] alias = alias.split('.')[-1] - return '{}[{}]'.format(alias, format_list(itype.args)) + return f'{alias}[{format_list(itype.args)}]' else: # There are type arguments. Convert the arguments to strings. - return '{}[{}]'.format(base_str, format_list(itype.args)) + return f'{base_str}[{format_list(itype.args)}]' elif isinstance(typ, TypeVarType): # This is similar to non-generic instance types. return typ.name @@ -1751,7 +1751,7 @@ def format_literal_value(typ: LiteralType) -> str: # Prefer the name of the fallback class (if not tuple), as it's more informative. if typ.partial_fallback.type.fullname != 'builtins.tuple': return format(typ.partial_fallback) - s = 'Tuple[{}]'.format(format_list(typ.items)) + s = f'Tuple[{format_list(typ.items)}]' return s elif isinstance(typ, TypedDictType): # If the TypedDictType is named, return the name @@ -1766,7 +1766,7 @@ def format_literal_value(typ: LiteralType) -> str: s = 'TypedDict({{{}}})'.format(', '.join(items)) return s elif isinstance(typ, LiteralType): - return 'Literal[{}]'.format(format_literal_value(typ)) + return f'Literal[{format_literal_value(typ)}]' elif isinstance(typ, UnionType): literal_items, union_items = separate_union_literals(typ) @@ -1778,9 +1778,9 @@ def format_literal_value(typ: LiteralType) -> str: ) if len(union_items) == 1 and isinstance(get_proper_type(union_items[0]), NoneType): - return 'Optional[{}]'.format(literal_str) + return f'Optional[{literal_str}]' elif union_items: - return 'Union[{}, {}]'.format(format_list(union_items), literal_str) + return f'Union[{format_list(union_items)}, {literal_str}]' else: return literal_str else: @@ -1790,9 +1790,9 @@ def format_literal_value(typ: LiteralType) -> str: for t in typ.items) == 1) if print_as_optional: rest = [t for t in typ.items if not isinstance(get_proper_type(t), NoneType)] - return 'Optional[{}]'.format(format(rest[0])) + return f'Optional[{format(rest[0])}]' else: - s = 'Union[{}]'.format(format_list(typ.items)) + s = f'Union[{format_list(typ.items)}]' return s elif isinstance(typ, NoneType): @@ -1807,7 +1807,7 @@ def format_literal_value(typ: LiteralType) -> str: else: return '' elif isinstance(typ, TypeType): - return 'Type[{}]'.format(format(typ.item)) + return f'Type[{format(typ.item)}]' elif isinstance(typ, FunctionLike): func = typ if func.is_type_obj(): @@ -1820,7 +1820,7 @@ def format_literal_value(typ: LiteralType) -> str: else: return_type = format(func.ret_type) if func.is_ellipsis_args: - return 'Callable[..., {}]'.format(return_type) + return f'Callable[..., {return_type}]' param_spec = func.param_spec() if param_spec is not None: return f'Callable[{format(param_spec)}, {return_type}]' @@ -1830,7 +1830,7 @@ def format_literal_value(typ: LiteralType) -> str: func.arg_names, format, verbosity) - return 'Callable[[{}], {}]'.format(args, return_type) + return f'Callable[[{args}], {return_type}]' else: # Use a simple representation for function types; proper # function types may result in long and difficult-to-read @@ -1884,8 +1884,8 @@ def find_type_overlaps(*types: Type) -> Set[str]: for inst in collect_all_instances(type): d.setdefault(inst.type.name, set()).add(inst.type.fullname) for shortname in d.keys(): - if 'typing.{}'.format(shortname) in TYPES_FOR_UNIMPORTED_HINTS: - d[shortname].add('typing.{}'.format(shortname)) + if f'typing.{shortname}' in TYPES_FOR_UNIMPORTED_HINTS: + d[shortname].add(f'typing.{shortname}') overlaps: Set[str] = set() for fullnames in d.values(): @@ -1993,20 +1993,20 @@ def [T <: int] f(self, x: int, y: T) -> None if s: s = ', ' + s s = definition_args[0] + s - s = '{}({})'.format(tp.definition.name, s) + s = f'{tp.definition.name}({s})' elif tp.name: first_arg = tp.def_extras.get('first_arg') if first_arg: if s: s = ', ' + s s = first_arg + s - s = '{}({})'.format(tp.name.split()[0], s) # skip "of Class" part + s = f'{tp.name.split()[0]}({s})' # skip "of Class" part else: - s = '({})'.format(s) + s = f'({s})' s += ' -> ' if tp.type_guard is not None: - s += 'TypeGuard[{}]'.format(format_type_bare(tp.type_guard)) + s += f'TypeGuard[{format_type_bare(tp.type_guard)}]' else: s += format_type_bare(tp.ret_type) @@ -2017,7 +2017,7 @@ def [T <: int] f(self, x: int, y: T) -> None upper_bound = get_proper_type(tvar.upper_bound) if (isinstance(upper_bound, Instance) and upper_bound.type.fullname != 'builtins.object'): - tvars.append('{} <: {}'.format(tvar.name, format_type_bare(upper_bound))) + tvars.append(f'{tvar.name} <: {format_type_bare(upper_bound)}') elif tvar.values: tvars.append('{} in ({})' .format(tvar.name, ', '.join([format_type_bare(tp) @@ -2028,7 +2028,7 @@ def [T <: int] f(self, x: int, y: T) -> None # For other TypeVarLikeTypes, just use the repr tvars.append(repr(tvar)) s = '[{}] {}'.format(', '.join(tvars), s) - return 'def {}'.format(s) + return f'def {s}' def variance_string(variance: int) -> str: @@ -2134,7 +2134,7 @@ def format_string_list(lst: List[str]) -> str: if len(lst) == 1: return lst[0] elif len(lst) <= 5: - return '%s and %s' % (', '.join(lst[:-1]), lst[-1]) + return '{} and {}'.format(', '.join(lst[:-1]), lst[-1]) else: return '%s, ... and %s (%i methods suppressed)' % ( ', '.join(lst[:2]), lst[-1], len(lst) - 3) @@ -2151,14 +2151,14 @@ def format_item_name_list(s: Iterable[str]) -> str: def callable_name(type: FunctionLike) -> Optional[str]: name = type.get_name() if name is not None and name[0] != '<': - return '"{}"'.format(name).replace(' of ', '" of "') + return f'"{name}"'.replace(' of ', '" of "') return name def for_function(callee: CallableType) -> str: name = callable_name(callee) if name is not None: - return ' for {}'.format(name) + return f' for {name}' return '' @@ -2194,7 +2194,7 @@ def pretty_seq(args: Sequence[str], conjunction: str) -> str: if len(quoted) == 1: return quoted[0] if len(quoted) == 2: - return "{} {} {}".format(quoted[0], conjunction, quoted[1]) + return f"{quoted[0]} {conjunction} {quoted[1]}" last_sep = ", " + conjunction + " " return ", ".join(quoted[:-1]) + last_sep + quoted[-1] @@ -2218,7 +2218,7 @@ def append_invariance_notes(notes: List[str], arg_type: Instance, 'which is covariant in the value type') if invariant_type and covariant_suggestion: notes.append( - '"{}" is invariant -- see '.format(invariant_type) + + f'"{invariant_type}" is invariant -- see ' + "https://mypy.readthedocs.io/en/stable/common_issues.html#variance") notes.append(covariant_suggestion) return notes @@ -2256,11 +2256,11 @@ def make_inferred_type_note(context: Context, def format_key_list(keys: List[str], *, short: bool = False) -> str: - formatted_keys = ['"{}"'.format(key) for key in keys] + formatted_keys = [f'"{key}"' for key in keys] td = '' if short else 'TypedDict ' if len(keys) == 0: - return 'no {}keys'.format(td) + return f'no {td}keys' elif len(keys) == 1: - return '{}key {}'.format(td, formatted_keys[0]) + return f'{td}key {formatted_keys[0]}' else: return '{}keys ({})'.format(td, ', '.join(formatted_keys)) diff --git a/mypy/metastore.py b/mypy/metastore.py index 3d4cdeff3400..29f1bbba2feb 100644 --- a/mypy/metastore.py +++ b/mypy/metastore.py @@ -95,7 +95,7 @@ def read(self, name: str) -> str: if not self.cache_dir_prefix: raise FileNotFoundError() - with open(os.path.join(self.cache_dir_prefix, name), 'r') as f: + with open(os.path.join(self.cache_dir_prefix, name)) as f: return f.read() def write(self, name: str, data: str, mtime: Optional[float] = None) -> bool: @@ -179,7 +179,7 @@ def _query(self, name: str, field: str) -> Any: if not self.db: raise FileNotFoundError() - cur = self.db.execute('SELECT {} FROM files WHERE path = ?'.format(field), (name,)) + cur = self.db.execute(f'SELECT {field} FROM files WHERE path = ?', (name,)) results = cur.fetchall() if not results: raise FileNotFoundError() diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 94d2dd34c16e..bee99156a570 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -25,14 +25,18 @@ from mypy.stubinfo import is_legacy_bundled_package from mypy import pyinfo + # Paths to be searched in find_module(). SearchPaths = NamedTuple( 'SearchPaths', - [('python_path', Tuple[str, ...]), # where user code is found - ('mypy_path', Tuple[str, ...]), # from $MYPYPATH or config variable - ('package_path', Tuple[str, ...]), # from get_site_packages_dirs() - ('typeshed_path', Tuple[str, ...]), # paths in typeshed - ]) + [ + ('python_path', Tuple[str, ...]), # where user code is found + ('mypy_path', Tuple[str, ...]), # from $MYPYPATH or config variable + ('package_path', Tuple[str, ...]), # from get_site_packages_dirs() + ('typeshed_path', Tuple[str, ...]), # paths in typeshed + ] +) + # Package dirs are a two-tuple of path to search and whether to verify the module OnePackageDir = Tuple[str, bool] @@ -113,7 +117,7 @@ def __init__(self, path: Optional[str], module: Optional[str], self.base_dir = base_dir # Directory where the package is rooted (e.g. 'xxx/yyy') def __repr__(self) -> str: - return 'BuildSource(path=%r, module=%r, has_text=%s, base_dir=%r)' % ( + return 'BuildSource(path={!r}, module={!r}, has_text={}, base_dir={!r})'.format( self.path, self.module, self.text is not None, @@ -526,7 +530,7 @@ def matches_exclude(subpath: str, for exclude in excludes: if re.search(exclude, subpath_str): if verbose: - print("TRACE: Excluding {} (matches pattern {})".format(subpath_str, exclude), + print(f"TRACE: Excluding {subpath_str} (matches pattern {exclude})", file=sys.stderr) return True return False @@ -538,7 +542,7 @@ def verify_module(fscache: FileSystemCache, id: str, path: str, prefix: str) -> path = os.path.dirname(path) for i in range(id.count('.')): path = os.path.dirname(path) - if not any(fscache.isfile_case(os.path.join(path, '__init__{}'.format(extension)), + if not any(fscache.isfile_case(os.path.join(path, f'__init__{extension}'), prefix) for extension in PYTHON_EXTENSIONS): return False @@ -552,7 +556,7 @@ def highest_init_level(fscache: FileSystemCache, id: str, path: str, prefix: str level = 0 for i in range(id.count('.')): path = os.path.dirname(path) - if any(fscache.isfile_case(os.path.join(path, '__init__{}'.format(extension)), + if any(fscache.isfile_case(os.path.join(path, f'__init__{extension}'), prefix) for extension in PYTHON_EXTENSIONS): level = i + 1 @@ -601,7 +605,7 @@ def default_lib_path(data_dir: str, path.append('/usr/local/lib/mypy') if not path: print("Could not resolve typeshed subdirectories. Your mypy install is broken.\n" - "Python executable is located at {0}.\nMypy located at {1}".format( + "Python executable is located at {}.\nMypy located at {}".format( sys.executable, data_dir), file=sys.stderr) sys.exit(1) return path @@ -672,7 +676,7 @@ def _parse_pth_file(dir: str, pth_filename: str) -> Iterator[str]: pth_file = os.path.join(dir, pth_filename) try: - f = open(pth_file, "r") + f = open(pth_file) except OSError: return with f: @@ -789,7 +793,7 @@ def compute_search_paths(sources: List[BuildSource], if (site_dir in mypypath or any(p.startswith(site_dir + os.path.sep) for p in mypypath) or os.path.altsep and any(p.startswith(site_dir + os.path.altsep) for p in mypypath)): - print("{} is in the MYPYPATH. Please remove it.".format(site_dir), file=sys.stderr) + print(f"{site_dir} is in the MYPYPATH. Please remove it.", file=sys.stderr) print("See https://mypy.readthedocs.io/en/stable/running_mypy.html" "#how-mypy-handles-imports for more info", file=sys.stderr) sys.exit(1) diff --git a/mypy/mro.py b/mypy/mro.py index 2aeb96e4e756..1bea83c6d97d 100644 --- a/mypy/mro.py +++ b/mypy/mro.py @@ -11,7 +11,7 @@ def calculate_mro(info: TypeInfo, obj_type: Optional[Callable[[], Instance]] = N Raise MroError if cannot determine mro. """ mro = linearize_hierarchy(info, obj_type) - assert mro, "Could not produce a MRO at all for %s" % (info,) + assert mro, f"Could not produce a MRO at all for {info}" info.mro = mro # The property of falling back to Any is inherited. info.fallback_to_any = any(baseinfo.fallback_to_any for baseinfo in info.mro) @@ -36,7 +36,7 @@ def linearize_hierarchy(info: TypeInfo, bases = [obj_type().type] lin_bases = [] for base in bases: - assert base is not None, "Cannot linearize bases for %s %s" % (info.fullname, bases) + assert base is not None, f"Cannot linearize bases for {info.fullname} {bases}" lin_bases.append(linearize_hierarchy(base, obj_type)) lin_bases.append(bases) return [info] + merge(lin_bases) diff --git a/mypy/nodes.py b/mypy/nodes.py index 5a27783e97e1..c0e53ea9367c 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -254,7 +254,7 @@ def deserialize(cls, data: JsonDict) -> 'SymbolNode': method = deserialize_map.get(classname) if method is not None: return method(data) - raise NotImplementedError('unexpected .class {}'.format(classname)) + raise NotImplementedError(f'unexpected .class {classname}') # Items: fullname, related symbol table node, surrounding type (if any) @@ -1660,7 +1660,7 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T: return visitor.visit_name_expr(self) def serialize(self) -> JsonDict: - assert False, "Serializing NameExpr: %s" % (self,) + assert False, f"Serializing NameExpr: {self}" class MemberExpr(RefExpr): @@ -2868,18 +2868,18 @@ def type_str(typ: 'mypy.types.Type') -> str: description = name + str_conv.format_id(self.names[name].node) node = self.names[name].node if isinstance(node, Var) and node.type: - description += ' ({})'.format(type_str(node.type)) + description += f' ({type_str(node.type)})' names.append(description) items = [ - 'Name({})'.format(self.fullname), + f'Name({self.fullname})', base, mro, ('Names', names), ] if self.declared_metaclass: - items.append('DeclaredMetaclass({})'.format(type_str(self.declared_metaclass))) + items.append(f'DeclaredMetaclass({type_str(self.declared_metaclass)})') if self.metaclass_type: - items.append('MetaclassType({})'.format(type_str(self.metaclass_type))) + items.append(f'MetaclassType({type_str(self.metaclass_type)})') return mypy.strconv.dump_tagged( items, head, @@ -3328,12 +3328,12 @@ def copy(self) -> 'SymbolTableNode': return new def __str__(self) -> str: - s = '{}/{}'.format(node_kinds[self.kind], short_type(self.node)) + s = f'{node_kinds[self.kind]}/{short_type(self.node)}' if isinstance(self.node, SymbolNode): - s += ' ({})'.format(self.node.fullname) + s += f' ({self.node.fullname})' # Include declared type of variables and functions. if self.type is not None: - s += ' : {}'.format(self.type) + s += f' : {self.type}' return s def serialize(self, prefix: str, name: str) -> JsonDict: @@ -3358,7 +3358,7 @@ def serialize(self, prefix: str, name: str) -> JsonDict: if isinstance(self.node, MypyFile): data['cross_ref'] = self.node.fullname else: - assert self.node is not None, '%s:%s' % (prefix, name) + assert self.node is not None, f'{prefix}:{name}' if prefix is not None: fullname = self.node.fullname if (fullname is not None and '.' in fullname @@ -3366,7 +3366,7 @@ def serialize(self, prefix: str, name: str) -> JsonDict: and not (isinstance(self.node, Var) and self.node.from_module_getattr)): assert not isinstance(self.node, PlaceholderNode), ( - 'Definition of {} is unexpectedly incomplete'.format(fullname) + f'Definition of {fullname} is unexpectedly incomplete' ) data['cross_ref'] = fullname return data @@ -3468,7 +3468,7 @@ def get_member_expr_fullname(expr: MemberExpr) -> Optional[str]: initial = get_member_expr_fullname(expr.expr) else: return None - return '{}.{}'.format(initial, expr.name) + return f'{initial}.{expr.name}' deserialize_map: Final = { @@ -3519,7 +3519,7 @@ def check_arg_names(names: Sequence[Optional[str]], nodes: List[T], fail: Callab seen_names: Set[Optional[str]] = set() for name, node in zip(names, nodes): if name is not None and name in seen_names: - fail('Duplicate argument "{}" in {}'.format(name, description), node) + fail(f'Duplicate argument "{name}" in {description}', node) break seen_names.add(name) diff --git a/mypy/operators.py b/mypy/operators.py index aa26cb2ec6e9..85cbfcb99528 100644 --- a/mypy/operators.py +++ b/mypy/operators.py @@ -51,7 +51,7 @@ ">>", } -inplace_operator_methods: Final = set("__i" + op_methods[op][2:] for op in ops_with_inplace_method) +inplace_operator_methods: Final = {"__i" + op_methods[op][2:] for op in ops_with_inplace_method} reverse_op_methods: Final = { '__add__': '__radd__', @@ -99,7 +99,7 @@ '__rshift__', } -normal_from_reverse_op: Final = dict((m, n) for n, m in reverse_op_methods.items()) +normal_from_reverse_op: Final = {m: n for n, m in reverse_op_methods.items()} reverse_op_method_set: Final = set(reverse_op_methods.values()) unary_op_methods: Final = { diff --git a/mypy/options.py b/mypy/options.py index 4cb2434958f4..b8bc53feb89c 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -325,7 +325,7 @@ def snapshot(self) -> object: return d def __repr__(self) -> str: - return 'Options({})'.format(pprint.pformat(self.snapshot())) + return f'Options({pprint.pformat(self.snapshot())})' def apply_changes(self, changes: Dict[str, object]) -> 'Options': new_options = Options() diff --git a/mypy/plugin.py b/mypy/plugin.py index dfa446521548..74a62d6510d2 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -175,11 +175,10 @@ def analyze_callable_args(self, arglist: TypeList) -> Optional[Tuple[List[Type], # A context for a hook that semantically analyzes an unbound type. -AnalyzeTypeContext = NamedTuple( - 'AnalyzeTypeContext', [ - ('type', UnboundType), # Type to analyze - ('context', Context), # Relevant location context (e.g. for error messages) - ('api', TypeAnalyzerPluginInterface)]) +class AnalyzeTypeContext(NamedTuple): + type: UnboundType # Type to analyze + context: Context # Relevant location context (e.g. for error messages) + api: TypeAnalyzerPluginInterface @mypyc_attr(allow_interpreted_subclasses=True) @@ -384,100 +383,96 @@ def is_stub_file(self) -> bool: # A context for querying for configuration data about a module for # cache invalidation purposes. -ReportConfigContext = NamedTuple( - 'ReportConfigContext', [ - ('id', str), # Module name - ('path', str), # Module file path - ('is_check', bool) # Is this invocation for checking whether the config matches - ]) +class ReportConfigContext(NamedTuple): + id: str # Module name + path: str # Module file path + is_check: bool # Is this invocation for checking whether the config matches + # A context for a function signature hook that infers a better signature for a # function. Note that argument types aren't available yet. If you need them, # you have to use a method hook instead. -FunctionSigContext = NamedTuple( - 'FunctionSigContext', [ - ('args', List[List[Expression]]), # Actual expressions for each formal argument - ('default_signature', CallableType), # Original signature of the method - ('context', Context), # Relevant location context (e.g. for error messages) - ('api', CheckerPluginInterface)]) +class FunctionSigContext(NamedTuple): + args: List[List[Expression]] # Actual expressions for each formal argument + default_signature: CallableType # Original signature of the method + context: Context # Relevant location context (e.g. for error messages) + api: CheckerPluginInterface + # A context for a function hook that infers the return type of a function with # a special signature. # # A no-op callback would just return the inferred return type, but a useful # callback at least sometimes can infer a more precise type. -FunctionContext = NamedTuple( - 'FunctionContext', [ - ('arg_types', List[List[Type]]), # List of actual caller types for each formal argument - ('arg_kinds', List[List[ArgKind]]), # Ditto for argument kinds, see nodes.ARG_* constants - # Names of formal parameters from the callee definition, - # these will be sufficient in most cases. - ('callee_arg_names', List[Optional[str]]), - # Names of actual arguments in the call expression. For example, - # in a situation like this: - # def func(**kwargs) -> None: - # pass - # func(kw1=1, kw2=2) - # callee_arg_names will be ['kwargs'] and arg_names will be [['kw1', 'kw2']]. - ('arg_names', List[List[Optional[str]]]), - ('default_return_type', Type), # Return type inferred from signature - ('args', List[List[Expression]]), # Actual expressions for each formal argument - ('context', Context), # Relevant location context (e.g. for error messages) - ('api', CheckerPluginInterface)]) +class FunctionContext(NamedTuple): + arg_types: List[List[Type]] # List of actual caller types for each formal argument + arg_kinds: List[List[ArgKind]] # Ditto for argument kinds, see nodes.ARG_* constants + # Names of formal parameters from the callee definition, + # these will be sufficient in most cases. + callee_arg_names: List[Optional[str]] + # Names of actual arguments in the call expression. For example, + # in a situation like this: + # def func(**kwargs) -> None: + # pass + # func(kw1=1, kw2=2) + # callee_arg_names will be ['kwargs'] and arg_names will be [['kw1', 'kw2']]. + arg_names: List[List[Optional[str]]] + default_return_type: Type # Return type inferred from signature + args: List[List[Expression]] # Actual expressions for each formal argument + context: Context # Relevant location context (e.g. for error messages) + api: CheckerPluginInterface + # A context for a method signature hook that infers a better signature for a # method. Note that argument types aren't available yet. If you need them, # you have to use a method hook instead. # TODO: document ProperType in the plugin changelog/update issue. -MethodSigContext = NamedTuple( - 'MethodSigContext', [ - ('type', ProperType), # Base object type for method call - ('args', List[List[Expression]]), # Actual expressions for each formal argument - ('default_signature', CallableType), # Original signature of the method - ('context', Context), # Relevant location context (e.g. for error messages) - ('api', CheckerPluginInterface)]) +class MethodSigContext(NamedTuple): + type: ProperType # Base object type for method call + args: List[List[Expression]] # Actual expressions for each formal argument + default_signature: CallableType # Original signature of the method + context: Context # Relevant location context (e.g. for error messages) + api: CheckerPluginInterface + # A context for a method hook that infers the return type of a method with a # special signature. # # This is very similar to FunctionContext (only differences are documented). -MethodContext = NamedTuple( - 'MethodContext', [ - ('type', ProperType), # Base object type for method call - ('arg_types', List[List[Type]]), # List of actual caller types for each formal argument - # see FunctionContext for details about names and kinds - ('arg_kinds', List[List[ArgKind]]), - ('callee_arg_names', List[Optional[str]]), - ('arg_names', List[List[Optional[str]]]), - ('default_return_type', Type), # Return type inferred by mypy - ('args', List[List[Expression]]), # Lists of actual expressions for every formal argument - ('context', Context), - ('api', CheckerPluginInterface)]) +class MethodContext(NamedTuple): + type: ProperType # Base object type for method call + arg_types: List[List[Type]] # List of actual caller types for each formal argument + # see FunctionContext for details about names and kinds + arg_kinds: List[List[ArgKind]] + callee_arg_names: List[Optional[str]] + arg_names: List[List[Optional[str]]] + default_return_type: Type # Return type inferred by mypy + args: List[List[Expression]] # Lists of actual expressions for every formal argument + context: Context + api: CheckerPluginInterface + # A context for an attribute type hook that infers the type of an attribute. -AttributeContext = NamedTuple( - 'AttributeContext', [ - ('type', ProperType), # Type of object with attribute - ('default_attr_type', Type), # Original attribute type - ('context', Context), # Relevant location context (e.g. for error messages) - ('api', CheckerPluginInterface)]) +class AttributeContext(NamedTuple): + type: ProperType # Type of object with attribute + default_attr_type: Type # Original attribute type + context: Context # Relevant location context (e.g. for error messages) + api: CheckerPluginInterface + # A context for a class hook that modifies the class definition. -ClassDefContext = NamedTuple( - 'ClassDefContext', [ - ('cls', ClassDef), # The class definition - ('reason', Expression), # The expression being applied (decorator, metaclass, base class) - ('api', SemanticAnalyzerPluginInterface) - ]) +class ClassDefContext(NamedTuple): + cls: ClassDef # The class definition + reason: Expression # The expression being applied (decorator, metaclass, base class) + api: SemanticAnalyzerPluginInterface + # A context for dynamic class definitions like # Base = declarative_base() -DynamicClassDefContext = NamedTuple( - 'DynamicClassDefContext', [ - ('call', CallExpr), # The r.h.s. of dynamic class definition - ('name', str), # The name this class is being assigned to - ('api', SemanticAnalyzerPluginInterface) - ]) +class DynamicClassDefContext(NamedTuple): + call: CallExpr # The r.h.s. of dynamic class definition + name: str # The name this class is being assigned to + api: SemanticAnalyzerPluginInterface @mypyc_attr(allow_interpreted_subclasses=True) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 0f6431d4700a..38fd2f040be5 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -251,7 +251,7 @@ def _get_decorator_optional_bool_argument( return False if attr_value.fullname == 'builtins.None': return None - ctx.api.fail('"{}" argument must be True or False.'.format(name), ctx.reason) + ctx.api.fail(f'"{name}" argument must be True or False.', ctx.reason) return default return default else: @@ -683,7 +683,7 @@ def _make_frozen(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute] # can modify it. var = Var(attribute.name, ctx.cls.info[attribute.name].type) var.info = ctx.cls.info - var._fullname = '%s.%s' % (ctx.cls.info.fullname, var.name) + var._fullname = f'{ctx.cls.info.fullname}.{var.name}' ctx.cls.info.names[var.name] = SymbolTableNode(MDEF, var) var.is_property = True diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index 90db614404bd..985a3f0fa6c7 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -39,7 +39,7 @@ def _get_bool_argument(ctx: ClassDefContext, expr: CallExpr, if attr_value: ret = ctx.api.parse_bool(attr_value) if ret is None: - ctx.api.fail('"{}" argument must be True or False.'.format(name), expr) + ctx.api.fail(f'"{name}" argument must be True or False.', expr) return default return ret return default diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index acbb0460ae07..a7fa2cfaa868 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -37,11 +37,11 @@ def get_method_signature_hook(self, fullname: str if fullname == 'typing.Mapping.get': return typed_dict_get_signature_callback - elif fullname in set(n + '.setdefault' for n in TPDICT_FB_NAMES): + elif fullname in {n + '.setdefault' for n in TPDICT_FB_NAMES}: return typed_dict_setdefault_signature_callback - elif fullname in set(n + '.pop' for n in TPDICT_FB_NAMES): + elif fullname in {n + '.pop' for n in TPDICT_FB_NAMES}: return typed_dict_pop_signature_callback - elif fullname in set(n + '.update' for n in TPDICT_FB_NAMES): + elif fullname in {n + '.update' for n in TPDICT_FB_NAMES}: return typed_dict_update_signature_callback elif fullname == 'ctypes.Array.__setitem__': return ctypes.array_setitem_callback @@ -61,11 +61,11 @@ def get_method_hook(self, fullname: str return int_neg_callback elif fullname in ('builtins.tuple.__mul__', 'builtins.tuple.__rmul__'): return tuple_mul_callback - elif fullname in set(n + '.setdefault' for n in TPDICT_FB_NAMES): + elif fullname in {n + '.setdefault' for n in TPDICT_FB_NAMES}: return typed_dict_setdefault_callback - elif fullname in set(n + '.pop' for n in TPDICT_FB_NAMES): + elif fullname in {n + '.pop' for n in TPDICT_FB_NAMES}: return typed_dict_pop_callback - elif fullname in set(n + '.__delitem__' for n in TPDICT_FB_NAMES): + elif fullname in {n + '.__delitem__' for n in TPDICT_FB_NAMES}: return typed_dict_delitem_callback elif fullname == 'ctypes.Array.__getitem__': return ctypes.array_getitem_callback diff --git a/mypy/plugins/enums.py b/mypy/plugins/enums.py index ea9a02f5b41e..afd59bf0374d 100644 --- a/mypy/plugins/enums.py +++ b/mypy/plugins/enums.py @@ -20,11 +20,11 @@ from mypy.subtypes import is_equivalent from mypy.semanal_enum import ENUM_BASES -ENUM_NAME_ACCESS: Final = {"{}.name".format(prefix) for prefix in ENUM_BASES} | { - "{}._name_".format(prefix) for prefix in ENUM_BASES +ENUM_NAME_ACCESS: Final = {f"{prefix}.name" for prefix in ENUM_BASES} | { + f"{prefix}._name_" for prefix in ENUM_BASES } -ENUM_VALUE_ACCESS: Final = {"{}.value".format(prefix) for prefix in ENUM_BASES} | { - "{}._value_".format(prefix) for prefix in ENUM_BASES +ENUM_VALUE_ACCESS: Final = {f"{prefix}.value" for prefix in ENUM_BASES} | { + f"{prefix}._value_" for prefix in ENUM_BASES } @@ -75,8 +75,8 @@ def _infer_value_type_with_auto_fallback( """ if proper_type is None: return None - if not ((isinstance(proper_type, Instance) and - proper_type.type.fullname == 'enum.auto')): + if not (isinstance(proper_type, Instance) and + proper_type.type.fullname == 'enum.auto'): return proper_type assert isinstance(ctx.type, Instance), 'An incorrect ctx.type was passed.' info = ctx.type.type diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index e52d478927e8..9e4d24283049 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -20,7 +20,9 @@ } -_MethodInfo = NamedTuple('_MethodInfo', [('is_static', bool), ('type', CallableType)]) +class _MethodInfo(NamedTuple): + is_static: bool + type: CallableType def functools_total_ordering_maker_callback(ctx: mypy.plugin.ClassDefContext, diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index f4b3d7c19425..0eb855d3a315 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -12,21 +12,22 @@ from typing import List, NamedTuple, Optional, Sequence, TypeVar, Union from typing_extensions import Final -SingledispatchTypeVars = NamedTuple('SingledispatchTypeVars', [ - ('return_type', Type), - ('fallback', CallableType), -]) -RegisterCallableInfo = NamedTuple('RegisterCallableInfo', [ - ('register_type', Type), - ('singledispatch_obj', Instance), -]) +class SingledispatchTypeVars(NamedTuple): + return_type: Type + fallback: CallableType + + +class RegisterCallableInfo(NamedTuple): + register_type: Type + singledispatch_obj: Instance + SINGLEDISPATCH_TYPE: Final = 'functools._SingleDispatchCallable' -SINGLEDISPATCH_REGISTER_METHOD: Final = '{}.register'.format(SINGLEDISPATCH_TYPE) +SINGLEDISPATCH_REGISTER_METHOD: Final = f'{SINGLEDISPATCH_TYPE}.register' -SINGLEDISPATCH_CALLABLE_CALL_METHOD: Final = '{}.__call__'.format(SINGLEDISPATCH_TYPE) +SINGLEDISPATCH_CALLABLE_CALL_METHOD: Final = f'{SINGLEDISPATCH_TYPE}.__call__' def get_singledispatch_info(typ: Instance) -> Optional[SingledispatchTypeVars]: @@ -55,7 +56,7 @@ def get_first_arg(args: List[List[T]]) -> Optional[T]: def make_fake_register_class_instance(api: CheckerPluginInterface, type_args: Sequence[Type] ) -> Instance: defn = ClassDef(REGISTER_RETURN_CLASS, Block([])) - defn.fullname = 'functools.{}'.format(REGISTER_RETURN_CLASS) + defn.fullname = f'functools.{REGISTER_RETURN_CLASS}' info = TypeInfo(SymbolTable(), defn, "functools") obj_type = api.named_generic_type('builtins.object', []).type info.bases = [Instance(obj_type, [])] diff --git a/mypy/report.py b/mypy/report.py index 23b5c78ef929..8d3465f3c1f0 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -131,8 +131,7 @@ def should_skip_path(path: str) -> bool: def iterate_python_lines(path: str) -> Iterator[Tuple[int, str]]: """Return an iterator over (line number, line text) from a Python file.""" with tokenize.open(path) as input_file: - for line_info in enumerate(input_file, 1): - yield line_info + yield from enumerate(input_file, 1) class FuncCounterVisitor(TraverserVisitor): @@ -262,10 +261,10 @@ def _report_any_exprs(self) -> None: for filename in sorted(self.counts): (num_any, num_total) = self.counts[filename] coverage = (float(num_total - num_any) / float(num_total)) * 100 - coverage_str = '{:.2f}%'.format(coverage) + coverage_str = f'{coverage:.2f}%' rows.append([filename, str(num_any), str(num_total), coverage_str]) rows.sort(key=lambda x: x[0]) - total_row = ["Total", str(total_any), str(total_expr), '{:.2f}%'.format(total_coverage)] + total_row = ["Total", str(total_any), str(total_expr), f'{total_coverage:.2f}%'] self._write_out_report('any-exprs.txt', column_names, rows, total_row) def _report_types_of_anys(self) -> None: @@ -506,7 +505,7 @@ def _get_any_info_for_line(visitor: stats.StatisticsVisitor, lineno: int) -> str for typ in visitor.any_line_map[lineno]: counter[typ.type_of_any] += 1 for any_type, occurrences in counter.items(): - result += "\n{} (x{})".format(type_of_any_name_map[any_type], occurrences) + result += f"\n{type_of_any_name_map[any_type]} (x{occurrences})" return result else: return "No Anys on this line!" @@ -541,10 +540,10 @@ def get_line_rate(covered_lines: int, total_lines: int) -> str: if total_lines == 0: return str(1.0) else: - return '{:.4f}'.format(covered_lines / total_lines) + return f'{covered_lines / total_lines:.4f}' -class CoberturaPackage(object): +class CoberturaPackage: """Container for XML and statistics mapping python modules to Cobertura package.""" def __init__(self, name: str) -> None: diff --git a/mypy/semanal.py b/mypy/semanal.py index 8b0148958984..eb0411f0afc8 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -460,7 +460,7 @@ def add_implicit_module_attrs(self, file_node: MypyFile) -> None: assert isinstance(node, TypeInfo) typ = Instance(node, [self.str_type(), AnyType(TypeOfAny.special_form)]) else: - assert t is not None, 'type should be specified for {}'.format(name) + assert t is not None, f'type should be specified for {name}' typ = UnboundType(t) existing = file_node.names.get(name) @@ -924,7 +924,7 @@ def process_static_or_class_method_in_overload(self, defn: OverloadedFuncDef) -> elif isinstance(item, FuncDef): inner = item else: - assert False, "The 'item' variable is an unexpected type: {}".format(type(item)) + assert False, f"The 'item' variable is an unexpected type: {type(item)}" class_status.append(inner.is_class) static_status.append(inner.is_static) @@ -934,7 +934,7 @@ def process_static_or_class_method_in_overload(self, defn: OverloadedFuncDef) -> elif isinstance(defn.impl, FuncDef): inner = defn.impl else: - assert False, "Unexpected impl type: {}".format(type(defn.impl)) + assert False, f"Unexpected impl type: {type(defn.impl)}" class_status.append(inner.is_class) static_status.append(inner.is_static) @@ -969,7 +969,7 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - self.fail("Decorated property not supported", item) item.func.accept(self) else: - self.fail('Unexpected definition for property "{}"'.format(first_item.func.name), + self.fail(f'Unexpected definition for property "{first_item.func.name}"', item) deleted_items.append(i + 1) for i in reversed(deleted_items): @@ -1536,7 +1536,7 @@ def analyze_base_classes( else: msg = 'Invalid base class' if name: - msg += ' "{}"'.format(name) + msg += f' "{name}"' self.fail(msg, base_expr) is_error = True continue @@ -1570,7 +1570,7 @@ def configure_base_classes(self, elif isinstance(base, AnyType): if self.options.disallow_subclassing_any: if isinstance(base_expr, (NameExpr, MemberExpr)): - msg = 'Class cannot subclass "{}" (has type "Any")'.format(base_expr.name) + msg = f'Class cannot subclass "{base_expr.name}" (has type "Any")' else: msg = 'Class cannot subclass value of type "Any"' self.fail(msg, base_expr) @@ -1579,12 +1579,12 @@ def configure_base_classes(self, msg = 'Invalid base class' name = self.get_name_repr_of_expr(base_expr) if name: - msg += ' "{}"'.format(name) + msg += f' "{name}"' self.fail(msg, base_expr) info.fallback_to_any = True if self.options.disallow_any_unimported and has_any_from_unimported_type(base): if isinstance(base_expr, (NameExpr, MemberExpr)): - prefix = "Base type {}".format(base_expr.name) + prefix = f"Base type {base_expr.name}" else: prefix = "Base type" self.msg.unimported_type_becomes_any(prefix, base, base_expr) @@ -1942,7 +1942,7 @@ def report_missing_module_attribute( imported_id, context, module_public=module_public, module_hidden=module_hidden ) return - message = 'Module "{}" has no attribute "{}"'.format(import_id, source_id) + message = f'Module "{import_id}" has no attribute "{source_id}"' # Suggest alternatives, if any match is found. module = self.modules.get(import_id) if module: @@ -1954,7 +1954,7 @@ def report_missing_module_attribute( matches = best_matches(source_id, alternatives)[:3] if matches: suggestion = "; maybe {}?".format(pretty_seq(matches, "or")) - message += "{}".format(suggestion) + message += f"{suggestion}" self.fail(message, context, code=codes.ATTR_DEFINED) self.add_unknown_imported_symbol( imported_id, context, target_name=None, module_public=module_public, @@ -1963,7 +1963,7 @@ def report_missing_module_attribute( if import_id == 'typing': # The user probably has a missing definition in a test fixture. Let's verify. - fullname = 'builtins.{}'.format(source_id.lower()) + fullname = f'builtins.{source_id.lower()}' if (self.lookup_fully_qualified_or_none(fullname) is None and fullname in SUGGESTED_TEST_FIXTURES): # Yes. Generate a helpful note. @@ -3126,7 +3126,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: if self.options.disallow_any_unimported: for idx, constraint in enumerate(values, start=1): if has_any_from_unimported_type(constraint): - prefix = "Constraint {}".format(idx) + prefix = f"Constraint {idx}" self.msg.unimported_type_becomes_any(prefix, constraint, s) if has_any_from_unimported_type(upper_bound): @@ -3167,11 +3167,11 @@ def check_typevarlike_name(self, call: CallExpr, name: str, context: Context) -> call.callee.name if isinstance(call.callee, NameExpr) else call.callee.fullname ) if len(call.args) < 1: - self.fail("Too few arguments for {}()".format(typevarlike_type), context) + self.fail(f"Too few arguments for {typevarlike_type}()", context) return False if (not isinstance(call.args[0], (StrExpr, BytesExpr, UnicodeExpr)) or not call.arg_kinds[0] == ARG_POS): - self.fail("{}() expects a string literal as first argument".format(typevarlike_type), + self.fail(f"{typevarlike_type}() expects a string literal as first argument", context) return False elif call.args[0].value != name: @@ -3792,7 +3792,7 @@ def visit_global_decl(self, g: GlobalDecl) -> None: self.statement = g for name in g.names: if name in self.nonlocal_decls[-1]: - self.fail('Name "{}" is nonlocal and global'.format(name), g) + self.fail(f'Name "{name}" is nonlocal and global', g) self.global_decls[-1].add(name) def visit_nonlocal_decl(self, d: NonlocalDecl) -> None: @@ -3805,14 +3805,14 @@ def visit_nonlocal_decl(self, d: NonlocalDecl) -> None: if table is not None and name in table: break else: - self.fail('No binding for nonlocal "{}" found'.format(name), d) + self.fail(f'No binding for nonlocal "{name}" found', d) if self.locals[-1] is not None and name in self.locals[-1]: self.fail('Name "{}" is already defined in local ' 'scope before nonlocal declaration'.format(name), d) if name in self.global_decls[-1]: - self.fail('Name "{}" is nonlocal and global'.format(name), d) + self.fail(f'Name "{name}" is nonlocal and global', d) self.nonlocal_decls[-1].add(name) def visit_print_stmt(self, s: PrintStmt) -> None: @@ -4880,9 +4880,9 @@ def add_redefinition(self, symbol.no_serialize = True while True: if i == 1: - new_name = '{}-redefinition'.format(name) + new_name = f'{name}-redefinition' else: - new_name = '{}-redefinition{}'.format(name, i) + new_name = f'{name}-redefinition{i}' existing = names.get(new_name) if existing is None: names[new_name] = symbol @@ -5109,7 +5109,7 @@ def process_placeholder(self, name: str, kind: str, ctx: Context) -> None: self.defer(ctx) def cannot_resolve_name(self, name: str, kind: str, ctx: Context) -> None: - self.fail('Cannot resolve {} "{}" (possible cyclic definition)'.format(kind, name), ctx) + self.fail(f'Cannot resolve {kind} "{name}" (possible cyclic definition)', ctx) def qualified_name(self, name: str) -> str: if self.type is not None: @@ -5217,12 +5217,12 @@ def name_not_defined(self, name: str, ctx: Context, namespace: Optional[str] = N # later on. Defer current target. self.record_incomplete_ref() return - message = 'Name "{}" is not defined'.format(name) + message = f'Name "{name}" is not defined' self.fail(message, ctx, code=codes.NAME_DEFINED) - if 'builtins.{}'.format(name) in SUGGESTED_TEST_FIXTURES: + if f'builtins.{name}' in SUGGESTED_TEST_FIXTURES: # The user probably has a missing definition in a test fixture. Let's verify. - fullname = 'builtins.{}'.format(name) + fullname = f'builtins.{name}' if self.lookup_fully_qualified_or_none(fullname) is None: # Yes. Generate a helpful note. self.msg.add_fixture_note(fullname, ctx) @@ -5236,7 +5236,7 @@ def name_not_defined(self, name: str, ctx: Context, namespace: Optional[str] = N for name in TYPES_FOR_UNIMPORTED_HINTS } for module in modules_with_unimported_hints: - fullname = '{}.{}'.format(module, name).lower() + fullname = f'{module}.{name}'.lower() if fullname not in lowercased: continue # User probably forgot to import these types. @@ -5266,10 +5266,10 @@ def already_defined(self, elif node and node.line != -1 and self.is_local_name(node.fullname): # TODO: Using previous symbol node may give wrong line. We should use # the line number where the binding was established instead. - extra_msg = ' on line {}'.format(node.line) + extra_msg = f' on line {node.line}' else: extra_msg = ' (possibly by an import)' - self.fail('{} "{}" already defined{}'.format(noun, unmangle(name), extra_msg), ctx, + self.fail(f'{noun} "{unmangle(name)}" already defined{extra_msg}', ctx, code=codes.NO_REDEF) def name_already_defined(self, @@ -5457,7 +5457,7 @@ def schedule_patch(self, priority: int, patch: Callable[[], None]) -> None: def report_hang(self) -> None: print('Deferral trace:') for mod, line in self.deferral_debug_context: - print(' {}:{}'.format(mod, line)) + print(f' {mod}:{line}') self.errors.report(-1, -1, 'INTERNAL ERROR: maximum semantic analysis iteration count reached', blocker=True) diff --git a/mypy/semanal_classprop.py b/mypy/semanal_classprop.py index 4be57b64342e..e985b55a20d1 100644 --- a/mypy/semanal_classprop.py +++ b/mypy/semanal_classprop.py @@ -105,14 +105,14 @@ def calculate_class_abstract_status(typ: TypeInfo, is_stub_file: bool, errors: E def report(message: str, severity: str) -> None: errors.report(typ.line, typ.column, message, severity=severity) - attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract)) - report("Class {} has abstract attributes {}".format(typ.fullname, attrs), 'error') + attrs = ", ".join(f'"{attr}"' for attr in sorted(abstract)) + report(f"Class {typ.fullname} has abstract attributes {attrs}", 'error') report("If it is meant to be abstract, add 'abc.ABCMeta' as an explicit metaclass", 'note') if typ.is_final and abstract: - attrs = ", ".join('"{}"'.format(attr) for attr in sorted(abstract)) + attrs = ", ".join(f'"{attr}"' for attr in sorted(abstract)) errors.report(typ.line, typ.column, - "Final class {} has abstract attributes {}".format(typ.fullname, attrs)) + f"Final class {typ.fullname} has abstract attributes {attrs}") def check_protocol_status(info: TypeInfo, errors: Errors) -> None: diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 8b5ecf1b1df4..251767bef3e0 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -106,7 +106,7 @@ def build_enum_call_typeinfo(self, name: str, items: List[str], fullname: str, var = Var(item) var.info = info var.is_property = True - var._fullname = '{}.{}'.format(info.fullname, item) + var._fullname = f'{info.fullname}.{item}' info.names[item] = SymbolTableNode(MDEF, var) return info @@ -127,7 +127,7 @@ def parse_enum_call_args(self, call: CallExpr, valid_name = [None, 'value', 'names', 'module', 'qualname', 'type', 'start'] for arg_name in call.arg_names: if arg_name not in valid_name: - self.fail_enum_call_arg('Unexpected keyword argument "{}"'.format(arg_name), call) + self.fail_enum_call_arg(f'Unexpected keyword argument "{arg_name}"', call) value, names = None, None for arg_name, arg in zip(call.arg_names, args): if arg_name == 'value': diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 2357225caebb..4e05dfb99605 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -266,7 +266,7 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str # TODO: Share code with check_argument_count in checkexpr.py? args = call.args if len(args) < 2: - self.fail('Too few arguments for "{}()"'.format(type_name), call) + self.fail(f'Too few arguments for "{type_name}()"', call) return None defaults: List[Expression] = [] if len(args) > 2: @@ -289,11 +289,11 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str ) break if call.arg_kinds[:2] != [ARG_POS, ARG_POS]: - self.fail('Unexpected arguments to "{}()"'.format(type_name), call) + self.fail(f'Unexpected arguments to "{type_name}()"', call) return None if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): self.fail( - '"{}()" expects a string literal as the first argument'.format(type_name), call) + f'"{type_name}()" expects a string literal as the first argument', call) return None typename = cast(Union[StrExpr, BytesExpr, UnicodeExpr], call.args[0]).value types: List[Type] = [] @@ -333,10 +333,10 @@ def parse_namedtuple_args(self, call: CallExpr, fullname: str types = [AnyType(TypeOfAny.unannotated) for _ in items] underscore = [item for item in items if item.startswith('_')] if underscore: - self.fail('"{}()" field names cannot start with an underscore: '.format(type_name) + self.fail(f'"{type_name}()" field names cannot start with an underscore: ' + ', '.join(underscore), call) if len(defaults) > len(items): - self.fail('Too many defaults given in call to "{}()"'.format(type_name), call) + self.fail(f'Too many defaults given in call to "{type_name}()"', call) defaults = defaults[:len(items)] return items, types, defaults, typename, True @@ -420,7 +420,7 @@ def add_field(var: Var, is_initialized_in_class: bool = False, var.info = info var.is_initialized_in_class = is_initialized_in_class var.is_property = is_property - var._fullname = '%s.%s' % (info.fullname, var.name) + var._fullname = f'{info.fullname}.{var.name}' info.names[var.name] = SymbolTableNode(MDEF, var) fields = [Var(item, typ) for item, typ in zip(items, types)] @@ -528,7 +528,7 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]: continue ctx = named_tuple_info.names[prohibited].node assert ctx is not None - self.fail('Cannot overwrite NamedTuple attribute "{}"'.format(prohibited), + self.fail(f'Cannot overwrite NamedTuple attribute "{prohibited}"', ctx) # Restore the names in the original symbol table. This ensures that the symbol diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 72a89150bb64..6d6c4ac9f0d4 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -189,7 +189,7 @@ def set_callable_name(sig: Type, fdef: FuncDef) -> ProperType: else: class_name = fdef.info.name return sig.with_name( - '{} of {}'.format(fdef.name, class_name)) + f'{fdef.name} of {class_name}') else: return sig.with_name(fdef.name) else: diff --git a/mypy/semanal_typeargs.py b/mypy/semanal_typeargs.py index f8e14d28661a..483154000d1b 100644 --- a/mypy/semanal_typeargs.py +++ b/mypy/semanal_typeargs.py @@ -121,8 +121,8 @@ def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: s message_registry.INVALID_TYPEVAR_ARG_VALUE.format(type.name), context, code=codes.TYPE_VAR) else: - class_name = '"{}"'.format(type.name) - actual_type_name = '"{}"'.format(actual.type.name) + class_name = f'"{type.name}"' + actual_type_name = f'"{actual.type.name}"' self.fail( message_registry.INCOMPATIBLE_TYPEVAR_VALUE.format( arg_name, class_name, actual_type_name), diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index ffc6a7df3931..3b384bdec1a3 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -156,7 +156,7 @@ def analyze_typeddict_classdef_fields( self.fail('Overwriting TypedDict field "{}" while extending' .format(name), stmt) if name in fields: - self.fail('Duplicate TypedDict key "{}"'.format(name), stmt) + self.fail(f'Duplicate TypedDict key "{name}"', stmt) continue # Append name and type in this case... fields.append(name) @@ -282,7 +282,7 @@ def parse_typeddict_args( return self.fail_typeddict_arg("Unexpected arguments to TypedDict()", call) if len(args) == 3 and call.arg_names[2] != 'total': return self.fail_typeddict_arg( - 'Unexpected keyword argument "{}" for "TypedDict"'.format(call.arg_names[2]), call) + f'Unexpected keyword argument "{call.arg_names[2]}" for "TypedDict"', call) if not isinstance(args[0], (StrExpr, BytesExpr, UnicodeExpr)): return self.fail_typeddict_arg( "TypedDict() expects a string literal as the first argument", call) @@ -328,7 +328,7 @@ def parse_typeddict_fields_with_types( key = field_name_expr.value items.append(key) if key in seen_keys: - self.fail('Duplicate TypedDict key "{}"'.format(key), field_name_expr) + self.fail(f'Duplicate TypedDict key "{key}"', field_name_expr) seen_keys.add(key) else: name_context = field_name_expr or field_type_expr diff --git a/mypy/server/astdiff.py b/mypy/server/astdiff.py index f41a54752fee..1f3b68fbde1b 100644 --- a/mypy/server/astdiff.py +++ b/mypy/server/astdiff.py @@ -89,8 +89,8 @@ def compare_symbol_table_snapshots( Return a set of fully-qualified names (e.g., 'mod.func' or 'mod.Class.method'). """ # Find names only defined only in one version. - names1 = {'%s.%s' % (name_prefix, name) for name in snapshot1} - names2 = {'%s.%s' % (name_prefix, name) for name in snapshot2} + names1 = {f'{name_prefix}.{name}' for name in snapshot1} + names2 = {f'{name_prefix}.{name}' for name in snapshot2} triggers = names1 ^ names2 # Look for names defined in both versions that are different. @@ -99,7 +99,7 @@ def compare_symbol_table_snapshots( item2 = snapshot2[name] kind1 = item1[0] kind2 = item2[0] - item_name = '%s.%s' % (name_prefix, name) + item_name = f'{name_prefix}.{name}' if kind1 != kind2: # Different kind of node in two snapshots -> trivially different. triggers.add(item_name) @@ -338,7 +338,7 @@ def visit_callable_type(self, typ: CallableType) -> SnapshotItem: return ('CallableType', snapshot_types(typ.arg_types), snapshot_type(typ.ret_type), - tuple([encode_optional_str(name) for name in typ.arg_names]), + tuple(encode_optional_str(name) for name in typ.arg_names), tuple(typ.arg_kinds), typ.is_type_obj(), typ.is_ellipsis_args) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index c7623ff26c7f..b70a43db2540 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -361,20 +361,20 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None: elif isinstance(rvalue, CallExpr) and isinstance(rvalue.analyzed, NamedTupleExpr): # Depend on types of named tuple items. info = rvalue.analyzed.info - prefix = '%s.%s' % (self.scope.current_full_target(), info.name) + prefix = f'{self.scope.current_full_target()}.{info.name}' for name, symnode in info.names.items(): if not name.startswith('_') and isinstance(symnode.node, Var): typ = symnode.node.type if typ: self.add_type_dependencies(typ) self.add_type_dependencies(typ, target=make_trigger(prefix)) - attr_target = make_trigger('%s.%s' % (prefix, name)) + attr_target = make_trigger(f'{prefix}.{name}') self.add_type_dependencies(typ, target=attr_target) elif isinstance(rvalue, CallExpr) and isinstance(rvalue.analyzed, TypedDictExpr): # Depend on the underlying typeddict type info = rvalue.analyzed.info assert info.typeddict_type is not None - prefix = '%s.%s' % (self.scope.current_full_target(), info.name) + prefix = f'{self.scope.current_full_target()}.{info.name}' self.add_type_dependencies(info.typeddict_type, target=make_trigger(prefix)) elif isinstance(rvalue, CallExpr) and isinstance(rvalue.analyzed, EnumCallExpr): # Enum values are currently not checked, but for future we add the deps on them @@ -440,7 +440,7 @@ def process_lvalue(self, lvalue: Expression) -> None: # global variable. lvalue_type = self.get_non_partial_lvalue_type(lvalue) type_triggers = self.get_type_triggers(lvalue_type) - attr_trigger = make_trigger('%s.%s' % (self.scope.current_full_target(), + attr_trigger = make_trigger('{}.{}'.format(self.scope.current_full_target(), lvalue.name)) for type_trigger in type_triggers: self.add_dependency(type_trigger, attr_trigger) @@ -827,10 +827,10 @@ def attribute_triggers(self, typ: Type, name: str) -> List[str]: if isinstance(typ, TupleType): typ = typ.partial_fallback if isinstance(typ, Instance): - member = '%s.%s' % (typ.type.fullname, name) + member = f'{typ.type.fullname}.{name}' return [make_trigger(member)] elif isinstance(typ, FunctionLike) and typ.is_type_obj(): - member = '%s.%s' % (typ.type_object().fullname, name) + member = f'{typ.type_object().fullname}.{name}' triggers = [make_trigger(member)] triggers.extend(self.attribute_triggers(typ.fallback, name)) return triggers diff --git a/mypy/server/mergecheck.py b/mypy/server/mergecheck.py index 476d1cc809f7..2c28242251e9 100644 --- a/mypy/server/mergecheck.py +++ b/mypy/server/mergecheck.py @@ -50,7 +50,8 @@ def check_consistency(o: object) -> None: path2 = get_path(sym2, seen, parents) if fn in m: - print('\nDuplicate %r nodes with fullname %r found:' % (type(sym).__name__, fn)) + print('\nDuplicate {!r} nodes with fullname {!r} found:'.format( + type(sym).__name__, fn)) print('[1] %d: %s' % (id(sym1), path_to_str(path1))) print('[2] %d: %s' % (id(sym2), path_to_str(path2))) @@ -72,11 +73,11 @@ def path_to_str(path: List[Tuple[object, object]]) -> str: result += '[%s]' % repr(attr) else: if isinstance(obj, Var): - result += '.%s(%s:%s)' % (attr, t, obj.name) + result += f'.{attr}({t}:{obj.name})' elif t in ('BuildManager', 'FineGrainedBuildManager'): # Omit class name for some classes that aren't part of a class # hierarchy since there isn't much ambiguity. result += '.%s' % attr else: - result += '.%s(%s)' % (attr, t) + result += f'.{attr}({t})' return result diff --git a/mypy/server/objgraph.py b/mypy/server/objgraph.py index 89f0ba79388b..236f70d04e38 100644 --- a/mypy/server/objgraph.py +++ b/mypy/server/objgraph.py @@ -69,8 +69,7 @@ def get_edge_candidates(o: object) -> Iterator[Tuple[object, object]]: except AssertionError: pass if isinstance(o, Mapping): - for k, v in o.items(): - yield k, v + yield from o.items() elif isinstance(o, Iterable) and not isinstance(o, str): for i, e in enumerate(o): yield i, e diff --git a/mypy/server/trigger.py b/mypy/server/trigger.py index c10264766ae6..3b9b02d20c81 100644 --- a/mypy/server/trigger.py +++ b/mypy/server/trigger.py @@ -21,4 +21,4 @@ def make_wildcard_trigger(module: str) -> str: This is used for "from m import *" dependencies. """ - return '<%s%s>' % (module, WILDCARD_TAG) + return f'<{module}{WILDCARD_TAG}>' diff --git a/mypy/server/update.py b/mypy/server/update.py index 2f57001766d5..233e989a0e36 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -493,17 +493,21 @@ def ensure_trees_loaded(manager: BuildManager, graph: Dict[str, State], # - Remaining changed modules that are not processed yet as (module id, path) # tuples (non-empty if the original changed module imported other new # modules) -NormalUpdate = NamedTuple('NormalUpdate', [('module', str), - ('path', str), - ('remaining', List[Tuple[str, str]]), - ('tree', Optional[MypyFile])]) +class NormalUpdate(NamedTuple): + module: str + path: str + remaining: List[Tuple[str, str]] + tree: Optional[MypyFile] + # The result of update_module_isolated when there is a blocking error. Items # are similar to NormalUpdate (but there are fewer). -BlockedUpdate = NamedTuple('BlockedUpdate', [('module', str), - ('path', str), - ('remaining', List[Tuple[str, str]]), - ('messages', List[str])]) +class BlockedUpdate(NamedTuple): + module: str + path: str + remaining: List[Tuple[str, str]] + messages: List[str] + UpdateResult = Union[NormalUpdate, BlockedUpdate] @@ -1128,9 +1132,9 @@ def target_from_node(module: str, return module else: # OverloadedFuncDef or FuncDef if node.info: - return '%s.%s' % (node.info.fullname, node.name) + return f'{node.info.fullname}.{node.name}' else: - return '%s.%s' % (module, node.name) + return f'{module}.{node.name}' if sys.platform != "win32": diff --git a/mypy/strconv.py b/mypy/strconv.py index 1a08423b4164..13f3e04ef712 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -39,7 +39,7 @@ def get_id(self, o: object) -> Optional[int]: def format_id(self, o: object) -> str: if self.id_mapper: - return '<{}>'.format(self.get_id(o)) + return f'<{self.get_id(o)}>' else: return '' @@ -53,7 +53,7 @@ def dump(self, nodes: Sequence[object], obj: 'mypy.nodes.Context') -> str: tag = short_type(obj) + ':' + str(obj.get_line()) if self.show_ids: assert self.id_mapper is not None - tag += '<{}>'.format(self.get_id(obj)) + tag += f'<{self.get_id(obj)}>' return dump_tagged(nodes, tag, self) def func_helper(self, o: 'mypy.nodes.FuncItem') -> List[object]: @@ -109,7 +109,7 @@ def visit_import(self, o: 'mypy.nodes.Import') -> str: a = [] for id, as_id in o.ids: if as_id is not None: - a.append('{} : {}'.format(id, as_id)) + a.append(f'{id} : {as_id}') else: a.append(id) return 'Import:{}({})'.format(o.line, ', '.join(a)) @@ -118,7 +118,7 @@ def visit_import_from(self, o: 'mypy.nodes.ImportFrom') -> str: a = [] for name, as_name in o.names: if as_name is not None: - a.append('{} : {}'.format(name, as_name)) + a.append(f'{name} : {as_name}') else: a.append(name) return 'ImportFrom:{}({}, [{}])'.format(o.line, "." * o.relative + o.id, ', '.join(a)) @@ -133,7 +133,7 @@ def visit_func_def(self, o: 'mypy.nodes.FuncDef') -> str: a.insert(0, o.name) arg_kinds = {arg.kind for arg in o.arguments} if len(arg_kinds & {mypy.nodes.ARG_NAMED, mypy.nodes.ARG_NAMED_OPT}) > 0: - a.insert(1, 'MaxPos({})'.format(o.max_pos)) + a.insert(1, f'MaxPos({o.max_pos})') if o.is_abstract: a.insert(-1, 'Abstract') if o.is_static: @@ -170,11 +170,11 @@ def visit_class_def(self, o: 'mypy.nodes.ClassDef') -> str: if o.type_vars: a.insert(1, ('TypeVars', o.type_vars)) if o.metaclass: - a.insert(1, 'Metaclass({})'.format(o.metaclass)) + a.insert(1, f'Metaclass({o.metaclass})') if o.decorators: a.insert(1, ('Decorators', o.decorators)) if o.info and o.info._promote: - a.insert(1, 'Promote({})'.format(o.info._promote)) + a.insert(1, f'Promote({o.info._promote})') if o.info and o.info.tuple_type: a.insert(1, ('TupleType', [o.info.tuple_type])) if o.info and o.info.fallback_to_any: @@ -329,16 +329,16 @@ def visit_match_stmt(self, o: 'mypy.nodes.MatchStmt') -> str: # Simple expressions def visit_int_expr(self, o: 'mypy.nodes.IntExpr') -> str: - return 'IntExpr({})'.format(o.value) + return f'IntExpr({o.value})' def visit_str_expr(self, o: 'mypy.nodes.StrExpr') -> str: - return 'StrExpr({})'.format(self.str_repr(o.value)) + return f'StrExpr({self.str_repr(o.value)})' def visit_bytes_expr(self, o: 'mypy.nodes.BytesExpr') -> str: - return 'BytesExpr({})'.format(self.str_repr(o.value)) + return f'BytesExpr({self.str_repr(o.value)})' def visit_unicode_expr(self, o: 'mypy.nodes.UnicodeExpr') -> str: - return 'UnicodeExpr({})'.format(self.str_repr(o.value)) + return f'UnicodeExpr({self.str_repr(o.value)})' def str_repr(self, s: str) -> str: s = re.sub(r'\\u[0-9a-fA-F]{4}', lambda m: '\\' + m.group(0), s) @@ -346,10 +346,10 @@ def str_repr(self, s: str) -> str: lambda m: r'\u%.4x' % ord(m.group(0)), s) def visit_float_expr(self, o: 'mypy.nodes.FloatExpr') -> str: - return 'FloatExpr({})'.format(o.value) + return f'FloatExpr({o.value})' def visit_complex_expr(self, o: 'mypy.nodes.ComplexExpr') -> str: - return 'ComplexExpr({})'.format(o.value) + return f'ComplexExpr({o.value})' def visit_ellipsis(self, o: 'mypy.nodes.EllipsisExpr') -> str: return 'Ellipsis' @@ -362,7 +362,7 @@ def visit_name_expr(self, o: 'mypy.nodes.NameExpr') -> str: o.is_inferred_def or o.is_special_form, o.node) if isinstance(o.node, mypy.nodes.Var) and o.node.is_final: - pretty += ' = {}'.format(o.node.final_value) + pretty += f' = {o.node.final_value}' return short_type(o) + '(' + pretty + ')' def pretty_name(self, name: str, kind: Optional[int], fullname: Optional[str], @@ -379,13 +379,13 @@ def pretty_name(self, name: str, kind: Optional[int], fullname: Optional[str], elif kind == mypy.nodes.GDEF or (fullname != name and fullname is not None): # Append fully qualified name for global references. - n += ' [{}{}]'.format(fullname, id) + n += f' [{fullname}{id}]' elif kind == mypy.nodes.LDEF: # Add tag to signify a local reference. - n += ' [l{}]'.format(id) + n += f' [l{id}]' elif kind == mypy.nodes.MDEF: # Add tag to signify a member reference. - n += ' [m{}]'.format(id) + n += f' [m{id}]' else: n += id return n @@ -481,7 +481,7 @@ def visit_type_var_expr(self, o: 'mypy.nodes.TypeVarExpr') -> str: if o.values: a += [('Values', o.values)] if not mypy.types.is_named_instance(o.upper_bound, 'builtins.object'): - a += ['UpperBound({})'.format(o.upper_bound)] + a += [f'UpperBound({o.upper_bound})'] return self.dump(a, o) def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> str: @@ -493,7 +493,7 @@ def visit_paramspec_expr(self, o: 'mypy.nodes.ParamSpecExpr') -> str: if o.variance == mypy.nodes.CONTRAVARIANT: a += ['Variance(CONTRAVARIANT)'] if not mypy.types.is_named_instance(o.upper_bound, 'builtins.object'): - a += ['UpperBound({})'.format(o.upper_bound)] + a += [f'UpperBound({o.upper_bound})'] return self.dump(a, o) def visit_type_var_tuple_expr(self, o: 'mypy.nodes.TypeVarTupleExpr') -> str: @@ -505,11 +505,11 @@ def visit_type_var_tuple_expr(self, o: 'mypy.nodes.TypeVarTupleExpr') -> str: if o.variance == mypy.nodes.CONTRAVARIANT: a += ['Variance(CONTRAVARIANT)'] if not mypy.types.is_named_instance(o.upper_bound, 'builtins.object'): - a += ['UpperBound({})'.format(o.upper_bound)] + a += [f'UpperBound({o.upper_bound})'] return self.dump(a, o) def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> str: - return 'TypeAliasExpr({})'.format(o.type) + return f'TypeAliasExpr({o.type})' def visit_namedtuple_expr(self, o: 'mypy.nodes.NamedTupleExpr') -> str: return 'NamedTupleExpr:{}({}, {})'.format(o.line, @@ -517,14 +517,14 @@ def visit_namedtuple_expr(self, o: 'mypy.nodes.NamedTupleExpr') -> str: o.info.tuple_type) def visit_enum_call_expr(self, o: 'mypy.nodes.EnumCallExpr') -> str: - return 'EnumCallExpr:{}({}, {})'.format(o.line, o.info.name, o.items) + return f'EnumCallExpr:{o.line}({o.info.name}, {o.items})' def visit_typeddict_expr(self, o: 'mypy.nodes.TypedDictExpr') -> str: return 'TypedDictExpr:{}({})'.format(o.line, o.info.name) def visit__promote_expr(self, o: 'mypy.nodes.PromoteExpr') -> str: - return 'PromoteExpr:{}({})'.format(o.line, o.type) + return f'PromoteExpr:{o.line}({o.type})' def visit_newtype_expr(self, o: 'mypy.nodes.NewTypeExpr') -> str: return 'NewTypeExpr:{}({}, {})'.format(o.line, o.name, diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 1c31223d26a1..a596ddb4f78d 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -52,11 +52,10 @@ def __eq__(self, other: Any) -> bool: return False -FunctionSig = NamedTuple('FunctionSig', [ - ('name', str), - ('args', List[ArgSig]), - ('ret_type', str) -]) +class FunctionSig(NamedTuple): + name: str + args: List[ArgSig] + ret_type: str # States of the docstring parser. @@ -237,7 +236,7 @@ def infer_sig_from_docstring(docstr: Optional[str], name: str) -> Optional[List[ def is_unique_args(sig: FunctionSig) -> bool: """return true if function argument names are unique""" - return len(sig.args) == len(set((arg.name for arg in sig.args))) + return len(sig.args) == len({arg.name for arg in sig.args}) # Return only signatures that have unique argument names. Mypy fails on non-unique arg names. return [sig for sig in sigs if is_unique_args(sig)] diff --git a/mypy/stubgen.py b/mypy/stubgen.py index eade0bbdc363..41fc58a2b0fc 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -259,14 +259,14 @@ def visit_unbound_type(self, t: UnboundType) -> str: s = t.name self.stubgen.import_tracker.require_name(s) if t.args: - s += '[{}]'.format(self.args_str(t.args)) + s += f'[{self.args_str(t.args)}]' return s def visit_none_type(self, t: NoneType) -> str: return "None" def visit_type_list(self, t: TypeList) -> str: - return '[{}]'.format(self.list_str(t.items)) + return f'[{self.list_str(t.items)}]' def args_str(self, args: Iterable[Type]) -> str: """Convert an array of arguments to strings and join the results with commas. @@ -279,7 +279,7 @@ def args_str(self, args: Iterable[Type]) -> str: for arg in args: arg_str = arg.accept(self) if isinstance(arg, UnboundType) and arg.original_str_fallback in types: - res.append("'{}'".format(arg_str)) + res.append(f"'{arg_str}'") else: res.append(arg_str) return ', '.join(res) @@ -307,7 +307,7 @@ def visit_call_expr(self, node: CallExpr) -> str: elif kind == ARG_STAR2: args.append('**' + arg.accept(self)) elif kind == ARG_NAMED: - args.append('{}={}'.format(name, arg.accept(self))) + args.append(f'{name}={arg.accept(self)}') else: raise ValueError("Unknown argument kind %s in call" % kind) return "{}({})".format(callee, ", ".join(args)) @@ -333,7 +333,7 @@ def visit_str_expr(self, node: StrExpr) -> str: def visit_index_expr(self, node: IndexExpr) -> str: base = node.base.accept(self) index = node.index.accept(self) - return "{}[{}]".format(base, index) + return f"{base}[{index}]" def visit_tuple_expr(self, node: TupleExpr) -> str: return ", ".join(n.accept(self) for n in node.items) @@ -436,21 +436,21 @@ def import_lines(self) -> List[str]: # This name was found in a from ... import ... # Collect the name in the module_map if name in self.reverse_alias: - name = '{} as {}'.format(self.reverse_alias[name], name) + name = f'{self.reverse_alias[name]} as {name}' elif name in self.reexports: - name = '{} as {}'.format(name, name) + name = f'{name} as {name}' module_map[m].append(name) else: # This name was found in an import ... # We can already generate the import line if name in self.reverse_alias: source = self.reverse_alias[name] - result.append("import {} as {}\n".format(source, name)) + result.append(f"import {source} as {name}\n") elif name in self.reexports: assert '.' not in name # Because reexports only has nonqualified names - result.append("import {} as {}\n".format(name, name)) + result.append(f"import {name} as {name}\n") else: - result.append("import {}\n".format(self.direct_imports[name])) + result.append(f"import {self.direct_imports[name]}\n") # Now generate all the from ... import ... lines collected in module_map for module, names in sorted(module_map.items()): @@ -635,7 +635,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, for s in self._decorators: self.add(s) self.clear_decorators() - self.add("%s%sdef %s(" % (self._indent, 'async ' if o.is_coroutine else '', o.name)) + self.add("{}{}def {}(".format(self._indent, 'async ' if o.is_coroutine else '', o.name)) self.record_name(o.name) args: List[str] = [] for i, arg_ in enumerate(o.arguments): @@ -653,7 +653,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, # Luckily, an argument explicitly annotated with "Any" has # type "UnboundType" and will not match. if not isinstance(get_proper_type(annotated_type), AnyType): - annotation = ": {}".format(self.print_annotation(annotated_type)) + annotation = f": {self.print_annotation(annotated_type)}" if kind.is_named() and not any(arg.startswith('*') for arg in args): args.append('*') @@ -664,14 +664,14 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, if typename == '': annotation = '=...' else: - annotation = ': {} = ...'.format(typename) + annotation = f': {typename} = ...' else: annotation += ' = ...' arg = name + annotation elif kind == ARG_STAR: - arg = '*%s%s' % (name, annotation) + arg = f'*{name}{annotation}' elif kind == ARG_STAR2: - arg = '**%s%s' % (name, annotation) + arg = f'**{name}{annotation}' else: arg = name + annotation args.append(arg) @@ -711,7 +711,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, retfield = ' -> ' + retname self.add(', '.join(args)) - self.add("){}: ...\n".format(retfield)) + self.add(f"){retfield}: ...\n") self._state = FUNC def is_none_expr(self, expr: Expression) -> bool: @@ -807,10 +807,10 @@ def process_member_expr_decorator(self, expr: MemberExpr, context: Decorator) -> if expr.name == 'abstractproperty': self.import_tracker.require_name(expr.expr.name) self.add_decorator('%s' % ('property')) - self.add_decorator('%s.%s' % (expr.expr.name, 'abstractmethod')) + self.add_decorator('{}.{}'.format(expr.expr.name, 'abstractmethod')) else: self.import_tracker.require_name(expr.expr.name) - self.add_decorator('%s.%s' % (expr.expr.name, expr.name)) + self.add_decorator(f'{expr.expr.name}.{expr.name}') is_abstract = True elif expr.name == 'coroutine': if (isinstance(expr.expr, MemberExpr) and @@ -835,7 +835,7 @@ def process_member_expr_decorator(self, expr: MemberExpr, context: Decorator) -> self.import_tracker.reverse_alias.get(expr.expr.name) in TYPING_MODULE_NAMES) and expr.name == 'overload'): self.import_tracker.require_name(expr.expr.name) - self.add_decorator('%s.%s' % (expr.expr.name, 'overload')) + self.add_decorator('{}.{}'.format(expr.expr.name, 'overload')) is_overload = True return is_abstract, is_overload @@ -845,7 +845,7 @@ def visit_class_def(self, o: ClassDef) -> None: if not self._indent and self._state != EMPTY: sep = len(self._output) self.add('\n') - self.add('%sclass %s' % (self._indent, o.name)) + self.add(f'{self._indent}class {o.name}') self.record_name(o.name) base_types = self.get_base_types(o) if base_types: @@ -892,7 +892,7 @@ def get_base_types(self, cdef: ClassDef) -> List[str]: base_types.append(base.name) elif isinstance(base, MemberExpr): modname = get_qualified_name(base.expr) - base_types.append('%s.%s' % (modname, base.name)) + base_types.append(f'{modname}.{base.name}') elif isinstance(base, IndexExpr): p = AliasPrinter(self) base_types.append(base.accept(p)) @@ -961,18 +961,18 @@ def process_namedtuple(self, lvalue: NameExpr, rvalue: CallExpr) -> None: list_items = cast(List[StrExpr], rvalue.args[1].items) items = [item.value for item in list_items] else: - self.add('%s%s: Incomplete' % (self._indent, lvalue.name)) + self.add(f'{self._indent}{lvalue.name}: Incomplete') self.import_tracker.require_name('Incomplete') return self.import_tracker.require_name('NamedTuple') - self.add('{}class {}(NamedTuple):'.format(self._indent, lvalue.name)) + self.add(f'{self._indent}class {lvalue.name}(NamedTuple):') if len(items) == 0: self.add(' ...\n') else: self.import_tracker.require_name('Incomplete') self.add('\n') for item in items: - self.add('{} {}: Incomplete\n'.format(self._indent, item)) + self.add(f'{self._indent} {item}: Incomplete\n') self._state = CLASS def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: @@ -1020,7 +1020,7 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool: def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None: p = AliasPrinter(self) - self.add("{} = {}\n".format(lvalue.name, rvalue.accept(p))) + self.add(f"{lvalue.name} = {rvalue.accept(p)}\n") self.record_name(lvalue.name) self._vars[-1].append(lvalue.name) @@ -1036,7 +1036,7 @@ def visit_if_stmt(self, o: IfStmt) -> None: super().visit_if_stmt(o) def visit_import_all(self, o: ImportAll) -> None: - self.add_import_line('from %s%s import *\n' % ('.' * o.relative, o.id)) + self.add_import_line('from {}{} import *\n'.format('.' * o.relative, o.id)) def visit_import_from(self, o: ImportFrom) -> None: exported_names: Set[str] = set() @@ -1127,10 +1127,10 @@ def get_init(self, lvalue: str, rvalue: Expression, self.import_tracker.module_for.get('Final') in TYPING_MODULE_NAMES): # Final without type argument is invalid in stubs. final_arg = self.get_str_type_of_node(rvalue) - typename += '[{}]'.format(final_arg) + typename += f'[{final_arg}]' else: typename = self.get_str_type_of_node(rvalue) - return '%s%s: %s\n' % (self._indent, lvalue, typename) + return f'{self._indent}{lvalue}: {typename}\n' def add(self, string: str) -> None: """Add text to generated stub.""" @@ -1139,7 +1139,7 @@ def add(self, string: str) -> None: def add_decorator(self, name: str) -> None: if not self._indent and self._state not in (EMPTY, FUNC): self._decorators.append('\n') - self._decorators.append('%s@%s\n' % (self._indent, name)) + self._decorators.append(f'{self._indent}@{name}\n') def clear_decorators(self) -> None: self._decorators.clear() @@ -1295,7 +1295,7 @@ def get_qualified_name(o: Expression) -> str: if isinstance(o, NameExpr): return o.name elif isinstance(o, MemberExpr): - return '%s.%s' % (get_qualified_name(o.expr), o.name) + return f'{get_qualified_name(o.expr)}.{o.name}' else: return ERROR_MARKER @@ -1420,7 +1420,7 @@ def is_non_library_module(module: str) -> bool: def translate_module_name(module: str, relative: int) -> Tuple[str, int]: for pkg in VENDOR_PACKAGES: for alt in 'six.moves', 'six': - substr = '{}.{}'.format(pkg, alt) + substr = f'{pkg}.{alt}' if (module.endswith('.' + substr) or (module == substr and relative)): return alt, 0 @@ -1513,7 +1513,7 @@ def generate_asts_for_modules(py_modules: List[StubSource], try: res = build([module.source for module in py_modules], mypy_options) except CompileError as e: - raise SystemExit("Critical error during semantic analysis: {}".format(e)) from e + raise SystemExit(f"Critical error during semantic analysis: {e}") from e for mod in py_modules: mod.ast = res.graph[mod.module].tree diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 4b0bd4a62552..e9fa0ef62bb2 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -73,7 +73,7 @@ def generate_stub_for_c_module(module_name: str, continue if name not in done and not inspect.ismodule(obj): type_str = strip_or_import(get_type_fullname(type(obj)), module, imports) - variables.append('%s: %s' % (name, type_str)) + variables.append(f'{name}: {type_str}') output = [] for line in sorted(set(imports)): output.append(line) @@ -133,7 +133,7 @@ def is_c_type(obj: object) -> bool: def is_pybind11_overloaded_function_docstring(docstr: str, name: str) -> bool: - return docstr.startswith("{}(*args, **kwargs)\n".format(name) + + return docstr.startswith(f"{name}(*args, **kwargs)\n" + "Overloaded function.\n\n") @@ -254,7 +254,7 @@ def strip_or_import(typ: str, module: ModuleType, imports: List[str]) -> str: if arg_module == 'builtins': stripped_type = typ[len('builtins') + 1:] else: - imports.append('import %s' % (arg_module,)) + imports.append(f'import {arg_module}') if stripped_type == 'NoneType': stripped_type = 'None' return stripped_type @@ -304,14 +304,14 @@ def infer_prop_type(docstr: Optional[str]) -> Optional[str]: if is_static_property(obj): trailing_comment = " # read-only" if readonly else "" static_properties.append( - '{}: ClassVar[{}] = ...{}'.format(name, inferred, trailing_comment) + f'{name}: ClassVar[{inferred}] = ...{trailing_comment}' ) else: # regular property if readonly: ro_properties.append('@property') - ro_properties.append('def {}(self) -> {}: ...'.format(name, inferred)) + ro_properties.append(f'def {name}(self) -> {inferred}: ...') else: - rw_properties.append('{}: {}'.format(name, inferred)) + rw_properties.append(f'{name}: {inferred}') def generate_c_type_stub(module: ModuleType, @@ -370,7 +370,7 @@ def generate_c_type_stub(module: ModuleType, if is_skipped_attribute(attr): continue if attr not in done: - static_properties.append('%s: ClassVar[%s] = ...' % ( + static_properties.append('{}: ClassVar[{}] = ...'.format( attr, strip_or_import(get_type_fullname(type(value)), module, imports))) all_bases = type.mro(obj) if all_bases[-1] is object: @@ -398,7 +398,7 @@ def generate_c_type_stub(module: ModuleType, else: bases_str = '' if types or static_properties or rw_properties or methods or ro_properties: - output.append('class %s%s:' % (class_name, bases_str)) + output.append(f'class {class_name}{bases_str}:') for line in types: if output and output[-1] and \ not output[-1].startswith('class') and line.startswith('class'): @@ -413,11 +413,11 @@ def generate_c_type_stub(module: ModuleType, for line in ro_properties: output.append(' %s' % line) else: - output.append('class %s%s: ...' % (class_name, bases_str)) + output.append(f'class {class_name}{bases_str}: ...') def get_type_fullname(typ: type) -> str: - return '%s.%s' % (typ.__module__, getattr(typ, '__qualname__', typ.__name__)) + return '{}.{}'.format(typ.__module__, getattr(typ, '__qualname__', typ.__name__)) def method_name_sort_key(name: str) -> Tuple[int, str]: diff --git a/mypy/stubtest.py b/mypy/stubtest.py index d3bc40bc27e8..ea0deb35092f 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -105,9 +105,9 @@ def get_description(self, concise: bool = False) -> str: stub_loc_str = "" if stub_line: - stub_loc_str += " at line {}".format(stub_line) + stub_loc_str += f" at line {stub_line}" if stub_file: - stub_loc_str += " in file {}".format(Path(stub_file)) + stub_loc_str += f" in file {Path(stub_file)}" runtime_line = None runtime_file = None @@ -123,9 +123,9 @@ def get_description(self, concise: bool = False) -> str: runtime_loc_str = "" if runtime_line: - runtime_loc_str += " at line {}".format(runtime_line) + runtime_loc_str += f" at line {runtime_line}" if runtime_file: - runtime_loc_str += " in file {}".format(Path(runtime_file)) + runtime_loc_str += f" in file {Path(runtime_file)}" output = [ _style("error: ", color="red", bold=True), @@ -172,7 +172,7 @@ def test_module(module_name: str) -> Iterator[Error]: # mentioned in __all__ __import__(module_name, fromlist=["*"]) except Exception as e: - yield Error([module_name], "failed to import: {}".format(e), stub, MISSING) + yield Error([module_name], f"failed to import: {e}", stub, MISSING) return with warnings.catch_warnings(): @@ -207,11 +207,11 @@ def verify_mypyfile( return # Check things in the stub - to_check = set( + to_check = { m for m, o in stub.names.items() if not o.module_hidden and (not is_probably_private(m) or hasattr(runtime, m)) - ) + } def _belongs_to_runtime(r: types.ModuleType, attr: str) -> bool: obj = getattr(r, attr) @@ -302,7 +302,7 @@ class SubClass(runtime): # type: ignore for entry in sorted(to_check): mangled_entry = entry if entry.startswith("__") and not entry.endswith("__"): - mangled_entry = "_{}{}".format(stub.name, entry) + mangled_entry = f"_{stub.name}{entry}" stub_to_verify = next((t.names[entry].node for t in stub.mro if entry in t.names), MISSING) assert stub_to_verify is not None try: @@ -478,7 +478,7 @@ def get_desc(arg: Any) -> str: arg_type = get_type(arg) return ( get_name(arg) - + (": {}".format(arg_type) if arg_type else "") + + (f": {arg_type}" if arg_type else "") + (" = ..." if has_default(arg) else "") ) @@ -550,7 +550,7 @@ def from_overloadedfuncdef(stub: nodes.OverloadedFuncDef) -> "Signature[nodes.Ar # For positional-only args, we allow overloads to have different names for the same # argument. To accomplish this, we just make up a fake index-based name. name = ( - "__{}".format(index) + f"__{index}" if arg.variable.name.startswith("__") or assume_positional_only else arg.variable.name ) @@ -649,24 +649,24 @@ def _verify_signature( # If the variable is in runtime.kwonly, it's just mislabelled as not a # keyword-only argument if stub_arg.variable.name not in runtime.kwonly: - yield 'runtime does not have argument "{}"'.format(stub_arg.variable.name) + yield f'runtime does not have argument "{stub_arg.variable.name}"' else: - yield 'stub argument "{}" is not keyword-only'.format(stub_arg.variable.name) + yield f'stub argument "{stub_arg.variable.name}" is not keyword-only' if stub.varpos is not None: - yield 'runtime does not have *args argument "{}"'.format(stub.varpos.variable.name) + yield f'runtime does not have *args argument "{stub.varpos.variable.name}"' elif len(stub.pos) < len(runtime.pos): for runtime_arg in runtime.pos[len(stub.pos):]: if runtime_arg.name not in stub.kwonly: - yield 'stub does not have argument "{}"'.format(runtime_arg.name) + yield f'stub does not have argument "{runtime_arg.name}"' else: - yield 'runtime argument "{}" is not keyword-only'.format(runtime_arg.name) + yield f'runtime argument "{runtime_arg.name}" is not keyword-only' # Checks involving *args if len(stub.pos) <= len(runtime.pos) or runtime.varpos is None: if stub.varpos is None and runtime.varpos is not None: - yield 'stub does not have *args argument "{}"'.format(runtime.varpos.name) + yield f'stub does not have *args argument "{runtime.varpos.name}"' if stub.varpos is not None and runtime.varpos is None: - yield 'runtime does not have *args argument "{}"'.format(stub.varpos.variable.name) + yield f'runtime does not have *args argument "{stub.varpos.variable.name}"' # Check keyword-only args for arg in sorted(set(stub.kwonly) & set(runtime.kwonly)): @@ -682,26 +682,26 @@ def _verify_signature( # takes *kwargs, since runtime logic may prevent additional arguments from actually being # accepted. for arg in sorted(set(stub.kwonly) - set(runtime.kwonly)): - yield 'runtime does not have argument "{}"'.format(arg) + yield f'runtime does not have argument "{arg}"' for arg in sorted(set(runtime.kwonly) - set(stub.kwonly)): - if arg in set(stub_arg.variable.name for stub_arg in stub.pos): + if arg in {stub_arg.variable.name for stub_arg in stub.pos}: # Don't report this if we've reported it before if len(stub.pos) > len(runtime.pos) and runtime.varpos is not None: - yield 'stub argument "{}" is not keyword-only'.format(arg) + yield f'stub argument "{arg}" is not keyword-only' else: - yield 'stub does not have argument "{}"'.format(arg) + yield f'stub does not have argument "{arg}"' # Checks involving **kwargs if stub.varkw is None and runtime.varkw is not None: # As mentioned above, don't enforce that the stub takes **kwargs. # Also check against positional parameters, to avoid a nitpicky message when an argument # isn't marked as keyword-only - stub_pos_names = set(stub_arg.variable.name for stub_arg in stub.pos) + stub_pos_names = {stub_arg.variable.name for stub_arg in stub.pos} # Ideally we'd do a strict subset check, but in practice the errors from that aren't useful if not set(runtime.kwonly).issubset(set(stub.kwonly) | stub_pos_names): - yield 'stub does not have **kwargs argument "{}"'.format(runtime.varkw.name) + yield f'stub does not have **kwargs argument "{runtime.varkw.name}"' if stub.varkw is not None and runtime.varkw is None: - yield 'runtime does not have **kwargs argument "{}"'.format(stub.varkw.variable.name) + yield f'runtime does not have **kwargs argument "{stub.varkw.variable.name}"' @verify.register(nodes.FuncItem) @@ -803,7 +803,7 @@ def verify_var( if should_error: yield Error( object_path, - "variable differs from runtime type {}".format(runtime_type), + f"variable differs from runtime type {runtime_type}", stub, runtime, ) @@ -855,7 +855,7 @@ def verify_overloadedfuncdef( "is inconsistent, " + message, stub, runtime, - stub_desc=str(stub.type) + "\nInferred signature: {}".format(stub_sig), + stub_desc=str(stub.type) + f"\nInferred signature: {stub_sig}", runtime_desc="def " + str(signature), ) @@ -1400,7 +1400,7 @@ def set_strict_flags() -> None: # not needed yet # This lets us allowlist errors that don't manifest at all on some systems if not allowlist[w] and not allowlist_regexes[w].fullmatch(""): exit_code = 1 - print("note: unused allowlist entry {}".format(w)) + print(f"note: unused allowlist entry {w}") # Print the generated allowlist if args.generate_allowlist: diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 5772d3fc9981..8ed73cab203b 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -66,11 +66,9 @@ def walk_packages(inspect: ModuleInspect, yield prop.name if prop.is_c_module: # Recursively iterate through the subpackages - for submodule in walk_packages(inspect, prop.subpackages, verbose): - yield submodule + yield from walk_packages(inspect, prop.subpackages, verbose) else: - for submodule in prop.subpackages: - yield submodule + yield from prop.subpackages def find_module_path_and_all_py2(module: str, @@ -83,7 +81,7 @@ def find_module_path_and_all_py2(module: str, Raise CantImport if the module can't be imported, or exit if it's a C extension module. """ - cmd_template = '{interpreter} -c "%s"'.format(interpreter=interpreter) + cmd_template = f'{interpreter} -c "%s"' code = ("import importlib, json; mod = importlib.import_module('%s'); " "print(mod.__file__); print(json.dumps(getattr(mod, '__all__', None)))") % module try: @@ -188,7 +186,7 @@ def generate_guarded(mod: str, target: str, def report_missing(mod: str, message: Optional[str] = '', traceback: str = '') -> None: if message: message = ' with error: ' + message - print('{}: Failed to import, skipping{}'.format(mod, message)) + print(f'{mod}: Failed to import, skipping{message}') m = re.search(r"ModuleNotFoundError: No module named '([^']*)'", traceback) if m: missing_module = m.group(1) @@ -202,8 +200,8 @@ def fail_missing(mod: str, reason: ModuleNotFoundReason) -> None: elif reason is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: clarification = "(module likely exists, but is not PEP 561 compatible)" else: - clarification = "(unknown reason '{}')".format(reason) - raise SystemExit("Can't find module '{}' {}".format(mod, clarification)) + clarification = f"(unknown reason '{reason}')" + raise SystemExit(f"Can't find module '{mod}' {clarification}") @overload diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 8bb592702157..bbde38c5f92f 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -591,7 +591,7 @@ def visit_type_type(self, left: TypeType) -> bool: return False def visit_type_alias_type(self, left: TypeAliasType) -> bool: - assert False, "This should be never called, got {}".format(left) + assert False, f"This should be never called, got {left}" T = TypeVar('T', Instance, TypeAliasType) @@ -1588,7 +1588,7 @@ def visit_type_type(self, left: TypeType) -> bool: return False def visit_type_alias_type(self, left: TypeAliasType) -> bool: - assert False, "This should be never called, got {}".format(left) + assert False, f"This should be never called, got {left}" def is_more_precise(left: Type, right: Type, *, ignore_promotions: bool = False) -> bool: diff --git a/mypy/suggestions.py b/mypy/suggestions.py index 46f8ff28dc76..fbbf00118e82 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -62,18 +62,18 @@ import os -PyAnnotateSignature = TypedDict('PyAnnotateSignature', - {'return_type': str, 'arg_types': List[str]}) +class PyAnnotateSignature(TypedDict): + return_type: str + arg_types: List[str] -Callsite = NamedTuple( - 'Callsite', - [('path', str), - ('line', int), - ('arg_kinds', List[List[ArgKind]]), - ('callee_arg_names', List[Optional[str]]), - ('arg_names', List[List[Optional[str]]]), - ('arg_types', List[List[Type]])]) +class Callsite(NamedTuple): + path: str + line: int + arg_kinds: List[List[ArgKind]] + callee_arg_names: List[Optional[str]] + arg_names: List[List[Optional[str]]] + arg_types: List[List[Type]] class SuggestionPlugin(Plugin): @@ -250,7 +250,7 @@ def suggest_callsites(self, function: str) -> str: callsites, _ = self.get_callsites(node) return '\n'.join(dedup( - ["%s:%s: %s" % (path, line, self.format_args(arg_kinds, arg_names, arg_types)) + [f"{path}:{line}: {self.format_args(arg_kinds, arg_names, arg_types)}" for path, line, arg_kinds, _, arg_names, arg_types in callsites] )) @@ -483,7 +483,7 @@ def format_args(self, arg = '**' + arg elif kind.is_named(): if name: - arg = "%s=%s" % (name, arg) + arg = f"{name}={arg}" args.append(arg) return "(%s)" % (", ".join(args)) @@ -504,14 +504,14 @@ def find_node(self, key: str) -> Tuple[str, str, FuncDef]: ' package.module.Class.method or path/to/file.py:line'.format(key)) file, line = key.split(':') if not line.isdigit(): - raise SuggestionFailure('Line number must be a number. Got {}'.format(line)) + raise SuggestionFailure(f'Line number must be a number. Got {line}') line_number = int(line) modname, node = self.find_node_by_file_and_line(file, line_number) tail = node.fullname[len(modname) + 1:] # add one to account for '.' else: target = split_target(self.fgmanager.graph, key) if not target: - raise SuggestionFailure("Cannot find module for %s" % (key,)) + raise SuggestionFailure(f"Cannot find module for {key}") modname, tail = target node = self.find_node_by_module_and_name(modname, tail) @@ -589,7 +589,7 @@ def find_node_by_file_and_line(self, file: str, line: int) -> Tuple[str, SymbolN closest_line = sym_line node = sym.node if not node: - raise SuggestionFailure('Cannot find a function at line {}'.format(line)) + raise SuggestionFailure(f'Cannot find a function at line {line}') return modname, node def extract_from_decorator(self, node: Decorator) -> Optional[FuncDef]: @@ -720,7 +720,7 @@ def score_type(self, t: Type, arg_pos: bool) -> int: return 0 def score_callable(self, t: CallableType) -> int: - return (sum([self.score_type(x, arg_pos=True) for x in t.arg_types]) + + return (sum(self.score_type(x, arg_pos=True) for x in t.arg_types) + self.score_type(t.ret_type, arg_pos=False)) @@ -803,7 +803,7 @@ def visit_instance(self, t: Instance) -> str: if (mod, obj) == ('builtins', 'tuple'): mod, obj = 'typing', 'Tuple[' + t.args[0].accept(self) + ', ...]' elif t.args: - obj += '[{}]'.format(self.list_str(t.args)) + obj += f'[{self.list_str(t.args)}]' if mod_obj == ('builtins', 'unicode'): return 'Text' @@ -819,7 +819,7 @@ def visit_tuple_type(self, t: TupleType) -> str: if fallback_name != 'builtins.tuple': return t.partial_fallback.accept(self) s = self.list_str(t.items) - return 'Tuple[{}]'.format(s) + return f'Tuple[{s}]' def visit_uninhabited_type(self, t: UninhabitedType) -> str: return "Any" @@ -829,7 +829,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> str: def visit_union_type(self, t: UnionType) -> str: if len(t.items) == 2 and is_optional(t): - return "Optional[{}]".format(remove_optional(t).accept(self)) + return f"Optional[{remove_optional(t).accept(self)}]" else: return super().visit_union_type(t) @@ -845,7 +845,7 @@ def visit_callable_type(self, t: CallableType) -> str: args = [typ.accept(self) for typ in t.arg_types] arg_str = "[{}]".format(", ".join(args)) - return "Callable[{}, {}]".format(arg_str, t.ret_type.accept(self)) + return f"Callable[{arg_str}, {t.ret_type.accept(self)}]" class StrToText(TypeTranslator): diff --git a/mypy/tvar_scope.py b/mypy/tvar_scope.py index c1fe1cd6be35..ecb00938fec9 100644 --- a/mypy/tvar_scope.py +++ b/mypy/tvar_scope.py @@ -120,7 +120,7 @@ def get_binding(self, item: Union[str, SymbolTableNode]) -> Optional[TypeVarLike return None def __str__(self) -> str: - me = ", ".join('{}: {}`{}'.format(k, v.name, v.id) for k, v in self.scope.items()) + me = ", ".join(f'{k}: {v.name}`{v.id}' for k, v in self.scope.items()) if self.parent is None: return me - return "{} <- {}".format(str(self.parent), me) + return f"{self.parent} <- {me}" diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 41225a6061f3..536e170a9644 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -99,7 +99,7 @@ def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> str: nongen_builtins = get_nongen_builtins((3, 8)) replacement = nongen_builtins[name] if replacement and propose_alt: - msg += ', use "{}" instead'.format(replacement) + msg += f', use "{replacement}" instead' return msg @@ -199,7 +199,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) self.api.record_incomplete_ref() return AnyType(TypeOfAny.special_form) if node is None: - self.fail('Internal error (node is None, kind={})'.format(sym.kind), t) + self.fail(f'Internal error (node is None, kind={sym.kind})', t) return AnyType(TypeOfAny.special_form) fullname = node.fullname hook = self.plugin.get_type_analyze_hook(fullname) @@ -213,11 +213,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) tvar_def = self.tvar_scope.get_binding(sym) if isinstance(sym.node, ParamSpecExpr): if tvar_def is None: - self.fail('ParamSpec "{}" is unbound'.format(t.name), t) + self.fail(f'ParamSpec "{t.name}" is unbound', t) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, ParamSpecType) if len(t.args) > 0: - self.fail('ParamSpec "{}" used with arguments'.format(t.name), t) + self.fail(f'ParamSpec "{t.name}" used with arguments', t) # Change the line number return ParamSpecType( tvar_def.name, tvar_def.fullname, tvar_def.id, tvar_def.flavor, @@ -230,7 +230,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if isinstance(sym.node, TypeVarExpr) and tvar_def is not None: assert isinstance(tvar_def, TypeVarType) if len(t.args) > 0: - self.fail('Type variable "{}" used with arguments'.format(t.name), t) + self.fail(f'Type variable "{t.name}" used with arguments', t) # Change the line number return TypeVarType( tvar_def.name, tvar_def.fullname, tvar_def.id, tvar_def.values, @@ -244,11 +244,11 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) return AnyType(TypeOfAny.from_error) if isinstance(sym.node, TypeVarTupleExpr): if tvar_def is None: - self.fail('TypeVarTuple "{}" is unbound'.format(t.name), t) + self.fail(f'TypeVarTuple "{t.name}" is unbound', t) return AnyType(TypeOfAny.from_error) assert isinstance(tvar_def, TypeVarTupleType) if len(t.args) > 0: - self.fail('Type variable "{}" used with arguments'.format(t.name), t) + self.fail(f'Type variable "{t.name}" used with arguments', t) # Change the line number return TypeVarTupleType( tvar_def.name, tvar_def.fullname, tvar_def.id, @@ -299,7 +299,7 @@ def cannot_resolve_type(self, t: UnboundType) -> None: # need access to MessageBuilder here. Also move the similar # message generation logic in semanal.py. self.api.fail( - 'Cannot resolve name "{}" (possible cyclic definition)'.format(t.name), + f'Cannot resolve name "{t.name}" (possible cyclic definition)', t) def apply_concatenate_operator(self, t: UnboundType) -> Type: @@ -779,10 +779,10 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type: if t.base_type_name in ('builtins.int', 'builtins.bool'): # The only time it makes sense to use an int or bool is inside of # a literal type. - msg = "Invalid type: try using Literal[{}] instead?".format(repr(t.literal_value)) + msg = f"Invalid type: try using Literal[{repr(t.literal_value)}] instead?" elif t.base_type_name in ('builtins.float', 'builtins.complex'): # We special-case warnings for floats and complex numbers. - msg = "Invalid type: {} literals cannot be used as a type".format(t.simple_name()) + msg = f"Invalid type: {t.simple_name()} literals cannot be used as a type" else: # And in all other cases, we default to a generic error message. # Note: the reason why we use a generic error message for strings @@ -1042,14 +1042,14 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> Optional[L # TODO: Once we start adding support for enums, make sure we report a custom # error for case 2 as well. if arg.type_of_any not in (TypeOfAny.from_error, TypeOfAny.special_form): - self.fail('Parameter {} of Literal[...] cannot be of type "Any"'.format(idx), ctx) + self.fail(f'Parameter {idx} of Literal[...] cannot be of type "Any"', ctx) return None elif isinstance(arg, RawExpressionType): # A raw literal. Convert it directly into a literal if we can. if arg.literal_value is None: name = arg.simple_name() if name in ('float', 'complex'): - msg = 'Parameter {} of Literal[...] cannot be of type "{}"'.format(idx, name) + msg = f'Parameter {idx} of Literal[...] cannot be of type "{name}"' else: msg = 'Invalid type: Literal[...] cannot contain arbitrary expressions' self.fail(msg, ctx) @@ -1076,7 +1076,7 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> Optional[L out.extend(union_result) return out else: - self.fail('Parameter {} of Literal[...] is invalid'.format(idx), ctx) + self.fail(f'Parameter {idx} of Literal[...] is invalid', ctx) return None def analyze_type(self, t: Type) -> Type: @@ -1138,7 +1138,7 @@ def bind_function_type_variables( defs: List[TypeVarLikeType] = [] for name, tvar in typevars: if not self.tvar_scope.allow_binding(tvar.fullname): - self.fail('Type variable "{}" is bound by an outer class'.format(name), defn) + self.fail(f'Type variable "{name}" is bound by an outer class', defn) self.tvar_scope.bind_new(name, tvar) binding = self.tvar_scope.get_binding(tvar.fullname) assert binding is not None @@ -1178,7 +1178,7 @@ def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = Fa if analyzed.prefix.arg_types: self.fail('Invalid location for Concatenate', t) else: - self.fail('Invalid location for ParamSpec "{}"'.format(analyzed.name), t) + self.fail(f'Invalid location for ParamSpec "{analyzed.name}"', t) self.note( 'You can use ParamSpec as the first argument to Callable, e.g., ' "'Callable[{}, int]'".format(analyzed.name), @@ -1314,7 +1314,7 @@ def fix_instance(t: Instance, fail: MsgCallback, note: MsgCallback, return # Invalid number of type parameters. n = len(t.type.type_vars) - s = '{} type arguments'.format(n) + s = f'{n} type arguments' if n == 0: s = 'no type arguments' elif n == 1: diff --git a/mypy/typeops.py b/mypy/typeops.py index dbfeebe42f14..e2e44b915c0c 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -811,7 +811,7 @@ def try_contracting_literals_in_union(types: Sequence[Type]) -> List[ProperType] if fullname not in sum_types: sum_types[fullname] = (set(get_enum_values(typ.fallback)) if typ.fallback.type.is_enum - else set((True, False)), + else {True, False}, []) literals, indexes = sum_types[fullname] literals.discard(typ.value) diff --git a/mypy/types.py b/mypy/types.py index 69776e4188ad..a958e6c67dcc 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -200,7 +200,7 @@ def deserialize_type(data: Union[JsonDict, str]) -> 'Type': method = deserialize_map.get(classname) if method is not None: return method(data) - raise NotImplementedError('unexpected .class {}'.format(classname)) + raise NotImplementedError(f'unexpected .class {classname}') class Type(mypy.nodes.Context): @@ -235,11 +235,11 @@ def __repr__(self) -> str: return self.accept(TypeStrVisitor()) def serialize(self) -> Union[JsonDict, str]: - raise NotImplementedError('Cannot serialize {} instance'.format(self.__class__.__name__)) + raise NotImplementedError(f'Cannot serialize {self.__class__.__name__} instance') @classmethod def deserialize(cls, data: JsonDict) -> 'Type': - raise NotImplementedError('Cannot deserialize {} instance'.format(cls.__name__)) + raise NotImplementedError(f'Cannot deserialize {cls.__name__} instance') class TypeAliasType(Type): @@ -375,7 +375,7 @@ def __init__(self, type_guard: Type): self.type_guard = type_guard def __repr__(self) -> str: - return "TypeGuard({})".format(self.type_guard) + return f"TypeGuard({self.type_guard})" class RequiredType(Type): @@ -388,9 +388,9 @@ def __init__(self, item: Type, *, required: bool) -> None: def __repr__(self) -> str: if self.required: - return "Required[{}]".format(self.item) + return f"Required[{self.item}]" else: - return "NotRequired[{}]".format(self.item) + return f"NotRequired[{self.item}]" def accept(self, visitor: 'TypeVisitor[T]') -> T: return self.item.accept(visitor) @@ -1261,11 +1261,11 @@ def with_name(self, name: str) -> 'FunctionLike': pass def get_name(self) -> Optional[str]: pass -FormalArgument = NamedTuple('FormalArgument', [ - ('name', Optional[str]), - ('pos', Optional[int]), - ('typ', Type), - ('required', bool)]) +class FormalArgument(NamedTuple): + name: Optional[str] + pos: Optional[int] + typ: Type + required: bool # TODO: should this take bound typevars too? what would this take? @@ -1635,7 +1635,7 @@ def max_possible_positional_args(self) -> int: This takes into account *arg and **kwargs but excludes keyword-only args.""" if self.is_var_arg or self.is_kw_arg: return sys.maxsize - return sum([kind.is_positional() for kind in self.arg_kinds]) + return sum(kind.is_positional() for kind in self.arg_kinds) def formal_arguments(self, include_star_args: bool = False) -> List[FormalArgument]: """Return a list of the formal arguments of this callable, ignoring *arg and **kwargs. @@ -2234,7 +2234,7 @@ def value_repr(self) -> str: # If this is backed by an enum, if self.is_enum_literal(): - return '{}.{}'.format(fallback_name, self.value) + return f'{fallback_name}.{self.value}' if fallback_name == 'builtins.bytes': # Note: 'builtins.bytes' only appears in Python 3, so we want to @@ -2522,7 +2522,7 @@ def accept(self, visitor: 'TypeVisitor[T]') -> T: def serialize(self) -> str: # We should never get here since all placeholders should be replaced # during semantic analysis. - assert False, "Internal error: unresolved placeholder type {}".format(self.fullname) + assert False, f"Internal error: unresolved placeholder type {self.fullname}" @overload @@ -2593,18 +2593,18 @@ def __init__(self, id_mapper: Optional[IdMapper] = None) -> None: def visit_unbound_type(self, t: UnboundType) -> str: s = t.name + '?' if t.args: - s += '[{}]'.format(self.list_str(t.args)) + s += f'[{self.list_str(t.args)}]' return s def visit_type_list(self, t: TypeList) -> str: - return ''.format(self.list_str(t.items)) + return f'' def visit_callable_argument(self, t: CallableArgument) -> str: typ = t.typ.accept(self) if t.name is None: - return "{}({})".format(t.constructor, typ) + return f"{t.constructor}({typ})" else: - return "{}({}, {})".format(t.constructor, typ, t.name) + return f"{t.constructor}({typ}, {t.name})" def visit_any(self, t: AnyType) -> str: if self.any_as_dots and t.type_of_any == TypeOfAny.special_form: @@ -2624,35 +2624,35 @@ def visit_deleted_type(self, t: DeletedType) -> str: if t.source is None: return "" else: - return "".format(t.source) + return f"" def visit_instance(self, t: Instance) -> str: if t.last_known_value and not t.args: # Instances with a literal fallback should never be generic. If they are, # something went wrong so we fall back to showing the full Instance repr. - s = '{}?'.format(t.last_known_value) + s = f'{t.last_known_value}?' else: s = t.type.fullname or t.type.name or '' if t.args: if t.type.fullname == 'builtins.tuple': assert len(t.args) == 1 - s += '[{}, ...]'.format(self.list_str(t.args)) + s += f'[{self.list_str(t.args)}, ...]' else: - s += '[{}]'.format(self.list_str(t.args)) + s += f'[{self.list_str(t.args)}]' if self.id_mapper: - s += '<{}>'.format(self.id_mapper.id(t.type)) + s += f'<{self.id_mapper.id(t.type)}>' return s def visit_type_var(self, t: TypeVarType) -> str: if t.name is None: # Anonymous type variable type (only numeric id). - s = '`{}'.format(t.id) + s = f'`{t.id}' else: # Named type variable type. - s = '{}`{}'.format(t.name, t.id) + s = f'{t.name}`{t.id}' if self.id_mapper and t.upper_bound: - s += '(upper_bound={})'.format(t.upper_bound.accept(self)) + s += f'(upper_bound={t.upper_bound.accept(self)})' return s def visit_param_spec(self, t: ParamSpecType) -> str: @@ -2740,13 +2740,13 @@ def visit_callable_type(self, t: CallableType) -> str: s += ', ' s += f'*{n}.args, **{n}.kwargs' - s = '({})'.format(s) + s = f'({s})' if not isinstance(get_proper_type(t.ret_type), NoneType): if t.type_guard is not None: - s += ' -> TypeGuard[{}]'.format(t.type_guard.accept(self)) + s += f' -> TypeGuard[{t.type_guard.accept(self)}]' else: - s += ' -> {}'.format(t.ret_type.accept(self)) + s += f' -> {t.ret_type.accept(self)}' if t.variables: vs = [] @@ -2755,9 +2755,9 @@ def visit_callable_type(self, t: CallableType) -> str: # We reimplement TypeVarType.__repr__ here in order to support id_mapper. if var.values: vals = '({})'.format(', '.join(val.accept(self) for val in var.values)) - vs.append('{} in {}'.format(var.name, vals)) + vs.append(f'{var.name} in {vals}') elif not is_named_instance(var.upper_bound, 'builtins.object'): - vs.append('{} <: {}'.format(var.name, var.upper_bound.accept(self))) + vs.append(f'{var.name} <: {var.upper_bound.accept(self)}') else: vs.append(var.name) else: @@ -2765,7 +2765,7 @@ def visit_callable_type(self, t: CallableType) -> str: vs.append(var.name) s = '{} {}'.format('[{}]'.format(', '.join(vs)), s) - return 'def {}'.format(s) + return f'def {s}' def visit_overloaded(self, t: Overloaded) -> str: a = [] @@ -2778,15 +2778,15 @@ def visit_tuple_type(self, t: TupleType) -> str: if t.partial_fallback and t.partial_fallback.type: fallback_name = t.partial_fallback.type.fullname if fallback_name != 'builtins.tuple': - return 'Tuple[{}, fallback={}]'.format(s, t.partial_fallback.accept(self)) - return 'Tuple[{}]'.format(s) + return f'Tuple[{s}, fallback={t.partial_fallback.accept(self)}]' + return f'Tuple[{s}]' def visit_typeddict_type(self, t: TypedDictType) -> str: def item_str(name: str, typ: str) -> str: if name in t.required_keys: - return '{!r}: {}'.format(name, typ) + return f'{name!r}: {typ}' else: - return '{!r}?: {}'.format(name, typ) + return f'{name!r}?: {typ}' s = '{' + ', '.join(item_str(name, typ.accept(self)) for name, typ in t.items.items()) + '}' @@ -2794,21 +2794,21 @@ def item_str(name: str, typ: str) -> str: if t.fallback and t.fallback.type: if t.fallback.type.fullname not in TPDICT_FB_NAMES: prefix = repr(t.fallback.type.fullname) + ', ' - return 'TypedDict({}{})'.format(prefix, s) + return f'TypedDict({prefix}{s})' def visit_raw_expression_type(self, t: RawExpressionType) -> str: return repr(t.literal_value) def visit_literal_type(self, t: LiteralType) -> str: - return 'Literal[{}]'.format(t.value_repr()) + return f'Literal[{t.value_repr()}]' def visit_star_type(self, t: StarType) -> str: s = t.type.accept(self) - return '*{}'.format(s) + return f'*{s}' def visit_union_type(self, t: UnionType) -> str: s = self.list_str(t.items) - return 'Union[{}]'.format(s) + return f'Union[{s}]' def visit_partial_type(self, t: PartialType) -> str: if t.type is None: @@ -2821,10 +2821,10 @@ def visit_ellipsis_type(self, t: EllipsisType) -> str: return '...' def visit_type_type(self, t: TypeType) -> str: - return 'Type[{}]'.format(t.item.accept(self)) + return f'Type[{t.item.accept(self)}]' def visit_placeholder_type(self, t: PlaceholderType) -> str: - return ''.format(t.fullname) + return f'' def visit_type_alias_type(self, t: TypeAliasType) -> str: if t.alias is not None: @@ -2836,7 +2836,7 @@ def visit_type_alias_type(self, t: TypeAliasType) -> str: return '' def visit_unpack_type(self, t: UnpackType) -> str: - return 'Unpack[{}]'.format(t.type.accept(self)) + return f'Unpack[{t.type.accept(self)}]' def list_str(self, a: Iterable[Type]) -> str: """Convert items of an array to strings (pretty-print types) diff --git a/mypy/typestate.py b/mypy/typestate.py index 9e3d724bb185..bbb593ce0daf 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -202,7 +202,7 @@ def __iter__(self) -> Iterator[int]: # a concrete class may not be reprocessed, so not all -> deps # are added. for base_info in info.mro[:-1]: - trigger = make_trigger('%s.%s' % (base_info.fullname, attr)) + trigger = make_trigger(f'{base_info.fullname}.{attr}') if 'typing' in trigger or 'builtins' in trigger: # TODO: avoid everything from typeshed continue From 3c1a762965dff65fd96817e9fef671be7377b287 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 May 2022 17:12:56 -0600 Subject: [PATCH 45/79] mypy: use more f-strings (#12714) Done largely using https://github.com/ikamensh/flynt I went over this pretty closely since I wasn't familiar with the tool I made a couple changes and left out a couple instances which were harder to parse --- mypy/build.py | 41 +++++++++++++------------- mypy/checker.py | 8 ++--- mypy/checkexpr.py | 2 +- mypy/checkstrformat.py | 2 +- mypy/config_parser.py | 10 +++---- mypy/dmypy/client.py | 8 ++--- mypy/dmypy_server.py | 2 +- mypy/dmypy_util.py | 2 +- mypy/fastparse.py | 2 +- mypy/main.py | 21 +++++++------- mypy/memprofile.py | 10 +++---- mypy/messages.py | 53 ++++++++++++++-------------------- mypy/moduleinspect.py | 2 +- mypy/nodes.py | 7 ++--- mypy/plugins/dataclasses.py | 2 +- mypy/plugins/singledispatch.py | 4 +-- mypy/report.py | 7 ++--- mypy/semanal.py | 18 ++++++------ mypy/semanal_enum.py | 12 ++++---- mypy/semanal_newtype.py | 2 +- mypy/semanal_typeddict.py | 2 +- mypy/server/deps.py | 2 +- mypy/server/mergecheck.py | 4 +-- mypy/server/trigger.py | 2 +- mypy/server/update.py | 25 ++++++++-------- mypy/strconv.py | 18 +++++------- mypy/stubdoc.py | 4 +-- mypy/stubgen.py | 32 ++++++++++---------- mypy/stubgenc.py | 16 +++++----- mypy/stubutil.py | 10 +++---- mypy/suggestions.py | 13 ++++----- mypy/test/data.py | 9 ++---- mypy/test/testcheck.py | 2 +- mypy/test/testdeps.py | 5 ++-- mypy/test/testdiff.py | 3 +- mypy/test/testfinegrained.py | 6 ++-- mypy/test/testmerge.py | 12 +++----- mypy/test/testparse.py | 3 +- mypy/test/testpythoneval.py | 2 +- mypy/test/testsemanal.py | 9 ++---- mypy/test/teststubgen.py | 6 ++-- mypy/test/teststubtest.py | 3 +- mypy/test/testtransform.py | 3 +- mypy/test/testtypegen.py | 3 +- mypy/typeanal.py | 9 +++--- mypy/types.py | 6 ++-- mypy/util.py | 2 +- 47 files changed, 190 insertions(+), 236 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 107a291ad582..ba2d1b1b3d35 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -665,7 +665,7 @@ def dump_stats(self) -> None: if self.options.dump_build_stats: print("Stats:") for key, value in sorted(self.stats_summary().items()): - print("{:24}{}".format(key + ":", value)) + print(f"{key + ':':24}{value}") def use_fine_grained_cache(self) -> bool: return self.cache_enabled and self.options.use_fine_grained_cache @@ -1083,7 +1083,7 @@ def read_deps_cache(manager: BuildManager, except FileNotFoundError: matched = False if not matched: - manager.log('Invalid or missing fine-grained deps cache: {}'.format(meta['path'])) + manager.log(f"Invalid or missing fine-grained deps cache: {meta['path']}") return None return module_deps_metas @@ -1485,8 +1485,7 @@ def write_cache(id: str, path: str, tree: MypyFile, # Obtain file paths. meta_json, data_json, _ = get_cache_names(id, path, manager.options) - manager.log('Writing {} {} {} {}'.format( - id, path, meta_json, data_json)) + manager.log(f'Writing {id} {path} {meta_json} {data_json}') # Update tree.path so that in bazel mode it's made relative (since # sometimes paths leak out). @@ -1590,7 +1589,7 @@ def delete_cache(id: str, path: str, manager: BuildManager) -> None: # tracked separately. meta_path, data_path, _ = get_cache_names(id, path, manager.options) cache_paths = [meta_path, data_path] - manager.log('Deleting {} {} {}'.format(id, path, " ".join(x for x in cache_paths if x))) + manager.log(f"Deleting {id} {path} {' '.join(x for x in cache_paths if x)}") for filename in cache_paths: try: @@ -2490,7 +2489,7 @@ def find_module_and_diagnose(manager: BuildManager, and not options.custom_typeshed_dir): raise CompileError([ f'mypy: "{os.path.relpath(result)}" shadows library module "{id}"', - 'note: A user-defined top-level module with name "%s" is not supported' % id + f'note: A user-defined top-level module with name "{id}" is not supported' ]) return (result, follow_imports) else: @@ -2523,7 +2522,7 @@ def find_module_and_diagnose(manager: BuildManager, # If we can't find a root source it's always fatal. # TODO: This might hide non-fatal errors from # root sources processed earlier. - raise CompileError(["mypy: can't find module '%s'" % id]) + raise CompileError([f"mypy: can't find module '{id}'"]) else: raise ModuleNotFound @@ -2670,21 +2669,21 @@ def log_configuration(manager: BuildManager, sources: List[BuildSource]) -> None ] for conf_name, conf_value in configuration_vars: - manager.log("{:24}{}".format(conf_name + ":", conf_value)) + manager.log(f"{conf_name + ':':24}{conf_value}") for source in sources: - manager.log("{:24}{}".format("Found source:", source)) + manager.log(f"{'Found source:':24}{source}") # Complete list of searched paths can get very long, put them under TRACE for path_type, paths in manager.search_paths._asdict().items(): if not paths: - manager.trace("No %s" % path_type) + manager.trace(f"No {path_type}") continue - manager.trace("%s:" % path_type) + manager.trace(f"{path_type}:") for pth in paths: - manager.trace(" %s" % pth) + manager.trace(f" {pth}") # The driver @@ -2720,7 +2719,7 @@ def dispatch(sources: List[BuildSource], if not graph: print("Nothing to do?!", file=stdout) return graph - manager.log("Loaded graph with %d nodes (%.3f sec)" % (len(graph), t1 - t0)) + manager.log(f"Loaded graph with {len(graph)} nodes ({t1 - t0:.3f} sec)") if manager.options.dump_graph: dump_graph(graph, stdout) return graph @@ -3009,7 +3008,7 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: scc.append('builtins') if manager.options.verbosity >= 2: for id in scc: - manager.trace("Priorities for %s:" % id, + manager.trace(f"Priorities for {id}:", " ".join("%s:%d" % (x, graph[id].priorities[x]) for x in graph[id].dependencies if x in ascc and x in graph[id].priorities)) @@ -3059,19 +3058,19 @@ def process_graph(graph: Graph, manager: BuildManager) -> None: # (on some platforms). if oldest_in_scc < newest_in_deps: fresh = False - fresh_msg = "out of date by %.0f seconds" % (newest_in_deps - oldest_in_scc) + fresh_msg = f"out of date by {newest_in_deps - oldest_in_scc:.0f} seconds" else: fresh_msg = "fresh" elif undeps: - fresh_msg = "stale due to changed suppression (%s)" % " ".join(sorted(undeps)) + fresh_msg = f"stale due to changed suppression ({' '.join(sorted(undeps))})" elif stale_scc: fresh_msg = "inherently stale" if stale_scc != ascc: - fresh_msg += " (%s)" % " ".join(sorted(stale_scc)) + fresh_msg += f" ({' '.join(sorted(stale_scc))})" if stale_deps: - fresh_msg += " with stale deps (%s)" % " ".join(sorted(stale_deps)) + fresh_msg += f" with stale deps ({' '.join(sorted(stale_deps))})" else: - fresh_msg = "stale due to deps (%s)" % " ".join(sorted(stale_deps)) + fresh_msg = f"stale due to deps ({' '.join(sorted(stale_deps))})" # Initialize transitive_error for all SCC members from union # of transitive_error of dependencies. @@ -3371,7 +3370,7 @@ def topsort(data: Dict[T, Set[T]]) -> Iterable[Set[T]]: data = {item: (dep - ready) for item, dep in data.items() if item not in ready} - assert not data, "A cyclic dependency exists amongst %r" % data + assert not data, f"A cyclic dependency exists amongst {data!r}" def missing_stubs_file(cache_dir: str) -> str: @@ -3388,7 +3387,7 @@ def record_missing_stub_packages(cache_dir: str, missing_stub_packages: Set[str] if missing_stub_packages: with open(fnam, 'w') as f: for pkg in sorted(missing_stub_packages): - f.write('%s\n' % pkg) + f.write(f'{pkg}\n') else: if os.path.isfile(fnam): os.remove(fnam) diff --git a/mypy/checker.py b/mypy/checker.py index d0820e483d65..cff637dbb57a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -886,7 +886,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str]) self.msg.unimported_type_becomes_any("Return type", ret_type, fdef) for idx, arg_type in enumerate(fdef.type.arg_types): if has_any_from_unimported_type(arg_type): - prefix = f"Argument {idx + 1} to \"{fdef.name}\"" + prefix = f'Argument {idx + 1} to "{fdef.name}"' self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) check_for_explicit_any(fdef.type, self.options, self.is_typeshed_stub, self.msg, context=fdef) @@ -1918,9 +1918,7 @@ def check_final_enum(self, defn: ClassDef, base: TypeInfo) -> None: for sym in base.names.values(): if self.is_final_enum_value(sym): self.fail( - 'Cannot extend enum with existing members: "{}"'.format( - base.name, - ), + f'Cannot extend enum with existing members: "{base.name}"', defn, ) break @@ -2571,7 +2569,7 @@ def check_compatibility_super(self, lvalue: RefExpr, lvalue_type: Optional[Type] return self.check_subtype(compare_type, base_type, rvalue, message_registry.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, 'expression has type', - 'base class "%s" defined the type as' % base.name, + f'base class "{base.name}" defined the type as', code=codes.ASSIGNMENT) return True diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ed6fd73acfa5..9dfc0e2a6458 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -263,7 +263,7 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: result = self.object_type() else: if isinstance(node, PlaceholderNode): - assert False, 'PlaceholderNode %r leaked to checker' % node.fullname + assert False, f'PlaceholderNode {node.fullname!r} leaked to checker' # Unknown reference; use any type implicitly to avoid # generating extra type errors. result = AnyType(TypeOfAny.from_error) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 589a1fd6ac40..20b3716ea513 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -718,7 +718,7 @@ def check_mapping_str_interpolation(self, specifiers: List[ConversionSpecifier], self.chk.check_subtype(rep_type, expected_type, replacements, message_registry.INCOMPATIBLE_TYPES_IN_STR_INTERPOLATION, 'expression has type', - 'placeholder with key \'%s\' has type' % specifier.key, + f'placeholder with key \'{specifier.key}\' has type', code=codes.STRING_FORMATTING) if specifier.conv_type == 's': self.check_s_special_cases(expr, rep_type, expr) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a9ba8535a5d6..952c3f96f29a 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -211,10 +211,10 @@ def parse_config_file(options: Options, set_strict_flags: Callable[[], None], if 'mypy' not in parser: if filename or file_read not in defaults.SHARED_CONFIG_FILES: - print("%s: No [mypy] section in config file" % file_read, file=stderr) + print(f"{file_read}: No [mypy] section in config file", file=stderr) else: section = parser['mypy'] - prefix = '{}: [{}]: '.format(file_read, 'mypy') + prefix = f"{file_read}: [mypy]: " updates, report_dirs = parse_section( prefix, options, set_strict_flags, section, config_types, stderr) for k, v in updates.items(): @@ -322,7 +322,7 @@ def destructure_overrides(toml_data: Dict[str, Any]) -> Dict[str, Any]: for module in modules: module_overrides = override.copy() del module_overrides['module'] - old_config_name = 'mypy-%s' % module + old_config_name = f'mypy-{module}' if old_config_name not in result: result[old_config_name] = module_overrides else: @@ -447,7 +447,7 @@ def convert_to_boolean(value: Optional[Any]) -> bool: if not isinstance(value, str): value = str(value) if value.lower() not in configparser.RawConfigParser.BOOLEAN_STATES: - raise ValueError('Not a boolean: %s' % value) + raise ValueError(f'Not a boolean: {value}') return configparser.RawConfigParser.BOOLEAN_STATES[value.lower()] @@ -552,7 +552,7 @@ def get_config_module_names(filename: Optional[str], modules: List[str]) -> str: return '' if not is_toml(filename): - return ", ".join("[mypy-%s]" % module for module in modules) + return ", ".join(f"[mypy-{module}]" for module in modules) return "module = ['%s']" % ("', '".join(sorted(modules))) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 56ec804ad7a7..3ed85dca9750 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -273,7 +273,7 @@ def do_run(args: argparse.Namespace) -> None: response = request(args.status_file, 'run', version=__version__, args=args.flags) # If the daemon signals that a restart is necessary, do it if 'restart' in response: - print('Restarting: {}'.format(response['restart'])) + print(f"Restarting: {response['restart']}") restart_server(args, allow_sources=True) response = request(args.status_file, 'run', version=__version__, args=args.flags) @@ -300,7 +300,7 @@ def do_status(args: argparse.Namespace) -> None: if args.verbose or 'error' in response: show_stats(response) if 'error' in response: - fail("Daemon is stuck; consider %s kill" % sys.argv[0]) + fail(f"Daemon is stuck; consider {sys.argv[0]} kill") print("Daemon is up and running") @@ -311,7 +311,7 @@ def do_stop(args: argparse.Namespace) -> None: response = request(args.status_file, 'stop', timeout=5) if 'error' in response: show_stats(response) - fail("Daemon is stuck; consider %s kill" % sys.argv[0]) + fail(f"Daemon is stuck; consider {sys.argv[0]} kill") else: print("Daemon stopped") @@ -389,7 +389,7 @@ def check_output(response: Dict[str, Any], verbose: bool, try: out, err, status_code = response['out'], response['err'], response['status'] except KeyError: - fail("Response: %s" % str(response)) + fail(f"Response: {str(response)}") sys.stdout.write(out) sys.stdout.flush() sys.stderr.write(err) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index de03d6400525..3fbda6b1a7d8 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -264,7 +264,7 @@ def run_command(self, command: str, data: Dict[str, object]) -> Dict[str, object key = 'cmd_' + command method = getattr(self.__class__, key, None) if method is None: - return {'error': "Unrecognized command '%s'" % command} + return {'error': f"Unrecognized command '{command}'"} else: if command not in {'check', 'recheck', 'run'}: # Only the above commands use some error formatting. diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index 8a527afe5762..2b458c51e5a4 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -27,5 +27,5 @@ def receive(connection: IPCBase) -> Any: except Exception as e: raise OSError("Data received is not valid JSON") from e if not isinstance(data, dict): - raise OSError("Data received is not a dict (%s)" % str(type(data))) + raise OSError(f"Data received is not a dict ({type(data)})") return data diff --git a/mypy/fastparse.py b/mypy/fastparse.py index e4e8f4a7888d..242b6d260c1e 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -56,7 +56,7 @@ if sys.version_info >= (3, 8): import ast as ast3 assert 'kind' in ast3.Constant._fields, \ - "This 3.8.0 alpha (%s) is too old; 3.8.0a3 required" % sys.version.split()[0] + f"This 3.8.0 alpha ({sys.version.split()[0]}) is too old; 3.8.0a3 required" # TODO: Num, Str, Bytes, NameConstant, Ellipsis are deprecated in 3.8. # TODO: Index, ExtSlice are deprecated in 3.9. from ast import ( diff --git a/mypy/main.py b/mypy/main.py index 6b0238295314..f598194455c1 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -791,9 +791,9 @@ def add_invertible_flag(flag: str, description='Generate a report in the specified format.') for report_type in sorted(defaults.REPORTER_NAMES): if report_type not in {'memory-xml'}: - report_group.add_argument('--%s-report' % report_type.replace('_', '-'), + report_group.add_argument(f"--{report_type.replace('_', '-')}-report", metavar='DIR', - dest='special-opts:%s_report' % report_type) + dest=f'special-opts:{report_type}_report') other_group = parser.add_argument_group( title='Miscellaneous') @@ -918,7 +918,7 @@ def add_invertible_flag(flag: str, # Don't explicitly test if "config_file is not None" for this check. # This lets `--config-file=` (an empty string) be used to disable all config files. if config_file and not os.path.exists(config_file): - parser.error("Cannot find config file '%s'" % config_file) + parser.error(f"Cannot find config file '{config_file}'") options = Options() @@ -989,8 +989,7 @@ def set_strict_flags() -> None: invalid_codes = (enabled_codes | disabled_codes) - valid_error_codes if invalid_codes: - parser.error("Invalid error code(s): %s" % - ', '.join(sorted(invalid_codes))) + parser.error(f"Invalid error code(s): {', '.join(sorted(invalid_codes))}") options.disabled_error_codes |= {error_codes[code] for code in disabled_codes} options.enabled_error_codes |= {error_codes[code] for code in enabled_codes} @@ -1090,17 +1089,17 @@ def process_package_roots(fscache: Optional[FileSystemCache], package_root = [] for root in options.package_root: if os.path.isabs(root): - parser.error("Package root cannot be absolute: %r" % root) + parser.error(f"Package root cannot be absolute: {root!r}") drive, root = os.path.splitdrive(root) if drive and drive != current_drive: - parser.error("Package root must be on current drive: %r" % (drive + root)) + parser.error(f"Package root must be on current drive: {drive + root!r}") # Empty package root is always okay. if root: root = os.path.relpath(root) # Normalize the heck out of it. if not root.endswith(os.sep): root = root + os.sep if root.startswith(dotdotslash): - parser.error("Package root cannot be above current directory: %r" % root) + parser.error(f"Package root cannot be above current directory: {root!r}") if root in trivial_paths: root = '' package_root.append(root) @@ -1119,9 +1118,9 @@ def process_cache_map(parser: argparse.ArgumentParser, for i in range(0, n, 3): source, meta_file, data_file = special_opts.cache_map[i:i + 3] if source in options.cache_map: - parser.error("Duplicate --cache-map source %s)" % source) + parser.error(f"Duplicate --cache-map source {source})") if not source.endswith('.py') and not source.endswith('.pyi'): - parser.error("Invalid --cache-map source %s (triple[0] must be *.py[i])" % source) + parser.error(f"Invalid --cache-map source {source} (triple[0] must be *.py[i])") if not meta_file.endswith('.meta.json'): parser.error("Invalid --cache-map meta_file %s (triple[1] must be *.meta.json)" % meta_file) @@ -1140,7 +1139,7 @@ def maybe_write_junit_xml(td: float, serious: bool, messages: List[str], options def fail(msg: str, stderr: TextIO, options: Options) -> NoReturn: """Fail with a serious error.""" - stderr.write('%s\n' % msg) + stderr.write(f'{msg}\n') maybe_write_junit_xml(0.0, serious=True, messages=[msg], options=options) sys.exit(2) diff --git a/mypy/memprofile.py b/mypy/memprofile.py index 7494fed75750..ac49fd346abc 100644 --- a/mypy/memprofile.py +++ b/mypy/memprofile.py @@ -33,23 +33,23 @@ def collect_memory_stats() -> Tuple[Dict[str, int], n = type(obj).__name__ if hasattr(obj, '__dict__'): # Keep track of which class a particular __dict__ is associated with. - inferred[id(obj.__dict__)] = '%s (__dict__)' % n + inferred[id(obj.__dict__)] = f'{n} (__dict__)' if isinstance(obj, (Node, Type)): # type: ignore if hasattr(obj, '__dict__'): for x in obj.__dict__.values(): if isinstance(x, list): # Keep track of which node a list is associated with. - inferred[id(x)] = '%s (list)' % n + inferred[id(x)] = f'{n} (list)' if isinstance(x, tuple): # Keep track of which node a list is associated with. - inferred[id(x)] = '%s (tuple)' % n + inferred[id(x)] = f'{n} (tuple)' for k in get_class_descriptors(type(obj)): x = getattr(obj, k, None) if isinstance(x, list): - inferred[id(x)] = '%s (list)' % n + inferred[id(x)] = f'{n} (list)' if isinstance(x, tuple): - inferred[id(x)] = '%s (tuple)' % n + inferred[id(x)] = f'{n} (tuple)' freqs: Dict[str, int] = {} memuse: Dict[str, int] = {} diff --git a/mypy/messages.py b/mypy/messages.py index 801f84c2580b..70d79384c1a9 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -365,8 +365,7 @@ def unsupported_operand_types(self, if self.are_type_names_disabled(): msg = f'Unsupported operand types for {op} (likely involving Union)' else: - msg = 'Unsupported operand types for {} ({} and {})'.format( - op, left_str, right_str) + msg = f'Unsupported operand types for {op} ({left_str} and {right_str})' self.fail(msg, context, code=code) def unsupported_left_operand(self, op: str, typ: Type, @@ -374,8 +373,7 @@ def unsupported_left_operand(self, op: str, typ: Type, if self.are_type_names_disabled(): msg = f'Unsupported left operand type for {op} (some union)' else: - msg = 'Unsupported left operand type for {} ({})'.format( - op, format_type(typ)) + msg = f'Unsupported left operand type for {op} ({format_type(typ)})' self.fail(msg, context, code=codes.OPERATOR) def not_callable(self, typ: Type, context: Context) -> Type: @@ -707,7 +705,7 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str, arg_type: if not matches: matches = best_matches(name, not_matching_type_args) if matches: - msg += "; did you mean {}?".format(pretty_seq(matches[:3], "or")) + msg += f"; did you mean {pretty_seq(matches[:3], 'or')}?" self.fail(msg, context, code=codes.CALL_ARG) module = find_defining_module(self.modules, callee) if module: @@ -980,7 +978,7 @@ def too_many_string_formatting_arguments(self, context: Context) -> None: code=codes.STRING_FORMATTING) def unsupported_placeholder(self, placeholder: str, context: Context) -> None: - self.fail('Unsupported format character "%s"' % placeholder, context, + self.fail(f'Unsupported format character "{placeholder}"', context, code=codes.STRING_FORMATTING) def string_interpolation_with_star_and_key(self, context: Context) -> None: @@ -999,7 +997,7 @@ def requires_int_or_char(self, context: Context, context, code=codes.STRING_FORMATTING) def key_not_in_mapping(self, key: str, context: Context) -> None: - self.fail('Key "%s" not found in mapping' % key, context, + self.fail(f'Key "{key}" not found in mapping', context, code=codes.STRING_FORMATTING) def string_interpolation_mixing_key_and_non_keys(self, context: Context) -> None: @@ -1007,7 +1005,7 @@ def string_interpolation_mixing_key_and_non_keys(self, context: Context) -> None code=codes.STRING_FORMATTING) def cannot_determine_type(self, name: str, context: Context) -> None: - self.fail('Cannot determine type of "%s"' % name, context, code=codes.HAS_TYPE) + self.fail(f'Cannot determine type of "{name}"', context, code=codes.HAS_TYPE) def cannot_determine_type_in_base(self, name: str, base: str, context: Context) -> None: self.fail(f'Cannot determine type of "{name}" in base class "{base}"', context) @@ -1029,7 +1027,7 @@ def incompatible_conditional_function_def(self, defn: FuncDef) -> None: def cannot_instantiate_abstract_class(self, class_name: str, abstract_attributes: List[str], context: Context) -> None: - attrs = format_string_list(['"%s"' % a for a in abstract_attributes]) + attrs = format_string_list([f'"{a}"' for a in abstract_attributes]) self.fail('Cannot instantiate abstract class "%s" with abstract ' 'attribute%s %s' % (class_name, plural_s(abstract_attributes), attrs), @@ -1047,7 +1045,7 @@ def cant_assign_to_method(self, context: Context) -> None: code=codes.ASSIGNMENT) def cant_assign_to_classvar(self, name: str, context: Context) -> None: - self.fail('Cannot assign to class variable "%s" via instance' % name, context) + self.fail(f'Cannot assign to class variable "{name}" via instance', context) def final_cant_override_writable(self, name: str, ctx: Context) -> None: self.fail(f'Cannot override writable attribute "{name}" with a final one', ctx) @@ -1072,8 +1070,7 @@ def final_without_value(self, ctx: Context) -> None: def read_only_property(self, name: str, type: TypeInfo, context: Context) -> None: - self.fail('Property "{}" defined in "{}" is read-only'.format( - name, type.name), context) + self.fail(f'Property "{name}" defined in "{type.name}" is read-only', context) def incompatible_typevar_value(self, callee: CallableType, @@ -1142,8 +1139,7 @@ def operator_method_signatures_overlap( def forward_operator_not_callable( self, forward_method: str, context: Context) -> None: - self.fail('Forward operator "{}" is not callable'.format( - forward_method), context) + self.fail(f'Forward operator "{forward_method}" is not callable', context) def signatures_incompatible(self, method: str, other_method: str, context: Context) -> None: @@ -1322,8 +1318,7 @@ def disallowed_any_type(self, typ: Type, context: Context) -> None: self.fail(message, context) def incorrectly_returning_any(self, typ: Type, context: Context) -> None: - message = 'Returning Any from function declared to return {}'.format( - format_type(typ)) + message = f'Returning Any from function declared to return {format_type(typ)}' self.fail(message, context, code=codes.NO_ANY_RETURN) def incorrect__exit__return(self, context: Context) -> None: @@ -1481,8 +1476,7 @@ def report_protocol_problems(self, if conflict_types and (not is_subtype(subtype, erase_type(supertype)) or not subtype.type.defn.type_vars or not supertype.type.defn.type_vars): - self.note('Following member(s) of {} have ' - 'conflicts:'.format(format_type(subtype)), + self.note(f'Following member(s) of {format_type(subtype)} have conflicts:', context, code=code) for name, got, exp in conflict_types[:MAX_ITEMS]: @@ -1562,8 +1556,7 @@ def print_more(self, *, code: Optional[ErrorCode] = None) -> None: if len(conflicts) > max_items: - self.note('<{} more conflict(s) not shown>' - .format(len(conflicts) - max_items), + self.note(f'<{len(conflicts) - max_items} more conflict(s) not shown>', context, offset=offset, code=code) def try_report_long_tuple_assignment_error(self, @@ -1670,9 +1663,7 @@ def format_callable_args(arg_types: List[Type], arg_kinds: List[ArgKind], else: constructor = ARG_CONSTRUCTOR_NAMES[arg_kind] if arg_kind.is_star() or arg_name is None: - arg_strings.append("{}({})".format( - constructor, - format(arg_type))) + arg_strings.append(f"{constructor}({format(arg_type)})") else: arg_strings.append("{}({}, {})".format( constructor, @@ -1760,10 +1751,8 @@ def format_literal_value(typ: LiteralType) -> str: items = [] for (item_name, item_type) in typ.items.items(): modifier = '' if item_name in typ.required_keys else '?' - items.append('{!r}{}: {}'.format(item_name, - modifier, - format(item_type))) - s = 'TypedDict({{{}}})'.format(', '.join(items)) + items.append(f'{item_name!r}{modifier}: {format(item_type)}') + s = f"TypedDict({{{', '.join(items)}}})" return s elif isinstance(typ, LiteralType): return f'Literal[{format_literal_value(typ)}]' @@ -2027,7 +2016,7 @@ def [T <: int] f(self, x: int, y: T) -> None else: # For other TypeVarLikeTypes, just use the repr tvars.append(repr(tvar)) - s = '[{}] {}'.format(', '.join(tvars), s) + s = f"[{', '.join(tvars)}] {s}" return f'def {s}' @@ -2134,7 +2123,7 @@ def format_string_list(lst: List[str]) -> str: if len(lst) == 1: return lst[0] elif len(lst) <= 5: - return '{} and {}'.format(', '.join(lst[:-1]), lst[-1]) + return f"{', '.join(lst[:-1])} and {lst[-1]}" else: return '%s, ... and %s (%i methods suppressed)' % ( ', '.join(lst[:2]), lst[-1], len(lst) - 3) @@ -2143,9 +2132,9 @@ def format_string_list(lst: List[str]) -> str: def format_item_name_list(s: Iterable[str]) -> str: lst = list(s) if len(lst) <= 5: - return '(' + ', '.join(['"%s"' % name for name in lst]) + ')' + return '(' + ', '.join([f'"{name}"' for name in lst]) + ')' else: - return '(' + ', '.join(['"%s"' % name for name in lst[:5]]) + ', ...)' + return '(' + ', '.join([f'"{name}"' for name in lst[:5]]) + ', ...)' def callable_name(type: FunctionLike) -> Optional[str]: @@ -2263,4 +2252,4 @@ def format_key_list(keys: List[str], *, short: bool = False) -> str: elif len(keys) == 1: return f'{td}key {formatted_keys[0]}' else: - return '{}keys ({})'.format(td, ', '.join(formatted_keys)) + return f"{td}keys ({', '.join(formatted_keys)})" diff --git a/mypy/moduleinspect.py b/mypy/moduleinspect.py index ebcbb25ea5e5..2b2068e0b7c5 100644 --- a/mypy/moduleinspect.py +++ b/mypy/moduleinspect.py @@ -138,7 +138,7 @@ def get_package_properties(self, package_id: str) -> ModuleProperties: if res is None: # The process died; recover and report error. self._start() - raise InspectError('Process died when importing %r' % package_id) + raise InspectError(f'Process died when importing {package_id!r}') if isinstance(res, str): # Error importing module if self.counter > 0: diff --git a/mypy/nodes.py b/mypy/nodes.py index c0e53ea9367c..4ffa3116a118 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -483,7 +483,7 @@ def deserialize(cls, data: JsonDict) -> 'ImportedName': assert False, "ImportedName should never be serialized" def __str__(self) -> str: - return 'ImportedName(%s)' % self.target_fullname + return f'ImportedName({self.target_fullname})' FUNCBASE_FLAGS: Final = ["is_property", "is_class", "is_static", "is_final"] @@ -2778,7 +2778,7 @@ def __getitem__(self, name: str) -> 'SymbolTableNode': raise KeyError(name) def __repr__(self) -> str: - return '' % self.fullname + return f'' def __bool__(self) -> bool: # We defined this here instead of just overriding it in @@ -2859,8 +2859,7 @@ def type_str(typ: 'mypy.types.Type') -> str: head = 'TypeInfo' + str_conv.format_id(self) if self.bases: - base = 'Bases({})'.format(', '.join(type_str(base) - for base in self.bases)) + base = f"Bases({', '.join(type_str(base) for base in self.bases)})" mro = 'Mro({})'.format(', '.join(item.fullname + str_conv.format_id(item) for item in self.mro)) names = [] diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 5827a21e8ccf..62b4c89bd674 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -195,7 +195,7 @@ def transform(self) -> None: if existing_method is not None and not existing_method.plugin_generated: assert existing_method.node ctx.api.fail( - 'You may not have a custom %s method when order=True' % method_name, + f'You may not have a custom {method_name} method when order=True', existing_method.node, ) diff --git a/mypy/plugins/singledispatch.py b/mypy/plugins/singledispatch.py index 0eb855d3a315..d6150836c562 100644 --- a/mypy/plugins/singledispatch.py +++ b/mypy/plugins/singledispatch.py @@ -48,9 +48,7 @@ def get_first_arg(args: List[List[T]]) -> Optional[T]: REGISTER_RETURN_CLASS: Final = '_SingleDispatchRegisterCallable' -REGISTER_CALLABLE_CALL_METHOD: Final = 'functools.{}.__call__'.format( - REGISTER_RETURN_CLASS -) +REGISTER_CALLABLE_CALL_METHOD: Final = f'functools.{REGISTER_RETURN_CLASS}.__call__' def make_fake_register_class_instance(api: CheckerPluginInterface, type_args: Sequence[Type] diff --git a/mypy/report.py b/mypy/report.py index 8d3465f3c1f0..28fa5c274b74 100644 --- a/mypy/report.py +++ b/mypy/report.py @@ -181,8 +181,7 @@ def on_finish(self) -> None: with open(os.path.join(self.output_dir, "linecount.txt"), "w") as f: f.write("{:7} {:7} {:6} {:6} total\n".format(*total_counts)) for c, p in counts: - f.write('{:7} {:7} {:6} {:6} {}\n'.format( - c[0], c[1], c[2], c[3], p)) + f.write(f'{c[0]:7} {c[1]:7} {c[2]:6} {c[3]:6} {p}\n') register_reporter('linecount', LineCountReporter) @@ -490,7 +489,7 @@ def on_file(self, # Assumes a layout similar to what XmlReporter uses. xslt_path = os.path.relpath('mypy-html.xslt', path) transform_pi = etree.ProcessingInstruction('xml-stylesheet', - 'type="text/xsl" href="%s"' % pathname2url(xslt_path)) + f'type="text/xsl" href="{pathname2url(xslt_path)}"') root.addprevious(transform_pi) self.schema.assertValid(doc) @@ -526,7 +525,7 @@ def on_finish(self) -> None: total=str(file_info.total())) xslt_path = os.path.relpath('mypy-html.xslt', '.') transform_pi = etree.ProcessingInstruction('xml-stylesheet', - 'type="text/xsl" href="%s"' % pathname2url(xslt_path)) + f'type="text/xsl" href="{pathname2url(xslt_path)}"') root.addprevious(transform_pi) self.schema.assertValid(doc) diff --git a/mypy/semanal.py b/mypy/semanal.py index eb0411f0afc8..a9445a9c8748 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -366,7 +366,7 @@ def prepare_builtins_namespace(self, file_node: MypyFile) -> None: for name in CORE_BUILTIN_CLASSES: cdef = ClassDef(name, Block([])) # Dummy ClassDef, will be replaced later info = TypeInfo(SymbolTable(), cdef, 'builtins') - info._fullname = 'builtins.%s' % name + info._fullname = f'builtins.{name}' names[name] = SymbolTableNode(GDEF, info) bool_info = names['bool'].node @@ -388,7 +388,7 @@ def prepare_builtins_namespace(self, file_node: MypyFile) -> None: for name, typ in special_var_types: v = Var(name, typ) - v._fullname = 'builtins.%s' % name + v._fullname = f'builtins.{name}' file_node.names[name] = SymbolTableNode(GDEF, v) # @@ -1107,7 +1107,7 @@ def visit_decorator(self, dec: Decorator) -> None: def check_decorated_function_is_method(self, decorator: str, context: Context) -> None: if not self.type or self.is_func_scope(): - self.fail('"%s" used with a non-method' % decorator, context) + self.fail(f'"{decorator}" used with a non-method', context) # # Classes @@ -1722,7 +1722,7 @@ def verify_base_classes(self, defn: ClassDef) -> bool: return False dup = find_duplicate(info.direct_base_classes()) if dup: - self.fail('Duplicate base class "%s"' % dup.name, defn, blocker=True) + self.fail(f'Duplicate base class "{dup.name}"', defn, blocker=True) return False return not cycle @@ -1749,7 +1749,7 @@ def analyze_metaclass(self, defn: ClassDef) -> None: elif isinstance(defn.metaclass, MemberExpr): metaclass_name = get_member_expr_fullname(defn.metaclass) if metaclass_name is None: - self.fail('Dynamic metaclass not supported for "%s"' % defn.name, defn.metaclass) + self.fail(f'Dynamic metaclass not supported for "{defn.name}"', defn.metaclass) return sym = self.lookup_qualified(metaclass_name, defn.metaclass) if sym is None: @@ -1766,7 +1766,7 @@ def analyze_metaclass(self, defn: ClassDef) -> None: self.defer(defn) return if not isinstance(sym.node, TypeInfo) or sym.node.tuple_type is not None: - self.fail('Invalid metaclass "%s"' % metaclass_name, defn.metaclass) + self.fail(f'Invalid metaclass "{metaclass_name}"', defn.metaclass) return if not sym.node.is_metaclass(): self.fail('Metaclasses not inheriting from "type" are not supported', @@ -1788,7 +1788,7 @@ def analyze_metaclass(self, defn: ClassDef) -> None: # Inconsistency may happen due to multiple baseclasses even in classes that # do not declare explicit metaclass, but it's harder to catch at this stage if defn.metaclass is not None: - self.fail('Inconsistent metaclass structure for "%s"' % defn.name, defn) + self.fail(f'Inconsistent metaclass structure for "{defn.name}"', defn) else: if defn.info.metaclass_type.type.has_base('enum.EnumMeta'): defn.info.is_enum = True @@ -1953,7 +1953,7 @@ def report_missing_module_attribute( alternatives = set(module.names.keys()).difference({source_id}) matches = best_matches(source_id, alternatives)[:3] if matches: - suggestion = "; maybe {}?".format(pretty_seq(matches, "or")) + suggestion = f"; maybe {pretty_seq(matches, 'or')}?" message += f"{suggestion}" self.fail(message, context, code=codes.ATTR_DEFINED) self.add_unknown_imported_symbol( @@ -3120,7 +3120,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> bool: # Also give error for another type variable with the same name. (isinstance(existing.node, TypeVarExpr) and existing.node is call.analyzed)): - self.fail('Cannot redefine "%s" as a type variable' % name, s) + self.fail(f'Cannot redefine "{name}" as a type variable', s) return False if self.options.disallow_any_unimported: diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 251767bef3e0..0f09a4bf9457 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -119,11 +119,11 @@ def parse_enum_call_args(self, call: CallExpr, """ args = call.args if not all([arg_kind in [ARG_POS, ARG_NAMED] for arg_kind in call.arg_kinds]): - return self.fail_enum_call_arg("Unexpected arguments to %s()" % class_name, call) + return self.fail_enum_call_arg(f"Unexpected arguments to {class_name}()", call) if len(args) < 2: - return self.fail_enum_call_arg("Too few arguments for %s()" % class_name, call) + return self.fail_enum_call_arg(f"Too few arguments for {class_name}()", call) if len(args) > 6: - return self.fail_enum_call_arg("Too many arguments for %s()" % class_name, call) + return self.fail_enum_call_arg(f"Too many arguments for {class_name}()", call) valid_name = [None, 'value', 'names', 'module', 'qualname', 'type', 'start'] for arg_name in call.arg_names: if arg_name not in valid_name: @@ -140,7 +140,7 @@ def parse_enum_call_args(self, call: CallExpr, names = args[1] if not isinstance(value, (StrExpr, UnicodeExpr)): return self.fail_enum_call_arg( - "%s() expects a string literal as the first argument" % class_name, call) + f"{class_name}() expects a string literal as the first argument", call) items = [] values: List[Optional[Expression]] = [] if isinstance(names, (StrExpr, UnicodeExpr)): @@ -171,7 +171,7 @@ def parse_enum_call_args(self, call: CallExpr, for key, value in names.items: if not isinstance(key, (StrExpr, UnicodeExpr)): return self.fail_enum_call_arg( - "%s() with dict literal requires string literals" % class_name, call) + f"{class_name}() with dict literal requires string literals", call) items.append(key.value) values.append(value) elif isinstance(args[1], RefExpr) and isinstance(args[1].node, Var): @@ -198,7 +198,7 @@ def parse_enum_call_args(self, call: CallExpr, class_name, call) if len(items) == 0: - return self.fail_enum_call_arg("%s() needs at least one item" % class_name, call) + return self.fail_enum_call_arg(f"{class_name}() needs at least one item", call) if not values: values = [None] * len(items) assert len(items) == len(values) diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index 047df3d85b83..948c5b36052f 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -127,7 +127,7 @@ def analyze_newtype_declaration(self, # Give a better error message than generic "Name already defined". if (existing and not isinstance(existing.node, PlaceholderNode) and not s.rvalue.analyzed): - self.fail('Cannot redefine "%s" as a NewType' % name, s) + self.fail(f'Cannot redefine "{name}" as a NewType', s) # This dummy NewTypeExpr marks the call as sufficiently analyzed; it will be # overwritten later with a fully complete NewTypeExpr if there are no other diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 3b384bdec1a3..4087f477c597 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -86,7 +86,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> Tuple[bool, Optional[Typ typeddict_bases.append(expr) else: assert isinstance(expr.node, TypeInfo) - self.fail('Duplicate base class "%s"' % expr.node.name, defn) + self.fail(f'Duplicate base class "{expr.node.name}"', defn) else: self.fail("All bases of a new TypedDict must be TypedDict types", defn) diff --git a/mypy/server/deps.py b/mypy/server/deps.py index b70a43db2540..f339344e79b5 100644 --- a/mypy/server/deps.py +++ b/mypy/server/deps.py @@ -1040,4 +1040,4 @@ def dump_all_dependencies(modules: Dict[str, MypyFile], for trigger, targets in sorted(all_deps.items(), key=lambda x: x[0]): print(trigger) for target in sorted(targets): - print(' %s' % target) + print(f' {target}') diff --git a/mypy/server/mergecheck.py b/mypy/server/mergecheck.py index 2c28242251e9..41d19f60f436 100644 --- a/mypy/server/mergecheck.py +++ b/mypy/server/mergecheck.py @@ -70,14 +70,14 @@ def path_to_str(path: List[Tuple[object, object]]) -> str: for attr, obj in path: t = type(obj).__name__ if t in ('dict', 'tuple', 'SymbolTable', 'list'): - result += '[%s]' % repr(attr) + result += f'[{repr(attr)}]' else: if isinstance(obj, Var): result += f'.{attr}({t}:{obj.name})' elif t in ('BuildManager', 'FineGrainedBuildManager'): # Omit class name for some classes that aren't part of a class # hierarchy since there isn't much ambiguity. - result += '.%s' % attr + result += f'.{attr}' else: result += f'.{attr}({t})' return result diff --git a/mypy/server/trigger.py b/mypy/server/trigger.py index 3b9b02d20c81..bfd542a40537 100644 --- a/mypy/server/trigger.py +++ b/mypy/server/trigger.py @@ -9,7 +9,7 @@ def make_trigger(name: str) -> str: - return '<%s>' % name + return f'<{name}>' def make_wildcard_trigger(module: str) -> str: diff --git a/mypy/server/update.py b/mypy/server/update.py index 233e989a0e36..e50bb1d158a2 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -236,7 +236,7 @@ def update(self, if self.blocking_error: # Handle blocking errors first. We'll exit as soon as we find a # module that still has blocking errors. - self.manager.log_fine_grained('existing blocker: %s' % self.blocking_error[0]) + self.manager.log_fine_grained(f'existing blocker: {self.blocking_error[0]}') changed_modules = dedupe_modules([self.blocking_error] + changed_modules) blocking_error = self.blocking_error[0] self.blocking_error = None @@ -322,7 +322,7 @@ def update_one(self, and next_id not in self.previous_modules and next_id not in initial_set): self.manager.log_fine_grained( - 'skip %r (module with blocking error not in import graph)' % next_id) + f'skip {next_id!r} (module with blocking error not in import graph)') return changed_modules, (next_id, next_path), None result = self.update_module(next_id, next_path, next_id in removed_set) @@ -333,8 +333,7 @@ def update_one(self, t1 = time.time() self.manager.log_fine_grained( - "update once: {} in {:.3f}s - {} left".format( - next_id, t1 - t0, len(changed_modules))) + f"update once: {next_id} in {t1 - t0:.3f}s - {len(changed_modules)} left") return changed_modules, (next_id, next_path), blocker_messages @@ -362,7 +361,7 @@ def update_module(self, - Module which was actually processed as (id, path) tuple - If there was a blocking error, the error messages from it """ - self.manager.log_fine_grained('--- update single %r ---' % module) + self.manager.log_fine_grained(f'--- update single {module!r} ---') self.updated_modules.append(module) # builtins and friends could potentially get triggered because @@ -406,7 +405,7 @@ def update_module(self, if is_verbose(self.manager): filtered = [trigger for trigger in triggered if not trigger.endswith('__>')] - self.manager.log_fine_grained('triggered: %r' % sorted(filtered)) + self.manager.log_fine_grained(f'triggered: {sorted(filtered)!r}') self.triggered.extend(triggered | self.previous_targets_with_errors) if module in graph: graph[module].update_fine_grained_deps(self.deps) @@ -534,7 +533,7 @@ def update_module_isolated(module: str, Returns a named tuple describing the result (see above for details). """ if module not in graph: - manager.log_fine_grained('new module %r' % module) + manager.log_fine_grained(f'new module {module!r}') if not manager.fscache.isfile(path) or force_removed: delete_module(module, path, graph, manager) @@ -592,7 +591,7 @@ def restore(ids: List[str]) -> None: remaining_modules = changed_modules # The remaining modules haven't been processed yet so drop them. restore([id for id, _ in remaining_modules]) - manager.log_fine_grained('--> %r (newly imported)' % module) + manager.log_fine_grained(f'--> {module!r} (newly imported)') else: remaining_modules = [] @@ -662,7 +661,7 @@ def delete_module(module_id: str, path: str, graph: Graph, manager: BuildManager) -> None: - manager.log_fine_grained('delete module %r' % module_id) + manager.log_fine_grained(f'delete module {module_id!r}') # TODO: Remove deps for the module (this only affects memory use, not correctness) if module_id in graph: del graph[module_id] @@ -815,7 +814,7 @@ def propagate_changes_using_dependencies( if id is not None and id not in up_to_date_modules: if id not in todo: todo[id] = set() - manager.log_fine_grained('process target with error: %s' % target) + manager.log_fine_grained(f'process target with error: {target}') more_nodes, _ = lookup_target(manager, target) todo[id].update(more_nodes) triggered = set() @@ -835,7 +834,7 @@ def propagate_changes_using_dependencies( up_to_date_modules = set() targets_with_errors = set() if is_verbose(manager): - manager.log_fine_grained('triggered: %r' % list(triggered)) + manager.log_fine_grained(f'triggered: {list(triggered)!r}') return remaining_modules @@ -891,7 +890,7 @@ def find_targets_recursive( if module_id not in result: result[module_id] = set() - manager.log_fine_grained('process: %s' % target) + manager.log_fine_grained(f'process: {target}') deferred, stale_proto = lookup_target(manager, target) if stale_proto: stale_protos.add(stale_proto) @@ -1040,7 +1039,7 @@ def lookup_target(manager: BuildManager, """ def not_found() -> None: manager.log_fine_grained( - "Can't find matching target for %s (stale dependency?)" % target) + f"Can't find matching target for {target} (stale dependency?)") modules = manager.modules items = split_target(modules, target) diff --git a/mypy/strconv.py b/mypy/strconv.py index 13f3e04ef712..8d6cf92d8f2a 100644 --- a/mypy/strconv.py +++ b/mypy/strconv.py @@ -112,7 +112,7 @@ def visit_import(self, o: 'mypy.nodes.Import') -> str: a.append(f'{id} : {as_id}') else: a.append(id) - return 'Import:{}({})'.format(o.line, ', '.join(a)) + return f"Import:{o.line}({', '.join(a)})" def visit_import_from(self, o: 'mypy.nodes.ImportFrom') -> str: a = [] @@ -121,10 +121,10 @@ def visit_import_from(self, o: 'mypy.nodes.ImportFrom') -> str: a.append(f'{name} : {as_name}') else: a.append(name) - return 'ImportFrom:{}({}, [{}])'.format(o.line, "." * o.relative + o.id, ', '.join(a)) + return f"ImportFrom:{o.line}({'.' * o.relative + o.id}, [{', '.join(a)}])" def visit_import_all(self, o: 'mypy.nodes.ImportAll') -> str: - return 'ImportAll:{}({})'.format(o.line, "." * o.relative + o.id) + return f"ImportAll:{o.line}({'.' * o.relative + o.id})" # Definitions @@ -418,7 +418,7 @@ def visit_call_expr(self, o: 'mypy.nodes.CallExpr') -> str: elif kind == mypy.nodes.ARG_STAR2: extra.append(('DictVarArg', [o.args[i]])) else: - raise RuntimeError("unknown kind %s" % kind) + raise RuntimeError(f"unknown kind {kind}") a: List[Any] = [o.callee, ("Args", args)] return self.dump(a + extra, o) @@ -512,23 +512,19 @@ def visit_type_alias_expr(self, o: 'mypy.nodes.TypeAliasExpr') -> str: return f'TypeAliasExpr({o.type})' def visit_namedtuple_expr(self, o: 'mypy.nodes.NamedTupleExpr') -> str: - return 'NamedTupleExpr:{}({}, {})'.format(o.line, - o.info.name, - o.info.tuple_type) + return f'NamedTupleExpr:{o.line}({o.info.name}, {o.info.tuple_type})' def visit_enum_call_expr(self, o: 'mypy.nodes.EnumCallExpr') -> str: return f'EnumCallExpr:{o.line}({o.info.name}, {o.items})' def visit_typeddict_expr(self, o: 'mypy.nodes.TypedDictExpr') -> str: - return 'TypedDictExpr:{}({})'.format(o.line, - o.info.name) + return f'TypedDictExpr:{o.line}({o.info.name})' def visit__promote_expr(self, o: 'mypy.nodes.PromoteExpr') -> str: return f'PromoteExpr:{o.line}({o.type})' def visit_newtype_expr(self, o: 'mypy.nodes.NewTypeExpr') -> str: - return 'NewTypeExpr:{}({}, {})'.format(o.line, o.name, - self.dump([o.old_type], o)) + return f'NewTypeExpr:{o.line}({o.name}, {self.dump([o.old_type], o)})' def visit_lambda_expr(self, o: 'mypy.nodes.LambdaExpr') -> str: a = self.func_helper(o) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index a596ddb4f78d..175b6f9f432c 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -311,8 +311,8 @@ def build_signature(positional: Sequence[str], if arg.startswith('*'): args.append(arg) else: - args.append('%s=...' % arg) - sig = '(%s)' % ', '.join(args) + args.append(f'{arg}=...') + sig = f"({', '.join(args)})" # Ad-hoc fixes. sig = sig.replace('(self)', '') return sig diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 41fc58a2b0fc..2d3e8e8f48ef 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -309,8 +309,8 @@ def visit_call_expr(self, node: CallExpr) -> str: elif kind == ARG_NAMED: args.append(f'{name}={arg.accept(self)}') else: - raise ValueError("Unknown argument kind %s in call" % kind) - return "{}({})".format(callee, ", ".join(args)) + raise ValueError(f"Unknown argument kind {kind} in call") + return f"{callee}({', '.join(args)})" def visit_name_expr(self, node: NameExpr) -> str: self.stubgen.import_tracker.require_name(node.name) @@ -339,7 +339,7 @@ def visit_tuple_expr(self, node: TupleExpr) -> str: return ", ".join(n.accept(self) for n in node.items) def visit_list_expr(self, node: ListExpr) -> str: - return "[{}]".format(", ".join(n.accept(self) for n in node.items)) + return f"[{', '.join(n.accept(self) for n in node.items)}]" def visit_ellipsis(self, node: EllipsisExpr) -> str: return "..." @@ -454,7 +454,7 @@ def import_lines(self) -> List[str]: # Now generate all the from ... import ... lines collected in module_map for module, names in sorted(module_map.items()): - result.append("from {} import {}\n".format(module, ', '.join(sorted(names)))) + result.append(f"from {module} import {', '.join(sorted(names))}\n") return result @@ -589,7 +589,7 @@ def visit_mypy_file(self, o: MypyFile) -> None: self.add('\n') self.add('# Names in __all__ with no definition:\n') for name in sorted(undefined_names): - self.add('# %s\n' % name) + self.add(f'# {name}\n') def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None: """@property with setters and getters, or @overload chain""" @@ -635,7 +635,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, for s in self._decorators: self.add(s) self.clear_decorators() - self.add("{}{}def {}(".format(self._indent, 'async ' if o.is_coroutine else '', o.name)) + self.add(f"{self._indent}{'async ' if o.is_coroutine else ''}def {o.name}(") self.record_name(o.name) args: List[str] = [] for i, arg_ in enumerate(o.arguments): @@ -799,7 +799,7 @@ def process_member_expr_decorator(self, expr: MemberExpr, context: Decorator) -> is_abstract = False is_overload = False if expr.name == 'setter' and isinstance(expr.expr, NameExpr): - self.add_decorator('%s.setter' % expr.expr.name) + self.add_decorator(f'{expr.expr.name}.setter') elif (isinstance(expr.expr, NameExpr) and (expr.expr.name == 'abc' or self.import_tracker.reverse_alias.get(expr.expr.name) == 'abc') and @@ -835,7 +835,7 @@ def process_member_expr_decorator(self, expr: MemberExpr, context: Decorator) -> self.import_tracker.reverse_alias.get(expr.expr.name) in TYPING_MODULE_NAMES) and expr.name == 'overload'): self.import_tracker.require_name(expr.expr.name) - self.add_decorator('{}.{}'.format(expr.expr.name, 'overload')) + self.add_decorator(f"{expr.expr.name}.overload") is_overload = True return is_abstract, is_overload @@ -865,7 +865,7 @@ def visit_class_def(self, o: ClassDef) -> None: base_types.append(type_str) self.add_typing_import('Protocol') if base_types: - self.add('(%s)' % ', '.join(base_types)) + self.add(f"({', '.join(base_types)})") self.add(':\n') n = len(self._output) self._indent += ' ' @@ -1036,7 +1036,7 @@ def visit_if_stmt(self, o: IfStmt) -> None: super().visit_if_stmt(o) def visit_import_all(self, o: ImportAll) -> None: - self.add_import_line('from {}{} import *\n'.format('.' * o.relative, o.id)) + self.add_import_line(f"from {'.' * o.relative}{o.id} import *\n") def visit_import_from(self, o: ImportFrom) -> None: exported_names: Set[str] = set() @@ -1227,7 +1227,7 @@ def get_str_type_of_node(self, rvalue: Expression, if can_infer_optional and \ isinstance(rvalue, NameExpr) and rvalue.name == 'None': self.add_typing_import('Incomplete') - return '{} | None'.format(self.typing_name('Incomplete')) + return f"{self.typing_name('Incomplete')} | None" if can_be_any: self.add_typing_import('Incomplete') return self.typing_name('Incomplete') @@ -1492,7 +1492,7 @@ def parse_source_file(mod: StubSource, mypy_options: MypyOptions) -> None: if errors.is_blockers(): # Syntax error! for m in errors.new_messages(): - sys.stderr.write('%s\n' % m) + sys.stderr.write(f'{m}\n') sys.exit(1) @@ -1504,7 +1504,7 @@ def generate_asts_for_modules(py_modules: List[StubSource], if not py_modules: return # Nothing to do here, but there may be C modules if verbose: - print('Processing %d files...' % len(py_modules)) + print(f'Processing {len(py_modules)} files...') if parse_only: for mod in py_modules: parse_source_file(mod, mypy_options) @@ -1557,7 +1557,7 @@ def collect_docs_signatures(doc_dir: str) -> Tuple[Dict[str, str], Dict[str, str """ all_sigs: List[Sig] = [] all_class_sigs: List[Sig] = [] - for path in glob.glob('%s/*.rst' % doc_dir): + for path in glob.glob(f'{doc_dir}/*.rst'): with open(path) as f: loc_sigs, loc_class_sigs = parse_all_signatures(f.readlines()) all_sigs += loc_sigs @@ -1610,9 +1610,9 @@ def generate_stubs(options: Options) -> None: if not options.quiet and num_modules > 0: print('Processed %d modules' % num_modules) if len(files) == 1: - print('Generated %s' % files[0]) + print(f'Generated {files[0]}') else: - print('Generated files under %s' % common_dir_prefix(files) + os.sep) + print(f'Generated files under {common_dir_prefix(files)}' + os.sep) HEADER = """%(prog)s [-h] [--py2] [more options, see -h] diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index e9fa0ef62bb2..682ed418ffc7 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -47,7 +47,7 @@ def generate_stub_for_c_module(module_name: str, will be overwritten. """ module = importlib.import_module(module_name) - assert is_c_module(module), '%s is not a C module' % module_name + assert is_c_module(module), f'{module_name} is not a C module' subdir = os.path.dirname(target) if subdir and not os.path.isdir(subdir): os.makedirs(subdir) @@ -90,7 +90,7 @@ def generate_stub_for_c_module(module_name: str, output = add_typing_import(output) with open(target, 'w') as file: for line in output: - file.write('%s\n' % line) + file.write(f'{line}\n') def add_typing_import(output: List[str]) -> List[str]: @@ -100,7 +100,7 @@ def add_typing_import(output: List[str]) -> List[str]: if any(re.search(r'\b%s\b' % name, line) for line in output): names.append(name) if names: - return ['from typing import %s' % ', '.join(names), ''] + output + return [f"from typing import {', '.join(names)}", ''] + output else: return output[:] @@ -405,19 +405,19 @@ def generate_c_type_stub(module: ModuleType, output.append('') output.append(' ' + line) for line in static_properties: - output.append(' %s' % line) + output.append(f' {line}') for line in rw_properties: - output.append(' %s' % line) + output.append(f' {line}') for line in methods: - output.append(' %s' % line) + output.append(f' {line}') for line in ro_properties: - output.append(' %s' % line) + output.append(f' {line}') else: output.append(f'class {class_name}{bases_str}: ...') def get_type_fullname(typ: type) -> str: - return '{}.{}'.format(typ.__module__, getattr(typ, '__qualname__', typ.__name__)) + return f"{typ.__module__}.{getattr(typ, '__qualname__', typ.__name__)}" def method_name_sort_key(name: str) -> Tuple[int, str]: diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 8ed73cab203b..55f8c0b29345 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -54,10 +54,10 @@ def walk_packages(inspect: ModuleInspect, """ for package_name in packages: if package_name in NOT_IMPORTABLE_MODULES: - print('%s: Skipped (blacklisted)' % package_name) + print(f'{package_name}: Skipped (blacklisted)') continue if verbose: - print('Trying to import %r for runtime introspection' % package_name) + print(f'Trying to import {package_name!r} for runtime introspection') try: prop = inspect.get_package_properties(package_name) except InspectError: @@ -144,7 +144,7 @@ def find_module_path_and_all_py3(inspect: ModuleInspect, # TODO: Support custom interpreters. if verbose: - print('Trying to import %r for runtime introspection' % module) + print(f'Trying to import {module!r} for runtime introspection') try: mod = inspect.get_package_properties(module) except InspectError as e: @@ -166,7 +166,7 @@ def generate_guarded(mod: str, target: str, Optionally report success. """ if verbose: - print('Processing %s' % mod) + print(f'Processing {mod}') try: yield except Exception as e: @@ -177,7 +177,7 @@ def generate_guarded(mod: str, target: str, print("Stub generation failed for", mod, file=sys.stderr) else: if verbose: - print('Created %s' % target) + print(f'Created {target}') PY2_MODULES = {'cStringIO', 'urlparse', 'collections.UserDict'} diff --git a/mypy/suggestions.py b/mypy/suggestions.py index fbbf00118e82..d311d0edde63 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -485,7 +485,7 @@ def format_args(self, if name: arg = f"{name}={arg}" args.append(arg) - return "(%s)" % (", ".join(args)) + return f"({', '.join(args)})" def find_node(self, key: str) -> Tuple[str, str, FuncDef]: """From a target name, return module/target names and the func def. @@ -518,10 +518,10 @@ def find_node(self, key: str) -> Tuple[str, str, FuncDef]: if isinstance(node, Decorator): node = self.extract_from_decorator(node) if not node: - raise SuggestionFailure("Object %s is a decorator we can't handle" % key) + raise SuggestionFailure(f"Object {key} is a decorator we can't handle") if not isinstance(node, FuncDef): - raise SuggestionFailure("Object %s is not a function" % key) + raise SuggestionFailure(f"Object {key} is not a function") return modname, tail, node @@ -686,10 +686,7 @@ def pyannotate_signature( def format_signature(self, sig: PyAnnotateSignature) -> str: """Format a callable type in a way suitable as an annotation... kind of""" - return "({}) -> {}".format( - ", ".join(sig['arg_types']), - sig['return_type'] - ) + return f"({', '.join(sig['arg_types'])}) -> {sig['return_type']}" def format_type(self, cur_module: Optional[str], typ: Type) -> str: if self.use_fixme and isinstance(get_proper_type(typ), AnyType): @@ -843,7 +840,7 @@ def visit_callable_type(self, t: CallableType) -> str: # other thing, and I suspect this will produce more better # results than falling back to `...` args = [typ.accept(self) for typ in t.arg_types] - arg_str = "[{}]".format(", ".join(args)) + arg_str = f"[{', '.join(args)}]" return f"Callable[{arg_str}, {t.ret_type.accept(self)}]" diff --git a/mypy/test/data.py b/mypy/test/data.py index 9f1c6a1aa24c..de7e38693f23 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -162,13 +162,11 @@ def parse_test_case(case: 'DataDrivenTestCase') -> None: triggered = item.data else: raise ValueError( - 'Invalid section header {} in {} at line {}'.format( - item.id, case.file, item.line)) + f'Invalid section header {item.id} in {case.file} at line {item.line}') if out_section_missing: raise ValueError( - '{}, line {}: Required output section not found'.format( - case.file, first_item.line)) + f'{case.file}, line {first_item.line}: Required output section not found') for passnum in stale_modules.keys(): if passnum not in rechecked_modules: @@ -500,8 +498,7 @@ def expand_errors(input: List[str], output: List[str], fnam: str) -> None: output.append( f'{fnam}:{i + 1}: {severity}: {message}') else: - output.append('{}:{}:{}: {}: {}'.format( - fnam, i + 1, col, severity, message)) + output.append(f'{fnam}:{i + 1}:{col}: {severity}: {message}') def fix_win_path(line: str) -> str: diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 2f74e543d469..f0dc4bc6a671 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -346,7 +346,7 @@ def parse_module(self, cache = FindModuleCache(search_paths, fscache=None, options=None) for module_name in module_names.split(' '): path = cache.find_module(module_name) - assert isinstance(path, str), "Can't find ad hoc case file: %s" % module_name + assert isinstance(path, str), f"Can't find ad hoc case file: {module_name}" with open(path, encoding='utf8') as f: program_text = f.read() out.append((module_name, path, program_text)) diff --git a/mypy/test/testdeps.py b/mypy/test/testdeps.py index d3d184be7f01..7446d44339a0 100644 --- a/mypy/test/testdeps.py +++ b/mypy/test/testdeps.py @@ -67,15 +67,14 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: if source.startswith((' {', '.join(sorted(targets))}" # Clean up output a bit line = line.replace('__main__', 'm') a.append(line) assert_string_arrays_equal( testcase.output, a, - 'Invalid output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid output ({testcase.file}, line {testcase.line})') def build(self, source: str, diff --git a/mypy/test/testdiff.py b/mypy/test/testdiff.py index 9e1e9097152b..56f4564e91d3 100644 --- a/mypy/test/testdiff.py +++ b/mypy/test/testdiff.py @@ -47,8 +47,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: assert_string_arrays_equal( testcase.output, a, - 'Invalid output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid output ({testcase.file}, line {testcase.line})') def build(self, source: str, options: Options) -> Tuple[List[str], Optional[Dict[str, MypyFile]]]: diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 9fbfdc4395fe..783e21e2869e 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -130,15 +130,13 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: assert_string_arrays_equal( testcase.output, a, - 'Invalid output ({}, line {})'.format( - testcase.file, testcase.line)) + f'Invalid output ({testcase.file}, line {testcase.line})') if testcase.triggered: assert_string_arrays_equal( testcase.triggered, self.format_triggered(all_triggered), - 'Invalid active triggers ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid active triggers ({testcase.file}, line {testcase.line})') def get_options(self, source: str, diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 2e2f6b67d34a..fe0de2a7fe2d 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -98,8 +98,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: assert_string_arrays_equal( testcase.output, a, - 'Invalid output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid output ({testcase.file}, line {testcase.line})') def build(self, source: str, testcase: DataDrivenTestCase) -> Optional[BuildResult]: options = parse_options(source, testcase, incremental_step=1) @@ -142,7 +141,7 @@ def dump(self, return self.dump_symbol_tables(modules) elif kind == TYPES: return self.dump_types(manager) - assert False, 'Invalid kind %s' % kind + assert False, f'Invalid kind {kind}' def dump_asts(self, modules: Dict[str, MypyFile]) -> List[str]: a = [] @@ -177,8 +176,7 @@ def format_symbol_table_node(self, node: SymbolTableNode) -> str: return 'UNBOUND_IMPORTED' return 'None' if isinstance(node.node, Node): - s = '{}<{}>'.format(str(type(node.node).__name__), - self.id_mapper.id(node.node)) + s = f'{str(type(node.node).__name__)}<{self.id_mapper.id(node.node)}>' else: s = f'? ({type(node.node)})' if (isinstance(node.node, Var) and node.node.type and @@ -230,9 +228,7 @@ def dump_types(self, manager: FineGrainedBuildManager) -> List[str]: for expr in sorted(type_map, key=lambda n: (n.line, short_type(n), str(n) + str(type_map[n]))): typ = type_map[expr] - a.append('{}:{}: {}'.format(short_type(expr), - expr.line, - self.format_type(typ))) + a.append(f'{short_type(expr)}:{expr.line}: {self.format_type(typ)}') return a def format_type(self, typ: Type) -> str: diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 1587147c0777..340d6b904476 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -83,5 +83,4 @@ def test_parse_error(testcase: DataDrivenTestCase) -> None: # are equivalent. assert_string_arrays_equal( testcase.output, e.messages, - 'Invalid compiler output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid compiler output ({testcase.file}, line {testcase.line})') diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 8002b7410ee8..770738294755 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -70,7 +70,7 @@ def test_python_evaluation(testcase: DataDrivenTestCase, cache_dir: str) -> None return else: interpreter = python3_path - mypy_cmdline.append('--python-version={}'.format('.'.join(map(str, PYTHON3_VERSION)))) + mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}") m = re.search('# flags: (.*)$', '\n'.join(testcase.input), re.MULTILINE) if m: diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index a58dc4a3960a..c7a623507ac1 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -102,8 +102,7 @@ def test_semanal(testcase: DataDrivenTestCase) -> None: a = normalize_error_messages(a) assert_string_arrays_equal( testcase.output, a, - 'Invalid semantic analyzer output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid semantic analyzer output ({testcase.file}, line {testcase.line})') # Semantic analyzer error test cases @@ -165,8 +164,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: a = e.messages assert_string_arrays_equal( testcase.output, a, - 'Invalid semantic analyzer output ({}, line {})'.format( - testcase.file, testcase.line)) + f'Invalid semantic analyzer output ({testcase.file}, line {testcase.line})') # Type info export test cases @@ -200,8 +198,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: a = e.messages assert_string_arrays_equal( testcase.output, a, - 'Invalid semantic analyzer output ({}, line {})'.format( - testcase.file, testcase.line)) + f'Invalid semantic analyzer output ({testcase.file}, line {testcase.line})') class TypeInfoMap(Dict[str, TypeInfo]): diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 97e3d98597cb..3c2b2967fb3c 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -662,11 +662,11 @@ def test_infer_setitem_sig(self) -> None: def test_infer_binary_op_sig(self) -> None: for op in ('eq', 'ne', 'lt', 'le', 'gt', 'ge', 'add', 'radd', 'sub', 'rsub', 'mul', 'rmul'): - assert_equal(infer_method_sig('__%s__' % op), [self_arg, ArgSig(name='other')]) + assert_equal(infer_method_sig(f'__{op}__'), [self_arg, ArgSig(name='other')]) def test_infer_unary_op_sig(self) -> None: for op in ('neg', 'pos'): - assert_equal(infer_method_sig('__%s__' % op), [self_arg]) + assert_equal(infer_method_sig(f'__{op}__'), [self_arg]) def test_generate_c_type_stub_no_crash_for_object(self) -> None: output: List[str] = [] @@ -1074,7 +1074,7 @@ def test_non_existent(self) -> None: def module_to_path(out_dir: str, module: str) -> str: - fnam = os.path.join(out_dir, '{}.pyi'.format(module.replace('.', '/'))) + fnam = os.path.join(out_dir, f"{module.replace('.', '/')}.pyi") if not os.path.exists(fnam): alt_fnam = fnam.replace('.pyi', '/__init__.pyi') if os.path.exists(alt_fnam): diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index a17edbea24aa..de48c9ce2723 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -1167,8 +1167,7 @@ def test_config_file(self) -> None: runtime = "temp = 5\n" stub = "from decimal import Decimal\ntemp: Decimal\n" config_file = ( - "[mypy]\n" - "plugins={}/test-data/unit/plugins/decimal_to_int.py\n".format(root_dir) + f"[mypy]\nplugins={root_dir}/test-data/unit/plugins/decimal_to_int.py\n" ) output = run_stubtest(stub=stub, runtime=runtime, options=[]) assert remove_color_code(output) == ( diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index e1e3b6ab63ed..bd400b254ff4 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -72,5 +72,4 @@ def test_transform(testcase: DataDrivenTestCase) -> None: a = normalize_error_messages(a) assert_string_arrays_equal( testcase.output, a, - 'Invalid semantic analyzer output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid semantic analyzer output ({testcase.file}, line {testcase.line})') diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index c652b38ba6fe..a91cd0a2972d 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -68,5 +68,4 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: a = e.messages assert_string_arrays_equal( testcase.output, a, - 'Invalid type checker output ({}, line {})'.format(testcase.file, - testcase.line)) + f'Invalid type checker output ({testcase.file}, line {testcase.line})') diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 536e170a9644..d734a1cc330f 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -94,7 +94,8 @@ def analyze_type_alias(node: Expression, def no_subscript_builtin_alias(name: str, propose_alt: bool = True) -> str: - msg = '"{}" is not subscriptable'.format(name.split('.')[-1]) + class_name = name.split('.')[-1] + msg = f'"{class_name}" is not subscriptable' # This should never be called if the python_version is 3.9 or newer nongen_builtins = get_nongen_builtins((3, 8)) replacement = nongen_builtins[name] @@ -971,8 +972,7 @@ def analyze_callable_args(self, arglist: TypeList) -> Optional[Tuple[List[Type], # Looking it up already put an error message in return None elif found.fullname not in ARG_KINDS_BY_CONSTRUCTOR: - self.fail('Invalid argument constructor "{}"'.format( - found.fullname), arg) + self.fail(f'Invalid argument constructor "{found.fullname}"', arg) return None else: assert found.fullname is not None @@ -1322,8 +1322,7 @@ def fix_instance(t: Instance, fail: MsgCallback, note: MsgCallback, act = str(len(t.args)) if act == '0': act = 'none' - fail('"{}" expects {}, but {} given'.format( - t.type.name, s, act), t, code=codes.TYPE_ARG) + fail(f'"{t.type.name}" expects {s}, but {act} given', t, code=codes.TYPE_ARG) # Construct the correct number of type arguments, as # otherwise the type checker may crash as it expects # things to be right. diff --git a/mypy/types.py b/mypy/types.py index a958e6c67dcc..8f45e87b8fb9 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -2754,7 +2754,7 @@ def visit_callable_type(self, t: CallableType) -> str: if isinstance(var, TypeVarType): # We reimplement TypeVarType.__repr__ here in order to support id_mapper. if var.values: - vals = '({})'.format(', '.join(val.accept(self) for val in var.values)) + vals = f"({', '.join(val.accept(self) for val in var.values)})" vs.append(f'{var.name} in {vals}') elif not is_named_instance(var.upper_bound, 'builtins.object'): vs.append(f'{var.name} <: {var.upper_bound.accept(self)}') @@ -2763,7 +2763,7 @@ def visit_callable_type(self, t: CallableType) -> str: else: # For other TypeVarLikeTypes, just use the name vs.append(var.name) - s = '{} {}'.format('[{}]'.format(', '.join(vs)), s) + s = f"[{', '.join(vs)}] {s}" return f'def {s}' @@ -2771,7 +2771,7 @@ def visit_overloaded(self, t: Overloaded) -> str: a = [] for i in t.items: a.append(i.accept(self)) - return 'Overload({})'.format(', '.join(a)) + return f"Overload({', '.join(a)})" def visit_tuple_type(self, t: TupleType) -> str: s = self.list_str(t.items) diff --git a/mypy/util.py b/mypy/util.py index 1a3628458c48..c207fb7e18cd 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -743,7 +743,7 @@ def format_error( if blockers: msg += ' (errors prevented further checking)' else: - msg += ' (checked {} source file{})'.format(n_sources, 's' if n_sources != 1 else '') + msg += f" (checked {n_sources} source file{'s' if n_sources != 1 else ''})" if not use_color: return msg return self.style(msg, 'red', bold=True) From ad177f99fd3322072e389a1a7432ee7b15c84472 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 1 May 2022 17:16:16 -0600 Subject: [PATCH 46/79] mypyc: run pyupgrade (#12708) Re-attempt of #10741 Ran: `pyupgrade --py36-plus $(fd -e py) --keep-runtime-typing` I mostly only needed to change things where NamedTuple comments got dropped. --- mypyc/analysis/dataflow.py | 2 +- mypyc/analysis/ircheck.py | 8 +- mypyc/build.py | 12 +- mypyc/codegen/cstring.py | 4 +- mypyc/codegen/emit.py | 186 +++++++++++++-------------- mypyc/codegen/emitclass.py | 152 +++++++++++----------- mypyc/codegen/emitfunc.py | 82 ++++++------ mypyc/codegen/emitmodule.py | 112 ++++++++-------- mypyc/codegen/emitwrapper.py | 90 ++++++------- mypyc/common.py | 6 +- mypyc/crash.py | 2 +- mypyc/ir/class_ir.py | 11 +- mypyc/ir/func_ir.py | 8 +- mypyc/ir/ops.py | 6 +- mypyc/ir/pprint.py | 24 ++-- mypyc/ir/rtypes.py | 24 ++-- mypyc/irbuild/builder.py | 8 +- mypyc/irbuild/callable_class.py | 2 +- mypyc/irbuild/classdef.py | 4 +- mypyc/irbuild/env_class.py | 4 +- mypyc/irbuild/function.py | 4 +- mypyc/irbuild/generator.py | 2 +- mypyc/irbuild/ll_builder.py | 6 +- mypyc/irbuild/statement.py | 2 +- mypyc/namegen.py | 2 +- mypyc/primitives/int_ops.py | 2 + mypyc/primitives/registry.py | 1 + mypyc/test-data/fixtures/testutil.py | 6 +- mypyc/test/test_cheader.py | 6 +- mypyc/test/test_commandline.py | 2 +- mypyc/test/test_run.py | 4 +- mypyc/test/test_serialization.py | 14 +- mypyc/test/testutil.py | 8 +- mypyc/transform/uninit.py | 2 +- 34 files changed, 406 insertions(+), 402 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 9b459f77edbe..3b79f101a670 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -125,7 +125,7 @@ def __init__(self, before: 'AnalysisDict[T]', after: 'AnalysisDict[T]') -> None: self.after = after def __str__(self) -> str: - return 'before: %s\nafter: %s\n' % (self.before, self.after) + return f'before: {self.before}\nafter: {self.after}\n' GenAndKill = Tuple[Set[Value], Set[Value]] diff --git a/mypyc/analysis/ircheck.py b/mypyc/analysis/ircheck.py index 6450766f404c..6c8e8d7f18e5 100644 --- a/mypyc/analysis/ircheck.py +++ b/mypyc/analysis/ircheck.py @@ -17,7 +17,7 @@ from mypyc.ir.func_ir import FuncIR, FUNC_STATICMETHOD -class FnError(object): +class FnError: def __init__(self, source: Union[Op, BasicBlock], desc: str) -> None: self.source = source self.desc = desc @@ -129,8 +129,7 @@ def check_op_sources_valid(fn: FuncIR) -> List[FnError]: return errors -disjoint_types = set( - [ +disjoint_types = { int_rprimitive.name, bytes_rprimitive.name, str_rprimitive.name, @@ -139,8 +138,7 @@ def check_op_sources_valid(fn: FuncIR) -> List[FnError]: set_rprimitive.name, tuple_rprimitive.name, range_rprimitive.name, - ] -) +} def can_coerce_to(src: RType, dest: RType) -> bool: diff --git a/mypyc/build.py b/mypyc/build.py index 2c04eed9e64c..f5ff0201ffaf 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -200,7 +200,7 @@ def generate_c(sources: List[BuildSource], t1 = time.time() if compiler_options.verbose: - print("Parsed and typechecked in {:.3f}s".format(t1 - t0)) + print(f"Parsed and typechecked in {t1 - t0:.3f}s") if not messages and result: errors = Errors() @@ -212,7 +212,7 @@ def generate_c(sources: List[BuildSource], t2 = time.time() if compiler_options.verbose: - print("Compiled to C in {:.3f}s".format(t2 - t1)) + print(f"Compiled to C in {t2 - t1:.3f}s") # ... you know, just in case. if options.junit_xml: @@ -304,7 +304,7 @@ def write_file(path: str, contents: str) -> None: try: with open(path, 'rb') as f: old_contents: Optional[bytes] = f.read() - except IOError: + except OSError: old_contents = None if old_contents != encoded_contents: os.makedirs(os.path.dirname(path), exist_ok=True) @@ -513,8 +513,8 @@ def mypycify( cflags: List[str] = [] if compiler.compiler_type == 'unix': cflags += [ - '-O{}'.format(opt_level), - '-g{}'.format(debug_level), + f'-O{opt_level}', + f'-g{debug_level}', '-Werror', '-Wno-unused-function', '-Wno-unused-label', '-Wno-unreachable-code', '-Wno-unused-variable', '-Wno-unused-command-line-argument', '-Wno-unknown-warning-option', @@ -535,7 +535,7 @@ def mypycify( elif debug_level in ('2', '3'): debug_level = "FULL" cflags += [ - '/O{}'.format(opt_level), + f'/O{opt_level}', f'/DEBUG:{debug_level}', '/wd4102', # unreferenced label '/wd4101', # unreferenced local variable diff --git a/mypyc/codegen/cstring.py b/mypyc/codegen/cstring.py index 3626d2625e84..dba2bf814246 100644 --- a/mypyc/codegen/cstring.py +++ b/mypyc/codegen/cstring.py @@ -23,7 +23,7 @@ from typing_extensions import Final -CHAR_MAP: Final = ["\\{:03o}".format(i) for i in range(256)] +CHAR_MAP: Final = [f"\\{i:03o}" for i in range(256)] # It is safe to use string.printable as it always uses the C locale. for c in string.printable: @@ -32,7 +32,7 @@ # These assignments must come last because we prioritize simple escape # sequences over any other representation. for c in ('\'', '"', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'): - escaped = '\\{}'.format(c) + escaped = f'\\{c}' decoded = escaped.encode('ascii').decode('unicode_escape') CHAR_MAP[ord(decoded)] = escaped diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 531121134b03..94ea940ca3e6 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -161,7 +161,7 @@ def emit_label(self, label: Union[BasicBlock, str]) -> None: else: text = self.label(label) # Extra semicolon prevents an error when the next line declares a tempvar - self.fragments.append('{}: ;\n'.format(text)) + self.fragments.append(f'{text}: ;\n') def emit_from_emitter(self, emitter: 'Emitter') -> None: self.fragments.extend(emitter.fragments) @@ -201,7 +201,7 @@ def get_module_group_prefix(self, module_name: str) -> str: target_group_name = groups.get(module_name) if target_group_name and target_group_name != self.context.group_name: self.context.group_deps.add(target_group_name) - return 'exports_{}.'.format(exported_name(target_group_name)) + return f'exports_{exported_name(target_group_name)}.' else: return '' @@ -225,7 +225,7 @@ def static_name(self, id: str, module: Optional[str], prefix: str = STATIC_PREFI # the pointer also. star_maybe = '*' if lib_prefix else '' suffix = self.names.private_name(module or '', id) - return '{}{}{}{}'.format(star_maybe, lib_prefix, prefix, suffix) + return f'{star_maybe}{lib_prefix}{prefix}{suffix}' def type_struct_name(self, cl: ClassIR) -> str: return self.static_name(cl.name, cl.module_name, prefix=TYPE_PREFIX) @@ -254,13 +254,13 @@ def c_error_value(self, rtype: RType) -> str: return self.c_undefined_value(rtype) def native_function_name(self, fn: FuncDecl) -> str: - return '{}{}'.format(NATIVE_PREFIX, fn.cname(self.names)) + return f'{NATIVE_PREFIX}{fn.cname(self.names)}' def tuple_c_declaration(self, rtuple: RTuple) -> List[str]: result = [ - '#ifndef MYPYC_DECLARED_{}'.format(rtuple.struct_name), - '#define MYPYC_DECLARED_{}'.format(rtuple.struct_name), - 'typedef struct {} {{'.format(rtuple.struct_name), + f'#ifndef MYPYC_DECLARED_{rtuple.struct_name}', + f'#define MYPYC_DECLARED_{rtuple.struct_name}', + f'typedef struct {rtuple.struct_name} {{', ] if len(rtuple.types) == 0: # empty tuple # Empty tuples contain a flag so that they can still indicate @@ -269,9 +269,9 @@ def tuple_c_declaration(self, rtuple: RTuple) -> List[str]: else: i = 0 for typ in rtuple.types: - result.append('{}f{};'.format(self.ctype_spaced(typ), i)) + result.append(f'{self.ctype_spaced(typ)}f{i};') i += 1 - result.append('}} {};'.format(rtuple.struct_name)) + result.append(f'}} {rtuple.struct_name};') values = self.tuple_undefined_value_helper(rtuple) result.append('static {} {} = {{ {} }};'.format( self.ctype(rtuple), self.tuple_undefined_value(rtuple), ''.join(values))) @@ -295,8 +295,8 @@ def emit_undefined_attr_check(self, rtype: RType, attr_expr: str, attr_expr, compare, self.c_undefined_value(rtype) ) if unlikely: - check = '(unlikely{})'.format(check) - self.emit_line('if {} {{'.format(check)) + check = f'(unlikely{check})' + self.emit_line(f'if {check} {{') def tuple_undefined_check_cond( self, rtuple: RTuple, tuple_expr_in_c: str, @@ -363,7 +363,7 @@ def emit_inc_ref(self, dest: str, rtype: RType, *, rare: bool = False) -> None: self.emit_line('CPyTagged_INCREF(%s);' % dest) elif isinstance(rtype, RTuple): for i, item_type in enumerate(rtype.types): - self.emit_inc_ref('{}.f{}'.format(dest, i), item_type) + self.emit_inc_ref(f'{dest}.f{i}', item_type) elif not rtype.is_unboxed: # Always inline, since this is a simple op self.emit_line('CPy_INCREF(%s);' % dest) @@ -385,19 +385,19 @@ def emit_dec_ref(self, x = 'X' if is_xdec else '' if is_int_rprimitive(rtype): if rare: - self.emit_line('CPyTagged_%sDecRef(%s);' % (x, dest)) + self.emit_line(f'CPyTagged_{x}DecRef({dest});') else: # Inlined - self.emit_line('CPyTagged_%sDECREF(%s);' % (x, dest)) + self.emit_line(f'CPyTagged_{x}DECREF({dest});') elif isinstance(rtype, RTuple): for i, item_type in enumerate(rtype.types): - self.emit_dec_ref('{}.f{}'.format(dest, i), item_type, is_xdec=is_xdec, rare=rare) + self.emit_dec_ref(f'{dest}.f{i}', item_type, is_xdec=is_xdec, rare=rare) elif not rtype.is_unboxed: if rare: - self.emit_line('CPy_%sDecRef(%s);' % (x, dest)) + self.emit_line(f'CPy_{x}DecRef({dest});') else: # Inlined - self.emit_line('CPy_%sDECREF(%s);' % (x, dest)) + self.emit_line(f'CPy_{x}DECREF({dest});') # Otherwise assume it's an unboxed, pointerless value and do nothing. def pretty_name(self, typ: RType) -> str: @@ -447,7 +447,7 @@ def emit_cast(self, assert isinstance(error, ReturnHandler) handle_error = 'return %s;' % error.value if raise_exception: - raise_exc = 'CPy_TypeError("{}", {}); '.format(self.pretty_name(typ), src) + raise_exc = f'CPy_TypeError("{self.pretty_name(typ)}", {src}); ' err = raise_exc + handle_error else: err = handle_error @@ -458,13 +458,13 @@ def emit_cast(self, assert value_type is not None if is_same_type(value_type, typ): if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') check = '({} != Py_None)' if likely: - check = '(likely{})'.format(check) + check = f'(likely{check})' self.emit_arg_check(src, dest, typ, check.format(src), optional) self.emit_lines( - ' {} = {};'.format(dest, src), + f' {dest} = {src};', 'else {', err, '}') @@ -475,7 +475,7 @@ def emit_cast(self, or is_str_rprimitive(typ) or is_range_rprimitive(typ) or is_float_rprimitive(typ) or is_int_rprimitive(typ) or is_bool_rprimitive(typ) or is_bit_rprimitive(typ)): if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') if is_list_rprimitive(typ): prefix = 'PyList' elif is_dict_rprimitive(typ): @@ -496,41 +496,41 @@ def emit_cast(self, assert False, 'unexpected primitive type' check = '({}_Check({}))' if likely: - check = '(likely{})'.format(check) + check = f'(likely{check})' self.emit_arg_check(src, dest, typ, check.format(prefix, src), optional) self.emit_lines( - ' {} = {};'.format(dest, src), + f' {dest} = {src};', 'else {', err, '}') elif is_bytes_rprimitive(typ): if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') check = '(PyBytes_Check({}) || PyByteArray_Check({}))' if likely: - check = '(likely{})'.format(check) + check = f'(likely{check})' self.emit_arg_check(src, dest, typ, check.format(src, src), optional) self.emit_lines( - ' {} = {};'.format(dest, src), + f' {dest} = {src};', 'else {', err, '}') elif is_tuple_rprimitive(typ): if declare_dest: - self.emit_line('{} {};'.format(self.ctype(typ), dest)) + self.emit_line(f'{self.ctype(typ)} {dest};') check = '(PyTuple_Check({}))' if likely: - check = '(likely{})'.format(check) + check = f'(likely{check})' self.emit_arg_check(src, dest, typ, check.format(src), optional) self.emit_lines( - ' {} = {};'.format(dest, src), + f' {dest} = {src};', 'else {', err, '}') elif isinstance(typ, RInstance): if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') concrete = all_concrete_classes(typ.class_ir) # If there are too many concrete subclasses or we can't find any # (meaning the code ought to be dead or we aren't doing global opts), @@ -548,31 +548,31 @@ def emit_cast(self, check = full_str.format( src=src, targets=[self.type_struct_name(ir) for ir in concrete]) if likely: - check = '(likely{})'.format(check) + check = f'(likely{check})' self.emit_arg_check(src, dest, typ, check, optional) self.emit_lines( - ' {} = {};'.format(dest, src), + f' {dest} = {src};', 'else {', err, '}') elif is_none_rprimitive(typ): if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') check = '({} == Py_None)' if likely: - check = '(likely{})'.format(check) + check = f'(likely{check})' self.emit_arg_check(src, dest, typ, check.format(src), optional) self.emit_lines( - ' {} = {};'.format(dest, src), + f' {dest} = {src};', 'else {', err, '}') elif is_object_rprimitive(typ): if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') self.emit_arg_check(src, dest, typ, '', optional) - self.emit_line('{} = {};'.format(dest, src)) + self.emit_line(f'{dest} = {src};') if optional: self.emit_line('}') elif isinstance(typ, RUnion): @@ -596,12 +596,12 @@ def emit_union_cast(self, The arguments are similar to emit_cast. """ if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') good_label = self.new_label() if optional: - self.emit_line('if ({} == NULL) {{'.format(src)) - self.emit_line('{} = {};'.format(dest, self.c_error_value(typ))) - self.emit_line('goto {};'.format(good_label)) + self.emit_line(f'if ({src} == NULL) {{') + self.emit_line(f'{dest} = {self.c_error_value(typ)};') + self.emit_line(f'goto {good_label};') self.emit_line('}') for item in typ.items: self.emit_cast(src, @@ -611,7 +611,7 @@ def emit_union_cast(self, raise_exception=False, optional=False, likely=False) - self.emit_line('if ({} != NULL) goto {};'.format(dest, good_label)) + self.emit_line(f'if ({dest} != NULL) goto {good_label};') # Handle cast failure. self.emit_line(err) self.emit_label(good_label) @@ -623,7 +623,7 @@ def emit_tuple_cast(self, src: str, dest: str, typ: RTuple, declare_dest: bool, The arguments are similar to emit_cast. """ if declare_dest: - self.emit_line('PyObject *{};'.format(dest)) + self.emit_line(f'PyObject *{dest};') # This reuse of the variable is super dodgy. We don't even # care about the values except to check whether they are # invalid. @@ -631,26 +631,26 @@ def emit_tuple_cast(self, src: str, dest: str, typ: RTuple, declare_dest: bool, self.emit_lines( 'if (unlikely(!(PyTuple_Check({r}) && PyTuple_GET_SIZE({r}) == {size}))) {{'.format( r=src, size=len(typ.types)), - '{} = NULL;'.format(dest), - 'goto {};'.format(out_label), + f'{dest} = NULL;', + f'goto {out_label};', '}') for i, item in enumerate(typ.types): # Since we did the checks above this should never fail - self.emit_cast('PyTuple_GET_ITEM({}, {})'.format(src, i), + self.emit_cast(f'PyTuple_GET_ITEM({src}, {i})', dest, item, declare_dest=False, raise_exception=False, optional=False) - self.emit_line('if ({} == NULL) goto {};'.format(dest, out_label)) + self.emit_line(f'if ({dest} == NULL) goto {out_label};') - self.emit_line('{} = {};'.format(dest, src)) + self.emit_line(f'{dest} = {src};') self.emit_label(out_label) def emit_arg_check(self, src: str, dest: str, typ: RType, check: str, optional: bool) -> None: if optional: - self.emit_line('if ({} == NULL) {{'.format(src)) - self.emit_line('{} = {};'.format(dest, self.c_error_value(typ))) + self.emit_line(f'if ({src} == NULL) {{') + self.emit_line(f'{dest} = {self.c_error_value(typ)};') if check != '': self.emit_line('{}if {}'.format('} else ' if optional else '', check)) elif optional: @@ -687,61 +687,61 @@ def emit_unbox(self, error = error or AssignHandler() # TODO: Verify refcount handling. if isinstance(error, AssignHandler): - failure = '%s = %s;' % (dest, self.c_error_value(typ)) + failure = f'{dest} = {self.c_error_value(typ)};' elif isinstance(error, GotoHandler): failure = 'goto %s;' % error.label else: assert isinstance(error, ReturnHandler) failure = 'return %s;' % error.value if raise_exception: - raise_exc = 'CPy_TypeError("{}", {}); '.format(self.pretty_name(typ), src) + raise_exc = f'CPy_TypeError("{self.pretty_name(typ)}", {src}); ' failure = raise_exc + failure if is_int_rprimitive(typ) or is_short_int_rprimitive(typ): if declare_dest: - self.emit_line('CPyTagged {};'.format(dest)) - self.emit_arg_check(src, dest, typ, '(likely(PyLong_Check({})))'.format(src), + self.emit_line(f'CPyTagged {dest};') + self.emit_arg_check(src, dest, typ, f'(likely(PyLong_Check({src})))', optional) if borrow: - self.emit_line(' {} = CPyTagged_BorrowFromObject({});'.format(dest, src)) + self.emit_line(f' {dest} = CPyTagged_BorrowFromObject({src});') else: - self.emit_line(' {} = CPyTagged_FromObject({});'.format(dest, src)) + self.emit_line(f' {dest} = CPyTagged_FromObject({src});') self.emit_line('else {') self.emit_line(failure) self.emit_line('}') elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ): # Whether we are borrowing or not makes no difference. if declare_dest: - self.emit_line('char {};'.format(dest)) - self.emit_arg_check(src, dest, typ, '(unlikely(!PyBool_Check({}))) {{'.format(src), + self.emit_line(f'char {dest};') + self.emit_arg_check(src, dest, typ, f'(unlikely(!PyBool_Check({src}))) {{', optional) self.emit_line(failure) self.emit_line('} else') - conversion = '{} == Py_True'.format(src) - self.emit_line(' {} = {};'.format(dest, conversion)) + conversion = f'{src} == Py_True' + self.emit_line(f' {dest} = {conversion};') elif is_none_rprimitive(typ): # Whether we are borrowing or not makes no difference. if declare_dest: - self.emit_line('char {};'.format(dest)) - self.emit_arg_check(src, dest, typ, '(unlikely({} != Py_None)) {{'.format(src), + self.emit_line(f'char {dest};') + self.emit_arg_check(src, dest, typ, f'(unlikely({src} != Py_None)) {{', optional) self.emit_line(failure) self.emit_line('} else') - self.emit_line(' {} = 1;'.format(dest)) + self.emit_line(f' {dest} = 1;') elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) if declare_dest: - self.emit_line('{} {};'.format(self.ctype(typ), dest)) + self.emit_line(f'{self.ctype(typ)} {dest};') # HACK: The error handling for unboxing tuples is busted # and instead of fixing it I am just wrapping it in the # cast code which I think is right. This is not good. if optional: - self.emit_line('if ({} == NULL) {{'.format(src)) - self.emit_line('{} = {};'.format(dest, self.c_error_value(typ))) + self.emit_line(f'if ({src} == NULL) {{') + self.emit_line(f'{dest} = {self.c_error_value(typ)};') self.emit_line('} else {') cast_temp = self.temp_name() self.emit_tuple_cast(src, cast_temp, typ, declare_dest=True, err='', src_type=None) - self.emit_line('if (unlikely({} == NULL)) {{'.format(cast_temp)) + self.emit_line(f'if (unlikely({cast_temp} == NULL)) {{') # self.emit_arg_check(src, dest, typ, # '(!PyTuple_Check({}) || PyTuple_Size({}) != {}) {{'.format( @@ -749,11 +749,11 @@ def emit_unbox(self, self.emit_line(failure) # TODO: Decrease refcount? self.emit_line('} else {') if not typ.types: - self.emit_line('{}.empty_struct_error_flag = 0;'.format(dest)) + self.emit_line(f'{dest}.empty_struct_error_flag = 0;') for i, item_type in enumerate(typ.types): temp = self.temp_name() # emit_tuple_cast above checks the size, so this should not fail - self.emit_line('PyObject *{} = PyTuple_GET_ITEM({}, {});'.format(temp, src, i)) + self.emit_line(f'PyObject *{temp} = PyTuple_GET_ITEM({src}, {i});') temp2 = self.temp_name() # Unbox or check the item. if item_type.is_unboxed: @@ -768,7 +768,7 @@ def emit_unbox(self, if not borrow: self.emit_inc_ref(temp, object_rprimitive) self.emit_cast(temp, temp2, item_type, declare_dest=True) - self.emit_line('{}.f{} = {};'.format(dest, i, temp2)) + self.emit_line(f'{dest}.f{i} = {temp2};') self.emit_line('}') if optional: self.emit_line('}') @@ -791,54 +791,54 @@ def emit_box(self, src: str, dest: str, typ: RType, declare_dest: bool = False, declaration = '' if is_int_rprimitive(typ) or is_short_int_rprimitive(typ): # Steal the existing reference if it exists. - self.emit_line('{}{} = CPyTagged_StealAsObject({});'.format(declaration, dest, src)) + self.emit_line(f'{declaration}{dest} = CPyTagged_StealAsObject({src});') elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ): # N.B: bool is special cased to produce a borrowed value # after boxing, so we don't need to increment the refcount # when this comes directly from a Box op. - self.emit_lines('{}{} = {} ? Py_True : Py_False;'.format(declaration, dest, src)) + self.emit_lines(f'{declaration}{dest} = {src} ? Py_True : Py_False;') if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) elif is_none_rprimitive(typ): # N.B: None is special cased to produce a borrowed value # after boxing, so we don't need to increment the refcount # when this comes directly from a Box op. - self.emit_lines('{}{} = Py_None;'.format(declaration, dest)) + self.emit_lines(f'{declaration}{dest} = Py_None;') if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) elif is_int32_rprimitive(typ): - self.emit_line('{}{} = PyLong_FromLong({});'.format(declaration, dest, src)) + self.emit_line(f'{declaration}{dest} = PyLong_FromLong({src});') elif is_int64_rprimitive(typ): - self.emit_line('{}{} = PyLong_FromLongLong({});'.format(declaration, dest, src)) + self.emit_line(f'{declaration}{dest} = PyLong_FromLongLong({src});') elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) - self.emit_line('{}{} = PyTuple_New({});'.format(declaration, dest, len(typ.types))) - self.emit_line('if (unlikely({} == NULL))'.format(dest)) + self.emit_line(f'{declaration}{dest} = PyTuple_New({len(typ.types)});') + self.emit_line(f'if (unlikely({dest} == NULL))') self.emit_line(' CPyError_OutOfMemory();') # TODO: Fail if dest is None for i in range(0, len(typ.types)): if not typ.is_unboxed: - self.emit_line('PyTuple_SET_ITEM({}, {}, {}.f{}'.format(dest, i, src, i)) + self.emit_line(f'PyTuple_SET_ITEM({dest}, {i}, {src}.f{i}') else: inner_name = self.temp_name() - self.emit_box('{}.f{}'.format(src, i), inner_name, typ.types[i], + self.emit_box(f'{src}.f{i}', inner_name, typ.types[i], declare_dest=True) - self.emit_line('PyTuple_SET_ITEM({}, {}, {});'.format(dest, i, inner_name)) + self.emit_line(f'PyTuple_SET_ITEM({dest}, {i}, {inner_name});') else: assert not typ.is_unboxed # Type is boxed -- trivially just assign. - self.emit_line('{}{} = {};'.format(declaration, dest, src)) + self.emit_line(f'{declaration}{dest} = {src};') def emit_error_check(self, value: str, rtype: RType, failure: str) -> None: """Emit code for checking a native function return value for uncaught exception.""" if not isinstance(rtype, RTuple): - self.emit_line('if ({} == {}) {{'.format(value, self.c_error_value(rtype))) + self.emit_line(f'if ({value} == {self.c_error_value(rtype)}) {{') else: if len(rtype.types) == 0: return # empty tuples can't fail. else: cond = self.tuple_undefined_check_cond(rtype, value, self.c_error_value, '==') - self.emit_line('if ({}) {{'.format(cond)) + self.emit_line(f'if ({cond}) {{') self.emit_lines(failure, '}') def emit_gc_visit(self, target: str, rtype: RType) -> None: @@ -851,15 +851,15 @@ def emit_gc_visit(self, target: str, rtype: RType) -> None: # Not refcounted -> no pointers -> no GC interaction. return elif isinstance(rtype, RPrimitive) and rtype.name == 'builtins.int': - self.emit_line('if (CPyTagged_CheckLong({})) {{'.format(target)) - self.emit_line('Py_VISIT(CPyTagged_LongAsObject({}));'.format(target)) + self.emit_line(f'if (CPyTagged_CheckLong({target})) {{') + self.emit_line(f'Py_VISIT(CPyTagged_LongAsObject({target}));') self.emit_line('}') elif isinstance(rtype, RTuple): for i, item_type in enumerate(rtype.types): - self.emit_gc_visit('{}.f{}'.format(target, i), item_type) + self.emit_gc_visit(f'{target}.f{i}', item_type) elif self.ctype(rtype) == 'PyObject *': # The simplest case. - self.emit_line('Py_VISIT({});'.format(target)) + self.emit_line(f'Py_VISIT({target});') else: assert False, 'emit_gc_visit() not implemented for %s' % repr(rtype) @@ -873,16 +873,16 @@ def emit_gc_clear(self, target: str, rtype: RType) -> None: # Not refcounted -> no pointers -> no GC interaction. return elif isinstance(rtype, RPrimitive) and rtype.name == 'builtins.int': - self.emit_line('if (CPyTagged_CheckLong({})) {{'.format(target)) - self.emit_line('CPyTagged __tmp = {};'.format(target)) - self.emit_line('{} = {};'.format(target, self.c_undefined_value(rtype))) + self.emit_line(f'if (CPyTagged_CheckLong({target})) {{') + self.emit_line(f'CPyTagged __tmp = {target};') + self.emit_line(f'{target} = {self.c_undefined_value(rtype)};') self.emit_line('Py_XDECREF(CPyTagged_LongAsObject(__tmp));') self.emit_line('}') elif isinstance(rtype, RTuple): for i, item_type in enumerate(rtype.types): - self.emit_gc_clear('{}.f{}'.format(target, i), item_type) + self.emit_gc_clear(f'{target}.f{i}', item_type) elif self.ctype(rtype) == 'PyObject *' and self.c_undefined_value(rtype) == 'NULL': # The simplest case. - self.emit_line('Py_CLEAR({});'.format(target)) + self.emit_line(f'Py_CLEAR({target});') else: assert False, 'emit_gc_clear() not implemented for %s' % repr(rtype) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 9c960cf80bdd..437b50444d63 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -20,11 +20,11 @@ def native_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: - return '{}{}'.format(NATIVE_PREFIX, fn.cname(emitter.names)) + return f'{NATIVE_PREFIX}{fn.cname(emitter.names)}' def wrapper_slot(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: - return '{}{}'.format(PREFIX, fn.cname(emitter.names)) + return f'{PREFIX}{fn.cname(emitter.names)}' # We maintain a table from dunder function names to struct slots they @@ -164,7 +164,7 @@ def generate_class_type_decl(cl: ClassIR, c_emitter: Emitter, context = c_emitter.context name = emitter.type_struct_name(cl) context.declarations[name] = HeaderDeclaration( - 'PyTypeObject *{};'.format(emitter.type_struct_name(cl)), + f'PyTypeObject *{emitter.type_struct_name(cl)};', needs_export=True) # If this is a non-extension class, all we want is the type object decl. @@ -175,7 +175,7 @@ def generate_class_type_decl(cl: ClassIR, c_emitter: Emitter, generate_full = not cl.is_trait and not cl.builtin_base if generate_full: context.declarations[emitter.native_function_name(cl.ctor)] = HeaderDeclaration( - '{};'.format(native_function_header(cl.ctor, emitter)), + f'{native_function_header(cl.ctor, emitter)};', needs_export=True, ) @@ -188,19 +188,19 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: name = cl.name name_prefix = cl.name_prefix(emitter.names) - setup_name = '{}_setup'.format(name_prefix) - new_name = '{}_new'.format(name_prefix) - members_name = '{}_members'.format(name_prefix) - getseters_name = '{}_getseters'.format(name_prefix) - vtable_name = '{}_vtable'.format(name_prefix) - traverse_name = '{}_traverse'.format(name_prefix) - clear_name = '{}_clear'.format(name_prefix) - dealloc_name = '{}_dealloc'.format(name_prefix) - methods_name = '{}_methods'.format(name_prefix) - vtable_setup_name = '{}_trait_vtable_setup'.format(name_prefix) + setup_name = f'{name_prefix}_setup' + new_name = f'{name_prefix}_new' + members_name = f'{name_prefix}_members' + getseters_name = f'{name_prefix}_getseters' + vtable_name = f'{name_prefix}_vtable' + traverse_name = f'{name_prefix}_traverse' + clear_name = f'{name_prefix}_clear' + dealloc_name = f'{name_prefix}_dealloc' + methods_name = f'{name_prefix}_methods' + vtable_setup_name = f'{name_prefix}_trait_vtable_setup' fields: Dict[str, str] = OrderedDict() - fields['tp_name'] = '"{}"'.format(name) + fields['tp_name'] = f'"{name}"' generate_full = not cl.is_trait and not cl.builtin_base needs_getseters = cl.needs_getseters or not cl.is_generated @@ -209,9 +209,9 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: fields['tp_new'] = new_name if generate_full: - fields['tp_dealloc'] = '(destructor){}_dealloc'.format(name_prefix) - fields['tp_traverse'] = '(traverseproc){}_traverse'.format(name_prefix) - fields['tp_clear'] = '(inquiry){}_clear'.format(name_prefix) + fields['tp_dealloc'] = f'(destructor){name_prefix}_dealloc' + fields['tp_traverse'] = f'(traverseproc){name_prefix}_traverse' + fields['tp_clear'] = f'(inquiry){name_prefix}_clear' if needs_getseters: fields['tp_getset'] = getseters_name fields['tp_methods'] = methods_name @@ -236,7 +236,7 @@ def emit_line() -> None: slots = generate_slots(cl, slot_defs, emitter) if slots: table_struct_name = generate_side_table_for_class(cl, table_name, type, slots, emitter) - fields['tp_{}'.format(table_name)] = '&{}'.format(table_struct_name) + fields[f'tp_{table_name}'] = f'&{table_struct_name}' richcompare_name = generate_richcompare_wrapper(cl, emitter) if richcompare_name: @@ -245,11 +245,11 @@ def emit_line() -> None: # If the class inherits from python, make space for a __dict__ struct_name = cl.struct_name(emitter.names) if cl.builtin_base: - base_size = 'sizeof({})'.format(cl.builtin_base) + base_size = f'sizeof({cl.builtin_base})' elif cl.is_trait: base_size = 'sizeof(PyObject)' else: - base_size = 'sizeof({})'.format(struct_name) + base_size = f'sizeof({struct_name})' # Since our types aren't allocated using type() we need to # populate these fields ourselves if we want them to have correct # values. PyType_Ready will inherit the offsets from tp_base but @@ -259,17 +259,17 @@ def emit_line() -> None: if cl.has_dict: # __dict__ lives right after the struct and __weakref__ lives right after that # TODO: They should get members in the struct instead of doing this nonsense. - weak_offset = '{} + sizeof(PyObject *)'.format(base_size) + weak_offset = f'{base_size} + sizeof(PyObject *)' emitter.emit_lines( - 'PyMemberDef {}[] = {{'.format(members_name), - '{{"__dict__", T_OBJECT_EX, {}, 0, NULL}},'.format(base_size), - '{{"__weakref__", T_OBJECT_EX, {}, 0, NULL}},'.format(weak_offset), + f'PyMemberDef {members_name}[] = {{', + f'{{"__dict__", T_OBJECT_EX, {base_size}, 0, NULL}},', + f'{{"__weakref__", T_OBJECT_EX, {weak_offset}, 0, NULL}},', '{0}', '};', ) fields['tp_members'] = members_name - fields['tp_basicsize'] = '{} + 2*sizeof(PyObject *)'.format(base_size) + fields['tp_basicsize'] = f'{base_size} + 2*sizeof(PyObject *)' fields['tp_dictoffset'] = base_size fields['tp_weaklistoffset'] = weak_offset else: @@ -279,7 +279,7 @@ def emit_line() -> None: # Declare setup method that allocates and initializes an object. type is the # type of the class being initialized, which could be another class if there # is an interpreted subclass. - emitter.emit_line('static PyObject *{}(PyTypeObject *type);'.format(setup_name)) + emitter.emit_line(f'static PyObject *{setup_name}(PyTypeObject *type);') assert cl.ctor is not None emitter.emit_line(native_function_header(cl.ctor, emitter) + ';') @@ -323,10 +323,10 @@ def emit_line() -> None: flags.append('_Py_TPFLAGS_HAVE_VECTORCALL') fields['tp_flags'] = ' | '.join(flags) - emitter.emit_line("static PyTypeObject {}_template_ = {{".format(emitter.type_struct_name(cl))) + emitter.emit_line(f"static PyTypeObject {emitter.type_struct_name(cl)}_template_ = {{") emitter.emit_line("PyVarObject_HEAD_INIT(NULL, 0)") for field, value in fields.items(): - emitter.emit_line(".{} = {},".format(field, value)) + emitter.emit_line(f".{field} = {value},") emitter.emit_line("};") emitter.emit_line("static PyTypeObject *{t}_template = &{t}_template_;".format( t=emitter.type_struct_name(cl))) @@ -344,11 +344,11 @@ def emit_line() -> None: def getter_name(cl: ClassIR, attribute: str, names: NameGenerator) -> str: - return names.private_name(cl.module_name, '{}_get{}'.format(cl.name, attribute)) + return names.private_name(cl.module_name, f'{cl.name}_get{attribute}') def setter_name(cl: ClassIR, attribute: str, names: NameGenerator) -> str: - return names.private_name(cl.module_name, '{}_set{}'.format(cl.name, attribute)) + return names.private_name(cl.module_name, f'{cl.name}_set{attribute}') def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: @@ -370,7 +370,7 @@ def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None: if isinstance(rtype, RTuple): emitter.declare_tuple_struct(rtype) - lines.append('}} {};'.format(cl.struct_name(emitter.names))) + lines.append(f'}} {cl.struct_name(emitter.names)};') lines.append('') emitter.context.declarations[cl.struct_name(emitter.names)] = HeaderDeclaration( lines, @@ -439,11 +439,11 @@ def trait_offset_table_name(trait: ClassIR) -> str: # Emit vtable setup function emitter.emit_line('static bool') - emitter.emit_line('{}{}(void)'.format(NATIVE_PREFIX, vtable_setup_name)) + emitter.emit_line(f'{NATIVE_PREFIX}{vtable_setup_name}(void)') emitter.emit_line('{') if base.allow_interpreted_subclasses and not shadow: - emitter.emit_line('{}{}_shadow();'.format(NATIVE_PREFIX, vtable_setup_name)) + emitter.emit_line(f'{NATIVE_PREFIX}{vtable_setup_name}_shadow();') subtables = [] for trait, vtable in base.trait_vtables.items(): @@ -458,7 +458,7 @@ def trait_offset_table_name(trait: ClassIR) -> str: emitter.emit_line('return 1;') emitter.emit_line('}') - return vtable_name if not subtables else "{} + {}".format(vtable_name, len(subtables) * 3) + return vtable_name if not subtables else f"{vtable_name} + {len(subtables) * 3}" def generate_offset_table(trait_offset_table_name: str, @@ -466,7 +466,7 @@ def generate_offset_table(trait_offset_table_name: str, trait: ClassIR, cl: ClassIR) -> None: """Generate attribute offset row of a trait vtable.""" - emitter.emit_line('size_t {}_scratch[] = {{'.format(trait_offset_table_name)) + emitter.emit_line(f'size_t {trait_offset_table_name}_scratch[] = {{') for attr in trait.attributes: emitter.emit_line('offsetof({}, {}),'.format( cl.struct_name(emitter.names), emitter.attr(attr) @@ -485,7 +485,7 @@ def generate_vtable(entries: VTableEntries, emitter: Emitter, subtables: List[Tuple[ClassIR, str, str]], shadow: bool) -> None: - emitter.emit_line('CPyVTableItem {}_scratch[] = {{'.format(vtable_name)) + emitter.emit_line(f'CPyVTableItem {vtable_name}_scratch[] = {{') if subtables: emitter.emit_line('/* Array of trait vtables */') for trait, table, offset_table in subtables: @@ -516,26 +516,26 @@ def generate_setup_for_class(cl: ClassIR, emitter: Emitter) -> None: """Generate a native function that allocates an instance of a class.""" emitter.emit_line('static PyObject *') - emitter.emit_line('{}(PyTypeObject *type)'.format(func_name)) + emitter.emit_line(f'{func_name}(PyTypeObject *type)') emitter.emit_line('{') - emitter.emit_line('{} *self;'.format(cl.struct_name(emitter.names))) + emitter.emit_line(f'{cl.struct_name(emitter.names)} *self;') emitter.emit_line('self = ({struct} *)type->tp_alloc(type, 0);'.format( struct=cl.struct_name(emitter.names))) emitter.emit_line('if (self == NULL)') emitter.emit_line(' return NULL;') if shadow_vtable_name: - emitter.emit_line('if (type != {}) {{'.format(emitter.type_struct_name(cl))) - emitter.emit_line('self->vtable = {};'.format(shadow_vtable_name)) + emitter.emit_line(f'if (type != {emitter.type_struct_name(cl)}) {{') + emitter.emit_line(f'self->vtable = {shadow_vtable_name};') emitter.emit_line('} else {') - emitter.emit_line('self->vtable = {};'.format(vtable_name)) + emitter.emit_line(f'self->vtable = {vtable_name};') emitter.emit_line('}') else: - emitter.emit_line('self->vtable = {};'.format(vtable_name)) + emitter.emit_line(f'self->vtable = {vtable_name};') if cl.has_method('__call__') and emitter.use_vectorcall(): name = cl.method_decl('__call__').cname(emitter.names) - emitter.emit_line('self->vectorcall = {}{};'.format(PREFIX, name)) + emitter.emit_line(f'self->vectorcall = {PREFIX}{name};') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): @@ -562,9 +562,9 @@ def generate_constructor_for_class(cl: ClassIR, vtable_name: str, emitter: Emitter) -> None: """Generate a native function that allocates and initializes an instance of a class.""" - emitter.emit_line('{}'.format(native_function_header(fn, emitter))) + emitter.emit_line(f'{native_function_header(fn, emitter)}') emitter.emit_line('{') - emitter.emit_line('PyObject *self = {}({});'.format(setup_name, emitter.type_struct_name(cl))) + emitter.emit_line(f'PyObject *self = {setup_name}({emitter.type_struct_name(cl)});') emitter.emit_line('if (self == NULL)') emitter.emit_line(' return NULL;') args = ', '.join(['self'] + [REG_PREFIX + arg.name for arg in fn.sig.args]) @@ -602,11 +602,11 @@ def generate_init_for_class(cl: ClassIR, __init__ methods return a PyObject. Translate NULL to -1, everything else to 0. """ - func_name = '{}_init'.format(cl.name_prefix(emitter.names)) + func_name = f'{cl.name_prefix(emitter.names)}_init' emitter.emit_line('static int') emitter.emit_line( - '{}(PyObject *self, PyObject *args, PyObject *kwds)'.format(func_name)) + f'{func_name}(PyObject *self, PyObject *args, PyObject *kwds)') emitter.emit_line('{') emitter.emit_line('return {}{}(self, args, kwds) != NULL ? 0 : -1;'.format( PREFIX, init_fn.cname(emitter.names))) @@ -622,18 +622,18 @@ def generate_new_for_class(cl: ClassIR, emitter: Emitter) -> None: emitter.emit_line('static PyObject *') emitter.emit_line( - '{}(PyTypeObject *type, PyObject *args, PyObject *kwds)'.format(func_name)) + f'{func_name}(PyTypeObject *type, PyObject *args, PyObject *kwds)') emitter.emit_line('{') # TODO: Check and unbox arguments if not cl.allow_interpreted_subclasses: - emitter.emit_line('if (type != {}) {{'.format(emitter.type_struct_name(cl))) + emitter.emit_line(f'if (type != {emitter.type_struct_name(cl)}) {{') emitter.emit_line( 'PyErr_SetString(PyExc_TypeError, "interpreted classes cannot inherit from compiled");' ) emitter.emit_line('return NULL;') emitter.emit_line('}') - emitter.emit_line('return {}(type);'.format(setup_name)) + emitter.emit_line(f'return {setup_name}(type);') emitter.emit_line('}') @@ -642,9 +642,9 @@ def generate_new_for_trait(cl: ClassIR, emitter: Emitter) -> None: emitter.emit_line('static PyObject *') emitter.emit_line( - '{}(PyTypeObject *type, PyObject *args, PyObject *kwds)'.format(func_name)) + f'{func_name}(PyTypeObject *type, PyObject *args, PyObject *kwds)') emitter.emit_line('{') - emitter.emit_line('if (type != {}) {{'.format(emitter.type_struct_name(cl))) + emitter.emit_line(f'if (type != {emitter.type_struct_name(cl)}) {{') emitter.emit_line( 'PyErr_SetString(PyExc_TypeError, ' '"interpreted classes cannot inherit from compiled traits");' @@ -669,7 +669,7 @@ def generate_traverse_for_class(cl: ClassIR, emitter.emit_line('{') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): - emitter.emit_gc_visit('self->{}'.format(emitter.attr(attr)), rtype) + emitter.emit_gc_visit(f'self->{emitter.attr(attr)}', rtype) if cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that @@ -687,11 +687,11 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: emitter.emit_line('static int') - emitter.emit_line('{}({} *self)'.format(func_name, cl.struct_name(emitter.names))) + emitter.emit_line(f'{func_name}({cl.struct_name(emitter.names)} *self)') emitter.emit_line('{') for base in reversed(cl.base_mro): for attr, rtype in base.attributes.items(): - emitter.emit_gc_clear('self->{}'.format(emitter.attr(attr)), rtype) + emitter.emit_gc_clear(f'self->{emitter.attr(attr)}', rtype) if cl.has_dict: struct_name = cl.struct_name(emitter.names) # __dict__ lives right after the struct and __weakref__ lives right after that @@ -710,12 +710,12 @@ def generate_dealloc_for_class(cl: ClassIR, clear_func_name: str, emitter: Emitter) -> None: emitter.emit_line('static void') - emitter.emit_line('{}({} *self)'.format(dealloc_func_name, cl.struct_name(emitter.names))) + emitter.emit_line(f'{dealloc_func_name}({cl.struct_name(emitter.names)} *self)') emitter.emit_line('{') emitter.emit_line('PyObject_GC_UnTrack(self);') # The trashcan is needed to handle deep recursive deallocations - emitter.emit_line('CPy_TRASHCAN_BEGIN(self, {})'.format(dealloc_func_name)) - emitter.emit_line('{}(self);'.format(clear_func_name)) + emitter.emit_line(f'CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})') + emitter.emit_line(f'{clear_func_name}(self);') emitter.emit_line('Py_TYPE(self)->tp_free((PyObject *)self);') emitter.emit_line('CPy_TRASHCAN_END(self)') emitter.emit_line('}') @@ -724,12 +724,12 @@ def generate_dealloc_for_class(cl: ClassIR, def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: - emitter.emit_line('static PyMethodDef {}[] = {{'.format(name)) + emitter.emit_line(f'static PyMethodDef {name}[] = {{') for fn in cl.methods.values(): if fn.decl.is_prop_setter or fn.decl.is_prop_getter: continue - emitter.emit_line('{{"{}",'.format(fn.name)) - emitter.emit_line(' (PyCFunction){}{},'.format(PREFIX, fn.cname(emitter.names))) + emitter.emit_line(f'{{"{fn.name}",') + emitter.emit_line(f' (PyCFunction){PREFIX}{fn.cname(emitter.names)},') if use_fastcall(emitter.capi_version): flags = ['METH_FASTCALL'] else: @@ -758,10 +758,10 @@ def generate_side_table_for_class(cl: ClassIR, type: str, slots: Dict[str, str], emitter: Emitter) -> Optional[str]: - name = '{}_{}'.format(cl.name_prefix(emitter.names), name) - emitter.emit_line('static {} {} = {{'.format(type, name)) + name = f'{cl.name_prefix(emitter.names)}_{name}' + emitter.emit_line(f'static {type} {name} = {{') for field, value in slots.items(): - emitter.emit_line(".{} = {},".format(field, value)) + emitter.emit_line(f".{field} = {value},") emitter.emit_line("};") return name @@ -796,20 +796,20 @@ def generate_getseter_declarations(cl: ClassIR, emitter: Emitter) -> None: def generate_getseters_table(cl: ClassIR, name: str, emitter: Emitter) -> None: - emitter.emit_line('static PyGetSetDef {}[] = {{'.format(name)) + emitter.emit_line(f'static PyGetSetDef {name}[] = {{') if not cl.is_trait: for attr in cl.attributes: - emitter.emit_line('{{"{}",'.format(attr)) + emitter.emit_line(f'{{"{attr}",') emitter.emit_line(' (getter){}, (setter){},'.format( getter_name(cl, attr, emitter.names), setter_name(cl, attr, emitter.names))) emitter.emit_line(' NULL, NULL},') for prop in cl.properties: - emitter.emit_line('{{"{}",'.format(prop)) - emitter.emit_line(' (getter){},'.format(getter_name(cl, prop, emitter.names))) + emitter.emit_line(f'{{"{prop}",') + emitter.emit_line(f' (getter){getter_name(cl, prop, emitter.names)},') setter = cl.properties[prop][1] if setter: - emitter.emit_line(' (setter){},'.format(setter_name(cl, prop, emitter.names))) + emitter.emit_line(f' (setter){setter_name(cl, prop, emitter.names)},') emitter.emit_line('NULL, NULL},') else: emitter.emit_line('NULL, NULL, NULL},') @@ -845,15 +845,15 @@ def generate_getter(cl: ClassIR, emitter.emit_line('{}({} *self, void *closure)'.format(getter_name(cl, attr, emitter.names), cl.struct_name(emitter.names))) emitter.emit_line('{') - attr_expr = 'self->{}'.format(attr_field) + attr_expr = f'self->{attr_field}' emitter.emit_undefined_attr_check(rtype, attr_expr, '==', unlikely=True) emitter.emit_line('PyErr_SetString(PyExc_AttributeError,') emitter.emit_line(' "attribute {} of {} undefined");'.format(repr(attr), repr(cl.name))) emitter.emit_line('return NULL;') emitter.emit_line('}') - emitter.emit_inc_ref('self->{}'.format(attr_field), rtype) - emitter.emit_box('self->{}'.format(attr_field), 'retval', rtype, declare_dest=True) + emitter.emit_inc_ref(f'self->{attr_field}', rtype) + emitter.emit_box(f'self->{attr_field}', 'retval', rtype, declare_dest=True) emitter.emit_line('return retval;') emitter.emit_line('}') @@ -879,9 +879,9 @@ def generate_setter(cl: ClassIR, emitter.emit_line('}') if rtype.is_refcounted: - attr_expr = 'self->{}'.format(attr_field) + attr_expr = f'self->{attr_field}' emitter.emit_undefined_attr_check(rtype, attr_expr, '!=') - emitter.emit_dec_ref('self->{}'.format(attr_field), rtype) + emitter.emit_dec_ref(f'self->{attr_field}', rtype) emitter.emit_line('}') if deletable: @@ -895,7 +895,7 @@ def generate_setter(cl: ClassIR, emitter.emit_lines('if (!tmp)', ' return -1;') emitter.emit_inc_ref('tmp', rtype) - emitter.emit_line('self->{} = tmp;'.format(attr_field)) + emitter.emit_line(f'self->{attr_field} = tmp;') if deletable: emitter.emit_line('} else') emitter.emit_line(' self->{} = {};'.format(attr_field, diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 294f416520f2..91b3a539adf5 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -31,13 +31,13 @@ def native_function_type(fn: FuncIR, emitter: Emitter) -> str: args = ', '.join(emitter.ctype(arg.type) for arg in fn.args) or 'void' ret = emitter.ctype(fn.ret_type) - return '{} (*)({})'.format(ret, args) + return f'{ret} (*)({args})' def native_function_header(fn: FuncDecl, emitter: Emitter) -> str: args = [] for arg in fn.sig.args: - args.append('{}{}{}'.format(emitter.ctype_spaced(arg.type), REG_PREFIX, arg.name)) + args.append(f'{emitter.ctype_spaced(arg.type)}{REG_PREFIX}{arg.name}') return '{ret_type}{name}({args})'.format( ret_type=emitter.ctype_spaced(fn.sig.ret_type), @@ -54,7 +54,7 @@ def generate_native_function(fn: FuncIR, body = Emitter(emitter.context, names) visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name) - declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter))) + declarations.emit_line(f'{native_function_header(fn.decl, emitter)} {{') body.indent() for r in all_values(fn.arg_regs, fn.blocks): @@ -143,7 +143,7 @@ def visit_branch(self, op: Branch) -> None: cond = '' if op.op == Branch.BOOL: expr_result = self.reg(op.value) - cond = '{}{}'.format(neg, expr_result) + cond = f'{neg}{expr_result}' elif op.op == Branch.IS_ERROR: typ = op.value.type compare = '!=' if negated else '==' @@ -163,22 +163,22 @@ def visit_branch(self, op: Branch) -> None: # For error checks, tell the compiler the branch is unlikely if op.traceback_entry is not None or op.rare: if not negated_rare: - cond = 'unlikely({})'.format(cond) + cond = f'unlikely({cond})' else: - cond = 'likely({})'.format(cond) + cond = f'likely({cond})' if false is self.next_block: if op.traceback_entry is None: - self.emit_line('if ({}) goto {};'.format(cond, self.label(true))) + self.emit_line(f'if ({cond}) goto {self.label(true)};') else: - self.emit_line('if ({}) {{'.format(cond)) + self.emit_line(f'if ({cond}) {{') self.emit_traceback(op) self.emit_lines( 'goto %s;' % self.label(true), '}' ) else: - self.emit_line('if ({}) {{'.format(cond)) + self.emit_line(f'if ({cond}) {{') self.emit_traceback(op) self.emit_lines( 'goto %s;' % self.label(true), @@ -195,10 +195,10 @@ def visit_tuple_set(self, op: TupleSet) -> None: tuple_type = op.tuple_type self.emitter.declare_tuple_struct(tuple_type) if len(op.items) == 0: # empty tuple - self.emit_line('{}.empty_struct_error_flag = 0;'.format(dest)) + self.emit_line(f'{dest}.empty_struct_error_flag = 0;') else: for i, item in enumerate(op.items): - self.emit_line('{}.f{} = {};'.format(dest, i, self.reg(item))) + self.emit_line(f'{dest}.f{i} = {self.reg(item)};') self.emit_inc_ref(dest, tuple_type) def visit_assign(self, op: Assign) -> None: @@ -207,7 +207,7 @@ def visit_assign(self, op: Assign) -> None: # clang whines about self assignment (which we might generate # for some casts), so don't emit it. if dest != src: - self.emit_line('%s = %s;' % (dest, src)) + self.emit_line(f'{dest} = {src};') def visit_assign_multi(self, op: AssignMulti) -> None: typ = op.dest.type @@ -225,10 +225,10 @@ def visit_load_error_value(self, op: LoadErrorValue) -> None: if isinstance(op.type, RTuple): values = [self.c_undefined_value(item) for item in op.type.types] tmp = self.temp_name() - self.emit_line('%s %s = { %s };' % (self.ctype(op.type), tmp, ', '.join(values))) - self.emit_line('%s = %s;' % (self.reg(op), tmp)) + self.emit_line('{} {} = {{ {} }};'.format(self.ctype(op.type), tmp, ', '.join(values))) + self.emit_line(f'{self.reg(op)} = {tmp};') else: - self.emit_line('%s = %s;' % (self.reg(op), + self.emit_line('{} = {};'.format(self.reg(op), self.c_error_value(op.type))) def visit_load_literal(self, op: LoadLiteral) -> None: @@ -251,7 +251,7 @@ def get_attr_expr(self, obj: str, op: Union[GetAttr, SetAttr], decl_cl: ClassIR) classes, and *(obj + attr_offset) for attributes defined by traits. We also insert all necessary C casts here. """ - cast = '({} *)'.format(op.class_type.struct_name(self.emitter.names)) + cast = f'({op.class_type.struct_name(self.emitter.names)} *)' if decl_cl.is_trait and op.class_type.class_ir.is_trait: # For pure trait access find the offset first, offsets # are ordered by attribute position in the cl.attributes dict. @@ -259,23 +259,23 @@ def get_attr_expr(self, obj: str, op: Union[GetAttr, SetAttr], decl_cl: ClassIR) trait_attr_index = list(decl_cl.attributes).index(op.attr) # TODO: reuse these names somehow? offset = self.emitter.temp_name() - self.declarations.emit_line('size_t {};'.format(offset)) + self.declarations.emit_line(f'size_t {offset};') self.emitter.emit_line('{} = {};'.format( offset, 'CPy_FindAttrOffset({}, {}, {})'.format( self.emitter.type_struct_name(decl_cl), - '({}{})->vtable'.format(cast, obj), + f'({cast}{obj})->vtable', trait_attr_index, ) )) - attr_cast = '({} *)'.format(self.ctype(op.class_type.attr_type(op.attr))) - return '*{}((char *){} + {})'.format(attr_cast, obj, offset) + attr_cast = f'({self.ctype(op.class_type.attr_type(op.attr))} *)' + return f'*{attr_cast}((char *){obj} + {offset})' else: # Cast to something non-trait. Note: for this to work, all struct # members for non-trait classes must obey monotonic linear growth. if op.class_type.class_ir.is_trait: assert not decl_cl.is_trait - cast = '({} *)'.format(decl_cl.struct_name(self.emitter.names)) + cast = f'({decl_cl.struct_name(self.emitter.names)} *)' return '({}{})->{}'.format( cast, obj, self.emitter.attr(op.attr) ) @@ -301,7 +301,7 @@ def visit_get_attr(self, op: GetAttr) -> None: else: # Otherwise, use direct or offset struct access. attr_expr = self.get_attr_expr(obj, op, decl_cl) - self.emitter.emit_line('{} = {};'.format(dest, attr_expr)) + self.emitter.emit_line(f'{dest} = {attr_expr};') self.emitter.emit_undefined_attr_check( attr_rtype, dest, '==', unlikely=True ) @@ -371,8 +371,8 @@ def visit_set_attr(self, op: SetAttr) -> None: self.emitter.emit_line('}') # This steal the reference to src, so we don't need to increment the arg self.emitter.emit_lines( - '{} = {};'.format(attr_expr, src), - '{} = 1;'.format(dest), + f'{attr_expr} = {src};', + f'{dest} = 1;', ) PREFIX_MAP: Final = { @@ -392,7 +392,7 @@ def visit_load_static(self, op: LoadStatic) -> None: s = repr(op.ann) if not any(x in s for x in ('/*', '*/', '\0')): ann = ' /* %s */' % s - self.emit_line('%s = %s;%s' % (dest, name, ann)) + self.emit_line(f'{dest} = {name};{ann}') def visit_init_static(self, op: InitStatic) -> None: value = self.reg(op.value) @@ -400,13 +400,13 @@ def visit_init_static(self, op: InitStatic) -> None: name = self.emitter.static_name(op.identifier, op.module_name, prefix) if op.namespace == NAMESPACE_TYPE: value = '(PyTypeObject *)%s' % value - self.emit_line('%s = %s;' % (name, value)) + self.emit_line(f'{name} = {value};') self.emit_inc_ref(name, op.value.type) def visit_tuple_get(self, op: TupleGet) -> None: dest = self.reg(op) src = self.reg(op.src) - self.emit_line('{} = {}.f{};'.format(dest, src, op.index)) + self.emit_line(f'{dest} = {src}.f{op.index};') self.emit_inc_ref(dest, op.type) def get_dest_assign(self, dest: Value) -> str: @@ -421,7 +421,7 @@ def visit_call(self, op: Call) -> None: args = ', '.join(self.reg(arg) for arg in op.args) lib = self.emitter.get_group_prefix(op.fn) cname = op.fn.cname(self.names) - self.emit_line('%s%s%s%s(%s);' % (dest, lib, NATIVE_PREFIX, cname, args)) + self.emit_line(f'{dest}{lib}{NATIVE_PREFIX}{cname}({args});') def visit_method_call(self, op: MethodCall) -> None: """Call native method.""" @@ -441,7 +441,7 @@ def visit_method_call(self, op: MethodCall) -> None: # turned into the class for class methods obj_args = ( [] if method.decl.kind == FUNC_STATICMETHOD else - ['(PyObject *)Py_TYPE({})'.format(obj)] if method.decl.kind == FUNC_CLASSMETHOD else + [f'(PyObject *)Py_TYPE({obj})'] if method.decl.kind == FUNC_CLASSMETHOD else [obj]) args = ', '.join(obj_args + [self.reg(arg) for arg in op.args]) mtype = native_function_type(method, self.emitter) @@ -485,7 +485,7 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> None: if isinstance(op.value, str): message = op.value.replace('"', '\\"') self.emitter.emit_line( - 'PyErr_SetString(PyExc_{}, "{}");'.format(op.class_name, message)) + f'PyErr_SetString(PyExc_{op.class_name}, "{message}");') elif isinstance(op.value, Value): self.emitter.emit_line( 'PyErr_SetObject(PyExc_{}, {});'.format(op.class_name, @@ -493,8 +493,8 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> None: else: assert False, 'op value type must be either str or Value' else: - self.emitter.emit_line('PyErr_SetNone(PyExc_{});'.format(op.class_name)) - self.emitter.emit_line('{} = 0;'.format(self.reg(op))) + self.emitter.emit_line(f'PyErr_SetNone(PyExc_{op.class_name});') + self.emitter.emit_line(f'{self.reg(op)} = 0;') def visit_call_c(self, op: CallC) -> None: if op.is_void: @@ -502,13 +502,13 @@ def visit_call_c(self, op: CallC) -> None: else: dest = self.get_dest_assign(op) args = ', '.join(self.reg(arg) for arg in op.args) - self.emitter.emit_line("{}{}({});".format(dest, op.function_name, args)) + self.emitter.emit_line(f"{dest}{op.function_name}({args});") def visit_truncate(self, op: Truncate) -> None: dest = self.reg(op) value = self.reg(op.src) # for C backend the generated code are straight assignments - self.emit_line("{} = {};".format(dest, value)) + self.emit_line(f"{dest} = {value};") def visit_load_global(self, op: LoadGlobal) -> None: dest = self.reg(op) @@ -517,13 +517,13 @@ def visit_load_global(self, op: LoadGlobal) -> None: s = repr(op.ann) if not any(x in s for x in ('/*', '*/', '\0')): ann = ' /* %s */' % s - self.emit_line('%s = %s;%s' % (dest, op.identifier, ann)) + self.emit_line(f'{dest} = {op.identifier};{ann}') def visit_int_op(self, op: IntOp) -> None: dest = self.reg(op) lhs = self.reg(op.lhs) rhs = self.reg(op.rhs) - self.emit_line('%s = %s %s %s;' % (dest, lhs, op.op_str[op.op], rhs)) + self.emit_line(f'{dest} = {lhs} {op.op_str[op.op]} {rhs};') def visit_comparison_op(self, op: ComparisonOp) -> None: dest = self.reg(op) @@ -545,7 +545,7 @@ def visit_comparison_op(self, op: ComparisonOp) -> None: elif isinstance(op.rhs, Integer) and op.rhs.value < 0: # Force signed ==/!= with negative operand lhs_cast = self.emit_signed_int_cast(op.lhs.type) - self.emit_line('%s = %s%s %s %s%s;' % (dest, lhs_cast, lhs, + self.emit_line('{} = {}{} {} {}{};'.format(dest, lhs_cast, lhs, op.op_str[op.op], rhs_cast, rhs)) def visit_load_mem(self, op: LoadMem) -> None: @@ -553,7 +553,7 @@ def visit_load_mem(self, op: LoadMem) -> None: src = self.reg(op.src) # TODO: we shouldn't dereference to type that are pointer type so far type = self.ctype(op.type) - self.emit_line('%s = *(%s *)%s;' % (dest, type, src)) + self.emit_line(f'{dest} = *({type} *){src};') def visit_set_mem(self, op: SetMem) -> None: dest = self.reg(op.dest) @@ -562,7 +562,7 @@ def visit_set_mem(self, op: SetMem) -> None: # clang whines about self assignment (which we might generate # for some casts), so don't emit it. if dest != src: - self.emit_line('*(%s *)%s = %s;' % (dest_type, dest, src)) + self.emit_line(f'*({dest_type} *){dest} = {src};') def visit_get_element_ptr(self, op: GetElementPtr) -> None: dest = self.reg(op) @@ -570,14 +570,14 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None: # TODO: support tuple type assert isinstance(op.src_type, RStruct) assert op.field in op.src_type.names, "Invalid field name." - self.emit_line('%s = (%s)&((%s *)%s)->%s;' % (dest, op.type._ctype, op.src_type.name, + self.emit_line('{} = ({})&(({} *){})->{};'.format(dest, op.type._ctype, op.src_type.name, src, op.field)) def visit_load_address(self, op: LoadAddress) -> None: typ = op.type dest = self.reg(op) src = self.reg(op.src) if isinstance(op.src, Register) else op.src - self.emit_line('%s = (%s)&%s;' % (dest, typ._ctype, src)) + self.emit_line(f'{dest} = ({typ._ctype})&{src};') def visit_keep_alive(self, op: KeepAlive) -> None: # This is a no-op. diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 35aa0046dcf9..6eea3f1ea881 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -344,7 +344,7 @@ def write_cache( # If the metadata isn't there, skip writing the cache. try: meta_data = result.manager.metastore.read(meta_path) - except IOError: + except OSError: continue newpath = get_state_ir_cache_name(st) @@ -422,15 +422,15 @@ def compile_modules_to_c( def generate_function_declaration(fn: FuncIR, emitter: Emitter) -> None: emitter.context.declarations[emitter.native_function_name(fn.decl)] = HeaderDeclaration( - '{};'.format(native_function_header(fn.decl, emitter)), + f'{native_function_header(fn.decl, emitter)};', needs_export=True) if fn.name != TOP_LEVEL_NAME: if is_fastcall_supported(fn, emitter.capi_version): emitter.context.declarations[PREFIX + fn.cname(emitter.names)] = HeaderDeclaration( - '{};'.format(wrapper_function_header(fn, emitter.names))) + f'{wrapper_function_header(fn, emitter.names)};') else: emitter.context.declarations[PREFIX + fn.cname(emitter.names)] = HeaderDeclaration( - '{};'.format(legacy_wrapper_function_header(fn, emitter.names))) + f'{legacy_wrapper_function_header(fn, emitter.names)};') def pointerize(decl: str, name: str) -> str: @@ -438,10 +438,10 @@ def pointerize(decl: str, name: str) -> str: # This doesn't work in general but does work for all our types... if '(' in decl: # Function pointer. Stick an * in front of the name and wrap it in parens. - return decl.replace(name, '(*{})'.format(name)) + return decl.replace(name, f'(*{name})') else: # Non-function pointer. Just stick an * in front of the name. - return decl.replace(name, '*{}'.format(name)) + return decl.replace(name, f'*{name}') def group_dir(group_name: str) -> str: @@ -506,9 +506,9 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: # reduce the number of compiler invocations needed if self.compiler_options.include_runtime_files: for name in RUNTIME_C_FILES: - base_emitter.emit_line('#include "{}"'.format(name)) - base_emitter.emit_line('#include "__native{}.h"'.format(self.short_group_suffix)) - base_emitter.emit_line('#include "__native_internal{}.h"'.format(self.short_group_suffix)) + base_emitter.emit_line(f'#include "{name}"') + base_emitter.emit_line(f'#include "__native{self.short_group_suffix}.h"') + base_emitter.emit_line(f'#include "__native_internal{self.short_group_suffix}.h"') emitter = base_emitter self.generate_literal_tables() @@ -516,9 +516,9 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: for module_name, module in self.modules: if multi_file: emitter = Emitter(self.context) - emitter.emit_line('#include "__native{}.h"'.format(self.short_group_suffix)) + emitter.emit_line(f'#include "__native{self.short_group_suffix}.h"') emitter.emit_line( - '#include "__native_internal{}.h"'.format(self.short_group_suffix)) + f'#include "__native_internal{self.short_group_suffix}.h"') self.declare_module(module_name, emitter) self.declare_internal_globals(module_name, emitter) @@ -543,7 +543,7 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: generate_legacy_wrapper_function( fn, emitter, self.source_paths[module_name], module_name) if multi_file: - name = ('__native_{}.c'.format(emitter.names.private_name(module_name))) + name = (f'__native_{emitter.names.private_name(module_name)}.c') file_contents.append((name, ''.join(emitter.fragments))) # The external header file contains type declarations while @@ -551,17 +551,17 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: # (which are shared between shared libraries via dynamic # exports tables and not accessed directly.) ext_declarations = Emitter(self.context) - ext_declarations.emit_line('#ifndef MYPYC_NATIVE{}_H'.format(self.group_suffix)) - ext_declarations.emit_line('#define MYPYC_NATIVE{}_H'.format(self.group_suffix)) + ext_declarations.emit_line(f'#ifndef MYPYC_NATIVE{self.group_suffix}_H') + ext_declarations.emit_line(f'#define MYPYC_NATIVE{self.group_suffix}_H') ext_declarations.emit_line('#include ') ext_declarations.emit_line('#include ') declarations = Emitter(self.context) - declarations.emit_line('#ifndef MYPYC_NATIVE_INTERNAL{}_H'.format(self.group_suffix)) - declarations.emit_line('#define MYPYC_NATIVE_INTERNAL{}_H'.format(self.group_suffix)) + declarations.emit_line(f'#ifndef MYPYC_NATIVE_INTERNAL{self.group_suffix}_H') + declarations.emit_line(f'#define MYPYC_NATIVE_INTERNAL{self.group_suffix}_H') declarations.emit_line('#include ') declarations.emit_line('#include ') - declarations.emit_line('#include "__native{}.h"'.format(self.short_group_suffix)) + declarations.emit_line(f'#include "__native{self.short_group_suffix}.h"') declarations.emit_line() declarations.emit_line('int CPyGlobalsInit(void);') declarations.emit_line() @@ -578,9 +578,9 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: short_lib = exported_name(lib.split('.')[-1]) declarations.emit_lines( '#include <{}>'.format( - os.path.join(group_dir(lib), "__native_{}.h".format(short_lib)) + os.path.join(group_dir(lib), f"__native_{short_lib}.h") ), - 'struct export_table_{} exports_{};'.format(elib, elib) + f'struct export_table_{elib} exports_{elib};' ) sorted_decls = self.toposort_declarations() @@ -594,7 +594,7 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: decls = ext_declarations if declaration.is_type else declarations if not declaration.is_type: decls.emit_lines( - 'extern {}'.format(declaration.decl[0]), *declaration.decl[1:]) + f'extern {declaration.decl[0]}', *declaration.decl[1:]) # If there is a definition, emit it. Otherwise repeat the declaration # (without an extern). if declaration.defn: @@ -614,11 +614,11 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: output_dir = group_dir(self.group_name) if self.group_name else '' return file_contents + [ - (os.path.join(output_dir, '__native{}.c'.format(self.short_group_suffix)), + (os.path.join(output_dir, f'__native{self.short_group_suffix}.c'), ''.join(emitter.fragments)), - (os.path.join(output_dir, '__native_internal{}.h'.format(self.short_group_suffix)), + (os.path.join(output_dir, f'__native_internal{self.short_group_suffix}.h'), ''.join(declarations.fragments)), - (os.path.join(output_dir, '__native{}.h'.format(self.short_group_suffix)), + (os.path.join(output_dir, f'__native{self.short_group_suffix}.h'), ''.join(ext_declarations.fragments)), ] @@ -699,7 +699,7 @@ def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) -> decl_emitter.emit_lines( '', - 'struct export_table{} {{'.format(self.group_suffix), + f'struct export_table{self.group_suffix} {{', ) for name, decl in decls.items(): if decl.needs_export: @@ -709,11 +709,11 @@ def generate_export_table(self, decl_emitter: Emitter, code_emitter: Emitter) -> code_emitter.emit_lines( '', - 'static struct export_table{} exports = {{'.format(self.group_suffix), + f'static struct export_table{self.group_suffix} exports = {{', ) for name, decl in decls.items(): if decl.needs_export: - code_emitter.emit_line('&{},'.format(name)) + code_emitter.emit_line(f'&{name},') code_emitter.emit_line('};') @@ -772,13 +772,13 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: for mod, _ in self.modules: name = exported_name(mod) emitter.emit_lines( - 'extern PyObject *CPyInit_{}(void);'.format(name), + f'extern PyObject *CPyInit_{name}(void);', 'capsule = PyCapsule_New((void *)CPyInit_{}, "{}.init_{}", NULL);'.format( name, shared_lib_name(self.group_name), name), 'if (!capsule) {', 'goto fail;', '}', - 'res = PyObject_SetAttrString(module, "init_{}", capsule);'.format(name), + f'res = PyObject_SetAttrString(module, "init_{name}", capsule);', 'Py_DECREF(capsule);', 'if (res < 0) {', 'goto fail;', @@ -793,7 +793,7 @@ def generate_shared_lib_init(self, emitter: Emitter) -> None: shared_lib_name(group)), 'struct export_table_{} *pexports_{} = PyCapsule_Import("{}.exports", 0);'.format( egroup, egroup, shared_lib_name(group)), - 'if (!pexports_{}) {{'.format(egroup), + f'if (!pexports_{egroup}) {{', 'goto fail;', '}', 'memcpy(&exports_{group}, pexports_{group}, sizeof(exports_{group}));'.format( @@ -821,10 +821,10 @@ def generate_globals_init(self, emitter: Emitter) -> None: emitter.emit_line('CPy_Init();') for symbol, fixup in self.simple_inits: - emitter.emit_line('{} = {};'.format(symbol, fixup)) + emitter.emit_line(f'{symbol} = {fixup};') values = 'CPyLit_Str, CPyLit_Bytes, CPyLit_Int, CPyLit_Float, CPyLit_Complex, CPyLit_Tuple' - emitter.emit_lines('if (CPyStatics_Initialize(CPyStatics, {}) < 0) {{'.format(values), + emitter.emit_lines(f'if (CPyStatics_Initialize(CPyStatics, {values}) < 0) {{', 'return -1;', '}') @@ -838,7 +838,7 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module """Emit the PyModuleDef struct for a module and the module init function.""" # Emit module methods module_prefix = emitter.names.private_name(module_name) - emitter.emit_line('static PyMethodDef {}module_methods[] = {{'.format(module_prefix)) + emitter.emit_line(f'static PyMethodDef {module_prefix}module_methods[] = {{') for fn in module.functions: if fn.class_name is not None or fn.name == TOP_LEVEL_NAME: continue @@ -859,13 +859,13 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module emitter.emit_line() # Emit module definition struct - emitter.emit_lines('static struct PyModuleDef {}module = {{'.format(module_prefix), + emitter.emit_lines(f'static struct PyModuleDef {module_prefix}module = {{', 'PyModuleDef_HEAD_INIT,', - '"{}",'.format(module_name), + f'"{module_name}",', 'NULL, /* docstring */', '-1, /* size of per-interpreter state of the module,', ' or -1 if the module keeps state in global variables. */', - '{}module_methods'.format(module_prefix), + f'{module_prefix}module_methods', '};') emitter.emit_line() # Emit module init function. If we are compiling just one module, this @@ -874,9 +874,9 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module # the shared library, and in this case we use an internal module # initialized function that will be called by the shim. if not self.use_shared_lib: - declaration = 'PyMODINIT_FUNC PyInit_{}(void)'.format(module_name) + declaration = f'PyMODINIT_FUNC PyInit_{module_name}(void)' else: - declaration = 'PyObject *CPyInit_{}(void)'.format(exported_name(module_name)) + declaration = f'PyObject *CPyInit_{exported_name(module_name)}(void)' emitter.emit_lines(declaration, '{') emitter.emit_line('PyObject* modname = NULL;') @@ -887,21 +887,21 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module # imported, whereas this we want to have to stop a circular import. module_static = self.module_internal_static_name(module_name, emitter) - emitter.emit_lines('if ({}) {{'.format(module_static), - 'Py_INCREF({});'.format(module_static), - 'return {};'.format(module_static), + emitter.emit_lines(f'if ({module_static}) {{', + f'Py_INCREF({module_static});', + f'return {module_static};', '}') - emitter.emit_lines('{} = PyModule_Create(&{}module);'.format(module_static, module_prefix), - 'if (unlikely({} == NULL))'.format(module_static), + emitter.emit_lines(f'{module_static} = PyModule_Create(&{module_prefix}module);', + f'if (unlikely({module_static} == NULL))', ' goto fail;') emitter.emit_line( 'modname = PyObject_GetAttrString((PyObject *){}, "__name__");'.format( module_static)) module_globals = emitter.static_name('globals', module_name) - emitter.emit_lines('{} = PyModule_GetDict({});'.format(module_globals, module_static), - 'if (unlikely({} == NULL))'.format(module_globals), + emitter.emit_lines(f'{module_globals} = PyModule_GetDict({module_static});', + f'if (unlikely({module_globals} == NULL))', ' goto fail;') # HACK: Manually instantiate generated classes here @@ -914,7 +914,7 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module '{t} = (PyTypeObject *)CPyType_FromTemplate(' '(PyObject *){t}_template, NULL, modname);' .format(t=type_struct)) - emitter.emit_lines('if (unlikely(!{}))'.format(type_struct), + emitter.emit_lines(f'if (unlikely(!{type_struct}))', ' goto fail;') emitter.emit_lines('if (CPyGlobalsInit() < 0)', @@ -924,19 +924,19 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module emitter.emit_lines('Py_DECREF(modname);') - emitter.emit_line('return {};'.format(module_static)) + emitter.emit_line(f'return {module_static};') emitter.emit_lines('fail:', - 'Py_CLEAR({});'.format(module_static), + f'Py_CLEAR({module_static});', 'Py_CLEAR(modname);') for name, typ in module.final_names: static_name = emitter.static_name(name, module_name) emitter.emit_dec_ref(static_name, typ, is_xdec=True) undef = emitter.c_undefined_value(typ) - emitter.emit_line('{} = {};'.format(static_name, undef)) + emitter.emit_line(f'{static_name} = {undef};') # the type objects returned from CPyType_FromTemplate are all new references # so we have to decref them for t in type_structs: - emitter.emit_line('Py_CLEAR({});'.format(t)) + emitter.emit_line(f'Py_CLEAR({t});') emitter.emit_line('return NULL;') emitter.emit_line('}') @@ -946,7 +946,7 @@ def generate_top_level_call(self, module: ModuleIR, emitter: Emitter) -> None: for fn in reversed(module.functions): if fn.name == TOP_LEVEL_NAME: emitter.emit_lines( - 'char result = {}();'.format(emitter.native_function_name(fn.decl)), + f'char result = {emitter.native_function_name(fn.decl)}();', 'if (result == 2)', ' goto fail;', ) @@ -986,18 +986,18 @@ def declare_global(self, type_spaced: str, name: str, *, initializer: Optional[str] = None) -> None: if '[' not in type_spaced: - base = '{}{}'.format(type_spaced, name) + base = f'{type_spaced}{name}' else: a, b = type_spaced.split('[', 1) - base = '{}{}[{}'.format(a, name, b) + base = f'{a}{name}[{b}' if not initializer: defn = None else: - defn = ['{} = {};'.format(base, initializer)] + defn = [f'{base} = {initializer};'] if name not in self.context.declarations: self.context.declarations[name] = HeaderDeclaration( - '{};'.format(base), + f'{base};', defn=defn, ) @@ -1028,7 +1028,7 @@ def declare_finals( for name, typ in final_names: static_name = emitter.static_name(name, module) emitter.context.declarations[static_name] = HeaderDeclaration( - '{}{};'.format(emitter.ctype_spaced(typ), static_name), + f'{emitter.ctype_spaced(typ)}{static_name};', [self.final_definition(module, name, typ, emitter)], needs_export=True) @@ -1041,7 +1041,7 @@ def final_definition( undefined = '{{ {} }}'.format(''.join(emitter.tuple_undefined_value_helper(typ))) else: undefined = emitter.c_undefined_value(typ) - return '{}{} = {};'.format(emitter.ctype_spaced(typ), static_name, undefined) + return f'{emitter.ctype_spaced(typ)}{static_name} = {undefined};' def declare_static_pyobject(self, identifier: str, emitter: Emitter) -> None: symbol = emitter.static_name(identifier, None) diff --git a/mypyc/codegen/emitwrapper.py b/mypyc/codegen/emitwrapper.py index dd08bdb40bf3..a68438c5f0db 100644 --- a/mypyc/codegen/emitwrapper.py +++ b/mypyc/codegen/emitwrapper.py @@ -86,8 +86,8 @@ def reorder_arg_groups(groups: Dict[ArgKind, List[RuntimeArg]]) -> List[RuntimeA def make_static_kwlist(args: List[RuntimeArg]) -> str: - arg_names = ''.join('"{}", '.format(arg.name) for arg in args) - return 'static const char * const kwlist[] = {{{}0}};'.format(arg_names) + arg_names = ''.join(f'"{arg.name}", ' for arg in args) + return f'static const char * const kwlist[] = {{{arg_names}0}};' def make_format_string(func_name: Optional[str], groups: Dict[ArgKind, List[RuntimeArg]]) -> str: @@ -116,7 +116,7 @@ def make_format_string(func_name: Optional[str], groups: Dict[ArgKind, List[Runt if groups[ARG_NAMED]: format += '@' + 'O' * len(groups[ARG_NAMED]) if func_name is not None: - format += ':{}'.format(func_name) + format += f':{func_name}' return format @@ -129,13 +129,13 @@ def generate_wrapper_function(fn: FuncIR, In particular, this handles unboxing the arguments, calling the native function, and then boxing the return value. """ - emitter.emit_line('{} {{'.format(wrapper_function_header(fn, emitter.names))) + emitter.emit_line(f'{wrapper_function_header(fn, emitter.names)} {{') # If fn is a method, then the first argument is a self param real_args = list(fn.args) if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD: arg = real_args.pop(0) - emitter.emit_line('PyObject *obj_{} = self;'.format(arg.name)) + emitter.emit_line(f'PyObject *obj_{arg.name} = self;') # Need to order args as: required, optional, kwonly optional, kwonly required # This is because CPyArg_ParseStackAndKeywords format string requires @@ -146,20 +146,20 @@ def generate_wrapper_function(fn: FuncIR, emitter.emit_line(make_static_kwlist(reordered_args)) fmt = make_format_string(fn.name, groups) # Define the arguments the function accepts (but no types yet) - emitter.emit_line('static CPyArg_Parser parser = {{"{}", kwlist, 0}};'.format(fmt)) + emitter.emit_line(f'static CPyArg_Parser parser = {{"{fmt}", kwlist, 0}};') for arg in real_args: emitter.emit_line('PyObject *obj_{}{};'.format( arg.name, ' = NULL' if arg.optional else '')) - cleanups = ['CPy_DECREF(obj_{});'.format(arg.name) + cleanups = [f'CPy_DECREF(obj_{arg.name});' for arg in groups[ARG_STAR] + groups[ARG_STAR2]] arg_ptrs: List[str] = [] if groups[ARG_STAR] or groups[ARG_STAR2]: - arg_ptrs += ['&obj_{}'.format(groups[ARG_STAR][0].name) if groups[ARG_STAR] else 'NULL'] - arg_ptrs += ['&obj_{}'.format(groups[ARG_STAR2][0].name) if groups[ARG_STAR2] else 'NULL'] - arg_ptrs += ['&obj_{}'.format(arg.name) for arg in reordered_args] + arg_ptrs += [f'&obj_{groups[ARG_STAR][0].name}' if groups[ARG_STAR] else 'NULL'] + arg_ptrs += [f'&obj_{groups[ARG_STAR2][0].name}' if groups[ARG_STAR2] else 'NULL'] + arg_ptrs += [f'&obj_{arg.name}' for arg in reordered_args] if fn.name == '__call__' and use_vectorcall(emitter.capi_version): nargs = 'PyVectorcall_NARGS(nargs)' @@ -212,13 +212,13 @@ def generate_legacy_wrapper_function(fn: FuncIR, In particular, this handles unboxing the arguments, calling the native function, and then boxing the return value. """ - emitter.emit_line('{} {{'.format(legacy_wrapper_function_header(fn, emitter.names))) + emitter.emit_line(f'{legacy_wrapper_function_header(fn, emitter.names)} {{') # If fn is a method, then the first argument is a self param real_args = list(fn.args) if fn.class_name and not fn.decl.kind == FUNC_STATICMETHOD: arg = real_args.pop(0) - emitter.emit_line('PyObject *obj_{} = self;'.format(arg.name)) + emitter.emit_line(f'PyObject *obj_{arg.name} = self;') # Need to order args as: required, optional, kwonly optional, kwonly required # This is because CPyArg_ParseTupleAndKeywords format string requires @@ -231,14 +231,14 @@ def generate_legacy_wrapper_function(fn: FuncIR, emitter.emit_line('PyObject *obj_{}{};'.format( arg.name, ' = NULL' if arg.optional else '')) - cleanups = ['CPy_DECREF(obj_{});'.format(arg.name) + cleanups = [f'CPy_DECREF(obj_{arg.name});' for arg in groups[ARG_STAR] + groups[ARG_STAR2]] arg_ptrs: List[str] = [] if groups[ARG_STAR] or groups[ARG_STAR2]: - arg_ptrs += ['&obj_{}'.format(groups[ARG_STAR][0].name) if groups[ARG_STAR] else 'NULL'] - arg_ptrs += ['&obj_{}'.format(groups[ARG_STAR2][0].name) if groups[ARG_STAR2] else 'NULL'] - arg_ptrs += ['&obj_{}'.format(arg.name) for arg in reordered_args] + arg_ptrs += [f'&obj_{groups[ARG_STAR][0].name}' if groups[ARG_STAR] else 'NULL'] + arg_ptrs += [f'&obj_{groups[ARG_STAR2][0].name}' if groups[ARG_STAR2] else 'NULL'] + arg_ptrs += [f'&obj_{arg.name}' for arg in reordered_args] emitter.emit_lines( 'if (!CPyArg_ParseTupleAndKeywords(args, kw, "{}", "{}", kwlist{})) {{'.format( @@ -322,7 +322,7 @@ def generate_bin_op_forward_only_wrapper(fn: FuncIR, # return NotImplemented # ... rmethod = reverse_op_methods[fn.name] - emitter.emit_line('_Py_IDENTIFIER({});'.format(rmethod)) + emitter.emit_line(f'_Py_IDENTIFIER({rmethod});') emitter.emit_line( 'return CPy_CallReverseOpMethod(obj_left, obj_right, "{}", &PyId_{});'.format( op_methods_to_symbols[fn.name], @@ -370,7 +370,7 @@ def generate_bin_op_both_wrappers(cl: ClassIR, gen.emit_call() gen.emit_error_handling() emitter.emit_line('} else {') - emitter.emit_line('_Py_IDENTIFIER({});'.format(fn_rev.name)) + emitter.emit_line(f'_Py_IDENTIFIER({fn_rev.name});') emitter.emit_line( 'return CPy_CallReverseOpMethod(obj_left, obj_right, "{}", &PyId_{});'.format( op_methods_to_symbols[fn.name], @@ -395,18 +395,18 @@ def generate_bin_op_both_wrappers(cl: ClassIR, def generate_richcompare_wrapper(cl: ClassIR, emitter: Emitter) -> Optional[str]: """Generates a wrapper for richcompare dunder methods.""" # Sort for determinism on Python 3.5 - matches = sorted([name for name in RICHCOMPARE_OPS if cl.has_method(name)]) + matches = sorted(name for name in RICHCOMPARE_OPS if cl.has_method(name)) if not matches: return None - name = '{}_RichCompare_{}'.format(DUNDER_PREFIX, cl.name_prefix(emitter.names)) + name = f'{DUNDER_PREFIX}_RichCompare_{cl.name_prefix(emitter.names)}' emitter.emit_line( 'static PyObject *{name}(PyObject *obj_lhs, PyObject *obj_rhs, int op) {{'.format( name=name) ) emitter.emit_line('switch (op) {') for func in matches: - emitter.emit_line('case {}: {{'.format(RICHCOMPARE_OPS[func])) + emitter.emit_line(f'case {RICHCOMPARE_OPS[func]}: {{') method = cl.get_method(func) assert method is not None generate_wrapper_core(method, emitter, arg_names=['lhs', 'rhs']) @@ -423,7 +423,7 @@ def generate_richcompare_wrapper(cl: ClassIR, emitter: Emitter) -> Optional[str] def generate_get_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __get__ methods.""" - name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) + name = f'{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}' emitter.emit_line( 'static PyObject *{name}(PyObject *self, PyObject *instance, PyObject *owner) {{'. format(name=name)) @@ -438,7 +438,7 @@ def generate_get_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: def generate_hash_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __hash__ methods.""" - name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) + name = f'{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}' emitter.emit_line('static Py_ssize_t {name}(PyObject *self) {{'.format( name=name )) @@ -463,7 +463,7 @@ def generate_hash_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: def generate_len_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __len__ methods.""" - name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) + name = f'{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}' emitter.emit_line('static Py_ssize_t {name}(PyObject *self) {{'.format( name=name )) @@ -486,7 +486,7 @@ def generate_len_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: def generate_bool_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __bool__ methods.""" - name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) + name = f'{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}' emitter.emit_line('static int {name}(PyObject *self) {{'.format( name=name )) @@ -510,7 +510,7 @@ def generate_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: This is only called from a combined __delitem__/__setitem__ wrapper. """ name = '{}{}{}'.format(DUNDER_PREFIX, '__delitem__', cl.name_prefix(emitter.names)) - input_args = ', '.join('PyObject *obj_{}'.format(arg.name) for arg in fn.args) + input_args = ', '.join(f'PyObject *obj_{arg.name}' for arg in fn.args) emitter.emit_line('static int {name}({input_args}) {{'.format( name=name, input_args=input_args, @@ -541,14 +541,14 @@ def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> args = list(args) + [RuntimeArg('___value', object_rprimitive, ARG_POS)] name = '{}{}{}'.format(DUNDER_PREFIX, '__setitem__', cl.name_prefix(emitter.names)) - input_args = ', '.join('PyObject *obj_{}'.format(arg.name) for arg in args) + input_args = ', '.join(f'PyObject *obj_{arg.name}' for arg in args) emitter.emit_line('static int {name}({input_args}) {{'.format( name=name, input_args=input_args, )) # First check if this is __delitem__ - emitter.emit_line('if (obj_{} == NULL) {{'.format(args[2].name)) + emitter.emit_line(f'if (obj_{args[2].name} == NULL) {{') if del_name is not None: # We have a native implementation, so call it emitter.emit_line('return {}(obj_{}, obj_{});'.format(del_name, @@ -557,7 +557,7 @@ def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> else: # Try to call superclass method instead emitter.emit_line( - 'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});'.format(args[0].name)) + f'PyObject *super = CPy_Super(CPyModule_builtins, obj_{args[0].name});') emitter.emit_line('if (super == NULL) return -1;') emitter.emit_line( 'PyObject *result = PyObject_CallMethod(super, "__delitem__", "O", obj_{});'.format( @@ -572,14 +572,14 @@ def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> generate_set_del_item_wrapper_inner(fn, emitter, args) else: emitter.emit_line( - 'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});'.format(args[0].name)) + f'PyObject *super = CPy_Super(CPyModule_builtins, obj_{args[0].name});') emitter.emit_line('if (super == NULL) return -1;') emitter.emit_line('PyObject *result;') if method_cls is None and cl.builtin_base is None: - msg = "'{}' object does not support item assignment".format(cl.name) + msg = f"'{cl.name}' object does not support item assignment" emitter.emit_line( - 'PyErr_SetString(PyExc_TypeError, "{}");'.format(msg)) + f'PyErr_SetString(PyExc_TypeError, "{msg}");') emitter.emit_line('result = NULL;') else: # A base class may have __setitem__ @@ -597,7 +597,7 @@ def generate_set_del_item_wrapper_inner(fn: FuncIR, emitter: Emitter, args: Sequence[RuntimeArg]) -> None: for arg in args: generate_arg_check(arg.name, arg.type, emitter, GotoHandler('fail')) - native_args = ', '.join('arg_{}'.format(arg.name) for arg in args) + native_args = ', '.join(f'arg_{arg.name}' for arg in args) emitter.emit_line('{}val = {}{}({});'.format(emitter.ctype_spaced(fn.ret_type), NATIVE_PREFIX, fn.cname(emitter.names), @@ -612,7 +612,7 @@ def generate_set_del_item_wrapper_inner(fn: FuncIR, emitter: Emitter, def generate_contains_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for a native __contains__ method.""" - name = '{}{}{}'.format(DUNDER_PREFIX, fn.name, cl.name_prefix(emitter.names)) + name = f'{DUNDER_PREFIX}{fn.name}{cl.name_prefix(emitter.names)}' emitter.emit_line( 'static int {name}(PyObject *self, PyObject *obj_item) {{'. format(name=name)) @@ -676,8 +676,8 @@ def generate_arg_check(name: str, error = error or AssignHandler() if typ.is_unboxed: # Borrow when unboxing to avoid reference count manipulation. - emitter.emit_unbox('obj_{}'.format(name), - 'arg_{}'.format(name), + emitter.emit_unbox(f'obj_{name}', + f'arg_{name}', typ, declare_dest=True, raise_exception=raise_exception, @@ -687,15 +687,15 @@ def generate_arg_check(name: str, elif is_object_rprimitive(typ): # Object is trivial since any object is valid if optional: - emitter.emit_line('PyObject *arg_{};'.format(name)) - emitter.emit_line('if (obj_{} == NULL) {{'.format(name)) - emitter.emit_line('arg_{} = {};'.format(name, emitter.c_error_value(typ))) - emitter.emit_lines('} else {', 'arg_{} = obj_{}; '.format(name, name), '}') + emitter.emit_line(f'PyObject *arg_{name};') + emitter.emit_line(f'if (obj_{name} == NULL) {{') + emitter.emit_line(f'arg_{name} = {emitter.c_error_value(typ)};') + emitter.emit_lines('} else {', f'arg_{name} = obj_{name}; ', '}') else: - emitter.emit_line('PyObject *arg_{} = obj_{};'.format(name, name)) + emitter.emit_line(f'PyObject *arg_{name} = obj_{name};') else: - emitter.emit_cast('obj_{}'.format(name), - 'arg_{}'.format(name), + emitter.emit_cast(f'obj_{name}', + f'arg_{name}', typ, declare_dest=True, raise_exception=raise_exception, @@ -739,7 +739,7 @@ def use_goto(self) -> bool: def emit_header(self) -> None: """Emit the function header of the wrapper implementation.""" - input_args = ', '.join('PyObject *obj_{}'.format(arg) for arg in self.arg_names) + input_args = ', '.join(f'PyObject *obj_{arg}' for arg in self.arg_names) self.emitter.emit_line('static PyObject *{name}({input_args}) {{'.format( name=self.wrapper_name(), input_args=input_args, @@ -766,7 +766,7 @@ def emit_call(self, not_implemented_handler: str = '') -> None: If not_implemented_handler is non-empty, use this C code to handle a NotImplemented return value (if it's possible based on the return type). """ - native_args = ', '.join('arg_{}'.format(arg) for arg in self.arg_names) + native_args = ', '.join(f'arg_{arg}' for arg in self.arg_names) ret_type = self.ret_type emitter = self.emitter if ret_type.is_unboxed or self.use_goto(): diff --git a/mypyc/common.py b/mypyc/common.py index 6080649f7eb6..e07bbe2511cb 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -75,7 +75,7 @@ def shared_lib_name(group_name: str) -> str: (This just adds a suffix to the final component.) """ - return '{}__mypyc'.format(group_name) + return f'{group_name}__mypyc' def short_name(name: str) -> str: @@ -107,7 +107,7 @@ def get_id_from_name(name: str, fullname: str, line: int) -> str: it handles the case where the function is named '_', in which case multiple different functions could have the same name.""" if unnamed_function(name): - return "{}.{}".format(fullname, line) + return f"{fullname}.{line}" else: return fullname @@ -115,7 +115,7 @@ def get_id_from_name(name: str, fullname: str, line: int) -> str: def short_id_from_name(func_name: str, shortname: str, line: Optional[int]) -> str: if unnamed_function(func_name): assert line is not None - partial_name = "{}.{}".format(shortname, line) + partial_name = f"{shortname}.{line}" else: partial_name = shortname return partial_name diff --git a/mypyc/crash.py b/mypyc/crash.py index 04948dd08dec..b248e27bbdb8 100644 --- a/mypyc/crash.py +++ b/mypyc/crash.py @@ -27,5 +27,5 @@ def crash_report(module_path: str, line: int) -> 'NoReturn': print('Traceback (most recent call last):') for s in traceback.format_list(tb + tb2): print(s.rstrip('\n')) - print('{}:{}: {}: {}'.format(module_path, line, type(err).__name__, err)) + print(f'{module_path}:{line}: {type(err).__name__}: {err}') raise SystemExit(2) diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index d6407610e2bc..2e3e2b15c930 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -75,6 +75,7 @@ ('method', FuncIR), ('shadow_method', Optional[FuncIR])]) + VTableEntries = List[VTableMethod] @@ -162,7 +163,7 @@ def __repr__(self) -> str: @property def fullname(self) -> str: - return "{}.{}".format(self.module_name, self.name) + return f"{self.module_name}.{self.name}" def real_base(self) -> Optional['ClassIR']: """Return the actual concrete base class, if there is one.""" @@ -172,7 +173,7 @@ def real_base(self) -> Optional['ClassIR']: def vtable_entry(self, name: str) -> int: assert self.vtable is not None, "vtable not computed yet" - assert name in self.vtable, '%r has no attribute %r' % (self.name, name) + assert name in self.vtable, f'{self.name!r} has no attribute {name!r}' return self.vtable[name] def attr_details(self, name: str) -> Tuple[RType, 'ClassIR']: @@ -181,7 +182,7 @@ def attr_details(self, name: str) -> Tuple[RType, 'ClassIR']: return ir.attributes[name], ir if name in ir.property_types: return ir.property_types[name], ir - raise KeyError('%r has no attribute %r' % (self.name, name)) + raise KeyError(f'{self.name!r} has no attribute {name!r}') def attr_type(self, name: str) -> RType: return self.attr_details(name)[0] @@ -190,7 +191,7 @@ def method_decl(self, name: str) -> FuncDecl: for ir in self.mro: if name in ir.method_decls: return ir.method_decls[name] - raise KeyError('%r has no attribute %r' % (self.name, name)) + raise KeyError(f'{self.name!r} has no attribute {name!r}') def method_sig(self, name: str) -> FuncSignature: return self.method_decl(name).sig @@ -234,7 +235,7 @@ def name_prefix(self, names: NameGenerator) -> str: return names.private_name(self.module_name, self.name) def struct_name(self, names: NameGenerator) -> str: - return '{}Object'.format(exported_name(self.fullname)) + return f'{exported_name(self.fullname)}Object' def get_method_and_class(self, name: str) -> Optional[Tuple[FuncIR, 'ClassIR']]: for ir in self.mro: diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index 1426b0ecdf0f..6a5a720e309b 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -31,7 +31,7 @@ def optional(self) -> bool: return self.kind.is_optional() def __repr__(self) -> str: - return 'RuntimeArg(name=%s, type=%s, optional=%r, pos_only=%r)' % ( + return 'RuntimeArg(name={}, type={}, optional={!r}, pos_only={!r})'.format( self.name, self.type, self.optional, self.pos_only) def serialize(self) -> JsonDict: @@ -58,7 +58,7 @@ def __init__(self, args: Sequence[RuntimeArg], ret_type: RType) -> None: self.ret_type = ret_type def __repr__(self) -> str: - return 'FuncSignature(args=%r, ret=%r)' % (self.args, self.ret_type) + return f'FuncSignature(args={self.args!r}, ret={self.ret_type!r})' def serialize(self) -> JsonDict: return {'args': [t.serialize() for t in self.args], 'ret_type': self.ret_type.serialize()} @@ -234,9 +234,9 @@ def cname(self, names: NameGenerator) -> str: def __repr__(self) -> str: if self.class_name: - return ''.format(self.class_name, self.name) + return f'' else: - return ''.format(self.name) + return f'' def serialize(self) -> JsonDict: # We don't include blocks in the serialized version diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 65480ebcc7c3..ecd2293c657f 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -145,7 +145,7 @@ def is_void(self) -> bool: return False def __repr__(self) -> str: - return '' % (self.name, hex(id(self))) + return f'' class Integer(Value): @@ -279,7 +279,7 @@ def targets(self) -> Sequence[BasicBlock]: def set_target(self, i: int, new: BasicBlock) -> None: """Update a basic block target.""" - raise AssertionError("Invalid set_target({}, {})".format(self, i)) + raise AssertionError(f"Invalid set_target({self}, {i})") class Goto(ControlOp): @@ -474,7 +474,7 @@ def __init__(self, src: Value, is_xdec: bool = False, line: int = -1) -> None: self.is_xdec = is_xdec def __repr__(self) -> str: - return '<%sDecRef %r>' % ('X' if self.is_xdec else '', self.src) + return '<{}DecRef {!r}>'.format('X' if self.is_xdec else '', self.src) def sources(self) -> List[Value]: return [self.src] diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index daa0fd0f86df..aab8dc86664f 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -40,15 +40,15 @@ def visit_goto(self, op: Goto) -> str: def visit_branch(self, op: Branch) -> str: fmt, typ = self.branch_op_names[op.op] if op.negated: - fmt = 'not {}'.format(fmt) + fmt = f'not {fmt}' cond = self.format(fmt, op.value) tb = '' if op.traceback_entry: tb = ' (error at %s:%d)' % op.traceback_entry - fmt = 'if {} goto %l{} else goto %l'.format(cond, tb) + fmt = f'if {cond} goto %l{tb} else goto %l' if typ: - fmt += ' :: {}'.format(typ) + fmt += f' :: {typ}' return self.format(fmt, op.true, op.false) def visit_return(self, op: Return) -> str: @@ -83,16 +83,16 @@ def visit_set_attr(self, op: SetAttr) -> str: return self.format('%r.%s = %r; %r = is_error', op.obj, op.attr, op.src, op) def visit_load_static(self, op: LoadStatic) -> str: - ann = ' ({})'.format(repr(op.ann)) if op.ann else '' + ann = f' ({repr(op.ann)})' if op.ann else '' name = op.identifier if op.module_name is not None: - name = '{}.{}'.format(op.module_name, name) + name = f'{op.module_name}.{name}' return self.format('%r = %s :: %s%s', op, name, op.namespace, ann) def visit_init_static(self, op: InitStatic) -> str: name = op.identifier if op.module_name is not None: - name = '{}.{}'.format(op.module_name, name) + name = f'{op.module_name}.{name}' return self.format('%s = %r :: %s', name, op.value, op.namespace) def visit_tuple_get(self, op: TupleGet) -> str: @@ -106,21 +106,21 @@ def visit_inc_ref(self, op: IncRef) -> str: s = self.format('inc_ref %r', op.src) # TODO: Remove bool check (it's unboxed) if is_bool_rprimitive(op.src.type) or is_int_rprimitive(op.src.type): - s += ' :: {}'.format(short_name(op.src.type.name)) + s += f' :: {short_name(op.src.type.name)}' return s def visit_dec_ref(self, op: DecRef) -> str: s = self.format('%sdec_ref %r', 'x' if op.is_xdec else '', op.src) # TODO: Remove bool check (it's unboxed) if is_bool_rprimitive(op.src.type) or is_int_rprimitive(op.src.type): - s += ' :: {}'.format(short_name(op.src.type.name)) + s += f' :: {short_name(op.src.type.name)}' return s def visit_call(self, op: Call) -> str: args = ', '.join(self.format('%r', arg) for arg in op.args) # TODO: Display long name? short_name = op.fn.shortname - s = '%s(%s)' % (short_name, args) + s = f'{short_name}({args})' if not op.is_void: s = self.format('%r = ', op) + s return s @@ -163,7 +163,7 @@ def visit_truncate(self, op: Truncate) -> str: return self.format("%r = truncate %r: %t to %t", op, op.src, op.src_type, op.type) def visit_load_global(self, op: LoadGlobal) -> str: - ann = ' ({})'.format(repr(op.ann)) if op.ann else '' + ann = f' ({repr(op.ann)})' if op.ann else '' return self.format('%r = load_global %s :: static%s', op, op.identifier, ann) def visit_int_op(self, op: IntOp) -> str: @@ -248,7 +248,7 @@ def format(self, fmt: str, *args: Any) -> str: # String result.append(str(arg)) else: - raise ValueError('Invalid format sequence %{}'.format(typespec)) + raise ValueError(f'Invalid format sequence %{typespec}') i = n + 2 else: i = n @@ -267,7 +267,7 @@ def format_registers(func_ir: FuncIR, i += 1 group.append(names[regs[i]]) i += 1 - result.append('%s :: %s' % (', '.join(group), regs[i0].type)) + result.append('{} :: {}'.format(', '.join(group), regs[i0].type)) return result diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 4bf71883b15d..2c875d7c8f01 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -65,7 +65,7 @@ def __repr__(self) -> str: return '<%s>' % self.__class__.__name__ def serialize(self) -> Union[JsonDict, str]: - raise NotImplementedError('Cannot serialize {} instance'.format(self.__class__.__name__)) + raise NotImplementedError(f'Cannot serialize {self.__class__.__name__} instance') def deserialize_type(data: Union[JsonDict, str], ctx: 'DeserMaps') -> 'RType': @@ -86,7 +86,7 @@ def deserialize_type(data: Union[JsonDict, str], ctx: 'DeserMaps') -> 'RType': elif data == "void": return RVoid() else: - assert False, "Can't find class {}".format(data) + assert False, f"Can't find class {data}" elif data['.class'] == 'RTuple': return RTuple.deserialize(data, ctx) elif data['.class'] == 'RUnion': @@ -445,7 +445,7 @@ def visit_rprimitive(self, t: 'RPrimitive') -> str: return 'I' elif t._ctype == 'char': return 'C' - assert not t.is_unboxed, "{} unexpected unboxed type".format(t) + assert not t.is_unboxed, f"{t} unexpected unboxed type" return 'O' def visit_rtuple(self, t: 'RTuple') -> str: @@ -488,8 +488,8 @@ def __init__(self, types: List[RType]) -> None: # in the same way python can just assign a Tuple[int, bool] to a Tuple[int, bool]. self.unique_id = self.accept(TupleNameVisitor()) # Nominally the max c length is 31 chars, but I'm not honestly worried about this. - self.struct_name = 'tuple_{}'.format(self.unique_id) - self._ctype = '{}'.format(self.struct_name) + self.struct_name = f'tuple_{self.unique_id}' + self._ctype = f'{self.struct_name}' def accept(self, visitor: 'RTypeVisitor[T]') -> T: return visitor.visit_rtuple(self) @@ -548,7 +548,7 @@ def compute_rtype_alignment(typ: RType) -> int: items = typ.types else: assert False, "invalid rtype for computing alignment" - max_alignment = max([compute_rtype_alignment(item) for item in items]) + max_alignment = max(compute_rtype_alignment(item) for item in items) return max_alignment @@ -622,12 +622,14 @@ def accept(self, visitor: 'RTypeVisitor[T]') -> T: def __str__(self) -> str: # if not tuple(unnamed structs) - return '%s{%s}' % (self.name, ', '.join(name + ":" + str(typ) + return '{}{{{}}}'.format(self.name, ', '.join(name + ":" + str(typ) for name, typ in zip(self.names, self.types))) def __repr__(self) -> str: - return '' % (self.name, ', '.join(name + ":" + repr(typ) for name, typ - in zip(self.names, self.types))) + return ''.format( + self.name, ', '.join(name + ":" + repr(typ) + for name, typ in zip(self.names, self.types)) + ) def __eq__(self, other: object) -> bool: return (isinstance(other, RStruct) and self.name == other.name @@ -774,10 +776,10 @@ def accept(self, visitor: 'RTypeVisitor[T]') -> T: return visitor.visit_rarray(self) def __str__(self) -> str: - return '%s[%s]' % (self.item_type, self.length) + return f'{self.item_type}[{self.length}]' def __repr__(self) -> str: - return '' % (self.item_type, self.length) + return f'' def __eq__(self, other: object) -> bool: return (isinstance(other, RArray) and self.item_type == other.item_type diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 57baa8dbf574..72c03801e326 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -419,7 +419,7 @@ def init_final_static(self, if class_name is None: name = lvalue.name else: - name = '{}.{}'.format(class_name, lvalue.name) + name = f'{class_name}.{lvalue.name}' assert name is not None, "Full name not set for variable" coerced = self.coerce(rvalue_reg, type_override or self.node_type(lvalue), lvalue.line) self.final_names.append((name, coerced.type)) @@ -432,7 +432,7 @@ def load_final_static(self, fullname: str, typ: RType, line: int, module, name = split_name return self.builder.load_static_checked( typ, name, module, line=line, - error_msg='value for final name "{}" was not set'.format(error_name)) + error_msg=f'value for final name "{error_name}" was not set') def load_final_literal_value(self, val: Union[int, str, bytes, float, bool], line: int) -> Value: @@ -685,7 +685,7 @@ def pop_loop_stack(self) -> None: def spill(self, value: Value) -> AssignmentTarget: """Moves a given Value instance into the generator class' environment class.""" - name = '{}{}'.format(TEMP_ATTR_NAME, self.temp_counter) + name = f'{TEMP_ATTR_NAME}{self.temp_counter}' self.temp_counter += 1 target = self.add_var_to_env_class(Var(name), value.type, self.fn_info.generator_class) # Shouldn't be able to fail, so -1 for line @@ -817,7 +817,7 @@ def get_final_ref(self, expr: MemberExpr) -> Optional[Tuple[str, Var, bool]]: is_final = sym.node.is_final or expr_fullname == 'enum.Enum' if is_final: final_var = sym.node - fullname = '{}.{}'.format(sym.node.info.fullname, final_var.name) + fullname = f'{sym.node.info.fullname}.{final_var.name}' native = self.is_native_module(expr.expr.node.module_name) elif self.is_module_member_expr(expr): # a module attribute diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py index 0261332800ae..fe561cfc531d 100644 --- a/mypyc/irbuild/callable_class.py +++ b/mypyc/irbuild/callable_class.py @@ -45,7 +45,7 @@ class for the nested function. # else: # def foo(): ----> foo_obj_0() # return False - name = base_name = '{}_obj'.format(builder.fn_info.namespaced_name()) + name = base_name = f'{builder.fn_info.namespaced_name()}_obj' count = 0 while name in builder.callable_class_names: name = base_name + '_' + str(count) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 80ec331cd550..9a458181dc6c 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -588,11 +588,11 @@ def check_deletable_declaration(builder: IRBuilder, cl: ClassIR, line: int) -> N for attr in cl.deletable: if attr not in cl.attributes: if not cl.has_attr(attr): - builder.error('Attribute "{}" not defined'.format(attr), line) + builder.error(f'Attribute "{attr}" not defined', line) continue for base in cl.mro: if attr in base.property_types: - builder.error('Cannot make property "{}" deletable'.format(attr), line) + builder.error(f'Cannot make property "{attr}" deletable', line) break else: _, base = cl.attr_details(attr) diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py index 44bcccb507d0..9ed764c8bcca 100644 --- a/mypyc/irbuild/env_class.py +++ b/mypyc/irbuild/env_class.py @@ -43,7 +43,7 @@ class is generated, the function environment has not yet been Return a ClassIR representing an environment for a function containing a nested function. """ - env_class = ClassIR('{}_env'.format(builder.fn_info.namespaced_name()), + env_class = ClassIR(f'{builder.fn_info.namespaced_name()}_env', builder.module_name, is_generated=True) env_class.attributes[SELF_NAME] = RInstance(env_class) if builder.fn_info.is_nested: @@ -122,7 +122,7 @@ def load_outer_env(builder: IRBuilder, Returns the register where the environment class was loaded. """ env = builder.add(GetAttr(base, ENV_ATTR_NAME, builder.fn_info.fitem.line)) - assert isinstance(env.type, RInstance), '{} must be of type RInstance'.format(env) + assert isinstance(env.type, RInstance), f'{env} must be of type RInstance' for symbol, target in outer_env.items(): env.type.class_ir.attributes[symbol.name] = target.type diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 5b567251111a..275d3449f812 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -130,7 +130,7 @@ def transform_lambda_expr(builder: IRBuilder, expr: LambdaExpr) -> Value: fsig = FuncSignature(runtime_args, ret_type) - fname = '{}{}'.format(LAMBDA_NAME, builder.lambda_counter) + fname = f'{LAMBDA_NAME}{builder.lambda_counter}' builder.lambda_counter += 1 func_ir, func_reg = gen_func_item(builder, expr, fname, fsig) assert func_reg is not None @@ -993,7 +993,7 @@ def load_singledispatch_registry(builder: IRBuilder, dispatch_func_obj: Value, l def singledispatch_main_func_name(orig_name: str) -> str: - return '__mypyc_singledispatch_main_function_{}__'.format(orig_name) + return f'__mypyc_singledispatch_main_function_{orig_name}__' def get_registry_identifier(fitem: FuncDef) -> str: diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py index 7655939b4412..7a96d390e156 100644 --- a/mypyc/irbuild/generator.py +++ b/mypyc/irbuild/generator.py @@ -64,7 +64,7 @@ def instantiate_generator_class(builder: IRBuilder) -> Value: def setup_generator_class(builder: IRBuilder) -> ClassIR: - name = '{}_gen'.format(builder.fn_info.namespaced_name()) + name = f'{builder.fn_info.namespaced_name()}_gen' generator_class_ir = ClassIR(name, builder.module_name, is_generated=True) generator_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_info.env_class) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 98a69d92406d..1927773489b1 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -821,7 +821,7 @@ def load_static_checked(self, typ: RType, identifier: str, module_name: Optional line: int = -1, error_msg: Optional[str] = None) -> Value: if error_msg is None: - error_msg = 'name "{}" is not defined'.format(identifier) + error_msg = f'name "{identifier}" is not defined' ok_block, error_block = BasicBlock(), BasicBlock() value = self.add(LoadStatic(typ, identifier, module_name, namespace, line=line)) self.add(Branch(value, error_block, ok_block, Branch.IS_ERROR, rare=True)) @@ -838,7 +838,7 @@ def load_module(self, name: str) -> Value: def get_native_type(self, cls: ClassIR) -> Value: """Load native type object.""" - fullname = '%s.%s' % (cls.module_name, cls.name) + fullname = f'{cls.module_name}.{cls.name}' return self.load_native_type_object(fullname) def load_native_type_object(self, fullname: str) -> Value: @@ -1332,7 +1332,7 @@ def matching_call_c(self, if all(is_subtype(actual.type, formal) for actual, formal in zip(args, desc.arg_types)): if matching: - assert matching.priority != desc.priority, 'Ambiguous:\n1) %s\n2) %s' % ( + assert matching.priority != desc.priority, 'Ambiguous:\n1) {}\n2) {}'.format( matching, desc) if desc.priority > matching.priority: matching = desc diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 6a744781ee50..9c9273b0cd76 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -666,7 +666,7 @@ def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) if isinstance(target.obj_type, RInstance): cl = target.obj_type.class_ir if not cl.is_deletable(target.attr): - builder.error('"{}" cannot be deleted'.format(target.attr), line) + builder.error(f'"{target.attr}" cannot be deleted', line) builder.note( 'Using "__deletable__ = ' + '[\'\']" in the class body enables "del obj."', line) diff --git a/mypyc/namegen.py b/mypyc/namegen.py index acf901caf93c..99abf8a759ff 100644 --- a/mypyc/namegen.py +++ b/mypyc/namegen.py @@ -73,7 +73,7 @@ def private_name(self, module: str, partial_name: Optional[str] = None) -> str: module_prefix = module + '.' else: module_prefix = '' - actual = exported_name('{}{}'.format(module_prefix, partial_name)) + actual = exported_name(f'{module_prefix}{partial_name}') self.translations[module, partial_name] = actual return actual diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index f4a2ce8c66e3..44703528976c 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -124,6 +124,7 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: int_neg_op = int_unary_op('-', 'CPyTagged_Negate') int_invert_op = int_unary_op('~', 'CPyTagged_Invert') + # Primitives related to integer comparison operations: # Description for building int comparison ops @@ -139,6 +140,7 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: ('c_func_negated', bool), ('c_func_swap_operands', bool)]) + # Equals operation on two boxed tagged integers int_equal_ = custom_op( arg_types=[int_rprimitive, int_rprimitive], diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 5ed910549f5a..0174051ec98d 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -60,6 +60,7 @@ ('extra_int_constants', List[Tuple[int, RType]]), ('priority', int)]) + # A description for C load operations including LoadGlobal and LoadAddress LoadAddressDescription = NamedTuple( 'LoadAddressDescription', [('name', str), diff --git a/mypyc/test-data/fixtures/testutil.py b/mypyc/test-data/fixtures/testutil.py index 462b02c1946d..f6c20835a287 100644 --- a/mypyc/test-data/fixtures/testutil.py +++ b/mypyc/test-data/fixtures/testutil.py @@ -11,10 +11,10 @@ def assertRaises(typ: type, msg: str = '') -> Iterator[None]: try: yield except Exception as e: - assert isinstance(e, typ), "{} is not a {}".format(e, typ.__name__) - assert msg in str(e), 'Message "{}" does not match "{}"'.format(e, msg) + assert isinstance(e, typ), f"{e} is not a {typ.__name__}" + assert msg in str(e), f'Message "{e}" does not match "{msg}"' else: - assert False, "Expected {} but got no exception".format(typ.__name__) + assert False, f"Expected {typ.__name__} but got no exception" T = TypeVar('T') U = TypeVar('U') diff --git a/mypyc/test/test_cheader.py b/mypyc/test/test_cheader.py index 0301a6670720..0966059e2443 100644 --- a/mypyc/test/test_cheader.py +++ b/mypyc/test/test_cheader.py @@ -19,8 +19,8 @@ def test_primitives_included_in_header(self) -> None: def check_name(name: str) -> None: if name.startswith('CPy'): - assert re.search(r'\b{}\b'.format(name), header), ( - '"{}" is used in mypyc.primitives but not declared in CPy.h'.format(name)) + assert re.search(fr'\b{name}\b', header), ( + f'"{name}" is used in mypyc.primitives but not declared in CPy.h') for values in [registry.method_call_ops.values(), registry.function_ops.values(), @@ -33,7 +33,7 @@ def check_name(name: str) -> None: check_name(op.c_function_name) primitives_path = os.path.join(os.path.dirname(__file__), '..', 'primitives') - for fnam in glob.glob('{}/*.py'.format(primitives_path)): + for fnam in glob.glob(f'{primitives_path}/*.py'): with open(fnam) as f: content = f.read() for name in re.findall(r'c_function_name=["\'](CPy[A-Z_a-z0-9]+)', content): diff --git a/mypyc/test/test_commandline.py b/mypyc/test/test_commandline.py index 5c80d0fddb1d..3ca380f8eebd 100644 --- a/mypyc/test/test_commandline.py +++ b/mypyc/test/test_commandline.py @@ -59,7 +59,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: cwd='tmp') finally: suffix = 'pyd' if sys.platform == 'win32' else 'so' - so_paths = glob.glob('tmp/**/*.{}'.format(suffix), recursive=True) + so_paths = glob.glob(f'tmp/**/*.{suffix}', recursive=True) for path in so_paths: os.remove(path) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 2a2aa8fae0e4..466815534fdb 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -263,7 +263,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> # Assert that an output file got created suffix = 'pyd' if sys.platform == 'win32' else 'so' - assert glob.glob('native.*.{}'.format(suffix)) or glob.glob('native.{}'.format(suffix)) + assert glob.glob(f'native.*.{suffix}') or glob.glob(f'native.{suffix}') driver_path = 'driver.py' if not os.path.isfile(driver_path): @@ -309,7 +309,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> msg = 'Invalid output' expected = testcase.output else: - msg = 'Invalid output (step {})'.format(incremental_step) + msg = f'Invalid output (step {incremental_step})' expected = testcase.output2.get(incremental_step, []) if not expected: diff --git a/mypyc/test/test_serialization.py b/mypyc/test/test_serialization.py index 5737ccc7b623..683bb807620e 100644 --- a/mypyc/test/test_serialization.py +++ b/mypyc/test/test_serialization.py @@ -46,27 +46,27 @@ def assert_blobs_same(x: Any, y: Any, trail: Tuple[Any, ...]) -> None: The `trail` argument is used in error messages. """ - assert type(x) is type(y), ("Type mismatch at {}".format(trail), type(x), type(y)) + assert type(x) is type(y), (f"Type mismatch at {trail}", type(x), type(y)) if isinstance(x, (FuncDecl, FuncIR, ClassIR)): - assert x.fullname == y.fullname, "Name mismatch at {}".format(trail) + assert x.fullname == y.fullname, f"Name mismatch at {trail}" elif isinstance(x, OrderedDict): - assert len(x.keys()) == len(y.keys()), "Keys mismatch at {}".format(trail) + assert len(x.keys()) == len(y.keys()), f"Keys mismatch at {trail}" for (xk, xv), (yk, yv) in zip(x.items(), y.items()): assert_blobs_same(xk, yk, trail + ("keys",)) assert_blobs_same(xv, yv, trail + (xk,)) elif isinstance(x, dict): - assert x.keys() == y.keys(), "Keys mismatch at {}".format(trail) + assert x.keys() == y.keys(), f"Keys mismatch at {trail}" for k in x.keys(): assert_blobs_same(x[k], y[k], trail + (k,)) elif isinstance(x, Iterable) and not isinstance(x, str): for i, (xv, yv) in enumerate(zip(x, y)): assert_blobs_same(xv, yv, trail + (i,)) elif isinstance(x, RType): - assert is_same_type(x, y), "RType mismatch at {}".format(trail) + assert is_same_type(x, y), f"RType mismatch at {trail}" elif isinstance(x, FuncSignature): - assert is_same_signature(x, y), "Signature mismatch at {}".format(trail) + assert is_same_signature(x, y), f"Signature mismatch at {trail}" else: - assert x == y, "Value mismatch at {}".format(trail) + assert x == y, f"Value mismatch at {trail}" def assert_modules_same(ir1: ModuleIR, ir2: ModuleIR) -> None: diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 985356bc469b..c5dc2588a7e2 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -134,7 +134,7 @@ def update_testcase_output(testcase: DataDrivenTestCase, output: List[str]) -> N # We can't rely on the test line numbers to *find* the test, since # we might fix multiple tests in a run. So find it by the case # header. Give up if there are multiple tests with the same name. - test_slug = '[case {}]'.format(testcase.name) + test_slug = f'[case {testcase.name}]' if data_lines.count(test_slug) != 1: return start_idx = data_lines.index(test_slug) @@ -165,7 +165,7 @@ def assert_test_output(testcase: DataDrivenTestCase, assert_string_arrays_equal( expected_output, actual, - '{} ({}, line {})'.format(message, testcase.file, testcase.line)) + f'{message} ({testcase.file}, line {testcase.line})') def get_func_names(expected: List[str]) -> List[str]: @@ -205,7 +205,7 @@ def show_c(cfiles: List[List[Tuple[str, str]]]) -> None: heading('Generated C') for group in cfiles: for cfile, ctext in group: - print('== {} =='.format(cfile)) + print(f'== {cfile} ==') print_with_line_numbers(ctext) heading('End C') @@ -262,5 +262,5 @@ def infer_ir_build_options_from_test_name(name: str) -> Optional[CompilerOptions if m: options.capi_version = (int(m.group(1)), int(m.group(2))) elif '_py' in name or '_Python' in name: - assert False, 'Invalid _py* suffix (should be _pythonX_Y): {}'.format(name) + assert False, f'Invalid _py* suffix (should be _pythonX_Y): {name}' return options diff --git a/mypyc/transform/uninit.py b/mypyc/transform/uninit.py index 47544e511734..ca21d2690636 100644 --- a/mypyc/transform/uninit.py +++ b/mypyc/transform/uninit.py @@ -72,7 +72,7 @@ def split_blocks_at_uninits(blocks: List[BasicBlock], line=op.line)) raise_std = RaiseStandardError( RaiseStandardError.UNBOUND_LOCAL_ERROR, - 'local variable "{}" referenced before assignment'.format(src.name), + f'local variable "{src.name}" referenced before assignment', op.line) error_block.ops.append(raise_std) error_block.ops.append(Unreachable()) From 9f8b814fc5b3bb8daf0ef04aca3dfd1fb056bc8b Mon Sep 17 00:00:00 2001 From: Marcel Otoboni Date: Tue, 3 May 2022 09:05:15 -0300 Subject: [PATCH 47/79] Add check if python_version was parsed as float in pyproject.toml (#12558) Fixes #12108 Co-authored-by: Marcel-ICMC Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- mypy/config_parser.py | 18 ++++++++++++------ test-data/unit/cmdline.test | 9 +++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 952c3f96f29a..678e68cca886 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -24,8 +24,8 @@ _INI_PARSER_CALLABLE: _TypeAlias = Callable[[Any], _CONFIG_VALUE_TYPES] -def parse_version(v: str) -> Tuple[int, int]: - m = re.match(r'\A(\d)\.(\d+)\Z', v) +def parse_version(v: Union[str, float]) -> Tuple[int, int]: + m = re.match(r'\A(\d)\.(\d+)\Z', str(v)) if not m: raise argparse.ArgumentTypeError( f"Invalid python version '{v}' (expected format: 'x.y')") @@ -36,9 +36,15 @@ def parse_version(v: str) -> Tuple[int, int]: f"Python 2.{minor} is not supported (must be 2.7)") elif major == 3: if minor < defaults.PYTHON3_VERSION_MIN[1]: - raise argparse.ArgumentTypeError( - "Python 3.{} is not supported (must be {}.{} or higher)".format(minor, - *defaults.PYTHON3_VERSION_MIN)) + msg = "Python 3.{0} is not supported (must be {1}.{2} or higher)".format( + minor, + *defaults.PYTHON3_VERSION_MIN + ) + + if isinstance(v, float): + msg += ". You may need to put quotes around your Python version" + + raise argparse.ArgumentTypeError(msg) else: raise argparse.ArgumentTypeError( f"Python major version '{major}' out of range (must be 2 or 3)") @@ -142,7 +148,7 @@ def check_follow_imports(choice: str) -> str: # Reuse the ini_config_types and overwrite the diff toml_config_types: Final[Dict[str, _INI_PARSER_CALLABLE]] = ini_config_types.copy() toml_config_types.update({ - 'python_version': lambda s: parse_version(str(s)), + 'python_version': parse_version, 'strict_optional_whitelist': try_split, 'mypy_path': lambda s: [expand_path(p) for p in try_split(s, '[,:]')], 'files': lambda s: split_and_match_files_list(try_split(s)), diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 7fc517643342..66dfd4edff2e 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -561,6 +561,15 @@ main.py:1: error: Cannot find implementation or library stub for module named "a main.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports main.py:1: error: Cannot find implementation or library stub for module named "a" +[case testPythonVersionWrongFormatPyProjectTOML] +# cmd: mypy -c pass +[file pyproject.toml] +\[tool.mypy] +python_version = 3.10 +[out] +pyproject.toml: [mypy]: python_version: Python 3.1 is not supported (must be 3.4 or higher). You may need to put quotes around your Python version +== Return code: 0 + [case testPythonVersionTooOld10] # cmd: mypy -c pass [file mypy.ini] From 4746519effeafc64da7687af8fe1685d44545219 Mon Sep 17 00:00:00 2001 From: dzcode <9089037+dzcode@users.noreply.github.com> Date: Tue, 3 May 2022 15:12:00 -0600 Subject: [PATCH 48/79] Fix crash when using decorator in class scope (#12724) Fixes #12474 --- mypy/semanal.py | 6 +++--- test-data/unit/check-classes.test | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index a9445a9c8748..62ec1c823ec2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4927,7 +4927,7 @@ def _get_node_for_class_scoped_import( # when it can also be a FuncBase. Once fixed, `f` in the following can be removed. # See also https://github.com/mypyc/mypyc/issues/892 f = cast(Any, lambda x: x) - if isinstance(f(symbol_node), (FuncBase, Var)): + if isinstance(f(symbol_node), (Decorator, FuncBase, Var)): # For imports in class scope, we construct a new node to represent the symbol and # set its `info` attribute to `self.type`. existing = self.current_symbol_table().get(name) @@ -4935,7 +4935,7 @@ def _get_node_for_class_scoped_import( # The redefinition checks in `add_symbol_table_node` don't work for our # constructed Var / FuncBase, so check for possible redefinitions here. existing is not None - and isinstance(f(existing.node), (FuncBase, Var)) + and isinstance(f(existing.node), (Decorator, FuncBase, Var)) and ( isinstance(f(existing.type), f(AnyType)) or f(existing.type) == f(symbol_node).type @@ -4944,7 +4944,7 @@ def _get_node_for_class_scoped_import( return existing.node # Construct the new node - if isinstance(f(symbol_node), FuncBase): + if isinstance(f(symbol_node), (FuncBase, Decorator)): # In theory we could construct a new node here as well, but in practice # it doesn't work well, see #12197 typ: Optional[Type] = AnyType(TypeOfAny.from_error) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index d74481a55cbb..5c1e8dfa44f4 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -7284,3 +7284,19 @@ def meth1(self: Any, y: str) -> str: ... T = TypeVar("T") def meth2(self: Any, y: T) -> T: ... + +[case testClassScopeImportWithWrapperAndError] +class Foo: + from mod import foo # E: Unsupported class scoped import + +[file mod.py] +from typing import Any, Callable, TypeVar + +FuncT = TypeVar("FuncT", bound=Callable[..., Any]) +def identity_wrapper(func: FuncT) -> FuncT: + return func + +@identity_wrapper +def foo(self: Any) -> str: + return "" + From 4f07c79aea0fef61ab649d6acedf01186f1054eb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 4 May 2022 18:23:25 +0200 Subject: [PATCH 49/79] Fix TypeError with enable-incomplete-features flag (#12726) --- mypy/main.py | 2 +- test-data/unit/cmdline.test | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mypy/main.py b/mypy/main.py index f598194455c1..c735bb389a35 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -869,7 +869,7 @@ def add_invertible_flag(flag: str, # Must be followed by another flag or by '--' (and then only file args may follow). parser.add_argument('--cache-map', nargs='+', dest='special-opts:cache_map', help=argparse.SUPPRESS) - parser.add_argument('--enable-incomplete-features', default=False, + parser.add_argument('--enable-incomplete-features', action='store_true', help=argparse.SUPPRESS) # options specifying code to check diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 66dfd4edff2e..a1b4d986b98a 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1394,3 +1394,8 @@ class C: pass b \d+ b\.c \d+ .* + +[case testCmdlineEnableIncompleteFeatures] +# cmd: mypy --enable-incomplete-features a.py +[file a.py] +pass From 269adee09330f6f1836b0ae13e20450c9f708c2a Mon Sep 17 00:00:00 2001 From: frerikandriessen <85996204+frerikandriessen@users.noreply.github.com> Date: Sat, 7 May 2022 20:57:59 +0200 Subject: [PATCH 50/79] Add more precise error message for Callable annotation (#12518) Adds a more precise error message for a Callable annotation, if the option --disallow-any-generics (part of --strict mode) has been used. The suggestion in the old message was incorrect in this scenario. Fixes #11757 --- mypy/typeanal.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d734a1cc330f..98e37bd0aa40 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -950,7 +950,10 @@ def analyze_callable_type(self, t: UnboundType) -> Type: return AnyType(TypeOfAny.from_error) ret = maybe_ret else: - self.fail('Please use "Callable[[], ]" or "Callable"', t) + if self.options.disallow_any_generics: + self.fail('Please use "Callable[[], ]"', t) + else: + self.fail('Please use "Callable[[], ]" or "Callable"', t) return AnyType(TypeOfAny.from_error) assert isinstance(ret, CallableType) return ret.accept(self) From 91e890fe308e7b9c8ab969a4831a4ff13fbc0aa3 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Sat, 7 May 2022 15:32:10 -0700 Subject: [PATCH 51/79] Add info about classes and types to 'getting started' docs (#6557) Co-authored-by: 97littleleaf11 <97littleleaf11@users.noreply.github.com> Co-authored-by: Jingchen Ye <11172084+97littleleaf11@users.noreply.github.com> Co-authored-by: AlexWaygood --- docs/source/class_basics.rst | 2 + docs/source/getting_started.rst | 136 +++++++++++++++++++++++------ docs/source/installed_packages.rst | 9 ++ docs/source/running_mypy.rst | 2 +- 4 files changed, 121 insertions(+), 28 deletions(-) diff --git a/docs/source/class_basics.rst b/docs/source/class_basics.rst index a7f57afee4e7..48734a514ada 100644 --- a/docs/source/class_basics.rst +++ b/docs/source/class_basics.rst @@ -1,3 +1,5 @@ +.. _class-basics: + Class basics ============ diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index ee102ad8b639..6d92ce30d723 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -4,7 +4,7 @@ Getting started =============== This chapter introduces some core concepts of mypy, including function -annotations, the :py:mod:`typing` module, library stubs, and more. +annotations, the :py:mod:`typing` module, stub files, and more. Be sure to read this chapter carefully, as the rest of the documentation may not make much sense otherwise. @@ -317,28 +317,119 @@ syntax like so: # If you're using Python 3.6+ my_global_dict: Dict[int, float] = {} -.. _stubs-intro: -Library stubs and typeshed -************************** +Types and classes +***************** + +So far, we've only seen examples of pre-existing types like the ``int`` +or ``float`` builtins, or generic types from ``collections.abc`` and +``typing``, such as ``Iterable``. However, these aren't the only types you can +use: in fact, you can use any Python class as a type! + +For example, suppose you've defined a custom class representing a bank account: + +.. code-block:: python + + class BankAccount: + # Note: It is ok to omit type hints for the "self" parameter. + # Mypy will infer the correct type. + + def __init__(self, account_name: str, initial_balance: int = 0) -> None: + # Note: Mypy will infer the correct types of your fields + # based on the types of the parameters. + self.account_name = account_name + self.balance = initial_balance + + def deposit(self, amount: int) -> None: + self.balance += amount + + def withdraw(self, amount: int) -> None: + self.balance -= amount + + def overdrawn(self) -> bool: + return self.balance < 0 + +You can declare that a function will accept any instance of your class +by simply annotating the parameters with ``BankAccount``: + +.. code-block:: python + + def transfer(src: BankAccount, dst: BankAccount, amount: int) -> None: + src.withdraw(amount) + dst.deposit(amount) + + account_1 = BankAccount('Alice', 400) + account_2 = BankAccount('Bob', 200) + transfer(account_1, account_2, 50) + +In fact, the ``transfer`` function we wrote above can accept more then just +instances of ``BankAccount``: it can also accept any instance of a *subclass* +of ``BankAccount``. For example, suppose you write a new class that looks like this: + +.. code-block:: python + + class AuditedBankAccount(BankAccount): + def __init__(self, account_name: str, initial_balance: int = 0) -> None: + super().__init__(account_name, initial_balance) + self.audit_log: list[str] = [] -Mypy uses library *stubs* to type check code interacting with library -modules, including the Python standard library. A library stub defines -a skeleton of the public interface of the library, including classes, -variables and functions, and their types. Mypy ships with stubs for -the standard library from the `typeshed -`_ project, which contains library -stubs for the Python builtins, the standard library, and selected -third-party packages. + def deposit(self, amount: int) -> None: + self.audit_log.append(f"Deposited {amount}") + self.balance += amount -For example, consider this code: + def withdraw(self, amount: int) -> None: + self.audit_log.append(f"Withdrew {amount}") + self.balance -= amount + +Since ``AuditedBankAccount`` is a subclass of ``BankAccount``, we can directly pass +in instances of it into our ``transfer`` function: .. code-block:: python - x = chr(4) + audited = AuditedBankAccount('Charlie', 300) + transfer(account_1, audited, 100) # Type checks! + +This behavior is actually a fundamental aspect of the PEP 484 type system: when +we annotate some variable with a type ``T``, we are actually telling mypy that +variable can be assigned an instance of ``T``, or an instance of a *subclass* of ``T``. +The same rule applies to type hints on parameters or fields. + +See :ref:`class-basics` to learn more about how to work with code involving classes. + + +.. _stubs-intro: + +Stubs files and typeshed +************************ -Without a library stub, mypy would have no way of inferring the type of ``x`` -and checking that the argument to :py:func:`chr` has a valid type. +Mypy also understands how to work with classes found in the standard library. +For example, here is a function which uses the ``Path`` object from the +`pathlib standard library module `_: + +.. code-block:: python + + from pathlib import Path + + def load_template(template_path: Path, name: str) -> str: + # Mypy understands that 'file_path.read_text()' returns a str... + template = template_path.read_text() + + # ...so understands this line type checks. + return template.replace('USERNAME', name) + +This behavior may surprise you if you're familiar with how +Python internally works. The standard library does not use type hints +anywhere, so how did mypy know that ``Path.read_text()`` returns a ``str``, +or that ``str.replace(...)`` accepts exactly two ``str`` arguments? + +The answer is that mypy comes bundled with *stub files* from the +the `typeshed `_ project, which +contains stub files for the Python builtins, the standard library, +and selected third-party packages. + +A *stub file* is a file containing a skeleton of the public interface +of that Python module, including classes, variables, functions -- and +most importantly, their types. Mypy complains if it can't find a stub (or a real module) for a library module that you import. Some modules ship with stubs or inline @@ -349,7 +440,7 @@ the stubs for the ``requests`` package like this: .. code-block:: shell - python3 -m pip install types-requests + $ python3 -m pip install types-requests The stubs are usually packaged in a distribution named ``types-``. Note that the distribution name may be @@ -363,17 +454,8 @@ often suggest the name of the stub distribution: prog.py:1: note: Hint: "python3 -m pip install types-PyYAML" ... -.. note:: - - Starting in mypy 0.900, most third-party package stubs must be - installed explicitly. This decouples mypy and stub versioning, - allowing stubs to updated without updating mypy. This also allows - stubs not originally included with mypy to be installed. Earlier - mypy versions included a fixed set of stubs for third-party - packages. - You can also :ref:`create -stubs ` easily. We discuss ways of silencing complaints +stubs ` easily. We discuss strategies for handling errors about missing stubs in :ref:`ignore-missing-imports`. Configuring mypy diff --git a/docs/source/installed_packages.rst b/docs/source/installed_packages.rst index 8e5bff04ea72..73e808bb9be4 100644 --- a/docs/source/installed_packages.rst +++ b/docs/source/installed_packages.rst @@ -33,6 +33,15 @@ you can create such packages. example), it is recommended that you also pin the versions of all your stub package dependencies. +.. note:: + + Starting in mypy 0.900, most third-party package stubs must be + installed explicitly. This decouples mypy and stub versioning, + allowing stubs to updated without updating mypy. This also allows + stubs not originally included with mypy to be installed. Earlier + mypy versions included a fixed set of stubs for third-party + packages. + Using installed packages with mypy (PEP 561) ******************************************** diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 070e4556c04e..46ad2c65c386 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -322,7 +322,7 @@ this error, try: In some rare cases, you may get the "Cannot find implementation or library stub for module" error even when the module is installed in your system. This can happen when the module is both missing type hints and is installed -on your system in a unconventional way. +on your system in an unconventional way. In this case, follow the steps above on how to handle :ref:`missing type hints in third party libraries `. From 23e2a51e05ce38aa765fe67ab1507836227b301b Mon Sep 17 00:00:00 2001 From: Danny Weinberg Date: Sat, 7 May 2022 16:10:26 -0700 Subject: [PATCH 52/79] Add a class attribute hook to the plugin system (#9881) This adds a get_class_attribute_hook to plugins to modify attributes on classes (as opposed to the existing get_attribute_hook, which is for attributes on instances). Fixes #9645 --- docs/source/extending_mypy.rst | 4 + mypy/checkmember.py | 28 +++++-- mypy/plugin.py | 29 +++++++- test-data/unit/check-custom-plugin.test | 89 +++++++++++++++++++++++ test-data/unit/plugins/class_attr_hook.py | 20 +++++ 5 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 test-data/unit/plugins/class_attr_hook.py diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index 5c59bef506cc..00c328be7728 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -198,6 +198,10 @@ fields which already exist on the class. *Exception:* if :py:meth:`__getattr__ < :py:meth:`__getattribute__ ` is a method on the class, the hook is called for all fields which do not refer to methods. +**get_class_attribute_hook()** is similar to above, but for attributes on classes rather than instances. +Unlike above, this does not have special casing for :py:meth:`__getattr__ ` or +:py:meth:`__getattribute__ `. + **get_class_decorator_hook()** can be used to update class definition for given class decorators. For example, you can add some attributes to the class to match runtime behaviour: diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 29d2728c2174..964ab301d171 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -704,10 +704,13 @@ def analyze_class_attribute_access(itype: Instance, if override_info: info = override_info + fullname = '{}.{}'.format(info.fullname, name) + hook = mx.chk.plugin.get_class_attribute_hook(fullname) + node = info.get(name) if not node: if info.fallback_to_any: - return AnyType(TypeOfAny.special_form) + return apply_class_attr_hook(mx, hook, AnyType(TypeOfAny.special_form)) return None is_decorated = isinstance(node.node, Decorator) @@ -732,14 +735,16 @@ def analyze_class_attribute_access(itype: Instance, if info.is_enum and not (mx.is_lvalue or is_decorated or is_method): enum_class_attribute_type = analyze_enum_class_attribute_access(itype, name, mx) if enum_class_attribute_type: - return enum_class_attribute_type + return apply_class_attr_hook(mx, hook, enum_class_attribute_type) t = node.type if t: if isinstance(t, PartialType): symnode = node.node assert isinstance(symnode, Var) - return mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode, mx.context) + return apply_class_attr_hook(mx, hook, + mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode, + mx.context)) # Find the class where method/variable was defined. if isinstance(node.node, Decorator): @@ -790,7 +795,8 @@ def analyze_class_attribute_access(itype: Instance, mx.self_type, original_vars=original_vars) if not mx.is_lvalue: result = analyze_descriptor_access(result, mx) - return result + + return apply_class_attr_hook(mx, hook, result) elif isinstance(node.node, Var): mx.not_ready_callback(name, mx.context) return AnyType(TypeOfAny.special_form) @@ -814,7 +820,7 @@ def analyze_class_attribute_access(itype: Instance, if is_decorated: assert isinstance(node.node, Decorator) if node.node.type: - return node.node.type + return apply_class_attr_hook(mx, hook, node.node.type) else: mx.not_ready_callback(name, mx.context) return AnyType(TypeOfAny.from_error) @@ -826,7 +832,17 @@ def analyze_class_attribute_access(itype: Instance, # unannotated implicit class methods we do this here. if node.node.is_class: typ = bind_self(typ, is_classmethod=True) - return typ + return apply_class_attr_hook(mx, hook, typ) + + +def apply_class_attr_hook(mx: MemberContext, + hook: Optional[Callable[[AttributeContext], Type]], + result: Type, + ) -> Optional[Type]: + if hook: + result = hook(AttributeContext(get_proper_type(mx.original_type), + result, mx.context, mx.chk)) + return result def analyze_enum_class_attribute_access(itype: Instance, diff --git a/mypy/plugin.py b/mypy/plugin.py index 74a62d6510d2..72d28c39436a 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -637,10 +637,10 @@ def get_method_hook(self, fullname: str def get_attribute_hook(self, fullname: str ) -> Optional[Callable[[AttributeContext], Type]]: - """Adjust type of a class attribute. + """Adjust type of an instance attribute. - This method is called with attribute full name using the class where the attribute was - defined (or Var.info.fullname for generated attributes). + This method is called with attribute full name using the class of the instance where + the attribute was defined (or Var.info.fullname for generated attributes). For classes without __getattr__ or __getattribute__, this hook is only called for names of fields/properties (but not methods) that exist in the instance MRO. @@ -667,6 +667,25 @@ class Derived(Base): """ return None + def get_class_attribute_hook(self, fullname: str + ) -> Optional[Callable[[AttributeContext], Type]]: + """ + Adjust type of a class attribute. + + This method is called with attribute full name using the class where the attribute was + defined (or Var.info.fullname for generated attributes). + + For example: + + class Cls: + x: Any + + Cls.x + + get_class_attribute_hook is called with '__main__.Cls.x' as fullname. + """ + return None + def get_class_decorator_hook(self, fullname: str ) -> Optional[Callable[[ClassDefContext], None]]: """Update class definition for given class decorators. @@ -788,6 +807,10 @@ def get_attribute_hook(self, fullname: str ) -> Optional[Callable[[AttributeContext], Type]]: return self._find_hook(lambda plugin: plugin.get_attribute_hook(fullname)) + def get_class_attribute_hook(self, fullname: str + ) -> Optional[Callable[[AttributeContext], Type]]: + return self._find_hook(lambda plugin: plugin.get_class_attribute_hook(fullname)) + def get_class_decorator_hook(self, fullname: str ) -> Optional[Callable[[ClassDefContext], None]]: return self._find_hook(lambda plugin: plugin.get_class_decorator_hook(fullname)) diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index b5bf0a67eb91..ee19113f000f 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -902,3 +902,92 @@ reveal_type(f()) # N: Revealed type is "builtins.str" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/method_in_decorator.py + +[case testClassAttrPluginClassVar] +# flags: --config-file tmp/mypy.ini + +from typing import Type + +class Cls: + attr = 'test' + unchanged = 'test' + +reveal_type(Cls().attr) # N: Revealed type is "builtins.str" +reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +reveal_type(Cls.unchanged) # N: Revealed type is "builtins.str" +x: Type[Cls] +reveal_type(x.attr) # N: Revealed type is "builtins.int" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_attr_hook.py + +[case testClassAttrPluginMethod] +# flags: --config-file tmp/mypy.ini + +class Cls: + def attr(self) -> None: + pass + +reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_attr_hook.py + +[case testClassAttrPluginEnum] +# flags: --config-file tmp/mypy.ini + +import enum + +class Cls(enum.Enum): + attr = 'test' + +reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_attr_hook.py + +[case testClassAttrPluginMetaclassAnyBase] +# flags: --config-file tmp/mypy.ini + +from typing import Any, Type +class M(type): + attr = 'test' + +B: Any +class Cls(B, metaclass=M): + pass + +reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_attr_hook.py + +[case testClassAttrPluginMetaclassRegularBase] +# flags: --config-file tmp/mypy.ini + +from typing import Any, Type +class M(type): + attr = 'test' + +class B: + attr = None + +class Cls(B, metaclass=M): + pass + +reveal_type(Cls.attr) # N: Revealed type is "builtins.int" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_attr_hook.py + +[case testClassAttrPluginPartialType] +# flags: --config-file tmp/mypy.ini + +class Cls: + attr = None + def f(self) -> int: + return Cls.attr + 1 + +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_attr_hook.py diff --git a/test-data/unit/plugins/class_attr_hook.py b/test-data/unit/plugins/class_attr_hook.py new file mode 100644 index 000000000000..348e5df0ee03 --- /dev/null +++ b/test-data/unit/plugins/class_attr_hook.py @@ -0,0 +1,20 @@ +from typing import Callable, Optional + +from mypy.plugin import AttributeContext, Plugin +from mypy.types import Type as MypyType + + +class ClassAttrPlugin(Plugin): + def get_class_attribute_hook(self, fullname: str + ) -> Optional[Callable[[AttributeContext], MypyType]]: + if fullname == '__main__.Cls.attr': + return my_hook + return None + + +def my_hook(ctx: AttributeContext) -> MypyType: + return ctx.api.named_generic_type('builtins.int', []) + + +def plugin(_version: str): + return ClassAttrPlugin From 49d5cc907c391b4bcf52f9966f008f80badf12ab Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Sun, 8 May 2022 02:01:14 +0100 Subject: [PATCH 53/79] Ensure instances of `CallableType` can always be hashed (#12741) --- mypy/types.py | 9 +++++++-- test-data/unit/check-typeddict.test | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mypy/types.py b/mypy/types.py index 8f45e87b8fb9..afe1a88e06b1 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -14,7 +14,7 @@ import mypy.nodes from mypy.state import state from mypy.nodes import ( - INVARIANT, SymbolNode, FuncDef, + INVARIANT, SymbolNode, FuncDef, FakeInfo, ArgKind, ARG_POS, ARG_STAR, ARG_STAR2, ) from mypy.util import IdMapper @@ -1766,7 +1766,12 @@ def expand_param_spec(self, variables=[*variables, *self.variables]) def __hash__(self) -> int: - return hash((self.ret_type, self.is_type_obj(), + # self.is_type_obj() will fail if self.fallback.type is a FakeInfo + if isinstance(self.fallback.type, FakeInfo): + is_type_obj = 2 + else: + is_type_obj = self.is_type_obj() + return hash((self.ret_type, is_type_obj, self.is_ellipsis_args, self.name, tuple(self.arg_types), tuple(self.arg_names), tuple(self.arg_kinds))) diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 297e2df78a9b..1ca9ec593d11 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -221,6 +221,17 @@ reveal_type(d) # N: Revealed type is "TypedDict('__main__.D', {'y': builtins.in [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictWithClassmethodAlternativeConstructorDoesNotCrash] +# https://github.com/python/mypy/issues/5653 +from typing import TypedDict + +class Foo(TypedDict): + bar: str + @classmethod # E: Invalid statement in TypedDict definition; expected "field_name: field_type" + def baz(cls) -> "Foo": ... +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testCanCreateTypedDictTypeWithUnderscoreItemName] from mypy_extensions import TypedDict Point = TypedDict('Point', {'x': int, 'y': int, '_fallback': object}) From fb11c98e3aa8931104ac9f4618c1342c045d999c Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 8 May 2022 05:55:39 +0300 Subject: [PATCH 54/79] Fixes generic inference in functions with `TypeGuard` (#11797) Fixes #11780, fixes #11428 --- mypy/applytype.py | 7 ++++ mypy/checkexpr.py | 22 +++++++------ test-data/unit/check-typeguard.test | 50 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/mypy/applytype.py b/mypy/applytype.py index 51a10c7084cf..b32b88fa3276 100644 --- a/mypy/applytype.py +++ b/mypy/applytype.py @@ -103,6 +103,12 @@ def apply_generic_arguments( # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] + # Apply arguments to TypeGuard if any. + if callable.type_guard is not None: + type_guard = expand_type(callable.type_guard, id_to_type) + else: + type_guard = None + # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] @@ -110,4 +116,5 @@ def apply_generic_arguments( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, + type_guard=type_guard, ) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9dfc0e2a6458..21fd7d81967f 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -344,11 +344,6 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> ret_type=self.object_type(), fallback=self.named_type('builtins.function')) callee_type = get_proper_type(self.accept(e.callee, type_context, always_allow_any=True)) - if (isinstance(e.callee, RefExpr) - and isinstance(callee_type, CallableType) - and callee_type.type_guard is not None): - # Cache it for find_isinstance_check() - e.callee.type_guard = callee_type.type_guard if (self.chk.options.disallow_untyped_calls and self.chk.in_checked_function() and isinstance(callee_type, CallableType) @@ -886,10 +881,19 @@ def check_call_expr_with_callee_type(self, # Unions are special-cased to allow plugins to act on each item in the union. elif member is not None and isinstance(object_type, UnionType): return self.check_union_call_expr(e, object_type, member) - return self.check_call(callee_type, e.args, e.arg_kinds, e, - e.arg_names, callable_node=e.callee, - callable_name=callable_name, - object_type=object_type)[0] + ret_type, callee_type = self.check_call( + callee_type, e.args, e.arg_kinds, e, + e.arg_names, callable_node=e.callee, + callable_name=callable_name, + object_type=object_type, + ) + proper_callee = get_proper_type(callee_type) + if (isinstance(e.callee, RefExpr) + and isinstance(proper_callee, CallableType) + and proper_callee.type_guard is not None): + # Cache it for find_isinstance_check() + e.callee.type_guard = proper_callee.type_guard + return ret_type def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str) -> Type: """"Type check calling a member expression where the base type is a union.""" diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index ecefce091405..64fc7ea695cb 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -547,3 +547,53 @@ accepts_typeguard(with_typeguard_a) # E: Argument 1 to "accepts_typeguard" has accepts_typeguard(with_typeguard_b) accepts_typeguard(with_typeguard_c) [builtins fixtures/tuple.pyi] + +[case testTypeGuardWithIdentityGeneric] +from typing import TypeVar +from typing_extensions import TypeGuard + +_T = TypeVar("_T") + +def identity(val: _T) -> TypeGuard[_T]: + pass + +def func1(name: _T): + reveal_type(name) # N: Revealed type is "_T`-1" + if identity(name): + reveal_type(name) # N: Revealed type is "_T`-1" + +def func2(name: str): + reveal_type(name) # N: Revealed type is "builtins.str" + if identity(name): + reveal_type(name) # N: Revealed type is "builtins.str" +[builtins fixtures/tuple.pyi] + +[case testTypeGuardWithGenericInstance] +from typing import TypeVar, List +from typing_extensions import TypeGuard + +_T = TypeVar("_T") + +def is_list_of_str(val: _T) -> TypeGuard[List[_T]]: + pass + +def func(name: str): + reveal_type(name) # N: Revealed type is "builtins.str" + if is_list_of_str(name): + reveal_type(name) # N: Revealed type is "builtins.list[builtins.str]" +[builtins fixtures/tuple.pyi] + +[case testTypeGuardWithTupleGeneric] +from typing import TypeVar, Tuple +from typing_extensions import TypeGuard + +_T = TypeVar("_T") + +def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeGuard[Tuple[_T, _T]]: + pass + +def func(names: Tuple[str, ...]): + reveal_type(names) # N: Revealed type is "builtins.tuple[builtins.str, ...]" + if is_two_element_tuple(names): + reveal_type(names) # N: Revealed type is "Tuple[builtins.str, builtins.str]" +[builtins fixtures/tuple.pyi] From f28bf11496b14e5b6a4610438b4a6582fbd0d3c1 Mon Sep 17 00:00:00 2001 From: Tomoki Nakagawa <33854773+nakatomotoi@users.noreply.github.com> Date: Mon, 9 May 2022 08:19:05 +0900 Subject: [PATCH 55/79] Add error when making abstractmethod final (#12743) Fixes #12164 --- mypy/semanal.py | 2 ++ test-data/unit/check-final.test | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 62ec1c823ec2..555cb749074e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1103,6 +1103,8 @@ def visit_decorator(self, dec: Decorator) -> None: dec.func.accept(self) if dec.decorators and dec.var.is_property: self.fail('Decorated property not supported', dec) + if dec.func.is_abstract and dec.func.is_final: + self.fail(f"Method {dec.func.name} is both abstract and final", dec) def check_decorated_function_is_method(self, decorator: str, context: Context) -> None: diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index a955d021647d..2f298ad1be3b 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -1091,3 +1091,21 @@ class A: b: ClassVar[Final[int]] # E: Final can be only used as an outermost qualifier in a variable annotation c: ClassVar[Final] = 1 # E: Final can be only used as an outermost qualifier in a variable annotation [out] + +[case testFinalClassWithAbstractMethod] +from typing import final +from abc import ABC, abstractmethod + +@final +class A(ABC): # E: Final class __main__.A has abstract attributes "B" + @abstractmethod + def B(self) -> None: ... + +[case testFinalDefiningFuncWithAbstractMethod] +from typing import final +from abc import ABC, abstractmethod + +class A(ABC): + @final # E: Method B is both abstract and final + @abstractmethod + def B(self) -> None: ... From 612074f05ba5d65e016c031dc94b13f962d9c6b4 Mon Sep 17 00:00:00 2001 From: 97littleleaf11 <11172084+97littleleaf11@users.noreply.github.com> Date: Mon, 9 May 2022 07:27:53 +0800 Subject: [PATCH 56/79] Add documentation for -xfail tests (#12750) --- test-data/unit/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test-data/unit/README.md b/test-data/unit/README.md index 06a5b77ef769..080e8770d9f7 100644 --- a/test-data/unit/README.md +++ b/test-data/unit/README.md @@ -34,16 +34,18 @@ with text "abc..." - use `\` to escape the `#` character and indicate that the rest of the line is part of the error message - repeating `# E: ` several times in one line indicates multiple expected errors in one line -- `W: ...` and `N: ...` works exactly like `E:`, but report a warning and a note respectively +- `W: ...` and `N: ...` works exactly like `E: ...`, but report a warning and a note respectively - lines that don't contain the above should cause no type check errors - optional `[builtins fixtures/...]` tells the type checker to use stubs from the indicated file (see Fixtures section below) -- optional `[out]` is an alternative to the "# E:" notation: it indicates that +- optional `[out]` is an alternative to the `# E: ` notation: it indicates that any text after it contains the expected type checking error messages. -Usually, "E: " is preferred because it makes it easier to associate the +Usually, `# E: ` is preferred because it makes it easier to associate the errors with the code generating them at a glance, and to change the code of the test without having to change line numbers in `[out]` - an empty `[out]` section has no effect +- to add tests for a feature that hasn't been implemented yet, append `-xfail` + to the end of the test name - to run just this test, use `pytest -n0 -k testNewSyntaxBasics` From e1c03ab0f37495ef4072892778ab126d04860814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Hor=C3=A1=C4=8Dek?= <4643182+shoracek@users.noreply.github.com> Date: Mon, 9 May 2022 16:58:57 +0200 Subject: [PATCH 57/79] Make ValuePattern non-exhaustive (#12751) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes behaviour of PatternChecker so that ValuePattern does not cover the entire (non-literal) type since it can catch only a single value. Fixes: #12545 Signed-off-by: Štěpán Horáček --- mypy/checkpattern.py | 4 +++- test-data/unit/check-python310.test | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 6a8a0196306c..978b03b342f5 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -23,7 +23,7 @@ from mypy.typeops import try_getting_str_literals_from_type, make_simplified_union, \ coerce_to_literal from mypy.types import ( - ProperType, AnyType, TypeOfAny, Instance, Type, UninhabitedType, get_proper_type, + LiteralType, ProperType, AnyType, TypeOfAny, Instance, Type, UninhabitedType, get_proper_type, TypedDictType, TupleType, NoneType, UnionType ) from mypy.typevars import fill_typevars @@ -183,6 +183,8 @@ def visit_value_pattern(self, o: ValuePattern) -> PatternType: o, default=current_type ) + if not isinstance(get_proper_type(narrowed_type), (LiteralType, UninhabitedType)): + return PatternType(narrowed_type, UnionType.make_union([narrowed_type, rest_type]), {}) return PatternType(narrowed_type, rest_type, {}) def visit_singleton_pattern(self, o: SingletonPattern) -> PatternType: diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index cb39a519a146..818981238b3b 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1576,3 +1576,18 @@ def f(x: AST) -> None: reveal_type(b) # N: Revealed type is "builtins.int" reveal_type(c) # N: Revealed type is "builtins.str" [builtins fixtures/tuple.pyi] + +[case testMatchReachableDottedNames] +# flags: --warn-unreachable +class Consts: + BLANK = "" + SPECIAL = "asdf" + +def test_func(test_str: str) -> str: + match test_str: + case Consts.BLANK: + return "blank" + case Consts.SPECIAL: + return "special" + case _: + return "other" From 03901ef657daf6f477e6fe933c42ac270cf5034b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 11 May 2022 13:38:34 +0100 Subject: [PATCH 58/79] Running dataclass transform in a later pass to fix crashes (#12762) The dataclass plugin could crash if it encountered a placeholder. Fix the issue by running the plugin after the main semantic analysis pass, when all placeholders have been resolved. Also add a new hook called get_class_decorator_hook_2 that is used by the dataclass plugin. We may want to do a similar change to the attrs plugin, but let's change one thing at a time. Fix #12685. --- mypy/plugin.py | 32 ++++++++++++- mypy/plugins/dataclasses.py | 47 +++++++++++++----- mypy/plugins/default.py | 11 ++++- mypy/semanal.py | 37 ++++++++------- mypy/semanal_main.py | 59 +++++++++++++++++++++-- test-data/unit/check-dataclasses.test | 68 ++++++++++++++++++++++++++- 6 files changed, 217 insertions(+), 37 deletions(-) diff --git a/mypy/plugin.py b/mypy/plugin.py index 72d28c39436a..2f571d7eecc6 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -692,9 +692,33 @@ def get_class_decorator_hook(self, fullname: str The plugin can modify a TypeInfo _in place_ (for example add some generated methods to the symbol table). This hook is called after the class body was - semantically analyzed. + semantically analyzed, but *there may still be placeholders* (typically + caused by forward references). - The hook is called with full names of all class decorators, for example + NOTE: Usually get_class_decorator_hook_2 is the better option, since it + guarantees that there are no placeholders. + + The hook is called with full names of all class decorators. + + The hook can be called multiple times per class, so it must be + idempotent. + """ + return None + + def get_class_decorator_hook_2(self, fullname: str + ) -> Optional[Callable[[ClassDefContext], bool]]: + """Update class definition for given class decorators. + + Similar to get_class_decorator_hook, but this runs in a later pass when + placeholders have been resolved. + + The hook can return False if some base class hasn't been + processed yet using class hooks. It causes all class hooks + (that are run in this same pass) to be invoked another time for + the file(s) currently being processed. + + The hook can be called multiple times per class, so it must be + idempotent. """ return None @@ -815,6 +839,10 @@ def get_class_decorator_hook(self, fullname: str ) -> Optional[Callable[[ClassDefContext], None]]: return self._find_hook(lambda plugin: plugin.get_class_decorator_hook(fullname)) + def get_class_decorator_hook_2(self, fullname: str + ) -> Optional[Callable[[ClassDefContext], bool]]: + return self._find_hook(lambda plugin: plugin.get_class_decorator_hook_2(fullname)) + def get_metaclass_hook(self, fullname: str ) -> Optional[Callable[[ClassDefContext], None]]: return self._find_hook(lambda plugin: plugin.get_metaclass_hook(fullname)) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 62b4c89bd674..24077bb4a549 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -107,10 +107,19 @@ def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None: class DataclassTransformer: + """Implement the behavior of @dataclass. + + Note that this may be executed multiple times on the same class, so + everything here must be idempotent. + + This runs after the main semantic analysis pass, so you can assume that + there are no placeholders. + """ + def __init__(self, ctx: ClassDefContext) -> None: self._ctx = ctx - def transform(self) -> None: + def transform(self) -> bool: """Apply all the necessary transformations to the underlying dataclass so as to ensure it is fully type checked according to the rules in PEP 557. @@ -119,12 +128,11 @@ def transform(self) -> None: info = self._ctx.cls.info attributes = self.collect_attributes() if attributes is None: - # Some definitions are not ready, defer() should be already called. - return + # Some definitions are not ready. We need another pass. + return False for attr in attributes: if attr.type is None: - ctx.api.defer() - return + return False decorator_arguments = { 'init': _get_decorator_bool_argument(self._ctx, 'init', True), 'eq': _get_decorator_bool_argument(self._ctx, 'eq', True), @@ -236,6 +244,8 @@ def transform(self) -> None: 'frozen': decorator_arguments['frozen'], } + return True + def add_slots(self, info: TypeInfo, attributes: List[DataclassAttribute], @@ -294,6 +304,9 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: b: SomeOtherType = ... are collected. + + Return None if some dataclass base class hasn't been processed + yet and thus we'll need to ask for another pass. """ # First, collect attributes belonging to the current class. ctx = self._ctx @@ -315,14 +328,11 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: sym = cls.info.names.get(lhs.name) if sym is None: - # This name is likely blocked by a star import. We don't need to defer because - # defer() is already called by mark_incomplete(). + # There was probably a semantic analysis error. continue node = sym.node - if isinstance(node, PlaceholderNode): - # This node is not ready yet. - return None + assert not isinstance(node, PlaceholderNode) assert isinstance(node, Var) # x: ClassVar[int] is ignored by dataclasses. @@ -390,6 +400,9 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: # we'll have unmodified attrs laying around. all_attrs = attrs.copy() for info in cls.info.mro[1:-1]: + if 'dataclass_tag' in info.metadata and 'dataclass' not in info.metadata: + # We haven't processed the base class yet. Need another pass. + return None if 'dataclass' not in info.metadata: continue @@ -517,11 +530,21 @@ def _add_dataclass_fields_magic_attribute(self) -> None: ) -def dataclass_class_maker_callback(ctx: ClassDefContext) -> None: +def dataclass_tag_callback(ctx: ClassDefContext) -> None: + """Record that we have a dataclass in the main semantic analysis pass. + + The later pass implemented by DataclassTransformer will use this + to detect dataclasses in base classes. + """ + # The value is ignored, only the existence matters. + ctx.cls.info.metadata['dataclass_tag'] = {} + + +def dataclass_class_maker_callback(ctx: ClassDefContext) -> bool: """Hooks into the class typechecking process to add support for dataclasses. """ transformer = DataclassTransformer(ctx) - transformer.transform() + return transformer.transform() def _collect_field_args(expr: Expression, diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index a7fa2cfaa868..50e0e8cb4315 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -117,12 +117,21 @@ def get_class_decorator_hook(self, fullname: str auto_attribs_default=None, ) elif fullname in dataclasses.dataclass_makers: - return dataclasses.dataclass_class_maker_callback + return dataclasses.dataclass_tag_callback elif fullname in functools.functools_total_ordering_makers: return functools.functools_total_ordering_maker_callback return None + def get_class_decorator_hook_2(self, fullname: str + ) -> Optional[Callable[[ClassDefContext], bool]]: + from mypy.plugins import dataclasses + + if fullname in dataclasses.dataclass_makers: + return dataclasses.dataclass_class_maker_callback + + return None + def contextmanager_callback(ctx: FunctionContext) -> Type: """Infer a better return type for 'contextlib.contextmanager'.""" diff --git a/mypy/semanal.py b/mypy/semanal.py index 555cb749074e..d68928ef21ad 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1234,43 +1234,44 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> bool: def apply_class_plugin_hooks(self, defn: ClassDef) -> None: """Apply a plugin hook that may infer a more precise definition for a class.""" - def get_fullname(expr: Expression) -> Optional[str]: - if isinstance(expr, CallExpr): - return get_fullname(expr.callee) - elif isinstance(expr, IndexExpr): - return get_fullname(expr.base) - elif isinstance(expr, RefExpr): - if expr.fullname: - return expr.fullname - # If we don't have a fullname look it up. This happens because base classes are - # analyzed in a different manner (see exprtotype.py) and therefore those AST - # nodes will not have full names. - sym = self.lookup_type_node(expr) - if sym: - return sym.fullname - return None for decorator in defn.decorators: - decorator_name = get_fullname(decorator) + decorator_name = self.get_fullname_for_hook(decorator) if decorator_name: hook = self.plugin.get_class_decorator_hook(decorator_name) if hook: hook(ClassDefContext(defn, decorator, self)) if defn.metaclass: - metaclass_name = get_fullname(defn.metaclass) + metaclass_name = self.get_fullname_for_hook(defn.metaclass) if metaclass_name: hook = self.plugin.get_metaclass_hook(metaclass_name) if hook: hook(ClassDefContext(defn, defn.metaclass, self)) for base_expr in defn.base_type_exprs: - base_name = get_fullname(base_expr) + base_name = self.get_fullname_for_hook(base_expr) if base_name: hook = self.plugin.get_base_class_hook(base_name) if hook: hook(ClassDefContext(defn, base_expr, self)) + def get_fullname_for_hook(self, expr: Expression) -> Optional[str]: + if isinstance(expr, CallExpr): + return self.get_fullname_for_hook(expr.callee) + elif isinstance(expr, IndexExpr): + return self.get_fullname_for_hook(expr.base) + elif isinstance(expr, RefExpr): + if expr.fullname: + return expr.fullname + # If we don't have a fullname look it up. This happens because base classes are + # analyzed in a different manner (see exprtotype.py) and therefore those AST + # nodes will not have full names. + sym = self.lookup_type_node(expr) + if sym: + return sym.fullname + return None + def analyze_class_keywords(self, defn: ClassDef) -> None: for value in defn.keywords.values(): value.accept(self) diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 7a82032b46b7..bb0af8edc46f 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -45,6 +45,8 @@ from mypy.checker import FineGrainedDeferredNode from mypy.server.aststrip import SavedAttributes from mypy.util import is_typeshed_file +from mypy.options import Options +from mypy.plugin import ClassDefContext import mypy.build if TYPE_CHECKING: @@ -82,6 +84,8 @@ def semantic_analysis_for_scc(graph: 'Graph', scc: List[str], errors: Errors) -> apply_semantic_analyzer_patches(patches) # This pass might need fallbacks calculated above. check_type_arguments(graph, scc, errors) + # Run class decorator hooks (they requite complete MROs and no placeholders). + apply_class_plugin_hooks(graph, scc, errors) calculate_class_properties(graph, scc, errors) check_blockers(graph, scc) # Clean-up builtins, so that TypeVar etc. are not accessible without importing. @@ -132,6 +136,7 @@ def semantic_analysis_for_targets( check_type_arguments_in_targets(nodes, state, state.manager.errors) calculate_class_properties(graph, [state.id], state.manager.errors) + apply_class_plugin_hooks(graph, [state.id], state.manager.errors) def restore_saved_attrs(saved_attrs: SavedAttributes) -> None: @@ -382,14 +387,62 @@ def check_type_arguments_in_targets(targets: List[FineGrainedDeferredNode], stat target.node.accept(analyzer) +def apply_class_plugin_hooks(graph: 'Graph', scc: List[str], errors: Errors) -> None: + """Apply class plugin hooks within a SCC. + + We run these after to the main semantic analysis so that the hooks + don't need to deal with incomplete definitions such as placeholder + types. + + Note that some hooks incorrectly run during the main semantic + analysis pass, for historical reasons. + """ + num_passes = 0 + incomplete = True + # If we encounter a base class that has not been processed, we'll run another + # pass. This should eventually reach a fixed point. + while incomplete: + assert num_passes < 10, "Internal error: too many class plugin hook passes" + num_passes += 1 + incomplete = False + for module in scc: + state = graph[module] + tree = state.tree + assert tree + for _, node, _ in tree.local_definitions(): + if isinstance(node.node, TypeInfo): + if not apply_hooks_to_class(state.manager.semantic_analyzer, + module, node.node, state.options, tree, errors): + incomplete = True + + +def apply_hooks_to_class(self: SemanticAnalyzer, + module: str, + info: TypeInfo, + options: Options, + file_node: MypyFile, + errors: Errors) -> bool: + # TODO: Move more class-related hooks here? + defn = info.defn + ok = True + for decorator in defn.decorators: + with self.file_context(file_node, options, info): + decorator_name = self.get_fullname_for_hook(decorator) + if decorator_name: + hook = self.plugin.get_class_decorator_hook_2(decorator_name) + if hook: + ok = ok and hook(ClassDefContext(defn, decorator, self)) + return ok + + def calculate_class_properties(graph: 'Graph', scc: List[str], errors: Errors) -> None: for module in scc: - tree = graph[module].tree + state = graph[module] + tree = state.tree assert tree for _, node, _ in tree.local_definitions(): if isinstance(node.node, TypeInfo): - saved = (module, node.node, None) # module, class, function - with errors.scope.saved_scope(saved) if errors.scope else nullcontext(): + with state.manager.semantic_analyzer.file_context(tree, state.options, node.node): calculate_class_abstract_status(node.node, tree.is_stub, errors) check_protocol_status(node.node, errors) calculate_class_vars(node.node) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index bce1ee24a31a..4cddc59b0153 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1207,7 +1207,7 @@ class A2: from dataclasses import dataclass @dataclass -class A: # E: Name "x" already defined (possibly by an import) +class A: x: int = 0 x: int = 0 # E: Name "x" already defined on line 7 @@ -1619,3 +1619,69 @@ Child(x='', y='') # E: Argument "y" to "Child" has incompatible type "str"; exp Child(x='', y=1) Child(x=None, y=None) [builtins fixtures/dataclasses.pyi] + +[case testDataclassGenericInheritanceSpecialCase1] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Generic, TypeVar, List + +T = TypeVar("T") + +@dataclass +class Parent(Generic[T]): + x: List[T] + +@dataclass +class Child1(Parent["Child2"]): ... + +@dataclass +class Child2(Parent["Child1"]): ... + +def f(c: Child2) -> None: + reveal_type(Child1([c]).x) # N: Revealed type is "builtins.list[__main__.Child2]" + +def g(c: Child1) -> None: + reveal_type(Child2([c]).x) # N: Revealed type is "builtins.list[__main__.Child1]" +[builtins fixtures/dataclasses.pyi] + +[case testDataclassGenericInheritanceSpecialCase2] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Generic, TypeVar + +T = TypeVar("T") + +# A subclass might be analyzed before base in import cycles. They are +# defined here in reversed order to simulate this. + +@dataclass +class Child1(Parent["Child2"]): + x: int + +@dataclass +class Child2(Parent["Child1"]): + y: int + +@dataclass +class Parent(Generic[T]): + key: str + +Child1(x=1, key='') +Child2(y=1, key='') +[builtins fixtures/dataclasses.pyi] + +[case testDataclassGenericWithBound] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Generic, TypeVar + +T = TypeVar("T", bound="C") + +@dataclass +class C(Generic[T]): + x: int + +c: C[C] +d: C[str] # E: Type argument "str" of "C" must be a subtype of "C[Any]" +C(x=2) +[builtins fixtures/dataclasses.pyi] From 8faf44ad44f19c2dcf3f31f12eed5e58494fc3a3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 11 May 2022 15:16:19 +0100 Subject: [PATCH 59/79] Fix crash related to functools.total_ordering and forward reference (#12767) Run the plugin in a later pass to avoid placeholder nodes. Fixes #11728. --- mypy/plugins/default.py | 6 +++--- mypy/plugins/functools.py | 10 ++++++---- test-data/unit/check-functools.test | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 50e0e8cb4315..0ae95eb040db 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -96,7 +96,6 @@ def get_class_decorator_hook(self, fullname: str ) -> Optional[Callable[[ClassDefContext], None]]: from mypy.plugins import attrs from mypy.plugins import dataclasses - from mypy.plugins import functools if fullname in attrs.attr_class_makers: return attrs.attr_class_maker_callback @@ -118,17 +117,18 @@ def get_class_decorator_hook(self, fullname: str ) elif fullname in dataclasses.dataclass_makers: return dataclasses.dataclass_tag_callback - elif fullname in functools.functools_total_ordering_makers: - return functools.functools_total_ordering_maker_callback return None def get_class_decorator_hook_2(self, fullname: str ) -> Optional[Callable[[ClassDefContext], bool]]: from mypy.plugins import dataclasses + from mypy.plugins import functools if fullname in dataclasses.dataclass_makers: return dataclasses.dataclass_class_maker_callback + elif fullname in functools.functools_total_ordering_makers: + return functools.functools_total_ordering_maker_callback return None diff --git a/mypy/plugins/functools.py b/mypy/plugins/functools.py index 9e4d24283049..db10b7f1a262 100644 --- a/mypy/plugins/functools.py +++ b/mypy/plugins/functools.py @@ -26,25 +26,25 @@ class _MethodInfo(NamedTuple): def functools_total_ordering_maker_callback(ctx: mypy.plugin.ClassDefContext, - auto_attribs_default: bool = False) -> None: + auto_attribs_default: bool = False) -> bool: """Add dunder methods to classes decorated with functools.total_ordering.""" if ctx.api.options.python_version < (3,): # This plugin is not supported in Python 2 mode (it's a no-op). - return + return True comparison_methods = _analyze_class(ctx) if not comparison_methods: ctx.api.fail( 'No ordering operation defined when using "functools.total_ordering": < > <= >=', ctx.reason) - return + return True # prefer __lt__ to __le__ to __gt__ to __ge__ root = max(comparison_methods, key=lambda k: (comparison_methods[k] is None, k)) root_method = comparison_methods[root] if not root_method: # None of the defined comparison methods can be analysed - return + return True other_type = _find_other_type(root_method) bool_type = ctx.api.named_type('builtins.bool') @@ -61,6 +61,8 @@ def functools_total_ordering_maker_callback(ctx: mypy.plugin.ClassDefContext, args = [Argument(Var('other', other_type), other_type, None, ARG_POS)] add_method_to_class(ctx.api, ctx.cls, additional_op, args, ret_type) + return True + def _find_other_type(method: _MethodInfo) -> Type: """Find the type of the ``other`` argument in a comparison method.""" diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 5f9159ab9c52..a2c6ba2eee05 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -132,3 +132,24 @@ from typing import TypeVar, Generic _T = TypeVar('_T') class cached_property(Generic[_T]): ... [builtins fixtures/property.pyi] + +[case testTotalOrderingWithForwardReference] +from typing import Generic, Any, TypeVar +import functools + +T = TypeVar("T", bound="C") + +@functools.total_ordering +class D(Generic[T]): + def __lt__(self, other: Any) -> bool: + ... + +class C: + pass + +def f(d: D[C]) -> None: + reveal_type(d.__gt__) # N: Revealed type is "def (other: Any) -> builtins.bool" + +d: D[int] # E: Type argument "int" of "D" must be a subtype of "C" +[builtins fixtures/ops.pyi] +[builtins fixtures/dict.pyi] From 5ceaf3d7ec67a9bdee78d656e24dc9e25051759c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 13 May 2022 13:38:20 +0100 Subject: [PATCH 60/79] [mypyc] Fix TypeError in lambda argument to overloaded function (#12780) Previously we could export an invalid type for a lambda passed to an overloaded function. We find the matching overload variant by type checking the arguments against all overload items. We exported types for lambdas always from the final potential overload item, even if that wasn't the matching one. This could result in mypyc adding invalid type coercions that could result in bogus TypeErrors. The fix is to store types inferred when looking for matching overload items into temporary type maps, and only the type map from the matching item gets merged into the exported types. This doesn't fully solve the problem -- if there are Any types in the arguments, we can still export invalid types. This should be enough to unblock syncing typeshed (#12766). Typeshed started triggering the issue in compiled mypy when re.sub was changed to an overloaded function. Work on #12773. --- mypy/build.py | 5 +- mypy/checker.py | 247 ++++++++++++++++------------ mypy/checkexpr.py | 40 +++-- mypy/checkstrformat.py | 8 +- mypyc/test-data/run-functions.test | 28 ++++ test-data/unit/typexport-basic.test | 38 ++++- 6 files changed, 238 insertions(+), 128 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index ba2d1b1b3d35..c0b9aff5ab32 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2217,7 +2217,10 @@ def type_checker(self) -> TypeChecker: return self._type_checker def type_map(self) -> Dict[Expression, Type]: - return self.type_checker().type_map + # We can extract the master type map directly since at this + # point no temporary type maps can be active. + assert len(self.type_checker()._type_maps) == 1 + return self.type_checker()._type_maps[0] def type_check_second_pass(self) -> bool: if self.options.semantic_analysis_only: diff --git a/mypy/checker.py b/mypy/checker.py index cff637dbb57a..109a3b1f15d2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -161,8 +161,15 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface): errors: Errors # Utility for generating messages msg: MessageBuilder - # Types of type checked nodes - type_map: Dict[Expression, Type] + # Types of type checked nodes. The first item is the "master" type + # map that will store the final, exported types. Additional items + # are temporary type maps used during type inference, and these + # will be eventually popped and either discarded or merged into + # the master type map. + # + # Avoid accessing this directly, but prefer the lookup_type(), + # has_type() etc. helpers instead. + _type_maps: List[Dict[Expression, Type]] # Helper for managing conditional types binder: ConditionalTypeBinder @@ -246,7 +253,7 @@ def __init__(self, errors: Errors, modules: Dict[str, MypyFile], options: Option self.partial_reported = set() self.var_decl_frames = {} self.deferred_nodes = [] - self.type_map = {} + self._type_maps = [{}] self.module_refs = set() self.pass_num = 0 self.current_node_deferred = False @@ -283,7 +290,9 @@ def reset(self) -> None: self.partial_reported.clear() self.module_refs.clear() self.binder = ConditionalTypeBinder() - self.type_map.clear() + self._type_maps[1:] = [] + self._type_maps[0].clear() + self.temp_type_map = None assert self.inferred_attribute_types is None assert self.partial_types == [] @@ -2227,9 +2236,9 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if len(s.lvalues) > 1: # Chained assignment (e.g. x = y = ...). # Make sure that rvalue type will not be reinferred. - if s.rvalue not in self.type_map: + if not self.has_type(s.rvalue): self.expr_checker.accept(s.rvalue) - rvalue = self.temp_node(self.type_map[s.rvalue], s) + rvalue = self.temp_node(self.lookup_type(s.rvalue), s) for lv in s.lvalues[:-1]: with self.enter_final_context(s.is_final_def): self.check_assignment(lv, rvalue, s.type is None) @@ -2935,7 +2944,9 @@ def check_multi_assignment_from_union(self, lvalues: List[Expression], rvalue: E infer_lvalue_type=infer_lvalue_type, rv_type=item, undefined_rvalue=True) for t, lv in zip(transposed, self.flatten_lvalues(lvalues)): - t.append(self.type_map.pop(lv, AnyType(TypeOfAny.special_form))) + # We can access _type_maps directly since temporary type maps are + # only created within expressions. + t.append(self._type_maps[0].pop(lv, AnyType(TypeOfAny.special_form))) union_types = tuple(make_simplified_union(col) for col in transposed) for expr, items in assignments.items(): # Bind a union of types collected in 'assignments' to every expression. @@ -3986,8 +3997,8 @@ def visit_decorator(self, e: Decorator) -> None: # if this is a expression like @b.a where b is an object, get the type of b # so we can pass it the method hook in the plugins object_type: Optional[Type] = None - if fullname is None and isinstance(d, MemberExpr) and d.expr in self.type_map: - object_type = self.type_map[d.expr] + if fullname is None and isinstance(d, MemberExpr) and self.has_type(d.expr): + object_type = self.lookup_type(d.expr) fullname = self.expr_checker.method_fullname(object_type, d.name) self.check_for_untyped_decorator(e.func, dec, d) sig, t2 = self.expr_checker.check_call(dec, [temp], @@ -4545,8 +4556,6 @@ def find_type_equals_check(self, node: ComparisonExpr, expr_indices: List[int] expr_indices: The list of indices of expressions in ``node`` that are being compared """ - type_map = self.type_map - def is_type_call(expr: CallExpr) -> bool: """Is expr a call to type with one argument?""" return (refers_to_fullname(expr.callee, 'builtins.type') @@ -4565,7 +4574,7 @@ def is_type_call(expr: CallExpr) -> bool: if isinstance(expr, CallExpr) and is_type_call(expr): exprs_in_type_calls.append(expr.args[0]) else: - current_type = get_isinstance_type(expr, type_map) + current_type = self.get_isinstance_type(expr) if current_type is None: continue if type_being_compared is not None: @@ -4588,7 +4597,7 @@ def is_type_call(expr: CallExpr) -> bool: else_maps: List[TypeMap] = [] for expr in exprs_in_type_calls: current_if_type, current_else_type = self.conditional_types_with_intersection( - type_map[expr], + self.lookup_type(expr), type_being_compared, expr ) @@ -4633,12 +4642,11 @@ def find_isinstance_check(self, node: Expression Can return None, None in situations involving NoReturn. """ if_map, else_map = self.find_isinstance_check_helper(node) - new_if_map = self.propagate_up_typemap_info(self.type_map, if_map) - new_else_map = self.propagate_up_typemap_info(self.type_map, else_map) + new_if_map = self.propagate_up_typemap_info(if_map) + new_else_map = self.propagate_up_typemap_info(else_map) return new_if_map, new_else_map def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeMap]: - type_map = self.type_map if is_true_literal(node): return {}, None if is_false_literal(node): @@ -4653,8 +4661,8 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM return conditional_types_to_typemaps( expr, *self.conditional_types_with_intersection( - type_map[expr], - get_isinstance_type(node.args[1], type_map), + self.lookup_type(expr), + self.get_isinstance_type(node.args[1]), expr ) ) @@ -4662,12 +4670,12 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM if len(node.args) != 2: # the error will be reported elsewhere return {}, {} if literal(expr) == LITERAL_TYPE: - return self.infer_issubclass_maps(node, expr, type_map) + return self.infer_issubclass_maps(node, expr) elif refers_to_fullname(node.callee, 'builtins.callable'): if len(node.args) != 1: # the error will be reported elsewhere return {}, {} if literal(expr) == LITERAL_TYPE: - vartype = type_map[expr] + vartype = self.lookup_type(expr) return self.conditional_callable_type_map(expr, vartype) elif isinstance(node.callee, RefExpr): if node.callee.type_guard is not None: @@ -4691,14 +4699,14 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM operand_types = [] narrowable_operand_index_to_hash = {} for i, expr in enumerate(operands): - if expr not in type_map: + if not self.has_type(expr): return {}, {} - expr_type = type_map[expr] + expr_type = self.lookup_type(expr) operand_types.append(expr_type) if (literal(expr) == LITERAL_TYPE and not is_literal_none(expr) - and not is_literal_enum(type_map, expr)): + and not self.is_literal_enum(expr)): h = literal_hash(expr) if h is not None: narrowable_operand_index_to_hash[i] = h @@ -4871,7 +4879,7 @@ def has_no_custom_eq_checks(t: Type) -> bool: # Restrict the type of the variable to True-ish/False-ish in the if and else branches # respectively - original_vartype = type_map[node] + original_vartype = self.lookup_type(node) self._check_for_truthy_type(original_vartype, node) vartype = try_expanding_sum_type_to_union(original_vartype, "builtins.bool") @@ -4890,7 +4898,6 @@ def has_no_custom_eq_checks(t: Type) -> bool: return if_map, else_map def propagate_up_typemap_info(self, - existing_types: Mapping[Expression, Type], new_types: TypeMap) -> TypeMap: """Attempts refining parent expressions of any MemberExpr or IndexExprs in new_types. @@ -4924,7 +4931,7 @@ def propagate_up_typemap_info(self, output_map[expr] = expr_type # Next, try using this information to refine the parent types, if applicable. - new_mapping = self.refine_parent_types(existing_types, expr, expr_type) + new_mapping = self.refine_parent_types(expr, expr_type) for parent_expr, proposed_parent_type in new_mapping.items(): # We don't try inferring anything if we've already inferred something for # the parent expression. @@ -4935,7 +4942,6 @@ def propagate_up_typemap_info(self, return output_map def refine_parent_types(self, - existing_types: Mapping[Expression, Type], expr: Expression, expr_type: Type) -> Mapping[Expression, Type]: """Checks if the given expr is a 'lookup operation' into a union and iteratively refines @@ -4958,7 +4964,7 @@ def refine_parent_types(self, # operation against arbitrary types. if isinstance(expr, MemberExpr): parent_expr = expr.expr - parent_type = existing_types.get(parent_expr) + parent_type = self.lookup_type_or_none(parent_expr) member_name = expr.name def replay_lookup(new_parent_type: ProperType) -> Optional[Type]: @@ -4981,9 +4987,9 @@ def replay_lookup(new_parent_type: ProperType) -> Optional[Type]: return member_type elif isinstance(expr, IndexExpr): parent_expr = expr.base - parent_type = existing_types.get(parent_expr) + parent_type = self.lookup_type_or_none(parent_expr) - index_type = existing_types.get(expr.index) + index_type = self.lookup_type_or_none(expr.index) if index_type is None: return output @@ -5335,7 +5341,41 @@ def str_type(self) -> Instance: def store_type(self, node: Expression, typ: Type) -> None: """Store the type of a node in the type map.""" - self.type_map[node] = typ + self._type_maps[-1][node] = typ + + def has_type(self, node: Expression) -> bool: + for m in reversed(self._type_maps): + if node in m: + return True + return False + + def lookup_type_or_none(self, node: Expression) -> Optional[Type]: + for m in reversed(self._type_maps): + if node in m: + return m[node] + return None + + def lookup_type(self, node: Expression) -> Type: + for m in reversed(self._type_maps): + t = m.get(node) + if t is not None: + return t + raise KeyError(node) + + def store_types(self, d: Dict[Expression, Type]) -> None: + self._type_maps[-1].update(d) + + @contextmanager + def local_type_map(self) -> Iterator[Dict[Expression, Type]]: + """Store inferred types into a temporary type map (returned). + + This can be used to perform type checking "experiments" without + affecting exported types (which are used by mypyc). + """ + temp_type_map: Dict[Expression, Type] = {} + self._type_maps.append(temp_type_map) + yield temp_type_map + self._type_maps.pop() def in_checked_function(self) -> bool: """Should we type-check the current function? @@ -5579,11 +5619,10 @@ def push_type_map(self, type_map: 'TypeMap') -> None: def infer_issubclass_maps(self, node: CallExpr, expr: Expression, - type_map: Dict[Expression, Type] ) -> Tuple[TypeMap, TypeMap]: """Infer type restrictions for an expression in issubclass call.""" - vartype = type_map[expr] - type = get_isinstance_type(node.args[1], type_map) + vartype = self.lookup_type(expr) + type = self.get_isinstance_type(node.args[1]) if isinstance(vartype, TypeVarType): vartype = vartype.upper_bound vartype = get_proper_type(vartype) @@ -5683,6 +5722,75 @@ def is_writable_attribute(self, node: Node) -> bool: else: return False + def get_isinstance_type(self, expr: Expression) -> Optional[List[TypeRange]]: + if isinstance(expr, OpExpr) and expr.op == '|': + left = self.get_isinstance_type(expr.left) + right = self.get_isinstance_type(expr.right) + if left is None or right is None: + return None + return left + right + all_types = get_proper_types(flatten_types(self.lookup_type(expr))) + types: List[TypeRange] = [] + for typ in all_types: + if isinstance(typ, FunctionLike) and typ.is_type_obj(): + # Type variables may be present -- erase them, which is the best + # we can do (outside disallowing them here). + erased_type = erase_typevars(typ.items[0].ret_type) + types.append(TypeRange(erased_type, is_upper_bound=False)) + elif isinstance(typ, TypeType): + # Type[A] means "any type that is a subtype of A" rather than "precisely type A" + # we indicate this by setting is_upper_bound flag + types.append(TypeRange(typ.item, is_upper_bound=True)) + elif isinstance(typ, Instance) and typ.type.fullname == 'builtins.type': + object_type = Instance(typ.type.mro[-1], []) + types.append(TypeRange(object_type, is_upper_bound=True)) + elif isinstance(typ, AnyType): + types.append(TypeRange(typ, is_upper_bound=False)) + else: # we didn't see an actual type, but rather a variable with unknown value + return None + if not types: + # this can happen if someone has empty tuple as 2nd argument to isinstance + # strictly speaking, we should return UninhabitedType but for simplicity we will simply + # refuse to do any type inference for now + return None + return types + + def is_literal_enum(self, n: Expression) -> bool: + """Returns true if this expression (with the given type context) is an Enum literal. + + For example, if we had an enum: + + class Foo(Enum): + A = 1 + B = 2 + + ...and if the expression 'Foo' referred to that enum within the current type context, + then the expression 'Foo.A' would be a literal enum. However, if we did 'a = Foo.A', + then the variable 'a' would *not* be a literal enum. + + We occasionally special-case expressions like 'Foo.A' and treat them as a single primitive + unit for the same reasons we sometimes treat 'True', 'False', or 'None' as a single + primitive unit. + """ + if not isinstance(n, MemberExpr) or not isinstance(n.expr, NameExpr): + return False + + parent_type = self.lookup_type_or_none(n.expr) + member_type = self.lookup_type_or_none(n) + if member_type is None or parent_type is None: + return False + + parent_type = get_proper_type(parent_type) + member_type = get_proper_type(coerce_to_literal(member_type)) + if not isinstance(parent_type, FunctionLike) or not isinstance(member_type, LiteralType): + return False + + if not parent_type.is_type_obj(): + return False + + return (member_type.is_enum_literal() + and member_type.fallback.type == parent_type.type_object()) + @overload def conditional_types(current_type: Type, @@ -5785,42 +5893,6 @@ def is_false_literal(n: Expression) -> bool: or isinstance(n, IntExpr) and n.value == 0) -def is_literal_enum(type_map: Mapping[Expression, Type], n: Expression) -> bool: - """Returns true if this expression (with the given type context) is an Enum literal. - - For example, if we had an enum: - - class Foo(Enum): - A = 1 - B = 2 - - ...and if the expression 'Foo' referred to that enum within the current type context, - then the expression 'Foo.A' would be a literal enum. However, if we did 'a = Foo.A', - then the variable 'a' would *not* be a literal enum. - - We occasionally special-case expressions like 'Foo.A' and treat them as a single primitive - unit for the same reasons we sometimes treat 'True', 'False', or 'None' as a single - primitive unit. - """ - if not isinstance(n, MemberExpr) or not isinstance(n.expr, NameExpr): - return False - - parent_type = type_map.get(n.expr) - member_type = type_map.get(n) - if member_type is None or parent_type is None: - return False - - parent_type = get_proper_type(parent_type) - member_type = get_proper_type(coerce_to_literal(member_type)) - if not isinstance(parent_type, FunctionLike) or not isinstance(member_type, LiteralType): - return False - - if not parent_type.is_type_obj(): - return False - - return member_type.is_enum_literal() and member_type.fallback.type == parent_type.type_object() - - def is_literal_none(n: Expression) -> bool: """Returns true if this expression is the 'None' literal/keyword.""" return isinstance(n, NameExpr) and n.fullname == 'builtins.None' @@ -5986,41 +6058,6 @@ def flatten_types(t: Type) -> List[Type]: return [t] -def get_isinstance_type(expr: Expression, - type_map: Dict[Expression, Type]) -> Optional[List[TypeRange]]: - if isinstance(expr, OpExpr) and expr.op == '|': - left = get_isinstance_type(expr.left, type_map) - right = get_isinstance_type(expr.right, type_map) - if left is None or right is None: - return None - return left + right - all_types = get_proper_types(flatten_types(type_map[expr])) - types: List[TypeRange] = [] - for typ in all_types: - if isinstance(typ, FunctionLike) and typ.is_type_obj(): - # Type variables may be present -- erase them, which is the best - # we can do (outside disallowing them here). - erased_type = erase_typevars(typ.items[0].ret_type) - types.append(TypeRange(erased_type, is_upper_bound=False)) - elif isinstance(typ, TypeType): - # Type[A] means "any type that is a subtype of A" rather than "precisely type A" - # we indicate this by setting is_upper_bound flag - types.append(TypeRange(typ.item, is_upper_bound=True)) - elif isinstance(typ, Instance) and typ.type.fullname == 'builtins.type': - object_type = Instance(typ.type.mro[-1], []) - types.append(TypeRange(object_type, is_upper_bound=True)) - elif isinstance(typ, AnyType): - types.append(TypeRange(typ, is_upper_bound=False)) - else: # we didn't see an actual type, but rather a variable whose value is unknown to us - return None - if not types: - # this can happen if someone has empty tuple as 2nd argument to isinstance - # strictly speaking, we should return UninhabitedType but for simplicity we will simply - # refuse to do any type inference for now - return None - return types - - def expand_func(defn: FuncItem, map: Dict[TypeVarId, Type]) -> FuncItem: visitor = TypeTransformVisitor(map) ret = defn.accept(visitor) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 21fd7d81967f..bfbe961adc7a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -369,9 +369,9 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> # be invoked for these. if (fullname is None and isinstance(e.callee, MemberExpr) - and e.callee.expr in self.chk.type_map): + and self.chk.has_type(e.callee.expr)): member = e.callee.name - object_type = self.chk.type_map[e.callee.expr] + object_type = self.chk.lookup_type(e.callee.expr) ret_type = self.check_call_expr_with_callee_type(callee_type, e, fullname, object_type, member) if isinstance(e.callee, RefExpr) and len(e.args) == 2: @@ -401,8 +401,8 @@ def check_str_format_call(self, e: CallExpr) -> None: format_value = None if isinstance(e.callee.expr, (StrExpr, UnicodeExpr)): format_value = e.callee.expr.value - elif e.callee.expr in self.chk.type_map: - base_typ = try_getting_literal(self.chk.type_map[e.callee.expr]) + elif self.chk.has_type(e.callee.expr): + base_typ = try_getting_literal(self.chk.lookup_type(e.callee.expr)) if isinstance(base_typ, LiteralType) and isinstance(base_typ.value, str): format_value = base_typ.value if format_value is not None: @@ -442,7 +442,7 @@ def always_returns_none(self, node: Expression) -> bool: if self.defn_returns_none(node.node): return True if isinstance(node, MemberExpr) and node.node is None: # instance or class attribute - typ = get_proper_type(self.chk.type_map.get(node.expr)) + typ = get_proper_type(self.chk.lookup_type(node.expr)) if isinstance(typ, Instance): info = typ.type elif isinstance(typ, CallableType) and typ.is_type_obj(): @@ -478,7 +478,7 @@ def defn_returns_none(self, defn: Optional[SymbolNode]) -> bool: def check_runtime_protocol_test(self, e: CallExpr) -> None: for expr in mypy.checker.flatten(e.args[1]): - tp = get_proper_type(self.chk.type_map[expr]) + tp = get_proper_type(self.chk.lookup_type(expr)) if (isinstance(tp, CallableType) and tp.is_type_obj() and tp.type_object().is_protocol and not tp.type_object().runtime_protocol): @@ -486,7 +486,7 @@ def check_runtime_protocol_test(self, e: CallExpr) -> None: def check_protocol_issubclass(self, e: CallExpr) -> None: for expr in mypy.checker.flatten(e.args[1]): - tp = get_proper_type(self.chk.type_map[expr]) + tp = get_proper_type(self.chk.lookup_type(expr)) if (isinstance(tp, CallableType) and tp.is_type_obj() and tp.type_object().is_protocol): attr_members = non_method_protocol_members(tp.type_object()) @@ -1740,18 +1740,20 @@ def infer_overload_return_type(self, return_types: List[Type] = [] inferred_types: List[Type] = [] args_contain_any = any(map(has_any_type, arg_types)) + type_maps: List[Dict[Expression, Type]] = [] for typ in plausible_targets: assert self.msg is self.chk.msg with self.msg.filter_errors() as w: - ret_type, infer_type = self.check_call( - callee=typ, - args=args, - arg_kinds=arg_kinds, - arg_names=arg_names, - context=context, - callable_name=callable_name, - object_type=object_type) + with self.chk.local_type_map() as m: + ret_type, infer_type = self.check_call( + callee=typ, + args=args, + arg_kinds=arg_kinds, + arg_names=arg_names, + context=context, + callable_name=callable_name, + object_type=object_type) is_match = not w.has_new_errors() if is_match: # Return early if possible; otherwise record info so we can @@ -1761,6 +1763,7 @@ def infer_overload_return_type(self, matches.append(typ) return_types.append(ret_type) inferred_types.append(infer_type) + type_maps.append(m) if len(matches) == 0: # No match was found @@ -1769,8 +1772,10 @@ def infer_overload_return_type(self, # An argument of type or containing the type 'Any' caused ambiguity. # We try returning a precise type if we can. If not, we give up and just return 'Any'. if all_same_types(return_types): + self.chk.store_types(type_maps[0]) return return_types[0], inferred_types[0] elif all_same_types([erase_type(typ) for typ in return_types]): + self.chk.store_types(type_maps[0]) return erase_type(return_types[0]), erase_type(inferred_types[0]) else: return self.check_call(callee=AnyType(TypeOfAny.special_form), @@ -1782,6 +1787,7 @@ def infer_overload_return_type(self, object_type=object_type) else: # Success! No ambiguity; return the first match. + self.chk.store_types(type_maps[0]) return return_types[0], inferred_types[0] def overload_erased_call_targets(self, @@ -3546,10 +3552,10 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type: # Type context available. self.chk.return_types.append(inferred_type.ret_type) self.chk.check_func_item(e, type_override=type_override) - if e.expr() not in self.chk.type_map: + if not self.chk.has_type(e.expr()): # TODO: return expression must be accepted before exiting function scope. self.accept(e.expr(), allow_none_return=True) - ret_type = self.chk.type_map[e.expr()] + ret_type = self.chk.lookup_type(e.expr()) self.chk.return_types.pop() return replace_callable_return_type(inferred_type, ret_type) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index 20b3716ea513..60a0d35ede08 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -317,7 +317,7 @@ def check_specs_in_format_call(self, call: CallExpr, assert len(replacements) == len(specs) for spec, repl in zip(specs, replacements): repl = self.apply_field_accessors(spec, repl, ctx=call) - actual_type = repl.type if isinstance(repl, TempNode) else self.chk.type_map.get(repl) + actual_type = repl.type if isinstance(repl, TempNode) else self.chk.lookup_type(repl) assert actual_type is not None # Special case custom formatting. @@ -370,7 +370,7 @@ def perform_special_format_checks(self, spec: ConversionSpecifier, call: CallExp if spec.conv_type == 'c': if isinstance(repl, (StrExpr, BytesExpr)) and len(repl.value) != 1: self.msg.requires_int_or_char(call, format_call=True) - c_typ = get_proper_type(self.chk.type_map[repl]) + c_typ = get_proper_type(self.chk.lookup_type(repl)) if isinstance(c_typ, Instance) and c_typ.last_known_value: c_typ = c_typ.last_known_value if isinstance(c_typ, LiteralType) and isinstance(c_typ.value, str): @@ -442,7 +442,7 @@ def get_expr_by_position(self, pos: int, call: CallExpr) -> Optional[Expression] # Fall back to *args when present in call. star_arg = star_args[0] - varargs_type = get_proper_type(self.chk.type_map[star_arg]) + varargs_type = get_proper_type(self.chk.lookup_type(star_arg)) if (not isinstance(varargs_type, Instance) or not varargs_type.type.has_base('typing.Sequence')): # Error should be already reported. @@ -465,7 +465,7 @@ def get_expr_by_name(self, key: str, call: CallExpr) -> Optional[Expression]: if not star_args_2: return None star_arg_2 = star_args_2[0] - kwargs_type = get_proper_type(self.chk.type_map[star_arg_2]) + kwargs_type = get_proper_type(self.chk.lookup_type(star_arg_2)) if (not isinstance(kwargs_type, Instance) or not kwargs_type.type.has_base('typing.Mapping')): # Error should be already reported. diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index 66b56503f329..77e9c9ed32f7 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -1192,3 +1192,31 @@ def foo(): pass def test_decorator_name(): assert foo.__name__ == "foo" + +[case testLambdaArgToOverloaded] +from lib import sub + +def test_str_overload() -> None: + assert sub('x', lambda m: m) == 'x' + +def test_bytes_overload() -> None: + assert sub(b'x', lambda m: m) == b'x' + +[file lib.py] +from typing import overload, Callable, TypeVar, Generic + +T = TypeVar("T") + +class Match(Generic[T]): + def __init__(self, x: T) -> None: + self.x = x + + def group(self, n: int) -> T: + return self.x + +@overload +def sub(s: str, f: Callable[[str], str]) -> str: ... +@overload +def sub(s: bytes, f: Callable[[bytes], bytes]) -> bytes: ... +def sub(s, f): + return f(s) diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index bdefb49e3038..5cbdf38d1b4f 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -1182,6 +1182,43 @@ IntExpr(2) : Literal[1]? OpExpr(2) : builtins.str StrExpr(2) : Literal['%d']? +[case testExportOverloadArgType] +## LambdaExpr|NameExpr +from typing import List, overload, Callable +@overload +def f(x: int, f: Callable[[int], int]) -> None: ... +@overload +def f(x: str, f: Callable[[str], str]) -> None: ... +def f(x): ... +f( + 1, lambda x: x) +[builtins fixtures/list.pyi] +[out] +NameExpr(8) : Overload(def (x: builtins.int, f: def (builtins.int) -> builtins.int), def (x: builtins.str, f: def (builtins.str) -> builtins.str)) +LambdaExpr(9) : def (builtins.int) -> builtins.int +NameExpr(9) : builtins.int + +[case testExportOverloadArgTypeNested] +## LambdaExpr +from typing import overload, Callable +@overload +def f(x: int, f: Callable[[int], int]) -> int: ... +@overload +def f(x: str, f: Callable[[str], str]) -> str: ... +def f(x): ... +f( + f(1, lambda y: y), + lambda x: x) +f( + f('x', lambda y: y), + lambda x: x) +[builtins fixtures/list.pyi] +[out] +LambdaExpr(9) : def (builtins.int) -> builtins.int +LambdaExpr(10) : def (builtins.int) -> builtins.int +LambdaExpr(12) : def (builtins.str) -> builtins.str +LambdaExpr(13) : def (builtins.str) -> builtins.str + -- TODO -- -- test expressions @@ -1193,7 +1230,6 @@ StrExpr(2) : Literal['%d']? -- more complex lambda (multiple arguments etc.) -- list comprehension -- generator expression --- overloads -- other things -- type inference -- default argument value From 50a653e0714e303178c890854b9074fc030907f0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 13 May 2022 14:49:53 +0100 Subject: [PATCH 61/79] Fix forward references and generic inheritance in attrs classes (#12772) Move the attrs plugin to a later pass, so that we won't have placeholders. Fix various issues related to forward references and generic inheritance, including some crashes. This is follow-up to #12762 and related to #12656 and #12633. --- mypy/plugins/attrs.py | 112 ++++++++++++++---------- mypy/plugins/default.py | 40 +++++---- test-data/unit/check-attr.test | 143 +++++++++++++++++++++++++++++++ test-data/unit/fixtures/dict.pyi | 2 +- 4 files changed, 235 insertions(+), 62 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 38fd2f040be5..dbce8a402141 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -7,7 +7,6 @@ import mypy.plugin # To avoid circular imports. from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError -from mypy.lookup import lookup_fully_qualified from mypy.nodes import ( Context, Argument, Var, ARG_OPT, ARG_POS, TypeInfo, AssignmentStmt, TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncDef, @@ -61,10 +60,12 @@ class Converter: """Holds information about a `converter=` argument""" def __init__(self, - name: Optional[str] = None, - is_attr_converters_optional: bool = False) -> None: - self.name = name + type: Optional[Type] = None, + is_attr_converters_optional: bool = False, + is_invalid_converter: bool = False) -> None: + self.type = type self.is_attr_converters_optional = is_attr_converters_optional + self.is_invalid_converter = is_invalid_converter class Attribute: @@ -89,29 +90,14 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: init_type = self.init_type or self.info[self.name].type - if self.converter.name: + if self.converter.type and not self.converter.is_invalid_converter: # When a converter is set the init_type is overridden by the first argument # of the converter method. - converter = lookup_fully_qualified(self.converter.name, ctx.api.modules, - raise_on_missing=False) - if not converter: - # The converter may be a local variable. Check there too. - converter = ctx.api.lookup_qualified(self.converter.name, self.info, True) - - # Get the type of the converter. - converter_type: Optional[Type] = None - if converter and isinstance(converter.node, TypeInfo): - from mypy.checkmember import type_object_type # To avoid import cycle. - converter_type = type_object_type(converter.node, ctx.api.named_type) - elif converter and isinstance(converter.node, OverloadedFuncDef): - converter_type = converter.node.type - elif converter and converter.type: - converter_type = converter.type - + converter_type = self.converter.type init_type = None converter_type = get_proper_type(converter_type) if isinstance(converter_type, CallableType) and converter_type.arg_types: - init_type = ctx.api.anal_type(converter_type.arg_types[0]) + init_type = converter_type.arg_types[0] elif isinstance(converter_type, Overloaded): types: List[Type] = [] for item in converter_type.items: @@ -124,8 +110,7 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: types.append(item.arg_types[0]) # Make a union of all the valid types. if types: - args = make_simplified_union(types) - init_type = ctx.api.anal_type(args) + init_type = make_simplified_union(types) if self.converter.is_attr_converters_optional and init_type: # If the converter was attr.converter.optional(type) then add None to @@ -135,9 +120,8 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: if not init_type: ctx.api.fail("Cannot determine __init__ type from converter", self.context) init_type = AnyType(TypeOfAny.from_error) - elif self.converter.name == '': + elif self.converter.is_invalid_converter: # This means we had a converter but it's not of a type we can infer. - # Error was shown in _get_converter_name init_type = AnyType(TypeOfAny.from_error) if init_type is None: @@ -170,8 +154,9 @@ def serialize(self) -> JsonDict: 'has_default': self.has_default, 'init': self.init, 'kw_only': self.kw_only, - 'converter_name': self.converter.name, + 'converter_type': self.converter.type.serialize() if self.converter.type else None, 'converter_is_attr_converters_optional': self.converter.is_attr_converters_optional, + 'converter_is_invalid_converter': self.converter.is_invalid_converter, 'context_line': self.context.line, 'context_column': self.context.column, 'init_type': self.init_type.serialize() if self.init_type else None, @@ -185,22 +170,26 @@ def deserialize(cls, info: TypeInfo, raw_init_type = data['init_type'] init_type = deserialize_and_fixup_type(raw_init_type, api) if raw_init_type else None + converter_type = None + if data['converter_type']: + converter_type = deserialize_and_fixup_type(data['converter_type'], api) return Attribute(data['name'], info, data['has_default'], data['init'], data['kw_only'], - Converter(data['converter_name'], data['converter_is_attr_converters_optional']), + Converter(converter_type, data['converter_is_attr_converters_optional'], + data['converter_is_invalid_converter']), Context(line=data['context_line'], column=data['context_column']), init_type) def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None: """Expands type vars in the context of a subtype when an attribute is inherited from a generic super type.""" - if not isinstance(self.init_type, TypeVarType): - return - - self.init_type = map_type_from_supertype(self.init_type, sub_type, self.info) + if self.init_type: + self.init_type = map_type_from_supertype(self.init_type, sub_type, self.info) + else: + self.init_type = None def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool: @@ -258,9 +247,19 @@ def _get_decorator_optional_bool_argument( return default +def attr_tag_callback(ctx: 'mypy.plugin.ClassDefContext') -> None: + """Record that we have an attrs class in the main semantic analysis pass. + + The later pass implemented by attr_class_maker_callback will use this + to detect attrs lasses in base classes. + """ + # The value is ignored, only the existence matters. + ctx.cls.info.metadata['attrs_tag'] = {} + + def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', auto_attribs_default: Optional[bool] = False, - frozen_default: bool = False) -> None: + frozen_default: bool = False) -> bool: """Add necessary dunder methods to classes decorated with attr.s. attrs is a package that lets you define classes without writing dull boilerplate code. @@ -271,6 +270,9 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', into properties. See http://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works. + + If this returns False, some required metadata was not ready yet and we need another + pass. """ info = ctx.cls.info @@ -283,17 +285,26 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', kw_only = _get_decorator_bool_argument(ctx, 'kw_only', False) match_args = _get_decorator_bool_argument(ctx, 'match_args', True) + early_fail = False if ctx.api.options.python_version[0] < 3: if auto_attribs: ctx.api.fail("auto_attribs is not supported in Python 2", ctx.reason) - return + early_fail = True if not info.defn.base_type_exprs: # Note: This will not catch subclassing old-style classes. ctx.api.fail("attrs only works with new-style classes", info.defn) - return + early_fail = True if kw_only: ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, ctx.reason) - return + early_fail = True + if early_fail: + _add_empty_metadata(info) + return True + + for super_info in ctx.cls.info.mro[1:-1]: + if 'attrs_tag' in super_info.metadata and 'attrs' not in super_info.metadata: + # Super class is not ready yet. Request another pass. + return False attributes = _analyze_class(ctx, auto_attribs, kw_only) @@ -301,12 +312,10 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', for attr in attributes: node = info.get(attr.name) if node is None: - # This name is likely blocked by a star import. We don't need to defer because - # defer() is already called by mark_incomplete(). - return - if node.type is None and not ctx.api.final_iteration: - ctx.api.defer() - return + # This name is likely blocked by some semantic analysis error that + # should have been reported already. + _add_empty_metadata(info) + return True _add_attrs_magic_attribute(ctx, [(attr.name, info[attr.name].type) for attr in attributes]) if slots: @@ -330,6 +339,8 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', if frozen: _make_frozen(ctx, attributes) + return True + def _get_frozen(ctx: 'mypy.plugin.ClassDefContext', frozen_default: bool) -> bool: """Return whether this class is frozen.""" @@ -423,6 +434,14 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext', return attributes +def _add_empty_metadata(info: TypeInfo) -> None: + """Add empty metadata to mark that we've finished processing this class.""" + info.metadata['attrs'] = { + 'attributes': [], + 'frozen': False, + } + + def _detect_auto_attribs(ctx: 'mypy.plugin.ClassDefContext') -> bool: """Return whether auto_attribs should be enabled or disabled. @@ -602,12 +621,13 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext', if (isinstance(converter.node, FuncDef) and converter.node.type and isinstance(converter.node.type, FunctionLike)): - return Converter(converter.node.fullname) + return Converter(converter.node.type) elif (isinstance(converter.node, OverloadedFuncDef) and is_valid_overloaded_converter(converter.node)): - return Converter(converter.node.fullname) + return Converter(converter.node.type) elif isinstance(converter.node, TypeInfo): - return Converter(converter.node.fullname) + from mypy.checkmember import type_object_type # To avoid import cycle. + return Converter(type_object_type(converter.node, ctx.api.named_type)) if (isinstance(converter, CallExpr) and isinstance(converter.callee, RefExpr) @@ -625,7 +645,7 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext', "Unsupported converter, only named functions and types are currently supported", converter ) - return Converter('') + return Converter(None, is_invalid_converter=True) return Converter(None) diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index 0ae95eb040db..40997803aa7e 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -94,10 +94,34 @@ def get_attribute_hook(self, fullname: str def get_class_decorator_hook(self, fullname: str ) -> Optional[Callable[[ClassDefContext], None]]: + from mypy.plugins import dataclasses from mypy.plugins import attrs + + # These dataclass and attrs hooks run in the main semantic analysis pass + # and only tag known dataclasses/attrs classes, so that the second + # hooks (in get_class_decorator_hook_2) can detect dataclasses/attrs classes + # in the MRO. + if fullname in dataclasses.dataclass_makers: + return dataclasses.dataclass_tag_callback + if (fullname in attrs.attr_class_makers + or fullname in attrs.attr_dataclass_makers + or fullname in attrs.attr_frozen_makers + or fullname in attrs.attr_define_makers): + return attrs.attr_tag_callback + + return None + + def get_class_decorator_hook_2(self, fullname: str + ) -> Optional[Callable[[ClassDefContext], bool]]: from mypy.plugins import dataclasses + from mypy.plugins import functools + from mypy.plugins import attrs - if fullname in attrs.attr_class_makers: + if fullname in dataclasses.dataclass_makers: + return dataclasses.dataclass_class_maker_callback + elif fullname in functools.functools_total_ordering_makers: + return functools.functools_total_ordering_maker_callback + elif fullname in attrs.attr_class_makers: return attrs.attr_class_maker_callback elif fullname in attrs.attr_dataclass_makers: return partial( @@ -115,20 +139,6 @@ def get_class_decorator_hook(self, fullname: str attrs.attr_class_maker_callback, auto_attribs_default=None, ) - elif fullname in dataclasses.dataclass_makers: - return dataclasses.dataclass_tag_callback - - return None - - def get_class_decorator_hook_2(self, fullname: str - ) -> Optional[Callable[[ClassDefContext], bool]]: - from mypy.plugins import dataclasses - from mypy.plugins import functools - - if fullname in dataclasses.dataclass_makers: - return dataclasses.dataclass_class_maker_callback - elif fullname in functools.functools_total_ordering_makers: - return functools.functools_total_ordering_maker_callback return None diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index a69bd473624d..fdb0da7e0fce 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -544,6 +544,37 @@ reveal_type(sub.three) # N: Revealed type is "builtins.float" [builtins fixtures/bool.pyi] +[case testAttrsGenericInheritance3] +import attr +from typing import Any, Callable, Generic, TypeVar, List + +T = TypeVar("T") +S = TypeVar("S") + +@attr.s(auto_attribs=True) +class Parent(Generic[T]): + f: Callable[[T], Any] + +@attr.s(auto_attribs=True) +class Child(Parent[T]): ... + +class A: ... +def func(obj: A) -> bool: ... + +reveal_type(Child[A](func).f) # N: Revealed type is "def (__main__.A) -> Any" + +@attr.s(auto_attribs=True) +class Parent2(Generic[T]): + a: List[T] + +@attr.s(auto_attribs=True) +class Child2(Generic[T, S], Parent2[S]): + b: List[T] + +reveal_type(Child2([A()], [1]).a) # N: Revealed type is "builtins.list[__main__.A]" +reveal_type(Child2[int, A]([A()], [1]).b) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + [case testAttrsMultiGenericInheritance] from typing import Generic, TypeVar import attr @@ -1010,6 +1041,9 @@ class Good(object): @attr.s class Bad: # E: attrs only works with new-style classes pass +@attr.s +class SubclassOfBad(Bad): + pass [builtins_py2 fixtures/bool.pyi] [case testAttrsAutoAttribsPy2] @@ -1591,3 +1625,112 @@ class B: class AB(A, B): pass [builtins fixtures/attr.pyi] + +[case testAttrsForwardReferenceInTypeVarBound] +from typing import TypeVar, Generic +import attr + +T = TypeVar("T", bound="C") + +@attr.define +class D(Generic[T]): + x: int + +class C: + pass +[builtins fixtures/attr.pyi] + +[case testComplexTypeInAttrIb] +import a + +[file a.py] +import attr +import b +from typing import Callable + +@attr.s +class C: + a = attr.ib(type=Lst[int]) + # Note that for this test, the 'Value of type "int" is not indexable' errors are silly, + # and a consequence of Callable etc. being set to an int in the test stub. + b = attr.ib(type=Callable[[], C]) +[builtins fixtures/bool.pyi] + +[file b.py] +import attr +import a +from typing import List as Lst, Optional + +@attr.s +class D: + a = attr.ib(type=Lst[int]) + b = attr.ib(type=Optional[int]) +[builtins fixtures/list.pyi] +[out] +tmp/b.py:8: error: Value of type "int" is not indexable +tmp/a.py:7: error: Name "Lst" is not defined +tmp/a.py:10: error: Value of type "int" is not indexable + +[case testAttrsGenericInheritanceSpecialCase1] +import attr +from typing import Generic, TypeVar, List + +T = TypeVar("T") + +@attr.define +class Parent(Generic[T]): + x: List[T] + +@attr.define +class Child1(Parent["Child2"]): ... + +@attr.define +class Child2(Parent["Child1"]): ... + +def f(c: Child2) -> None: + reveal_type(Child1([c]).x) # N: Revealed type is "builtins.list[__main__.Child2]" + +def g(c: Child1) -> None: + reveal_type(Child2([c]).x) # N: Revealed type is "builtins.list[__main__.Child1]" +[builtins fixtures/list.pyi] + +[case testAttrsGenericInheritanceSpecialCase2] +import attr +from typing import Generic, TypeVar + +T = TypeVar("T") + +# A subclass might be analyzed before base in import cycles. They are +# defined here in reversed order to simulate this. + +@attr.define +class Child1(Parent["Child2"]): + x: int + +@attr.define +class Child2(Parent["Child1"]): + y: int + +@attr.define +class Parent(Generic[T]): + key: str + +Child1(x=1, key='') +Child2(y=1, key='') +[builtins fixtures/list.pyi] + +[case testAttrsUnsupportedConverterWithDisallowUntypedDefs] +# flags: --disallow-untyped-defs +import attr +from typing import Mapping, Any, Union + +def default_if_none(factory: Any) -> Any: pass + +@attr.s(slots=True, frozen=True) +class C: + name: Union[str, None] = attr.ib(default=None) + options: Mapping[str, Mapping[str, Any]] = attr.ib( + default=None, converter=default_if_none(factory=dict) \ + # E: Unsupported converter, only named functions and types are currently supported + ) +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/fixtures/dict.pyi b/test-data/unit/fixtures/dict.pyi index f8a5e3481d13..9e7cb6f8c70d 100644 --- a/test-data/unit/fixtures/dict.pyi +++ b/test-data/unit/fixtures/dict.pyi @@ -34,7 +34,7 @@ class dict(Mapping[KT, VT]): class int: # for convenience def __add__(self, x: Union[int, complex]) -> int: pass def __sub__(self, x: Union[int, complex]) -> int: pass - def __neg__(self): pass + def __neg__(self) -> int: pass real: int imag: int From 7fbf4deb845d6041700d1addc6eaa2097560f0f3 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 13 May 2022 14:18:31 -0700 Subject: [PATCH 62/79] Sync typeshed (#12766) Source commit: https://github.com/python/typeshed/commit/a27f15ef0eb12b961d0fb58f8615a3df901a9176 Co-authored-by: hauntsaninja <> --- mypy/typeshed/stdlib/_ast.pyi | 2 +- mypy/typeshed/stdlib/_csv.pyi | 62 +- mypy/typeshed/stdlib/_decimal.pyi | 18 +- mypy/typeshed/stdlib/_socket.pyi | 4 + mypy/typeshed/stdlib/_typeshed/__init__.pyi | 25 +- mypy/typeshed/stdlib/asyncio/transports.pyi | 6 +- mypy/typeshed/stdlib/builtins.pyi | 76 ++- mypy/typeshed/stdlib/contextlib.pyi | 4 +- mypy/typeshed/stdlib/csv.pyi | 49 +- mypy/typeshed/stdlib/dataclasses.pyi | 22 +- mypy/typeshed/stdlib/datetime.pyi | 7 +- mypy/typeshed/stdlib/enum.pyi | 20 +- mypy/typeshed/stdlib/http/__init__.pyi | 22 +- mypy/typeshed/stdlib/inspect.pyi | 73 ++- mypy/typeshed/stdlib/logging/__init__.pyi | 2 +- .../stdlib/multiprocessing/managers.pyi | 18 +- mypy/typeshed/stdlib/os/__init__.pyi | 4 + mypy/typeshed/stdlib/posix.pyi | 3 + mypy/typeshed/stdlib/re.pyi | 70 ++- mypy/typeshed/stdlib/socket.pyi | 23 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 2 + mypy/typeshed/stdlib/statistics.pyi | 8 +- mypy/typeshed/stdlib/subprocess.pyi | 559 +++++++++++++++++- mypy/typeshed/stdlib/sys.pyi | 8 +- mypy/typeshed/stdlib/tkinter/__init__.pyi | 178 +++++- mypy/typeshed/stdlib/types.pyi | 4 + mypy/typeshed/stdlib/typing.pyi | 65 +- mypy/typeshed/stdlib/typing_extensions.pyi | 20 +- mypy/typeshed/stdlib/urllib/error.pyi | 2 + mypy/typeshed/stdlib/warnings.pyi | 49 +- .../typeshed/stdlib/xml/etree/ElementTree.pyi | 2 +- mypy/typeshed/stdlib/zipfile.pyi | 2 + .../stubs/mypy-extensions/METADATA.toml | 1 - .../stubs/mypy-extensions/mypy_extensions.pyi | 17 +- test-data/unit/cmdline.test | 4 +- test-data/unit/pythoneval.test | 14 +- 36 files changed, 1263 insertions(+), 182 deletions(-) diff --git a/mypy/typeshed/stdlib/_ast.pyi b/mypy/typeshed/stdlib/_ast.pyi index cb13c7452081..1305b0c94d9b 100644 --- a/mypy/typeshed/stdlib/_ast.pyi +++ b/mypy/typeshed/stdlib/_ast.pyi @@ -319,7 +319,7 @@ class FormattedValue(expr): if sys.version_info >= (3, 10): __match_args__ = ("value", "conversion", "format_spec") value: expr - conversion: int | None + conversion: int format_spec: expr | None class JoinedStr(expr): diff --git a/mypy/typeshed/stdlib/_csv.pyi b/mypy/typeshed/stdlib/_csv.pyi index ae9031df6e81..7d15365d3b02 100644 --- a/mypy/typeshed/stdlib/_csv.pyi +++ b/mypy/typeshed/stdlib/_csv.pyi @@ -1,5 +1,6 @@ +from _typeshed import SupportsWrite from collections.abc import Iterable, Iterator -from typing import Any, Protocol, Union +from typing import Any, Union from typing_extensions import Literal, TypeAlias __version__: str @@ -9,6 +10,10 @@ QUOTE_MINIMAL: Literal[0] QUOTE_NONE: Literal[3] QUOTE_NONNUMERIC: Literal[2] +# Ideally this would be `QUOTE_ALL | QUOTE_MINIMAL | QUOTE_NONE | QUOTE_NONNUMERIC` +# However, using literals in situations like these can cause false-positives (see #7258) +_QuotingType: TypeAlias = int + class Error(Exception): ... class Dialect: @@ -18,28 +23,63 @@ class Dialect: doublequote: bool skipinitialspace: bool lineterminator: str - quoting: int - strict: int + quoting: _QuotingType + strict: bool def __init__(self) -> None: ... _DialectLike: TypeAlias = Union[str, Dialect, type[Dialect]] class _reader(Iterator[list[str]]): - dialect: Dialect + @property + def dialect(self) -> Dialect: ... line_num: int def __next__(self) -> list[str]: ... class _writer: - dialect: Dialect + @property + def dialect(self) -> Dialect: ... def writerow(self, row: Iterable[Any]) -> Any: ... def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ... -class _Writer(Protocol): - def write(self, __s: str) -> object: ... - -def writer(csvfile: _Writer, dialect: _DialectLike = ..., **fmtparams: Any) -> _writer: ... -def reader(csvfile: Iterable[str], dialect: _DialectLike = ..., **fmtparams: Any) -> _reader: ... -def register_dialect(name: str, dialect: Any = ..., **fmtparams: Any) -> None: ... +def writer( + csvfile: SupportsWrite[str], + dialect: _DialectLike = ..., + *, + delimiter: str = ..., + quotechar: str | None = ..., + escapechar: str | None = ..., + doublequote: bool = ..., + skipinitialspace: bool = ..., + lineterminator: str = ..., + quoting: _QuotingType = ..., + strict: bool = ..., +) -> _writer: ... +def reader( + csvfile: Iterable[str], + dialect: _DialectLike = ..., + *, + delimiter: str = ..., + quotechar: str | None = ..., + escapechar: str | None = ..., + doublequote: bool = ..., + skipinitialspace: bool = ..., + lineterminator: str = ..., + quoting: _QuotingType = ..., + strict: bool = ..., +) -> _reader: ... +def register_dialect( + name: str, + dialect: Any = ..., + *, + delimiter: str = ..., + quotechar: str | None = ..., + escapechar: str | None = ..., + doublequote: bool = ..., + skipinitialspace: bool = ..., + lineterminator: str = ..., + quoting: _QuotingType = ..., + strict: bool = ..., +) -> None: ... def unregister_dialect(name: str) -> None: ... def get_dialect(name: str) -> Dialect: ... def list_dialects() -> list[str]: ... diff --git a/mypy/typeshed/stdlib/_decimal.pyi b/mypy/typeshed/stdlib/_decimal.pyi index fdeca8f7c42f..a259058ee163 100644 --- a/mypy/typeshed/stdlib/_decimal.pyi +++ b/mypy/typeshed/stdlib/_decimal.pyi @@ -52,7 +52,23 @@ class FloatOperation(DecimalException, TypeError): ... def setcontext(__context: Context) -> None: ... def getcontext() -> Context: ... -def localcontext(ctx: Context | None = ...) -> _ContextManager: ... + +if sys.version_info >= (3, 11): + def localcontext( + ctx: Context | None = ..., + *, + prec: int | None = ..., + rounding: str | None = ..., + Emin: int | None = ..., + Emax: int | None = ..., + capitals: int | None = ..., + clamp: int | None = ..., + traps: dict[_TrapType, bool] | None = ..., + flags: dict[_TrapType, bool] | None = ..., + ) -> _ContextManager: ... + +else: + def localcontext(ctx: Context | None = ...) -> _ContextManager: ... class Decimal: def __new__(cls: type[Self], value: _DecimalNew = ..., context: Context | None = ...) -> Self: ... diff --git a/mypy/typeshed/stdlib/_socket.pyi b/mypy/typeshed/stdlib/_socket.pyi index 2366412050cd..e49cdfbb983a 100644 --- a/mypy/typeshed/stdlib/_socket.pyi +++ b/mypy/typeshed/stdlib/_socket.pyi @@ -256,6 +256,8 @@ SO_SNDLOWAT: int SO_SNDTIMEO: int SO_TYPE: int SO_USELOOPBACK: int +if sys.platform == "linux" and sys.version_info >= (3, 11): + SO_INCOMING_CPU: int TCP_CORK: int TCP_DEFER_ACCEPT: int TCP_FASTOPEN: int @@ -271,6 +273,8 @@ TCP_SYNCNT: int TCP_WINDOW_CLAMP: int if sys.version_info >= (3, 7): TCP_NOTSENT_LOWAT: int +if sys.version_info >= (3, 11) and sys.platform == "darwin": + TCP_CONNECTION_INFO: int # Specifically-documented constants diff --git a/mypy/typeshed/stdlib/_typeshed/__init__.pyi b/mypy/typeshed/stdlib/_typeshed/__init__.pyi index a80ea0702f77..d5e0c691e8c0 100644 --- a/mypy/typeshed/stdlib/_typeshed/__init__.pyi +++ b/mypy/typeshed/stdlib/_typeshed/__init__.pyi @@ -5,6 +5,7 @@ import array import ctypes import mmap +import pickle import sys from collections.abc import Awaitable, Container, Iterable, Set as AbstractSet from os import PathLike @@ -63,12 +64,27 @@ class SupportsAllComparisons(SupportsDunderLT, SupportsDunderGT, SupportsDunderL SupportsRichComparison: TypeAlias = SupportsDunderLT | SupportsDunderGT SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison) # noqa: Y001 +# Dunder protocols + +class SupportsAdd(Protocol): + def __add__(self, __x: Any) -> Any: ... + class SupportsDivMod(Protocol[_T_contra, _T_co]): def __divmod__(self, __other: _T_contra) -> _T_co: ... class SupportsRDivMod(Protocol[_T_contra, _T_co]): def __rdivmod__(self, __other: _T_contra) -> _T_co: ... +# This protocol is generic over the iterator type, while Iterable is +# generic over the type that is iterated over. +class SupportsIter(Protocol[_T_co]): + def __iter__(self) -> _T_co: ... + +# This protocol is generic over the iterator type, while AsyncIterable is +# generic over the type that is iterated over. +class SupportsAiter(Protocol[_T_co]): + def __aiter__(self) -> _T_co: ... + class SupportsLenAndGetItem(Protocol[_T_co]): def __len__(self) -> int: ... def __getitem__(self, __k: int) -> _T_co: ... @@ -194,8 +210,13 @@ class SupportsWrite(Protocol[_T_contra]): ReadOnlyBuffer: TypeAlias = bytes # stable # Anything that implements the read-write buffer interface. # The buffer interface is defined purely on the C level, so we cannot define a normal Protocol -# for it. Instead we have to list the most common stdlib buffer classes in a Union. -WriteableBuffer: TypeAlias = bytearray | memoryview | array.array[Any] | mmap.mmap | ctypes._CData # stable +# for it (until PEP 688 is implemented). Instead we have to list the most common stdlib buffer classes in a Union. +if sys.version_info >= (3, 8): + WriteableBuffer: TypeAlias = ( + bytearray | memoryview | array.array[Any] | mmap.mmap | ctypes._CData | pickle.PickleBuffer + ) # stable +else: + WriteableBuffer: TypeAlias = bytearray | memoryview | array.array[Any] | mmap.mmap | ctypes._CData # stable # Same as _WriteableBuffer, but also includes read-only buffer types (like bytes). ReadableBuffer: TypeAlias = ReadOnlyBuffer | WriteableBuffer # stable diff --git a/mypy/typeshed/stdlib/asyncio/transports.pyi b/mypy/typeshed/stdlib/asyncio/transports.pyi index a8cd753c2af8..7e17beb9f630 100644 --- a/mypy/typeshed/stdlib/asyncio/transports.pyi +++ b/mypy/typeshed/stdlib/asyncio/transports.pyi @@ -28,9 +28,7 @@ class ReadTransport(BaseTransport): class WriteTransport(BaseTransport): def set_write_buffer_limits(self, high: int | None = ..., low: int | None = ...) -> None: ... def get_write_buffer_size(self) -> int: ... - if sys.version_info >= (3, 9): - def get_write_buffer_limits(self) -> tuple[int, int]: ... - + def get_write_buffer_limits(self) -> tuple[int, int]: ... def write(self, data: Any) -> None: ... def writelines(self, list_of_data: list[Any]) -> None: ... def write_eof(self) -> None: ... @@ -53,4 +51,6 @@ class SubprocessTransport(BaseTransport): class _FlowControlMixin(Transport): def __init__(self, extra: Mapping[Any, Any] | None = ..., loop: AbstractEventLoop | None = ...) -> None: ... + def set_write_buffer_limits(self, high: int | None = ..., low: int | None = ...) -> None: ... + def get_write_buffer_size(self) -> int: ... def get_write_buffer_limits(self) -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/builtins.pyi b/mypy/typeshed/stdlib/builtins.pyi index bf1e6cde2c08..d3d34c72fcfc 100644 --- a/mypy/typeshed/stdlib/builtins.pyi +++ b/mypy/typeshed/stdlib/builtins.pyi @@ -11,8 +11,11 @@ from _typeshed import ( ReadableBuffer, Self, StrOrBytesPath, + SupportsAdd, + SupportsAiter, SupportsAnext, SupportsDivMod, + SupportsIter, SupportsKeysAndGetItem, SupportsLenAndGetItem, SupportsNext, @@ -71,12 +74,6 @@ _SupportsAnextT = TypeVar("_SupportsAnextT", bound=SupportsAnext[Any], covariant _AwaitableT = TypeVar("_AwaitableT", bound=Awaitable[Any]) _AwaitableT_co = TypeVar("_AwaitableT_co", bound=Awaitable[Any], covariant=True) -class _SupportsIter(Protocol[_T_co]): - def __iter__(self) -> _T_co: ... - -class _SupportsAiter(Protocol[_T_co]): - def __aiter__(self) -> _T_co: ... - class object: __doc__: str | None __dict__: dict[str, Any] @@ -122,7 +119,8 @@ class staticmethod(Generic[_R_co]): if sys.version_info >= (3, 10): __name__: str __qualname__: str - __wrapped__: Callable[..., _R_co] + @property + def __wrapped__(self) -> Callable[..., _R_co]: ... def __call__(self, *args: Any, **kwargs: Any) -> _R_co: ... class classmethod(Generic[_R_co]): @@ -135,7 +133,8 @@ class classmethod(Generic[_R_co]): if sys.version_info >= (3, 10): __name__: str __qualname__: str - __wrapped__: Callable[..., _R_co] + @property + def __wrapped__(self) -> Callable[..., _R_co]: ... class type: @property @@ -251,11 +250,9 @@ class int: def __rmod__(self, __x: int) -> int: ... def __rdivmod__(self, __x: int) -> tuple[int, int]: ... @overload - def __pow__(self, __x: int, __modulo: Literal[0]) -> NoReturn: ... - @overload - def __pow__(self, __x: int, __modulo: int) -> int: ... + def __pow__(self, __x: Literal[0]) -> Literal[1]: ... @overload - def __pow__(self, __x: Literal[0], __modulo: None = ...) -> Literal[1]: ... + def __pow__(self, __x: Literal[0], __modulo: None) -> Literal[1]: ... @overload def __pow__(self, __x: _PositiveInteger, __modulo: None = ...) -> int: ... @overload @@ -264,6 +261,10 @@ class int: # return type must be Any as `int | float` causes too many false-positive errors @overload def __pow__(self, __x: int, __modulo: None = ...) -> Any: ... + @overload + def __pow__(self, __x: int, __modulo: Literal[0]) -> NoReturn: ... + @overload + def __pow__(self, __x: int, __modulo: int) -> int: ... def __rpow__(self, __x: int, __mod: int | None = ...) -> Any: ... def __and__(self, __n: int) -> int: ... def __or__(self, __n: int) -> int: ... @@ -328,7 +329,12 @@ class float: def __rtruediv__(self, __x: float) -> float: ... def __rmod__(self, __x: float) -> float: ... def __rdivmod__(self, __x: float) -> tuple[float, float]: ... - # Returns complex if the argument is negative. + @overload + def __rpow__(self, __x: _PositiveInteger, __modulo: None = ...) -> float: ... + @overload + def __rpow__(self, __x: _NegativeInteger, __mod: None = ...) -> complex: ... + # Returning `complex` for the general case gives too many false-positive errors. + @overload def __rpow__(self, __x: float, __mod: None = ...) -> Any: ... def __getnewargs__(self) -> tuple[float]: ... def __trunc__(self) -> int: ... @@ -1092,7 +1098,7 @@ class _PathLike(Protocol[_AnyStr_co]): def __fspath__(self) -> _AnyStr_co: ... if sys.version_info >= (3, 10): - def aiter(__async_iterable: _SupportsAiter[_SupportsAnextT]) -> _SupportsAnextT: ... + def aiter(__async_iterable: SupportsAiter[_SupportsAnextT]) -> _SupportsAnextT: ... class _SupportsSynchronousAnext(Protocol[_AwaitableT_co]): def __anext__(self) -> _AwaitableT_co: ... @@ -1144,9 +1150,22 @@ def eval( ) -> Any: ... # Comment above regarding `eval` applies to `exec` as well -def exec( - __source: str | ReadableBuffer | CodeType, __globals: dict[str, Any] | None = ..., __locals: Mapping[str, object] | None = ... -) -> None: ... +if sys.version_info >= (3, 11): + def exec( + __source: str | ReadableBuffer | CodeType, + __globals: dict[str, Any] | None = ..., + __locals: Mapping[str, object] | None = ..., + *, + closure: tuple[_Cell, ...] | None = ..., + ) -> None: ... + +else: + def exec( + __source: str | ReadableBuffer | CodeType, + __globals: dict[str, Any] | None = ..., + __locals: Mapping[str, object] | None = ..., + ) -> None: ... + def exit(code: object = ...) -> NoReturn: ... class filter(Iterator[_T], Generic[_T]): @@ -1183,8 +1202,14 @@ def help(request: object = ...) -> None: ... def hex(__number: int | SupportsIndex) -> str: ... def id(__obj: object) -> int: ... def input(__prompt: object = ...) -> str: ... + +class _GetItemIterable(Protocol[_T_co]): + def __getitem__(self, __i: int) -> _T_co: ... + +@overload +def iter(__iterable: SupportsIter[_SupportsNextT]) -> _SupportsNextT: ... @overload -def iter(__iterable: _SupportsIter[_SupportsNextT]) -> _SupportsNextT: ... +def iter(__iterable: _GetItemIterable[_T]) -> Iterator[_T]: ... @overload def iter(__function: Callable[[], _T | None], __sentinel: None) -> Iterator[_T]: ... @overload @@ -1423,6 +1448,10 @@ if sys.version_info >= (3, 8): @overload def pow(base: int, exp: int, mod: None = ...) -> Any: ... @overload + def pow(base: _PositiveInteger, exp: float, mod: None = ...) -> float: ... + @overload + def pow(base: _NegativeInteger, exp: float, mod: None = ...) -> complex: ... + @overload def pow(base: float, exp: int, mod: None = ...) -> float: ... # float base & float exp could return float or complex # return type must be Any (same as complex base, complex exp), @@ -1456,6 +1485,10 @@ else: @overload def pow(__base: int, __exp: int, __mod: None = ...) -> Any: ... @overload + def pow(__base: _PositiveInteger, __exp: float, __mod: None = ...) -> float: ... + @overload + def pow(__base: _NegativeInteger, __exp: float, __mod: None = ...) -> complex: ... + @overload def pow(__base: float, __exp: int, __mod: None = ...) -> float: ... @overload def pow(__base: float, __exp: complex | _SupportsSomeKindOfPow, __mod: None = ...) -> Any: ... @@ -1501,11 +1534,8 @@ def sorted( @overload def sorted(__iterable: Iterable[_T], *, key: Callable[[_T], SupportsRichComparison], reverse: bool = ...) -> list[_T]: ... -class _SupportsSum(Protocol): - def __add__(self, __x: Any) -> Any: ... - -_SumT = TypeVar("_SumT", bound=_SupportsSum) -_SumS = TypeVar("_SumS", bound=_SupportsSum) +_SumT = TypeVar("_SumT", bound=SupportsAdd) +_SumS = TypeVar("_SumS", bound=SupportsAdd) @overload def sum(__iterable: Iterable[_SumT]) -> _SumT | Literal[0]: ... diff --git a/mypy/typeshed/stdlib/contextlib.pyi b/mypy/typeshed/stdlib/contextlib.pyi index c93eeac3b44f..1b6ee4298174 100644 --- a/mypy/typeshed/stdlib/contextlib.pyi +++ b/mypy/typeshed/stdlib/contextlib.pyi @@ -78,7 +78,7 @@ _F = TypeVar("_F", bound=Callable[..., Any]) _P = ParamSpec("_P") _ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None] -_CM_EF = TypeVar("_CM_EF", AbstractContextManager[Any], _ExitFunc) +_CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any] | _ExitFunc) class ContextDecorator: def __call__(self, func: _F) -> _F: ... @@ -177,7 +177,7 @@ class ExitStack(AbstractContextManager[ExitStack]): if sys.version_info >= (3, 7): _ExitCoroFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], Awaitable[bool]] - _ACM_EF = TypeVar("_ACM_EF", AbstractAsyncContextManager[Any], _ExitCoroFunc) + _ACM_EF = TypeVar("_ACM_EF", bound=AbstractAsyncContextManager[Any] | _ExitCoroFunc) class AsyncExitStack(AbstractAsyncContextManager[AsyncExitStack]): def __init__(self) -> None: ... diff --git a/mypy/typeshed/stdlib/csv.pyi b/mypy/typeshed/stdlib/csv.pyi index dcb3f19bebe1..de69c71ad941 100644 --- a/mypy/typeshed/stdlib/csv.pyi +++ b/mypy/typeshed/stdlib/csv.pyi @@ -1,4 +1,6 @@ import sys + +# actually csv.Dialect is a different class to _csv.Dialect at runtime, but for typing purposes, they're identical from _csv import ( QUOTE_ALL as QUOTE_ALL, QUOTE_MINIMAL as QUOTE_MINIMAL, @@ -8,6 +10,7 @@ from _csv import ( Error as Error, __version__ as __version__, _DialectLike, + _QuotingType, _reader, _writer, field_size_limit as field_size_limit, @@ -18,9 +21,10 @@ from _csv import ( unregister_dialect as unregister_dialect, writer as writer, ) -from _typeshed import Self +from _typeshed import Self, SupportsWrite from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence from typing import Any, Generic, TypeVar, overload +from typing_extensions import Literal if sys.version_info >= (3, 8): from builtins import dict as _DictReadMapping @@ -59,7 +63,7 @@ class excel(Dialect): doublequote: bool skipinitialspace: bool lineterminator: str - quoting: int + quoting: _QuotingType class excel_tab(excel): delimiter: str @@ -70,7 +74,7 @@ class unix_dialect(Dialect): doublequote: bool skipinitialspace: bool lineterminator: str - quoting: int + quoting: _QuotingType class DictReader(Generic[_T], Iterator[_DictReadMapping[_T, str]]): fieldnames: Sequence[_T] | None @@ -87,8 +91,15 @@ class DictReader(Generic[_T], Iterator[_DictReadMapping[_T, str]]): restkey: str | None = ..., restval: str | None = ..., dialect: _DialectLike = ..., - *args: Any, - **kwds: Any, + *, + delimiter: str = ..., + quotechar: str | None = ..., + escapechar: str | None = ..., + doublequote: bool = ..., + skipinitialspace: bool = ..., + lineterminator: str = ..., + quoting: _QuotingType = ..., + strict: bool = ..., ) -> None: ... @overload def __init__( @@ -98,8 +109,15 @@ class DictReader(Generic[_T], Iterator[_DictReadMapping[_T, str]]): restkey: str | None = ..., restval: str | None = ..., dialect: _DialectLike = ..., - *args: Any, - **kwds: Any, + *, + delimiter: str = ..., + quotechar: str | None = ..., + escapechar: str | None = ..., + doublequote: bool = ..., + skipinitialspace: bool = ..., + lineterminator: str = ..., + quoting: _QuotingType = ..., + strict: bool = ..., ) -> None: ... def __iter__(self: Self) -> Self: ... def __next__(self) -> _DictReadMapping[_T, str]: ... @@ -107,17 +125,24 @@ class DictReader(Generic[_T], Iterator[_DictReadMapping[_T, str]]): class DictWriter(Generic[_T]): fieldnames: Collection[_T] restval: Any | None - extrasaction: str + extrasaction: Literal["raise", "ignore"] writer: _writer def __init__( self, - f: Any, + f: SupportsWrite[str], fieldnames: Collection[_T], restval: Any | None = ..., - extrasaction: str = ..., + extrasaction: Literal["raise", "ignore"] = ..., dialect: _DialectLike = ..., - *args: Any, - **kwds: Any, + *, + delimiter: str = ..., + quotechar: str | None = ..., + escapechar: str | None = ..., + doublequote: bool = ..., + skipinitialspace: bool = ..., + lineterminator: str = ..., + quoting: _QuotingType = ..., + strict: bool = ..., ) -> None: ... if sys.version_info >= (3, 8): def writeheader(self) -> Any: ... diff --git a/mypy/typeshed/stdlib/dataclasses.pyi b/mypy/typeshed/stdlib/dataclasses.pyi index f58c6f9f1460..1cbf998dd303 100644 --- a/mypy/typeshed/stdlib/dataclasses.pyi +++ b/mypy/typeshed/stdlib/dataclasses.pyi @@ -79,7 +79,23 @@ else: @overload def dataclass(_cls: None) -> Callable[[type[_T]], type[_T]]: ... -if sys.version_info >= (3, 10): +if sys.version_info >= (3, 11): + @overload + def dataclass( + *, + init: bool = ..., + repr: bool = ..., + eq: bool = ..., + order: bool = ..., + unsafe_hash: bool = ..., + frozen: bool = ..., + match_args: bool = ..., + kw_only: bool = ..., + slots: bool = ..., + weakref_slot: bool = ..., + ) -> Callable[[type[_T]], type[_T]]: ... + +elif sys.version_info >= (3, 10): @overload def dataclass( *, @@ -227,7 +243,7 @@ class InitVar(Generic[_T]): if sys.version_info >= (3, 10): def make_dataclass( cls_name: str, - fields: Iterable[str | tuple[str, type] | tuple[str, type, Field[Any]]], + fields: Iterable[str | tuple[str, type] | tuple[str, type, Any]], *, bases: tuple[type, ...] = ..., namespace: dict[str, Any] | None = ..., @@ -245,7 +261,7 @@ if sys.version_info >= (3, 10): else: def make_dataclass( cls_name: str, - fields: Iterable[str | tuple[str, type] | tuple[str, type, Field[Any]]], + fields: Iterable[str | tuple[str, type] | tuple[str, type, Any]], *, bases: tuple[type, ...] = ..., namespace: dict[str, Any] | None = ..., diff --git a/mypy/typeshed/stdlib/datetime.pyi b/mypy/typeshed/stdlib/datetime.pyi index 113c679743fd..e2a359d0a536 100644 --- a/mypy/typeshed/stdlib/datetime.pyi +++ b/mypy/typeshed/stdlib/datetime.pyi @@ -4,7 +4,9 @@ from time import struct_time from typing import ClassVar, NamedTuple, NoReturn, SupportsAbs, TypeVar, overload from typing_extensions import Literal, TypeAlias, final -if sys.version_info >= (3, 9): +if sys.version_info >= (3, 11): + __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC") +elif sys.version_info >= (3, 9): __all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR") _D = TypeVar("_D", bound=date) @@ -29,6 +31,9 @@ class timezone(tzinfo): def __init__(self, offset: timedelta, name: str = ...) -> None: ... def __hash__(self) -> int: ... +if sys.version_info >= (3, 11): + UTC: timezone + if sys.version_info >= (3, 9): class _IsoCalendarDate(NamedTuple): year: int diff --git a/mypy/typeshed/stdlib/enum.pyi b/mypy/typeshed/stdlib/enum.pyi index a7c84c5b1c0d..9ebeba37ab71 100644 --- a/mypy/typeshed/stdlib/enum.pyi +++ b/mypy/typeshed/stdlib/enum.pyi @@ -4,7 +4,7 @@ from _typeshed import Self from abc import ABCMeta from builtins import property as _builtins_property from collections.abc import Iterable, Iterator, Mapping -from typing import Any, TypeVar, overload +from typing import Any, Generic, TypeVar, overload from typing_extensions import Literal, TypeAlias if sys.version_info >= (3, 11): @@ -21,6 +21,8 @@ if sys.version_info >= (3, 11): "unique", "property", "verify", + "member", + "nonmember", "FlagBoundary", "STRICT", "CONFORM", @@ -54,6 +56,15 @@ _EnumerationT = TypeVar("_EnumerationT", bound=type[Enum]) # _EnumNames: TypeAlias = str | Iterable[str] | Iterable[Iterable[str | Any]] | Mapping[str, Any] +if sys.version_info >= (3, 11): + class nonmember(Generic[_EnumMemberT]): + value: _EnumMemberT + def __init__(self, value: _EnumMemberT) -> None: ... + + class member(Generic[_EnumMemberT]): + value: _EnumMemberT + def __init__(self, value: _EnumMemberT) -> None: ... + class _EnumDict(dict[str, Any]): def __init__(self) -> None: ... def __setitem__(self, key: str, value: Any) -> None: ... @@ -155,7 +166,12 @@ class Enum(metaclass=EnumMeta): def _missing_(cls, value: object) -> Any: ... @staticmethod def _generate_next_value_(name: str, start: int, count: int, last_values: list[Any]) -> Any: ... - def __new__(cls: type[Self], value: Any) -> Self: ... + # It's not true that `__new__` will accept any argument type, + # so ideally we'd use `Any` to indicate that the argument type is inexpressible. + # However, using `Any` causes too many false-positives for those using mypy's `--disallow-any-expr` + # (see #7752, #2539, mypy/#5788), + # and in practice using `object` here has the same effect as using `Any`. + def __new__(cls: type[Self], value: object) -> Self: ... def __dir__(self) -> list[str]: ... def __format__(self, format_spec: str) -> str: ... def __hash__(self) -> Any: ... diff --git a/mypy/typeshed/stdlib/http/__init__.pyi b/mypy/typeshed/stdlib/http/__init__.pyi index 822cc0932939..10c1d5926e84 100644 --- a/mypy/typeshed/stdlib/http/__init__.pyi +++ b/mypy/typeshed/stdlib/http/__init__.pyi @@ -2,7 +2,13 @@ import sys from enum import IntEnum from typing_extensions import Literal -__all__ = ["HTTPStatus"] +if sys.version_info >= (3, 11): + from enum import StrEnum + +if sys.version_info >= (3, 11): + __all__ = ["HTTPStatus", "HTTPMethod"] +else: + __all__ = ["HTTPStatus"] class HTTPStatus(IntEnum): @property @@ -74,3 +80,17 @@ class HTTPStatus(IntEnum): EARLY_HINTS: Literal[103] IM_A_TEAPOT: Literal[418] TOO_EARLY: Literal[425] + +if sys.version_info >= (3, 11): + class HTTPMethod(StrEnum): + @property + def description(self) -> str: ... + CONNECT: str + DELETE: str + GET: str + HEAD: str + OPTIONS: str + PATCH: str + POST: str + PUT: str + TRACE: str diff --git a/mypy/typeshed/stdlib/inspect.pyi b/mypy/typeshed/stdlib/inspect.pyi index 7ca9c9bb3fc4..38d928f43c9a 100644 --- a/mypy/typeshed/stdlib/inspect.pyi +++ b/mypy/typeshed/stdlib/inspect.pyi @@ -1,3 +1,4 @@ +import dis import enum import sys import types @@ -459,20 +460,64 @@ def unwrap(func: Callable[..., Any], *, stop: Callable[[Any], Any] | None = ...) # The interpreter stack # -class Traceback(NamedTuple): - filename: str - lineno: int - function: str - code_context: list[str] | None - index: int | None # type: ignore[assignment] - -class FrameInfo(NamedTuple): - frame: FrameType - filename: str - lineno: int - function: str - code_context: list[str] | None - index: int | None # type: ignore[assignment] +if sys.version_info >= (3, 11): + class _Traceback(NamedTuple): + filename: str + lineno: int + function: str + code_context: list[str] | None + index: int | None # type: ignore[assignment] + + class Traceback(_Traceback): + positions: dis.Positions | None + def __new__( + cls: type[Self], + filename: str, + lineno: int, + function: str, + code_context: list[str] | None, + index: int | None, + *, + positions: dis.Positions | None = ..., + ) -> Self: ... + + class _FrameInfo(NamedTuple): + frame: FrameType + filename: str + lineno: int + function: str + code_context: list[str] | None + index: int | None # type: ignore[assignment] + + class FrameInfo(_FrameInfo): + positions: dis.Positions | None + def __new__( + cls: type[Self], + frame: FrameType, + filename: str, + lineno: int, + function: str, + code_context: list[str] | None, + index: int | None, + *, + positions: dis.Positions | None = ..., + ) -> Self: ... + +else: + class Traceback(NamedTuple): + filename: str + lineno: int + function: str + code_context: list[str] | None + index: int | None # type: ignore[assignment] + + class FrameInfo(NamedTuple): + frame: FrameType + filename: str + lineno: int + function: str + code_context: list[str] | None + index: int | None # type: ignore[assignment] def getframeinfo(frame: FrameType | TracebackType, context: int = ...) -> Traceback: ... def getouterframes(frame: Any, context: int = ...) -> list[FrameInfo]: ... diff --git a/mypy/typeshed/stdlib/logging/__init__.pyi b/mypy/typeshed/stdlib/logging/__init__.pyi index edb15061a588..6ad4cd4f94e7 100644 --- a/mypy/typeshed/stdlib/logging/__init__.pyi +++ b/mypy/typeshed/stdlib/logging/__init__.pyi @@ -408,7 +408,7 @@ class LogRecord: ) -> None: ... def getMessage(self) -> str: ... -_L = TypeVar("_L", Logger, LoggerAdapter[Logger], LoggerAdapter[Any]) +_L = TypeVar("_L", bound=Logger | LoggerAdapter[Any]) class LoggerAdapter(Generic[_L]): logger: _L diff --git a/mypy/typeshed/stdlib/multiprocessing/managers.pyi b/mypy/typeshed/stdlib/multiprocessing/managers.pyi index 234660cc4f80..b8d5ddda0f35 100644 --- a/mypy/typeshed/stdlib/multiprocessing/managers.pyi +++ b/mypy/typeshed/stdlib/multiprocessing/managers.pyi @@ -76,9 +76,21 @@ class Server: def accept_connection(self, c: Connection, name: str) -> None: ... class BaseManager: - def __init__( - self, address: Any | None = ..., authkey: bytes | None = ..., serializer: str = ..., ctx: BaseContext | None = ... - ) -> None: ... + if sys.version_info >= (3, 11): + def __init__( + self, + address: Any | None = ..., + authkey: bytes | None = ..., + serializer: str = ..., + ctx: BaseContext | None = ..., + *, + shutdown_timeout: float = ..., + ) -> None: ... + else: + def __init__( + self, address: Any | None = ..., authkey: bytes | None = ..., serializer: str = ..., ctx: BaseContext | None = ... + ) -> None: ... + def get_server(self) -> Server: ... def connect(self) -> None: ... def start(self, initializer: Callable[..., Any] | None = ..., initargs: Iterable[Any] = ...) -> None: ... diff --git a/mypy/typeshed/stdlib/os/__init__.pyi b/mypy/typeshed/stdlib/os/__init__.pyi index 76c114591d32..2310de701d54 100644 --- a/mypy/typeshed/stdlib/os/__init__.pyi +++ b/mypy/typeshed/stdlib/os/__init__.pyi @@ -605,6 +605,10 @@ def fstat(fd: int) -> stat_result: ... def ftruncate(__fd: int, __length: int) -> None: ... def fsync(fd: FileDescriptorLike) -> None: ... def isatty(__fd: int) -> bool: ... + +if sys.platform != "win32" and sys.version_info >= (3, 11): + def login_tty(__fd: int) -> None: ... + def lseek(__fd: int, __position: int, __how: int) -> int: ... def open(path: StrOrBytesPath, flags: int, mode: int = ..., *, dir_fd: int | None = ...) -> int: ... def pipe() -> tuple[int, int]: ... diff --git a/mypy/typeshed/stdlib/posix.pyi b/mypy/typeshed/stdlib/posix.pyi index 5dba5b36e3d2..e248db397ab8 100644 --- a/mypy/typeshed/stdlib/posix.pyi +++ b/mypy/typeshed/stdlib/posix.pyi @@ -269,6 +269,9 @@ if sys.platform != "win32": if sys.version_info >= (3, 10): from os import RWF_APPEND as RWF_APPEND + if sys.version_info >= (3, 11): + from os import login_tty as login_tty + if sys.version_info >= (3, 9): from os import CLD_KILLED as CLD_KILLED, CLD_STOPPED as CLD_STOPPED, waitstatus_to_exitcode as waitstatus_to_exitcode diff --git a/mypy/typeshed/stdlib/re.pyi b/mypy/typeshed/stdlib/re.pyi index ff2a55fb4e61..2f4f3a3a0ed4 100644 --- a/mypy/typeshed/stdlib/re.pyi +++ b/mypy/typeshed/stdlib/re.pyi @@ -1,6 +1,7 @@ import enum import sre_compile import sys +from _typeshed import ReadableBuffer from collections.abc import Callable, Iterator from sre_constants import error as error from typing import Any, AnyStr, overload @@ -155,70 +156,67 @@ if sys.version_info < (3, 7): # undocumented _pattern_type: type -# Type-wise these overloads are unnecessary, they could also be modeled using +# Type-wise the compile() overloads are unnecessary, they could also be modeled using # unions in the parameter types. However mypy has a bug regarding TypeVar # constraints (https://github.com/python/mypy/issues/11880), # which limits us here because AnyStr is a constrained TypeVar. +# pattern arguments do *not* accept arbitrary buffers such as bytearray, +# because the pattern must be hashable. @overload def compile(pattern: AnyStr, flags: _FlagsType = ...) -> Pattern[AnyStr]: ... @overload def compile(pattern: Pattern[AnyStr], flags: _FlagsType = ...) -> Pattern[AnyStr]: ... @overload -def search(pattern: AnyStr, string: AnyStr, flags: _FlagsType = ...) -> Match[AnyStr] | None: ... +def search(pattern: str | Pattern[str], string: str, flags: _FlagsType = ...) -> Match[str] | None: ... @overload -def search(pattern: Pattern[AnyStr], string: AnyStr, flags: _FlagsType = ...) -> Match[AnyStr] | None: ... +def search(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = ...) -> Match[bytes] | None: ... @overload -def match(pattern: AnyStr, string: AnyStr, flags: _FlagsType = ...) -> Match[AnyStr] | None: ... +def match(pattern: str | Pattern[str], string: str, flags: _FlagsType = ...) -> Match[str] | None: ... @overload -def match(pattern: Pattern[AnyStr], string: AnyStr, flags: _FlagsType = ...) -> Match[AnyStr] | None: ... +def match(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = ...) -> Match[bytes] | None: ... @overload -def fullmatch(pattern: AnyStr, string: AnyStr, flags: _FlagsType = ...) -> Match[AnyStr] | None: ... +def fullmatch(pattern: str | Pattern[str], string: str, flags: _FlagsType = ...) -> Match[str] | None: ... @overload -def fullmatch(pattern: Pattern[AnyStr], string: AnyStr, flags: _FlagsType = ...) -> Match[AnyStr] | None: ... +def fullmatch(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = ...) -> Match[bytes] | None: ... @overload -def split(pattern: AnyStr, string: AnyStr, maxsplit: int = ..., flags: _FlagsType = ...) -> list[AnyStr | Any]: ... +def split(pattern: str | Pattern[str], string: str, maxsplit: int = ..., flags: _FlagsType = ...) -> list[str | Any]: ... @overload -def split(pattern: Pattern[AnyStr], string: AnyStr, maxsplit: int = ..., flags: _FlagsType = ...) -> list[AnyStr | Any]: ... +def split( + pattern: bytes | Pattern[bytes], string: ReadableBuffer, maxsplit: int = ..., flags: _FlagsType = ... +) -> list[bytes | Any]: ... @overload -def findall(pattern: AnyStr, string: AnyStr, flags: _FlagsType = ...) -> list[Any]: ... +def findall(pattern: str | Pattern[str], string: str, flags: _FlagsType = ...) -> list[Any]: ... @overload -def findall(pattern: Pattern[AnyStr], string: AnyStr, flags: _FlagsType = ...) -> list[Any]: ... - -# Return an iterator yielding match objects over all non-overlapping matches -# for the RE pattern in string. The string is scanned left-to-right, and -# matches are returned in the order found. Empty matches are included in the -# result unless they touch the beginning of another match. -@overload -def finditer(pattern: AnyStr, string: AnyStr, flags: _FlagsType = ...) -> Iterator[Match[AnyStr]]: ... +def findall(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = ...) -> list[Any]: ... @overload -def finditer(pattern: Pattern[AnyStr], string: AnyStr, flags: _FlagsType = ...) -> Iterator[Match[AnyStr]]: ... +def finditer(pattern: str | Pattern[str], string: str, flags: _FlagsType = ...) -> Iterator[Match[str]]: ... @overload -def sub(pattern: AnyStr, repl: AnyStr, string: AnyStr, count: int = ..., flags: _FlagsType = ...) -> AnyStr: ... +def finditer(pattern: bytes | Pattern[bytes], string: ReadableBuffer, flags: _FlagsType = ...) -> Iterator[Match[bytes]]: ... @overload def sub( - pattern: AnyStr, repl: Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = ..., flags: _FlagsType = ... -) -> AnyStr: ... -@overload -def sub(pattern: Pattern[AnyStr], repl: AnyStr, string: AnyStr, count: int = ..., flags: _FlagsType = ...) -> AnyStr: ... + pattern: str | Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = ..., flags: _FlagsType = ... +) -> str: ... @overload def sub( - pattern: Pattern[AnyStr], repl: Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = ..., flags: _FlagsType = ... -) -> AnyStr: ... -@overload -def subn(pattern: AnyStr, repl: AnyStr, string: AnyStr, count: int = ..., flags: _FlagsType = ...) -> tuple[AnyStr, int]: ... -@overload -def subn( - pattern: AnyStr, repl: Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = ..., flags: _FlagsType = ... -) -> tuple[AnyStr, int]: ... + pattern: bytes | Pattern[bytes], + repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], + string: ReadableBuffer, + count: int = ..., + flags: _FlagsType = ..., +) -> bytes: ... @overload def subn( - pattern: Pattern[AnyStr], repl: AnyStr, string: AnyStr, count: int = ..., flags: _FlagsType = ... -) -> tuple[AnyStr, int]: ... + pattern: str | Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = ..., flags: _FlagsType = ... +) -> tuple[str, int]: ... @overload def subn( - pattern: Pattern[AnyStr], repl: Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = ..., flags: _FlagsType = ... -) -> tuple[AnyStr, int]: ... + pattern: bytes | Pattern[bytes], + repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], + string: ReadableBuffer, + count: int = ..., + flags: _FlagsType = ..., +) -> tuple[bytes, int]: ... def escape(pattern: AnyStr) -> AnyStr: ... def purge() -> None: ... def template(pattern: AnyStr | Pattern[AnyStr], flags: _FlagsType = ...) -> Pattern[AnyStr]: ... diff --git a/mypy/typeshed/stdlib/socket.pyi b/mypy/typeshed/stdlib/socket.pyi index 7801940f8564..4f8ec07ccc95 100644 --- a/mypy/typeshed/stdlib/socket.pyi +++ b/mypy/typeshed/stdlib/socket.pyi @@ -366,6 +366,8 @@ if sys.platform == "linux" and sys.version_info >= (3, 9): ) if sys.platform == "linux" and sys.version_info >= (3, 10): from _socket import IPPROTO_MPTCP as IPPROTO_MPTCP +if sys.platform == "linux" and sys.version_info >= (3, 11): + from _socket import SO_INCOMING_CPU as SO_INCOMING_CPU if sys.platform == "win32": from _socket import ( RCVALL_IPLEVEL as RCVALL_IPLEVEL, @@ -605,11 +607,22 @@ class SocketIO(RawIOBase): def mode(self) -> Literal["rb", "wb", "rwb"]: ... def getfqdn(name: str = ...) -> str: ... -def create_connection( - address: tuple[str | None, int], - timeout: float | None = ..., # noqa: F811 - source_address: tuple[bytearray | bytes | str, int] | None = ..., -) -> socket: ... + +if sys.version_info >= (3, 11): + def create_connection( + address: tuple[str | None, int], + timeout: float | None = ..., # noqa: F811 + source_address: tuple[bytearray | bytes | str, int] | None = ..., + *, + all_errors: bool = ..., + ) -> socket: ... + +else: + def create_connection( + address: tuple[str | None, int], + timeout: float | None = ..., # noqa: F811 + source_address: tuple[bytearray | bytes | str, int] | None = ..., + ) -> socket: ... if sys.version_info >= (3, 8): def has_dualstack_ipv6() -> bool: ... diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index 87e843c5fb26..a6ccc9977c1c 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -460,3 +460,5 @@ if sys.version_info >= (3, 11): def __len__(self) -> int: ... def __enter__(self: Self) -> Self: ... def __exit__(self, __typ: object, __val: object, __tb: object) -> Literal[False]: ... + def __getitem__(self, __item: SupportsIndex | slice) -> int: ... + def __setitem__(self, __item: SupportsIndex | slice, __value: int) -> None: ... diff --git a/mypy/typeshed/stdlib/statistics.pyi b/mypy/typeshed/stdlib/statistics.pyi index 540ccfcaaa8c..e6c3d8f35bc6 100644 --- a/mypy/typeshed/stdlib/statistics.pyi +++ b/mypy/typeshed/stdlib/statistics.pyi @@ -94,7 +94,13 @@ else: def median(data: Iterable[_NumberT]) -> _NumberT: ... def median_low(data: Iterable[SupportsRichComparisonT]) -> SupportsRichComparisonT: ... def median_high(data: Iterable[SupportsRichComparisonT]) -> SupportsRichComparisonT: ... -def median_grouped(data: Iterable[_NumberT], interval: _NumberT = ...) -> _NumberT: ... + +if sys.version_info >= (3, 11): + def median_grouped(data: Iterable[SupportsFloat], interval: SupportsFloat = ...) -> float: ... + +else: + def median_grouped(data: Iterable[_NumberT], interval: _NumberT = ...) -> _NumberT | float: ... + def mode(data: Iterable[_HashableT]) -> _HashableT: ... if sys.version_info >= (3, 8): diff --git a/mypy/typeshed/stdlib/subprocess.pyi b/mypy/typeshed/stdlib/subprocess.pyi index 98bbf7d33f90..83178e15d9e8 100644 --- a/mypy/typeshed/stdlib/subprocess.pyi +++ b/mypy/typeshed/stdlib/subprocess.pyi @@ -118,6 +118,12 @@ else: _T = TypeVar("_T") +# These two are private but documented +if sys.version_info >= (3, 11): + _USE_VFORK: bool +if sys.version_info >= (3, 8): + _USE_POSIX_SPAWN: bool + class CompletedProcess(Generic[_T]): # morally: _CMD args: Any @@ -792,13 +798,562 @@ class Popen(Generic[AnyStr]): stdout: IO[AnyStr] | None stderr: IO[AnyStr] | None pid: int - returncode: int + returncode: int | Any universal_newlines: bool # Technically it is wrong that Popen provides __new__ instead of __init__ # but this shouldn't come up hopefully? - if sys.version_info >= (3, 7): + if sys.version_info >= (3, 11): + # process_group is added in 3.11 + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str, + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + process_group: int | None = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str | None = ..., + errors: str, + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + process_group: int | None = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + *, + universal_newlines: Literal[True], + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + # where the *real* keyword only args start + text: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + process_group: int | None = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: Literal[True], + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + process_group: int | None = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: Literal[False] = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: Literal[None, False] = ..., + encoding: None = ..., + errors: None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + process_group: int | None = ..., + ) -> Popen[bytes]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + process_group: int | None = ..., + ) -> Popen[Any]: ... + elif sys.version_info >= (3, 10): + # pipesize is added in 3.10 + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str, + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str | None = ..., + errors: str, + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + *, + universal_newlines: Literal[True], + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + # where the *real* keyword only args start + text: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: Literal[True], + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: Literal[False] = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: Literal[None, False] = ..., + encoding: None = ..., + errors: None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + ) -> Popen[bytes]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + pipesize: int = ..., + ) -> Popen[Any]: ... + elif sys.version_info >= (3, 9): + # user, group, extra_groups, umask were added in 3.9 + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str, + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str | None = ..., + errors: str, + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + *, + universal_newlines: Literal[True], + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + # where the *real* keyword only args start + text: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: Literal[True], + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + ) -> Popen[str]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: Literal[False] = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: Literal[None, False] = ..., + encoding: None = ..., + errors: None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + ) -> Popen[bytes]: ... + @overload + def __new__( + cls, + args: _CMD, + bufsize: int = ..., + executable: StrOrBytesPath | None = ..., + stdin: _FILE | None = ..., + stdout: _FILE | None = ..., + stderr: _FILE | None = ..., + preexec_fn: Callable[[], Any] | None = ..., + close_fds: bool = ..., + shell: bool = ..., + cwd: StrOrBytesPath | None = ..., + env: _ENV | None = ..., + universal_newlines: bool = ..., + startupinfo: Any | None = ..., + creationflags: int = ..., + restore_signals: bool = ..., + start_new_session: bool = ..., + pass_fds: Any = ..., + *, + text: bool | None = ..., + encoding: str | None = ..., + errors: str | None = ..., + user: str | int | None = ..., + group: str | int | None = ..., + extra_groups: Iterable[str | int] | None = ..., + umask: int = ..., + ) -> Popen[Any]: ... + elif sys.version_info >= (3, 7): # text is added in 3.7 @overload def __new__( diff --git a/mypy/typeshed/stdlib/sys.pyi b/mypy/typeshed/stdlib/sys.pyi index 92b806e04420..4e24cbd167d9 100644 --- a/mypy/typeshed/stdlib/sys.pyi +++ b/mypy/typeshed/stdlib/sys.pyi @@ -122,6 +122,9 @@ class _flags(_uninstantiable_structseq, _FlagTuple): if sys.version_info >= (3, 10): @property def warn_default_encoding(self) -> int: ... # undocumented + if sys.version_info >= (3, 11): + @property + def safe_path(self) -> bool: ... float_info: _float_info @@ -320,9 +323,8 @@ class _asyncgen_hooks(structseq[_AsyncgenHook], tuple[_AsyncgenHook, _AsyncgenHo def get_asyncgen_hooks() -> _asyncgen_hooks: ... def set_asyncgen_hooks(firstiter: _AsyncgenHook = ..., finalizer: _AsyncgenHook = ...) -> None: ... -if sys.version_info >= (3, 6): - if sys.platform == "win32": - def _enablelegacywindowsfsencoding() -> None: ... +if sys.platform == "win32": + def _enablelegacywindowsfsencoding() -> None: ... if sys.version_info >= (3, 7): def get_coroutine_origin_tracking_depth() -> int: ... diff --git a/mypy/typeshed/stdlib/tkinter/__init__.pyi b/mypy/typeshed/stdlib/tkinter/__init__.pyi index 66c52107067d..582503971e15 100644 --- a/mypy/typeshed/stdlib/tkinter/__init__.pyi +++ b/mypy/typeshed/stdlib/tkinter/__init__.pyi @@ -6,7 +6,7 @@ from enum import Enum from tkinter.constants import * from tkinter.font import _FontDescription from types import TracebackType -from typing import Any, Generic, Protocol, TypeVar, Union, overload +from typing import Any, Generic, NamedTuple, Protocol, TypeVar, Union, overload from typing_extensions import Literal, TypeAlias, TypedDict if sys.version_info >= (3, 9): @@ -198,6 +198,14 @@ _ScreenUnits: TypeAlias = str | float # Often the right type instead of int. Ma _XYScrollCommand: TypeAlias = str | Callable[[float, float], Any] # -xscrollcommand and -yscrollcommand in 'options' manual page _TakeFocusValue: TypeAlias = Union[int, Literal[""], Callable[[str], bool | None]] # -takefocus in manual page named 'options' +if sys.version_info >= (3, 11): + class _VersionInfoType(NamedTuple): + major: int + minor: int + micro: int + releaselevel: str + serial: int + class EventType(str, Enum): Activate: str ButtonPress: str @@ -377,6 +385,9 @@ class Misc: def lower(self, belowThis: Any | None = ...) -> None: ... def tkraise(self, aboveThis: Any | None = ...) -> None: ... lift = tkraise + if sys.version_info >= (3, 11): + def info_patchlevel(self) -> _VersionInfoType: ... + def winfo_atom(self, name: str, displayof: Literal[0] | Misc | None = ...) -> int: ... def winfo_atomname(self, id: int, displayof: Literal[0] | Misc | None = ...) -> str: ... def winfo_cells(self) -> int: ... @@ -475,6 +486,8 @@ class Misc: def unbind_class(self, className: str, sequence: str) -> None: ... def mainloop(self, n: int = ...) -> None: ... def quit(self) -> None: ... + @property + def _windowingsystem(self) -> Literal["win32", "aqua", "x11"]: ... def nametowidget(self, name: str | Misc | _tkinter.Tcl_Obj) -> Any: ... def register( self, func: Callable[..., Any], subst: Callable[..., Sequence[Any]] | None = ..., needcleanup: int = ... @@ -1221,8 +1234,9 @@ class Canvas(Widget, XView, YView): def coords(self, __tagOrId: str | _CanvasItemId, __args: list[int] | list[float] | tuple[float, ...]) -> None: ... @overload def coords(self, __tagOrId: str | _CanvasItemId, __x1: float, __y1: float, *args: float) -> None: ... - # create_foo() methods accept coords as a list, a tuple, or as separate arguments. - # Keyword arguments should be the same in each pair of overloads. + # create_foo() methods accept coords as a list or tuple, or as separate arguments. + # Lists and tuples can be flat as in [1, 2, 3, 4], or nested as in [(1, 2), (3, 4)]. + # Keyword arguments should be the same in all overloads of each method. def create_arc(self, *args, **kw) -> _CanvasItemId: ... def create_bitmap(self, *args, **kw) -> _CanvasItemId: ... def create_image(self, *args, **kw) -> _CanvasItemId: ... @@ -1260,7 +1274,43 @@ class Canvas(Widget, XView, YView): @overload def create_line( self, - __coords: tuple[float, float, float, float] | list[int] | list[float], + __xy_pair_0: tuple[float, float], + __xy_pair_1: tuple[float, float], + *, + activedash: str | list[int] | tuple[int, ...] = ..., + activefill: _Color = ..., + activestipple: str = ..., + activewidth: _ScreenUnits = ..., + arrow: Literal["first", "last", "both"] = ..., + arrowshape: tuple[float, float, float] = ..., + capstyle: Literal["round", "projecting", "butt"] = ..., + dash: str | list[int] | tuple[int, ...] = ..., + dashoffset: _ScreenUnits = ..., + disableddash: str | list[int] | tuple[int, ...] = ..., + disabledfill: _Color = ..., + disabledstipple: _Bitmap = ..., + disabledwidth: _ScreenUnits = ..., + fill: _Color = ..., + joinstyle: Literal["round", "bevel", "miter"] = ..., + offset: _ScreenUnits = ..., + smooth: bool = ..., + splinesteps: float = ..., + state: Literal["normal", "active", "disabled"] = ..., + stipple: _Bitmap = ..., + tags: str | list[str] | tuple[str, ...] = ..., + width: _ScreenUnits = ..., + ) -> _CanvasItemId: ... + @overload + def create_line( + self, + __coords: ( + tuple[float, float, float, float] + | tuple[tuple[float, float], tuple[float, float]] + | list[int] + | list[float] + | list[tuple[int, int]] + | list[tuple[float, float]] + ), *, activedash: str | list[int] | tuple[int, ...] = ..., activefill: _Color = ..., @@ -1320,7 +1370,44 @@ class Canvas(Widget, XView, YView): @overload def create_oval( self, - __coords: tuple[float, float, float, float] | list[int] | list[float], + __xy_pair_0: tuple[float, float], + __xy_pair_1: tuple[float, float], + *, + activedash: str | list[int] | tuple[int, ...] = ..., + activefill: _Color = ..., + activeoutline: _Color = ..., + activeoutlinestipple: _Color = ..., + activestipple: str = ..., + activewidth: _ScreenUnits = ..., + dash: str | list[int] | tuple[int, ...] = ..., + dashoffset: _ScreenUnits = ..., + disableddash: str | list[int] | tuple[int, ...] = ..., + disabledfill: _Color = ..., + disabledoutline: _Color = ..., + disabledoutlinestipple: _Color = ..., + disabledstipple: _Bitmap = ..., + disabledwidth: _ScreenUnits = ..., + fill: _Color = ..., + offset: _ScreenUnits = ..., + outline: _Color = ..., + outlineoffset: _ScreenUnits = ..., + outlinestipple: _Bitmap = ..., + state: Literal["normal", "active", "disabled"] = ..., + stipple: _Bitmap = ..., + tags: str | list[str] | tuple[str, ...] = ..., + width: _ScreenUnits = ..., + ) -> _CanvasItemId: ... + @overload + def create_oval( + self, + __coords: ( + tuple[float, float, float, float] + | tuple[tuple[float, float], tuple[float, float]] + | list[int] + | list[float] + | list[tuple[int, int]] + | list[tuple[float, float]] + ), *, activedash: str | list[int] | tuple[int, ...] = ..., activefill: _Color = ..., @@ -1384,7 +1471,47 @@ class Canvas(Widget, XView, YView): @overload def create_polygon( self, - __coords: tuple[float, ...] | list[int] | list[float], + __xy_pair_0: tuple[float, float], + __xy_pair_1: tuple[float, float], + *xy_pairs: tuple[float, float], + activedash: str | list[int] | tuple[int, ...] = ..., + activefill: _Color = ..., + activeoutline: _Color = ..., + activeoutlinestipple: _Color = ..., + activestipple: str = ..., + activewidth: _ScreenUnits = ..., + dash: str | list[int] | tuple[int, ...] = ..., + dashoffset: _ScreenUnits = ..., + disableddash: str | list[int] | tuple[int, ...] = ..., + disabledfill: _Color = ..., + disabledoutline: _Color = ..., + disabledoutlinestipple: _Color = ..., + disabledstipple: _Bitmap = ..., + disabledwidth: _ScreenUnits = ..., + fill: _Color = ..., + joinstyle: Literal["round", "bevel", "miter"] = ..., + offset: _ScreenUnits = ..., + outline: _Color = ..., + outlineoffset: _ScreenUnits = ..., + outlinestipple: _Bitmap = ..., + smooth: bool = ..., + splinesteps: float = ..., + state: Literal["normal", "active", "disabled"] = ..., + stipple: _Bitmap = ..., + tags: str | list[str] | tuple[str, ...] = ..., + width: _ScreenUnits = ..., + ) -> _CanvasItemId: ... + @overload + def create_polygon( + self, + __coords: ( + tuple[float, ...] + | tuple[tuple[float, float], ...] + | list[int] + | list[float] + | list[tuple[int, int]] + | list[tuple[float, float]] + ), *, activedash: str | list[int] | tuple[int, ...] = ..., activefill: _Color = ..., @@ -1448,7 +1575,44 @@ class Canvas(Widget, XView, YView): @overload def create_rectangle( self, - __coords: tuple[float, float, float, float] | list[int] | list[float], + __xy_pair_0: tuple[float, float], + __xy_pair_1: tuple[float, float], + *, + activedash: str | list[int] | tuple[int, ...] = ..., + activefill: _Color = ..., + activeoutline: _Color = ..., + activeoutlinestipple: _Color = ..., + activestipple: str = ..., + activewidth: _ScreenUnits = ..., + dash: str | list[int] | tuple[int, ...] = ..., + dashoffset: _ScreenUnits = ..., + disableddash: str | list[int] | tuple[int, ...] = ..., + disabledfill: _Color = ..., + disabledoutline: _Color = ..., + disabledoutlinestipple: _Color = ..., + disabledstipple: _Bitmap = ..., + disabledwidth: _ScreenUnits = ..., + fill: _Color = ..., + offset: _ScreenUnits = ..., + outline: _Color = ..., + outlineoffset: _ScreenUnits = ..., + outlinestipple: _Bitmap = ..., + state: Literal["normal", "active", "disabled"] = ..., + stipple: _Bitmap = ..., + tags: str | list[str] | tuple[str, ...] = ..., + width: _ScreenUnits = ..., + ) -> _CanvasItemId: ... + @overload + def create_rectangle( + self, + __coords: ( + tuple[float, float, float, float] + | tuple[tuple[float, float], tuple[float, float]] + | list[int] + | list[float] + | list[tuple[int, int]] + | list[tuple[float, float]] + ), *, activedash: str | list[int] | tuple[int, ...] = ..., activefill: _Color = ..., diff --git a/mypy/typeshed/stdlib/types.pyi b/mypy/typeshed/stdlib/types.pyi index 872ed57a7c76..ed2476e44a86 100644 --- a/mypy/typeshed/stdlib/types.pyi +++ b/mypy/typeshed/stdlib/types.pyi @@ -651,6 +651,10 @@ if sys.version_info >= (3, 9): @property def __parameters__(self) -> tuple[Any, ...]: ... def __init__(self, origin: type, args: Any) -> None: ... + if sys.version_info >= (3, 11): + @property + def __unpacked__(self) -> bool: ... + def __getattr__(self, name: str) -> Any: ... # incomplete if sys.version_info >= (3, 10): diff --git a/mypy/typeshed/stdlib/typing.pyi b/mypy/typeshed/stdlib/typing.pyi index 28b588d79c9b..37ea55c9f2ef 100644 --- a/mypy/typeshed/stdlib/typing.pyi +++ b/mypy/typeshed/stdlib/typing.pyi @@ -1,6 +1,6 @@ import collections # Needed by aliases like DefaultDict, see mypy issue 2986 import sys -from _typeshed import Self as TypeshedSelf, SupportsKeysAndGetItem +from _typeshed import ReadableBuffer, Self as TypeshedSelf, SupportsKeysAndGetItem from abc import ABCMeta, abstractmethod from types import BuiltinFunctionType, CodeType, FrameType, FunctionType, MethodType, ModuleType, TracebackType from typing_extensions import Literal as _Literal, ParamSpec as _ParamSpec, final as _final @@ -88,6 +88,7 @@ if sys.version_info >= (3, 11): "assert_type", "cast", "clear_overloads", + "dataclass_transform", "final", "get_args", "get_origin", @@ -1079,7 +1080,10 @@ class Match(Generic[AnyStr]): # this match instance. @property def re(self) -> Pattern[AnyStr]: ... - def expand(self, template: AnyStr) -> AnyStr: ... + @overload + def expand(self: Match[str], template: str) -> str: ... + @overload + def expand(self: Match[bytes], template: ReadableBuffer) -> bytes: ... # group() returns "AnyStr" or "AnyStr | None", depending on the pattern. @overload def group(self, __group: _Literal[0] = ...) -> AnyStr: ... @@ -1124,20 +1128,49 @@ class Pattern(Generic[AnyStr]): def groups(self) -> int: ... @property def pattern(self) -> AnyStr: ... - def search(self, string: AnyStr, pos: int = ..., endpos: int = ...) -> Match[AnyStr] | None: ... - def match(self, string: AnyStr, pos: int = ..., endpos: int = ...) -> Match[AnyStr] | None: ... - def fullmatch(self, string: AnyStr, pos: int = ..., endpos: int = ...) -> Match[AnyStr] | None: ... - def split(self, string: AnyStr, maxsplit: int = ...) -> list[AnyStr | Any]: ... - def findall(self, string: AnyStr, pos: int = ..., endpos: int = ...) -> list[Any]: ... - def finditer(self, string: AnyStr, pos: int = ..., endpos: int = ...) -> Iterator[Match[AnyStr]]: ... @overload - def sub(self, repl: AnyStr, string: AnyStr, count: int = ...) -> AnyStr: ... + def search(self: Pattern[str], string: str, pos: int = ..., endpos: int = ...) -> Match[str] | None: ... + @overload + def search(self: Pattern[bytes], string: ReadableBuffer, pos: int = ..., endpos: int = ...) -> Match[bytes] | None: ... + @overload + def match(self: Pattern[str], string: str, pos: int = ..., endpos: int = ...) -> Match[str] | None: ... + @overload + def match(self: Pattern[bytes], string: ReadableBuffer, pos: int = ..., endpos: int = ...) -> Match[bytes] | None: ... + @overload + def fullmatch(self: Pattern[str], string: str, pos: int = ..., endpos: int = ...) -> Match[str] | None: ... + @overload + def fullmatch(self: Pattern[bytes], string: ReadableBuffer, pos: int = ..., endpos: int = ...) -> Match[bytes] | None: ... + @overload + def split(self: Pattern[str], string: str, maxsplit: int = ...) -> list[str | Any]: ... + @overload + def split(self: Pattern[bytes], string: ReadableBuffer, maxsplit: int = ...) -> list[bytes | Any]: ... + # return type depends on the number of groups in the pattern + @overload + def findall(self: Pattern[str], string: str, pos: int = ..., endpos: int = ...) -> list[Any]: ... + @overload + def findall(self: Pattern[bytes], string: ReadableBuffer, pos: int = ..., endpos: int = ...) -> list[Any]: ... + @overload + def finditer(self: Pattern[str], string: str, pos: int = ..., endpos: int = ...) -> Iterator[Match[str]]: ... + @overload + def finditer(self: Pattern[bytes], string: ReadableBuffer, pos: int = ..., endpos: int = ...) -> Iterator[Match[bytes]]: ... + @overload + def sub(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = ...) -> str: ... @overload - def sub(self, repl: Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = ...) -> AnyStr: ... + def sub( + self: Pattern[bytes], + repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], + string: ReadableBuffer, + count: int = ..., + ) -> bytes: ... @overload - def subn(self, repl: AnyStr, string: AnyStr, count: int = ...) -> tuple[AnyStr, int]: ... + def subn(self: Pattern[str], repl: str | Callable[[Match[str]], str], string: str, count: int = ...) -> tuple[str, int]: ... @overload - def subn(self, repl: Callable[[Match[AnyStr]], AnyStr], string: AnyStr, count: int = ...) -> tuple[AnyStr, int]: ... + def subn( + self: Pattern[bytes], + repl: ReadableBuffer | Callable[[Match[bytes]], ReadableBuffer], + string: ReadableBuffer, + count: int = ..., + ) -> tuple[bytes, int]: ... def __copy__(self) -> Pattern[AnyStr]: ... def __deepcopy__(self, __memo: Any) -> Pattern[AnyStr]: ... if sys.version_info >= (3, 9): @@ -1192,6 +1225,14 @@ if sys.version_info >= (3, 11): def assert_type(__val: _T, __typ: Any) -> _T: ... def clear_overloads() -> None: ... def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: ... + def dataclass_transform( + *, + eq_default: bool = ..., + order_default: bool = ..., + kw_only_default: bool = ..., + field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = ..., + **kwargs: Any, + ) -> Callable[[_T], _T]: ... # Type constructors diff --git a/mypy/typeshed/stdlib/typing_extensions.pyi b/mypy/typeshed/stdlib/typing_extensions.pyi index 1c75ec38e75c..b94daaba9f49 100644 --- a/mypy/typeshed/stdlib/typing_extensions.pyi +++ b/mypy/typeshed/stdlib/typing_extensions.pyi @@ -1,7 +1,7 @@ import abc import sys from _typeshed import Self as TypeshedSelf # see #6932 for why the alias cannot have a leading underscore -from typing import ( # noqa: Y022,Y027 +from typing import ( # noqa: Y022,Y027,Y039 TYPE_CHECKING as TYPE_CHECKING, Any, AsyncContextManager as AsyncContextManager, @@ -201,6 +201,7 @@ if sys.version_info >= (3, 11): assert_never as assert_never, assert_type as assert_type, clear_overloads as clear_overloads, + dataclass_transform as dataclass_transform, get_overloads as get_overloads, reveal_type as reveal_type, ) @@ -224,12 +225,11 @@ else: def __init__(self, name: str) -> None: ... def __iter__(self) -> Any: ... # Unpack[Self] -# Experimental (hopefully these will be in 3.11) -def dataclass_transform( - *, - eq_default: bool = ..., - order_default: bool = ..., - kw_only_default: bool = ..., - field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = ..., - **kwargs: object, -) -> Callable[[_T], _T]: ... + def dataclass_transform( + *, + eq_default: bool = ..., + order_default: bool = ..., + kw_only_default: bool = ..., + field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = ..., + **kwargs: object, + ) -> Callable[[_T], _T]: ... diff --git a/mypy/typeshed/stdlib/urllib/error.pyi b/mypy/typeshed/stdlib/urllib/error.pyi index 48c8287e979a..7a4de10d7cf6 100644 --- a/mypy/typeshed/stdlib/urllib/error.pyi +++ b/mypy/typeshed/stdlib/urllib/error.pyi @@ -9,6 +9,8 @@ class URLError(IOError): def __init__(self, reason: str | BaseException, filename: str | None = ...) -> None: ... class HTTPError(URLError, addinfourl): + @property + def headers(self) -> Message: ... # type: ignore[override] @property def reason(self) -> str: ... # type: ignore[override] code: int diff --git a/mypy/typeshed/stdlib/warnings.pyi b/mypy/typeshed/stdlib/warnings.pyi index bd7afb2d7cba..c9c143991e3a 100644 --- a/mypy/typeshed/stdlib/warnings.pyi +++ b/mypy/typeshed/stdlib/warnings.pyi @@ -1,3 +1,4 @@ +import sys from _warnings import warn as warn, warn_explicit as warn_explicit from collections.abc import Sequence from types import ModuleType, TracebackType @@ -56,12 +57,48 @@ class WarningMessage: ) -> None: ... class catch_warnings: - @overload - def __new__(cls, *, record: Literal[False] = ..., module: ModuleType | None = ...) -> _catch_warnings_without_records: ... - @overload - def __new__(cls, *, record: Literal[True], module: ModuleType | None = ...) -> _catch_warnings_with_records: ... - @overload - def __new__(cls, *, record: bool, module: ModuleType | None = ...) -> catch_warnings: ... + if sys.version_info >= (3, 11): + @overload + def __new__( + cls, + *, + record: Literal[False] = ..., + module: ModuleType | None = ..., + action: _ActionKind | None = ..., + category: type[Warning] = ..., + lineno: int = ..., + append: bool = ..., + ) -> _catch_warnings_without_records: ... + @overload + def __new__( + cls, + *, + record: Literal[True], + module: ModuleType | None = ..., + action: _ActionKind | None = ..., + category: type[Warning] = ..., + lineno: int = ..., + append: bool = ..., + ) -> _catch_warnings_with_records: ... + @overload + def __new__( + cls, + *, + record: bool, + module: ModuleType | None = ..., + action: _ActionKind | None = ..., + category: type[Warning] = ..., + lineno: int = ..., + append: bool = ..., + ) -> catch_warnings: ... + else: + @overload + def __new__(cls, *, record: Literal[False] = ..., module: ModuleType | None = ...) -> _catch_warnings_without_records: ... + @overload + def __new__(cls, *, record: Literal[True], module: ModuleType | None = ...) -> _catch_warnings_with_records: ... + @overload + def __new__(cls, *, record: bool, module: ModuleType | None = ...) -> catch_warnings: ... + def __enter__(self) -> list[WarningMessage] | None: ... def __exit__( self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None diff --git a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi index 414530b0a34c..dacb6fffcc6b 100644 --- a/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi +++ b/mypy/typeshed/stdlib/xml/etree/ElementTree.pyi @@ -319,7 +319,7 @@ def iterparse( class XMLPullParser: def __init__(self, events: Sequence[str] | None = ..., *, _parser: XMLParser | None = ...) -> None: ... - def feed(self, data: bytes) -> None: ... + def feed(self, data: str | bytes) -> None: ... def close(self) -> None: ... def read_events(self) -> Iterator[tuple[str, Element]]: ... diff --git a/mypy/typeshed/stdlib/zipfile.pyi b/mypy/typeshed/stdlib/zipfile.pyi index d5255b15c3c0..276f8df82a6d 100644 --- a/mypy/typeshed/stdlib/zipfile.pyi +++ b/mypy/typeshed/stdlib/zipfile.pyi @@ -222,6 +222,8 @@ class ZipFile: ) -> None: ... else: def writestr(self, zinfo_or_arcname: str | ZipInfo, data: bytes | str, compress_type: int | None = ...) -> None: ... + if sys.version_info >= (3, 11): + def mkdir(self, zinfo_or_directory: str | ZipInfo, mode: int = ...) -> None: ... class PyZipFile(ZipFile): def __init__( diff --git a/mypy/typeshed/stubs/mypy-extensions/METADATA.toml b/mypy/typeshed/stubs/mypy-extensions/METADATA.toml index 79b51931ee0b..582104d3a1a7 100644 --- a/mypy/typeshed/stubs/mypy-extensions/METADATA.toml +++ b/mypy/typeshed/stubs/mypy-extensions/METADATA.toml @@ -1,2 +1 @@ version = "0.4.*" -python2 = true diff --git a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi index 33b47244d385..412c3cb15142 100644 --- a/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi +++ b/mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi @@ -1,7 +1,7 @@ import abc -import sys from _typeshed import Self -from typing import Any, Callable, Generic, ItemsView, KeysView, Mapping, TypeVar, ValuesView +from collections.abc import Callable, ItemsView, KeysView, Mapping, ValuesView +from typing import Any, Generic, TypeVar _T = TypeVar("_T") _U = TypeVar("_U") @@ -15,16 +15,9 @@ class _TypedDict(Mapping[str, object], metaclass=abc.ABCMeta): # Mypy plugin hook for 'pop' expects that 'default' has a type variable type. def pop(self, k: NoReturn, default: _T = ...) -> object: ... # type: ignore def update(self: Self, __m: Self) -> None: ... - if sys.version_info >= (3, 0): - def items(self) -> ItemsView[str, object]: ... - def keys(self) -> KeysView[str]: ... - def values(self) -> ValuesView[object]: ... - else: - def has_key(self, k: str) -> bool: ... - def viewitems(self) -> ItemsView[str, object]: ... - def viewkeys(self) -> KeysView[str]: ... - def viewvalues(self) -> ValuesView[object]: ... - + def items(self) -> ItemsView[str, object]: ... + def keys(self) -> KeysView[str]: ... + def values(self) -> ValuesView[object]: ... def __delitem__(self, k: NoReturn) -> None: ... def TypedDict(typename: str, fields: dict[str, type[Any]], total: bool = ...) -> type[dict[str, Any]]: ... diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index a1b4d986b98a..86a975fc4949 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -644,10 +644,10 @@ python_version = 3.6 [file int_pow.py] a = 1 b = a + 2 -reveal_type(a**0) # N: Revealed type is "builtins.int" +reveal_type(a**0) # N: Revealed type is "Literal[1]" reveal_type(a**1) # N: Revealed type is "builtins.int" reveal_type(a**2) # N: Revealed type is "builtins.int" -reveal_type(a**-0) # N: Revealed type is "builtins.int" +reveal_type(a**-0) # N: Revealed type is "Literal[1]" reveal_type(a**-1) # N: Revealed type is "builtins.float" reveal_type(a**(-2)) # N: Revealed type is "builtins.float" reveal_type(a**b) # N: Revealed type is "Any" diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index a3f44fff5e33..b59d50feb986 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1005,8 +1005,11 @@ re.subn(bpat, b'', b'')[0] + b'' re.subn(bre, lambda m: b'', b'')[0] + b'' re.subn(bpat, lambda m: b'', b'')[0] + b'' [out] -_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "Sequence[object]" -_program.py:9: error: Cannot infer type argument 1 of "search" +_testReModuleBytes.py:7: error: No overload variant of "search" matches argument types "bytes", "str" +_testReModuleBytes.py:7: note: Possible overload variants: +_testReModuleBytes.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] +_testReModuleBytes.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Union[bytes, Union[bytearray, memoryview, array[Any], mmap, _CData]], flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] +_testReModuleBytes.py:9: error: Argument 1 to "search" has incompatible type "Pattern[bytes]"; expected "Union[str, Pattern[str]]" [case testReModuleString] # Regression tests for various overloads in the re module -- string version @@ -1029,8 +1032,11 @@ re.subn(spat, '', '')[0] + '' re.subn(sre, lambda m: '', '')[0] + '' re.subn(spat, lambda m: '', '')[0] + '' [out] -_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "Sequence[object]" -_program.py:9: error: Cannot infer type argument 1 of "search" +_testReModuleString.py:7: error: No overload variant of "search" matches argument types "str", "bytes" +_testReModuleString.py:7: note: Possible overload variants: +_testReModuleString.py:7: note: def search(pattern: Union[str, Pattern[str]], string: str, flags: Union[int, RegexFlag] = ...) -> Optional[Match[str]] +_testReModuleString.py:7: note: def search(pattern: Union[bytes, Pattern[bytes]], string: Union[bytes, Union[bytearray, memoryview, array[Any], mmap, _CData]], flags: Union[int, RegexFlag] = ...) -> Optional[Match[bytes]] +_testReModuleString.py:9: error: Argument 1 to "search" has incompatible type "Pattern[str]"; expected "Union[bytes, Pattern[bytes]]" [case testListSetitemTuple] from typing import List, Tuple From e63501c7a11abc54684bdd59872eb3b092b865ae Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 16 May 2022 09:18:30 +0100 Subject: [PATCH 63/79] Add test case for fixed dataclass-classmethod crash (#12794) --- test-data/unit/check-dataclasses.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 4cddc59b0153..7782652d66e5 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -495,6 +495,19 @@ reveal_type(A.foo("foo")) # N: Revealed type is "builtins.str" [builtins fixtures/dataclasses.pyi] +[case testClassmethodShadowingFieldDoesNotCrash] +# flags: --python-version 3.7 +from dataclasses import dataclass + +# This used to crash -- see #6217 +@dataclass +class Foo: + bar: str + @classmethod # E: Name "bar" already defined on line 7 + def bar(cls) -> "Foo": + return cls('asdf') +[builtins fixtures/dataclasses.pyi] + [case testDataclassesClassVars] # flags: --python-version 3.7 from dataclasses import dataclass From 17aec677e44da4488908680b90e0c145bff67d43 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 16 May 2022 09:19:55 +0100 Subject: [PATCH 64/79] Add test case for fixed dataclass-in-function crash (#12793) Closes #8703, which is a crash that was fixed by #12762 --- test-data/unit/check-dataclasses.test | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 7782652d66e5..771938119fb2 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1566,6 +1566,20 @@ A(a=func).a() A(a=func).a = func # E: Property "a" defined in "A" is read-only [builtins fixtures/dataclasses.pyi] +[case testDataclassInFunctionDoesNotCrash] +# flags: --python-version 3.7 +from dataclasses import dataclass + +def foo(): + @dataclass + class Foo: + foo: int + # This used to crash (see #8703) + # The return type of __call__ here needs to be something undefined + # In order to trigger the crash that existed prior to #12762 + def __call__(self) -> asdf: ... # E: Name "asdf" is not defined +[builtins fixtures/dataclasses.pyi] + [case testDataclassesMultipleInheritanceWithNonDataclass] # flags: --python-version 3.10 from dataclasses import dataclass From 613756bd7e7a41ad14f9a6045da04cfc4612c032 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 16 May 2022 09:20:43 +0100 Subject: [PATCH 65/79] Add test case for fixed generic-dataclass crash (#12791) Adds a test case for a crash that was fixed by #12762. Closes #12527. --- test-data/unit/check-dataclasses.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 771938119fb2..28498fb0190a 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1712,3 +1712,15 @@ c: C[C] d: C[str] # E: Type argument "str" of "C" must be a subtype of "C[Any]" C(x=2) [builtins fixtures/dataclasses.pyi] + +[case testDataclassGenericBoundToInvalidTypeVarDoesNotCrash] +# flags: --python-version 3.7 +import dataclasses +from typing import Generic, TypeVar + +T = TypeVar("T", bound="NotDefined") # E: Name "NotDefined" is not defined + +@dataclasses.dataclass +class C(Generic[T]): + x: float +[builtins fixtures/dataclasses.pyi] From 3b7468ea518ef961d75597d52b183579b0fd83db Mon Sep 17 00:00:00 2001 From: Max Jones Date: Mon, 16 May 2022 09:39:16 -0400 Subject: [PATCH 66/79] Update link in the readme and setup (#12788) --- README.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1be59f0b0027..c40a224ea042 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ We are always happy to answer questions! Here are some good places to ask them: - for general questions about Python typing, try [typing discussions](https://github.com/python/typing/discussions) If you're just getting started, -[the documentation](https://mypy.readthedocs.io/en/stable/introduction.html) +[the documentation](https://mypy.readthedocs.io/en/stable/index.html) and [type hints cheat sheet](https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html) can also help answer questions. diff --git a/setup.py b/setup.py index bba99fec8259..7999fb20216e 100644 --- a/setup.py +++ b/setup.py @@ -210,7 +210,7 @@ def run(self): include_package_data=True, project_urls={ 'News': 'http://mypy-lang.org/news.html', - 'Documentation': 'https://mypy.readthedocs.io/en/stable/introduction.html', + 'Documentation': 'https://mypy.readthedocs.io/en/stable/index.html', 'Repository': 'https://github.com/python/mypy', }, ) From e6bbf5f5e44854e1152ab9ddb46daa2608cf4076 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 16 May 2022 16:36:40 +0100 Subject: [PATCH 67/79] Typeshed cherry-picks: gettext and sqlite3 fixes (#12797) * gettext: Make GNUTranslations.CONTEXT not final (python/typeshed#7841) * sqlite3: Avoid optional type for 'description' (python/typeshed#7842) --- mypy/typeshed/stdlib/gettext.pyi | 2 +- mypy/typeshed/stdlib/sqlite3/dbapi2.pyi | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/typeshed/stdlib/gettext.pyi b/mypy/typeshed/stdlib/gettext.pyi index d02dda5350b7..829ade96cb40 100644 --- a/mypy/typeshed/stdlib/gettext.pyi +++ b/mypy/typeshed/stdlib/gettext.pyi @@ -96,7 +96,7 @@ class NullTranslations: class GNUTranslations(NullTranslations): LE_MAGIC: Final[int] BE_MAGIC: Final[int] - CONTEXT: Final[str] + CONTEXT: str VERSIONS: Sequence[int] @overload # ignores incompatible overloads diff --git a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi index a6ccc9977c1c..dc00a3971b0f 100644 --- a/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi +++ b/mypy/typeshed/stdlib/sqlite3/dbapi2.pyi @@ -374,8 +374,9 @@ class Cursor(Iterator[Any]): arraysize: int @property def connection(self) -> Connection: ... + # May be None, but using | Any instead to avoid slightly annoying false positives. @property - def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | None: ... + def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | Any: ... @property def lastrowid(self) -> int | None: ... row_factory: Callable[[Cursor, Row[Any]], object] | None From 85c21599cae51318344c23eecdfec6c3a2a9557a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 16 May 2022 16:57:49 +0100 Subject: [PATCH 68/79] Add dataclass InitVar test case (#12798) This test case cover a recent change in functionality: if an attribute is defined as an InitVar, it can't be assigned to. Previously this was supported (probably by accident). --- test-data/unit/check-dataclasses.test | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 28498fb0190a..abfcb79c0cc5 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1724,3 +1724,23 @@ T = TypeVar("T", bound="NotDefined") # E: Name "NotDefined" is not defined class C(Generic[T]): x: float [builtins fixtures/dataclasses.pyi] + +[case testDataclassInitVarCannotBeSet] +# flags: --python-version 3.7 +from dataclasses import dataclass, InitVar + +@dataclass +class C: + x: InitVar[int] = 0 + y: InitVar[str] = '' + + def f(self) -> None: + # This works at runtime, but it seems like an abuse of the InitVar + # feature and thus we don't support it + self.x = 1 # E: "C" has no attribute "x" + self.y: str = 'x' # E: "C" has no attribute "y" + +c = C() +c2 = C(x=1) +c.x # E: "C" has no attribute "x" +[builtins fixtures/dataclasses.pyi] From 18a510768bdf552248447d35d15f8e48b1aef53d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 May 2022 15:46:29 +0100 Subject: [PATCH 69/79] Fix nested namedtuple crash in incremental mode (#12803) Make sure the fullname of a named tuple defined within a method matches the nesting of the definition in the symbol table. Otherwise we'll have a crash during deserialization. In particular, a named tuple defined within a method will now be always stored in the symbol table of the surrounding class, instead of the global symbol table. Previously there was an inconsistency between old-style and new-style syntax. Fix #10913. --- mypy/semanal.py | 23 +++++++----- mypy/semanal_namedtuple.py | 5 ++- test-data/unit/check-incremental.test | 53 +++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index d68928ef21ad..a49e7c23edf5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1220,7 +1220,7 @@ def analyze_namedtuple_classdef(self, defn: ClassDef) -> bool: is_named_tuple, info = True, defn.info # type: bool, Optional[TypeInfo] else: is_named_tuple, info = self.named_tuple_analyzer.analyze_namedtuple_classdef( - defn, self.is_stub_file) + defn, self.is_stub_file, self.is_func_scope()) if is_named_tuple: if info is None: self.mark_incomplete(defn.name, defn) @@ -1462,7 +1462,10 @@ def prepare_class_def(self, defn: ClassDef, info: Optional[TypeInfo] = None) -> info._fullname = self.qualified_name(defn.name) else: info._fullname = info.name - self.add_symbol(defn.name, defn.info, defn) + local_name = defn.name + if '@' in local_name: + local_name = local_name.split('@')[0] + self.add_symbol(local_name, defn.info, defn) if self.is_nested_within_func_scope(): # We need to preserve local classes, let's store them # in globals under mangled unique names @@ -1471,17 +1474,17 @@ def prepare_class_def(self, defn: ClassDef, info: Optional[TypeInfo] = None) -> # incremental mode and we should avoid it. In general, this logic is too # ad-hoc and needs to be removed/refactored. if '@' not in defn.info._fullname: - local_name = defn.info.name + '@' + str(defn.line) - if defn.info.is_named_tuple: - # Module is already correctly set in _fullname for named tuples. - defn.info._fullname += '@' + str(defn.line) - else: - defn.info._fullname = self.cur_mod_id + '.' + local_name + global_name = defn.info.name + '@' + str(defn.line) + defn.info._fullname = self.cur_mod_id + '.' + global_name else: # Preserve name from previous fine-grained incremental run. - local_name = defn.info.name + global_name = defn.info.name defn.fullname = defn.info._fullname - self.globals[local_name] = SymbolTableNode(GDEF, defn.info) + if defn.info.is_named_tuple: + # Named tuple nested within a class is stored in the class symbol table. + self.add_symbol_skip_local(global_name, defn.info) + else: + self.globals[global_name] = SymbolTableNode(GDEF, defn.info) def make_empty_type_info(self, defn: ClassDef) -> TypeInfo: if (self.is_module_scope() diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 4e05dfb99605..07863dea2efb 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -53,7 +53,8 @@ def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None: self.options = options self.api = api - def analyze_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool + def analyze_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool, + is_func_scope: bool ) -> Tuple[bool, Optional[TypeInfo]]: """Analyze if given class definition can be a named tuple definition. @@ -70,6 +71,8 @@ def analyze_namedtuple_classdef(self, defn: ClassDef, is_stub_file: bool # This is a valid named tuple, but some types are incomplete. return True, None items, types, default_items = result + if is_func_scope and '@' not in defn.name: + defn.name += '@' + str(defn.line) info = self.build_namedtuple_typeinfo( defn.name, items, types, default_items, defn.line) defn.info = info diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index caba9b73e594..c604b386691b 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5658,3 +5658,56 @@ class D(C): [out] [out2] tmp/a.py:9: error: Trying to assign name "z" that is not in "__slots__" of type "a.D" + +[case testIncrementalWithDifferentKindsOfNestedTypesWithinMethod] +# flags: --python-version 3.7 + +import a + +[file a.py] +import b + +[file a.py.2] +import b +b.xyz + +[file b.py] +from typing import NamedTuple, NewType +from typing_extensions import TypedDict, TypeAlias +from enum import Enum +from dataclasses import dataclass + +class C: + def f(self) -> None: + class C: + c: int + class NT1(NamedTuple): + c: int + NT2 = NamedTuple("NT2", [("c", int)]) + class NT3(NT1): + pass + class TD(TypedDict): + c: int + TD2 = TypedDict("TD2", {"c": int}) + class E(Enum): + X = 1 + @dataclass + class DC: + c: int + Alias: TypeAlias = NT1 + N = NewType("N", NT1) + + c: C = C() + nt1: NT1 = NT1(c=1) + nt2: NT2 = NT2(c=1) + nt3: NT3 = NT3(c=1) + td: TD = TD(c=1) + td2: TD2 = TD2(c=1) + e: E = E.X + dc: DC = DC(c=1) + al: Alias = Alias(c=1) + n: N = N(NT1(c=1)) + +[builtins fixtures/dict.pyi] +[out2] +tmp/a.py:2: error: "object" has no attribute "xyz" From 3cd1e2c4d9213793adff1973c4b68f473154df21 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 18 May 2022 13:03:32 +0100 Subject: [PATCH 70/79] Fix namedtuple crash in unannotated function (#12804) Fixes #11121. --- mypy/semanal_namedtuple.py | 11 +++++++--- test-data/unit/check-incremental.test | 29 +++++++++++++++++++++++++++ test-data/unit/check-namedtuple.test | 14 +++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 07863dea2efb..109ec17cbc89 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -186,11 +186,16 @@ def check_namedtuple(self, # Error. Construct dummy return value. if var_name: name = var_name + if is_func_scope: + name += '@' + str(call.line) else: - name = 'namedtuple@' + str(call.line) + name = var_name = 'namedtuple@' + str(call.line) info = self.build_namedtuple_typeinfo(name, [], [], {}, node.line) - self.store_namedtuple_info(info, name, call, is_typed) - return name, info + self.store_namedtuple_info(info, var_name, call, is_typed) + if name != var_name or is_func_scope: + # NOTE: we skip local namespaces since they are not serialized. + self.api.add_symbol_skip_local(name, info) + return var_name, info if not ok: # This is a valid named tuple but some types are not ready. return typename, None diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index c604b386691b..79fa1c92c52e 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5711,3 +5711,32 @@ class C: [builtins fixtures/dict.pyi] [out2] tmp/a.py:2: error: "object" has no attribute "xyz" + +[case testIncrementalInvalidNamedTupleInUnannotatedFunction] +import a + +[file a.py] +import b + +[file a.py.2] +import b # f + +[file b.py] +from typing import NamedTuple + +def toplevel(fields): + TupleType = NamedTuple("TupleType", fields) + class InheritFromTuple(TupleType): + pass + NT2 = NamedTuple("bad", [('x', int)]) + nt2: NT2 = NT2(x=1) + +class C: + def method(self, fields): + TupleType = NamedTuple("TupleType", fields) + class InheritFromTuple(TupleType): + pass + NT2 = NamedTuple("bad", [('x', int)]) + nt2: NT2 = NT2(x=1) + +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index b95cc96f8115..c6f1fe3b1d04 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1120,3 +1120,17 @@ def bar1(c: C1) -> None: reveal_type(c) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.C1]" [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] + +[case testInvalidNamedTupleWithinFunction] +from collections import namedtuple + +def f(fields) -> None: + TupleType = namedtuple("TupleType", fields) \ + # E: List or tuple literal expected as the second argument to "namedtuple()" + class InheritFromTuple(TupleType): + pass + t: TupleType + it: InheritFromTuple + NT2 = namedtuple("bad", "x") # E: First argument to namedtuple() should be "NT2", not "bad" + nt2: NT2 = NT2(x=1) +[builtins fixtures/tuple.pyi] From cb2c07bd1b0cb8ff3dbf38d0ee2092ebc1c1162f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 20 May 2022 14:27:54 +0100 Subject: [PATCH 71/79] Fix crash on type alias definition inside dataclass declaration (#12792) Skip processing a type alias node and generate an error. Fixes #12544. --- mypy/plugins/dataclasses.py | 16 ++++++++++++++- test-data/unit/check-dataclasses.test | 28 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 24077bb4a549..00c46e1417c5 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -5,7 +5,7 @@ from mypy.nodes import ( ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_POS, ARG_STAR, ARG_STAR2, MDEF, - Argument, AssignmentStmt, CallExpr, Context, Expression, JsonDict, + Argument, AssignmentStmt, CallExpr, TypeAlias, Context, Expression, JsonDict, NameExpr, RefExpr, SymbolTableNode, TempNode, TypeInfo, Var, TypeVarExpr, PlaceholderNode ) @@ -333,6 +333,20 @@ def collect_attributes(self) -> Optional[List[DataclassAttribute]]: node = sym.node assert not isinstance(node, PlaceholderNode) + + if isinstance(node, TypeAlias): + ctx.api.fail( + ( + 'Type aliases inside dataclass definitions ' + 'are not supported at runtime' + ), + node + ) + # Skip processing this node. This doesn't match the runtime behaviour, + # but the only alternative would be to modify the SymbolTable, + # and it's a little hairy to do that in a plugin. + continue + assert isinstance(node, Var) # x: ClassVar[int] is ignored by dataclasses. diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index abfcb79c0cc5..972cc4d40a1e 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -526,6 +526,34 @@ Application.COUNTER = 1 [builtins fixtures/dataclasses.pyi] +[case testTypeAliasInDataclassDoesNotCrash] +# flags: --python-version 3.7 +from dataclasses import dataclass +from typing import Callable +from typing_extensions import TypeAlias + +@dataclass +class Foo: + x: int + +@dataclass +class One: + S: TypeAlias = Foo # E: Type aliases inside dataclass definitions are not supported at runtime + +a = One() +reveal_type(a.S) # N: Revealed type is "def (x: builtins.int) -> __main__.Foo" +a.S() # E: Missing positional argument "x" in call to "Foo" +reveal_type(a.S(5)) # N: Revealed type is "__main__.Foo" + +@dataclass +class Two: + S: TypeAlias = Callable[[int], str] # E: Type aliases inside dataclass definitions are not supported at runtime + +c = Two() +x = c.S # E: Member "S" is not assignable +reveal_type(x) # N: Revealed type is "Any" +[builtins fixtures/dataclasses.pyi] + [case testDataclassOrdering] # flags: --python-version 3.7 from dataclasses import dataclass From 6db3d96cf28879dc9018d5d8bcd45d30ba9b6817 Mon Sep 17 00:00:00 2001 From: pranavrajpal <78008260+pranavrajpal@users.noreply.github.com> Date: Thu, 19 May 2022 08:04:06 -0700 Subject: [PATCH 72/79] Avoid crashing on invalid python executables (#12812) When an invalid python executable is passed to mypy, show an error message instead of crashing. --- mypy/modulefinder.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index bee99156a570..4ab95dd6564f 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -12,6 +12,8 @@ import sys from enum import Enum, unique +from mypy.errors import CompileError + if sys.version_info >= (3, 11): import tomllib else: @@ -649,9 +651,15 @@ def get_site_packages_dirs(python_executable: Optional[str]) -> Tuple[List[str], else: # Use subprocess to get the package directory of given Python # executable - site_packages = ast.literal_eval( - subprocess.check_output([python_executable, pyinfo.__file__, 'getsitepackages'], - stderr=subprocess.PIPE).decode()) + try: + site_packages = ast.literal_eval( + subprocess.check_output([python_executable, pyinfo.__file__, 'getsitepackages'], + stderr=subprocess.PIPE).decode()) + except OSError as err: + reason = os.strerror(err.errno) + raise CompileError( + [f"mypy: Invalid python executable '{python_executable}': {reason}"] + ) from err return expand_site_packages(site_packages) From b673366df0b0ee859abe0de3e2e82d99203fcd84 Mon Sep 17 00:00:00 2001 From: Hugues Date: Fri, 20 May 2022 08:37:23 -0700 Subject: [PATCH 73/79] speedup typechecking of nested if expressions (#12700) Deeply nested if/else expressions have a worst-case exponential behavior. This will for instance manifest when returning literal values which cause repeated analysis of conditional branches with subtly different type context for each literal. This can be optimized by observing that a simple literal context will yield the same analysis as its fallback type, and likewise, two literals of the same fallback type will yield the same analysis. In those case we can avoid the repeated analysis and prevent the worst-case exponential behavior. Fixes #9591 --- mypy/checkexpr.py | 25 +++++++++++++++++++++---- mypy/typeops.py | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index bfbe961adc7a..0149f1971477 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -69,7 +69,7 @@ try_expanding_sum_type_to_union, tuple_fallback, make_simplified_union, true_only, false_only, erase_to_union_or_bound, function_type, callable_type, try_getting_str_literals, custom_special_method, - is_literal_type_like, + is_literal_type_like, simple_literal_type, ) from mypy.message_registry import ErrorMessage import mypy.errorcodes as codes @@ -3874,26 +3874,43 @@ def visit_conditional_expr(self, e: ConditionalExpr, allow_none_return: bool = F if_type = self.analyze_cond_branch(if_map, e.if_expr, context=ctx, allow_none_return=allow_none_return) + # we want to keep the narrowest value of if_type for union'ing the branches + # however, it would be silly to pass a literal as a type context. Pass the + # underlying fallback type instead. + if_type_fallback = simple_literal_type(get_proper_type(if_type)) or if_type + # Analyze the right branch using full type context and store the type full_context_else_type = self.analyze_cond_branch(else_map, e.else_expr, context=ctx, allow_none_return=allow_none_return) + if not mypy.checker.is_valid_inferred_type(if_type): # Analyze the right branch disregarding the left branch. else_type = full_context_else_type + # we want to keep the narrowest value of else_type for union'ing the branches + # however, it would be silly to pass a literal as a type context. Pass the + # underlying fallback type instead. + else_type_fallback = simple_literal_type(get_proper_type(else_type)) or else_type # If it would make a difference, re-analyze the left # branch using the right branch's type as context. - if ctx is None or not is_equivalent(else_type, ctx): + if ctx is None or not is_equivalent(else_type_fallback, ctx): # TODO: If it's possible that the previous analysis of # the left branch produced errors that are avoided # using this context, suppress those errors. - if_type = self.analyze_cond_branch(if_map, e.if_expr, context=else_type, + if_type = self.analyze_cond_branch(if_map, e.if_expr, context=else_type_fallback, allow_none_return=allow_none_return) + elif if_type_fallback == ctx: + # There is no point re-running the analysis if if_type is equal to ctx. + # That would be an exact duplicate of the work we just did. + # This optimization is particularly important to avoid exponential blowup with nested + # if/else expressions: https://github.com/python/mypy/issues/9591 + # TODO: would checking for is_proper_subtype also work and cover more cases? + else_type = full_context_else_type else: # Analyze the right branch in the context of the left # branch's type. - else_type = self.analyze_cond_branch(else_map, e.else_expr, context=if_type, + else_type = self.analyze_cond_branch(else_map, e.else_expr, context=if_type_fallback, allow_none_return=allow_none_return) # Only create a union type if the type context is a union, to be mostly diff --git a/mypy/typeops.py b/mypy/typeops.py index e2e44b915c0c..32ec5b6e7114 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -318,7 +318,7 @@ def simple_literal_value_key(t: ProperType) -> Optional[Tuple[str, ...]]: return None -def simple_literal_type(t: ProperType) -> Optional[Instance]: +def simple_literal_type(t: Optional[ProperType]) -> Optional[Instance]: """Extract the underlying fallback Instance type for a simple Literal""" if isinstance(t, Instance) and t.last_known_value is not None: t = t.last_known_value From 644d5f6f2f503f115e6656336ad266a7e03fd79d Mon Sep 17 00:00:00 2001 From: Hugues Date: Fri, 20 May 2022 08:49:52 -0700 Subject: [PATCH 74/79] checkexpr: cache type of container literals when possible (#12707) When a container (list, set, tuple, or dict) literal expression is used as an argument to an overloaded function it will get repeatedly typechecked. This becomes particularly problematic when the expression is somewhat large, as seen in #9427 To avoid repeated work, add a new cache in ExprChecker, mapping the AST node to the resolved type of the expression. Right now the cache is only used in the fast path, although it could conceivably be leveraged for the slow path as well in a follow-up commit. To further reduce duplicate work, when the fast-path doesn't work, we use the cache to make a note of that, to avoid repeatedly attempting to take the fast path. Fixes #9427 --- mypy/checker.py | 1 + mypy/checkexpr.py | 49 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 109a3b1f15d2..e5abcfcf4541 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -293,6 +293,7 @@ def reset(self) -> None: self._type_maps[1:] = [] self._type_maps[0].clear() self.temp_type_map = None + self.expr_checker.reset() assert self.inferred_attribute_types is None assert self.partial_types == [] diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0149f1971477..4cc91f9cc123 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -177,6 +177,9 @@ class ExpressionChecker(ExpressionVisitor[Type]): # Type context for type inference type_context: List[Optional[Type]] + # cache resolved types in some cases + resolved_type: Dict[Expression, ProperType] + strfrm_checker: StringFormatterChecker plugin: Plugin @@ -197,6 +200,11 @@ def __init__(self, self.type_overrides: Dict[Expression, Type] = {} self.strfrm_checker = StringFormatterChecker(self, self.chk, self.msg) + self.resolved_type = {} + + def reset(self) -> None: + self.resolved_type = {} + def visit_name_expr(self, e: NameExpr) -> Type: """Type check a name expression. @@ -3269,13 +3277,13 @@ def apply_type_arguments_to_callable( def visit_list_expr(self, e: ListExpr) -> Type: """Type check a list expression [...].""" - return self.check_lst_expr(e.items, 'builtins.list', '', e) + return self.check_lst_expr(e, 'builtins.list', '') def visit_set_expr(self, e: SetExpr) -> Type: - return self.check_lst_expr(e.items, 'builtins.set', '', e) + return self.check_lst_expr(e, 'builtins.set', '') def fast_container_type( - self, items: List[Expression], container_fullname: str + self, e: Union[ListExpr, SetExpr, TupleExpr], container_fullname: str ) -> Optional[Type]: """ Fast path to determine the type of a list or set literal, @@ -3290,21 +3298,28 @@ def fast_container_type( ctx = self.type_context[-1] if ctx: return None + rt = self.resolved_type.get(e, None) + if rt is not None: + return rt if isinstance(rt, Instance) else None values: List[Type] = [] - for item in items: + for item in e.items: if isinstance(item, StarExpr): # fallback to slow path + self.resolved_type[e] = NoneType() return None values.append(self.accept(item)) vt = join.join_type_list(values) if not allow_fast_container_literal(vt): + self.resolved_type[e] = NoneType() return None - return self.chk.named_generic_type(container_fullname, [vt]) + ct = self.chk.named_generic_type(container_fullname, [vt]) + self.resolved_type[e] = ct + return ct - def check_lst_expr(self, items: List[Expression], fullname: str, - tag: str, context: Context) -> Type: + def check_lst_expr(self, e: Union[ListExpr, SetExpr, TupleExpr], fullname: str, + tag: str) -> Type: # fast path - t = self.fast_container_type(items, fullname) + t = self.fast_container_type(e, fullname) if t: return t @@ -3323,10 +3338,10 @@ def check_lst_expr(self, items: List[Expression], fullname: str, variables=[tv]) out = self.check_call(constructor, [(i.expr if isinstance(i, StarExpr) else i) - for i in items], + for i in e.items], [(nodes.ARG_STAR if isinstance(i, StarExpr) else nodes.ARG_POS) - for i in items], - context)[0] + for i in e.items], + e)[0] return remove_instance_last_known_values(out) def visit_tuple_expr(self, e: TupleExpr) -> Type: @@ -3376,7 +3391,7 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type: else: # A star expression that's not a Tuple. # Treat the whole thing as a variable-length tuple. - return self.check_lst_expr(e.items, 'builtins.tuple', '', e) + return self.check_lst_expr(e, 'builtins.tuple', '') else: if not type_context_items or j >= len(type_context_items): tt = self.accept(item) @@ -3402,6 +3417,9 @@ def fast_dict_type(self, e: DictExpr) -> Optional[Type]: ctx = self.type_context[-1] if ctx: return None + rt = self.resolved_type.get(e, None) + if rt is not None: + return rt if isinstance(rt, Instance) else None keys: List[Type] = [] values: List[Type] = [] stargs: Optional[Tuple[Type, Type]] = None @@ -3415,6 +3433,7 @@ def fast_dict_type(self, e: DictExpr) -> Optional[Type]: ): stargs = (st.args[0], st.args[1]) else: + self.resolved_type[e] = NoneType() return None else: keys.append(self.accept(key)) @@ -3422,10 +3441,14 @@ def fast_dict_type(self, e: DictExpr) -> Optional[Type]: kt = join.join_type_list(keys) vt = join.join_type_list(values) if not (allow_fast_container_literal(kt) and allow_fast_container_literal(vt)): + self.resolved_type[e] = NoneType() return None if stargs and (stargs[0] != kt or stargs[1] != vt): + self.resolved_type[e] = NoneType() return None - return self.chk.named_generic_type('builtins.dict', [kt, vt]) + dt = self.chk.named_generic_type('builtins.dict', [kt, vt]) + self.resolved_type[e] = dt + return dt def visit_dict_expr(self, e: DictExpr) -> Type: """Type check a dict expression. From aa7c21a363271caed276c61899a7cb59bd2af49e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 20 May 2022 16:50:51 +0100 Subject: [PATCH 75/79] Typeshed cherry-pick: Ignore mypy errors in Python 2 builtins and typing (#7894) (#12826) From python/typeshed#7894. --- mypy/typeshed/stdlib/@python2/__builtin__.pyi | 4 ++-- mypy/typeshed/stdlib/@python2/builtins.pyi | 4 ++-- mypy/typeshed/stdlib/@python2/typing.pyi | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mypy/typeshed/stdlib/@python2/__builtin__.pyi b/mypy/typeshed/stdlib/@python2/__builtin__.pyi index d936e08b8266..4ede9dc9d8bd 100644 --- a/mypy/typeshed/stdlib/@python2/__builtin__.pyi +++ b/mypy/typeshed/stdlib/@python2/__builtin__.pyi @@ -711,7 +711,7 @@ class set(MutableSet[_T], Generic[_T]): def __and__(self, s: AbstractSet[object]) -> set[_T]: ... def __iand__(self: Self, s: AbstractSet[object]) -> Self: ... def __or__(self, s: AbstractSet[_S]) -> set[_T | _S]: ... - def __ior__(self: Self, s: AbstractSet[_T]) -> Self: ... + def __ior__(self: Self, s: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] @overload def __sub__(self: set[str], s: AbstractSet[Text | None]) -> set[_T]: ... @overload @@ -721,7 +721,7 @@ class set(MutableSet[_T], Generic[_T]): @overload def __isub__(self, s: AbstractSet[_T | None]) -> set[_T]: ... def __xor__(self, s: AbstractSet[_S]) -> set[_T | _S]: ... - def __ixor__(self: Self, s: AbstractSet[_T]) -> Self: ... + def __ixor__(self: Self, s: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] def __le__(self, s: AbstractSet[object]) -> bool: ... def __lt__(self, s: AbstractSet[object]) -> bool: ... def __ge__(self, s: AbstractSet[object]) -> bool: ... diff --git a/mypy/typeshed/stdlib/@python2/builtins.pyi b/mypy/typeshed/stdlib/@python2/builtins.pyi index d936e08b8266..4ede9dc9d8bd 100644 --- a/mypy/typeshed/stdlib/@python2/builtins.pyi +++ b/mypy/typeshed/stdlib/@python2/builtins.pyi @@ -711,7 +711,7 @@ class set(MutableSet[_T], Generic[_T]): def __and__(self, s: AbstractSet[object]) -> set[_T]: ... def __iand__(self: Self, s: AbstractSet[object]) -> Self: ... def __or__(self, s: AbstractSet[_S]) -> set[_T | _S]: ... - def __ior__(self: Self, s: AbstractSet[_T]) -> Self: ... + def __ior__(self: Self, s: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] @overload def __sub__(self: set[str], s: AbstractSet[Text | None]) -> set[_T]: ... @overload @@ -721,7 +721,7 @@ class set(MutableSet[_T], Generic[_T]): @overload def __isub__(self, s: AbstractSet[_T | None]) -> set[_T]: ... def __xor__(self, s: AbstractSet[_S]) -> set[_T | _S]: ... - def __ixor__(self: Self, s: AbstractSet[_T]) -> Self: ... + def __ixor__(self: Self, s: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] def __le__(self, s: AbstractSet[object]) -> bool: ... def __lt__(self, s: AbstractSet[object]) -> bool: ... def __ge__(self, s: AbstractSet[object]) -> bool: ... diff --git a/mypy/typeshed/stdlib/@python2/typing.pyi b/mypy/typeshed/stdlib/@python2/typing.pyi index affa82c825a2..d1c9ae574e98 100644 --- a/mypy/typeshed/stdlib/@python2/typing.pyi +++ b/mypy/typeshed/stdlib/@python2/typing.pyi @@ -239,9 +239,9 @@ class MutableSet(AbstractSet[_T], Generic[_T]): def clear(self) -> None: ... def pop(self) -> _T: ... def remove(self, element: _T) -> None: ... - def __ior__(self: Self, s: AbstractSet[_T]) -> Self: ... + def __ior__(self: Self, s: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] def __iand__(self: Self, s: AbstractSet[Any]) -> Self: ... - def __ixor__(self: Self, s: AbstractSet[_T]) -> Self: ... + def __ixor__(self: Self, s: AbstractSet[_T]) -> Self: ... # type: ignore[override,misc] def __isub__(self: Self, s: AbstractSet[Any]) -> Self: ... class MappingView(object): From 290f013dc11a955ff2d87e3610d873c0fef71e55 Mon Sep 17 00:00:00 2001 From: Hugues Date: Fri, 20 May 2022 08:56:30 -0700 Subject: [PATCH 76/79] FindModuleCache: optionally leverage BuildSourceSet (#12616) Given a large codebase with folder hierarchy of the form ``` foo/ company/ __init__.py foo/ bar/ company/ __init__.py bar/ baz/ company/ __init__.py baz/ ... ``` with >100 toplevel folders, the time spent in load_graph is dominated by find_module because this operation is itself O(n) where n is the number of input files, which ends up being O(n**2) because it is called for every import statement in the codebase and the way find_module work, it will always scan through each and every one of those toplevel directories for each and every import statement of company.* Introduce a fast path that leverages the fact that for imports within the code being typechecked, we already have a mapping of module import path to file path in BuildSourceSet Gated behind a command line flag (--fast-module-lookup) to assuage concerns about subtle issues in module lookup being introduced by this fast path. --- docs/source/command_line.rst | 23 +++++ docs/source/running_mypy.rst | 1 + mypy/build.py | 34 +------ mypy/main.py | 4 + mypy/modulefinder.py | 114 ++++++++++++++++++++- mypy/options.py | 2 + mypy/test/testcheck.py | 1 + test-data/unit/check-modules-fast.test | 136 +++++++++++++++++++++++++ 8 files changed, 283 insertions(+), 32 deletions(-) create mode 100644 test-data/unit/check-modules-fast.test diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 1a35d81a7ee9..908fa799da46 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -212,6 +212,29 @@ imports. By default, mypy will suppress any error messages generated within :pep:`561` compliant packages. Adding this flag will disable this behavior. +.. option:: --fast-module-lookup + + The default logic used to scan through search paths to resolve imports has a + quadratic worse-case behavior in some cases, which is for instance triggered + by a large number of folders sharing a top-level namespace as in: + + foo/ + company/ + foo/ + a.py + bar/ + company/ + bar/ + b.py + baz/ + company/ + baz/ + c.py + ... + + If you are in this situation, you can enable an experimental fast path by + setting the :option:`--fast-module-lookup` option. + .. _platform-configuration: diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 46ad2c65c386..caf05dcdf258 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -516,6 +516,7 @@ same directory on the search path, only the stub file is used. (However, if the files are in different directories, the one found in the earlier directory is used.) + Other advice and best practices ******************************* diff --git a/mypy/build.py b/mypy/build.py index c0b9aff5ab32..1196356d5bb8 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -43,8 +43,8 @@ from mypy.report import Reports # Avoid unconditional slow import from mypy.fixup import fixup_module from mypy.modulefinder import ( - BuildSource, compute_search_paths, FindModuleCache, SearchPaths, ModuleSearchResult, - ModuleNotFoundReason + BuildSource, BuildSourceSet, compute_search_paths, FindModuleCache, SearchPaths, + ModuleSearchResult, ModuleNotFoundReason ) from mypy.nodes import Expression from mypy.options import Options @@ -107,33 +107,6 @@ def __init__(self, manager: 'BuildManager', graph: Graph) -> None: self.errors: List[str] = [] # Filled in by build if desired -class BuildSourceSet: - """Efficiently test a file's membership in the set of build sources.""" - - def __init__(self, sources: List[BuildSource]) -> None: - self.source_text_present = False - self.source_modules: Set[str] = set() - self.source_paths: Set[str] = set() - - for source in sources: - if source.text is not None: - self.source_text_present = True - elif source.path: - self.source_paths.add(source.path) - else: - self.source_modules.add(source.module) - - def is_source(self, file: MypyFile) -> bool: - if file.path and file.path in self.source_paths: - return True - elif file._fullname in self.source_modules: - return True - elif self.source_text_present: - return True - else: - return False - - def build(sources: List[BuildSource], options: Options, alt_lib_path: Optional[str] = None, @@ -630,7 +603,8 @@ def __init__(self, data_dir: str, or options.use_fine_grained_cache) and not has_reporters) self.fscache = fscache - self.find_module_cache = FindModuleCache(self.search_paths, self.fscache, self.options) + self.find_module_cache = FindModuleCache(self.search_paths, self.fscache, self.options, + source_set=self.source_set) self.metastore = create_metastore(options) # a mapping from source files to their corresponding shadow files diff --git a/mypy/main.py b/mypy/main.py index c735bb389a35..57727821274e 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -881,6 +881,10 @@ def add_invertible_flag(flag: str, '--explicit-package-bases', default=False, help="Use current directory and MYPYPATH to determine module names of files passed", group=code_group) + add_invertible_flag( + '--fast-module-lookup', default=False, + help=argparse.SUPPRESS, + group=code_group) code_group.add_argument( "--exclude", action="append", diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 4ab95dd6564f..43cc4fc0a6d3 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -23,6 +23,7 @@ from typing_extensions import Final, TypeAlias as _TypeAlias from mypy.fscache import FileSystemCache +from mypy.nodes import MypyFile from mypy.options import Options from mypy.stubinfo import is_legacy_bundled_package from mypy import pyinfo @@ -126,6 +127,33 @@ def __repr__(self) -> str: self.base_dir) +class BuildSourceSet: + """Helper to efficiently test a file's membership in a set of build sources.""" + + def __init__(self, sources: List[BuildSource]) -> None: + self.source_text_present = False + self.source_modules = {} # type: Dict[str, str] + self.source_paths = set() # type: Set[str] + + for source in sources: + if source.text is not None: + self.source_text_present = True + if source.path: + self.source_paths.add(source.path) + if source.module: + self.source_modules[source.module] = source.path or '' + + def is_source(self, file: MypyFile) -> bool: + if file.path and file.path in self.source_paths: + return True + elif file._fullname in self.source_modules: + return True + elif self.source_text_present: + return True + else: + return False + + class FindModuleCache: """Module finder with integrated cache. @@ -141,8 +169,10 @@ def __init__(self, search_paths: SearchPaths, fscache: Optional[FileSystemCache], options: Optional[Options], - stdlib_py_versions: Optional[StdlibVersions] = None) -> None: + stdlib_py_versions: Optional[StdlibVersions] = None, + source_set: Optional[BuildSourceSet] = None) -> None: self.search_paths = search_paths + self.source_set = source_set self.fscache = fscache or FileSystemCache() # Cache for get_toplevel_possibilities: # search_paths -> (toplevel_id -> list(package_dirs)) @@ -164,6 +194,53 @@ def clear(self) -> None: self.initial_components.clear() self.ns_ancestors.clear() + def find_module_via_source_set(self, id: str) -> Optional[ModuleSearchResult]: + """Fast path to find modules by looking through the input sources + + This is only used when --fast-module-lookup is passed on the command line.""" + if not self.source_set: + return None + + p = self.source_set.source_modules.get(id, None) + if p and self.fscache.isfile(p): + # We need to make sure we still have __init__.py all the way up + # otherwise we might have false positives compared to slow path + # in case of deletion of init files, which is covered by some tests. + # TODO: are there some combination of flags in which this check should be skipped? + d = os.path.dirname(p) + for _ in range(id.count('.')): + if not any(self.fscache.isfile(os.path.join(d, '__init__' + x)) + for x in PYTHON_EXTENSIONS): + return None + d = os.path.dirname(d) + return p + + idx = id.rfind('.') + if idx != -1: + # When we're looking for foo.bar.baz and can't find a matching module + # in the source set, look up for a foo.bar module. + parent = self.find_module_via_source_set(id[:idx]) + if parent is None or not isinstance(parent, str): + return None + + basename, ext = os.path.splitext(parent) + if (not any(parent.endswith('__init__' + x) for x in PYTHON_EXTENSIONS) + and (ext in PYTHON_EXTENSIONS and not self.fscache.isdir(basename))): + # If we do find such a *module* (and crucially, we don't want a package, + # hence the filtering out of __init__ files, and checking for the presence + # of a folder with a matching name), then we can be pretty confident that + # 'baz' will either be a top-level variable in foo.bar, or will not exist. + # + # Either way, spelunking in other search paths for another 'foo.bar.baz' + # module should be avoided because: + # 1. in the unlikely event that one were found, it's highly likely that + # it would be unrelated to the source being typechecked and therefore + # more likely to lead to erroneous results + # 2. as described in _find_module, in some cases the search itself could + # potentially waste significant amounts of time + return ModuleNotFoundReason.NOT_FOUND + return None + def find_lib_path_dirs(self, id: str, lib_path: Tuple[str, ...]) -> PackageDirs: """Find which elements of a lib_path have the directory a module needs to exist. @@ -229,7 +306,7 @@ def find_module(self, id: str, *, fast_path: bool = False) -> ModuleSearchResult elif top_level in self.stdlib_py_versions: use_typeshed = self._typeshed_has_version(top_level) self.results[id] = self._find_module(id, use_typeshed) - if (not fast_path + if (not (fast_path or (self.options is not None and self.options.fast_module_lookup)) and self.results[id] is ModuleNotFoundReason.NOT_FOUND and self._can_find_module_in_parent_dir(id)): self.results[id] = ModuleNotFoundReason.WRONG_WORKING_DIRECTORY @@ -295,6 +372,39 @@ def _can_find_module_in_parent_dir(self, id: str) -> bool: def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: fscache = self.fscache + # Fast path for any modules in the current source set. + # This is particularly important when there are a large number of search + # paths which share the first (few) component(s) due to the use of namespace + # packages, for instance: + # foo/ + # company/ + # __init__.py + # foo/ + # bar/ + # company/ + # __init__.py + # bar/ + # baz/ + # company/ + # __init__.py + # baz/ + # + # mypy gets [foo/company/foo, bar/company/bar, baz/company/baz, ...] as input + # and computes [foo, bar, baz, ...] as the module search path. + # + # This would result in O(n) search for every import of company.*, leading to + # O(n**2) behavior in load_graph as such imports are unsurprisingly present + # at least once, and usually many more times than that, in each and every file + # being parsed. + # + # Thankfully, such cases are efficiently handled by looking up the module path + # via BuildSourceSet. + p = (self.find_module_via_source_set(id) + if (self.options is not None and self.options.fast_module_lookup) + else None) + if p: + return p + # If we're looking for a module like 'foo.bar.baz', it's likely that most of the # many elements of lib_path don't even have a subdirectory 'foo/bar'. Discover # that only once and cache it for when we look for modules like 'foo.bar.blah' diff --git a/mypy/options.py b/mypy/options.py index b8bc53feb89c..254af61a0645 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -293,6 +293,8 @@ def __init__(self) -> None: self.cache_map: Dict[str, Tuple[str, str]] = {} # Don't properly free objects on exit, just kill the current process. self.fast_exit = True + # fast path for finding modules from source set + self.fast_module_lookup = False # Used to transform source code before parsing if not None # TODO: Make the type precise (AnyStr -> AnyStr) self.transform_source: Optional[Callable[[Any], Any]] = None diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index f0dc4bc6a671..7b995b9cc86b 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -40,6 +40,7 @@ 'check-multiple-inheritance.test', 'check-super.test', 'check-modules.test', + 'check-modules-fast.test', 'check-typevar-values.test', 'check-unsupported.test', 'check-unreachable-code.test', diff --git a/test-data/unit/check-modules-fast.test b/test-data/unit/check-modules-fast.test new file mode 100644 index 000000000000..875125c6532b --- /dev/null +++ b/test-data/unit/check-modules-fast.test @@ -0,0 +1,136 @@ +-- Type checker test cases dealing with module lookup edge cases +-- to ensure that --fast-module-lookup matches regular lookup behavior + +[case testModuleLookup] +# flags: --fast-module-lookup +import m +reveal_type(m.a) # N: Revealed type is "m.A" + +[file m.py] +class A: pass +a = A() + +[case testModuleLookupStub] +# flags: --fast-module-lookup +import m +reveal_type(m.a) # N: Revealed type is "m.A" + +[file m.pyi] +class A: pass +a = A() + +[case testModuleLookupFromImport] +# flags: --fast-module-lookup +from m import a +reveal_type(a) # N: Revealed type is "m.A" + +[file m.py] +class A: pass +a = A() + +[case testModuleLookupStubFromImport] +# flags: --fast-module-lookup +from m import a +reveal_type(a) # N: Revealed type is "m.A" + +[file m.pyi] +class A: pass +a = A() + + +[case testModuleLookupWeird] +# flags: --fast-module-lookup +from m import a +reveal_type(a) # N: Revealed type is "builtins.object" +reveal_type(a.b) # N: Revealed type is "m.a.B" + +[file m.py] +class A: pass +a = A() + +[file m/__init__.py] +[file m/a.py] +class B: pass +b = B() + + +[case testModuleLookupWeird2] +# flags: --fast-module-lookup +from m.a import b +reveal_type(b) # N: Revealed type is "m.a.B" + +[file m.py] +class A: pass +a = A() + +[file m/__init__.py] +[file m/a.py] +class B: pass +b = B() + + +[case testModuleLookupWeird3] +# flags: --fast-module-lookup +from m.a import b +reveal_type(b) # N: Revealed type is "m.a.B" + +[file m.py] +class A: pass +a = A() +[file m/__init__.py] +class B: pass +a = B() +[file m/a.py] +class B: pass +b = B() + + +[case testModuleLookupWeird4] +# flags: --fast-module-lookup +import m.a +m.a.b # E: "str" has no attribute "b" + +[file m.py] +class A: pass +a = A() +[file m/__init__.py] +class B: pass +a = 'foo' +b = B() +[file m/a.py] +class C: pass +b = C() + + +[case testModuleLookupWeird5] +# flags: --fast-module-lookup +import m.a as ma +reveal_type(ma.b) # N: Revealed type is "m.a.C" + +[file m.py] +class A: pass +a = A() +[file m/__init__.py] +class B: pass +a = 'foo' +b = B() +[file m/a.py] +class C: pass +b = C() + + +[case testModuleLookupWeird6] +# flags: --fast-module-lookup +from m.a import b +reveal_type(b) # N: Revealed type is "m.a.C" + +[file m.py] +class A: pass +a = A() +[file m/__init__.py] +class B: pass +a = 'foo' +b = B() +[file m/a.py] +class C: pass +b = C() From a54c84d0b0be3676e791960cb474dea00ac7f3bf Mon Sep 17 00:00:00 2001 From: Fabian Keller Date: Sat, 21 May 2022 16:00:40 +0200 Subject: [PATCH 77/79] Bring back type annotation support of dunder methods in stub generator (#12828) Fixes #12717 --- mypy/stubgenc.py | 2 +- test-data/stubgen/pybind11_mypy_demo/basics.pyi | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index 682ed418ffc7..9f90c7aafe69 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -288,7 +288,7 @@ def infer_prop_type(docstr: Optional[str]) -> Optional[str]: return None # Ignore special properties/attributes. - if name.startswith('__') and name.endswith('__'): + if is_skipped_attribute(name): return inferred = infer_prop_type(getattr(obj, '__doc__', None)) diff --git a/test-data/stubgen/pybind11_mypy_demo/basics.pyi b/test-data/stubgen/pybind11_mypy_demo/basics.pyi index 99093fd6087a..d75d7e86a667 100644 --- a/test-data/stubgen/pybind11_mypy_demo/basics.pyi +++ b/test-data/stubgen/pybind11_mypy_demo/basics.pyi @@ -5,6 +5,7 @@ PI: float class Point: class AngleUnit: + __members__: ClassVar[dict] = ... # read-only __entries: ClassVar[dict] = ... degree: ClassVar[Point.AngleUnit] = ... radian: ClassVar[Point.AngleUnit] = ... @@ -20,6 +21,7 @@ class Point: def name(self) -> str: ... class LengthUnit: + __members__: ClassVar[dict] = ... # read-only __entries: ClassVar[dict] = ... inch: ClassVar[Point.LengthUnit] = ... mm: ClassVar[Point.LengthUnit] = ... From 128661ccbf4396ed1538421c481f1773c7490169 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 23 May 2022 09:18:55 -0700 Subject: [PATCH 78/79] Friendlier errors for PEP 612 (#12832) Co-authored-by: hauntsaninja <> --- mypy/typeanal.py | 14 +++++++++++--- test-data/unit/check-parameter-specification.test | 3 ++- test-data/unit/semanal-errors.test | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 98e37bd0aa40..bd0f684653b2 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -944,9 +944,13 @@ def analyze_callable_type(self, t: UnboundType) -> Type: ) if maybe_ret is None: # Callable[?, RET] (where ? is something invalid) - # TODO(PEP612): change error to mention paramspec, once we actually have some - # support for it - self.fail('The first argument to Callable must be a list of types or "..."', t) + self.fail( + 'The first argument to Callable must be a ' + 'list of types, parameter specification, or "..."', t) + self.note( + 'See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas', # noqa: E501 + t + ) return AnyType(TypeOfAny.from_error) ret = maybe_ret else: @@ -1180,6 +1184,10 @@ def anal_type(self, t: Type, nested: bool = True, *, allow_param_spec: bool = Fa and analyzed.flavor == ParamSpecFlavor.BARE): if analyzed.prefix.arg_types: self.fail('Invalid location for Concatenate', t) + self.note( + 'You can use Concatenate as the first argument to Callable', + t + ) else: self.fail(f'Invalid location for ParamSpec "{analyzed.name}"', t) self.note( diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index 28b08aa7122f..4dae32978263 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -25,7 +25,8 @@ def foo1(x: Callable[P, int]) -> Callable[P, str]: ... def foo2(x: P) -> P: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' -def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for Concatenate +def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for Concatenate \ + # N: You can use Concatenate as the first argument to Callable def foo4(x: List[P]) -> None: ... # E: Invalid location for ParamSpec "P" \ # N: You can use ParamSpec as the first argument to Callable, e.g., 'Callable[P, int]' diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 14bc24f03a7b..a1ff4ec1c3e7 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -867,7 +867,8 @@ x = None # type: Callable[int, str] y = None # type: Callable[int] z = None # type: Callable[int, int, int] [out] -main:2: error: The first argument to Callable must be a list of types or "..." +main:2: error: The first argument to Callable must be a list of types, parameter specification, or "..." +main:2: note: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas main:3: error: Please use "Callable[[], ]" or "Callable" main:4: error: Please use "Callable[[], ]" or "Callable" From 0a4a190f25ed90f73f6290656ec65ddd6bdb1d4e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 24 May 2022 14:03:35 +0100 Subject: [PATCH 79/79] Update version to 0.960 --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index c7b4e30f2420..587e881b413b 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -5,7 +5,7 @@ # - Release versions have the form "0.NNN". # - Dev versions have the form "0.NNN+dev" (PLUS sign to conform to PEP 440). # - For 1.0 we'll switch back to 1.2.3 form. -__version__ = '0.960+dev' +__version__ = '0.960' base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))