8000 Fix 3903: untyped defs on derived classes by gantsevdenis · Pull Request #7548 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Fix 3903: untyped defs on derived classes #7548

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add new option, added test
  • Loading branch information
gantsevdenis committed Sep 21, 2019
commit cd1d3f4f8d12f7df1c4828a779316c7026a47eb7
5 changes: 5 additions & 0 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ definitions or calls.
It will assume all arguments have type ``Any`` and always infer ``Any``
as the return type.

``--inherit-signatures``
This flag must be used along with `--check-untyped-defs` flag. It allows
to use base class function signature in derived classes, without explicitly
annotating the derived class

``--disallow-untyped-decorators``
This flag reports an error whenever a function with type annotations
is decorated with a decorator without annotations.
Expand Down
22 changes: 21 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,21 @@ def _visit_func_def(self, defn: FuncDef) -> None:
8000 'redefinition with type',
'original type')

def find_base_signature(self, defn: FuncItem) -> Optional[FunctionLike]:
"""Given a function definition, try to find its base definition.

TODO: Cache results ?"""
if not defn.info:
return None
# Skip the current class
for base in defn.info.mro[1:]:
if defn.name() in base.names:
parent = base.names[defn.name()].node
assert isinstance(parent, FuncDef)
function_type = self.function_type(parent)
return function_type
return None

def check_func_item(self, defn: FuncItem,
type_override: Optional[CallableType] = None,
name: Optional[str] = None) -> None:
Expand All @@ -779,7 +794,12 @@ def check_func_item(self, defn: FuncItem,
self.dynamic_funcs.append(defn.is_dynamic() and not type_override)

with self.enter_partial_types(is_function=True):
typ = self.function_type(defn)
if not defn.unanalyzed_type \
and self.options.inherit_signatures\
and self.options.check_untyped_defs:
typ = self.find_base_signature(defn)
else:
typ = self.function_type(defn)
if type_override:
typ = type_override.copy_modified(line=typ.line, column=typ.column)
if isinstance(typ, CallableType):
Expand Down
4 changes: 3 additions & 1 deletion mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,9 @@ def add_invertible_flag(flag: str,
add_invertible_flag('--disallow-untyped-decorators', default=False, strict_flag=True,
help="Disallow decorating typed functions with untyped decorators",
group=untyped_group)

add_invertible_flag('--inherit-signatures', default=False, strict_flag=True,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be a strict flag, because --disallow-untyped-defs is already part of --strict IIRC.

help="Inherit signautes from base classes to child",
group=untyped_group)
none_group = parser.add_argument_group(
title='None and Optional handling',
description="Adjust how values of type 'None' are handled. For more context on "
Expand Down
4 changes: 4 additions & 0 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class BuildType:
"follow_imports_for_stubs",
"ignore_errors",
"ignore_missing_imports",
"inherit_signatures",
"local_partial_types",
"mypyc",
"no_implicit_optional",
Expand Down Expand Up @@ -104,6 +105,9 @@ def __init__(self) -> None:
# Type check unannotated functions
self.check_untyped_defs = False

# Inherit missing signatures from base classes
self.inherit_signatures = False

# Disallow decorating typed functions with untyped decorators
self.disallow_untyped_decorators = False

Expand Down
7 changes: 7 additions & 0 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -6223,3 +6223,10 @@ class Foo:
reveal_type(Foo().x) # N: Revealed type is 'Union[Any, None]'
reveal_type(Foo().y) # N: Revealed type is 'builtins.list[Any]'
[builtins fixtures/list.pyi]

[case testSignatureInheritance]
# flags: --inherit-signatures --check-untyped-defs
class Base:
def foo(self, a: float) -> float: return 2.0
class BadChild1(Base):
def foo(self, a): return "" # E: Incompatible return value type (got "str", expected "float")
0