def handle_element(elem, resource): new_resource = None prefixes = elem.xml_root.xml_model.prefixes if elem.xml_model.metadata_context_expr: if not elem.xml_model.metadata_context_expr.evaluate( context(elem, namespaces=prefixes)): return #Is there a cue that designates this element as a resource envelope? if elem.xml_model.metadata_resource_expr: if elem.xml_model.metadata_resource_expr == NODE_ID_MARKER: #FIXME: Isn't going from unicode -> xpath str -> unicode wasteful? new_resource = unicode(datatypes.string(elem.xml_nodeid)) else: new_resource = unicode( datatypes.string( elem.xml_model.metadata_resource_expr.evaluate( context(elem, namespaces=prefixes)))) #Is there a cue that designates a relationship in this element? if elem.xml_model.metadata_rel_expr: #Execute the XPath to get the relationship name/title rel = datatypes.string( elem.xml_model.metadata_rel_expr.evaluate( context(elem, namespaces=prefixes))) if elem.xml_model.metadata_value_expr: #Execute the XPath to get the relationship value val = elem.xml_model.metadata_value_expr.evaluate( context(elem, namespaces=prefixes)) elif new_resource is not None: #If the element is also a resource envelope, the default value is the new resource ID val = new_resource else: #Handle the default ak:value of "." val = datatypes.nodeset([elem]) yield (unicode(resource), unicode(rel), val) #Basically expandqname first #prefix, local = splitqname(rattr) #try: # ns = elem.xml_namespaces[prefix] # resource = ns + local #except KeyError: # resource = rattr if new_resource is not None: resource = new_resource for rel_expr, val_expr in elem.xml_model.other_rel_exprs: rel = datatypes.string( elem.xml_select(rel_expr, prefixes=prefixes)) val = elem.xml_select(val_expr, prefixes=prefixes) yield (unicode(resource), unicode(rel), val) for child in elem.xml_elements: for item in handle_element(child, resource): yield item return
def xml_avt(self, expr, prefixes=None): prefixes = prefixes or self.xml_namespaces.copy() from amara.xslt.expressions import avt from amara.xpath import context v = avt.avt_expression(expr) return unicode(v.evaluate(context(self, namespaces=prefixes)))
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 simple_evaluate(expr, node, prefixes=None): """ Designed to be the most simple/brain-dead interface to using XPath Usually invoked through Node objects using: node.xml_select(expr[, prefixes]) expr - XPath expression in string or compiled form node - the node to be used as core of the context for evaluating the XPath prefixes - (optional) any additional or overriding namespace mappings in the form of a dictionary of prefix: namespace the base namespace mappings are taken from in-scope declarations on the given node. This explicit dictionary is superimposed on the base mappings """ #Note: context.__init__(self, node, position=1, size=1, variables=None, namespaces=None, extmodules=(), extfunctions=None, output_parameters=None) try: prefixes_out = dict([(prefix, ns) for (prefix, ns) in node.xml_namespaces.iteritems()]) except AttributeError: prefixes_out = top_namespaces(node.xml_root) if prefixes: prefixes_out.update(prefixes) ctx = context(node, 0, 0, namespaces=prefixes_out) #extmodules=ext_modules) return ctx.evaluate(expr)
def simple_evaluate(expr, node, prefixes=None): """ Designed to be the most simple/brain-dead interface to using XPath Usually invoked through Node objects using: node.xml_select(expr[, prefixes]) expr - XPath expression in string or compiled form node - the node to be used as core of the context for evaluating the XPath prefixes - (optional) any additional or overriding namespace mappings in the form of a dictionary of prefix: namespace the base namespace mappings are taken from in-scope declarations on the given node. This explicit dictionary is superimposed on the base mappings """ # Note: context.__init__(self, node, position=1, size=1, variables=None, namespaces=None, extmodules=(), extfunctions=None, output_parameters=None) try: prefixes_out = dict([(prefix, ns) for (prefix, ns) in node.xml_namespaces.iteritems()]) except AttributeError: prefixes_out = top_namespaces(node.xml_root) if prefixes: prefixes_out.update(prefixes) ctx = context(node, 0, 0, namespaces=prefixes_out) # extmodules=ext_modules) return ctx.evaluate(expr)
def handle_element(elem, resource): new_resource = None prefixes = elem.xml_root.xml_model.prefixes if elem.xml_model.metadata_context_expr: if not elem.xml_model.metadata_context_expr.evaluate(context(elem, namespaces=prefixes)): return #Is there a cue that designates this element as a resource envelope? if elem.xml_model.metadata_resource_expr: if elem.xml_model.metadata_resource_expr == NODE_ID_MARKER: #FIXME: Isn't going from unicode -> xpath str -> unicode wasteful? new_resource = unicode(datatypes.string(elem.xml_nodeid)) else: new_resource = unicode(datatypes.string(elem.xml_model.metadata_resource_expr.evaluate(context(elem, namespaces=prefixes)))) #Is there a cue that designates a relationship in this element? if elem.xml_model.metadata_rel_expr: #Execute the XPath to get the relationship name/title rel = datatypes.string(elem.xml_model.metadata_rel_expr.evaluate(context(elem, namespaces=prefixes))) if elem.xml_model.metadata_value_expr: #Execute the XPath to get the relationship value val = elem.xml_model.metadata_value_expr.evaluate(context(elem, namespaces=prefixes)) elif new_resource is not None: #If the element is also a resource envelope, the default value is the new resource ID val = new_resource else: #Handle the default ak:value of "." val = datatypes.nodeset([elem]) yield (unicode(resource), unicode(rel), val) #Basically expandqname first #prefix, local = splitqname(rattr) #try: # ns = elem.xml_namespaces[prefix] # resource = ns + local #except KeyError: # resource = rattr if new_resource is not None: resource = new_resource for rel_expr, val_expr in elem.xml_model.other_rel_exprs: rel = datatypes.string(elem.xml_select(rel_expr, prefixes=prefixes)) val = elem.xml_select(val_expr, prefixes=prefixes) yield (unicode(resource), unicode(rel), val) for child in elem.xml_elements: for item in handle_element(child, resource): yield item return
def new_tst_method(cls, expected, factory, args, *test_args): if not test_args: test_args = (context(DOC, 1, 1),) def test_method(self): expr = factory(*args) for method_name in self.test_methods: result = getattr(expr, method_name)(*test_args) self.assertIsInstance(result, type(expected)) self.assertEquals(result, expected) return test_method
def new_tst_method(cls, expected, factory, args, *test_args): if not test_args: test_args = (context(DOC, 1, 1), ) def test_method(self): expr = factory(*args) for method_name in self.test_methods: result = getattr(expr, method_name)(*test_args) self.assertIsInstance(result, type(expected)) self.assertEquals(result, expected) return test_method
def new_tst_method(cls, expected, factory, args, node, principal_type=element): ctx = context(node, namespaces=NAMESPACES) compiler = xpathcompiler(ctx) # apply node-test using the default axis, 'child' nodes = iter(node) def test_method(self): node_test = factory(*args) node_filter = node_test.get_filter(compiler, principal_type) if node_filter: result = node_filter.select(ctx, nodes) else: result = nodes self.assertEquals(expected, list(result)) return test_method
def test_predicate(): ctx = context(ROOT, 1, 1) for args, expected in ( # FIXME: test literal optimization # FIXME: test `position() = Expr` optimization # FIXME: test `position() [>,>=] Expr` optimization # FIXME: test `Expr [<,<=] position()` optimization # FIXME: test numeric-type expression # test boolean-type expression (and_expr(TRUE, 'and', FALSE), []), (or_expr(TRUE, 'or', FALSE), [ROOT]), # FIXME: test object-type expression ): result = predicate(args).select(ctx, [ROOT]) result = list(result) assert result == expected, (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
from amara.xpath.locationpaths.predicates import predicates, predicate from amara.xpath.expressions.nodesets import union_expr, path_expr, filter_expr from test_expressions import ( base_expression, # boolean literals TRUE, FALSE, # nodeset literals nodeset_literal, ROOT, CHILD1, CHILD2, CHILD3) CONTEXT = context(CHILD1, 1, 1) def _check_nodeset_result(result, expected): assert isinstance(result, datatypes.nodeset) result = list(result) expected = list(expected) assert result == expected, (result, expected) def test_union_exr_new(): result = union_expr(nodeset_literal([ROOT, CHILD1]), nodeset_literal([ROOT])).evaluate_as_nodeset(CONTEXT) _check_nodeset_result(result, datatypes.nodeset([ROOT, CHILD1]))
# boolean literals TRUE, FALSE, # number literals (for special values) NOT_A_NUMBER, POSITIVE_INFINITY, NEGATIVE_INFINITY, # nodeset literals nodeset_literal, EMPTY_NODESET, ) from amara.xpath import context from test_expressions import DOC default_context = context(DOC, 1, 1) EGG1 = tree.element(None, "egg1") EGG1.xml_append(tree.text("egg1")) EGG2 = tree.element(None, "egg2") EGG2.xml_append(tree.text("egg2")) NUM = tree.element(None, "num") NUM0 = tree.attribute(None, "num0", "0") NUM.xml_attributes.setnode(NUM0) NUM2 = tree.attribute(None, "num2", "2") NUM.xml_attributes.setnode(NUM0) NUM4 = tree.attribute(None, "num4", "4") NUM.xml_attributes.setnode(NUM0) NUM31 = tree.attribute(None, "num31", "31")
#!/usr/bin/env python from amara.xpath import context, datatypes from amara.xpath.locationpaths.predicates import predicates, predicate from amara.xpath.expressions.nodesets import union_expr, path_expr, filter_expr from test_expressions import ( base_expression, # boolean literals TRUE, FALSE, # nodeset literals nodeset_literal, ROOT, CHILD1, CHILD2, CHILD3 ) CONTEXT = context(CHILD1, 1, 1) def _check_nodeset_result(result, expected): assert isinstance(result, datatypes.nodeset) result = list(result) expected = list(expected) assert result == expected, (result, expected) def test_union_exr_new(): result = union_expr(nodeset_literal([ROOT, CHILD1]), nodeset_literal([ROOT]) ).evaluate_as_nodeset(CONTEXT) _check_nodeset_result(result, datatypes.nodeset([ROOT, CHILD1])) def test_path_exr(): result = path_expr(nodeset_literal([ROOT, CHILD1]), '/', nodeset_literal([ROOT]) ).evaluate_as_nodeset(CONTEXT)
######################################################################## # test/xslt/test_avt.py from amara.test.xpath.test_expressions import DOC from amara.xpath import datatypes, context from amara.xslt import XsltError from amara.xslt.expressions.avt import avt_expression DEFAULT_CONTEXT = context(DOC, 1, 1) def test_avt(): for arg, expected in ( ('', ''), ('Senatus{{populisque}}romae', 'Senatus{populisque}romae'), ('Senatus{{{"populisque}}"}romae', 'Senatus{populisque}}romae'), ('{"{literal}"}', '{literal}'), ('{"{literal"}', '{literal'), ('{"literal}"}', 'literal}'), ('{"{{literal}}"}', '{{literal}}'), ('{"{{literal"}', '{{literal'), ('{"literal}}"}', 'literal}}'), ('{{{"literal"}', '{literal'), ('{{-{"literal"}', '{-literal'), ('{"literal"}}}', 'literal}'), ('{"literal"}-}}', 'literal-}'), ('{"100"}% {100}% {90+10}% 100% {"%"}1{0}0 %100', '100% 100% 100% 100% %100 %100'), ): result = avt_expression(arg).evaluate_as_string(DEFAULT_CONTEXT) assert isinstance(result, datatypes.string) assert result == expected, (result, expected)
def apply_updates(self, document): ctx = context(document) for command in self: command.instantiate(ctx) return document
#!/usr/bin/env python from amara import tree from amara.lib import testsupport from amara.xpath import context, datatypes from amara.xpath.locationpaths import (absolute_location_path, relative_location_path, location_step, abbreviated_absolute_location_path, abbreviated_relative_location_path) from amara.xpath.locationpaths.axisspecifiers import axis_specifier as axis from amara.xpath.locationpaths.nodetests import name_test from test_expressions import (base_expression, DOC, ROOT, CHILD1, CHILD2, CHILD3, LANG, GCHILDREN1, GCHILDREN2, LCHILDREN) CONTEXT_ROOT = context(ROOT, 1, 1) CONTEXT_CHILD1 = context(CHILD1, 1, 3) CHILD_STEP = location_step(axis('child'), name_test('*')) def test_absolute_location_path(): for args, expected in ( ([], datatypes.nodeset([DOC])), # /child::* ([relative_location_path(CHILD_STEP)], datatypes.nodeset([ROOT])), # /descendant::* ([ relative_location_path( location_step(axis('descendant'), name_test('*'))) ], datatypes.nodeset([ROOT, CHILD1] + GCHILDREN1 + [CHILD2] +
FALSE, # number literals (for special values) NOT_A_NUMBER, POSITIVE_INFINITY, NEGATIVE_INFINITY, # nodeset literals nodeset_literal, ROOT, CHILD1, CHILD2, CHILD3, LCHILD1, LCHILD2) # contexts for nodeset {CHILD1, CHILD2, CHILD3} CONTEXT1 = context(CHILD1, 1, 3) CONTEXT2 = context(CHILD2, 2, 3) # contexts for nodeset {LCHILD1, LCHILD2, NONASCIIQNAME} CONTEXTLANG1 = context(LCHILD1, 1, 3) CONTEXTLANG2 = context(LCHILD2, 2, 3) DOC = tree.entity() EGG1 = DOC.xml_append(tree.element(None, 'egg0')) EGG1.xml_append(tree.text('0')) EGG2 = DOC.xml_append(tree.element(None, 'egg1')) EGG2.xml_append(tree.text('1')) EGG3 = DOC.xml_append(tree.element(None, 'egg0')) EGG3.xml_append(tree.text('0')) EGG4 = DOC.xml_append(tree.element(None, 'egg1')) EGG4.xml_append(tree.text('1')) EGG5 = DOC.xml_append(tree.element(None, 'egg0'))
#!/usr/bin/env python from amara import tree from amara.lib import testsupport from amara.xpath import context, datatypes from amara.xpath.locationpaths import (absolute_location_path, relative_location_path, location_step, abbreviated_absolute_location_path, abbreviated_relative_location_path) from amara.xpath.locationpaths.axisspecifiers import axis_specifier as axis from amara.xpath.locationpaths.nodetests import name_test from test_expressions import ( base_expression, DOC, ROOT, CHILD1, CHILD2, CHILD3, LANG, GCHILDREN1, GCHILDREN2, LCHILDREN ) CONTEXT_ROOT = context(ROOT, 1, 1) CONTEXT_CHILD1 = context(CHILD1, 1, 3) CHILD_STEP = location_step(axis('child'), name_test('*')) def test_absolute_location_path(): for args, expected in ( ([], datatypes.nodeset([DOC])), # /child::* ([relative_location_path(CHILD_STEP)], datatypes.nodeset([ROOT])), # /descendant::* ([relative_location_path(location_step(axis('descendant'), name_test('*')))], datatypes.nodeset([ROOT, CHILD1] + GCHILDREN1 + [CHILD2] + GCHILDREN2 + [CHILD3, LANG] + LCHILDREN)), ):
base_expression, # boolean literals TRUE, FALSE, # number literals (for special values) NOT_A_NUMBER, POSITIVE_INFINITY, NEGATIVE_INFINITY, # nodeset literals nodeset_literal, EMPTY_NODESET, ) from amara.xpath import context from test_expressions import DOC default_context = context(DOC, 1, 1) EGG1 = tree.element(None, 'egg1') EGG1.xml_append(tree.text('egg1')) EGG2 = tree.element(None, 'egg2') EGG2.xml_append(tree.text('egg2')) NUM = tree.element(None, 'num') NUM0 = tree.attribute(None, 'num0', '0') NUM.xml_attributes.setnode(NUM0) NUM2 = tree.attribute(None, 'num2', '2') NUM.xml_attributes.setnode(NUM0) NUM4 = tree.attribute(None, 'num4', '4') NUM.xml_attributes.setnode(NUM0) NUM31 = tree.attribute(None, 'num31', '31') NUM.xml_attributes.setnode(NUM0)
#!/usr/bin/env python from amara import tree from amara.xpath import context, datatypes, XPathError from test_expressions import ( # expression TestCase base_expression, # nodeset literals DOC, PI, PI2, ROOT, CHILDREN, CHILD1, ATTR1, GCHILDREN1, GCHILD11, GCHILD12, TEXT1, CHILD2, ATTR2, IDATTR2, GCHILDREN2, GCHILD21, CHILD3, LANG, LCHILDREN, NONASCIIQNAME, TEXT_WS1, TEXT_WS2 ) CONTEXT_DOC = context(DOC, 1, 1) CONTEXT_ROOT = context(ROOT, 1, 1, variables={(None, 'foo'): datatypes.nodeset([ROOT])}) CONTEXT_CHILD1 = context(CHILD1, 1, 2, namespaces={'x': 'http://spam.com'}) CONTEXT_CHILD2 = context(CHILD2, 2, 2) CONTEXT_CHILD3 = context(CHILD3, 1, 1) CONTEXT_TEXT = context(TEXT1, 3, 3) CONTEXT_GCHILD11 = context(GCHILD11, 1, 2) CONTEXT_LANG = context(LANG, 1, 1) # <elements> # <element> # <x> # <y>a</y> # </x> # </element> # <element>
######################################################################## # test/xslt/test_avt.py from amara.test.xpath.test_expressions import DOC from amara.xpath import datatypes, context from amara.xslt import XsltError from amara.xslt.expressions.avt import avt_expression DEFAULT_CONTEXT = context(DOC, 1, 1) def test_avt(): for arg, expected in ( ('', ''), ('Senatus{{populisque}}romae', 'Senatus{populisque}romae'), ('Senatus{{{"populisque}}"}romae', 'Senatus{populisque}}romae'), ('{"{literal}"}', '{literal}'), ('{"{literal"}', '{literal'), ('{"literal}"}', 'literal}'), ('{"{{literal}}"}', '{{literal}}'), ('{"{{literal"}', '{{literal'), ('{"literal}}"}', 'literal}}'), ('{{{"literal"}', '{literal'), ('{{-{"literal"}', '{-literal'), ('{"literal"}}}', 'literal}'), ('{"literal"}-}}', 'literal-}'), ('{"100"}% {100}% {90+10}% 100% {"%"}1{0}0 %100', '100% 100% 100% 100% %100 %100'), ): result = avt_expression(arg).evaluate_as_string(DEFAULT_CONTEXT) assert isinstance(result, datatypes.string)
from amara import tree from amara.xpath import context, datatypes from amara.xpath.expressions.basics import string_literal, number_literal from test_expressions import ( base_expression, # boolean literals TRUE, FALSE, # number literals (for special values) NOT_A_NUMBER, POSITIVE_INFINITY, NEGATIVE_INFINITY, # nodeset literals nodeset_literal, ROOT, CHILD1, CHILD2, CHILD3, LCHILD1, LCHILD2 ) # contexts for nodeset {CHILD1, CHILD2, CHILD3} CONTEXT1 = context(CHILD1, 1, 3) CONTEXT2 = context(CHILD2, 2, 3) # contexts for nodeset {LCHILD1, LCHILD2, NONASCIIQNAME} CONTEXTLANG1 = context(LCHILD1, 1, 3) CONTEXTLANG2 = context(LCHILD2, 2, 3) DOC = tree.entity() EGG1 = DOC.xml_append(tree.element(None, 'egg0')) EGG1.xml_append(tree.text('0')) EGG2 = DOC.xml_append(tree.element(None, 'egg1')) EGG2.xml_append(tree.text('1')) EGG3 = DOC.xml_append(tree.element(None, 'egg0')) EGG3.xml_append(tree.text('0')) EGG4 = DOC.xml_append(tree.element(None, 'egg1')) EGG4.xml_append(tree.text('1')) EGG5 = DOC.xml_append(tree.element(None, 'egg0'))