-
-
Notifications
You must be signed in to change notification settings - Fork 378
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
Provide option in attrs.define to allow users to exclude parameters set to default value from repr
#1276
base: main
Are you sure you want to change the base?
Provide option in attrs.define to allow users to exclude parameters set to default value from repr
#1276
Conversation
Note that I'll also add examples to the docs before this is finalized/merged if the concept moves forward. |
@hynek I know you are probably busy, but I just wanted to check in to see if you had a chance to take a look and provide feedback. |
yeah sorry I'm swamped right now, as you can tell in my own PR #1267 that hasn't moved in a while. I don't have the headspace for bigger changes right now, but it won't get lost as long as you leave it open. I hope to be able to clean up the trackers before leaving for PyCon US (2024 – just in case ;)) |
No problem at all! I'll leave it open and add some of the finishing touches (tests cases, docs) as I have time on the next week or two. |
…ub.com/RNKuhns/attrs into repr_optionally_exclude_param_defaults
@hynek I've updated this to handle default factories and include a description of the functionality in the Let me know when you have a chance to take a look. |
CodSpeed Performance ReportMerging #1276 will degrade performances by 28.49%Comparing Summary
Benchmarks breakdown
|
Hey sorry once again, for all the delays. :( Before starting investigating, do you have any idea why this PR slows down class creation (not instantiation) by almost 30%? That's quite a lot and sadly a rather critical benchmark in the whole space of creating classes. :| |
" else:", | ||
" default_ = a.default.factory()", | ||
" else:", | ||
" default_ = a.default", |
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.
- I think you could generate the defaults only once and cache them for subsequent runs.
- IIRC, attrs applies converters on defaults, so shouldn't this do, too?
- Calling factories unconditionally is a bit problematic for things like
factory=lambda: next(generator)
because it would advance the caller's generator without an actual instantiation. Also, if a factory takes self, it might contain some more elaborate computation that should not be done over and over again (e.g. determining a file type). Overall, it might be better to keep track of the defaults produced on construction and compare against that?
FWIW, I had written a caller-side wrapper to hide defaults. (The way it works around the factory problem is by letting you mark a factory as dynamic if you don't want it called in repr.)
Disclaimer: I'm not an attrs maintainer, so my views may not matter here.
field's value is not the default. | ||
|
||
But the field is still excluded from the repr when it is set to the default value | ||
because `only_non_default_attr_in_repr` overrides `repr=True` or the repr being a |
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.
wouldn't it be nice for an explicit repr=True
to override the defaults hiding?
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.
ah, no, because then this wouldn't be available if a custom repr is given. so perhaps add a new argument always_repr
or something?
C() | ||
>>> C(x=1) | ||
C(x=1.5) | ||
``` |
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.
this functionality expects the defaults in the converted format.
I don't think this is correct (see the other comment). The real default for field x
would be 1.5
here:
>>> import attrs
>>>
>>> @attrs.define()
... class Test:
... x = attrs.field(default=1, converter=lambda v: v + 0.5)
...
>>> test = Test()
>>> test.x
1.5
(note: expand the file to see context)
@hynek I do plan to circle back on this. I've just got a couple things going on at the moment. |
Not a problem -- I'll take a look at this and work through what is driving those performance degradations. |
Summary
This PR provides functionality in line with #1193.
It adds a new parameter to attrs.define to allow users to toggle on/off the ability to create classes that dynamically generate their repr to include only parameters set to values other than their default. The added parameter is set to a default value that maintains the existing functionality (always have static repr).
The functionality is designed to work such as follows:
@hynek I'll look into creating some simple test cases (like above examples) to test cases for this. But I'd appreciate some feedback on whether this approach makes sense to you before I finish up with that.
Note there might be some nuisance edits to the
_make.py
thatruff
made when I saved the file in my setup, but these should be minor.Pull Request Check List
main
branch – use a separate branch!Our CI fails if coverage is not 100%.
.pyi
).tests/typing_example.py
.attr/__init__.pyi
, they've also been re-imported inattrs/__init__.pyi
.docs/api.rst
by hand.@attr.s()
have to be added by hand too.versionadded
,versionchanged
, ordeprecated
directives.The next version is the second number in the current release + 1.
The first number represents the current year.
So if the current version on PyPI is 22.2.0, the next version is gonna be 22.3.0.
If the next version is the first in the new year, it'll be 23.1.0.
.rst
and.md
files is written using semantic newlines.changelog.d
.