Skip to content

Commit 71166bc

Browse files
committed
Merge branch 'master' into fix-dataclass-init-only-and-property
2 parents 504089c + 5059ffd commit 71166bc

File tree

175 files changed

+5177
-844
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

175 files changed

+5177
-844
lines changed

docs/source/error_code_list.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,29 @@ annotations in an unchecked function:
11711171
Note that mypy will still exit with return code ``0``, since such behaviour is
11721172
specified by :pep:`484`.
11731173

1174+
.. _code-prop-decorator:
1175+
1176+
Decorator preceding property not supported [prop-decorator]
1177+
-----------------------------------------------------------
1178+
1179+
Mypy does not yet support analysis of decorators that precede the property
1180+
decorator. If the decorator does not preserve the declared type of the property,
1181+
mypy will not infer the correct type for the declaration. If the decorator cannot
1182+
be moved after the ``@property`` decorator, then you must use a type ignore
1183+
comment:
1184+
1185+
.. code-block:: python
1186+
1187+
class MyClass
1188+
@special # type: ignore[prop-decorator]
1189+
@property
1190+
def magic(self) -> str:
1191+
return "xyzzy"
1192+
1193+
.. note::
1194+
1195+
For backward compatibility, this error code is a subcode of the generic ``[misc]`` code.
1196+
11741197
.. _code-syntax:
11751198

11761199
Report syntax errors [syntax]

misc/sync-typeshed.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ def update_typeshed(typeshed_dir: str, commit: str | None) -> str:
5151
# Remove existing stubs.
5252
shutil.rmtree(stdlib_dir)
5353
# Copy new stdlib stubs.
54-
shutil.copytree(os.path.join(typeshed_dir, "stdlib"), stdlib_dir)
54+
shutil.copytree(
55+
os.path.join(typeshed_dir, "stdlib"), stdlib_dir, ignore=shutil.ignore_patterns("@tests")
56+
)
5557
shutil.copy(os.path.join(typeshed_dir, "LICENSE"), os.path.join("mypy", "typeshed"))
5658
return commit
5759

