My class has a method that does some stuff, and then wants to recursively operate on some data. Specifically, I'm generating an XML report whose output includes a flat list of elements derived from a hierarchical data structure.
Example Input (a Score)
{'wrapper':True, 'sub_scores':[
{'wrapper':True, 'sub_scores':[
{'wrapper':False, 'name':'count-objects', 'result':'pass', 'sub_scores':[]},
{'wrapper':False, 'name':'find-object', 'result':'pass', 'sub_scores':[
{'wrapper':False, 'name':'measure.x', 'result':'pass', 'sub_scores':[]},
{'wrapper':False, 'name':'measure.y', 'result':'pass', 'sub_scores':[]},
]},
{'wrapper':False, 'name':'find-object', 'result':'fail', 'sub_scores':[
{'wrapper':False, 'name':'measure.x', 'result':'skip', 'sub_scores':[]},
{'wrapper':False, 'name':'measure.y', 'result':'skip', 'sub_scores':[]},
]}
]}
]}
Desired Output
<report user="foo" time="1234">
<summary tests="7" pass="4" fail="1" skip="2"/>
<results file="foo.bin" />
<samples>
<sample name="count-objects" result="pass" />
<sample name="find-object" result="pass" />
<sample name="measure.x" result="pass" />
<sample name="measure.y" result="pass" />
<sample name="find-object" result="fail" />
<sample name="measure.x" result="skip" />
<sample name="measure.y" result="skip" />
</samples>
</report>
In JavaScript or Lua I would create a helper function within my method that does the recursion. In Ruby I would create a lambda or proc that does the recursion. Is the 'right' way to do this in Python to create a helper static function? Or is there a better way to keep the original method self-contained, perhaps allowing closures over local variables?
class Scorer:
def generate_report(self, score):
r = etree.Element('report', {'user': getpass.getuser(), 'time': timestamp()})
etree.SubElement(r, 'summary', {'tests': score.total(), 'pass': score.passed(),
'skip': score.passed(), 'fail': score.failed()})
etree.SubElement(r, 'results', {'file': self.results_path})
samples = etree.SubElement(r, 'samples')
Scorer._add_score_to_samples(samples, score)
return r
@staticmethod
def _add_score_to_samples(samples, score):
# Some scores are wrappers that should not be included in output
if not score.wrapper:
etree.SubElement(samples, 'sample', score.report_attr() )
for s in score.sub_scores:
Scorer._add_score_to_samples(samples, s)
I dislike 'polluting' my class with a private static method just to help out a specific method. However, if this is considered Pythonic, I'll happily continue down this path.