-
Notifications
You must be signed in to change notification settings - Fork 128
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
Import with DictImporter fails on custom classes when attributes dont match the constructors signature #86
Comments
This seems to be a pretty tricky problem and may not be solved. There are some seperate cases:
class MyClass(MyBaseClass, NodeMixin):
def __init__(self, name, length, parent=None, children=None):
super(MyClass, self).__init__()
self.name = name
# self.length = length # length is not saved but required during initialization
self.parent = parent
if children:
self.children = children
class MyClass(MyBaseClass, NodeMixin):
def __init__(self, name, length, parent=None, children=None):
super(MyClass, self).__init__()
self.name = name
self.length = length
self.width = length * 2 # some other argument, which is not in the constructor
self.parent = parent
if children:
self.children = children
# taken from documentation, create a tree
my0 = MyClass('my0', 0)
my1 = MyClass('my1', 1, parent=my0)
my2 = MyClass('my2', 0, parent=my0) However the call to the constructor of > python anytree_bug.py
my0 0 0
├── my1 1 2
└── my2 0 0
{'name': 'my0', 'length': 0, 'width': 0, 'children': [{'name': 'my1', 'length': 1, 'width': 2}, {'name': 'my2', 'length': 0, 'width': 0}]}
Traceback (most recent call last):
File "anytree_bug.py", line 43, in <module>
my0_new = importer.import_(my0_dict) # fails
File "python3.7/site-packages/anytree/importer/dictimporter.py", line 38, in import_
return self.__import(data)
File "python3.7/site-packages/anytree/importer/dictimporter.py", line 45, in __import
node = self.nodecls(parent=parent, **attrs)
TypeError: __init__() got an unexpected keyword argument 'width' This case can be solved with a smarter def matching_attriter(attrs, constructor):
import inspect
constructor_signature = inspect.signature(constructor) # <Signature (name, length, parent=None, children=None)>
# add all attributes that appear as arguments in the constructors signature
matched_arguments = {k: v for k, v in attrs if k in constructor_signature.parameters}
# check if all arguments are matched. Not necessary, but could prevent an error bacause of case 1 later on.
try:
constructor_signature.bind(**matched_arguments)
except TypeError:
pass
yield from matched_arguments.items()
from functools import partial
# export works
exporter = DictExporter(attriter=partial(matching_attriter, constructor=my0.__init__))
my0_dict = exporter.export(my0)
print(my0_dict)
importer = DictImporter(nodecls=MyClass)
my0_new = importer.import_(my0_dict) # works
|
Thanks for posting this! I just ran into the same issue. Just to get unblocked, I was wondering if I should just fork it and modify the base class for my own use. |
When using custom
NodeMixin
subclasses with attributes that don't match the constructors signature the import will fail. TheDictImporter
expects, that all attributes are present in the classes constructor.See the following code, which reproduces the bug, I copied most of it from the documentation and annotated the changes.
The text was updated successfully, but these errors were encountered: