# HG changeset patch # User athomas # Date 1182672647 0 # Node ID cccb6c748609aa4e72585a304934ed75ecf982e3 # Parent dfb45908fadc593018f6702a8318ba3befbc9047 Add XPath `matches()` function which, of course, supports the Python regular expression sytax. diff --git a/genshi/path.py b/genshi/path.py --- a/genshi/path.py +++ b/genshi/path.py @@ -755,6 +755,27 @@ def __repr__(self): return 'contains(%r, %r)' % (self.string1, self.string2) +class MatchesFunction(Function): + """The `matches` function, which returns whether a string matches a regular + expression. + """ + __slots__ = ['string1', 'string2'] + flag_mapping = {'s': re.S, 'm': re.M, 'i': re.I, 'x': re.X} + + def __init__(self, string1, string2, flags=''): + self.string1 = string1 + self.string2 = string2 + self.flags = self._map_flags(flags) + def __call__(self, kind, data, pos, namespaces, variables): + string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) + string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) + return re.search(string2, string1, self.flags) + def _map_flags(self, flags): + return reduce(lambda a, b: a | b, + [self.flag_map[flag] for flag in flags], re.U) + def __repr__(self): + return 'contains(%r, %r)' % (self.string1, self.string2) + class FalseFunction(Function): """The `false` function, which always returns the boolean `false` value.""" __slots__ = [] @@ -977,17 +998,16 @@ _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction, 'concat': ConcatFunction, 'contains': ContainsFunction, - 'false': FalseFunction, 'floor': FloorFunction, - 'local-name': LocalNameFunction, 'name': NameFunction, - 'namespace-uri': NamespaceUriFunction, + 'matches': MatchesFunction, 'false': FalseFunction, 'floor': + FloorFunction, 'local-name': LocalNameFunction, 'name': + NameFunction, 'namespace-uri': NamespaceUriFunction, 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction, 'number': NumberFunction, 'round': RoundFunction, - 'starts-with': StartsWithFunction, - 'string-length': StringLengthFunction, - 'substring': SubstringFunction, - 'substring-after': SubstringAfterFunction, - 'substring-before': SubstringBeforeFunction, - 'translate': TranslateFunction, 'true': TrueFunction} + 'starts-with': StartsWithFunction, 'string-length': + StringLengthFunction, 'substring': SubstringFunction, + 'substring-after': SubstringAfterFunction, 'substring-before': + SubstringBeforeFunction, 'translate': TranslateFunction, + 'true': TrueFunction} # Literals & Variables diff --git a/genshi/tests/path.py b/genshi/tests/path.py --- a/genshi/tests/path.py +++ b/genshi/tests/path.py @@ -328,6 +328,11 @@ path = Path('*[contains(name(), "oo")]') self.assertEqual('bar', path.select(xml).render()) + def test_predicate_matches_function(self): + xml = XML('barfoo') + path = Path('*[matches(name(), "foo|bar")]') + self.assertEqual('barfoo', path.select(xml).render()) + def test_predicate_false_function(self): xml = XML('bar') path = Path('*[false()]')