/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/globbing.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
import re
24
24
 
 
25
from bzrlib import errors
25
26
from bzrlib.trace import (
26
 
    warning
 
27
    mutter,
 
28
    warning,
27
29
    )
28
30
 
29
31
 
177
179
    so are matched first, then the basename patterns, then the fullpath
178
180
    patterns.
179
181
    """
 
182
    # We want to _add_patterns in a specific order (as per type_list below)
 
183
    # starting with the shortest and going to the longest.
 
184
    # As some Python version don't support ordered dicts the list below is
 
185
    # used to select inputs for _add_pattern in a specific order.
 
186
    pattern_types = [ "extension", "basename", "fullpath" ]
 
187
 
 
188
    pattern_info = {
 
189
        "extension" : {
 
190
            "translator" : _sub_extension,
 
191
            "prefix" : r'(?:.*/)?(?!.*/)(?:.*\.)'
 
192
        },
 
193
        "basename" : {
 
194
            "translator" : _sub_basename,
 
195
            "prefix" : r'(?:.*/)?(?!.*/)'
 
196
        },
 
197
        "fullpath" : {
 
198
            "translator" : _sub_fullpath,
 
199
            "prefix" : r''
 
200
        },
 
201
    }
 
202
 
180
203
    def __init__(self, patterns):
181
204
        self._regex_patterns = []
182
 
        path_patterns = []
183
 
        base_patterns = []
184
 
        ext_patterns = []
 
205
        pattern_lists = {
 
206
            "extension" : [],
 
207
            "basename" : [],
 
208
            "fullpath" : [],
 
209
        }
185
210
        for pat in patterns:
186
211
            pat = normalize_pattern(pat)
187
 
            if pat.startswith(u'RE:') or u'/' in pat:
188
 
                path_patterns.append(pat)
189
 
            elif pat.startswith(u'*.'):
190
 
                ext_patterns.append(pat)
191
 
            else:
192
 
                base_patterns.append(pat)
193
 
        self._add_patterns(ext_patterns,_sub_extension,
194
 
            prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
195
 
        self._add_patterns(base_patterns,_sub_basename,
196
 
            prefix=r'(?:.*/)?(?!.*/)')
197
 
        self._add_patterns(path_patterns,_sub_fullpath)
 
212
            pattern_lists[Globster.identify(pat)].append(pat)
 
213
        pi = Globster.pattern_info
 
214
        for t in Globster.pattern_types:
 
215
            self._add_patterns(pattern_lists[t], pi[t]["translator"],
 
216
                pi[t]["prefix"])
198
217
 
199
218
    def _add_patterns(self, patterns, translator, prefix=''):
200
219
        while patterns:
209
228
 
210
229
        :return A matching pattern or None if there is no matching pattern.
211
230
        """
212
 
        for regex, patterns in self._regex_patterns:
213
 
            match = regex.match(filename)
214
 
            if match:
215
 
                return patterns[match.lastindex -1]
 
231
        try:
 
232
            for regex, patterns in self._regex_patterns:
 
233
                match = regex.match(filename)
 
234
                if match:
 
235
                    return patterns[match.lastindex -1]
 
236
        except errors.InvalidPattern, e:
 
237
            # We can't show the default e.msg to the user as thats for
 
238
            # the combined pattern we sent to regex. Instead we indicate to
 
239
            # the user that an ignore file needs fixing.
 
240
            mutter('Invalid pattern found in regex: %s.', e.msg)
 
241
            e.msg = "File ~/.bazaar/ignore or .bzrignore contains error(s)."
 
242
            bad_patterns = ''
 
243
            for _, patterns in self._regex_patterns:
 
244
                for p in patterns:
 
245
                    if not Globster.is_pattern_valid(p):
 
246
                        bad_patterns += ('\n  %s' % p)
 
247
            e.msg += bad_patterns
 
248
            raise e
216
249
        return None
217
250
 
 
251
    @staticmethod
 
252
    def identify(pattern):
 
253
        """Returns pattern category.
 
254
 
 
255
        :param pattern: normalized pattern.
 
256
        Identify if a pattern is fullpath, basename or extension
 
257
        and returns the appropriate type.
 
258
        """
 
259
        if pattern.startswith(u'RE:') or u'/' in pattern:
 
260
            return "fullpath"
 
261
        elif pattern.startswith(u'*.'):
 
262
            return "extension"
 
263
        else:
 
264
            return "basename"
 
265
 
 
266
    @staticmethod
 
267
    def is_pattern_valid(pattern):
 
268
        """Returns True if pattern is valid.
 
269
 
 
270
        :param pattern: Normalized pattern.
 
271
        is_pattern_valid() assumes pattern to be normalized.
 
272
        see: globbing.normalize_pattern
 
273
        """
 
274
        result = True
 
275
        translator = Globster.pattern_info[Globster.identify(pattern)]["translator"]
 
276
        tpattern = '(%s)' % translator(pattern)
 
277
        try:
 
278
            re_obj = re.compile(tpattern, re.UNICODE)
 
279
            re_obj.search("") # force compile
 
280
        except errors.InvalidPattern, e:
 
281
            result = False
 
282
        return result
 
283
 
 
284
 
218
285
class ExceptionGlobster(object):
219
286
    """A Globster that supports exception patterns.
220
287
    
262
329
        self._regex_patterns = []
263
330
        for pat in patterns:
264
331
            pat = normalize_pattern(pat)
265
 
            if pat.startswith(u'RE:') or u'/' in pat:
266
 
                self._add_patterns([pat], _sub_fullpath)
267
 
            elif pat.startswith(u'*.'):
268
 
                self._add_patterns([pat], _sub_extension,
269
 
                    prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
270
 
            else:
271
 
                self._add_patterns([pat], _sub_basename,
272
 
                    prefix=r'(?:.*/)?(?!.*/)')
 
332
            t = Globster.identify(pat)
 
333
            self._add_patterns([pat], Globster.pattern_info[t]["translator"],
 
334
                Globster.pattern_info[t]["prefix"])
273
335
 
274
336
 
275
337
_slashes = re.compile(r'[\\/]+')