Repository: python/mypy

Issue:
stubgen doesn't add import for typing.Any when function signature has unannotated arguments
I'm pretty much sure the description is dumb, but not sure how to properly describe it. Let the tests speak for themselves:

```python
[case testFunctionPartiallyAnnotated]
def f(x) -> None:
    pass

def g(x, y: str):
    pass

class A:
    def f(self, x) -> None:
        pass

[out]
from typing import Any

def f(x: Any) -> None: ...
def g(x: Any, y: str) -> Any: ...

class A:
    def f(self, x: Any) -> None: ...
```

Test run yields:

```sh
$ pytest mypy/test/teststubgen.py::StubgenPythonSuite::testFunctionPartiallyAnnotated -v -n0
=================================== test session starts ===================================
platform linux -- Python 3.6.8, pytest-4.6.3, py-1.8.0, pluggy-0.12.0 -- /home/hoefling/projects/.venvs/mypy-py36/bin/python3.6
cachedir: .pytest_cache
rootdir: /home/hoefling/projects/python/mypy, inifile: pytest.ini
plugins: cov-2.7.1, xdist-1.29.0, forked-1.0.2
collected 1 item                                                                          

mypy/test/teststubgen.py::StubgenPythonSuite::testFunctionPartiallyAnnotated FAILED [100%]

======================================== FAILURES =========================================
_____________________________ testFunctionPartiallyAnnotated ______________________________
data: /home/hoefling/projects/python/mypy/test-data/unit/stubgen.test:1457:
/home/hoefling/projects/python/mypy/mypy/test/teststubgen.py:281: in run_case
    self.run_case_inner(testcase)
/home/hoefling/projects/python/mypy/mypy/test/teststubgen.py:309: in run_case_inner
    testcase.file, testcase.line))
/home/hoefling/projects/python/mypy/mypy/test/helpers.py:114: in assert_string_arrays_equal
    raise AssertionError(msg)
E   AssertionError: Invalid output (/home/hoefling/projects/python/mypy/test-data/unit/stubgen.test, line 1457)
---------------------------------- Captured stderr call -----------------------------------
Expected:
  from typing import Any                        (diff)
                                                (diff)
  def f(x: Any) -> None: ...                    (diff)
  def g(x: Any, y: str) -> Any: ...             (diff)
                                                (diff)
  class A:                                      (diff)
  ...
Actual:
  def f(x: Any) -> None: ...                    (diff)
  def g(x: Any, y: str) -> Any: ...             (diff)
                                                (diff)
  class A:                                      (diff)
  ...

Alignment of first line difference:
  E: from typing import Any
  A: def f(x: Any) -> None: ...
     ^
================================ 1 failed in 0.24 seconds =================================
```

Basically, once you have added type to anything in the function signature, the import `from typing import Any` is not written to the stub. As a fix, I would suggest to add another check for `annotated_type` being of type `AnyType`:

```diff
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index d3bc3c4b..7635e791 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -402,6 +402,8 @@ class StubGenerator(mypy.traverser.TraverserVisitor):
                 annotation = ": Any"
             elif annotated_type and not is_self_arg:
                 annotation = ": {}".format(self.print_annotation(annotated_type))
+                if isinstance(annotated_type, mypy.types.AnyType):
+                    self.add_typing_import("Any")
             else:
                 annotation = ""
             if arg_.initializer:
```

but not suggesting it as a PR until someone can confirm this is indeed the most appropriate solution for the issue.


Discussion:
Another suggestion would be to record the import in `AnnotationPrinter`, treating `AnyType` in a similar fashion as the `UnboundType`:

```diff
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
index d3bc3c4b..f7063ca0 100755
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -80,7 +80,7 @@ from mypy.stubutil import (
 from mypy.stubdoc import parse_all_signatures, find_unique_signatures, Sig
 from mypy.options import Options as MypyOptions
 from mypy.types import (
-    Type, TypeStrVisitor, CallableType,
+    AnyType, Type, TypeStrVisitor, CallableType,
     UnboundType, NoneType, TupleType, TypeList,
 )
 from mypy.visitor import NodeVisitor
@@ -162,6 +162,11 @@ class AnnotationPrinter(TypeStrVisitor):
         super().__init__()
         self.stubgen = stubgen
 
+    def visit_any(self, t: AnyType) -> str:
+        s = super().visit_any(t)
+        self.stubgen.import_tracker.require_name(s)
+        return s
+
     def visit_unbound_type(self, t: UnboundType) -> str:
         s = t.name
         self.stubgen.import_tracker.require_name(s)
```

Resolving patch:
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -80,7 +80,7 @@
 from mypy.stubdoc import parse_all_signatures, find_unique_signatures, Sig
 from mypy.options import Options as MypyOptions
 from mypy.types import (
-    Type, TypeStrVisitor, CallableType,
+    Type, TypeStrVisitor, CallableType, AnyType,
     UnboundType, NoneType, TupleType, TypeList,
 )
 from mypy.visitor import NodeVisitor
@@ -166,6 +166,11 @@ def __init__(self, stubgen: 'StubGenerator') -> None:
         super().__init__()
         self.stubgen = stubgen
 
+    def visit_any(self, t: AnyType) -> str:
+        s = super().visit_any(t)
+        self.stubgen.import_tracker.require_name(s)
+        return s
+
     def visit_unbound_type(self, t: UnboundType) -> str:
         s = t.name
         self.stubgen.import_tracker.require_name(s)


Test patch:
diff --git a/test-data/unit/stubgen.test b/test-data/unit/stubgen.test
--- a/test-data/unit/stubgen.test
+++ b/test-data/unit/stubgen.test
@@ -1460,3 +1460,23 @@ def f(x):
 from typing import Any
 
 def f(x: Any): ...
+
+[case testFunctionPartiallyAnnotated]
+def f(x) -> None:
+    pass
+
+def g(x, y: str):
+    pass
+
+class A:
+    def f(self, x) -> None:
+        pass
+
+[out]
+from typing import Any
+
+def f(x: Any) -> None: ...
+def g(x: Any, y: str) -> Any: ...
+
+class A:
+    def f(self, x: Any) -> None: ...