Skip to content

Commit 02882fe

Browse files
Merge branch 'master' into f-string-folding
2 parents 6ca315e + c71fef0 commit 02882fe

Some content is hidden

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

66 files changed

+3866
-1182
lines changed

.github/workflows/mypy_primer.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
--debug \
6868
--additional-flags="--debug-serialize" \
6969
--output concise \
70+
--mypy-install-librt \
7071
| tee diff_${{ matrix.shard-index }}.txt
7172
) || [ $? -eq 1 ]
7273
- if: ${{ matrix.shard-index == 0 }}

docs/source/command_line.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ definitions or calls.
372372

373373
.. option:: --untyped-calls-exclude
374374

375-
This flag allows to selectively disable :option:`--disallow-untyped-calls`
375+
This flag allows one to selectively disable :option:`--disallow-untyped-calls`
376376
for functions and methods defined in specific packages, modules, or classes.
377377
Note that each exclude entry acts as a prefix. For example (assuming there
378378
are no type annotations for ``third_party_lib`` available):
@@ -562,7 +562,7 @@ potentially problematic or redundant in some way.
562562

563563
.. option:: --deprecated-calls-exclude
564564

565-
This flag allows to selectively disable :ref:`deprecated<code-deprecated>` warnings
565+
This flag allows one to selectively disable :ref:`deprecated<code-deprecated>` warnings
566566
for functions and methods defined in specific packages, modules, or classes.
567567
Note that each exclude entry acts as a prefix. For example (assuming ``foo.A.func`` is deprecated):
568568

docs/source/error_code_list.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,8 +1032,8 @@ Warn about top level await expressions [top-level-await]
10321032
This error code is separate from the general ``[syntax]`` errors, because in
10331033
some environments (e.g. IPython) a top level ``await`` is allowed. In such
10341034
environments a user may want to use ``--disable-error-code=top-level-await``,
1035-
that allows to still have errors for other improper uses of ``await``, for
1036-
example:
1035+
which allows one to still have errors for other improper uses of ``await``,
1036+
for example:
10371037

10381038
.. code-block:: python
10391039

docs/source/mypy_daemon.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,16 @@ command.
252252
Statically inspect expressions
253253
******************************
254254

255-
The daemon allows to get declared or inferred type of an expression (or other
255+
The daemon allows one to get the declared or inferred type of an expression (or other
256256
information about an expression, such as known attributes or definition location)
257-
using ``dmypy inspect LOCATION`` command. The location of the expression should be
257+
using the ``dmypy inspect LOCATION`` command. The location of the expression should be
258258
specified in the format ``path/to/file.py:line:column[:end_line:end_column]``.
259259
Both line and column are 1-based. Both start and end position are inclusive.
260260
These rules match how mypy prints the error location in error messages.
261261

262262
If a span is given (i.e. all 4 numbers), then only an exactly matching expression
263263
is inspected. If only a position is given (i.e. 2 numbers, line and column), mypy
264-
will inspect all *expressions*, that include this position, starting from the
264+
will inspect all expressions that include this position, starting from the
265265
innermost one.
266266

267267
Consider this Python code snippet:

mypy-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ typing_extensions>=4.6.0
44
mypy_extensions>=1.0.0
55
pathspec>=0.9.0
66
tomli>=1.1.0; python_version<'3.11'
7+
librt>=0.1.0

mypy/build.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,7 +1546,7 @@ def write_cache(
15461546
source_hash: str,
15471547
ignore_all: bool,
15481548
manager: BuildManager,
1549-
) -> tuple[str, tuple[dict[str, Any], str, str] | None]:
1549+
) -> tuple[str, tuple[dict[str, Any], str] | None]:
15501550
"""Write cache files for a module.
15511551
15521552
Note that this mypy's behavior is still correct when any given
@@ -1568,7 +1568,7 @@ def write_cache(
15681568
15691569
Returns:
15701570
A tuple containing the interface hash and inner tuple with cache meta JSON
1571-
that should be written and paths to cache files (inner tuple may be None,
1571+
that should be written and path to cache file (inner tuple may be None,
15721572
if the cache data could not be written).
15731573
"""
15741574
metastore = manager.metastore
@@ -1662,12 +1662,10 @@ def write_cache(
16621662
"ignore_all": ignore_all,
16631663
"plugin_data": plugin_data,
16641664
}
1665-
return interface_hash, (meta, meta_json, data_json)
1665+
return interface_hash, (meta, meta_json)
16661666

