src/mercurialserver/ruleset.py
branchdebian
changeset 248 107d28ce67f5
parent 242 03d8f07230b3
child 297 9875791ab421
equal deleted inserted replaced
240:97a8fe72a35e 248:107d28ce67f5
     1 # Copyright 2008-2009 LShift Ltd
     1 """
     2 # Author(s):
     2 Glob-based, order-based rules matcher that can answer "maybe"
     3 # Paul Crowley <paul@lshift.net>
     3 where the inputs make clear that something is unknown.
     4 #
     4 """
     5 # This software may be used and distributed according to the terms
       
     6 # of the GNU General Public License, incorporated herein by reference.
       
     7 
     5 
     8 import sys
     6 import sys
     9 import re
     7 import re
    10 import os
     8 import os
    11 import os.path
     9 import os.path
    12 
    10 
    13 allowedchars = "A-Za-z0-9_-"
       
    14 
       
    15 def globmatcher(pattern):
    11 def globmatcher(pattern):
    16     p = "[^/]*".join(re.escape(c) for c in pattern.split("*"))
    12     p = "[^/]*".join(re.escape(c) for c in pattern.split("*"))
    17     # ** means "match recursively" ie "ignore directories"
    13     # ** means "match recursively" ie "ignore directories"
    18     rex = re.compile(p.replace("[^/]*[^/]*", ".*") + "$")
    14     return re.compile(p.replace("[^/]*[^/]*", ".*") + "$")
    19     # None matches everything
       
    20     return lambda x: x is None or rex.match(x) is not None
       
    21 
    15 
       
    16 # Returns True for a definite match
       
    17 # False for a definite non-match
       
    18 # None where we can't be sure because a key is None
    22 def rule(pairs):
    19 def rule(pairs):
    23     matchers = [(k, globmatcher(v)) for k, v in pairs]
    20     matchers = [(k, globmatcher(v)) for k, v in pairs]
    24     def c(kw):
    21     def c(kw):
    25         for k, m in matchers:
    22         for k, m in matchers:
    26             if k not in kw or not m(kw[k]):
    23             if k not in kw:
       
    24                 return False
       
    25             kkw = kw[k]
       
    26             if kkw is None:
       
    27                 return None
       
    28             if m.match(kkw) is None:
    27                 return False
    29                 return False
    28         return True
    30         return True
    29     return c
    31     return c
    30 
    32 
    31 class Ruleset(object):
    33 class Ruleset(object):
    44         self.preset.update(kw)
    46         self.preset.update(kw)
    45         
    47         
    46     def get(self, k):
    48     def get(self, k):
    47         return self.preset.get(k, None)
    49         return self.preset.get(k, None)
    48         
    50         
    49     def matchrule(self, kw):
    51     def matchrules(self, kw):
    50         d = self.preset.copy()
    52         d = self.preset.copy()
    51         d.update(kw)
    53         d.update(kw)
       
    54         res = set()
    52         for a, c in self.rules:
    55         for a, c in self.rules:
    53             if c(d):
    56             m = c(d)
    54                 return a
    57             if m is None:
    55         return None
    58                 # "Maybe match" - add it and carry on
       
    59                 res.add(a)
       
    60             elif m:
       
    61                 # Definite match - add it and stop
       
    62                 res.add(a)
       
    63                 break
       
    64         return res
    56 
    65 
    57     def allow(self, level, **kw):
    66     def allow(self, level, **kw):
    58         a = self.matchrule(kw)
    67         for a in self.matchrules(kw):
    59         return a in self.levels and self.levels.index(a) <= self.levels.index(level)
    68             if a in self.levels:
       
    69                 if self.levels.index(a) <= self.levels.index(level):
       
    70                     return True
       
    71         return False
    60     
    72     
    61     def readfile(self, fn):
    73     def readfile(self, fn):
       
    74         f = open(fn)
    62         try:
    75         try:
    63             f = open(fn)
    76             for l in f:
    64             try:
    77                 l = l.strip()
    65                 for l in f:
    78                 if len(l) == 0 or l.startswith("#"):
    66                     l = l.strip()
    79                     continue
    67                     if len(l) == 0 or l.startswith("#"):
    80                 l = l.split()
    68                         continue
    81                 self.add(l[0], rule([c.split("=", 1) for c in l[1:]]))
    69                     l = l.split()
    82         finally:
    70                     self.add(l[0], rule([c.split("=", 1) for c in l[1:]]))
    83             f.close()
    71             finally:
       
    72                 f.close()
       
    73         except Exception, e:
       
    74             print >> sys.stderr, "Failure reading rules file:", e
       
    75 
    84 
    76 rules = Ruleset()
    85 rules = Ruleset()
    77 
    86