~bzr-pqm/bzr/bzr.dev

3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
1
# Copyright (C) 2008 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
16
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
17
"""Rule-based definition of preferences for selected files in selected branches.
3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
18
3398.1.22 by Ian Clatworthy
minor tweaks
19
See ``bzr help rules`` for details.
3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
20
"""
21
22
from bzrlib import (
23
    config,
4913.5.24 by Gordon Tyler
Added cmdline.split function, which replaces commands.shlex_split_unicode.
24
    cmdline,
3398.1.30 by Ian Clatworthy
test unknown rules detection
25
    errors,
3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
26
    globbing,
27
    osutils,
28
    )
3398.1.6 by Ian Clatworthy
get properties_filename() test passing
29
from bzrlib.util.configobj import configobj
3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
30
31
3398.1.24 by Ian Clatworthy
make iter_search_rules a tree method
32
# Name of the file holding rules in a tree
33
RULES_TREE_FILENAME = ".bzrrules"
34
3398.1.28 by Ian Clatworthy
add namespace for rules
35
# Namespace prefix for per file preferences
3398.1.32 by Ian Clatworthy
namespace keyword changed to name
36
FILE_PREFS_PREFIX = 'name '
3398.1.28 by Ian Clatworthy
add namespace for rules
37
FILE_PREFS_PREFIX_LEN = len(FILE_PREFS_PREFIX)
38
4324.4.1 by Marius Kruger
Make it possible to blackboxtest rules
39
# The object providing default rules
40
_per_user_searcher = None
41
3398.1.24 by Ian Clatworthy
make iter_search_rules a tree method
42
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
43
class _RulesSearcher(object):
44
    """An object that provides rule-based preferences."""
3398.1.13 by Ian Clatworthy
rename properties to attributes
45
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
46
    def get_items(self, path):
47
        """Return the preferences for a path as name,value tuples.
3398.1.13 by Ian Clatworthy
rename properties to attributes
48
49
        :param path: tree relative path
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
50
        :return: () if no rule matched, otherwise a sequence of name,value
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
51
          tuples.
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
52
        """
53
        raise NotImplementedError(self.get_items)
54
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
55
    def get_selected_items(self, path, names):
56
        """Return selected preferences for a path as name,value tuples.
57
58
        :param path: tree relative path
59
        :param names: the list of preferences to lookup
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
60
        :return: () if no rule matched, otherwise a sequence of name,value
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
61
          tuples. The sequence is the same length as names,
62
          tuple order matches the order in names, and
63
          undefined preferences are given the value None.
64
        """
65
        raise NotImplementedError(self.get_selected_items)
66
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
67
68
class _IniBasedRulesSearcher(_RulesSearcher):
69
70
    def __init__(self, inifile):
71
        """Construct a _RulesSearcher based on an ini file.
72
3398.1.24 by Ian Clatworthy
make iter_search_rules a tree method
73
        The content will be decoded as utf-8.
74
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
75
        :param inifile: the name of the file or a sequence of lines.
76
        """
3398.1.24 by Ian Clatworthy
make iter_search_rules a tree method
77
        options = {'encoding': 'utf-8'}
78
        self._cfg = configobj.ConfigObj(inifile, options=options)
3398.1.28 by Ian Clatworthy
add namespace for rules
79
        sections = self._cfg.keys()
3946.1.1 by Ian Clatworthy
Multi-glob rules (Marius Kruger)
80
        patterns = []
3943.3.1 by Marius Kruger
add test and support for multi-glob rules
81
        self.pattern_to_section = {}
82
        for s in sections:
83
            if s.startswith(FILE_PREFS_PREFIX):
4913.5.24 by Gordon Tyler
Added cmdline.split function, which replaces commands.shlex_split_unicode.
84
                file_patterns = cmdline.split(s[FILE_PREFS_PREFIX_LEN:])
3943.3.1 by Marius Kruger
add test and support for multi-glob rules
85
                patterns.extend(file_patterns)
86
                for fp in file_patterns:
87
                    self.pattern_to_section[fp] = s
3398.1.28 by Ian Clatworthy
add namespace for rules
88
        if len(patterns) < len(sections):
89
            unknowns = [s for s in sections
90
                if not s.startswith(FILE_PREFS_PREFIX)]
3398.1.30 by Ian Clatworthy
test unknown rules detection
91
            raise errors.UnknownRules(unknowns)
3398.1.28 by Ian Clatworthy
add namespace for rules
92
        elif patterns:
3398.1.17 by Ian Clatworthy
search less when files not present
93
            self._globster = globbing._OrderedGlobster(patterns)
94
        else:
95
            self._globster = None
3398.1.3 by Ian Clatworthy
first cut at PropertiesProvider class
96
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
97
    def get_items(self, path):
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
98
        """See _RulesSearcher.get_items."""
3398.1.17 by Ian Clatworthy
search less when files not present
99
        if self._globster is None:
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
100
            return ()
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
101
        pat = self._globster.match(path)
102
        if pat is None:
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
103
            return ()
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
104
        else:
3943.3.1 by Marius Kruger
add test and support for multi-glob rules
105
            all = self._cfg[self.pattern_to_section[pat]]
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
106
            return tuple(all.items())
107
108
    def get_selected_items(self, path, names):
109
        """See _RulesSearcher.get_selected_items."""
110
        if self._globster is None:
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
111
            return ()
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
112
        pat = self._globster.match(path)
113
        if pat is None:
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
114
            return ()
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
115
        else:
3943.3.1 by Marius Kruger
add test and support for multi-glob rules
116
            all = self._cfg[self.pattern_to_section[pat]]
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
117
            return tuple((k, all.get(k)) for k in names)
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
118
119
120
class _StackedRulesSearcher(_RulesSearcher):
121
122
    def __init__(self, searchers):
123
        """Construct a _RulesSearcher based on a stack of other ones.
124
125
        :param searchers: a sequence of searchers.
126
        """
127
        self.searchers = searchers
128
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
129
    def get_items(self, path):
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
130
        """See _RulesSearcher.get_items."""
3398.1.18 by Ian Clatworthy
add tests for _StackedRulesSearcher
131
        for searcher in self.searchers:
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
132
            result = searcher.get_items(path)
133
            if result:
134
                return result
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
135
        return ()
3398.1.34 by Ian Clatworthy
changed API design as requested by jam during review
136
137
    def get_selected_items(self, path, names):
138
        """See _RulesSearcher.get_selected_items."""
139
        for searcher in self.searchers:
140
            result = searcher.get_selected_items(path, names)
141
            if result:
142
                return result
3564.1.1 by Ian Clatworthy
RuleSearchers need to return () instead of []
143
        return ()
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
144
145
146
def rules_filename():
147
    """Return the default rules filename."""
3398.1.23 by Ian Clatworthy
update doc to reflect file naming per poolie's review
148
    return osutils.pathjoin(config.config_dir(), 'rules')
3398.1.15 by Ian Clatworthy
search branch.rules and bazaar.rules for preferences
149
150
4324.4.1 by Marius Kruger
Make it possible to blackboxtest rules
151
def reset_rules():
152
    global _per_user_searcher
153
    _per_user_searcher = _IniBasedRulesSearcher(rules_filename())
154
155
reset_rules()