-
Notifications
You must be signed in to change notification settings - Fork 132
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
Attributes lost from Dict child classes #101
Comments
Hey @MisterVladimir! Thanks for contributing. I'm happy to support your usecase and your suggestions look sound. Would you be open to trying these changes in a pull request and see if the tests pass? You can remove superfluous test cases, if any. |
Thanks @mewwts. I'm working on it now, namely, on getting test_addict.Tests to run the same tests on Dict, or a child class. Expect a pull request this evening. |
Previously, when child classes were instantiated, all their nodes -- besides the top node -- remained instances of the parent (Dict) class. Added tests appropriate for these changes. All tests passed. See mewwts#101
I'm a little new to Travis, tests, etc., so apologies for polluting the commit history with incremental bug fixes, but it looks like tests pass on my machine with "python setup.py test" and with Travis. The second commit I made, which changed the test script, built with Python 3.6+ but not Python 2, probably because I used "super()" instead of the old "super(cls, obj)". That's been remedied in subsequent commits. Looks like the package builds with Python 2 and 3. |
No worries @MisterVladimir, I've wrestled countless times with that! I've gone ahead and merged #102. Thanks! |
I've released 2.2.0 as well https://github.com/mewwts/addict/releases/tag/v2.2.0. Cheers! |
I've just ran into several problems with this change, which caused breaking changes with my project, though it is understandable why they occurred after reading this issue. I just wanted to say that existing projects could break due to various assumptions we used to have before this change, so perhaps this might be a heads-up for others who also found their projects breaking from today onwards. Firstly, I have a base class which extends from addict import Dict
class BaseStrategy(Dict):
name = None
def __init__(self):
super(BaseStrategy, self).__init__()
# This line breaks because the class variable
# name is the same as the instance variable name.
self.name = self.__class__.name
class ExampleStrategy(BaseStrategy):
name = 'EXAMPLE'
example = ExampleStrategy() The error we get:
As I found out, this is bad practice so I was okay to rectify my code accordingly and rename the class variable to be a different name from my member variable. Secondly, perhaps slightly more concerning, is that the new from addict import Dict
class CustomDict(Dict):
def __init__(self, value):
super(CustomDict, self).__init__()
# The problem is that I do not initialize self.my as a Dict(),
# since I previously assumed that this would work.
self.my.custom.value = value
class CustomDict2(Dict):
def __init__(self, value, **kwargs):
super(CustomDict2, self).__init__()
self.my.custom.value = value
class CustomDict3(Dict):
def __init__(self, value=None, **kwargs):
super(CustomDict3, self).__init__()
self.my.custom.value = value
# Try running any of the following lines on its own
# custom_dict = CustomDict('hello world')
# custom_dict2 = CustomDict2('hello world')
# custom_dict3 = CustomDict3('hello world')
# The correct fix would be to initialize self.my as a Dict() first.
# This also breaks when I try to access a non-existent attribute:
class CustomDict4(Dict):
def __init__(self, value, **kwargs):
super(CustomDict4, self).__init__()
self.value = value
custom_dict4 = CustomDict4(1)
assert not custom_dict4.missing_attr
# The correct fix would be to do:
# assert 'missing_attr' not in custom_dict4 Some of the errors:
Although this was a minor version bump, it may have caused quite drastic inconvenience for others as well. I'm not really sure what recommendation to give, but just wanted to mention this. |
Thank you so much @irvinlim for pointing this out! First of all, sorry for breaking this for you. I was unaware of the implications this would have for clients inheriting from If I understand correctly, @MisterVladimir's test-case is so that for a subclass of Before this change setting a missing attribute on the subclass would return a The problem arises when the subclass defines its own Lines 64 to 65 in cf29d47
As far as I can see it's not possible to support both these usecases at the same time. On the one hand, I feel like what we just merged is the more expected functionality, however inheriting from On the other hand, this probably breaks the appeal of inheriting from I'm torn, but I'm open to reverting. I need to think some more about it. Do you guys have any other thoughts on this @irvinlim and @MisterVladimir? |
I can see where @MisterVladimir is coming from with the fix, as he expects the descendant nodes to be the same class as the object itself, meanwhile I expected my Dict subclasses to inherit the dot-notation elegance from addict. Actually, I don't think there's a right answer to the behaviour when subclassing My only gripe would be that error messages are now much more cryptic, due to the fact that users unaware of the internal implementation would see messages like this:
when in fact the error is due to a missing attribute or method, perhaps due to a typo. In fact, a I am perfectly okay with this change actually, because the breaking changes didn't take me very long to fix, but I just wonder how many others may be depending on the same functionality as I did. |
Thanks for the lively discussion, everyone. I agree that the most concerning issue is
Basically v2.2.0 throws an error if the child class' def __missing__(self, key):
try:
return self.__class__(__parent=self, __key=name)
except: #maybe limit to TypeError and RecursionError
return Dict(__parent=self, __key=name) I never realized one would want to implement an
@mewwts, would you be so kind as to link to an example? I actually need to make a config file for my own program, so a pointer would be a big help to this n00b. : ) |
Hey @MisterVladimir. I thought of a config class just as something like
which you can then pass around. I'm closing this now as it doesn't seem to be a problem for more users. @irvinlim thanks for using addict and reporting this. |
I'm reopening this as several users has hinted towards issues with the new implementation. |
When I implement my own class that inherits from Dict, I get an error accessing the attributes of the nested items. That is, anything besides the top node does not contain my class' attributes.
The class:
The errors come up when I do:
I think this can be fixed by changing
__getitem__
to return a instance ofself.__class__
instead ofDict
.or maybe instead of using the if/then statement in
__getitem__
we could just add a__missing__
method and get rid of__getitem__
altogether. Besides the if/then statement, as it standsDict
's__getitem__
is the same asdict.__getitem__
instead of
To be consistent, I'd also change any references to super(Dict, self) to super() e.g. in
__deepcopy__
.The text was updated successfully, but these errors were encountered: