Is this a typing.TypeAliasType bug?


def f():
    type i = int

    @dataclass
    class A:
            i: i

f()

I can do that just fine top-level, but if I do it inside a function I get NameError: name 'i' is not defined. Did you mean: 'id'?

Was this intended?

While classes aren’t true scopes, they still mostly behave like scopes within the class body[1].

So by naming the attribute i you are shadowing the i from the outer scope. However, since the assignment has not finished executing yet, i is still unbound. Just use a different name for the type alias, then it works just fine.


  1. excluding methods ↩︎

type i = int

@dataclass
class A:
        i: i

Sorry it wasn’t clear; this one works. Which is the oddity here.

If both didn’t work your explaination would make sense.

It is not related to type:

def f():
    name = 1

    class A:
        name: name


f()
    name: name
          ^^^^
NameError: name 'name' is not defined

Correct, it works with globals, but not with potential closures. It’s a quirk of how closures interact with the faux class scopes. Either way it doesn’t really have anything to do with TypeAliasType.

Generally I would steer clear of this pattern. It’s confusing to the reader, it also has the potential to confuse static analysis tools as soon as you defer evaluation of the annotation. Values and types are two different things, so they should be named differently.

2 Likes

See Naming and binding

A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.

def f():
    type i = int

    @dataclass
    class A:
        i: i

f()
    i = i
        ^
NameError: name 'i' is not defined. Did you mean: 'id'?
type i = int

@dataclass
class A:
    i: i

If the current scope is a function scope, and the name refers to a local variable that has not yet been bound to a value at the point where the name is used, an UnboundLocalError exception is raised. UnboundLocalError is a subclass of NameError.

def f():
    name = 'Python'

    def nested():
        name = name

    nested()

f()
    name = name
           ^^^^
UnboundLocalError: cannot access local variable 'name' where it is not associated with a value