Skip to content

Support unannotated converters for attr.ib #12815

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

Merged
merged 7 commits into from
Jun 23, 2022

Conversation

t4lz
Copy link
Contributor

@t4lz t4lz commented May 19, 2022

Description

Fixes #6172

If an unannotated converter function or a lambda expression is passed as a converter to attr.ib(), instead of giving an error, just take the type of the respective argument of the generated __init__() function to be Any, as suggested by @JelleZijlstra and @cgebbe.

Test Plan

Add two tests: one that tests the example from the issue of an unannotated function, and one that tests an example with a lambda expression as a converter.

@github-actions

This comment has been minimized.

Tal Zwick and others added 3 commits May 20, 2022 09:09
Instead of only saving the converter's type, and infering the init
argument type from that every time, directly save the init type, to
support unannotated converters (like lambdas) more easily.
For attributes that do not have a converter, have None for the Converter
instead of Converter(None).
Infer the type of the respective argument to the __init__ function to be
of type `Any`.
Make the type of the respective __init__ argument Any.
@t4lz t4lz force-pushed the attrs-converters branch from c3615da to 8e557f8 Compare May 20, 2022 07:12
@github-actions

This comment has been minimized.

@t4lz
Copy link
Contributor Author

t4lz commented Jun 15, 2022

Should I keep resolving new conflicts? Is anybody going to review this PR?

@JelleZijlstra JelleZijlstra self-requested a review June 15, 2022 14:59
@JelleZijlstra
Copy link
Member

I'll take a look next week. Sorry for the wait!

@t4lz
Copy link
Contributor Author

t4lz commented Jun 16, 2022

Great, thanks! No worries. I'll try to get to resolving the conflicts by then :)

Copy link
Member

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found some time today to review it! I fixed the merge conflict since it was easy, but have a few questions.


@attr.s
class Bar:
field = attr.ib(default=None, converter=foo)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some tests to ensure the behavior is correct here?

For example, use reveal_type(i.field) on an instance of Bar. Construct an instance with Bar(1) or some other type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, thank you! Is this better now?


@attr.s
class Bar:
name: str = attr.ib(converter=lambda s: s.lower())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added also here :)

is_attr_converters_optional: bool = False,
is_invalid_converter: bool = False) -> None:
self.type = type
self.is_attr_converters_optional = is_attr_converters_optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we need these attributes any more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the init_type inference logic was moved from Attribute initialization to parse_converter() and all of those attributes were only used for this inference. Now they are used internally on the parsing of the converter, but are not carried around in the Converter object, only the relevant init_type.


In a lot more detail:

Converter.type was holding the type of the function that was used as a converter. Now that we want to also deal with converters that do not have such a type, this field would be less meaningful. As this type is only used to infer an init_type for the attribute, and we now already have the init_type, we do not need to keep this value.
is_attr_converters_optional was used before to take the (deserialized) Converter object and when inferring an init_type from it and making it that type a Union with NoneType if optional. This is still done all the same, but directly on parsing the converter, so this internal value does not have to be saved and passed (and [de]serialized).
Same thing with is_invalid_converter: before it was kept in the Converter info so that if it is True AnyType(TypeOfAny.from_error) can be given as the init_type, now we directly keep the appropriate init_type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that clears it up.

@github-actions

This comment has been minimized.

`reveal_type()` and check output in the test for unannotated attrs
converter and lambda as a converter.
@JelleZijlstra JelleZijlstra self-assigned this Jun 20, 2022
@github-actions
Copy link
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@JelleZijlstra JelleZijlstra merged commit 5039c0f into python:master Jun 23, 2022
Gobot1234 pushed a commit to Gobot1234/mypy that referenced this pull request Aug 12, 2022
### Description

Fixes python#6172

If an unannotated converter function or a lambda expression is passed as a converter to `attr.ib()`, instead of giving an error, just take the type of the respective argument of the generated `__init__()` function to be `Any`, as suggested by @JelleZijlstra and @cgebbe.

## Test Plan

Add two tests: one that tests the example from the issue of an unannotated function, and one that tests an example with a lambda expression as a converter.

Co-authored-by: t4lz <t4lz.git@gmail.com>
Co-authored-by: Tal Zwick <tal.zwick@astrazeneca.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"Unsupported converter" for completely unanotated function
2 participants