WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit c2bf6bc

Browse files
authored
Merge pull request #787 from lindsay-stevens/pyxform-754
754: fix 'NoneType is not iterable' error for group with no children
2 parents 5b0ac3d + fccf018 commit c2bf6bc

File tree

8 files changed

+557
-489
lines changed

8 files changed

+557
-489
lines changed

pyxform/section.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __init__(
5656
):
5757
# Structure
5858
self.bind: dict | None = bind
59-
self.children: list[Section | Question] | None = None
59+
self.children: list[Section | Question] = []
6060
self.control: dict | None = control
6161
# instance is for custom instance attrs from survey e.g. instance::abc:xyz
6262
self.instance: dict | None = instance
@@ -73,6 +73,23 @@ def __init__(
7373
kwargs.pop(constants.CHILDREN, None)
7474
super().__init__(name=name, label=label, fields=fields, **kwargs)
7575

76+
self._link_children()
77+
78+
def _link_children(self):
79+
for child in self.children:
80+
child.parent = self
81+
82+
def add_child(self, child):
83+
self.children.append(child)
84+
child.parent = self
85+
86+
def add_children(self, children):
87+
if isinstance(children, list | tuple):
88+
for child in children:
89+
self.add_child(child)
90+
else:
91+
self.add_child(children)
92+
7693
def validate(self):
7794
super().validate()
7895
for element in self.children:

pyxform/survey_element.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,6 @@ def __init__(
121121
if len(kwargs) > 0:
122122
self.extra_data = kwargs
123123

124-
if hasattr(self, const.CHILDREN):
125-
self._link_children()
126-
127124
# Create a space label for unlabeled elements with the label
128125
# appearance tag. # This is because such elements are used to label the
129126
# options for selects in a field-list and might want blank labels for
@@ -141,24 +138,6 @@ def __init__(
141138
def name_for_xpath(self) -> str:
142139
return self.name
143140

144-
def _link_children(self):
145-
if self.children is not None:
146-
for child in self.children:
147-
child.parent = self
148-
149-
def add_child(self, child):
150-
if self.children is None:
151-
self.children = []
152-
self.children.append(child)
153-
child.parent = self
154-
155-
def add_children(self, children):
156-
if isinstance(children, list | tuple):
157-
for child in children:
158-
self.add_child(child)
159-
else:
160-
self.add_child(children)
161-
162141
def validate(self):
163142
if not is_xml_tag(self.name):
164143
invalid_char = re.search(INVALID_XFORM_TAG_REGEXP, self.name)

tests/test_fieldlist_labels.py

Lines changed: 0 additions & 92 deletions
This file was deleted.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,40 @@ def test_group_relevant_included_in_bind(self):
7979
],
8080
)
8181

82+
def test_table_list_appearance(self):
83+
md = """
84+
| survey |
85+
| | type | name | label | hint | appearance |
86+
| | begin_group | tablelist1 | Table_Y_N | | table-list minimal |
87+
| | select_one yes_no | options1a | Q1 | first row! | |
88+
| | select_one yes_no | options1b | Q2 | | |
89+
| | end_group | | | | |
90+
| choices |
91+
| | list_name | name | label |
92+
| | yes_no | yes | Yes |
93+
"""
94+
xml_contains = """
95+
<group appearance="field-list minimal" ref="/table-list-appearance-mod/tablelist1">
96+
<input ref="/table-list-appearance-mod/tablelist1/generated_table_list_label_2">
97+
<label>Table_Y_N</label>
98+
</input>
99+
<select1 appearance="label" ref="/table-list-appearance-mod/tablelist1/reserved_name_for_field_list_labels_3">
100+
<label> </label>
101+
<itemset nodeset="instance('yes_no')/root/item">
102+
<value ref="name"/>
103+
<label ref="label"/>
104+
</itemset>
105+
</select1>
106+
<select1 appearance="list-nolabel" ref="/table-list-appearance-mod/tablelist1/options1a">
107+
<label>Q1</label>
108+
<hint>first row!</hint>
109+
""".strip()
110+
self.assertPyxformXform(
111+
name="table-list-appearance-mod",
112+
md=md,
113+
xml__contains=[xml_contains],
114+
)
115+
82116

83117
class TestGroupParsing(PyxformTestCase):
84118
def test_names__group_basic_case__ok(self):
@@ -576,6 +610,96 @@ def test_group__no_begin_error__with_another_closed_repeat(self):
576610
error__contains=[SURVEY_001.format(row=4, type="group")],
577611
)
578612

613+
def test_empty_group__no_question__error(self):
614+
"""Should raise an error for an empty group with no questions."""
615+
md = """
616+
| survey |
617+
| | type | name | label |
618+
| | begin group | g1 | G1 |
619+
| | end group | | |
620+
"""
621+
self.assertPyxformXform(
622+
md=md,
623+
run_odk_validate=True, # Error about empty groups is from Validate only.
624+
odk_validate_error__contains=[
625+
"Group has no children! Group: ${g1}. The XML is invalid."
626+
],
627+
)
628+
629+
def test_empty_group__no_question_control__error(self):
630+
"""Should raise an error for an empty group with no question controls."""
631+
md = """
632+
| survey |
633+
| | type | name | label | calculation |
634+
| | begin group | g1 | G1 | |
635+
| | text | q1 | | 0 + 0 |
636+
| | end group | | | |
637+
"""
638+
self.assertPyxformXform(
639+
md=md,
640+
run_odk_validate=True, # Error about empty groups is from Validate only.
641+
odk_validate_error__contains=[
642+
"Group has no children! Group: ${g1}. The XML is invalid."
643+
],
644+
)
645+
646+
def test_unlabeled_group(self):
647+
self.assertPyxformXform(
648+
md="""
649+
| survey |
650+
| | type | name | label |
651+
| | begin_group | my-group | |
652+
| | text | my-text | my-text |
653+
| | end_group | | |
654+
""",
655+
warnings_count=1,
656+
warnings__contains=["[row : 2] Group has no label"],
657+
)
658+
659+
def test_unlabeled_group_alternate_syntax(self):
660+
self.assertPyxformXform(
661+
md="""
662+
| survey |
663+
| | type | name | label::English (en) |
664+
| | begin group | my-group | |
665+
| | text | my-text | my-text |
666+
| | end group | | |
667+
""",
668+
warnings_count=1,
669+
warnings__contains=["[row : 2] Group has no label"],
670+
)
671+
672+
def test_unlabeled_group_fieldlist(self):
673+
self.assertPyxformXform(
674+
md="""
675+
| survey |
676+
| | type | name | label | appearance |
677+
| | begin_group | my-group | | field-list |
678+
| | text | my-text | my-text | |
679+
| | end_group | | | |
680+
""",
681+
warnings_count=0,
682+
xml__xpath_match=[
683+
"""
684+
/h:html/h:body/x:group[
685+
@ref = '/test_name/my-group' and @appearance='field-list'
686+
]
687+
"""
688+
],
689+
)
690+
691+
def test_unlabeled_group_fieldlist_alternate_syntax(self):
692+
self.assertPyxformXform(
693+
md="""
694+
| survey |
695+
| | type | name | label::English (en) | appearance |
696+
| | begin group | my-group | | field-list |
697+
| | text | my-text | my-text | |
698+
| | end group | | | |
699+
""",
700+
warnings_count=0,
701+
)
702+
579703

580704
class TestGroupInternalRepresentations(TestCase):
581705
maxDiff = None

0 commit comments

Comments
 (0)