changeset 690:1240ada13334 experimental-match-fastpaths

more code/comment clean up - make sure to retain match order
author aflett
date Sat, 15 Mar 2008 05:42:29 +0000
parents 3d7288f373bd
children 52a597419c0d
files genshi/template/markup.py genshi/template/match.py
diffstat 2 files changed, 67 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -298,7 +298,8 @@
                     template = _apply_directives(template, ctxt, directives)
                     remaining = match_set
                     if 'match_once' not in hints:
-                        # match has not been removed, so we need an exclusion matchset
+                        # match has not been removed, so we need an
+                        # exclusion matchset
                         remaining = match_set.with_exclusion(match_template)
                         
                     body = self._exec(self._eval(self._flatten(template, ctxt),
--- a/genshi/template/match.py
+++ b/genshi/template/match.py
@@ -18,23 +18,62 @@
 
 
 class MatchSet(object):
+    """ A MatchSet is a set of matches discovered by the parser. This
+    class encapsulates the matching of a particular event to a set of
+    matches. It is optimized for basic tag matches, since that is by
+    far the most common use of py:match.
 
+    The two primary entry points into MatchSet are ``add``, which adds
+    a new py:match, and ``find_matches``, which returns all
+    /candidate/ match templates. The consumer of ``find_matches``
+    still must call each candidates' match() to ensure the event
+    really matches, and to maintain state within the match.
+
+    If a given py:match's path is simply a node name match,
+    (LocalNameTest) like "xyz", then MatchSet indexes that in a
+    dictionary that maps tag names to matches.
+
+    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):
         """
         If a parent is given, it means this is a wrapper around another
-        set. Just copy references to member variables in parent, but
-        also set exclude
+        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 = []
+            
+            # tag_templates are match templates whose path are simply
+            # a tag, like "body" or "img"
             self.tag_templates = {}
+
+            # other_templates include all other match templates, such
+            # 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
+            self.match_order = parent.match_order
             self.tag_templates = parent.tag_templates
             self.other_templates = parent.other_templates
+        
             self.exclude = copy(parent.exclude)
             if exclude is not None:
                 self.exclude.append(exclude)
@@ -44,8 +83,12 @@
         match_template is a tuple the form
         test, path, template, hints, namespace, directives
         """
+
+        self.match_order.append(match_template)
+        
         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
@@ -93,9 +136,10 @@
         new_match_set = cls(parent=self, exclude=exclude)
         return new_match_set
             
-    def find_matches(self, event):
-        """
-        Return a list of all valid templates that can be used for the given event.
+    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
+        self.tag_templates, then check self.other_templates.
         """
         kind, data, pos = event[:3]
 
@@ -104,13 +148,24 @@
             tag, attrs = data
             if tag.localname in self.tag_templates:
                 for template in self.tag_templates[tag.localname]:
-                    if template not in self.exclude:
-                        yield template
+                    yield template
 
         for template in self.other_templates:
-            if template not in self.exclude:
-                yield template
+            yield template
 
+    def find_matches(self, event):
+        """ Return a list of all valid templates that can be used for the
+        given event.
+
+        The basic work here is sorting the result of find_raw_matches
+        """
+
+        # remove exclusions
+        matches = filter(lambda template: template not in self.exclude,
+                         self.find_raw_matches(event))
+
+        # sort the results according to the order they were added
+        return sorted(matches, key=self.match_order.index)
 
     def __nonzero__(self):
         """
@@ -118,16 +173,6 @@
         """
         return bool(self.tag_templates or self.other_templates)
 
-    def __iter__(self):
-        """
-        I don't think we really need this, but it lets us behave like a list
-        """
-        for template_list in self.tag_templates.iteritems():
-            for template in template_list:
-                yield template
-        for template in self.other_templates:
-            yield template
-
     def __str__(self):
         parent = ""
         if self.parent:
Copyright (C) 2012-2017 Edgewall Software