-
-
Notifications
You must be signed in to change notification settings - Fork 385
BUG: fix evolve behaviour when a validator fails. #176
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
Conversation
TypeError may be coming from many sources, and it is not correct to assume it always comes from a non-existing attr member as the evolve codes now. Instead, we now check specifically for this case when raising a custom error, and just re-raise in other cases.
Codecov Report
@@ Coverage Diff @@
## master #176 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 8 8
Lines 551 555 +4
Branches 122 125 +3
=====================================
+ Hits 551 555 +4
Continue to review full report at Codecov.
|
So this is error handling case so performance doesn't really matter here, but there might be a more efficient way. Instead of constructing a dictionary, could you try something like (pseudocode):
Have to run now, will look again later. |
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.
pyperf
shows the getattr approach is a little faster so let's stick with that.
"{k} is not an attrs attribute on {cl}.".format(k=k, cl=cls)) | ||
for name in changes: | ||
if getattr(attrs, name, None) is None: | ||
k = exc.args[0].split("'")[1] |
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.
So there is actually a problem with this solution, because from attr import attributes, attr, evolve
from attr.validators import instance_of
@attributes
class C(object):
a = attr(validator=instance_of(int))
_b = attr(validator=instance_of(int))
c1 = C(1, 2)
# Should work ? Right now it does not (c2.b == 2)
c2 = evolve(c1, b=3)
# Should work ? That behaves as expected
c3 = evolve(c1, _b=3)
# Should that really work ??
c4 = evolve(c1, _b=3, b=4) |
Huh, good catch. There was a small discussion around whether to support the setter syntax or the init syntax over at #116. We settled on supporting both, since I figured it was easy to do and there's no ambiguity in the attribute names (i.e. you can't have
I don't actually know what the right way is to resolve Given the additional discussion of private attributes from #171, I'm in favor of only supporting the setter syntax in evolve. (Summoning @glyph here as well.) So only |
This code would be a simple fix I think.
But it still doesn't fix |
Fixing should be fairly easy once we agree on what the behaviour should be. Detecting |
Is this blocking on something? |
Can you comment on #176 (comment) ? |
Paging @glyph because he expressed opinions on this exact topic recently. |
I think the question of which syntax to use actually cuts to the heart of a serious design problem with Consider this case: import attr
@attr.s
class _Tree(object): # private type
_parent = attr.ib(default=None)
_children = attr.ib(default=attr.Factory(list))
def add(self, child):
if child._parent is not None:
child._parent = self
self._children.append(child)
def tree(children=()): # public constructor
new = _Tree()
for child in children:
new.add(child)
# ... application ...
leaf = tree()
branch = tree(leaf)
root = tree(branch) If you call The reason I'm raising this here is that the question as to whether to give If However, if we did something like this: @attr.s
class Point3D:
x = attr.ib()
y = attr.ib()
z = attr.ib()
evolve = attr.evolver()
Point3D(1, 2, 3).evolve(y=7) so that classes could explicitly "opt in" to evolvulation then it would make sense to use the private attribute names in that case. I suspect that this view may be controversial though. Thoughts? |
My thoughts at this point is that nobody building APIs should even expose the fact their classes are attrs-based. It has always been my focus to make attrs transparent to endusers. Honestly, I’ve personally even arrived at the point where I treat In my mind we have two conflicting arguments:
Did I forget something? My mind is wandering towards nr. 2 or |
My inclination is to say that for now, we should go ahead and make it consistent with But, the documentation should be adjusted to make it clear that the first argument to The only real problem with this is that sometimes the constructor has been made private through some other shenanigans, but Rather than try to hack up |
OK so back to my original hunch: |
JFTR @cournape , we have an agreement above. :) This needs to be fixed before 17.1, so I’d be super grateful if you could finish it up! No hurry though, there’s still some stuff open. Just wanted to stress that this should be unblocked (and if not, please yell at us). |
This is a naive solution to #175. TypeError may be coming from many sources, and it is not correct to assume it always comes from a non-existing attr member as the evolve codes now. Instead, we now check specifically for this case when raising a custom error, and just re-raise in other cases.
Fixes #175