Mercurial > genshi > genshi-test
changeset 38:fec9f4897415
Fix for #2 (incorrect context node in path expressions). Still some paths that produce incorrect results, but the common case seems to work now.
author | cmlenz |
---|---|
date | Mon, 03 Jul 2006 11:28:13 +0000 |
parents | 224b0b41d1da |
children | 71ecbe90aafc |
files | examples/includes/skins/default/layout.html examples/turbogears/markuptest/templates/master.html markup/path.py markup/template.py markup/tests/path.py |
diffstat | 5 files changed, 59 insertions(+), 41 deletions(-) [+] |
line wrap: on
line diff
--- a/examples/includes/skins/default/layout.html +++ b/examples/includes/skins/default/layout.html @@ -10,7 +10,7 @@ <body py:match="body"> <xi:include href="header.html" /> <div id="content"> - ${select('body/*')} + ${select('*')} </div> <xi:include href="footer.html" /> </body>
--- a/examples/turbogears/markuptest/templates/master.html +++ b/examples/turbogears/markuptest/templates/master.html @@ -7,7 +7,7 @@ <head py:match="head"> <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/> <title py:replace="''">Your title goes here</title> - ${select('*/*')} + ${select('*')} <style type="text/css"> #pageLogin { @@ -32,7 +32,7 @@ <div py:if="tg_flash" class="flash" py:content="tg_flash"></div> - ${select('*/*')} + ${select('*')} <p align="center"><img src="/static/images/tg_under_the_hood.png" alt="TurboGears under the hood"/></p> </body>
--- a/markup/path.py +++ b/markup/path.py @@ -55,6 +55,8 @@ else: raise NotImplementedError('XPath function "%s" not ' 'supported' % cur_tag) + elif op == '.': + steps.append([False, self._CurrentElement(), []]) else: cur_op += op cur_tag = '' @@ -67,7 +69,7 @@ node_test = self._AttributeByName(tag) else: if tag == '*': - node_test = self._AnyElement() + node_test = self._AnyChildElement() elif in_predicate: if len(tag) > 1 and (tag[0], tag[-1]) in self._QUOTES: node_test = self._LiteralString(tag[1:-1]) @@ -80,14 +82,17 @@ node_test) steps[-1][2].pop() else: - node_test = self._ElementByName(tag) + node_test = self._ChildElementByName(tag) if in_predicate: steps[-1][2].append(node_test) else: steps.append([closure, node_test, []]) cur_op = '' cur_tag = tag - self.steps = steps + + self.steps = [] + for step in steps: + self.steps.append(tuple(step)) def __repr__(self): return '<%s "%s">' % (self.__class__.__name__, self.source) @@ -119,17 +124,14 @@ depth = 1 while depth > 0: ev = stream.next() - if ev[0] is Stream.START: - depth += 1 - elif ev[0] is Stream.END: - depth -= 1 + depth += {Stream.START: 1, Stream.END: -1}.get(ev[0], 0) yield ev test(*ev) elif result: yield result return Stream(_generate()) - def test(self): + def test(self, ignore_context=False): """Returns a function that can be used to track whether the path matches a specific stream event. @@ -172,9 +174,11 @@ if matched: if stack[-1] == len(self.steps) - 1: - return matched - - stack[-1] += 1 + if ignore_context or len(stack) > 2 \ + or node_test.axis != 'child': + return matched + else: + stack[-1] += 1 elif kind is Stream.START and not closure: # If this step is not a closure, it cannot be matched until the @@ -195,17 +199,31 @@ return _test - class _AnyElement(object): - """Node test that matches any element.""" + class _NodeTest(object): + """Abstract node test.""" + axis = None + def __repr__(self): + return '<%s>' % self.__class__.__name__ + + class _CurrentElement(_NodeTest): + """Node test that matches the context node.""" + axis = 'self' def __call__(self, kind, *_): if kind is Stream.START: return True return None - def __repr__(self): - return '<%s>' % self.__class__.__name__ - class _ElementByName(object): - """Node test that matches an element with a specific tag name.""" + class _AnyChildElement(_NodeTest): + """Node test that matches any child element.""" + axis = 'child' + def __call__(self, kind, *_): + if kind is Stream.START: + return True + return None + + class _ChildElementByName(_NodeTest): + """Node test that matches a child element with a specific tag name.""" + axis = 'child' def __init__(self, name): self.name = QName(name) def __call__(self, kind, data, _): @@ -215,8 +233,9 @@ def __repr__(self): return '<%s "%s">' % (self.__class__.__name__, self.name) - class _AnyAttribute(object): + class _AnyAttribute(_NodeTest): """Node test that matches any attribute.""" + axis = 'attribute' def __call__(self, kind, data, pos): if kind is Stream.START: text = ''.join([val for _, val in data[1]]) @@ -224,11 +243,10 @@ return Stream.TEXT, text, pos return None return None - def __repr__(self): - return '<%s>' % (self.__class__.__name__) - class _AttributeByName(object): + class _AttributeByName(_NodeTest): """Node test that matches an attribute with a specific name.""" + axis = 'attribute' def __init__(self, name): self.name = QName(name) def __call__(self, kind, data, pos): @@ -240,25 +258,24 @@ def __repr__(self): return '<%s "%s">' % (self.__class__.__name__, self.name) - class _FunctionText(object): + class _Function(_NodeTest): + """Abstract node test representing a function.""" + + class _FunctionText(_Function): """Function that returns text content.""" def __call__(self, kind, data, pos): if kind is Stream.TEXT: return kind, data, pos return None - def __repr__(self): - return '<%s>' % (self.__class__.__name__) - class _LiteralString(object): + class _LiteralString(_NodeTest): """Always returns a literal string.""" def __init__(self, value): self.value = value def __call__(self, *_): return Stream.TEXT, self.value, (-1, -1) - def __repr__(self): - return '<%s>' % (self.__class__.__name__) - class _OperatorEq(object): + class _OperatorEq(_NodeTest): """Equality comparison operator.""" def __init__(self, lval, rval): self.lval = lval @@ -271,7 +288,7 @@ return '<%s %r = %r>' % (self.__class__.__name__, self.lval, self.rval) - class _OperatorNeq(object): + class _OperatorNeq(_NodeTest): """Inequality comparison operator.""" def __init__(self, lval, rval): self.lval = lval
--- a/markup/template.py +++ b/markup/template.py @@ -417,7 +417,8 @@ def __call__(self, stream, ctxt): self.stream = list(stream) - ctxt._match_templates.append((self.path.test(), self.path, self.stream)) + ctxt._match_templates.append((self.path.test(ignore_context=True), + self.path, self.stream)) return [] def __repr__(self):
--- a/markup/tests/path.py +++ b/markup/tests/path.py @@ -21,15 +21,15 @@ class PathTestCase(unittest.TestCase): def test_1step(self): - xml = XML('<root/>') - self.assertEqual('<root/>', Path('root').select(xml).render()) - self.assertEqual('<root/>', Path('//root').select(xml).render()) - + xml = XML('<root><elem/></root>') + self.assertEqual('<elem/>', Path('elem').select(xml).render()) + self.assertEqual('<elem/>', Path('//elem').select(xml).render()) + def test_1step_wildcard(self): - xml = XML('<root/>') - self.assertEqual('<root/>', Path('*').select(xml).render()) - self.assertEqual('<root/>', Path('//*').select(xml).render()) - + xml = XML('<root><elem/></root>') + self.assertEqual('<elem/>', Path('*').select(xml).render()) + self.assertEqual('<elem/>', Path('//*').select(xml).render()) + def test_1step_attribute(self): path = Path('@foo') self.assertEqual('', path.select(XML('<root/>')).render())