Fix the no password save issue for ajax login
[chromium-blink-merge.git] / tools / checkdeps / rules.py
blob09d718c7234b6cb0560c928990510e48ae836d37
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Base classes to represent dependency rules, used by checkdeps.py"""
8 import os
9 import re
12 class Rule(object):
13 """Specifies a single rule for an include, which can be one of
14 ALLOW, DISALLOW and TEMP_ALLOW.
15 """
17 # These are the prefixes used to indicate each type of rule. These
18 # are also used as values for self.allow to indicate which type of
19 # rule this is.
20 ALLOW = '+'
21 DISALLOW = '-'
22 TEMP_ALLOW = '!'
24 def __init__(self, allow, directory, source):
25 self.allow = allow
26 self._dir = directory
27 self._source = source
29 def __str__(self):
30 return '"%s%s" from %s.' % (self.allow, self._dir, self._source)
32 def ParentOrMatch(self, other):
33 """Returns true if the input string is an exact match or is a parent
34 of the current rule. For example, the input "foo" would match "foo/bar"."""
35 return self._dir == other or self._dir.startswith(other + '/')
37 def ChildOrMatch(self, other):
38 """Returns true if the input string would be covered by this rule. For
39 example, the input "foo/bar" would match the rule "foo"."""
40 return self._dir == other or other.startswith(self._dir + '/')
43 class MessageRule(Rule):
44 """A rule that has a simple message as the reason for failing,
45 unrelated to directory or source.
46 """
48 def __init__(self, reason):
49 super(MessageRule, self).__init__(Rule.DISALLOW, '', '')
50 self._reason = reason
52 def __str__(self):
53 return self._reason
56 def ParseRuleString(rule_string, source):
57 """Returns a tuple of a character indicating what type of rule this
58 is, and a string holding the path the rule applies to.
59 """
60 if not rule_string:
61 raise Exception('The rule string "%s" is empty\nin %s' %
62 (rule_string, source))
64 if not rule_string[0] in [Rule.ALLOW, Rule.DISALLOW, Rule.TEMP_ALLOW]:
65 raise Exception(
66 'The rule string "%s" does not begin with a "+", "-" or "!".' %
67 rule_string)
69 return (rule_string[0], rule_string[1:])
72 class Rules(object):
73 """Sets of rules for files in a directory.
75 By default, rules are added to the set of rules applicable to all
76 dependee files in the directory. Rules may also be added that apply
77 only to dependee files whose filename (last component of their path)
78 matches a given regular expression; hence there is one additional
79 set of rules per unique regular expression.
80 """
82 def __init__(self):
83 """Initializes the current rules with an empty rule list for all
84 files.
85 """
86 # We keep the general rules out of the specific rules dictionary,
87 # as we need to always process them last.
88 self._general_rules = []
90 # Keys are regular expression strings, values are arrays of rules
91 # that apply to dependee files whose basename matches the regular
92 # expression. These are applied before the general rules, but
93 # their internal order is arbitrary.
94 self._specific_rules = {}
96 def __str__(self):
97 result = ['Rules = {\n (apply to all files): [\n%s\n ],' % '\n'.join(
98 ' %s' % x for x in self._general_rules)]
99 for regexp, rules in self._specific_rules.iteritems():
100 result.append(' (limited to files matching %s): [\n%s\n ]' % (
101 regexp, '\n'.join(' %s' % x for x in rules)))
102 result.append(' }')
103 return '\n'.join(result)
105 def AddRule(self, rule_string, source, dependee_regexp=None):
106 """Adds a rule for the given rule string.
108 Args:
109 rule_string: The include_rule string read from the DEPS file to apply.
110 source: A string representing the location of that string (filename, etc.)
111 so that we can give meaningful errors.
112 dependee_regexp: The rule will only be applied to dependee files
113 whose filename (last component of their path)
114 matches the expression. None to match all
115 dependee files.
117 (rule_type, rule_dir) = ParseRuleString(rule_string, source)
119 if not dependee_regexp:
120 rules_to_update = self._general_rules
121 else:
122 if dependee_regexp in self._specific_rules:
123 rules_to_update = self._specific_rules[dependee_regexp]
124 else:
125 rules_to_update = []
127 # Remove any existing rules or sub-rules that apply. For example, if we're
128 # passed "foo", we should remove "foo", "foo/bar", but not "foobar".
129 rules_to_update = [x for x in rules_to_update
130 if not x.ParentOrMatch(rule_dir)]
131 rules_to_update.insert(0, Rule(rule_type, rule_dir, source))
133 if not dependee_regexp:
134 self._general_rules = rules_to_update
135 else:
136 self._specific_rules[dependee_regexp] = rules_to_update
138 def RuleApplyingTo(self, include_path, dependee_path):
139 """Returns the rule that applies to |include_path| for a dependee
140 file located at |dependee_path|.
142 dependee_filename = os.path.basename(dependee_path)
143 for regexp, specific_rules in self._specific_rules.iteritems():
144 if re.match(regexp, dependee_filename):
145 for rule in specific_rules:
146 if rule.ChildOrMatch(include_path):
147 return rule
148 for rule in self._general_rules:
149 if rule.ChildOrMatch(include_path):
150 return rule
151 return MessageRule('no rule applying.')