mypy/build.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
import mypy.semanal_main
4646
from mypy.checker import TypeChecker
47+
from mypy.error_formatter import OUTPUT_CHOICES, ErrorFormatter
4748
from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error
4849
from mypy.graph_utils import prepare_sccs, strongly_connected_components, topsort
4950
from mypy.indirection import TypeIndirectionVisitor
@@ -253,6 +254,7 @@ def _build(
253254
plugin=plugin,
254255
plugins_snapshot=snapshot,
255256
errors=errors,
257+
error_formatter=None if options.output is None else OUTPUT_CHOICES.get(options.output),
256258
flush_errors=flush_errors,
257259
fscache=fscache,
258260
stdout=stdout,
@@ -607,6 +609,7 @@ def __init__(
607609
fscache: FileSystemCache,
608610
stdout: TextIO,
609611
stderr: TextIO,
612+
error_formatter: ErrorFormatter | None = None,
610613
) -> None:
611614
self.stats: dict[str, Any] = {} # Values are ints or floats
612615
self.stdout = stdout
@@ -615,6 +618,7 @@ def __init__(
615618
self.data_dir = data_dir
616619
self.errors = errors
617620
self.errors.set_ignore_prefix(ignore_prefix)
621+
self.error_formatter = error_formatter
618622
self.search_paths = search_paths
619623
self.source_set = source_set
620624
self.reports = reports
@@ -3463,11 +3467,8 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No
34633467
for id in stale:
34643468
graph[id].transitive_error = True
34653469
for id in stale:
3466-
manager.flush_errors(
3467-
manager.errors.simplify_path(graph[id].xpath),
3468-
manager.errors.file_messages(graph[id].xpath),
3469-
False,
3470-
)
3470+
errors = manager.errors.file_messages(graph[id].xpath, formatter=manager.error_formatter)
3471+
manager.flush_errors(manager.errors.simplify_path(graph[id].xpath), errors, False)
34713472
graph[id].write_cache()
34723473
graph[id].mark_as_rechecked()
34733474

mypy/checker.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from typing_extensions import TypeAlias as _TypeAlias
2727

2828
import mypy.checkexpr
29-
from mypy import errorcodes as codes, message_registry, nodes, operators
29+
from mypy import errorcodes as codes, join, message_registry, nodes, operators
3030
from mypy.binder import ConditionalTypeBinder, Frame, get_declaration
3131
from mypy.checkmember import (
3232
MemberContext,
@@ -146,6 +146,7 @@
146146
from mypy.state import state
147147
from mypy.subtypes import (
148148
find_member,
149+
infer_class_variances,
149150
is_callable_compatible,
150151
is_equivalent,
151152
is_more_precise,
@@ -680,6 +681,11 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab
680681
inner_type = get_proper_type(inner_type)
681682
outer_type: CallableType | None = None
682683
if inner_type is not None and not isinstance(inner_type, AnyType):
684+
if isinstance(inner_type, TypeType):
685+
if isinstance(inner_type.item, Instance):
686+
inner_type = expand_type_by_instance(
687+
type_object_type(inner_type.item.type, self.named_type), inner_type.item
688+
)
683689
if isinstance(inner_type, CallableType):
684690
outer_type = inner_type
685691
elif isinstance(inner_type, Instance):
@@ -698,6 +704,21 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab
698704
)
699705
if isinstance(inner_call, CallableType):
700706
outer_type = inner_call
707+
elif isinstance(inner_type, UnionType):
708+
union_type = make_simplified_union(inner_type.items)
709+
if isinstance(union_type, UnionType):
710+
items = []
711+
for item in union_type.items:
712+
callable_item = self.extract_callable_type(item, ctx)
713+
if callable_item is None:
714+
break
715+
items.append(callable_item)
716+
else:
717+
joined_type = get_proper_type(join.join_type_list(items))
718+
if isinstance(joined_type, CallableType):
719+
outer_type = joined_type
720+
else:
721+
return self.extract_callable_type(union_type, ctx)
701722
if outer_type is None:
702723
self.msg.not_callable(inner_type, ctx)
703724
return outer_type
@@ -1003,7 +1024,7 @@ def _visit_func_def(self, defn: FuncDef) -> None:
10031024
"""Type check a function definition."""
10041025
self.check_func_item(defn, name=defn.name)
10051026
if defn.info:
1006-
if not defn.is_dynamic() and not defn.is_overload and not defn.is_decorated:
1027+
if not defn.is_overload and not defn.is_decorated:
10071028
# If the definition is the implementation for an
10081029
# overload, the legality of the override has already
10091030
# been typechecked, and decorated methods will be
@@ -1912,9 +1933,17 @@ def check_method_override(
19121933
Return a list of base classes which contain an attribute with the method name.
19131934
"""
19141935
# Check against definitions in base classes.
1936+
check_override_compatibility = defn.name not in (
1937+
"__init__",
1938+
"__new__",
1939+
"__init_subclass__",
1940+
"__post_init__",
1941+
) and (self.options.check_untyped_defs or not defn.is_dynamic())
19151942
found_method_base_classes: list[TypeInfo] = []
19161943
for base in defn.info.mro[1:]:
1917-
result = self.check_method_or_accessor_override_for_base(defn, base)
1944+
result = self.check_method_or_accessor_override_for_base(
1945+
defn, base, check_override_compatibility
1946+
)
19181947
if result is None:
19191948
# Node was deferred, we will have another attempt later.
19201949
return None
@@ -1923,7 +1952,10 @@ def check_method_override(
19231952
return found_method_base_classes
19241953

19251954
def check_method_or_accessor_override_for_base(
1926-
self, defn: FuncDef | OverloadedFuncDef | Decorator, base: TypeInfo
1955+
self,
1956+
defn: FuncDef | OverloadedFuncDef | Decorator,
1957+
base: TypeInfo,
1958+
check_override_compatibility: bool,
19271959
) -> bool | None:
19281960
"""Check if method definition is compatible with a base class.
19291961
@@ -1944,10 +1976,8 @@ def check_method_or_accessor_override_for_base(
19441976
if defn.is_final:
19451977
self.check_if_final_var_override_writable(name, base_attr.node, defn)
19461978
found_base_method = True
1947-
1948-
# Check the type of override.
1949-
if name not in ("__init__", "__new__", "__init_subclass__", "__post_init__"):
1950-
# Check method override
1979+
if check_override_compatibility:
1980+
# Check compatibility of the override signature
19511981
# (__init__, __new__, __init_subclass__ are special).
19521982
if self.check_method_override_for_base_with_name(defn, name, base):
19531983
return None
@@ -2374,7 +2404,7 @@ def visit_class_def(self, defn: ClassDef) -> None:
23742404
self.allow_abstract_call = old_allow_abstract_call
23752405
# TODO: Apply the sig to the actual TypeInfo so we can handle decorators
23762406
# that completely swap out the type. (e.g. Callable[[Type[A]], Type[B]])
2377-
if typ.defn.type_vars:
2407+
if typ.defn.type_vars and typ.defn.type_args is None:
23782408
for base_inst in typ.bases:
23792409
for base_tvar, base_decl_tvar in zip(
23802410
base_inst.args, base_inst.type.defn.type_vars
@@ -2396,6 +2426,7 @@ def visit_class_def(self, defn: ClassDef) -> None:
23962426
self.check_protocol_variance(defn)
23972427
if not defn.has_incompatible_baseclass and defn.info.is_enum:
23982428
self.check_enum(defn)
2429+
infer_class_variances(defn.info)
23992430

24002431
def check_final_deletable(self, typ: TypeInfo) -> None:
24012432
# These checks are only for mypyc. Only perform some checks that are easier
@@ -2566,6 +2597,9 @@ def check_protocol_variance(self, defn: ClassDef) -> None:
25662597
if they are actually covariant/contravariant, since this may break
25672598
transitivity of subtyping, see PEP 544.
25682599
"""
2600+
if defn.type_args is not None:
2601+
# Using new-style syntax (PEP 695), so variance will be inferred
2602+
return
25692603
info = defn.info
25702604
object_type = Instance(info.mro[-1], [])
25712605
tvars = info.defn.type_vars
@@ -3412,8 +3446,8 @@ def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExp
34123446
if (
34133447
lv.node.final_unset_in_class
34143448
and not lv.node.final_set_in_init
3415-
and not self.is_stub
3416-
and # It is OK to skip initializer in stub files.
3449+
and not self.is_stub # It is OK to skip initializer in stub files.
3450+
and
34173451
# Avoid extra error messages, if there is no type in Final[...],
34183452
# then we already reported the error about missing r.h.s.
34193453
isinstance(s, AssignmentStmt)

mypy/checkexpr.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,14 +1229,14 @@ def apply_function_plugin(
12291229
assert callback is not None # Assume that caller ensures this
12301230
return callback(
12311231
FunctionContext(
1232-
formal_arg_types,
1233-
formal_arg_kinds,
1234-
callee.arg_names,
1235-
formal_arg_names,
1236-
callee.ret_type,
1237-
formal_arg_exprs,
1238-
context,
1239-
self.chk,
1232+
arg_types=formal_arg_types,
1233+
arg_kinds=formal_arg_kinds,
1234+
callee_arg_names=callee.arg_names,
1235+
arg_names=formal_arg_names,
1236+
default_return_type=callee.ret_type,
1237+
args=formal_arg_exprs,
1238+
context=context,
1239+
api=self.chk,
12401240
)
12411241
)
12421242
else:
@@ -1246,15 +1246,15 @@ def apply_function_plugin(
12461246
object_type = get_proper_type(object_type)
12471247
return method_callback(
12481248
MethodContext(
1249-
object_type,
1250-
formal_arg_types,
1251-
formal_arg_kinds,
1252-
callee.arg_names,
1253-
formal_arg_names,
1254-
callee.ret_type,
1255-
formal_arg_exprs,
1256-
context,
1257-
self.chk,
1249+
type=object_type,
1250+
arg_types=formal_arg_types,
1251+
arg_kinds=formal_arg_kinds,
1252+
callee_arg_names=callee.arg_names,
1253+
arg_names=formal_arg_names,
1254+
default_return_type=callee.ret_type,
1255+
args=formal_arg_exprs,
1256+
context=context,
1257+
api=self.chk,
12581258
)
12591259
)
12601260

@@ -5223,15 +5223,16 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
52235223
self.chk.return_types.append(AnyType(TypeOfAny.special_form))
52245224
# Type check everything in the body except for the final return
52255225
# statement (it can contain tuple unpacking before return).
5226-
with self.chk.scope.push_function(e):
5226+
with self.chk.binder.frame_context(
5227+
can_skip=True, fall_through=0
5228+
), self.chk.scope.push_function(e):
52275229
# Lambdas can have more than one element in body,
52285230
# when we add "fictional" AssigmentStatement nodes, like in:
52295231
# `lambda (a, b): a`
52305232
for stmt in e.body.body[:-1]:
52315233
stmt.accept(self.chk)
52325234
# Only type check the return expression, not the return statement.
5233-
# This is important as otherwise the following statements would be
5234-
# considered unreachable. There's no useful type context.
5235+
# There's no useful type context.
52355236
ret_type = self.accept(e.expr(), allow_none_return=True)
52365237
fallback = self.named_type("builtins.function")
52375238
self.chk.return_types.pop()
@@ -5243,7 +5244,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
52435244
self.chk.check_func_item(e, type_override=type_override)
52445245
if not self.chk.has_type(e.expr()):
52455246
# TODO: return expression must be accepted before exiting function scope.
5246-
self.accept(e.expr(), allow_none_return=True)
5247+
with self.chk.binder.frame_context(can_skip=True, fall_through=0):
5248+
self.accept(e.expr(), allow_none_return=True)
52475249
ret_type = self.chk.lookup_type(e.expr())
52485250
self.chk.return_types.pop()
52495251
return replace_callable_return_type(inferred_type, ret_type)

mypy/checkmember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
654654
analyze_descriptor_access(
655655
descriptor_type, mx.copy_modified(original_type=original_type)
656656
)
657-
for original_type in instance_type.items
657+
for original_type in instance_type.relevant_items()
658658
]
659659
)
660660
elif not isinstance(descriptor_type, Instance):

mypy/dmypy_server.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ def cmd_recheck(
383383
removals = set(remove)
384384
sources = [s for s in sources if s.path and s.path not in removals]
385385
if update:
386+
# Sort list of file updates by extension, so *.pyi files are first.
387+
update.sort(key=lambda f: os.path.splitext(f)[1], reverse=True)
388+
386389
known = {s.path for s in sources if s.path}
387390
added = [p for p in update if p not in known]
388391
try:

mypy/error_formatter.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Defines the different custom formats in which mypy can output."""
2+
3+
import json
4+
from abc import ABC, abstractmethod
5+
from typing import TYPE_CHECKING
6+
7+
if TYPE_CHECKING:
8+
from mypy.errors import MypyError
9+
10+
11+
class ErrorFormatter(ABC):
12+
"""Base class to define how errors are formatted before being printed."""
13+
14+
@abstractmethod
15+
def report_error(self, error: "MypyError") -> str:
16+
raise NotImplementedError
17+
18+
19+
class JSONFormatter(ErrorFormatter):
20+
"""Formatter for basic JSON output format."""
21+
22+
def report_error(self, error: "MypyError") -> str:
23+
"""Prints out the errors as simple, static JSON lines."""
24+
return json.dumps(
25+
{
26+
"file": error.file_path,
27+
"line": error.line,
28+
"column": error.column,
29+
"message": error.message,
30+
"hint": None if len(error.hints) == 0 else "\n".join(error.hints),
31+
"code": None if error.errorcode is None else error.errorcode.code,
32+
"severity": error.severity,
33+
}
34+
)
35+
36+
37+
OUTPUT_CHOICES = {"json": JSONFormatter()}

mypy/errorcodes.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ def __hash__(self) -> int:
262262
default_enabled=False,
263263
)
264264

265-
266265
# Syntax errors are often blocking.
267266
SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General")
268267

@@ -281,6 +280,13 @@ def __hash__(self) -> int:
281280
sub_code_of=MISC,
282281
)
283282

283+
PROPERTY_DECORATOR = ErrorCode(
284+
"prop-decorator",
285+
"Decorators on top of @property are not supported",
286+
"General",
287+
sub_code_of=MISC,
288+
)
289+
284290
NARROWED_TYPE_NOT_SUBTYPE: Final[ErrorCode] = ErrorCode(
285291
"narrowed-type-not-subtype",
286292
"Warn if a TypeIs function's narrowed type is not a subtype of the original type",

0 commit comments

Comments
 (0)