Skip to content

Commit 6bf9401

Browse files
Merge branch 'master' into special_case_typeddict_get
2 parents 443f6c7 + de2f375 commit 6bf9401

Some content is hidden

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

68 files changed

+2666
-1196
lines changed

docs/source/common_issues.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ This example demonstrates both safe and unsafe overrides:
731731
732732
class NarrowerReturn(A):
733733
# A more specific return type is fine
734-
def test(self, t: Sequence[int]) -> List[str]: # OK
734+
def test(self, t: Sequence[int]) -> list[str]: # OK
735735
...
736736
737737
class GeneralizedReturn(A):
@@ -746,7 +746,7 @@ not necessary:
746746
.. code-block:: python
747747
748748
class NarrowerArgument(A):
749-
def test(self, t: List[int]) -> Sequence[str]: # type: ignore[override]
749+
def test(self, t: list[int]) -> Sequence[str]: # type: ignore[override]
750750
...
751751
752752
.. _unreachable:

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: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ def __init__(self, manager: BuildManager, graph: Graph) -> None:
143143
self.errors: list[str] = [] # Filled in by build if desired
144144

145145

146+
def build_error(msg: str) -> NoReturn:
147+
raise CompileError([f"mypy: error: {msg}"])
148+
149+
146150
def build(
147151
sources: list[BuildSource],
148152
options: Options,
@@ -241,6 +245,9 @@ def _build(
241245
errors = Errors(options, read_source=lambda path: read_py_file(path, cached_read))
242246
plugin, snapshot = load_plugins(options, errors, stdout, extra_plugins)
243247

248+
# Validate error codes after plugins are loaded.
249+
options.process_error_codes(error_callback=build_error)
250+
244251
# Add catch-all .gitignore to cache dir if we created it
245252
cache_dir_existed = os.path.isdir(options.cache_dir)
246253

@@ -1546,7 +1553,7 @@ def write_cache(
15461553
source_hash: str,
15471554
ignore_all: bool,
15481555
manager: BuildManager,
1549-
) -> tuple[str, tuple[dict[str, Any], str, str] | None]:
1556+
) -> tuple[str, tuple[dict[str, Any], str] | None]:
15501557
"""Write cache files for a module.
15511558
15521559
Note that this mypy's behavior is still correct when any given
@@ -1568,7 +1575,7 @@ def write_cache(
15681575
15691576
Returns:
15701577
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,
1578+
that should be written and path to cache file (inner tuple may be None,
15721579
if the cache data could not be written).
15731580
"""
15741581
metastore = manager.metastore
@@ -1662,12 +1669,10 @@ def write_cache(
16621669
"ignore_all": ignore_all,
16631670
"plugin_data": plugin_data,
16641671
}
1665-
return interface_hash, (meta, meta_json, data_json)
1672+
return interface_hash, (meta, meta_json)
16661673

16671674

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

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

16831686
"""Dependency manager.
16841687
@@ -1864,9 +1867,6 @@ class State:
18641867
# List of (path, line number) tuples giving context for import
18651868
import_context: list[tuple[str, int]]
18661869

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

@@ -1917,7 +1917,6 @@ def __init__(
19171917
self.manager = manager
19181918
State.order_counter += 1
19191919
self.order = State.order_counter
1920-
self.caller_state = caller_state
19211920
self.caller_line = caller_line
19221921
if caller_state:
19231922
self.import_context = caller_state.import_context.copy()
@@ -2008,11 +2007,6 @@ def __init__(
20082007
self.parse_file(temporary=temporary)
20092008
self.compute_dependencies()
20102009

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

24802474
return valid_refs
24812475

2482-
def write_cache(self) -> tuple[dict[str, Any], str, str] | None:
2476+
def write_cache(self) -> tuple[dict[str, Any], str] | None:
24832477
assert self.tree is not None, "Internal error: method must be called on parsed file only"
24842478
# We don't support writing cache files in fine-grained incremental mode.
24852479
if (
@@ -3477,14 +3471,13 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No
34773471
for id in stale:
34783472
meta_tuple = meta_tuples[id]
34793473
if meta_tuple is None:
3480-
graph[id].meta = None
34813474
continue
3482-
meta, meta_json, data_json = meta_tuple
3475+
meta, meta_json = meta_tuple
34833476
meta["dep_hashes"] = {
34843477
dep: graph[dep].interface_hash for dep in graph[id].dependencies if dep in graph
34853478
}
34863479
meta["error_lines"] = errors_by_id.get(id, [])
3487-
graph[id].meta = write_cache_meta(meta, manager, meta_json, data_json)
3480+
write_cache_meta(meta, manager, meta_json)
34883481

34893482

34903483
def sorted_components(

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/checker.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1801,7 +1801,8 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None:
18011801
"but must return a subtype of",
18021802
)
18031803
elif not isinstance(
1804-
get_proper_type(bound_type.ret_type), (AnyType, Instance, TupleType, UninhabitedType)
1804+
get_proper_type(bound_type.ret_type),
1805+
(AnyType, Instance, TupleType, UninhabitedType, LiteralType),
18051806
):
18061807
self.fail(
18071808
message_registry.NON_INSTANCE_NEW_TYPE.format(

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/checkmember.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member
412412
ret_type = tuple_fallback(ret_type)
413413
if isinstance(ret_type, TypedDictType):
414414
ret_type = ret_type.fallback
415+
if isinstance(ret_type, LiteralType):
416+
ret_type = ret_type.fallback
415417
if isinstance(ret_type, Instance):
416418
if not mx.is_operator:
417419
# When Python sees an operator (eg `3 == 4`), it automatically translates that

mypy/config_parser.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import sys
99
from io import StringIO
1010

11-
from mypy.errorcodes import error_codes
12-
1311
if sys.version_info >= (3, 11):
1412
import tomllib
1513
else:
@@ -87,15 +85,6 @@ def complain(x: object, additional_info: str = "") -> Never:
8785
complain(v)
8886

8987

90-
def validate_codes(codes: list[str]) -> list[str]:
91-
invalid_codes = set(codes) - set(error_codes.keys())
92-
if invalid_codes:
93-
raise argparse.ArgumentTypeError(
94-
f"Invalid error code(s): {', '.join(sorted(invalid_codes))}"
95-
)
96-
return codes
97-
98-
9988
def validate_package_allow_list(allow_list: list[str]) -> list[str]:
10089
for p in allow_list:
10190
msg = f"Invalid allow list entry: {p}"
@@ -209,8 +198,8 @@ def split_commas(value: str) -> list[str]:
209198
[p.strip() for p in split_commas(s)]
210199
),
211200
"enable_incomplete_feature": lambda s: [p.strip() for p in split_commas(s)],
212-
"disable_error_code": lambda s: validate_codes([p.strip() for p in split_commas(s)]),
213-
"enable_error_code": lambda s: validate_codes([p.strip() for p in split_commas(s)]),
201+
"disable_error_code": lambda s: [p.strip() for p in split_commas(s)],
202+
"enable_error_code": lambda s: [p.strip() for p in split_commas(s)],
214203
"package_root": lambda s: [p.strip() for p in split_commas(s)],
215204
"cache_dir": expand_path,
216205
"python_executable": expand_path,
@@ -234,8 +223,8 @@ def split_commas(value: str) -> list[str]:
234223
"always_false": try_split,
235224
"untyped_calls_exclude": lambda s: validate_package_allow_list(try_split(s)),
236225
"enable_incomplete_feature": try_split,
237-
"disable_error_code": lambda s: validate_codes(try_split(s)),
238-
"enable_error_code": lambda s: validate_codes(try_split(s)),
226+
"disable_error_code": lambda s: try_split(s),
227+
"enable_error_code": lambda s: try_split(s),
239228
"package_root": try_split,
240229
"exclude": str_or_array_as_list,
241230
"packages": try_split,

mypy/main.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,11 +1066,7 @@ def add_invertible_flag(
10661066
incremental_group.add_argument(
10671067
"--fixed-format-cache",
10681068
action="store_true",
1069-
help=(
1070-
"Use experimental fast and compact fixed format cache"
1071-
if compilation_status == "yes"
1072-
else argparse.SUPPRESS
1073-
),
1069+
help="Use new fast and compact fixed format cache",
10741070
)
10751071
incremental_group.add_argument(
10761072
"--skip-version-check",
@@ -1466,7 +1462,6 @@ def set_strict_flags() -> None:
14661462
validate_package_allow_list(options.untyped_calls_exclude)
14671463
validate_package_allow_list(options.deprecated_calls_exclude)
14681464

1469-
options.process_error_codes(error_callback=parser.error)
14701465
options.process_incomplete_features(error_callback=parser.error, warning_callback=print)
14711466

14721467
# Compute absolute path for custom typeshed (if present).

mypy/modulefinder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ def default_lib_path(
796796
custom_typeshed_dir = os.path.abspath(custom_typeshed_dir)
797797
typeshed_dir = os.path.join(custom_typeshed_dir, "stdlib")
798798
mypy_extensions_dir = os.path.join(custom_typeshed_dir, "stubs", "mypy-extensions")
799-
mypy_native_dir = os.path.join(custom_typeshed_dir, "stubs", "mypy-native")
799+
librt_dir = os.path.join(custom_typeshed_dir, "stubs", "librt")
800800
versions_file = os.path.join(typeshed_dir, "VERSIONS")
801801
if not os.path.isdir(typeshed_dir) or not os.path.isfile(versions_file):
802802
print(
@@ -812,13 +812,13 @@ def default_lib_path(
812812
data_dir = auto
813813
typeshed_dir = os.path.join(data_dir, "typeshed", "stdlib")
814814
mypy_extensions_dir = os.path.join(data_dir, "typeshed", "stubs", "mypy-extensions")
815-
mypy_native_dir = os.path.join(data_dir, "typeshed", "stubs", "mypy-native")
815+
librt_dir = os.path.join(data_dir, "typeshed", "stubs", "librt")
816816
path.append(typeshed_dir)
817817

818-
# Get mypy-extensions and mypy-native stubs from typeshed, since we treat them as
818+
# Get mypy-extensions and librt stubs from typeshed, since we treat them as
819819
# "internal" libraries, similar to typing and typing-extensions.
820820
path.append(mypy_extensions_dir)
821-
path.append(mypy_native_dir)
821+
path.append(librt_dir)
822822

823823
# Add fallback path that can be used if we have a broken installation.
824824
if sys.platform != "win32":

0 commit comments

Comments
 (0)