Skip to content
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

Fix nested choice #340

Merged
merged 2 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 68 additions & 38 deletions pyangbind/plugin/pybind.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,60 @@ def build_typedefs(ctx, defnd):
class_map[type_name.split(":")[1]] = class_map[type_name]


def get_children_elements(
ctx,
fd,
i_children,
module,
parent,
elements,
imports,
path=str(),
parent_cfg=True,
register_paths=True,
choice=False,
):
# Iterative function that is called to get the elements of
# a list of i_children.
# It treats 'choice' children, which have elements in its cases.
# It extends the list of elements and imports as childs are inspected.

for ch in i_children:
if ch.keyword == "choice":
# These are cases
for sub_choice_ch in ch.i_children:
get_children_elements(
ctx,
fd,
sub_choice_ch.i_children,
module,
parent,
elements,
imports,
path=path,
parent_cfg=parent_cfg,
choice=(ch.arg, sub_choice_ch.arg),
register_paths=register_paths,
)
else:
elements += get_element(
ctx,
fd,
ch,
module,
parent,
path + "/" + ch.arg,
parent_cfg=parent_cfg,
choice=choice,
register_paths=register_paths,
)
if ctx.opts.split_class_dir:
if (hasattr(ch, "i_children") and len(ch.i_children)) or (
ctx.opts.generate_presence and ch.search_one("presence")
):
imports.append(ch.arg)


def get_children(ctx, fd, i_children, module, parent, path=str(), parent_cfg=True, choice=False, register_paths=True):
# Iterative function that is called for all elements that have childen
# data nodes in the tree. This function resolves those nodes into the
Expand Down Expand Up @@ -731,45 +785,21 @@ def get_children(ctx, fd, i_children, module, parent, path=str(), parent_cfg=Tru
# that they are imported. Additionally, we need to find the elements that are
# within a case, and ensure that these are built with the corresponding
# choice specified.
if ctx.opts.split_class_dir:
import_req = []

for ch in i_children:
if ch.keyword == "choice":
for choice_ch in ch.i_children:
# these are case statements
for case_ch in choice_ch.i_children:
elements += get_element(
ctx,
fd,
case_ch,
module,
parent,
path + "/" + case_ch.arg,
parent_cfg=parent_cfg,
choice=(ch.arg, choice_ch.arg),
register_paths=register_paths,
)
if ctx.opts.split_class_dir:
if hasattr(case_ch, "i_children") and len(case_ch.i_children):
import_req.append(case_ch.arg)
else:
elements += get_element(
ctx,
fd,
ch,
module,
parent,
path + "/" + ch.arg,
parent_cfg=parent_cfg,
choice=choice,
register_paths=register_paths,
)
if ctx.opts.split_class_dir:
if (hasattr(ch, "i_children") and len(ch.i_children)) or (
ctx.opts.generate_presence and ch.search_one("presence")
):
import_req.append(ch.arg)
import_req = []
get_children_elements(
ctx,
fd,
i_children,
module,
parent,
elements,
import_req,
path=path,
parent_cfg=parent_cfg,
choice=choice,
register_paths=register_paths,
)

# Write out the import statements if needed.
if ctx.opts.split_class_dir:
Expand Down
17 changes: 17 additions & 0 deletions tests/choice/choice.yang
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ module choice {
}
}
}

case case-three {
choice choice-three- {
case case-three-one {
container case-three-one-container {
leaf case-three-one-leaf {
type int8;
}
}
}
case case-three-two {
leaf case-three-two-leaf {
type string;
}
}
}
}
}
}
}
40 changes: 29 additions & 11 deletions tests/choice/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,44 @@ def test_class_has_container(self):
self.assertTrue(hasattr(self.choice_obj, "container"), "Object does not have container")

def test_class_has_choice_containers(self):
for container in ["case_one_container", "case_two_container"]:
for container in ["case_one_container", "case_two_container", "case_three_one_container"]:
with self.subTest(container=container):
self.assertTrue(
hasattr(self.choice_obj.container, container),
"Object does not have choice container %s" % container,
)

def test_class_does_not_have_choices_as_attributes(self):
for choice in ["choice_one", "choice_two", "case_one", "case_two"]:
for choice in [
"choice_one",
"choice_three",
"case_one",
"case_two",
"case_three_one",
"case_three_two",
]:
with self.subTest(choice=choice):
self.assertFalse(
hasattr(self.choice_obj, choice), "Object has an erroneous choice option, %s" % choice
)

def test_case_leaf_default_values(self):
for leaf in ["case_one", "case_two"]:
for leaf in ["case_one", "case_two", "case_three_one"]:
with self.subTest(leaf=leaf):
container = getattr(self.choice_obj.container, "%s_container" % leaf)
value = getattr(container, "%s_leaf" % leaf)
self.assertEqual(value, 0, "Object does not have the correct value for %s_leaf, %s" % (leaf, value))

def test_set_choice_value(self):
self.choice_obj.container.case_one_container.case_one_leaf = 42
self.assertEqual(
self.choice_obj.container.case_one_container.case_one_leaf,
42,
"Object did not specify a value within the choice correctly, %s"
% self.choice_obj.container.case_one_container.case_one_leaf,
)
for leaf in ["case_one", "case_two", "case_three_one"]:
with self.subTest(leaf=leaf):
container = getattr(self.choice_obj.container, "%s_container" % leaf)
value = setattr(container, "%s_leaf" % leaf, 42)
self.assertEqual(
getattr(container, "%s_leaf" % leaf, 0),
42,
"Object did not specify a value within the choice correctly, %s" % container,
)

def test_set_choice_value_doesnt_set_other_choices(self):
self.choice_obj.container.case_one_container.case_one_leaf = 42
Expand All @@ -60,7 +69,7 @@ def test_change_choice_value(self):
self.assertEqual(
self.choice_obj.container.case_two_container.case_two_leaf,
42,
"Object did not allow the other half of the choice field to be specified, %s"
"Object did not allow the other choice field to be specified, %s"
% self.choice_obj.container.case_two_container.case_two_leaf,
)

Expand Down Expand Up @@ -100,6 +109,15 @@ def test_change_choice_list_resets_other_side(self):
"Adding to the second user list did not remove entries from the first",
)

def test_set_nested_choice(self):
for leaf in [
self.choice_obj.container.case_three_one_container.case_three_one_leaf,
self.choice_obj.container.case_three_two_leaf,
]:
with self.subTest(leaf=leaf):
leaf = 99
self.assertEqual(leaf, 99, "Nested choice leaf not set")


if __name__ == "__main__":
unittest.main()
Loading