Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class __annotations__ Dict Should Be Eagerly Evaluated #3839

Open
seandstewart opened this issue Sep 22, 2020 · 1 comment
Open

Class __annotations__ Dict Should Be Eagerly Evaluated #3839

seandstewart opened this issue Sep 22, 2020 · 1 comment

Comments

@seandstewart
Copy link
Contributor

Is your feature request related to a problem? Please describe.
As was found in #3829 - CPython currently supports using a custom mapping implementation for the __annotations__ object. This is not explicitly stated in the runtime effects of PEP 526, but there is an explicit test for it in test_grammar:

https://github.com/cython/cython/blob/master/tests/run/test_grammar.py#L491-L498

Describe the solution you'd like
As of #3829, we support the __annotations__ mapping on class scopes, but do so by creating the entry after we've analysed the rest of the class body and gathered annotations.

In order to support custom mappings for the __annotations__ namespace, and also support other side-effects of eager-declaration, such as del __annotations__, we should declare the __annotations__ entry in the scope once the first annotation in the body is encountered, then update the entry as new annotations are encountered.

This will also completely fulfill the requirement that the __annotations__ dict is mutable at runtime. As it is currently implemented, the __annotations__ dict is only mutable/delete-able after we construct the class.

Aside from the test in test_grammar, there is also additional behavior that should be supported:

class C:
    var: int
    del __annotations__

C.__annotations__
#> Traceback (most recent call last):
#>   File "<stdin>", line 1, in <module>
#> AttributeError: type object 'C' has no attribute '__annotations__'
class D:
    a: int
    __annotations__['b'] = 'str'

print(D.__annotations__)
#> {'a': 'int', 'b': 'str'}
class E:
    ...

E.__annotations__
#> Traceback (most recent call last):
#>   File "<input>", line 1, in <module>
#> AttributeError: type object 'E' has no attribute '__annotations__'
@da-woods
Copy link
Contributor

da-woods commented Sep 22, 2020

A few other tests that I think should be added to this - I suspect they'll pass fairly naturally with the full implementation, but it's worth making sure:

def test_branches(val):
    class C:
        # branches like this are unusual but supported
        if val:
            x: int = 1
        else:
            y: float = 2
    return C.__annotations__

def maybe_raise(val):
    if val:
        raise RuntimeError(val)
    return val

def test_exact_order(val):
    class C:
        try:
           x: int = maybe_raise(val)  # The annotation is set right after the assignment
        except:
           pass  # also, this does create an empty __annotations__ dict despite their being no annotated members
    return C.__annotations__

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants