Skip to content

Commit 6764f99

Browse files
authored
Merge branch 'master' into no-kwarg-for-star-args
2 parents 8a4cd57 + d68ea35 commit 6764f99

Some content is hidden

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

41 files changed

+845
-180
lines changed

mypy/checker.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,9 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
750750
defn.is_explicit_override
751751
and not found_method_base_classes
752752
and found_method_base_classes is not None
753+
# If the class has Any fallback, we can't be certain that a method
754+
# is really missing - it might come from unfollowed import.
755+
and not defn.info.fallback_to_any
753756
):
754757
self.msg.no_overridable_method(defn.name, defn)
755758
self.check_explicit_override_decorator(defn, found_method_base_classes, defn.impl)
@@ -5285,12 +5288,15 @@ def visit_decorator_inner(
52855288
# For overloaded functions/properties we already checked override for overload as a whole.
52865289
if allow_empty or skip_first_item:
52875290
return
5288-
if e.func.info and not e.func.is_dynamic() and not e.is_overload:
5291+
if e.func.info and not e.is_overload:
52895292
found_method_base_classes = self.check_method_override(e)
52905293
if (
52915294
e.func.is_explicit_override
52925295
and not found_method_base_classes
52935296
and found_method_base_classes is not None
5297+
# If the class has Any fallback, we can't be certain that a method
5298+
# is really missing - it might come from unfollowed import.
5299+
and not e.func.info.fallback_to_any
52945300
):
52955301
self.msg.no_overridable_method(e.func.name, e.func)
52965302
self.check_explicit_override_decorator(e.func, found_method_base_classes)

mypy/checkexpr.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4096,7 +4096,7 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
40964096
results = []
40974097
for name, method, obj, arg in variants:
40984098
with self.msg.filter_errors(save_filtered_errors=True) as local_errors:
4099-
result = self.check_method_call(op_name, obj, method, [arg], [ARG_POS], context)
4099+
result = self.check_method_call(name, obj, method, [arg], [ARG_POS], context)
41004100
if local_errors.has_new_errors():
41014101
errors.append(local_errors.filtered_errors())
41024102
results.append(result)
@@ -6297,7 +6297,13 @@ def narrow_type_from_binder(
62976297
known_type, restriction, prohibit_none_typevar_overlap=True
62986298
):
62996299
return None
6300-
return narrow_declared_type(known_type, restriction)
6300+
narrowed = narrow_declared_type(known_type, restriction)
6301+
if isinstance(get_proper_type(narrowed), UninhabitedType):
6302+
# If we hit this case, it means that we can't reliably mark the code as
6303+
# unreachable, but the resulting type can't be expressed in type system.
6304+
# Falling back to restriction is more intuitive in most cases.
6305+
return restriction
6306+
return narrowed
63016307
return known_type
63026308

63036309
def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool:

mypy/dmypy/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929

3030

3131
class AugmentedHelpFormatter(argparse.RawDescriptionHelpFormatter):
32-
def __init__(self, prog: str) -> None:
33-
super().__init__(prog=prog, max_help_position=30)
32+
def __init__(self, prog: str, **kwargs: Any) -> None:
33+
super().__init__(prog=prog, max_help_position=30, **kwargs)
3434

3535

3636
parser = argparse.ArgumentParser(

mypy/main.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ def show_messages(
249249

250250
# Make the help output a little less jarring.
251251
class AugmentedHelpFormatter(argparse.RawDescriptionHelpFormatter):
252-
def __init__(self, prog: str) -> None:
253-
super().__init__(prog=prog, max_help_position=28)
252+
def __init__(self, prog: str, **kwargs: Any) -> None:
253+
super().__init__(prog=prog, max_help_position=28, **kwargs)
254254

255255
def _fill_text(self, text: str, width: int, indent: str) -> str:
256256
if "\n" in text:
@@ -1449,9 +1449,7 @@ def set_strict_flags() -> None:
14491449
process_cache_map(parser, special_opts, options)
14501450

14511451
# Process --strict-bytes
1452-
if options.strict_bytes:
1453-
options.disable_bytearray_promotion = True
1454-
options.disable_memoryview_promotion = True
1452+
options.process_strict_bytes()
14551453

14561454
# An explicitly specified cache_fine_grained implies local_partial_types
14571455
# (because otherwise the cache is not compatible with dmypy)

mypy/modulefinder.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -506,21 +506,24 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
506506
dir_prefix = base_dir
507507
for _ in range(len(components) - 1):
508508
dir_prefix = os.path.dirname(dir_prefix)
509+
510+
# Stubs-only packages always take precedence over py.typed packages
511+
path_stubs = f"{base_path}-stubs{sepinit}.pyi"
512+
if fscache.isfile_case(path_stubs, dir_prefix):
513+
if verify and not verify_module(fscache, id, path_stubs, dir_prefix):
514+
near_misses.append((path_stubs, dir_prefix))
515+
else:
516+
return path_stubs
517+
509518
# Prefer package over module, i.e. baz/__init__.py* over baz.py*.
510519
for extension in PYTHON_EXTENSIONS:
511520
path = base_path + sepinit + extension
512-
path_stubs = base_path + "-stubs" + sepinit + extension
513521
if fscache.isfile_case(path, dir_prefix):
514522
has_init = True
515523
if verify and not verify_module(fscache, id, path, dir_prefix):
516524
near_misses.append((path, dir_prefix))
517525
continue
518526
return path
519-
elif fscache.isfile_case(path_stubs, dir_prefix):
520-
if verify and not verify_module(fscache, id, path_stubs, dir_prefix):
521-
near_misses.append((path_stubs, dir_prefix))
522-
continue
523-
return path_stubs
524527

525528
# In namespace mode, register a potential namespace package
526529
if self.options and self.options.namespace_packages:

mypy/options.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,16 @@ def process_incomplete_features(
466466
if feature in COMPLETE_FEATURES:
467467
warning_callback(f"Warning: {feature} is already enabled by default")
468468

469+
def process_strict_bytes(self) -> None:
470+
# Sync `--strict-bytes` and `--disable-{bytearray,memoryview}-promotion`
471+
if self.strict_bytes:
472+
# backwards compatibility
473+
self.disable_bytearray_promotion = True
474+
self.disable_memoryview_promotion = True
475+
elif self.disable_bytearray_promotion and self.disable_memoryview_promotion:
476+
# forwards compatibility
477+
self.strict_bytes = True
478+
469479
def apply_changes(self, changes: dict[str, object]) -> Options:
470480
# Note: effects of this method *must* be idempotent.
471481
new_options = Options()

mypy/semanal.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6382,6 +6382,8 @@ class C:
63826382
if node.name not in self.globals:
63836383
return True
63846384
global_node = self.globals[node.name]
6385+
if not self.is_textually_before_class(global_node.node):
6386+
return True
63856387
return not self.is_type_like(global_node.node)
63866388
return False
63876389

@@ -6409,6 +6411,13 @@ def is_textually_before_statement(self, node: SymbolNode) -> bool:
64096411
else:
64106412
return line_diff > 0
64116413

6414+
def is_textually_before_class(self, node: SymbolNode | None) -> bool:
6415+
"""Similar to above, but check if a node is defined before current class."""
6416+
assert self.type is not None
6417+
if node is None:
6418+
return False
6419+
return node.line < self.type.defn.line
6420+
64126421
def is_overloaded_item(self, node: SymbolNode, statement: Statement) -> bool:
64136422
"""Check whether the function belongs to the overloaded variants"""
64146423
if isinstance(node, OverloadedFuncDef) and isinstance(statement, FuncDef):

mypy/stubgen.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -920,13 +920,20 @@ def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
920920
continue
921921
if (
922922
isinstance(lvalue, NameExpr)
923-
and not self.is_private_name(lvalue.name)
924-
# it is never an alias with explicit annotation
925-
and not o.unanalyzed_type
926923
and self.is_alias_expression(o.rvalue)
924+
and not self.is_private_name(lvalue.name)
927925
):
928-
self.process_typealias(lvalue, o.rvalue)
929-
continue
926+
is_explicit_type_alias = (
927+
o.unanalyzed_type and getattr(o.type, "name", None) == "TypeAlias"
928+
)
929+
if is_explicit_type_alias:
930+
self.process_typealias(lvalue, o.rvalue, is_explicit_type_alias=True)
931+
continue
932+
933+
if not o.unanalyzed_type:
934+
self.process_typealias(lvalue, o.rvalue)
935+
continue
936+
930937
if isinstance(lvalue, (TupleExpr, ListExpr)):
931938
items = lvalue.items
932939
if isinstance(o.unanalyzed_type, TupleType): # type: ignore[misc]
@@ -1139,9 +1146,15 @@ def is_alias_expression(self, expr: Expression, top_level: bool = True) -> bool:
11391146
else:
11401147
return False
11411148

1142-
def process_typealias(self, lvalue: NameExpr, rvalue: Expression) -> None:
1149+
def process_typealias(
1150+
self, lvalue: NameExpr, rvalue: Expression, is_explicit_type_alias: bool = False
1151+
) -> None:
11431152
p = AliasPrinter(self)
1144-
self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n")
1153+
if is_explicit_type_alias:
1154+
self.import_tracker.require_name("TypeAlias")
1155+
self.add(f"{self._indent}{lvalue.name}: TypeAlias = {rvalue.accept(p)}\n")
1156+
else:
1157+
self.add(f"{self._indent}{lvalue.name} = {rvalue.accept(p)}\n")
11451158
self.record_name(lvalue.name)
11461159
self._vars[-1].append(lvalue.name)
11471160

mypy/stubtest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,6 +2003,7 @@ def warning_callback(msg: str) -> None:
20032003
options.process_incomplete_features(
20042004
error_callback=error_callback, warning_callback=warning_callback
20052005
)
2006+
options.process_strict_bytes()
20062007

20072008
try:
20082009
modules = build_stubs(modules, options, find_submodules=not args.check_typeshed)

mypy/subtypes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,11 @@ def visit_type_type(self, left: TypeType) -> bool:
10911091
right = self.right
10921092
if isinstance(right, TypeType):
10931093
return self._is_subtype(left.item, right.item)
1094+
if isinstance(right, Overloaded) and right.is_type_obj():
1095+
# Same as in other direction: if it's a constructor callable, all
1096+
# items should belong to the same class' constructor, so it's enough
1097+
# to check one of them.
1098+
return self._is_subtype(left, right.items[0])
10941099
if isinstance(right, CallableType):
10951100
if self.proper_subtype and not right.is_type_obj():
10961101
# We can't accept `Type[X]` as a *proper* subtype of Callable[P, X]

0 commit comments

Comments
 (0)