From 51ab5947aca93659bbf48b89bc54b2e6711be37e Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 00:20:01 +0200 Subject: [PATCH 1/7] Use implicit Any for return values of abstract functions --- mypy/stubgen.py | 3 +-- test-data/unit/stubgen.test | 24 +++++++++--------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 5efc25de4f41..0fd2cdb1403d 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -658,8 +658,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, elif isinstance(o, FuncDef) and (o.is_abstract or o.name in METHODS_WITH_RETURN_VALUE): # Always assume abstract methods return Any unless explicitly annotated. Also # some dunder methods should not have a None return type. - retname = self.typing_name('Any') - self.add_typing_import("Any") + retname = None # implicit Any elif not has_return_statement(o) and not is_abstract: retname = 'None' retfield = '' diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 66980cf110e5..6c1401702303 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -390,10 +390,8 @@ class A: def __getstate__(self): ... def __setstate__(self, state): ... [out] -from typing import Any - class A: - def __eq__(self) -> Any: ... + def __eq__(self): ... -- Tests that will perform runtime imports of modules. -- Don't use `_import` suffix if there are unquoted forward references. @@ -1527,11 +1525,10 @@ class Base(metaclass=ABCMeta): import abc from abc import abstractmethod from base import Base -from typing import Any class C(Base, metaclass=abc.ABCMeta): @abstractmethod - def other(self) -> Any: ... + def other(self): ... [case testInvalidNumberOfArgsInAnnotation] def f(x): @@ -1666,12 +1663,11 @@ class A: [out] import abc -from typing import Any class A(metaclass=abc.ABCMeta): @property @abc.abstractmethod - def x(self) -> Any: ... + def x(self): ... [case testAbstractProperty2_semanal] import other @@ -1683,12 +1679,11 @@ class A: [out] import abc -from typing import Any class A(metaclass=abc.ABCMeta): @property @abc.abstractmethod - def x(self) -> Any: ... + def x(self): ... [case testAbstractProperty3_semanal] import other @@ -1700,12 +1695,11 @@ class A: [out] import abc -from typing import Any class A(metaclass=abc.ABCMeta): @property @abc.abstractmethod - def x(self) -> Any: ... + def x(self): ... [case testClassWithNameAnyOrOptional] def f(x=object()): @@ -2198,10 +2192,10 @@ from typing import Any class C: x: Any = ... def __init__(self, x: Any) -> None: ... - def __lt__(self, other: Any) -> Any: ... - def __le__(self, other: Any) -> Any: ... - def __gt__(self, other: Any) -> Any: ... - def __ge__(self, other: Any) -> Any: ... + def __lt__(self, other: Any): ... + def __le__(self, other: Any): ... + def __gt__(self, other: Any): ... + def __ge__(self, other: Any): ... [case testNamedTupleInClass] from collections import namedtuple From 9220d89629ec8e82db99c17c75060af856423207 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 00:42:06 +0200 Subject: [PATCH 2/7] Don't explicitly annotate implicit Any return types --- mypy/stubgen.py | 7 ++++++- test-data/unit/stubgen.test | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 0fd2cdb1403d..0fe3bf64f116 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -654,7 +654,12 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, args.append(arg) retname = None if o.name != '__init__' and isinstance(o.unanalyzed_type, CallableType): - retname = self.print_annotation(o.unanalyzed_type.ret_type) + if isinstance(o.unanalyzed_type.ret_type, AnyType): + # Luckily, a function explicitly annotated with "Any" has + # return type "UnboundType" and will enter the else branch. + retname = None # implicit Any + else: + retname = self.print_annotation(o.unanalyzed_type.ret_type) elif isinstance(o, FuncDef) and (o.is_abstract or o.name in METHODS_WITH_RETURN_VALUE): # Always assume abstract methods return Any unless explicitly annotated. Also # some dunder methods should not have a None return type. diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 6c1401702303..6c669b14b5fb 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -1555,11 +1555,28 @@ class A: from typing import Any def f(x: Any) -> None: ... -def g(x: Any, y: str) -> Any: ... +def g(x: Any, y: str): ... class A: def f(self, x: Any) -> None: ... +[case testExplicitReturnedAny] +from typing import Any + +def f(x: str) -> Any: + pass +def g(x, y: str) -> Any: + pass +def h(x) -> Any: + pass + +[out] +from typing import Any + +def f(x: str) -> Any: ... +def g(x: Any, y: str) -> Any: ... +def h(x: Any) -> Any: ... + [case testPlacementOfDecorators] class A: @property From a248840e4af62e59e0ead4c8184b2547eb0586cf Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 00:48:14 +0200 Subject: [PATCH 3/7] foo --- test-data/unit/stubgen.test | 81 ++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 32 deletions(-) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 6c669b14b5fb..9f0f4f2642fa 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -17,8 +17,8 @@ def g(arg): [out] from typing import Any -def f(a: Any, b: Any) -> None: ... -def g(arg: Any) -> None: ... +def f(a, b) -> None: ... +def g(arg) -> None: ... [case testDefaultArgInt] def f(a, b=2): ... @@ -26,7 +26,7 @@ def g(b=-1, c=0): ... [out] from typing import Any -def f(a: Any, b: int = ...) -> None: ... +def f(a, b: int = ...) -> None: ... def g(b: int = ..., c: int = ...) -> None: ... [case testDefaultArgNone] @@ -61,7 +61,7 @@ def f(x=ord): ... [out] from typing import Any -def f(x: Any = ...) -> None: ... +def f(x=...) -> None: ... [case testPreserveFunctionAnnotation] def f(x: Foo) -> Bar: ... @@ -83,14 +83,14 @@ def f(x, *y): ... [out] from typing import Any -def f(x: Any, *y: Any) -> None: ... +def f(x, *y) -> None: ... [case testKwVarArgs] def f(x, **y): ... [out] from typing import Any -def f(x: Any, **y: Any) -> None: ... +def f(x, **y) -> None: ... [case testVarArgsWithKwVarArgs] def f(a, *b, **c): ... @@ -101,11 +101,11 @@ def j(a, *, b=1, **c): ... [out] from typing import Any -def f(a: Any, *b: Any, **c: Any) -> None: ... -def g(a: Any, *b: Any, c: int = ...) -> None: ... -def h(a: Any, *b: Any, c: int = ..., **d: Any) -> None: ... -def i(a: Any, *, b: int = ...) -> None: ... -def j(a: Any, *, b: int = ..., **c: Any) -> None: ... +def f(a, *b, **c) -> None: ... +def g(a, *b, c: int = ...) -> None: ... +def h(a, *b, c: int = ..., **d) -> None: ... +def i(a, *, b: int = ...) -> None: ... +def j(a, *, b: int = ..., **c) -> None: ... [case testClass] class A: @@ -116,7 +116,7 @@ def g(): ... from typing import Any class A: - def f(self, x: Any) -> None: ... + def f(self, x) -> None: ... def g() -> None: ... @@ -265,7 +265,7 @@ def foo(x): ... [out] from typing import Any -def foo(x: Any) -> None: ... +def foo(x) -> None: ... [case testMultipleAssignment] x, y = 1, 2 @@ -295,8 +295,8 @@ def g(x, *, y=1, z=2): ... [out] from typing import Any -def f(x: Any, *, y: int = ...) -> None: ... -def g(x: Any, *, y: int = ..., z: int = ...) -> None: ... +def f(x, *, y: int = ...) -> None: ... +def g(x, *, y: int = ..., z: int = ...) -> None: ... [case testProperty] class A: @@ -315,7 +315,7 @@ class A: @property def f(self): ... @f.setter - def f(self, x: Any) -> None: ... + def f(self, x) -> None: ... def h(self) -> None: ... [case testStaticMethod] @@ -327,7 +327,7 @@ from typing import Any class A: @staticmethod - def f(x: Any) -> None: ... + def f(x) -> None: ... [case testClassMethod] class A: @@ -774,7 +774,7 @@ def syslog(a): pass [out] from typing import Any -def syslog(a: Any) -> None: ... +def syslog(a) -> None: ... [case testAsyncAwait_fast_parser] async def f(a): @@ -782,7 +782,7 @@ async def f(a): [out] from typing import Any -async def f(a: Any) -> None: ... +async def f(a) -> None: ... [case testInferOptionalOnlyFunc] class A: @@ -1538,7 +1538,7 @@ def f(x): [out] from typing import Any -def f(x: Any): ... +def f(x): ... [case testFunctionPartiallyAnnotated] def f(x) -> None: @@ -1554,11 +1554,28 @@ class A: [out] from typing import Any -def f(x: Any) -> None: ... -def g(x: Any, y: str): ... +def f(x) -> None: ... +def g(x, y: str): ... class A: - def f(self, x: Any) -> None: ... + def f(self, x) -> None: ... + +[case testExplicitAnyArg] +from typing import Any + +def f(x: Any): + pass +def g(x, y: Any) -> str: + pass +def h(x: Any) -> str: + pass + +[out] +from typing import Any + +def f(x: Any): ... +def g(x, y: Any) -> str: ... +def h(x: Any) -> str: ... [case testExplicitReturnedAny] from typing import Any @@ -1574,8 +1591,8 @@ def h(x) -> Any: from typing import Any def f(x: str) -> Any: ... -def g(x: Any, y: str) -> Any: ... -def h(x: Any) -> Any: ... +def g(x, y: str) -> Any: ... +def h(x) -> Any: ... [case testPlacementOfDecorators] class A: @@ -1606,7 +1623,7 @@ class B: def x(self): ... y: str = ... @x.setter - def x(self, value: Any) -> None: ... + def x(self, value) -> None: ... [case testMisplacedTypeComment] def f(): @@ -1871,7 +1888,7 @@ def f(x, y): pass [out] from typing import Any -def f(x: Any, y: Any) -> None: ... +def f(x, y) -> None: ... [case testImportedModuleExits_import] # modules: a b c @@ -2208,11 +2225,11 @@ from typing import Any class C: x: Any = ... - def __init__(self, x: Any) -> None: ... - def __lt__(self, other: Any): ... - def __le__(self, other: Any): ... - def __gt__(self, other: Any): ... - def __ge__(self, other: Any): ... + def __init__(self, x) -> None: ... + def __lt__(self, other): ... + def __le__(self, other): ... + def __gt__(self, other): ... + def __ge__(self, other): ... [case testNamedTupleInClass] from collections import namedtuple From 2889a4bf4b514e65d7ef7a623fb961364ea1d668 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 01:12:24 +0200 Subject: [PATCH 4/7] Don't annotate implicit Any arguments --- mypy/stubgen.py | 35 +++++++++++++++++--------------- test-data/unit/stubgen.test | 40 +++---------------------------------- 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 0fe3bf64f116..2d737e0f2a9e 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -624,26 +624,25 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, # name their 0th argument other than self/cls is_self_arg = i == 0 and name == 'self' is_cls_arg = i == 0 and name == 'cls' - if (annotated_type is None - and not arg_.initializer - and not is_self_arg - and not is_cls_arg): - self.add_typing_import("Any") - annotation = ": {}".format(self.typing_name("Any")) - elif annotated_type and not is_self_arg and not is_cls_arg: + if annotated_type and not isinstance(annotated_type, AnyType) and \ + not is_self_arg and not is_cls_arg: + # Luckily, an argument explicitly annotated with "Any" has + # type "UnboundType" and will enter the else branch. annotation = ": {}".format(self.print_annotation(annotated_type)) else: annotation = "" if arg_.initializer: - initializer = '...' if kind in (ARG_NAMED, ARG_NAMED_OPT) and not any(arg.startswith('*') for arg in args): args.append('*') if not annotation: - typename = self.get_str_type_of_node(arg_.initializer, True) - annotation = ': {} = ...'.format(typename) + typename = self.get_str_type_of_node(arg_.initializer, True, False) + if typename == '': + annotation = '=...' + else: + annotation = ': {} = ...'.format(typename) else: - annotation += '={}'.format(initializer) + annotation += '=...' arg = name + annotation elif kind == ARG_STAR: arg = '*%s%s' % (name, annotation) @@ -655,8 +654,8 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, retname = None if o.name != '__init__' and isinstance(o.unanalyzed_type, CallableType): if isinstance(o.unanalyzed_type.ret_type, AnyType): - # Luckily, a function explicitly annotated with "Any" has - # return type "UnboundType" and will enter the else branch. + # Luckily, a return type explicitly annotated with "Any" has + # type "UnboundType" and will enter the else branch. retname = None # implicit Any else: retname = self.print_annotation(o.unanalyzed_type.ret_type) @@ -1154,7 +1153,8 @@ def is_private_member(self, fullname: str) -> bool: return False def get_str_type_of_node(self, rvalue: Expression, - can_infer_optional: bool = False) -> str: + can_infer_optional: bool = False, + can_be_any: bool = True) -> str: if isinstance(rvalue, IntExpr): return 'int' if isinstance(rvalue, StrExpr): @@ -1171,8 +1171,11 @@ def get_str_type_of_node(self, rvalue: Expression, isinstance(rvalue, NameExpr) and rvalue.name == 'None': self.add_typing_import('Any') return '{} | None'.format(self.typing_name('Any')) - self.add_typing_import('Any') - return self.typing_name('Any') + if can_be_any: + self.add_typing_import('Any') + return self.typing_name('Any') + else: + return '' def print_annotation(self, t: Type) -> str: printer = AnnotationPrinter(self) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 9f0f4f2642fa..5d838dd4fc2f 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -15,8 +15,6 @@ def f(a, b): def g(arg): pass [out] -from typing import Any - def f(a, b) -> None: ... def g(arg) -> None: ... @@ -24,8 +22,6 @@ def g(arg) -> None: ... def f(a, b=2): ... def g(b=-1, c=0): ... [out] -from typing import Any - def f(a, b: int = ...) -> None: ... def g(b: int = ..., c: int = ...) -> None: ... @@ -59,8 +55,6 @@ def f(x: float = ...) -> None: ... [case testDefaultArgOther] def f(x=ord): ... [out] -from typing import Any - def f(x=...) -> None: ... [case testPreserveFunctionAnnotation] @@ -81,15 +75,11 @@ x: Foo [case testVarArgs] def f(x, *y): ... [out] -from typing import Any - def f(x, *y) -> None: ... [case testKwVarArgs] def f(x, **y): ... [out] -from typing import Any - def f(x, **y) -> None: ... [case testVarArgsWithKwVarArgs] @@ -99,8 +89,6 @@ def h(a, *b, c=1, **d): ... def i(a, *, b=1): ... def j(a, *, b=1, **c): ... [out] -from typing import Any - def f(a, *b, **c) -> None: ... def g(a, *b, c: int = ...) -> None: ... def h(a, *b, c: int = ..., **d) -> None: ... @@ -113,8 +101,6 @@ class A: x = 1 def g(): ... [out] -from typing import Any - class A: def f(self, x) -> None: ... @@ -263,8 +249,6 @@ class B(A): ... @decorator def foo(x): ... [out] -from typing import Any - def foo(x) -> None: ... [case testMultipleAssignment] @@ -293,8 +277,6 @@ y: Any def f(x, *, y=1): ... def g(x, *, y=1, z=2): ... [out] -from typing import Any - def f(x, *, y: int = ...) -> None: ... def g(x, *, y: int = ..., z: int = ...) -> None: ... @@ -309,8 +291,6 @@ class A: def h(self): self.f = 1 [out] -from typing import Any - class A: @property def f(self): ... @@ -323,8 +303,6 @@ class A: @staticmethod def f(x): ... [out] -from typing import Any - class A: @staticmethod def f(x) -> None: ... @@ -772,16 +750,12 @@ class A(X): ... def syslog(a): pass def syslog(a): pass [out] -from typing import Any - def syslog(a) -> None: ... [case testAsyncAwait_fast_parser] async def f(a): x = await y [out] -from typing import Any - async def f(a) -> None: ... [case testInferOptionalOnlyFunc] @@ -1536,8 +1510,6 @@ def f(x): return '' [out] -from typing import Any - def f(x): ... [case testFunctionPartiallyAnnotated] @@ -1552,8 +1524,6 @@ class A: pass [out] -from typing import Any - def f(x) -> None: ... def g(x, y: str): ... @@ -1611,8 +1581,6 @@ class B: self.y = 'y' [out] -from typing import Any - class A: y: str = ... @property @@ -1736,8 +1704,7 @@ class A(metaclass=abc.ABCMeta): def x(self): ... [case testClassWithNameAnyOrOptional] -def f(x=object()): - return 1 +Y = object() def g(x=None): pass @@ -1752,7 +1719,8 @@ def Optional(): [out] from typing import Any as _Any -def f(x: _Any = ...): ... +Y: _Any + def g(x: _Any | None = ...) -> None: ... x: _Any @@ -1886,8 +1854,6 @@ def g() -> None: ... def f(x, y): pass [out] -from typing import Any - def f(x, y) -> None: ... [case testImportedModuleExits_import] From 15731f5f957ae8e83248c51bdc257ebc54bd7748 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 01:14:19 +0200 Subject: [PATCH 5/7] Fix spaces with annotation and default argument --- mypy/stubgen.py | 2 +- test-data/unit/stubgen.test | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 2d737e0f2a9e..b84728dda0ae 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -642,7 +642,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, else: annotation = ': {} = ...'.format(typename) else: - annotation += '=...' + annotation += ' = ...' arg = name + annotation elif kind == ARG_STAR: arg = '*%s%s' % (name, annotation) diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test index 5d838dd4fc2f..61657670e18b 100644 --- a/test-data/unit/stubgen.test +++ b/test-data/unit/stubgen.test @@ -59,8 +59,10 @@ def f(x=...) -> None: ... [case testPreserveFunctionAnnotation] def f(x: Foo) -> Bar: ... +def g(x: Foo = Foo()) -> Bar: ... [out] def f(x: Foo) -> Bar: ... +def g(x: Foo = ...) -> Bar: ... [case testPreserveVarAnnotation] x: Foo From f2dfba445518ddf80403f3f6cafec043ee6d7e2a Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 13:54:55 +0200 Subject: [PATCH 6/7] Use get_proper_type() --- mypy/stubgen.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 8d181b5b6db5..0eddf5fd6297 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -85,7 +85,7 @@ from mypy.options import Options as MypyOptions from mypy.types import ( Type, TypeStrVisitor, CallableType, UnboundType, NoneType, TupleType, TypeList, Instance, - AnyType + AnyType, get_proper_type ) from mypy.visitor import NodeVisitor from mypy.find_sources import create_source_list, InvalidSourceList @@ -624,13 +624,13 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, # name their 0th argument other than self/cls is_self_arg = i == 0 and name == 'self' is_cls_arg = i == 0 and name == 'cls' - if annotated_type and not isinstance(annotated_type, AnyType) and \ - not is_self_arg and not is_cls_arg: + annotation = "" + if annotated_type and not is_self_arg and not is_cls_arg: + print(annotated_type, get_proper_type(annotated_type)) # Luckily, an argument explicitly annotated with "Any" has - # type "UnboundType" and will enter the else branch. - annotation = ": {}".format(self.print_annotation(annotated_type)) - else: - annotation = "" + # type "UnboundType" and will not match. + if not isinstance(get_proper_type(annotated_type), AnyType): + annotation = ": {}".format(self.print_annotation(annotated_type)) if arg_.initializer: if kind in (ARG_NAMED, ARG_NAMED_OPT) and not any(arg.startswith('*') for arg in args): @@ -653,7 +653,7 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, args.append(arg) retname = None if o.name != '__init__' and isinstance(o.unanalyzed_type, CallableType): - if isinstance(o.unanalyzed_type.ret_type, AnyType): + if isinstance(get_proper_type(o.unanalyzed_type.ret_type), AnyType): # Luckily, a return type explicitly annotated with "Any" has # type "UnboundType" and will enter the else branch. retname = None # implicit Any From dc0ce20be5f9c21caf9c14af695cc7e80805eb64 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 11 Jun 2021 16:25:52 +0200 Subject: [PATCH 7/7] Remove debug statement Co-authored-by: Jelle Zijlstra --- mypy/stubgen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 0eddf5fd6297..7028b8da04b6 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -626,7 +626,6 @@ def visit_func_def(self, o: FuncDef, is_abstract: bool = False, is_cls_arg = i == 0 and name == 'cls' annotation = "" if annotated_type and not is_self_arg and not is_cls_arg: - print(annotated_type, get_proper_type(annotated_type)) # Luckily, an argument explicitly annotated with "Any" has # type "UnboundType" and will not match. if not isinstance(get_proper_type(annotated_type), AnyType):