Yep, your example, *boom*.

My use case is to for type annotations to resolve type encoders,
decoders and validators at runtime.

Without __future__, type annotations specified in closure scope are
correctly attached to class variables, function parameters and return
types. Because they're in scope at the time they're evaluated.

__future__ annotations breaks because the hint is not evaluated until
get_type_hints is called, which is too late; the scope is lost.

Instead of just storing an annotation as a string, how about storing an
object containing the string, plus the local scope? Then get_type_hint
could be made to successfully resolve it.


On Tue, 2020-12-08 at 20:44 -0800, Gregory P. Smith wrote:
> What is the utility of a type annotation when the thing it refers to
> cannot exist?
> 
> Deferred annotation lookups are intended to be something that
> analysis time can make sense of but can always have no useful meaning
> at runtime.
> 
> No nesting required:
> 
> ```
> from __future__ import annotations
> Class X:
>   ...
> 
> def foo(hi: X):
>   ...
> 
> del X
> ```
> 
> Now try analyzing foo at runtime...  I assume "Boom" with that
> NameError again?  (On a phone, can't try it now)
> 
> I believe this isn't a problem get_type_hints() can ever solve.
> 
> Code that does this isn't what I'd call "reasonably" structured for
> use with type hints. 
> 
> If anything, type checkers should try to warn about it?
> 
> -gps
> 
> On Tue, Dec 8, 2020, 7:03 PM Paul Bryan <pbr...@anode.ca> wrote:
> > Let's try an example that static type checkers should have no
> > problem with:
> > 
> > Python 3.9.0 (default, Oct 7 2020, 23:09:01) 
> > [GCC 10.2.0] on linux
> > Type "help", "copyright", "credits" or "license" for more
> > information.
> > >>> from __future__ import annotations
> > >>> 
> > >>> def make_a_class():
> > ... class A:
> > ... def get_b(self) -> B:
> > ... return B()
> > ... class B:
> > ... def get_a(self) -> A:
> > ... return A()
> > ... return A
> > ... 
> > >>> A = make_a_class()
> > >>> a = A()
> > >>> 
> > >>> import typing
> > >>> typing.get_type_hints(a.get_b)
> > Traceback (most recent call last):
> > File "<stdin>", line 1, in <module>
> > File "/usr/lib/python3.9/typing.py", line 1386, in get_type_hints
> > value = _eval_type(value, globalns, localns)
> > File "/usr/lib/python3.9/typing.py", line 254, in _eval_type
> > return t._evaluate(globalns, localns, recursive_guard)
> > File "/usr/lib/python3.9/typing.py", line 493, in _evaluate
> > eval(self.__forward_code__, globalns, localns),
> > File "<string>", line 1, in <module>
> > NameError: name 'B' is not defined
> > >>> 
> > 
> > 
> > 
> > 
> > On Tue, 2020-12-08 at 18:48 -0800, Guido van Rossum wrote:
> > > Yeah, static type checkers won't like it regardless.
> > > 
> > > On Tue, Dec 8, 2020 at 6:39 PM Paul Bryan <pbr...@anode.ca>
> > > wrote:
> > > > It appears that when from future import __annotations__, a type
> > > > hint annotation derived from a closure loses scope.
> > > > 
> > > > Simplistic example:
> > > > 
> > > > Python 3.9.0 (default, Oct 7 2020, 23:09:01) 
> > > > [GCC 10.2.0] on linux
> > > > Type "help", "copyright", "credits" or "license" for more
> > > > information.
> > > > >>> def make_a_class(data_type):
> > > > ... class Foo:
> > > > ... def put_data(self, data: data_type):
> > > > ... self.data = data
> > > > ... return Foo
> > > > ... 
> > > > >>> import typing
> > > > >>> foo = make_a_class(str)()
> > > > >>> typing.get_type_hints(foo.put_data)
> > > > {'data': <class 'str'>}
> > > > >>> 
> > > > 
> > > > 
> > > > If I add a single import to the top, it breaks:
> > > > 
> > > > Python 3.9.0 (default, Oct 7 2020, 23:09:01) 
> > > > [GCC 10.2.0] on linux
> > > > Type "help", "copyright", "credits" or "license" for more
> > > > information.
> > > > >>> from __future__ import annotations # added this line
> > > > >>> def make_a_class(data_type):
> > > > ... class Foo:
> > > > ... def put_data(self, data: data_type):
> > > > ... self.data = data
> > > > ... return Foo
> > > > ... 
> > > > >>> import typing
> > > > >>> foo = make_a_class(str)()
> > > > >>> typing.get_type_hints(foo.put_data)
> > > > Traceback (most recent call last):
> > > > File "<stdin>", line 1, in <module>
> > > > File "/usr/lib/python3.9/typing.py", line 1386, in
> > > > get_type_hints
> > > > value = _eval_type(value, globalns, localns)
> > > > File "/usr/lib/python3.9/typing.py", line 254, in _eval_type
> > > > return t._evaluate(globalns, localns, recursive_guard)
> > > > File "/usr/lib/python3.9/typing.py", line 493, in _evaluate
> > > > eval(self.__forward_code__, globalns, localns),
> > > > File "<string>", line 1, in <module>
> > > > NameError: name 'data_type' is not defined
> > > > >>> 
> > > > 
> > > > 
> > > > I don't see how I can supply the closure scope as localns to
> > > > get_type_hints. Any suggestions? Is constructing a
> > > > (dynamically-type-annotated) class in a function like this an
> > > > anti-pattern?
> > > > 
> > > > _______________________________________________
> > > > Python-Dev mailing list -- python-dev@python.org
> > > > To unsubscribe send an email to python-dev-le...@python.org
> > > > https://mail.python.org/mailman3/lists/python-dev.python.org/
> > > > Message archived at
> > > >
> > >
> >
> https://mail.python.org/archives/list/python-dev@python.org/message/5RK6VXF263F5I4CU7FUMOGOYN2UQG73Q/
> > > > Code of Conduct: http://python.org/psf/codeofconduct/
> > > 
> > > 
> > > _______________________________________________
> > > Python-Dev mailing list -- python-dev@python.org
> > > To unsubscribe send an email to python-dev-le...@python.org
> > > https://mail.python.org/mailman3/lists/python-dev.python.org/
> > > Message archived at
> > >
> >
> https://mail.python.org/archives/list/python-dev@python.org/message/NRH4HBD36WDIP4WR2L4TLTOYMQL2NUFV/
> > > Code of Conduct: http://python.org/psf/codeofconduct/
> > 
> > 
> > _______________________________________________
> > Python-Dev mailing list -- python-dev@python.org
> > To unsubscribe send an email to python-dev-le...@python.org
> > https://mail.python.org/mailman3/lists/python-dev.python.org/
> > Message archived at
> >
> https://mail.python.org/archives/list/python-dev@python.org/message/6P5GFROAVAYXU3DTELZRHHRCDRYUEWCG/
> > Code of Conduct: http://python.org/psf/codeofconduct/

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/TEFDJ67X6X5HKDMW37EABYUKUAY5CWVN/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to