Callable type annotations like `open` don't work when using `from __future__ import annotations`
AgarwalPragy opened this issue · 11 comments
PEP 563 -- Postponed Evaluation of Annotations made it so that annotations are stored as string literals, instead of the actual objects.
The annotations must then be resolved at runtime. https://www.python.org/dev/peps/pep-0563/#resolving-type-hints-at-runtime
File "/Users/pragy.a/Desktop/categorization/iab_tester/venv/lib/python3.7/site-packages/cliar/cliar.py", line 163, in __init__
self._register_commands(handlers)
File "/Users/pragy.a/Desktop/categorization/iab_tester/venv/lib/python3.7/site-packages/cliar/cliar.py", line 242, in _register_commands
self._register_arg(command_parser, arg_name, arg_data)
File "/Users/pragy.a/Desktop/categorization/iab_tester/venv/lib/python3.7/site-packages/cliar/cliar.py", line 215, in _register_arg
help=arg_data.help
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/argparse.py", line 1358, in add_argument
raise ValueError('%r is not callable' % (type_func,))
ValueError: 'open' is not callable
How do you get this error? I'm on Python 3.7.3 and everything is working fine. If you inspect arg.type
after this line, you'll see that its type in the case of open
is not string but a function.
How do you get this error? I'm on Python 3.7.3 and everything is working fine. If you inspect
arg.type
after this line, you'll see that its type in the case ofopen
is not string but a function.
Postponed evaluation of annotations isn't available yet in Python 3.7.3. It is a feature that will appear in Python 3.8 and will be default behavior in Python 4.0.
For now, one can enable (part of) the feature by doing from __future__ import annotations
, which causes all obj.__annotations__
to be a dict from name to a string. The string must then be resolved at runtime to get the actual object back.
Example:
from __future__ import annotations
from typing import get_type_hints
class X:
var: open
print(X.__annotations__['var'], type(X.__annotations__['var'])) # open <class 'str'>
hints = get_type_hints(X)
print(hints['var'], type(hints['var'])) # <built-in function open> <class 'builtin_function_or_method'>
Just added from __future__ import annotations
to cliar.py and the tests still pass. What am I doing wrong?
Just added
from __future__ import annotations
to cliar.py and the tests still pass. What am I doing wrong?
That's weird. It should not work.
Breaking example:
from __future__ import annotations
from cliar import Cliar
import sys
class Breaking(Cliar):
def foo(self, some_file: open):
print(some_file)
if __name__ == '__main__':
print(sys.version)
Breaking().parse()
/Users/pragy.a/Desktop/categorization/iab_tester/venv/bin/python /Users/pragy.a/Desktop/categorization/iab_tester/src/breaking.py
3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 16:52:21)
[Clang 6.0 (clang-600.0.57)]
Traceback (most recent call last):
File "/Users/pragy.a/Desktop/categorization/iab_tester/src/breaking.py", line 13, in <module>
Breaking().parse()
File "/Users/pragy.a/Desktop/categorization/iab_tester/venv/lib/python3.7/site-packages/cliar/cliar.py", line 163, in __init__
self._register_commands(handlers)
File "/Users/pragy.a/Desktop/categorization/iab_tester/venv/lib/python3.7/site-packages/cliar/cliar.py", line 242, in _register_commands
self._register_arg(command_parser, arg_name, arg_data)
File "/Users/pragy.a/Desktop/categorization/iab_tester/venv/lib/python3.7/site-packages/cliar/cliar.py", line 215, in _register_arg
help=arg_data.help
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/argparse.py", line 1358, in add_argument
raise ValueError('%r is not callable' % (type_func,))
ValueError: 'open' is not callable
Process finished with exit code 1
If you remove the future import, it works
from cliar import Cliar
import sys
class Breaking(Cliar):
def foo(self, some_file: open):
print(some_file)
if __name__ == '__main__':
print(sys.version)
Breaking().parse()
/Users/pragy.a/Desktop/categorization/iab_tester/venv/bin/python /Users/pragy.a/Desktop/categorization/iab_tester/src/breaking.py
3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 16:52:21)
[Clang 6.0 (clang-600.0.57)]
usage: breaking.py [-h] {foo} ...
optional arguments:
-h, --help show this help message and exit
commands:
{foo} Available commands:
foo
Process finished with exit code 0
Cliar Version: 1.2.3
OS: MacOS High Sierra (10.13.6)
Thanks for the detailed issue report. I was able to reproduce and fix the issue: f40abea
However, the fix works only in Python 3.7, so we're losing backward compatibility with 3.6 :-(
I'm still unsure if this is the right thing to do, at least at this point in time... AFAIU this change will not affect us until 4.0 is released, which is in the distant future.
If it's not the default behavior in 3.8, than things should continue to work as they are, right?
Moved the fix to a separate PR, since I'm not entirely sure it should be applied right now: #6
However, the fix works only in Python 3.7, so we're losing backward compatibility with 3.6 :-(
Oh, that's bad.
I'm still unsure if this is the right thing to do, at least at this point in time... AFAIU this change will not affect us until 4.0 is released, which is in the distant future.
If it's not the default behavior in 3.8, than things should continue to work as they are, right?
As long as someone can find this issue and refer to your fix, they can patch their local copies of Cliar to get it working, if they absolutely can't live without the future import. So this is good enough. Thanks :D
OK nevermind, no compat break necessary. Merging and releasing Cliar 1.2.5 with the fix.
Fixed in 1.2.5: https://github.com/moigagoo/cliar/blob/develop/changelog.md#125