177
179
so are matched first, then the basename patterns, then the fullpath
185
# We want to _add_patterns in a specific order (as per type_list below)
186
# starting with the shortest and going to the longest.
187
# As some Python version don't support ordered dicts the list below is
188
# used to select inputs for _add_pattern in a specific order.
189
type_list = [ TYPE_EXTENSION, TYPE_BASENAME, TYPE_FULLPATH ]
193
TYPE_FULLPATH : _sub_fullpath,
194
TYPE_BASENAME : _sub_basename,
195
TYPE_EXTENSION : _sub_extension,
198
# Prefixes used to combine various patterns.
199
# See: Globster._add_patterns
202
TYPE_BASENAME : r'(?:.*/)?(?!.*/)',
203
TYPE_EXTENSION : r'(?:.*/)?(?!.*/)(?:.*\.)',
180
206
def __init__(self, patterns):
181
207
self._regex_patterns = []
209
Globster.TYPE_FULLPATH : [],
210
Globster.TYPE_EXTENSION : [],
211
Globster.TYPE_BASENAME : [],
185
213
for pat in patterns:
186
214
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)
215
pattern_lists[Globster.identify(pat)].append(pat)
216
for t in Globster.type_list:
217
self._add_patterns(pattern_lists[t], Globster.translators[t],
218
Globster.prefixes[t])
199
220
def _add_patterns(self, patterns, translator, prefix=''):
210
231
: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]
234
for regex, patterns in self._regex_patterns:
235
match = regex.match(filename)
237
return patterns[match.lastindex -1]
238
except errors.InvalidPattern, e:
239
# We can't show the default e.msg to the user as thats for
240
# the combined pattern we sent to regex. Instead we indicate to
241
# the user that an ignore file needs fixing.
242
mutter('Invalid pattern found in regex: %s.', e.msg)
243
e.msg = "File ~/.bazaar/ignore or .bzrignore contains error(s)."
245
for _, patterns in self._regex_patterns:
247
if not Globster.is_pattern_valid(p):
248
bad_patterns += ('\n %s' % p)
249
e.msg += bad_patterns
254
def identify(pattern):
255
"""Returns pattern category.
257
:param pattern: normalized pattern.
258
Identify if a pattern is fullpath, basename or extension
259
and returns the appropriate type.
261
if pattern.startswith(u'RE:') or u'/' in pattern:
262
return Globster.TYPE_FULLPATH
263
elif pattern.startswith(u'*.'):
264
return Globster.TYPE_EXTENSION
266
return Globster.TYPE_BASENAME
269
def is_pattern_valid(pattern):
270
"""Returns True if pattern is valid.
272
:param pattern: Normalized pattern.
273
is_pattern_valid() assumes pattern to be normalized.
274
see: globbing.normalize_pattern
277
translator = Globster.translators[Globster.identify(pattern)]
278
tpattern = '(%s)' % translator(pattern)
280
re_obj = re.compile(tpattern, re.UNICODE)
281
re_obj.search("") # force compile
282
except errors.InvalidPattern, e:
218
287
class ExceptionGlobster(object):
219
288
"""A Globster that supports exception patterns.
262
331
self._regex_patterns = []
263
332
for pat in patterns:
264
333
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'(?:.*/)?(?!.*/)')
334
pat_type = Globster.identify(pat)
335
self._add_patterns([pat], Globster.translators[pat_type],
336
Globster.prefixes[pat_type])
275
339
_slashes = re.compile(r'[\\/]+')