-
-
Notifications
You must be signed in to change notification settings - Fork 14
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
nested_dict_to_tree breaks for Nodes with required args #338
Comments
Hello! For using custom node type, there is already a test case for every kind of constructor (e.g., dataframe to tree, dict to tree etc.) and this is the test case for nested_dict_to_tree (test_nested_dict_to_tree_custom_node_type) if you want to see how it is used. The intended usage is different from how you are using it. In For your suggestion to change the line to The below code will work, which is your code but reordering the attribute to make from typing import Any
from bigtree import Node, nested_dict_to_tree
class FooNode(Node):
def __init__(
self,
foo_name: str, # changed this
bar: str,
**kwargs: Any,
):
super().__init__(foo_name, **kwargs)
self.bar = bar
sample = {
"name": "a",
"bar": "test0", # modified this to include mandatory 'bar'
"foo": 1,
"children": [
{"name": "b", "bar": "test1", "foo": 2},
{"name": "c", "bar": "test2", "foo": 3, "children": [{"name": "d", "bar": "test3", "foo": 4}]},
],
}
root = nested_dict_to_tree(
sample, node_type=FooNode
)
root.show(all_attrs=True)
# a [bar=test0, foo=1]
# ├── b [bar=test1, foo=2]
# └── c [bar=test2, foo=3]
# └── d [bar=test3, foo=4] |
Thank you for the response and thorough explanation. However, I still have quite a bit of confusion about what you have said. Specifically,
This claim does not appear to be true from my understanding of the code. Here is the function signature for
Name is an optional attribute. The superclass of It is true that every node will have a name, as it has a default parameter assigned to it of the empty string (and errors if it somehow goes through with no name), but that doesn't change the fact that name is, indeed, an optional attribute in the signature.
This is already a requirement (again, from my understanding). I understand that your example works because it functionally makes the It seems that either
instead of
or I believe the issue I've pointed out is a legitimate bug. Of course, there's the final possibility that I am missing something, in which case I would appreciate clarification. I greatly appreciate all the work you have done in building this library! |
Describe the issue
The
nested_dict_to_tree
function explicitly builds nodes by callingnode_type(node_name...)
here.Names are an optional attribute of
Node
s as defined within the bigtree documentation. When one subclasses a bigtree node and adds required arguments, then callingnested_dict_to_tree
with the subclass as thenode_type
breaks down, because this line takes the first argument as the name of the node, rather than taking the argument defined by thename_key
.I would classify this as a bug, as while it produces consistent behavior in bigtree with no augmentations, it harms the extensibility of the package without bringing any clear benefit (at least as far as I can see).
I believe this specific issue could be solved, without introducing additional bugs, by rewriting the line to:
root = node_type(name=node_name, parent=parent_node, **child_dict)
Environment
bigtree
version: 0.19.3To Reproduce
Though the issue is generic, I offer an example to illustrate and make reproducibility simple.
Consider the following subclass node type.
With the following nested dictionary:
sample = {"name": "a", "foo": 1, "children": [{"name": "b", "foo": 2}, {"name": "c", "foo": 3, "children": [{"name": "d", "foo": 4}]}]}
Such that we have the following tree structure:
Calling
nested_dict_to_tree
on this withnode_type
set toFooNode
will result in an error, as the following occurs:Line 915 pops the value of the
name
key, say "a":node_name = child_dict.pop(name_key)
Line 921 initializes a
FooNode
with "a" as the first argument, but the first argument ofFooNode
isbar
(sincebar
is a required argument):root = node_type(node_name, parent=parent_node, **child_dict)
The actual
name
forFooNode
is thus never set, sincename
has already been popped off of the dictionary and provided to the incorrect argument in theNode
's init signature.Expected behaviour
I would expect the error to be avoided, since a name is provided, it's just not being provided to the appropriate argument in the function signature. At risk of repetition, the produced tree would look like:
where
foo
is an attribute of the node.Additional context
I am not sure whether the same issue arises in other constructors in the library, as I am primarily using
nested_dict_to_tree
. If it does, then it would be worth fixing for the sake of proper extensibility (and, if this change is made tonested_dict_to_tree
, consistency).Final Check
I will watch this issue to stay updated with any comments or requests for additional information. I understand that timely responses will help in resolving the issue more quickly.
I have thoroughly searched the documentation, and I have also checked for existing solutions or similar issues before submitting this issue.
The text was updated successfully, but these errors were encountered: