Skip to content

Commit 6447d48

Browse files
committed
Merge remote-tracking branch 'origin/master' into dont-check-generated-functions
2 parents 525280b + 379d59e commit 6447d48

26 files changed

+458
-138
lines changed

docs/source/command_line.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ in error messages.
787787
disable reporting most additional errors. The limit only applies
788788
if it seems likely that most of the remaining errors will not be
789789
useful or they may be overly noisy. If ``N`` is negative, there is
790-
no limit. The default limit is 200.
790+
no limit. The default limit is -1.
791791

792792
.. option:: --force-uppercase-builtins
793793

mypy/checker.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -5671,22 +5671,29 @@ def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeM
56715671
if node.arg_kinds[0] != nodes.ARG_POS:
56725672
# the first argument might be used as a kwarg
56735673
called_type = get_proper_type(self.lookup_type(node.callee))
5674-
assert isinstance(called_type, (CallableType, Overloaded))
5674+
5675+
# TODO: there are some more cases in check_call() to handle.
5676+
if isinstance(called_type, Instance):
5677+
call = find_member(
5678+
"__call__", called_type, called_type, is_operator=True
5679+
)
5680+
if call is not None:
5681+
called_type = get_proper_type(call)
56755682

56765683
# *assuming* the overloaded function is correct, there's a couple cases:
56775684
# 1) The first argument has different names, but is pos-only. We don't
56785685
# care about this case, the argument must be passed positionally.
56795686
# 2) The first argument allows keyword reference, therefore must be the
56805687
# same between overloads.
5681-
name = called_type.items[0].arg_names[0]
5682-
5683-
if name in node.arg_names:
5684-
idx = node.arg_names.index(name)
5685-
# we want the idx-th variable to be narrowed
5686-
expr = collapse_walrus(node.args[idx])
5687-
else:
5688-
self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node)
5689-
return {}, {}
5688+
if isinstance(called_type, (CallableType, Overloaded)):
5689+
name = called_type.items[0].arg_names[0]
5690+
if name in node.arg_names:
5691+
idx = node.arg_names.index(name)
5692+
# we want the idx-th variable to be narrowed
5693+
expr = collapse_walrus(node.args[idx])
5694+
else:
5695+
self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node)
5696+
return {}, {}
56905697
if literal(expr) == LITERAL_TYPE:
56915698
# Note: we wrap the target type, so that we can special case later.
56925699
# Namely, for isinstance() we use a normal meet, while TypeGuard is

