Patchwork match: simplify brittle predicate construction

login
register
mail settings
Submitter Martin von Zweigbergk
Date Sept. 19, 2014, 11:31 p.m.
Message ID <988529da78314e664f14.1411169484@handduk2.mtv.corp.google.com>
Download mbox | patch
Permalink /patch/5890/
State Accepted
Headers show

Comments

Martin von Zweigbergk - Sept. 19, 2014, 11:31 p.m.
# HG changeset patch
# User Martin von Zweigbergk <martinvonz@gmail.com>
# Date 1411159798 25200
#      Fri Sep 19 13:49:58 2014 -0700
# Node ID 988529da78314e664f14acbeae1830806b27f16e
# Parent  48791c2bea1ceda4e4f28bc11651e281d636ce1a
match: simplify brittle predicate construction

In match.__init__(), we create the matchfn predicate by and-ing
together the individual predicates for includes, excludes (negated)
and patterns. Instead of the current set of nested if/else blocks, we
can simplify by adding the predicates to a list and defining the
overall predicate in a generic way based on the components. We can
still optimize it for the 0-length and 1-length cases. This way, there
is no combinatorial explosion to deal with if new component predicates
are added, and there is less risk of getting the overall predicate
wrong.
Pierre-Yves David - Sept. 24, 2014, 12:03 a.m.
On 09/19/2014 04:31 PM, Martin von Zweigbergk wrote:
> # HG changeset patch
> # User Martin von Zweigbergk <martinvonz@gmail.com>
> # Date 1411159798 25200
> #      Fri Sep 19 13:49:58 2014 -0700
> # Node ID 988529da78314e664f14acbeae1830806b27f16e
> # Parent  48791c2bea1ceda4e4f28bc11651e281d636ce1a
> match: simplify brittle predicate construction

Excellent, pushed to the clowncopter.

Patch

diff --git a/mercurial/match.py b/mercurial/match.py
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -66,47 +66,39 @@ 
         self._ctx = ctx
         self._always = False
 
+        matchfns = []
         if include:
             kindpats = _normalize(include, 'glob', root, cwd, auditor)
             self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
+            matchfns.append(im)
         if exclude:
             kindpats = _normalize(exclude, 'glob', root, cwd, auditor)
             self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
+            matchfns.append(lambda f: not em(f))
         if exact:
             if isinstance(patterns, list):
                 self._files = patterns
             else:
                 self._files = list(patterns)
-            pm = self.exact
+            matchfns.append(self.exact)
         elif patterns:
             kindpats = _normalize(patterns, default, root, cwd, auditor)
             self._files = _roots(kindpats)
             self._anypats = self._anypats or _anypats(kindpats)
             self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
+            matchfns.append(pm)
 
-        if patterns or exact:
-            if include:
-                if exclude:
-                    m = lambda f: im(f) and not em(f) and pm(f)
-                else:
-                    m = lambda f: im(f) and pm(f)
-            else:
-                if exclude:
-                    m = lambda f: not em(f) and pm(f)
-                else:
-                    m = pm
+        if not matchfns:
+            m = util.always
+            self._always = True
+        elif len(matchfns) == 1:
+            m = matchfns[0]
         else:
-            if include:
-                if exclude:
-                    m = lambda f: im(f) and not em(f)
-                else:
-                    m = im
-            else:
-                if exclude:
-                    m = lambda f: not em(f)
-                else:
-                    m = lambda f: True
-                    self._always = True
+            def m(f):
+                for matchfn in matchfns:
+                    if not matchfn(f):
+                        return False
+                return True
 
         self.matchfn = m
         self._fmap = set(self._files)