65 |
65 |
|
66 |
66 |
def is_xml_func(node): return is_xml_func_name(node.tagName)
|
67 |
67 |
|
68 |
|
def process(node, on_error=exc.raise_, db=None, preserve=set(), strip=False):
|
|
68 |
def process(node, on_error=exc.raise_, rel_funcs=None, db=None):
|
69 |
69 |
'''Evaluates the XML functions in an XML tree.
|
70 |
|
@param preserve set(str...) XML functions not to remove.
|
|
70 |
@param rel_funcs None|set(str...) Relational functions
|
71 |
71 |
* container can be any iterable type
|
72 |
|
@param strip Whether to instead replace most XML functions with their last
|
73 |
|
parameter (usually the value) and evaluate only structural functions
|
|
72 |
* If != None: Non-relational functions are removed, or relational
|
|
73 |
functions are treated specially, depending on the db param (below).
|
|
74 |
@param db
|
|
75 |
* If None: Non-relational functions other than structural functions are
|
|
76 |
replaced with their last parameter (usually the value), not evaluated.
|
|
77 |
This is used in column-based mode to remove XML-only functions.
|
|
78 |
* If != None: Relational functions are evaluated directly. This is used
|
|
79 |
in row-based mode to combine relational and XML functions.
|
74 |
80 |
'''
|
75 |
|
preserve = set(preserve)
|
|
81 |
has_rel_funcs = rel_funcs != None
|
|
82 |
assert db == None or has_rel_funcs # rel_funcs required if db set
|
76 |
83 |
|
77 |
84 |
for child in xml_dom.NodeElemIter(node):
|
78 |
|
process(child, on_error, db, preserve, strip)
|
|
85 |
process(child, on_error, rel_funcs, db)
|
|
86 |
|
79 |
87 |
name = node.tagName
|
80 |
|
if not is_xml_func_name(name) or name in preserve: pass
|
81 |
|
elif strip and name not in structural_funcs: # just replace with last param
|
82 |
|
value = pop_value(list(xml_dom.NodeTextEntryIter(node)), None)
|
83 |
|
xml_dom.replace_with_text(node, value)
|
84 |
|
else:
|
85 |
|
try:
|
86 |
|
items = xml_dom.NodeTextEntryIter(node)
|
87 |
|
try: func = funcs[name]
|
88 |
|
except KeyError:
|
89 |
|
if db != None: # DB with relational functions available
|
90 |
|
value = sql.put(db, name, dict(items))
|
91 |
|
else: value = pop_value(list(items)) # pass value through
|
92 |
|
else: value = func(items, node) # local XML function
|
93 |
|
|
94 |
|
xml_dom.replace_with_text(node, value)
|
|
88 |
if not is_func_name(name): return # not any kind of function
|
|
89 |
|
|
90 |
# Change rel_funcs *after* processing child nodes, which needs orig value
|
|
91 |
if not has_rel_funcs: rel_funcs = set()
|
|
92 |
rel_funcs = set(rel_funcs)
|
|
93 |
|
|
94 |
row_mode = has_rel_funcs and db != None
|
|
95 |
column_mode = has_rel_funcs and db == None
|
|
96 |
items = xml_dom.NodeTextEntryIter(node)
|
|
97 |
|
|
98 |
if row_mode and name in rel_funcs: # row-based mode: evaluate using DB
|
|
99 |
value = sql.put(db, name, dict(items))
|
|
100 |
elif column_mode and not name in structural_funcs: # column-based mode
|
|
101 |
if name in rel_funcs: return # preserve relational functions
|
|
102 |
# otherwise XML-only, so just replace with last param
|
|
103 |
value = pop_value(list(items), None)
|
|
104 |
else: # local XML function
|
|
105 |
try: value = funcs[name](items, node)
|
95 |
106 |
except Exception, e: # also catch non-wrapped exceptions (XML func bugs)
|
96 |
107 |
# Save in case another exception raised, overwriting sys.exc_info()
|
97 |
108 |
exc.add_traceback(e)
|
... | ... | |
101 |
112 |
'\n'+term.emph_multiline(str_)))
|
102 |
113 |
|
103 |
114 |
on_error(e)
|
|
115 |
return # in case on_error() returns
|
|
116 |
xml_dom.replace_with_text(node, value)
|
104 |
117 |
|
105 |
118 |
##### XML functions
|
106 |
119 |
|
xml_func.py: process(): Refactored to emphasize special handling for row-based and column-based modes. In row-based mode, always use a DB relational function over a local XML function when possible, to faciliate testing of DB relational functions in row-based mode. (The shadowed local XML version will still be tested in non-DB modes, such as outputting to intermediate XML files.)