Skip to content

Way to change defaults? #630

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

Closed
quicknir opened this issue Mar 4, 2020 · 9 comments · Fixed by #650
Closed

Way to change defaults? #630

quicknir opened this issue Mar 4, 2020 · 9 comments · Fixed by #650
Labels

Comments

@quicknir
Copy link

quicknir commented Mar 4, 2020

I read this page on the mypy docs, https://mypy.readthedocs.io/en/stable/additional_features.html which says that you can't simply write a function like this:

def my_dataclass(cls):
    return attr.s(cls, auto_attribs=True)

And expect things to type check correctly.

Is there any convenient, mypy compatible way, to change defaults in attrs? I saw the RFC about defaults being inconvenient; better defaults (above all auto_attribs=True) will help but of course people will not necessarily agree on the best defaults, hence why being able to wrap it would be such a win.

It looks like pydantic has done it (https://github.com/samuelcolvin/pydantic/blob/master/pydantic/dataclasses.py), but it's hard for me to separate out the bits that are related to their extra functionality, from the bare minimum.

@hynek
Copy link
Member

hynek commented Mar 5, 2020

For you particular use case you can use our easter egg (that mypy knows about) @attr.dataclass until I finally get @attr.auto or @attrs.define done.

I guess being able to tell mypy about different options is a feature in mypy? Introducing global preferences for attrs is impossible, since too many projects in the ecosystem rely on it and that would cause quite the havoc.

I honestly find this frustrating too, since before typing, I had a bunch of pre-configured attrs decorators but that's all gone now. :|

@si14
Copy link

si14 commented Mar 21, 2020

+1 here. As a convention, we use attr.s(auto_attribs=True, slots=True, frozen=True) everywhere (with maybe a few exceptions when it has to be mutable) and it's a lot of boilerplate that we can't remove because of mypy. I can't imagine we're alone in this. Honestly, attrs changed the way we write code and it's absolutely brilliant, it'd be even better if we could avoid the boilerplate.

@hynek
Copy link
Member

hynek commented Mar 30, 2020

As said, that's all up to mypy, not us.

If we adopt mypy's attrs pluging as we discuss in #480, we might have a chance to do this.

It really depends on who is gonna be willing to help out tho, since I have no knowledge about mypy plugins and I'm not sure I have enough cycles free to learn it.

@euresti
Copy link
Contributor

euresti commented Apr 1, 2020

To be clear, we can make a pull request on mypy to fix this.

And what I have done to solve this problem is the following. Add a mypy plugin to your codebase that looks like this:

from mypy.plugin import Plugin
from mypy.plugins.attrs import (
    attr_attrib_makers,
    attr_class_makers,
    attr_dataclass_makers,
)

# These work just like attr.dataclass i.e. auto_attribs=True
attr_dataclass_makers.add("my_module.my_dataclass")

# This work just like attr.s i.e. it has the same defaults as attr.s
attr_class_makers.add("my_module.my_attr_s")

# These are our attr.ib makers.
attr_attrib_makers.add("my_module.my_attrib")

class MyPlugin(Plugin):
    # Our plugin does nothing but it has to exist so this file gets loaded.
    pass

def plugin(version):
    return MyPlugin

This won't let you change the defaults but if your methods take more arguments or have names you prefer you can use them and still get typechecking.

This is a python/mypy#5406 mypy issue that talks about the problem.

Edit: the argument to attr_class_makers.add line was incorrect.

@Tinche
Copy link
Member

Tinche commented Apr 1, 2020

@euresti Interesting.

Our current codebases have the problem that mypy doesn't recognize attrs wrappers. I've written a wrapper library for https://en.wikipedia.org/wiki/FlatBuffers that depends on attrs under the hood (things like this are a superpower of attrs with type definitions)[1]. You define classes like this:

@Flatbuffer(GetPostRequest)
class GetPostRequest:
    postId: str = attr.ib()

and it'll create an attrs class (under the hood it calls attr.s) and generate a bunch of hidden methods for de/serialization on it. Mypy currently has issues recognizing this as an attrs class. Any solution would be welcome.

[1] https://github.com/Tinche/flattrs

@quicknir
Copy link
Author

quicknir commented Apr 1, 2020

@euresti I have trouble understanding that code and your comment fully; why does it allow changing argument names but not changing defaults?

@euresti
Copy link
Contributor

euresti commented Apr 10, 2020

The mypy plugin basically detects calls to functions in attr_dataclass_makers and attr_class_makers and says whenever this method is called I need to modify the constructor and add all these attributes to the type. But if it's attr_dataclass_makers I know that auto_attribs defaults to True. If you feel adventurous you can go read the plugin code in mypy. https://github.com/python/mypy/blob/master/mypy/plugins/attrs.py

Sadly the only thing it handles is auto_attribs having a True or False default.

@Tinche
Copy link
Member

Tinche commented Apr 12, 2020

@euresti Just wanted to say thanks for sharing the snippet above. I've successfully integrated it into one of our flattrs-using codebases. Wish stuff like this was more publicly documented though!

@hynek
Copy link
Member

hynek commented Apr 12, 2020

Any suggestions where to put it Tin? 😇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants