def evaluate(self, expr): """ The main entry point for evaluating an XPath expression, using self as context expr - a unicode object with the XPath expression """ parsed = parser.parse(expr) return parsed.evaluate(self)
def check_xpath(self, test, node): ''' The XPath check is reminiscent of the XSLT pattern check. If any ancestor of the node can be used as context for the test XPath, such that the node is in the resulting node set, the test succeeds ''' #FIXME: optimize, at least for the simple node test case. No need to climb all the way up the tree for that #for i, t in enumerate(obj.test): #FIXME: add support for python callable tests # if isinstance(t, basestring): # obj.test[i:i+1] = [] if test not in self.cached_xpath: self.cached_xpath[test] = parser.parse(test) test = self.cached_xpath[test] #if hasattr(test, 'evaluate'): #if isinstance(test, unicode): cnode = node while cnode.xml_parent is not None: if node in test.evaluate( context(cnode.xml_parent, namespaces=cnode.xml_parent.xml_namespaces)): return True cnode = cnode.xml_parent return False
def _add(self, xpath, xpath_handler): nfa = to_nfa(xpath_parser.parse(xpath), self.namespaces) i = len(self.expressions) nfa.add_handler((i, xpath_handler)) exp = Expression(i, xpath, nfa) self.expressions.append(exp) return exp
def _run_parser_pass(test_cases): for args, expected, ctx in test_cases: result = parse(*args).evaluate(ctx) if hasattr(expected, "isnan") and expected.isnan(): assert result.isnan() continue if isinstance(expected, list): # convert nodesets to lists to prevent XPath-style nodeset compares result = list(result) expected = list(expected) assert result == expected, (args, result, expected)
def check_xpath(self, test, node): ''' The XPath check is reminiscent of the XSLT pattern check. If any ancestor of the node can be used as context for the test XPath, such that the node is in the resulting node set, the test succeeds ''' #FIXME: optimize, at least for the simple node test case. No need to climb all the way up the tree for that #for i, t in enumerate(obj.test): #FIXME: add support for python callable tests # if isinstance(t, basestring): # obj.test[i:i+1] = [] if test not in self.cached_xpath: self.cached_xpath[test] = parser.parse(test) test = self.cached_xpath[test] #if hasattr(test, 'evaluate'): #if isinstance(test, unicode): cnode = node while cnode.xml_parent is not None: if node in test.evaluate(context(cnode.xml_parent, namespaces=cnode.xml_parent.xml_namespaces)): return True cnode = cnode.xml_parent return False
def setup_model(self, parent=None): ''' Process an examplotron document for constraints ''' NSS = {u'ak': AKARA_NAMESPACE, u'eg': EG_NAMESPACE} parent = parent if parent is not None else self.model_document allowed_elements_test = [] if isinstance(parent, tree.element): #for a in parent.xml_attributes: #FIXME: Hack until this issue is fixed: http://trac.xml3k.org/ticket/8 for a in dict(parent.xml_attributes.items()): if a[0] not in [EG_NAMESPACE, AKARA_NAMESPACE]: parent.xml_model.attribute_types[a] = (self.model_document.xml_new_pname_mapping(a[0], a[1], iselement=False, update_class=False), None) for e in parent.xml_elements: #Constraint info eg_occurs = e.xml_attributes.get((EG_NAMESPACE, 'occurs')) if not (e.xml_namespace, e.xml_local) in parent.xml_model.element_types: parent.xml_model.element_types[e.xml_namespace, e.xml_local] = (self.model_document.xml_new_pname_mapping(e.xml_namespace, e.xml_local, update_class=False), None) if not eg_occurs in [u'?', u'*']: c = child_element_constraint(e.xml_namespace, e.xml_local) parent.xml_model.add_constraint(c) if not eg_occurs in [u'+', u'*']: parent.xml_model.add_constraint( constraint(u'count(%s) = 1'%named_node_test(e.xml_namespace, e.xml_local, parent), msg=u'Only one instance of element allowed') ) allowed_elements_test.append(named_node_test(e.xml_namespace, e.xml_local, parent)) #Metadata extraction cues #FIXME: Compile these XPath expressions mod = e.xml_model rattr = e.xml_select(u'@ak:resource', NSS) if rattr: #ak:resource="" should default to a generated ID mod.metadata_resource_expr = rattr[0].xml_value or NODE_ID_MARKER #rattr = e.xml_select(u'string(@ak:resource)', NSS) #if rattr: mod.metadata_resource_expr = rattr relattr = e.xml_select(u'@ak:rel', NSS) if relattr: if relattr[0].xml_value: mod.metadata_rel_expr = parser.parse(relattr[0].xml_value) else: mod.metadata_rel_expr = parser.parse(u'local-name()') valattr = e.xml_select(u'@ak:value', NSS) if valattr: if valattr[0].xml_value: mod.metadata_value_expr = parser.parse(valattr[0].xml_value) else: mod.metadata_value_expr = parser.parse(u'.') context_attr = e.xml_select(u'@ak:context', NSS) if context_attr: mod.metadata_context_expr = parser.parse(context_attr[0].xml_value) else: #If it doesn't state context, don't check context mod.metadata_context_expr = None #mod.metadata_context_expr = node_test(parent, e, 'parent') #Apply default relationship or value expression #If there's ak:rel but no ak:value or ak:resource, ak:value=u'.' #If there's ak:value but no ak:rel or ak:resource, ak:rel=u'local-name()' if mod.metadata_resource_expr: if (mod.metadata_value_expr and not mod.metadata_rel_expr): mod.metadata_rel_expr = parser.parse(u'local-name()') else: if (mod.metadata_rel_expr and not mod.metadata_value_expr): mod.metadata_value_expr = parser.parse(u'.') elif (mod.metadata_value_expr and not mod.metadata_rel_expr): mod.metadata_rel_expr = parser.parse(u'local-name()') if mod.metadata_resource_expr not in (NODE_ID_MARKER, None): if not isinstance(mod.metadata_resource_expr, expressions.expression): mod.metadata_resource_expr = parser.parse(mod.metadata_resource_expr) #if mod.metadata_rel_expr is not None: # mod.metadata_rel_expr = parser.parse(mod.metadata_rel_expr) #if mod.metadata_value_expr is not None: # mod.metadata_value_expr = parser.parse(mod.metadata_value_expr) relelem = e.xml_select(u'ak:rel', NSS) for rel in relelem: mod.other_rel_exprs.append((unicode(rel.name),unicode(rel.value))) #print e.xml_name, (mod.metadata_resource_expr, mod.metadata_rel_expr, mod.metadata_value_expr) #Recurse to process children self.setup_model(e) if allowed_elements_test: parent.xml_model.add_constraint( constraint(u'count(%s) = count(*)'%u'|'.join(allowed_elements_test), msg=u'Unexpected elements present') ) else: parent.xml_model.add_constraint( constraint(u'not(*)', msg=u'Element expected to be empty') )