~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/globbing.py

Merge first-try into propagate-exceptions

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'[\\/]+')