~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/globbing.py

'bzr ignore' now fails on bad patterns. failing patterns are displayed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
179
179
    so are matched first, then the basename patterns, then the fullpath
180
180
    patterns.
181
181
    """
 
182
    TYPE_FULLPATH = 1
 
183
    TYPE_BASENAME = 2
 
184
    TYPE_EXTENSION = 3
 
185
 
 
186
    translators = {
 
187
        TYPE_FULLPATH : _sub_fullpath,
 
188
        TYPE_BASENAME : _sub_basename,
 
189
        TYPE_EXTENSION : _sub_extension,
 
190
    }
 
191
 
 
192
    # Prefixes used to combine various patterns.
 
193
    # See: Globster._add_patterns
 
194
    prefixes = {
 
195
        TYPE_FULLPATH : r'',
 
196
        TYPE_BASENAME : r'(?:.*/)?(?!.*/)',
 
197
        TYPE_EXTENSION : r'(?:.*/)?(?!.*/)(?:.*\.)',
 
198
    }
 
199
 
182
200
    def __init__(self, patterns):
183
201
        self._regex_patterns = []
184
 
        path_patterns = []
185
 
        base_patterns = []
186
 
        ext_patterns = []
 
202
        pattern_lists = {
 
203
            Globster.TYPE_FULLPATH : [],
 
204
            Globster.TYPE_EXTENSION : [],
 
205
            Globster.TYPE_BASENAME : [],
 
206
        }
187
207
        for pat in patterns:
188
208
            pat = normalize_pattern(pat)
189
 
            if pat.startswith(u'RE:') or u'/' in pat:
190
 
                path_patterns.append(pat)
191
 
            elif pat.startswith(u'*.'):
192
 
                ext_patterns.append(pat)
193
 
            else:
194
 
                base_patterns.append(pat)
195
 
        self._add_patterns(ext_patterns,_sub_extension,
196
 
            prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
197
 
        self._add_patterns(base_patterns,_sub_basename,
198
 
            prefix=r'(?:.*/)?(?!.*/)')
199
 
        self._add_patterns(path_patterns,_sub_fullpath)
 
209
            pattern_lists[Globster.identify(pat)].append(pat)
 
210
        for pattern_type, patterns in pattern_lists.iteritems():
 
211
            self._add_patterns(patterns,
 
212
                Globster.translators[pattern_type],
 
213
                Globster.prefixes[pattern_type])
200
214
 
201
215
    def _add_patterns(self, patterns, translator, prefix=''):
202
216
        while patterns:
221
235
            # the combined pattern we sent to regex. Instead we indicate to
222
236
            # the user that an ignore file needs fixing.
223
237
            mutter('Invalid pattern found in regex: %s.', e.msg)
224
 
            e.msg = "File ~/.bazaar/ignore or .bzrignore contains errors."
 
238
            e.msg = "File ~/.bazaar/ignore or .bzrignore contains error(s)."
 
239
            bad_patterns = ''
 
240
            for _, patterns in self._regex_patterns:
 
241
                for p in patterns:
 
242
                    if not Globster.is_pattern_valid(p):
 
243
                        bad_patterns += ('\n  %s' % p)
 
244
            e.msg += bad_patterns
225
245
            raise e
226
246
        return None
227
247
 
 
248
    @staticmethod
 
249
    def identify(pattern):
 
250
        """Returns pattern category.
 
251
 
 
252
        :param pattern: normalized pattern.
 
253
        Identify if a pattern is fullpath, basename or extension
 
254
        and returns the appropriate type.
 
255
        """
 
256
        if pattern.startswith(u'RE:') or u'/' in pattern:
 
257
            return Globster.TYPE_FULLPATH
 
258
        elif pattern.startswith(u'*.'):
 
259
            return Globster.TYPE_EXTENSION
 
260
        else:
 
261
            return Globster.TYPE_BASENAME
 
262
 
 
263
    @staticmethod
 
264
    def is_pattern_valid(pattern):
 
265
        """Returns True if pattern is valid.
 
266
 
 
267
        :param pattern: Normalized pattern.
 
268
        is_pattern_valid() assumes pattern to be normalized.
 
269
        see: globbing.normalize_pattern
 
270
        """
 
271
        result = True
 
272
        translator = Globster.translators[Globster.identify(pattern)]
 
273
        tpattern = '(%s)' % translator(pattern)
 
274
        try:
 
275
            re_obj = re.compile(tpattern, re.UNICODE)
 
276
            re_obj.search("") # force compile
 
277
        except errors.InvalidPattern, e:
 
278
            result = False
 
279
        return result
 
280
 
 
281
 
228
282
class ExceptionGlobster(object):
229
283
    """A Globster that supports exception patterns.
230
284
    
272
326
        self._regex_patterns = []
273
327
        for pat in patterns:
274
328
            pat = normalize_pattern(pat)
275
 
            if pat.startswith(u'RE:') or u'/' in pat:
276
 
                self._add_patterns([pat], _sub_fullpath)
277
 
            elif pat.startswith(u'*.'):
278
 
                self._add_patterns([pat], _sub_extension,
279
 
                    prefix=r'(?:.*/)?(?!.*/)(?:.*\.)')
280
 
            else:
281
 
                self._add_patterns([pat], _sub_basename,
282
 
                    prefix=r'(?:.*/)?(?!.*/)')
 
329
            pat_type = Globster.identify(pat)
 
330
            self._add_patterns([pat], Globster.translators[pat_type],
 
331
                Globster.prefixes[pat_type])
283
332
 
284
333
 
285
334
_slashes = re.compile(r'[\\/]+')