Mercurial > genshi > mirror
diff genshi/template/match.py @ 703:af57b12e3dd2 experimental-match-fastpaths
merge in trunk up through r818 - fundamentally changed the way MatchSet works, but actually is more consistent now
author | aflett |
---|---|
date | Mon, 31 Mar 2008 22:47:50 +0000 |
parents | 52a597419c0d |
children | 422d0607ba85 |
line wrap: on
line diff
--- a/genshi/template/match.py +++ b/genshi/template/match.py @@ -2,6 +2,7 @@ from genshi.path import CHILD, LocalNameTest from copy import copy +from itertools import ifilter def is_simple_path(path): """ @@ -35,24 +36,25 @@ If the path is more complex like "xyz[k=z]" then then that match will always be returned by ``find_matches``. """ - def __init__(self, parent=None, exclude=None): + def __init__(self, parent=None, + min_index=None, + max_index=None): """ If a parent is given, it means this is a wrapper around another set. - If exclude is given, it means include everything in the - parent, but exclude a specific match template. """ self.parent = parent - self.current_index = 0 - if parent is None: # merely for indexing. Note that this is shared between # all MatchSets that share the same root parent. We don't # have to worry about exclusions here self.match_order = {} + self.min_index = None + self.max_index = None + # tag_templates are match templates whose path are simply # a tag, like "body" or "img" self.tag_templates = {} @@ -61,23 +63,23 @@ # as ones with complex paths like "[class=container]" self.other_templates = [] - # exclude is a list of templates to ignore when iterating - # through templates - self.exclude = [] - if exclude is not None: - self.exclude.append(exclude) else: # We have a parent: Just copy references to member - # variables in parent so that there's no performance loss, - # but make our own exclusion set, so we don't have to - # chain exclusions across a chain of MatchSets + # variables in parent so that there's no performance loss + self.max_index = parent.max_index + self.min_index = parent.min_index self.match_order = parent.match_order self.tag_templates = parent.tag_templates self.other_templates = parent.other_templates + + if max_index is not None: + assert self.max_index is None or max_index <= self.max_index + self.max_index = max_index + + if min_index is not None: + assert self.min_index is None or min_index > self.min_index + self.min_index = min_index - self.exclude = copy(parent.exclude) - if exclude is not None: - self.exclude.append(exclude) def add(self, match_template): """ @@ -91,7 +93,6 @@ path = match_template[1] - self.current_index += 1 if is_simple_path(path): # special cache of tag tag_name = path.paths[0][0][1].name @@ -133,15 +134,22 @@ return match_set single_match = classmethod(single_match) - def with_exclusion(self, exclude): + def before_template(self, match_template, inclusive): + cls = type(self) + max_index = self.match_order[id(match_template)] + if not inclusive: + max_index -= 1 + return cls(parent=self, max_index=max_index) + + def after_template(self, match_template): """ - Factory for creating a MatchSet based on another MatchSet, but - with certain templates excluded + Factory for creating a MatchSet that only matches templates after + the given match """ - cls = self.__class__ - new_match_set = cls(parent=self, exclude=exclude) - return new_match_set - + cls = type(self) + min_index = self.match_order[id(match_template)] + 1 + return cls(parent=self, min_index=min_index) + def find_raw_matches(self, event): """ Return a list of all valid templates that can be used for the given event. Ordering is funky because we first check @@ -167,8 +175,20 @@ """ # remove exclusions - matches = filter(lambda template: template not in self.exclude, - self.find_raw_matches(event)) + def can_match(template): + # make sure that + if (self.min_index is not None and + self.match_order[id(template)] < self.min_index): + return False + + if (self.max_index is not None and + self.match_order[id(template)] > self.max_index): + return False + + return True + + matches = ifilter(can_match, + self.find_raw_matches(event)) # sort the results according to the order they were added return sorted(matches, key=lambda v: self.match_order[id(v)]) @@ -177,6 +197,21 @@ """ allow this to behave as a list """ + + # this is easy - before the first element there is nothing + if self.max_index == -1: + return False + + # this isn't always right because match_order may shrink, but + # you'll never get a false-negative + if self.min_index == len(self.match_order): + return False + + # check for a range that is completely constrained + if self.min_index is not None and self.max_index is not None: + if self.min_index >= self.max_index: + return False + return bool(self.tag_templates or self.other_templates) def __str__(self): @@ -184,8 +219,7 @@ if self.parent: parent = ": child of 0x%x" % id(self.parent) - exclude = "" - if self.exclude: - exclude = " / excluding %d items" % len(self.exclude) - - return "<MatchSet 0x%x %d tag templates, %d other templates%s%s>" % (id(self), len(self.tag_templates), len(self.other_templates), parent, exclude) + return "<MatchSet 0x%x %d tag templates, %d other templates, range=[%s:%s]%s>" % ( + id(self), len(self.tag_templates), len(self.other_templates), + self.min_index, self.max_index, + parent)