11 def globmatcher(pattern): |
11 def globmatcher(pattern): |
12 p = "[^/]*".join(re.escape(c) for c in pattern.split("*")) |
12 p = "[^/]*".join(re.escape(c) for c in pattern.split("*")) |
13 # ** means "match recursively" ie "ignore directories" |
13 # ** means "match recursively" ie "ignore directories" |
14 return re.compile(p.replace("[^/]*[^/]*", ".*") + "$") |
14 return re.compile(p.replace("[^/]*[^/]*", ".*") + "$") |
15 |
15 |
16 # Returns True for a definite match |
16 # Returns 1 for a definite match |
17 # False for a definite non-match |
17 # -1 for a definite non-match |
18 # None where we can't be sure because a key is None |
18 # 0 where we can't be sure because a key is None |
|
19 def rmatch(k, m, kw): |
|
20 if k not in kw: |
|
21 return -1 |
|
22 kkw = kw[k] |
|
23 if kkw is None: |
|
24 return 0 |
|
25 elif m.match(kkw) is None: |
|
26 return -1 |
|
27 else: |
|
28 return 1 |
|
29 |
19 def rule(pairs): |
30 def rule(pairs): |
20 matchers = [(k, globmatcher(v)) for k, v in pairs] |
31 matchers = [(k, globmatcher(v)) for k, v in pairs] |
21 def c(kw): |
32 def c(kw): |
22 best = True |
33 return min(rmatch(k, m, kw) for k, m in matchers) |
23 for k, m in matchers: |
|
24 if k not in kw: |
|
25 return False |
|
26 kkw = kw[k] |
|
27 if kkw is None: |
|
28 best = None |
|
29 elif m.match(kkw) is None: |
|
30 return False |
|
31 return best |
|
32 return c |
34 return c |
33 |
35 |
34 class Ruleset(object): |
36 class Ruleset(object): |
35 '''Class representing the rules in a rule file''' |
37 '''Class representing the rules in a rule file''' |
36 |
38 |
39 def __init__(self): |
41 def __init__(self): |
40 self.rules = [] |
42 self.rules = [] |
41 self.preset = {} |
43 self.preset = {} |
42 |
44 |
43 def add(self, action, conditions): |
45 def add(self, action, conditions): |
44 self.rules.append((action, conditions)) |
46 # Unrecognized actions are off the high end |
|
47 if action in self.levels: |
|
48 self.rules.append((self.levels.index(action), conditions)) |
|
49 else: |
|
50 self.rules.append((len(self.levels), conditions)) |
45 |
51 |
46 def set(self, **kw): |
52 def set(self, **kw): |
47 self.preset.update(kw) |
53 self.preset.update(kw) |
48 |
54 |
49 def get(self, k): |
55 def get(self, k): |
50 return self.preset.get(k, None) |
56 return self.preset.get(k, None) |
51 |
57 |
52 def matchrules(self, kw): |
58 def allow(self, level, **kw): |
|
59 levelindex = self.levels.index(level) |
53 d = self.preset.copy() |
60 d = self.preset.copy() |
54 d.update(kw) |
61 d.update(kw) |
55 res = set() |
|
56 for a, c in self.rules: |
62 for a, c in self.rules: |
57 m = c(d) |
63 m = c(d) |
58 if m is None: |
64 if m == 1: |
59 # "Maybe match" - add it and carry on |
65 # Definite match - what it says goes |
60 res.add(a) |
66 return a <= levelindex |
61 elif m: |
67 elif m == 0: |
62 # Definite match - add it and stop |
68 # "Maybe match" - allow if it says yes, ignore if no |
63 res.add(a) |
69 if a <= levelindex: |
64 break |
|
65 return res |
|
66 |
|
67 def allow(self, level, **kw): |
|
68 for a in self.matchrules(kw): |
|
69 if a in self.levels: |
|
70 if self.levels.index(a) <= self.levels.index(level): |
|
71 return True |
70 return True |
72 return False |
71 return False |
73 |
72 |
74 def readfile(self, fn): |
73 def readfile(self, fn): |
75 f = open(fn) |
74 f = open(fn) |