-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add __dataclass_fields__ and __attrs_attrs__ to dataclasses #8578
Conversation
mypy/plugins/attrs.py
Outdated
def _add_attrs_magic_attribute(ctx: 'mypy.plugin.ClassDefContext') -> None: | ||
attr_name = '__attrs_attrs__' | ||
attr_type = ctx.api.named_type('__builtins__.tuple', [ | ||
ctx.api.named_type('attr.Attribute'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilevkivskyi ctx.api.named_type
is for builtin types, but what i should use instead of it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not for builtin types, it is for types that are guaranteed to be present (i.e. loaded).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilevkivskyi, and how to ensure that attr.Attribute
is loaded? Or maybe need to use Any
if it is not loaded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should be loaded, because the attr
module must be analyzed if this plugin is called. The tests fail because they run with partial stubs, not the real ones, see for example https://github.com/python/mypy/blob/master/test-data/unit/lib-stub/attr.pyi, Attribute
is not present there.
Also the latter is generic, so I would explicitly add a type argument here (even if it is Any
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should be loaded, because the attr module must be analyzed if this plugin is called.
I have a problem with different styles of import of attr
.
This example works fine:
import attr
@attr.s
class Foo:
pass
class AttrsProtocol(Protocol):
__attrs_attrs__: Tuple[attr.Attribute, ...]
def foo(x: AttrsProtocol) -> None:
pass
foo(Foo())
But if I change import to:
from attr import attrs, Attribute
@attrs
class Foo:
pass
I get exception:
File "/Users/tkukushkin/Projects/mypy/mypy/plugins/attrs.py", line 664, in _add_attrs_magic_attribute
ctx.api.named_type('attr.Attribute'),
File "/Users/tkukushkin/Projects/mypy/mypy/semanal.py", line 4143, in named_type
assert sym, "Internal error: attempted to construct unknown type"
AssertionError: Internal error: attempted to construct unknown type
Some tests fail because of this.
The tests fail because they run with partial stubs, not the real ones, see for example https://github.com/python/mypy/blob/master/test-data/unit/lib-stub/attr.pyi,
Attribute
is not present there.
Yes, I already added Attribute
to stub, but not pushed yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I just looked at the code of named_type()
and it is horrible, it uses lookup_qualified()
(which is just a normal symbol lookup following Python name resolution rules) so I wonder how it ever worked. You can try using named_type_or_none()
instead, that one looks reasonable.
Another option is to actually fix named_type()
, but it may be not the best task as a first contribution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add few dedicated tests and make this a non-draft.
mypy/plugins/attrs.py
Outdated
def _add_attrs_magic_attribute(ctx: 'mypy.plugin.ClassDefContext') -> None: | ||
attr_name = '__attrs_attrs__' | ||
attr_type = ctx.api.named_type('__builtins__.tuple', [ | ||
ctx.api.named_type('attr.Attribute'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should be loaded, because the attr
module must be analyzed if this plugin is called. The tests fail because they run with partial stubs, not the real ones, see for example https://github.com/python/mypy/blob/master/test-data/unit/lib-stub/attr.pyi, Attribute
is not present there.
Also the latter is generic, so I would explicitly add a type argument here (even if it is Any
).
mypy/plugins/dataclasses.py
Outdated
@@ -361,6 +363,21 @@ def _freeze(self, attributes: List[DataclassAttribute]) -> None: | |||
var._fullname = info.fullname + '.' + var.name | |||
info.names[var.name] = SymbolTableNode(MDEF, var) | |||
|
|||
def _add_dataclass_fields_magic_attribute(self) -> None: | |||
attr_name = '__dataclass_fields__' | |||
dataclass_field_type = self._ctx.api.named_type('dataclasses.Field') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here.
@ilevkivskyi Thanks for your suggestions. And second question: how do you recommend to fix tests |
@tkukushkin Thanks for updates! Here are answers to your questions:
I think it would make sense to add it.
I think creating dedicated partial stubs (fixtures) is fine.
Good catch! Yeah, I think this test passes because of bad partial stubs. In real stubs You can open a separate issue/PR for this. |
Fixed |
@JukkaL maybe you can review this pr? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few small comments, and CI is currently failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good apart from the stray file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I'll merge once CI passes (if I don't, feel free to remind me).
I really don't want to be "that guy" and ask when this will see a release... but do you have published release cadences? This has been merged for a month now and the last release was in June. I'm not trying to phrase this in an "omg what's the hold up?" kind of way - I totally get that other things can be blocking the release. It's just that I happened across an issue this morning whilst consuming djangorestframework-dataclasses that would be solved by this feature, so I'm a little itchy to see it released :) |
@tkukushkin Thanks a lot for this, I think this work is going to be very valuable going forward and I can close my failed attempt at #10467. How difficult would it be to have |
@Tinche I spent some time and got such a proof of concept: class AttrsFieldsPlugin(Plugin):
def get_function_signature_hook(self, fullname: str) -> Optional[Callable[[FunctionSigContext], FunctionLike]]:
if fullname == 'attr.fields':
return self._attrs_fiels_hook
return None
def _attrs_fiels_hook(self, context: FunctionSigContext) -> FunctionLike:
return CallableType(
arg_types=[AnyType(TypeOfAny.explicit)],
arg_kinds=[ArgKind.ARG_POS],
arg_names=['cls'],
ret_type=context.args[0][0].node.names['__attrs_attrs__'].type,
fallback=context.api.named_type('builtins.function'),
) Unfortunately, I don't have time to finalize this solution (need to correct the type of the function argument and deal with the situation when not attrs class passed to the I also understand attrs.fields returns named tuple, in this solution the function returns regular tuple, and I'm not sure if there is a way to fix that. |
Hello again! Thanks for looking into this. I think The problem isn't fields, but as you said the fact the plugin generates tuples instead of namedtuples for In any case, thanks for getting us this far. |
@Tinche I looked at your pull request – beautiful solution, didn't think of that. But yes, I don't really know how to make |
Blocking issue resolved by this update to mypy python/mypy#6568 python/mypy#8578 we can now check that a DataClass object conforms to our ConfigProtocol Protocol, allowing us to pass ally mypy checks.
Blocking issue resolved by this update to mypy python/mypy#6568 python/mypy#8578 we can now check that a DataClass object conforms to our ConfigProtocol Protocol, allowing us to pass ally mypy checks.
Fixes #6568.