-
-
Notifications
You must be signed in to change notification settings - Fork 374
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
Stop modifying classes and always create new ones #260
Conversation
I’m happy to take a look
…On Friday, October 6, 2017, Hynek Schlawack ***@***.***> wrote:
This allows us to harmonize class creation for dict and slots classes.
Interestingly this seems to break our Hypothesis strategies. I can't dig
any further right now but I wanted to put it out there.
Ref #223 <#223> #202
<#202>
------------------------------
You can view, comment on, or merge this pull request online at:
#260
Commit Summary
- Add replace_class argument to attr.s
- Use replaced classes in testing strategy
File Changes
- *A* changelog.d/260.deprecation.rst
<https://github.com/python-attrs/attrs/pull/260/files#diff-0> (8)
- *M* docs/api.rst
<https://github.com/python-attrs/attrs/pull/260/files#diff-1> (2)
- *M* src/attr/_make.py
<https://github.com/python-attrs/attrs/pull/260/files#diff-2> (10)
- *M* tests/test_dark_magic.py
<https://github.com/python-attrs/attrs/pull/260/files#diff-3> (80)
- *M* tests/utils.py
<https://github.com/python-attrs/attrs/pull/260/files#diff-4> (14)
Patch Links:
- https://github.com/python-attrs/attrs/pull/260.patch
- https://github.com/python-attrs/attrs/pull/260.diff
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#260>, or mute the thread
<https://github.com/notifications/unsubscribe-auth/AB0h8Qc8dZeGrjl_PSN85kJg4b19oUY2ks5spkx0gaJpZM4PwrtR>
.
|
A few more notes:
|
Hm after a 5 minute investigation, there seems to be a problem with On a side note, is it possible to overwrite the class |
🤦♂️ |
Codecov Report
@@ Coverage Diff @@
## master #260 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 9 9
Lines 609 653 +44
Branches 134 135 +1
=====================================
+ Hits 609 653 +44
Continue to review full report at Codecov.
|
changelog.d/260.deprecation.rst
Outdated
For the next year, the default value is ``replace_classes=False`` which is the current behavior. | ||
|
||
The default value will change to ``True`` no sooner than one year after this release. | ||
There are no concrete plans to remove ``replace_classes=False`` altogether but its use is discouraged. |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
Just to add another thought: my wish is that ideally, nobody notices a difference at all. So a three stage approach with a
Now, this kind of costs us a pointless year because people are not gonna try out that option unless they have a problem. It's like with pre-releases, nobody tests them. Ergo, in Oct 2018, there will be people yelling at us in any case and we've maybe saved <0.01% of our users some headaches. OTOH, I think I’ll do it anyway because I'm totally not ready to break hashing a second time this year. :| Also we'll be able to point to this: yes we did the right thing. I really hope this is the last fundamental screwup in I’d really like more input on this…happily also from @wsanchez @Lukasa @DRMacIver @wbolster @hawkowl |
I have added the current plan to the news fragment. |
I don't think I actually understand the problem well enough to comment yet. What actually is the interaction with hashing and equality here that causes the problem? I assume the "super" problem is that if I have something like:
Then the "Foo" in the method is different from the Foo outside the method and this causes a supertype mismatch of some sort? I assume the class you're creating can't be a subclass of the existing Foo. Are you copying over the namespace and is that what causes you problems? |
OK. I've now done the reading of the news fragment and understand some of this better. On the hashing thing... I think I'd personally be inclined to be less nice than you. I would argue that getting a hash when passing Personally the idea of adding a Were it me I would adopt the following plan:
Note however this is coming from the guy who thought "Lets get real drunk and turn coverage on for every Hypothesis user" was a good idea, so I may have more of a YOLO attitude to feature evolution than you are comfortable with. 😉 |
I tend to agree with @DRMacIver, though it's possible that all the wine I had over the weekend is influencing me into associating with the "Lets get real drunk and…" camp. I certainly agree regarding the hashing behavior; that's a bug and it should just get fixed. I don't see a strong argument for bending over to provide compatibility there; at a minimum, I'd have |
I’d like to stress one thing: What we could do is using this as a unification/simplification for creating classes and attach Then continue with #202 and use an enum to control what actually happens and deprecate using bool for the hash argument. 🤔 |
@hynek that sounds sensible to me. |
OK, I’ve spent yesterday basically rewriting the class generation code because the old one was all about tacking on and then copying or removing. Now it's much cleaner. There might be some doc inconsistencies I guess? I’m much happier with how it works now. |
Complete rewrite, we need to re-review.
changelog.d/223.change.rst
Outdated
``attrs``-decoratorated classes are never modified now. | ||
Instead a new class is created with the appropriated methods and attributes attached from the get go. | ||
|
||
For backward-compatibility, the behavior of getting hashing by ID on Python 3 when passing ``@attr.s(hash=False)`` is manually retained by setting ``__hash__`` to ``object.__hash__``. |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
changelog.d/260.change.rst
Outdated
``attrs``-decoratorated classes are never modified now. | ||
Instead a new class is created with the appropriated methods and attributes attached from the get go. | ||
|
||
For backward-compatibility, the behavior of getting hashing by ID on Python 3 when passing ``@attr.s(hash=False)`` is manually retained by setting ``__hash__`` to ``object.__hash__``. |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
Sorry, I somehow misparsed what you originally said (which was totally unambiguous) and interpreted it as something slightly different that I think is a better idea. What I would actually recommend here is that the default behaviour is to attach a |
The problem is that as of now, there is no explicit way of defining it. You totally can ask for unhashable but with cmp and the behavior also differs between Python versions. That’s why I want to start raising a warning as soon as there is actually a good way. |
They may contain a unhashable default.
OK folks, I get it: this PR is fucking scary. So I’m not gonna ask you to review and risk to share the blame. Mut what I am gonna ask you is to run it against your code bases and report back any breakage you encounter. Technically, nothing should break. I just did that and the result is 6dd4b1d. I’ll take all the blame, but please help me not have an emergency 17.4 by running your test suite once. :D |
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've attempted a review of this and decided that I don't really feel like I know this code well enough to do approve/request changes, but I've added some comments inline.
src/attr/_make.py
Outdated
super_attrs.extend( | ||
a | ||
for a in sub_attrs | ||
if a not in super_attrs and a.name not in non_super_names |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
continue | ||
for cell in closure_cells: | ||
if cell.cell_contents is self._cls: | ||
set_closure_cell(cell, cls) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
src/attr/_make.py
Outdated
raise TypeError( | ||
"Invalid value for hash. Must be True, False, or None." | ||
) | ||
elif hash is False or (hash is None and cmp is False): | ||
pass | ||
builder.add_hash_by_id() |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
""" | ||
if these is None: | ||
ca_list = [(name, attr) | ||
for name, attr | ||
in cls.__dict__.items() | ||
if isinstance(attr, _CountingAttr)] | ||
for name, _ in ca_list: | ||
delattr(cls, name) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
FWIW I've run the Hypothesis test suite with these changes and it seems fine, but I also don't think Hypothesis makes interesting enough use of attrs that that's a meaningful test of anything. |
I got one:
Works with PyPI, not with this branch (on 3.6). GL :D |
Thanks @DRMacIver, your look is very much appreciated! @Tinche not sure if trolling? :) |
I'm not trolling, this was a failure from my work codebase. I've simplified it of course. |
Related to the type work, not this PR right? Maybe open new issue and set milestone?
|
(I'm 100% OK with changing this code, by the way, just reporting on test results, as requested.) |
The test fails if we try to do a `hash is False and slots is True`.
Great news everyone, we’ve got ourselves another edge case: import attr
@attr.s
class BaseNoSlots:
def __init_subclass__(cls, param, **kwargs):
super().__init_subclass__(**kwargs)
cls.param = param
@attr.s
class Foo(BaseNoSlots, param='foo'):
pass
print(Foo.param) This is taken from #269 and https://repl.it/Mmkb/0 Since we didn’t intervene into the class generation for non-slots before, it just worked. Now we get: >>> import attr
...
... @attr.s
... class BaseNoSlots:
... def __init_subclass__(cls, param, **kwargs):
... super().__init_subclass__(**kwargs)
... cls.param = param
...
... @attr.s
... class Foo(BaseNoSlots, param='foo'):
... pass
Traceback (most recent call last):
File "<stdin>", line 10, in <module>
File "/Users/hynek/Projects/attrs/src/attr/_make.py", line 573, in attrs
return wrap(maybe_cls)
File "/Users/hynek/Projects/attrs/src/attr/_make.py", line 566, in wrap
return builder.build()
File "/Users/hynek/Projects/attrs/src/attr/_make.py", line 379, in build
cd,
TypeError: __init_subclass__() missing 1 required positional argument: 'param'
__init_subclass__() missing 1 required positional argument: 'param' |
In any case: I’m going to make this PR only about the refactoring which is a net win. Then we’ll get attrs 17.3 out of the door and then we‘ll tackle it again. Maybe we’ll find a better way to attach |
This allows us to harmonize class creation for dict and slots classes.
Interestingly this seems to break our Hypothesis strategies. I can't dig any further right now but I wanted to put it out there. Helpful comments welcome.
Ref #223 #202