This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: get_type_hints evaluates class variables before type hints
Type: behavior Stage: resolved
Components: Versions: Python 3.9, Python 3.8
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: eric.smith, gvanrossum, kj, xirdneh
Priority: normal Keywords:

Created on 2021-02-18 18:53 by xirdneh, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (5)
msg387266 - (view) Author: Josue Balandrano Coronel (xirdneh) Date: 2021-02-18 18:53
Using typing.get_type_hints() on classes returns the wrong value when a class variable has the same name as a type and a default value.

For instance:

    from dataclasses import dataclass
    from typing import get_type_hints

    @dataclass
    class DataClassHints:
        str: str="asdf"

    >>> get_type_hints(DataClassHints)
    ... NameError: name 'asdf' is not defined

Looks like get_type_hints() is using "asdf" as a type.
This is more clear if we use something like datetime:

    from datetime import datetime

    class ClassHints:
        datetime: datetime=datetime.now()

    >>> get_type_hints(ClassHints)
    ... {'datetime': datetime.datetime(2021, 2, 18, 12, 40, 16, 966502)

If we do the same thing in a method get_type_hints works correctly:

    class CorrectHints:
        def __init__(self, str: str="asdf", datetime: datetime=datetime.now()):
            self.str = str
            self.datetime = datetime

    >>> ch = CorrectHints()
    >>> ch.str
    ... 'asdf'

    >>> ch.datetime
    ... datetime.datetime(2021, 2, 18, 12, 41, 46, 214844)

    >>> get_type_hints(CorrectHints.__init__)
    ... {'str': str, 'datetime': datetime.datetime}

Also functions work correctly:
 
    def func_type_hints(str: str="asdf", datetime: datetime=datetime.now()):
        return f"str: {str}, datetime: {datetime}"


    >>> func_type_hints()
    ... 'str: asdf, datetime: 2021-02-18 12:44:21.102933'

    >>> get_type_hints(func_type_hints)
    ... {'str': str, 'datetime': datetime.datetime}

It looks like get_type_hints is evaluating the default value in a class variable before the type hint.
Here's another way to replicate this issue to make it clearer:

    class EvalOrderHints:
        datetime = datetime.now()
        datetime: datetime

    >>> get_type_hints(EvalOrderHints)
    ... {'datetime': datetime.datetime(2021, 2, 18, 12, 47, 56, 608261)}

    >>> EvalOrderHints().datetime
    ... datetime.datetime(2021, 2, 18, 12, 47, 56, 608261)
msg387328 - (view) Author: Ken Jin (kj) * (Python committer) Date: 2021-02-19 15:00
Hmm I noticed this occurs in Python 3.9 but not 3.10. If you insert ``from __future__ import annotations`` at the start of your code, it stops erroring.

Anyways, I don't think this is dataclass specific, the following code using a plain class also errors:

```
from typing import get_type_hints
class T:
    str: str = 'a'

get_type_hints(T) # Error.
```

Inspecting __annotations__ tells us why:

>>> T.__annotations__
{'str': 'a'}

You can see that the annotations are wrong. Meanwhile with from __future__ import annotations:
>>> T.__annotations__
{'str': 'str'}

Seeing that SETUP_ANNOTATIONS in ceval.c didn't change, I suspect it's compiler related.
msg387331 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-02-19 15:44
Sure looks like a compiler bug! Can you investigate whether this occurs in earlier Python versions? Maybe bisect if it doesn’t?
msg387379 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2021-02-20 01:18
Oops, I realized it's not really a compiler bug.  When the compiler sees

class C:
    str: str = "abc"

if effectively rearranges that to

class C:
    str = "abc"
    __annotations__["str"] = str

(where __annotations__ is initialized to {} at the start of the class scope).

This goes to prove once again that silly things like

str: str

are an anti-pattern and should not be used.
msg404482 - (view) Author: Ken Jin (kj) * (Python committer) Date: 2021-10-20 16:02
Rejecting this since annotations are expressions that have side effects (so anti-patterns are discouraged). If you *really* want this, I suspect str: "str"="asdf" or using `from __future__ import annotations` will work.
History
Date User Action Args
2022-04-11 14:59:41adminsetgithub: 87423
2021-10-20 16:02:12kjsetstatus: open -> closed
resolution: not a bug
messages: + msg404482

stage: resolved
2021-02-20 01:18:52gvanrossumsetmessages: + msg387379
2021-02-19 15:44:01gvanrossumsetmessages: + msg387331
2021-02-19 15:00:25kjsetnosy: + gvanrossum, kj
messages: + msg387328
2021-02-18 21:57:14eric.smithsetnosy: + eric.smith
2021-02-18 18:53:09xirdnehcreate