Skip to content

ast.parse() wrong (IMHO) AnnAssign.target.ctx when value is absent. #95184

Closed as not planned
@mrolle45

Description

@mrolle45

Bug report

Example program:

a: int
a: int = 0
(a): int
(a): int = 0
a.b: int
a.b: int = 0

`ast.parse() for this module results in

Module(
    body=[
        AnnAssign(
            target=Name(id='a', ctx=Store()),    (OK)
            annotation=Name(id='int', ctx=Load()),
            simple=1),
        AnnAssign(
            target=Name(id='a', ctx=Store()),
            annotation=Name(id='int', ctx=Load()),
            value=Constant(value=0),
            simple=1),
        AnnAssign(
            target=Name(id='a', ctx=Store()),    <--
            annotation=Name(id='int', ctx=Load()),
            simple=0),
        AnnAssign(
            target=Name(id='a', ctx=Store()),
            annotation=Name(id='int', ctx=Load()),
            value=Constant(value=0),
            simple=0),
        AnnAssign(
            target=Attribute(
                value=Name(id='a', ctx=Load()),
                attr='b',
                ctx=Store()),                    <--
            annotation=Name(id='int', ctx=Load()),
            simple=0),
        AnnAssign(
            target=Attribute(
                value=Name(id='a', ctx=Load()),
                attr='b',
                ctx=Store()),
            annotation=Name(id='int', ctx=Load()),
            value=Constant(value=0),
            simple=0)],
    type_ignores=[])

In the two cases marked with <--, there is no assignment made to the target, so IMHO, the ctx should be Load(), not Store().

In fact, in

a = 10
b = 20
class C:
    (a): int
    b: int
    print(locals())
    print(a, b)
    b = 2
    print(locals())
    print(a, b)
def f():
    (a): int
    b: int
    print(locals())
    b = 2
    print(locals())
    print(a, b)
f()

The output is

{'__module__': '__main__', '__qualname__': 'C', '__annotations__': {'b': <class 'int'>}}
          --- C locals, b is missing
10 20      --- C.a, C.b = the global a and b
{'__module__': '__main__', '__qualname__': 'C', '__annotations__': {'b': <class 'int'>}, 'b': 2}
          --- C locals after assign to b, b is now present
10 2      --- C.a, C.b = global a, local b
{}         --- f locals
{'b': 2}   --- f.locals after assign to b
10 2       --- a, b = global a, local b

(a): int does not make a a local variable in f or C. In fact, it generates no code at all.
b: int does make b a local variable

Another example:

(a): int
global a    # OK
b: int
global b    # SyntaxError: annotated name 'b' can't be global

Impact

I have a code analysis tool that walks through the ast syntax tree. It finds Name nodes and records that the name is a local variable when the ctx=Store().
In an AnnAssign statement, with simple=0 , I have to make a special case and ignore the ctx=Store().
With a.b: int; print(a.b), this does not affect my tool (at present), but it may confuse a static typing tool like mypy into thinking that a.b has a value, rather than reporting the print(a.b) as an error.

Your environment

  • CPython versions tested on: 3.9.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions