177
184
so are matched first, then the basename patterns, then the fullpath
187
# We want to _add_patterns in a specific order (as per type_list below)
188
# starting with the shortest and going to the longest.
189
# As some Python version don't support ordered dicts the list below is
190
# used to select inputs for _add_pattern in a specific order.
191
pattern_types = [ "extension", "basename", "fullpath" ]
195
"translator" : _sub_extension,
196
"prefix" : r'(?:.*/)?(?!.*/)(?:.*\.)'
199
"translator" : _sub_basename,
200
"prefix" : r'(?:.*/)?(?!.*/)'
203
"translator" : _sub_fullpath,
180
208
def __init__(self, patterns):
181
209
self._regex_patterns = []
185
215
for pat in patterns:
186
216
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)
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)
217
pattern_lists[Globster.identify(pat)].append(pat)
218
pi = Globster.pattern_info
219
for t in Globster.pattern_types:
220
self._add_patterns(pattern_lists[t], pi[t]["translator"],
199
223
def _add_patterns(self, patterns, translator, prefix=''):
201
grouped_rules = ['(%s)' % translator(pat) for pat in patterns[:99]]
226
'(%s)' % translator(pat) for pat in patterns[:99]]
202
227
joined_rule = '%s(?:%s)$' % (prefix, '|'.join(grouped_rules))
203
self._regex_patterns.append((re.compile(joined_rule, re.UNICODE),
228
# Explicitly use lazy_compile here, because we count on its
229
# nicer error reporting.
230
self._regex_patterns.append((
231
lazy_regex.lazy_compile(joined_rule, re.UNICODE),
205
233
patterns = patterns[99:]
210
238
:return A matching pattern or None if there is no matching pattern.
212
for regex, patterns in self._regex_patterns:
213
match = regex.match(filename)
215
return patterns[match.lastindex -1]
241
for regex, patterns in self._regex_patterns:
242
match = regex.match(filename)
244
return patterns[match.lastindex -1]
245
except errors.InvalidPattern, e:
246
# We can't show the default e.msg to the user as thats for
247
# the combined pattern we sent to regex. Instead we indicate to
248
# the user that an ignore file needs fixing.
249
mutter('Invalid pattern found in regex: %s.', e.msg)
250
e.msg = "File ~/.bazaar/ignore or .bzrignore contains error(s)."
252
for _, patterns in self._regex_patterns:
254
if not Globster.is_pattern_valid(p):
255
bad_patterns += ('\n %s' % p)
256
e.msg += bad_patterns
261
def identify(pattern):
262
"""Returns pattern category.
264
:param pattern: normalized pattern.
265
Identify if a pattern is fullpath, basename or extension
266
and returns the appropriate type.
268
if pattern.startswith(u'RE:') or u'/' in pattern:
270
elif pattern.startswith(u'*.'):
276
def is_pattern_valid(pattern):
277
"""Returns True if pattern is valid.
279
:param pattern: Normalized pattern.
280
is_pattern_valid() assumes pattern to be normalized.
281
see: globbing.normalize_pattern
284
translator = Globster.pattern_info[Globster.identify(pattern)]["translator"]
285
tpattern = '(%s)' % translator(pattern)
287
re_obj = lazy_regex.lazy_compile(tpattern, re.UNICODE)
288
re_obj.search("") # force compile
289
except errors.InvalidPattern, e:
218
294
class ExceptionGlobster(object):
219
295
"""A Globster that supports exception patterns.
262
338
self._regex_patterns = []
263
339
for pat in patterns:
264
340
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'(?:.*/)?(?!.*/)(?:.*\.)')
271
self._add_patterns([pat], _sub_basename,
272
prefix=r'(?:.*/)?(?!.*/)')
275
_slashes = re.compile(r'[\\/]+')
341
t = Globster.identify(pat)
342
self._add_patterns([pat], Globster.pattern_info[t]["translator"],
343
Globster.pattern_info[t]["prefix"])
346
_slashes = lazy_regex.lazy_compile(r'[\\/]+')
276
347
def normalize_pattern(pattern):
277
348
"""Converts backslashes in path patterns to forward slashes.