Repository: python/mypy

Issue:
TypeVar with Any upper bound generates missing attribute errors is used as Type[T]
Imagine the following code:

```
class Foo():
    NAME: ClassVar[str] = ""

T = TypeVar("T", bound=Foo)

def create_foo(object_class: Type[T]) -> T:
    print(T.NAME)  # Generates error in static analysis
    return T()
```

When this code is run through the static analysis, the marked line is flagged as an error because mypy thinks that `T` does not have the `NAME` attribute. When the same code is run through the dynamic analysis, the code works fine.


Discussion:
Surely you meant to write
```py
    print(object_class.NAME)
    return object_class()
```
This seems to work fine.
> Surely you meant to write

Ah, yes.

> This seems to work fine.

It works only in dynamic analysis, but not in static analysis. The static analysis generates this error:

```
"Type[T]" has no attribute "NAME"
```

Either adding `# type: ignore` or an explicit `cast(Type[Foo], object_class)` resolves the issue.
> It works only in dynamic analysis, but not in static analysis. The static analysis generates this error:

I'm confused. The version from your initial comment has a bug. Mypy reports an error because of that bug. If I fix the bug, mypy is happy:
```py
from typing import *

class Foo():
    NAME: ClassVar[str] = ""

T = TypeVar("T", bound=Foo)

def create_foo(object_class: Type[T]) -> T:
    print(object_class.NAME)
    return object_class()
```
Did you try that? Does it work for you?
~~deleted~~Sorry misfire. It appears the reported error from OP does not actually occur on corrected code, the error is in using `T` as a runtime name to replace the object.
> I'm confused. The version from your initial comment has a bug.

