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.

Author kj
Recipients gvanrossum, kj, larry, pbryan
Date 2021-04-29.15:36:15
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1619710575.65.0.0663118558617.issue42904@roundup.psfhosted.org>
In-reply-to
Content
@larry
> Is this "surprising, but required" behavior due specifically to this being a backwards-incompatible change?

Yes. That's the main factor. I've since learnt that there's sadly more to it though :( (see below).

> Does that mean I don't have the problem you're solving by reversing the order of namespace lookup, and I can just pass globals and locals in like normal?

I think it depends on the ergonomics of the function you're trying to achieve. I admit I haven't been fully keeping up with the inspect.get_annotations issue (sorry!), but here's what I learnt from get_type_hints:

(Partly copied over from PR 25632)
Example:

from typing import TypeVar, Generic
T = TypeVar('T')

class A(Generic[T]):
    __annotations__ = dict(
        some_b='B'
    )


class B(Generic[T]):
    class A(Generic[T]):
        pass
    __annotations__ = dict(
        my_inner_a1='B.A',
        my_inner_a2=A,
        my_outer_a='A'  # unless somebody calls get_type_hints with localns=B.__dict__
    )

>>> get_type_hints(B)

Currently (globalns then localns):
{'my_inner_a1': <class '__main__.B.A'>, 'my_inner_a2': <class '__main__.B.A'>, 'my_outer_a': <class '__main__.A'>}

Swapped (localns then globalns):
{'my_inner_a1': <class '__main__.B.A'>, 'my_inner_a2': <class '__main__.B.A'>, 'my_outer_a': <class '__main__.B.A'>}

I realized that looking into globalns then localns is a necessary evil: doing the converse (looking into localns first then globalns) would mean there is no way to point to the shadowed global `A`: it would always point to the local `B.A`. Unless of course you pass in localns=module.__dict__ or localns=globals().

Ultimately I think it's a sacrifice of ergonomics either way; looking into localns then globalns will require passing in the module's __dict__ to refer to a global being shadowed, while the converse (globalns then localns) introduces surprising eval behavior. Both are kind of tacky, but globalns then localns is slightly less so IMO. If the user wants to specify the inner class, they can just annotate 'B.A', if they want the outer, it's 'A'. But the localns then globalns approach will always point to `B.A`, the only way to access the shadowed global `A` is to pass in the strange looking argument localns=module.__dict__.

Phew, my head's spinning with localns and globalns now. Thanks for reading! I think it's your call. I'm inexperienced with elegant function design :P.
History
Date User Action Args
2021-04-29 15:36:15kjsetrecipients: + kj, gvanrossum, larry, pbryan
2021-04-29 15:36:15kjsetmessageid: <1619710575.65.0.0663118558617.issue42904@roundup.psfhosted.org>
2021-04-29 15:36:15kjlinkissue42904 messages
2021-04-29 15:36:15kjcreate