argparse_dataclass
argparse_dataclass copied to clipboard
ValueError when using `from __future__ import annotations`
With the below script, I get an exception:
from __future__ import annotations
from argparse_dataclass import dataclass
@dataclass
class MyArgs:
foo: int = 0
bar: str = 'hello'
baz: bool = False
def main():
print(MyArgs.parse_args())
if __name__ == '__main__':
main()
$ python3 targ.py
Traceback (most recent call last):
File "/Users/ibadawi/targ.py", line 4, in <module>
@dataclass
^^^^^^^^^
File "/Users/ibadawi/Library/Python/3.11/lib/python/site-packages/argparse_dataclass.py", line 474, in dataclass
return wrap(cls)
^^^^^^^^^
File "/Users/ibadawi/Library/Python/3.11/lib/python/site-packages/argparse_dataclass.py", line 465, in wrap
cls.parse_args = staticmethod(ArgumentParser(cls).parse_args)
^^^^^^^^^^^^^^^^^^^
File "/Users/ibadawi/Library/Python/3.11/lib/python/site-packages/argparse_dataclass.py", line 423, in __init__
_add_dataclass_options(options_class, self)
File "/Users/ibadawi/Library/Python/3.11/lib/python/site-packages/argparse_dataclass.py", line 374, in _add_dataclass_options
parser.add_argument(*args, **kwargs)
File "/usr/local/Cellar/[email protected]/3.11.2_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/argparse.py", line 1448, in add_argument
raise ValueError('%r is not callable' % (type_func,))
ValueError: 'int' is not callable
Removing from __future__ import annotations works fine and prints MyArgs(foo=0, bar='hello', baz=False) as expected.
It looks like the types as returned by dataclasses.fields() are just strings in this case. I'm not super familiar with writing code to process annotations but a workaround might be to use typing.get_type_hints() instead of dataclasses.fields() to get at the field types:
typing.get_type_hints(MyArgs):
{'bar': <class 'str'>, 'baz': <class 'bool'>, 'foo': <class 'int'>}
dataclasses.fields(MyArgs):
(Field(name='foo',type='int',default=0,default_factory=<dataclasses._MISSING_TYPE object at 0x10b1d5610>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD),
Field(name='bar',type='str',default='hello',default_factory=<dataclasses._MISSING_TYPE object at 0x10b1d5610>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD),
Field(name='baz',type='bool',default=False,default_factory=<dataclasses._MISSING_TYPE object at 0x10b1d5610>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=False,_field_type=_FIELD))
I encountered the same issue.
By patching argparse_dataclass.fields() based on @isbadawi's idea, I could solve the error.
from __future__ import annotations
from dataclasses import fields
import argparse_dataclass
from typing import get_type_hints
def _patch_fields(cls, *args, **kwargs):
t = get_type_hints(cls)
def _update(_f):
_f.type = t[_f.name]
return _f
return tuple(_update(f) for f in fields(cls, *args, **kwargs))
argparse_dataclass.fields = _patch_fields
@argparse_dataclass.dataclass
class MyArgs:
foo: int = 0
bar: str = 'hello'
baz: bool = False
def main():
print(MyArgs.parse_args())
if __name__ == '__main__':
main()
MyArgs(foo=0, bar='hello', baz=False)
See https://github.com/lovasoa/marshmallow_dataclass/pull/186 for a solution to a similar problem.
Personally I never do this import because it has caused problems in the past with other libraries but I would be willing to accept a PR that addresses this.
@mivade Thank you for your reply.
I sent PR (#51).