16671667

1668-
def write_cache_meta(
1669-
meta: dict[str, Any], manager: BuildManager, meta_json: str, data_json: str
1670-
) -> CacheMeta:
1668+
def write_cache_meta(meta: dict[str, Any], manager: BuildManager, meta_json: str) -> None:
16711669
# Write meta cache file
16721670
metastore = manager.metastore
16731671
meta_str = json_dumps(meta, manager.options.debug_cache)
@@ -1677,8 +1675,6 @@ def write_cache_meta(
16771675
# The next run will simply find the cache entry out of date.
16781676
manager.log(f"Error writing meta JSON file {meta_json}")
16791677

1680-
return cache_meta_from_dict(meta, data_json)
1681-
16821678

16831679
"""Dependency manager.
16841680
@@ -1864,9 +1860,6 @@ class State:
18641860
# List of (path, line number) tuples giving context for import
18651861
import_context: list[tuple[str, int]]
18661862

1867-
# The State from which this module was imported, if any
1868-
caller_state: State | None = None
1869-
18701863
# If caller_state is set, the line number in the caller where the import occurred
18711864
caller_line = 0
18721865

@@ -1917,7 +1910,6 @@ def __init__(
19171910
self.manager = manager
19181911
State.order_counter += 1
19191912
self.order = State.order_counter
1920-
self.caller_state = caller_state
19211913
self.caller_line = caller_line
19221914
if caller_state:
19231915
self.import_context = caller_state.import_context.copy()
@@ -2008,11 +2000,6 @@ def __init__(
20082000
self.parse_file(temporary=temporary)
20092001
self.compute_dependencies()
20102002

2011-
@property
2012-
def xmeta(self) -> CacheMeta:
2013-
assert self.meta, "missing meta on allegedly fresh module"
2014-
return self.meta
2015-
20162003
def add_ancestors(self) -> None:
20172004
if self.path is not None:
20182005
_, name = os.path.split(self.path)
@@ -2479,7 +2466,7 @@ def valid_references(self) -> set[str]:
24792466

24802467
return valid_refs
24812468

2482-
def write_cache(self) -> tuple[dict[str, Any], str, str] | None:
2469+
def write_cache(self) -> tuple[dict[str, Any], str] | None:
24832470
assert self.tree is not None, "Internal error: method must be called on parsed file only"
24842471
# We don't support writing cache files in fine-grained incremental mode.
24852472
if (
@@ -3477,18 +3464,17 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No
34773464
for id in stale:
34783465
meta_tuple = meta_tuples[id]
34793466
if meta_tuple is None:
3480-
graph[id].meta = None
34813467
continue
3482-
meta, meta_json, data_json = meta_tuple
3468+
meta, meta_json = meta_tuple
34833469
meta["dep_hashes"] = {
34843470
dep: graph[dep].interface_hash for dep in graph[id].dependencies if dep in graph
34853471
}
34863472
meta["error_lines"] = errors_by_id.get(id, [])
3487-
graph[id].meta = write_cache_meta(meta, manager, meta_json, data_json)
3473+
write_cache_meta(meta, manager, meta_json)
34883474

34893475

34903476
def sorted_components(
3491-
graph: Graph, vertices: AbstractSet[str] | None = None, pri_max: int = PRI_ALL
3477+
graph: Graph, vertices: AbstractSet[str] | None = None, pri_max: int = PRI_INDIRECT
34923478
) -> list[AbstractSet[str]]:
34933479
"""Return the graph's SCCs, topologically sorted by dependencies.
34943480

mypy/cache.py

Lines changed: 14 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,22 @@
11
from __future__ import annotations
22

33
from collections.abc import Sequence
4-
from typing import TYPE_CHECKING, Final
4+
from typing import Final
55

66
from mypy_extensions import u8
7-
8-
try:
9-
from native_internal import (
10-
Buffer as Buffer,
11-
read_bool as read_bool,
12-
read_float as read_float,
13-
read_int as read_int,
14-
read_str as read_str,
15-
read_tag as read_tag,
16-
write_bool as write_bool,
17-
write_float as write_float,
18-
write_int as write_int,
19-
write_str as write_str,
20-
write_tag as write_tag,
21-
)
22-
except ImportError:
23-
# TODO: temporary, remove this after we publish mypy-native on PyPI.
24-
if not TYPE_CHECKING:
25-
26-
class Buffer:
27-
def __init__(self, source: bytes = b"") -> None:
28-
raise NotImplementedError
29-
30-
def getvalue(self) -> bytes:
31-
raise NotImplementedError
32-
33-
def read_int(data: Buffer) -> int:
34-
raise NotImplementedError
35-
36-
def write_int(data: Buffer, value: int) -> None:
37-
raise NotImplementedError
38-
39-
def read_tag(data: Buffer) -> u8:
40-
raise NotImplementedError
41-
42-
def write_tag(data: Buffer, value: u8) -> None:
43-
raise NotImplementedError
44-
45-
def read_str(data: Buffer) -> str:
46-
raise NotImplementedError
47-
48-
def write_str(data: Buffer, value: str) -> None:
49-
raise NotImplementedError
50-
51-
def read_bool(data: Buffer) -> bool:
52-
raise NotImplementedError
53-
54-
def write_bool(data: Buffer, value: bool) -> None:
55-
raise NotImplementedError
56-
57-
def read_float(data: Buffer) -> float:
58-
raise NotImplementedError
59-
60-
def write_float(data: Buffer, value: float) -> None:
61-
raise NotImplementedError
62-
7+
from native_internal import (
8+
Buffer as Buffer,
9+
read_bool as read_bool,
10+
read_float as read_float,
11+
read_int as read_int,
12+
read_str as read_str,
13+
read_tag as read_tag,
14+
write_bool as write_bool,
15+
write_float as write_float,
16+
write_int as write_int,
17+
write_str as write_str,
18+
write_tag as write_tag,
19+
)
6320

6421
# Always use this type alias to refer to type tags.
6522
Tag = u8

mypy/checkexpr.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from mypy.checker_shared import ExpressionCheckerSharedApi
1919
from mypy.checkmember import analyze_member_access, has_operator
2020
from mypy.checkstrformat import StringFormatterChecker
21+
from mypy.constant_fold import constant_fold_expr
2122
from mypy.erasetype import erase_type, remove_instance_last_known_values, replace_meta_vars
2223
from mypy.errors import ErrorInfo, ErrorWatcher, report_internal_error
2324
from mypy.expandtype import (
@@ -656,11 +657,12 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) ->
656657
return ret_type
657658

658659
def check_str_format_call(self, e: CallExpr) -> None:
659-
"""More precise type checking for str.format() calls on literals."""
660+
"""More precise type checking for str.format() calls on literals and folded constants."""
660661
assert isinstance(e.callee, MemberExpr)
661662
format_value = None
662-
if isinstance(e.callee.expr, StrExpr):
663-
format_value = e.callee.expr.value
663+
folded_callee_expr = constant_fold_expr(e.callee.expr, "<unused>")
664+
if isinstance(folded_callee_expr, str):
665+
format_value = folded_callee_expr
664666
elif self.chk.has_type(e.callee.expr):
665667
typ = get_proper_type(self.chk.lookup_type(e.callee.expr))
666668
if (

mypy/semanal.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5928,8 +5928,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
59285928
if isinstance(sym.node, PlaceholderNode):
59295929
self.process_placeholder(expr.name, "attribute", expr)
59305930
return
5931-
if sym.node is not None:
5932-
self.record_imported_symbol(sym.node)
5931+
self.record_imported_symbol(sym)
59335932
expr.kind = sym.kind
59345933
expr.fullname = sym.fullname or ""
59355934
expr.node = sym.node
@@ -5960,7 +5959,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
59605959
if type_info:
59615960
n = type_info.names.get(expr.name)
59625961
if n is not None and isinstance(n.node, (MypyFile, TypeInfo, TypeAlias)):
5963-
self.record_imported_symbol(n.node)
5962+
self.record_imported_symbol(n)
59645963
expr.kind = n.kind
59655964
expr.fullname = n.fullname or ""
59665965
expr.node = n.node
@@ -6282,30 +6281,48 @@ def lookup(
62826281
self, name: str, ctx: Context, suppress_errors: bool = False
62836282
) -> SymbolTableNode | None:
62846283
node = self._lookup(name, ctx, suppress_errors)
6285-
if node is not None and node.node is not None:
6284+
if node is not None:
62866285
# This call is unfortunate from performance point of view, but
62876286
# needed for rare cases like e.g. testIncrementalChangingAlias.
6288-
self.record_imported_symbol(node.node)
6287+
self.record_imported_symbol(node)
62896288
return node
62906289

6291-
def record_imported_symbol(self, node: SymbolNode) -> None:
6290+
def record_imported_symbol(self, sym: SymbolTableNode) -> None:
62926291
"""If the symbol was not defined in current module, add its module to module_refs."""
6293-
if not node.fullname:
6292+
if sym.kind == LDEF or sym.node is None:
62946293
return
6294+
node = sym.node
6295+
if isinstance(node, PlaceholderNode) or not node.fullname:
6296+
# This node is not ready yet.
6297+
return
6298+
if node.fullname.startswith(("builtins.", "typing.")):
6299+
# Skip dependencies on builtins/typing.
6300+
return
6301+
# Modules, classes, and type aliases store defining module directly.
62956302
if isinstance(node, MypyFile):
62966303
fullname = node.fullname
62976304
elif isinstance(node, TypeInfo):
62986305
fullname = node.module_name
62996306
elif isinstance(node, TypeAlias):
63006307
fullname = node.module
6301-
elif isinstance(node, (Var, FuncDef, OverloadedFuncDef)) and node.info:
6302-
fullname = node.info.module_name
6308+
elif isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)):
6309+
# For functions/variables infer defining module from enclosing class.
6310+
info = node.var.info if isinstance(node, Decorator) else node.info
6311+
if info:
6312+
fullname = info.module_name
6313+
else:
6314+
# global function/variable
6315+
fullname = node.fullname.rsplit(".", maxsplit=1)[0]
63036316
else:
6304-
fullname = node.fullname.rsplit(".")[0]
6317+
# Some nodes (currently only TypeVarLikeExpr subclasses) don't store
6318+
# module fullname explicitly, infer it from the node fullname iteratively.
6319+
# TODO: this is not 100% robust for type variables nested within a class
6320+
# with a name that matches name of a submodule.
6321+
fullname = node.fullname.rsplit(".", maxsplit=1)[0]
63056322
if fullname == self.cur_mod_id:
63066323
return
63076324
while "." in fullname and fullname not in self.modules:
6308-
fullname = fullname.rsplit(".")[0]
6325+
fullname = fullname.rsplit(".", maxsplit=1)[0]
63096326
if fullname != self.cur_mod_id:
63106327
self.cur_mod_node.module_refs.add(fullname)
63116328

@@ -6519,8 +6536,8 @@ def lookup_qualified(
65196536
self.name_not_defined(name, ctx, namespace=namespace)
65206537
return None
65216538
sym = nextsym
6522-
if sym is not None and sym.node is not None:
6523-
self.record_imported_symbol(sym.node)
6539+
if sym is not None:
6540+
self.record_imported_symbol(sym)
65246541
return sym
65256542

65266543
def lookup_type_node(self, expr: Expression) -> SymbolTableNode | None:

mypy/semanal_shared.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,9 @@ def calculate_tuple_fallback(typ: TupleType) -> None:
303303
):
304304
items.append(unpacked_type.args[0])
305305
else:
306-
raise NotImplementedError
306+
# This is called before semanal_typeargs.py fixes broken unpacks,
307+
# where the error should also be generated.
308+
items.append(AnyType(TypeOfAny.from_error))
307309
else:
308310
items.append(item)
309311
fallback.args = (make_simplified_union(items),)

0 commit comments

Comments
 (0)