The bug was only in the bug report, the original code was written correctly (like in your code). I had to reduce my code to a smallest possible example to make it easier to understand. There was a typo in that, sorry :(

> If I fix the bug, mypy is happy:

Are you doing a static or dynamic analysis? It fails only in static analysis.

> Did you try that?

Yes. That is how my original code was written, the typo was introduced when I tried to reduce that for this bug report.

> Does it work for you?

Nope. It prints this:

```
"Type[T]" has no attribute "NAME"
```

Modifying the code as follows resolves the issue:

```
def create_foo(object_class: Type[T]) -> T:
    print(cast(Foo, object_class).NAME)
    return object_class()
```
What mypy version are you using? 

For Guido and myself

```python
from typing import *

class Foo():
    NAME: ClassVar[str] = ""

T = TypeVar("T", bound=Foo)

def create_foo(object_class: Type[T]) -> T:
    print(object_class.NAME)
    return object_class()
```

the above works with static analysis and at runtime.
At this point I think it would be useful if the OP clarified what they mean
by "static analysis" vs. "dynamic analysis". I've been understanding this
as "run mypy to check the program" versus "run the program with a Python
interpreter" but I wonder if there's more to it. Perhaps some corporate
infrastructure? A PyCharm feature? @CommanderPrS please clarify. How are
you running the static and dynamic analysis?

> What mypy version are you using?

mypy 0.550-dev

> How are you running the static and dynamic analysis?

To me, as a user, the static analysis is done by running `arc lint`. The dynamic analysis is done by running `buck test` (assuming there is `check_types = True` is added to the appropriate test/binary in the `TARGETS` file).

How EXACTLY mypy is hooked into Arcanist and Buck in our infrastructure, I am not 100% sure. The best person to ask is probably @ambv - I suspect he is the one who set the entire type checking thing up. Let me ping him to clarify this on this bug.
Alright, let's clarify first and then get to the actual issue.

@CommanderPrS, both cases are static analysis and both cases use mypy. The `arc lint` use case has a different config (only looks at the file under lint; ignores imports) whereas the `buck test` case runs a full mypy run over your entire program. It's wrapped as a unit test but it's still static analysis, your code is not executed by Python.

The actual example we're discussing is the one that Guido points out, not the one in the original comment. The original comment's code results in:
```
'T' is a type variable and only valid in type context
```

So I assume we're really talking about Guido's fixed example:
```py3
class C:
    NAME: str = "yeah"

T = TypeVar("T", bound=C)

def method(type: Type[T]) -> T:
    print(type.NAME)
```

**Now, finally, the actual issue.** When mypy is executed with `follow_imports=skip` and `ignore_missing_imports=True` then everything works fine for the example above if class C is in the same file. However, if class C comes from another file, then mypy raises an error:
```
"Type[T]" has no attribute "NAME"
```

Note: this only happens for a combination of Type and a TypeVar, e.g. `Type[T]`. If we would be using `Type[C]` or `C` in the annotation, mypy is happy regardless of whether C is in the same file or not.
OK this one is easy, this code in ``checkexpr.py``:
```python
        elif isinstance(typ.item, TypeVarType):
            if isinstance(typ.item.upper_bound, Instance):
                item = typ.item.upper_bound
```
should probably just consider other options for upper bound (such as ``Any`` in this case).
It would be nice if Facebook/Instagram engineers went through internal channels before reporting mypy issues, or at least clarified that they were Facebook/Instagram engineers when filing an issue. This issue wasted quite a bit of my time.
We're sorry!

We do have an internal channel to report problems, esp. that many of them end up being configuration-driven. While we cannot funnel people to *always* use it before writing on the bug tracker here, we will make a better job of advertising the internal channels from now on.

@gvanrossum, would it be helpful to reword `CONTRIBUTING.md` to explicitly address first-time bug reporters? For example, @carljm pointed out that it's important for us to always ask for a minimal repro using a clean virtualenv with mypy master installed in it. That would help in situations like this issue where integration is hiding what is happening under the hood.
I've been thinking about the bug reporting experience a bit, and I was thinking after finishing PEP 561 implementation about setting up a service that runs mypy on snippets of code in a virtualenv with a checked out version of master. It'd simplify noise and avoid issues like the above. Also we may consider starting to use the [issue template feature](https://help.github.com/articles/helping-people-contribute-to-your-project/) that Github provides.
 I think a simple issue template would be useful, even though most of the
time it will add noise -- we need to collect OS version, mypy version,
exact mypy invocation and probably other things to cover all bases, in
addition to example and errors, and please add comments pointing to the
lines where the errors occur -- but 90% of the time just the example and
the error is enough... A snippets service sounds a bit over-complicated.

I just encountered this issue, FFW (`Any` bound and missing attribute error).
ran into the same problem while using DataClasses and the Dataclasses_json library:

```
from dataclasses_json import DataClassJsonMixin

T = TypeVar('T', bound=DataClassJsonMixin)

class DataClassJsonDeserializer(Generic[T]):

    def __init__(self, dest_class: Type[T]):
        # TODO: Workaround for this issue:
        # https://github.com/python/mypy/issues/4351
        self.dest_class = cast(DataClassJsonMixin, dest_class)

    def deserialize(self, msg: bytes) -> T:
        return cast(T, self.dest_class.from_json(msg.decode()))
```

Relevant mypy.ini config is:

```
[mypy]
python_version = 3.7
warn_unused_configs = True
warn_return_any = True
follow_imports = silent
```

What I don't quite grok is why the inverse code (a serializer) doesn't require the object class:

```
class DataClassJsonSerializer(Generic[T]):
    def serialize(self, r: T) -> bytes:
        return cast(str, r.to_json()).encode()
```

Why does mypy assume `to_json()` exists on the class instance, but can't resolve the static function `from_json()` on the same type?

Resolving patch:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -280,6 +280,9 @@ def analyze_type_type_member_access(name: str,
             item = upper_bound
         elif isinstance(upper_bound, TupleType):
             item = tuple_fallback(upper_bound)
+        elif isinstance(upper_bound, AnyType):
+            mx = mx.copy_modified(messages=ignore_messages)
+            return _analyze_member_access(name, fallback, mx, override_info)
     elif isinstance(typ.item, TupleType):
         item = tuple_fallback(typ.item)
     elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj():


Test patch:
diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test
--- a/test-data/unit/check-typevar-values.test
+++ b/test-data/unit/check-typevar-values.test
@@ -598,6 +598,18 @@ class C:
 
 [builtins fixtures/list.pyi]
 
+[case testTypeVarWithAnyTypeBound]
+# flags: --follow-imports=skip
+from typing import Type, TypeVar
+from a import A
+T = TypeVar('T', bound=A)
+def method(t: Type[T]) -> None:
+    t.a
+[file a.py]
+class A:
+    a: int = 7
+[out]
+
 [case testParameterLessGenericAsRestriction]
 from typing import Sequence, Iterable, TypeVar
 S = TypeVar('S', Sequence, Iterable)