~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/globbing.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

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
    # 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
 
182
203
    def __init__(self, patterns):
183
204
        self._regex_patterns = []
184
 
        path_patterns = []
185
 
        base_patterns = []
186
 
        ext_patterns = []
 
205
        pattern_lists = {
 
206
            "extension" : [],
 
207
            "basename" : [],
 
208
            "fullpath" : [],
 
209
        }
187
210
        for pat in patterns:
188
211
            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)
 
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"])
200
217
 
201
218
    def _add_patterns(self, patterns, translator, prefix=''):
202
219
        while patterns:
221
238
            # the combined pattern we sent to regex. Instead we indicate to
222
239
            # the user that an ignore file needs fixing.
223
240
            mutter('Invalid pattern found in regex: %s.', e.msg)
224
 
            e.msg = "File ~/.bazaar/ignore or .bzrignore contains errors."
 
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
225
248
            raise e
226
249
        return None
227
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
 
228
285
class ExceptionGlobster(object):
229
286
    """A Globster that supports exception patterns.
230
287
    
272
329
        self._regex_patterns = []
273
330
        for pat in patterns:
274
331
            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'(?:.*/)?(?!.*/)')
 
332
            t = Globster.identify(pat)
 
333
            self._add_patterns([pat], Globster.pattern_info[t]["translator"],
 
334
                Globster.pattern_info[t]["prefix"])
283
335
 
284
336
 
285
337
_slashes = re.compile(r'[\\/]+')