Skip to content

Commit 419d2ce

Browse files
Merge branch 'master' into constant-fold-convert-format
2 parents c5570db + de2f375 commit 419d2ce

22 files changed

+140
-64
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/build.py

Lines changed: 7 additions & 0 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

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ def analyze_type_callable_member_access(name: str, typ: FunctionLike, mx: Member
410410
ret_type = tuple_fallback(ret_type)
411411
if isinstance(ret_type, TypedDictType):
412412
ret_type = ret_type.fallback
413+
if isinstance(ret_type, LiteralType):
414+
ret_type = ret_type.fallback
413415
if isinstance(ret_type, Instance):
414416
if not mx.is_operator:
415417
# 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":

mypy/test/teststubtest.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from collections.abc import Iterator
1313
from typing import Any, Callable
1414

15+
from pytest import raises
16+
1517
import mypy.stubtest
1618
from mypy import build, nodes
1719
from mypy.modulefinder import BuildSource
@@ -171,7 +173,12 @@ def build_helper(source: str) -> build.BuildResult:
171173

172174

173175
def run_stubtest_with_stderr(
174-
stub: str, runtime: str, options: list[str], config_file: str | None = None
176+
stub: str,
177+
runtime: str,
178+
options: list[str],
179+
config_file: str | None = None,
180+
output: io.StringIO | None = None,
181+
outerr: io.StringIO | None = None,
175182
) -> tuple[str, str]:
176183
with use_tmp_dir(TEST_MODULE_NAME) as tmp_dir:
177184
with open("builtins.pyi", "w") as f:
@@ -188,8 +195,8 @@ def run_stubtest_with_stderr(
188195
with open(f"{TEST_MODULE_NAME}_config.ini", "w") as f:
189196
f.write(config_file)
190197
options = options + ["--mypy-config-file", f"{TEST_MODULE_NAME}_config.ini"]
191-
output = io.StringIO()
192-
outerr = io.StringIO()
198+
output = io.StringIO() if output is None else output
199+
outerr = io.StringIO() if outerr is None else outerr
193200
with contextlib.redirect_stdout(output), contextlib.redirect_stderr(outerr):
194201
test_stubs(parse_options([TEST_MODULE_NAME] + options), use_builtins_fixtures=True)
195202
filtered_output = remove_color_code(
@@ -2888,14 +2895,20 @@ def test_config_file_error_codes_invalid(self) -> None:
28882895
runtime = "temp = 5\n"
28892896
stub = "temp: int\n"
28902897
config_file = "[mypy]\ndisable_error_code = not-a-valid-name\n"
2891-
output, outerr = run_stubtest_with_stderr(
2892-
stub=stub, runtime=runtime, options=[], config_file=config_file
2893-
)
2894-
assert output == "Success: no issues found in 1 module\n"
2895-
assert outerr == (
2896-
"test_module_config.ini: [mypy]: disable_error_code: "
2897-
"Invalid error code(s): not-a-valid-name\n"
2898-
)
2898+
output = io.StringIO()
2899+
outerr = io.StringIO()
2900+
with raises(SystemExit):
2901+
run_stubtest_with_stderr(
2902+
stub=stub,
2903+
runtime=runtime,
2904+
options=[],
2905+
config_file=config_file,
2906+
output=output,
2907+
outerr=outerr,
2908+
)
2909+
2910+
assert output.getvalue() == "error: Invalid error code(s): not-a-valid-name\n"
2911+
assert outerr.getvalue() == ""
28992912

29002913
def test_config_file_wrong_incomplete_feature(self) -> None:
29012914
runtime = "x = 1\n"

mypy/typeops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ def class_callable(
319319
default_ret_type = fill_typevars(info)
320320
explicit_type = init_ret_type if is_new else orig_self_type
321321
if (
322-
isinstance(explicit_type, (Instance, TupleType, UninhabitedType))
322+
isinstance(explicit_type, (Instance, TupleType, UninhabitedType, LiteralType))
323323
# We have to skip protocols, because it can be a subtype of a return type
324324
# by accident. Like `Hashable` is a subtype of `object`. See #11799
325325
and isinstance(default_ret_type, Instance)

mypy/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,8 @@ def type_object(self) -> mypy.nodes.TypeInfo:
22782278
ret = ret.partial_fallback
22792279
if isinstance(ret, TypedDictType):
22802280
ret = ret.fallback
2281+
if isinstance(ret, LiteralType):
2282+
ret = ret.fallback
22812283
assert isinstance(ret, Instance)
22822284
return ret.type
22832285

0 commit comments

Comments
 (0)