pytype doesn't recognize custom wrapper of dataclasses.field
I'd like to wrap dataclasses.field in a function that sets some metadata on the dataclasses.field and use that instead of the dataclasses.field itself (mostly to make the code more compact and readable). However when I do that, pytype complains that "non-default argument... follows default argument". See this made-up example and the following pytype error:
import dataclasses
def my_field(*args, **kwargs):
return dataclasses.field(*args, metadata={"bar": 2}, **kwargs)
@dataclasses.dataclass()
class Foo(object):
a: int = 1
b: int = dataclasses.field(default=1)
c: int = my_field(default=1)
Computing dependencies
Analyzing 1 sources with 0 local dependencies
ninja: Entering directory `/Users/clupo/Github/picosvg/.pytype'
[1/1] check test_pytype_dataclasses
FAILED: /Users/clupo/Github/picosvg/.pytype/pyi/test_pytype_dataclasses.pyi
/Users/clupo/Github/picosvg/.tox/lint/bin/python -m pytype.single --disable pyi-error --imports_info /Users/clupo/Github/picosvg/.pytype/imports/test_pytype_dataclasses.imports --module-name test_pytype_dataclasses -V 3.8 -o /Users/clupo/Github/picosvg/.pytype/pyi/test_pytype_dataclasses.pyi --analyze-annotated --nofail --quick /Users/clupo/Github/picosvg/test_pytype_dataclasses.py
File "/Users/clupo/Github/picosvg/test_pytype_dataclasses.py", line 9, in <module>: In method __init__, non-default argument c follows default argument [invalid-function-definition]
For more details, see https://google.github.io/pytype/errors.html#invalid-function-definition.
ninja: build stopped: subcommand failed.
Leaving directory '/Users/clupo/Github/picosvg/.pytype'
I'm not actually sure if this could ever be made to work with a static typechecker, so feel free to close the issue if unfixable on your end. Thanks
Thanks for the report! The problem appears to be that function.Args (https://github.com/google/pytype/blob/2b87d9222d6547b2fdc916c49dedafcfd29ae34d/pytype/function.py#L288-L289) stores kwargs in the starstarargs field, but the dataclass overlay's _get_default_var method (https://github.com/google/pytype/blob/2b87d9222d6547b2fdc916c49dedafcfd29ae34d/pytype/overlays/dataclass_overlay.py#L143) only checks the namedargs field.
Just realized that my fix only works for the case in which my_field is used in the same file in which it's defined. We also need to somehow modify the generated .pyi file so that when another module imports the one containing my_field, it knows to treat it like dataclasses.field.
@rchen152 thank you for working on this!
Looking into this a bit more: we can probably detect dataclasses.field wrappers by checking for methods that forward their inputs and return dataclasses.Field (https://github.com/python/typeshed/blob/ece1a3e525a2970873c9522035a83eade65f4604/stdlib/dataclasses.pyi#L42). Since Field is generic, it should be possible to preserve the information of whether the field has a default.
Unassigning myself because I won't have time to work on this in the near future.