mypy/checkexpr.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -6209,11 +6209,16 @@ class PolyTranslator(TypeTranslator):
62096209
See docstring for apply_poly() for details.
62106210
"""
62116211

6212-
def __init__(self, poly_tvars: Sequence[TypeVarLikeType]) -> None:
6212+
def __init__(
6213+
self,
6214+
poly_tvars: Iterable[TypeVarLikeType],
6215+
bound_tvars: frozenset[TypeVarLikeType] = frozenset(),
6216+
seen_aliases: frozenset[TypeInfo] = frozenset(),
6217+
) -> None:
62136218
self.poly_tvars = set(poly_tvars)
62146219
# This is a simplified version of TypeVarScope used during semantic analysis.
6215-
self.bound_tvars: set[TypeVarLikeType] = set()
6216-
self.seen_aliases: set[TypeInfo] = set()
6220+
self.bound_tvars = bound_tvars
6221+
self.seen_aliases = seen_aliases
62176222

62186223
def collect_vars(self, t: CallableType | Parameters) -> list[TypeVarLikeType]:
62196224
found_vars = []
@@ -6289,10 +6294,11 @@ def visit_instance(self, t: Instance) -> Type:
62896294
if t.args and t.type.is_protocol and t.type.protocol_members == ["__call__"]:
62906295
if t.type in self.seen_aliases:
62916296
raise PolyTranslationError()
6292-
self.seen_aliases.add(t.type)
62936297
call = find_member("__call__", t, t, is_operator=True)
62946298
assert call is not None
6295-
return call.accept(self)
6299+
return call.accept(
6300+
PolyTranslator(self.poly_tvars, self.bound_tvars, self.seen_aliases | {t.type})
6301+
)
62966302
return super().visit_instance(t)
62976303

62986304

mypy/constraints.py

+43-33
Original file line numberDiff line numberDiff line change
@@ -226,25 +226,22 @@ def infer_constraints_for_callable(
226226
actual_type = mapper.expand_actual_type(
227227
actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i]
228228
)
229-
if (
230-
param_spec
231-
and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2)
232-
and not incomplete_star_mapping
233-
):
229+
if param_spec and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2):
234230
# If actual arguments are mapped to ParamSpec type, we can't infer individual
235231
# constraints, instead store them and infer single constraint at the end.
236232
# It is impossible to map actual kind to formal kind, so use some heuristic.
237233
# This inference is used as a fallback, so relying on heuristic should be OK.
238-
param_spec_arg_types.append(
239-
mapper.expand_actual_type(
240-
actual_arg_type, arg_kinds[actual], None, arg_kinds[actual]
234+
if not incomplete_star_mapping:
235+
param_spec_arg_types.append(
236+
mapper.expand_actual_type(
237+
actual_arg_type, arg_kinds[actual], None, arg_kinds[actual]
238+
)
241239
)
242-
)
243-
actual_kind = arg_kinds[actual]
244-
param_spec_arg_kinds.append(
245-
ARG_POS if actual_kind not in (ARG_STAR, ARG_STAR2) else actual_kind
246-
)
247-
param_spec_arg_names.append(arg_names[actual] if arg_names else None)
240+
actual_kind = arg_kinds[actual]
241+
param_spec_arg_kinds.append(
242+
ARG_POS if actual_kind not in (ARG_STAR, ARG_STAR2) else actual_kind
243+
)
244+
param_spec_arg_names.append(arg_names[actual] if arg_names else None)
248245
else:
249246
c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF)
250247
constraints.extend(c)
@@ -267,6 +264,9 @@ def infer_constraints_for_callable(
267264
),
268265
)
269266
)
267+
if any(isinstance(v, ParamSpecType) for v in callee.variables):
268+
# As a perf optimization filter imprecise constraints only when we can have them.
269+
constraints = filter_imprecise_kinds(constraints)
270270
return constraints
271271

272272

@@ -1094,29 +1094,18 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]:
10941094
)
10951095

10961096
param_spec_target: Type | None = None
1097-
skip_imprecise = (
1098-
any(c.type_var == param_spec.id for c in res) and cactual.imprecise_arg_kinds
1099-
)
11001097
if not cactual_ps:
11011098
max_prefix_len = len([k for k in cactual.arg_kinds if k in (ARG_POS, ARG_OPT)])
11021099
prefix_len = min(prefix_len, max_prefix_len)
1103-
# This logic matches top-level callable constraint exception, if we managed
1104-
# to get other constraints for ParamSpec, don't infer one with imprecise kinds
1105-
if not skip_imprecise:
1106-
param_spec_target = Parameters(
1107-
arg_types=cactual.arg_types[prefix_len:],
1108-
arg_kinds=cactual.arg_kinds[prefix_len:],
1109-
arg_names=cactual.arg_names[prefix_len:],
1110-
variables=cactual.variables
1111-
if not type_state.infer_polymorphic
1112-
else [],
1113-
imprecise_arg_kinds=cactual.imprecise_arg_kinds,
1114-
)
1100+
param_spec_target = Parameters(
1101+
arg_types=cactual.arg_types[prefix_len:],
1102+
arg_kinds=cactual.arg_kinds[prefix_len:],
1103+
arg_names=cactual.arg_names[prefix_len:],
1104+
variables=cactual.variables if not type_state.infer_polymorphic else [],
1105+
imprecise_arg_kinds=cactual.imprecise_arg_kinds,
1106+
)
11151107
else:
1116-
if (
1117-
len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types)
1118-
and not skip_imprecise
1119-
):
1108+
if len(param_spec.prefix.arg_types) <= len(cactual_ps.prefix.arg_types):
11201109
param_spec_target = cactual_ps.copy_modified(
11211110
prefix=Parameters(
11221111
arg_types=cactual_ps.prefix.arg_types[prefix_len:],
@@ -1611,3 +1600,24 @@ def infer_callable_arguments_constraints(
16111600
infer_directed_arg_constraints(left_by_name.typ, right_by_name.typ, direction)
16121601
)
16131602
return res
1603+
1604+
1605+
def filter_imprecise_kinds(cs: list[Constraint]) -> list[Constraint]:
1606+
"""For each ParamSpec remove all imprecise constraints, if at least one precise available."""
1607+
have_precise = set()
1608+
for c in cs:
1609+
if not isinstance(c.origin_type_var, ParamSpecType):
1610+
continue
1611+
if (
1612+
isinstance(c.target, ParamSpecType)
1613+
or isinstance(c.target, Parameters)
1614+
and not c.target.imprecise_arg_kinds
1615+
):
1616+
have_precise.add(c.type_var)
1617+
new_cs = []
1618+
for c in cs:
1619+
if not isinstance(c.origin_type_var, ParamSpecType) or c.type_var not in have_precise:
1620+
new_cs.append(c)
1621+
if not isinstance(c.target, Parameters) or not c.target.imprecise_arg_kinds:
1622+
new_cs.append(c)
1623+
return new_cs

mypy/expandtype.py

+1
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ def visit_param_spec(self, t: ParamSpecType) -> Type:
253253
t.prefix.arg_kinds + repl.arg_kinds,
254254
t.prefix.arg_names + repl.arg_names,
255255
variables=[*t.prefix.variables, *repl.variables],
256+
imprecise_arg_kinds=repl.imprecise_arg_kinds,
256257
)
257258
else:
258259
# We could encode Any as trivial parameters etc., but it would be too verbose.

mypy/moduleinspect.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pkgutil
99
import queue
1010
import sys
11-
from multiprocessing import Process, Queue
11+
from multiprocessing import Queue, get_context
1212
from types import ModuleType
1313

1414

@@ -123,9 +123,13 @@ def __init__(self) -> None:
123123
self._start()
124124

125125
def _start(self) -> None:
126-
self.tasks: Queue[str] = Queue()
127-
self.results: Queue[ModuleProperties | str] = Queue()
128-
self.proc = Process(target=worker, args=(self.tasks, self.results, sys.path))
126+
if sys.platform == "linux":
127+
ctx = get_context("forkserver")
128+
else:
129+
ctx = get_context("spawn")
130+
self.tasks: Queue[str] = ctx.Queue()
131+
self.results: Queue[ModuleProperties | str] = ctx.Queue()
132+
self.proc = ctx.Process(target=worker, args=(self.tasks, self.results, sys.path))
129133
self.proc.start()
130134
self.counter = 0 # Number of successful roundtrips
131135

mypy/semanal.py

+18-17
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ def file_context(
775775
self.globals = file_node.names
776776
self.tvar_scope = TypeVarLikeScope()
777777

778-
self.named_tuple_analyzer = NamedTupleAnalyzer(options, self)
778+
self.named_tuple_analyzer = NamedTupleAnalyzer(options, self, self.msg)
779779
self.typed_dict_analyzer = TypedDictAnalyzer(options, self, self.msg)
780780
self.enum_call_analyzer = EnumCallAnalyzer(options, self)
781781
self.newtype_analyzer = NewTypeAnalyzer(options, self, self.msg)
@@ -2855,22 +2855,23 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
28552855
if self.check_and_set_up_type_alias(s):
28562856
s.is_alias_def = True
28572857
special_form = True
2858-
# * type variable definition
2859-
elif self.process_typevar_declaration(s):
2860-
special_form = True
2861-
elif self.process_paramspec_declaration(s):
2862-
special_form = True
2863-
elif self.process_typevartuple_declaration(s):
2864-
special_form = True
2865-
# * type constructors
2866-
elif self.analyze_namedtuple_assign(s):
2867-
special_form = True
2868-
elif self.analyze_typeddict_assign(s):
2869-
special_form = True
2870-
elif self.newtype_analyzer.process_newtype_declaration(s):
2871-
special_form = True
2872-
elif self.analyze_enum_assign(s):
2873-
special_form = True
2858+
elif isinstance(s.rvalue, CallExpr):
2859+
# * type variable definition
2860+
if self.process_typevar_declaration(s):
2861+
special_form = True
2862+
elif self.process_paramspec_declaration(s):
2863+
special_form = True
2864+
elif self.process_typevartuple_declaration(s):
2865+
special_form = True
2866+
# * type constructors
2867+
elif self.analyze_namedtuple_assign(s):
2868+
special_form = True
2869+
elif self.analyze_typeddict_assign(s):
2870+
special_form = True
2871+
elif self.newtype_analyzer.process_newtype_declaration(s):
2872+
special_form = True
2873+
elif self.analyze_enum_assign(s):
2874+
special_form = True
28742875

28752876
if special_form:
28762877
self.record_special_form_lvalue(s)

mypy/semanal_namedtuple.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Final, Iterator, List, Mapping, cast
1010

1111
from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
12+
from mypy.messages import MessageBuilder
1213
from mypy.nodes import (
1314
ARG_NAMED_OPT,
1415
ARG_OPT,
@@ -91,9 +92,12 @@
9192

9293

9394
class NamedTupleAnalyzer:
94-
def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None:
95+
def __init__(
96+
self, options: Options, api: SemanticAnalyzerInterface, msg: MessageBuilder
97+
) -> None:
9598
self.options = options
9699
self.api = api
100+
self.msg = msg
97101

98102
def analyze_namedtuple_classdef(
99103
self, defn: ClassDef, is_stub_file: bool, is_func_scope: bool
@@ -204,6 +208,10 @@ def check_namedtuple_classdef(
204208
)
205209
else:
206210
default_items[name] = stmt.rvalue
211+
if defn.keywords:
212+
for_function = ' for "__init_subclass__" of "NamedTuple"'
213+
for key in defn.keywords:
214+
self.msg.unexpected_keyword_argument_for_function(for_function, key, defn)
207215
return items, types, default_items, statements
208216

209217
def check_namedtuple(

mypy/semanal_typeddict.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def analyze_typeddict_classdef_fields(
325325
total = require_bool_literal_argument(self.api, defn.keywords["total"], "total", True)
326326
if defn.keywords and defn.keywords.keys() != {"total"}:
327327
for_function = ' for "__init_subclass__" of "TypedDict"'
328-
for key in defn.keywords.keys():
328+
for key in defn.keywords:
329329
if key == "total":
330330
continue
331331
self.msg.unexpected_keyword_argument_for_function(for_function, key, defn)

mypy/stubdoc.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,19 @@ def is_valid_type(s: str) -> bool:
3636
class ArgSig:
3737
"""Signature info for a single argument."""
3838

39-
def __init__(self, name: str, type: str | None = None, default: bool = False):
39+
def __init__(
40+
self,
41+
name: str,
42+
type: str | None = None,
43+
*,
44+
default: bool = False,
45+
default_value: str = "...",
46+
) -> None:
4047
self.name = name
4148
self.type = type
4249
# Does this argument have a default value?
4350
self.default = default
51+
self.default_value = default_value
4452

4553
def is_star_arg(self) -> bool:
4654
return self.name.startswith("*") and not self.name.startswith("**")
@@ -59,6 +67,7 @@ def __eq__(self, other: Any) -> bool:
5967
self.name == other.name
6068
and self.type == other.type
6169
and self.default == other.default
70+
and self.default_value == other.default_value
6271
)
6372
return False
6473

@@ -119,10 +128,10 @@ def format_sig(
119128
if arg_type:
120129
arg_def += ": " + arg_type
121130
if arg.default:
122-
arg_def += " = ..."
131+
arg_def += f" = {arg.default_value}"
123132

124133
elif arg.default:
125-
arg_def += "=..."
134+
arg_def += f"={arg.default_value}"
126135

127136
args.append(arg_def)
128137

0 commit comments

Comments
 (0)