##// END OF EJS Templates
match: remove head and tail args from _globre
match: remove head and tail args from _globre

File last commit:

r8582:a4c199e1 default
r8582:a4c199e1 default
Show More
match.py
251 lines | 7.6 KiB | text/x-python | PythonLexer
Martin Geisler
match: add copyright and license header
r8231 # match.py - file name matching
#
# Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
Matt Mackall
match: move util match functions over
r8570 import util, re
Matt Mackall
walk: introduce match objects
r6576
Matt Mackall
match: cleanup match classes a bit
r6604 class _match(object):
def __init__(self, root, cwd, files, mf, ap):
Matt Mackall
walk: introduce match objects
r6576 self._root = root
self._cwd = cwd
Matt Mackall
match: cleanup match classes a bit
r6604 self._files = files
Martin Geisler
replace set-like dictionaries with real sets...
r8152 self._fmap = set(files)
Matt Mackall
dirstate.walk: speed up calling match function
r6834 self.matchfn = mf
Matt Mackall
walk: introduce match objects
r6576 self._anypats = ap
def __call__(self, fn):
Matt Mackall
dirstate.walk: speed up calling match function
r6834 return self.matchfn(fn)
Matt Mackall
walk: introduce match objects
r6576 def __iter__(self):
for f in self._files:
yield f
def bad(self, f, msg):
return True
def dir(self, f):
pass
def missing(self, f):
pass
def exact(self, f):
return f in self._fmap
def rel(self, f):
return util.pathto(self._root, self._cwd, f)
def files(self):
return self._files
def anypats(self):
return self._anypats
Matt Mackall
match: add always, never, and exact methods
r6596
Matt Mackall
match: cleanup match classes a bit
r6604 class always(_match):
def __init__(self, root, cwd):
_match.__init__(self, root, cwd, [], lambda f: True, False)
class never(_match):
def __init__(self, root, cwd):
_match.__init__(self, root, cwd, [], lambda f: False, False)
Matt Mackall
match: add always, never, and exact methods
r6596
Matt Mackall
match: cleanup match classes a bit
r6604 class exact(_match):
def __init__(self, root, cwd, files):
Simon Heimberg
match: use self.exact instead of lambda...
r8522 _match.__init__(self, root, cwd, files, self.exact, False)
Matt Mackall
match: add always, never, and exact methods
r6596
Matt Mackall
match: cleanup match classes a bit
r6604 class match(_match):
Matt Mackall
match: add some default args
r8567 def __init__(self, root, cwd, patterns, include=[], exclude=[],
default='glob'):
Matt Mackall
match: fold _matcher into match.__init__
r8581 """build an object to match a set of file patterns
arguments:
root - the canonical root of the tree you're matching against
cwd - the current working directory, if relevant
patterns - patterns to find
include - patterns to include
exclude - patterns to exclude
default - if a pattern in names has no explicit type, assume this one
a pattern is one of:
'glob:<glob>' - a glob relative to cwd
're:<regexp>' - a regular expression
'path:<path>' - a path relative to canonroot
'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
'relpath:<path>' - a path relative to cwd
'relre:<regexp>' - a regexp that doesn't have to match the start of a name
'<something>' - one of the cases above, selected by the dflt_pat argument
"""
roots = []
anypats = bool(include or exclude)
if patterns:
pats = _normalize(patterns, default, root, cwd)
roots = _roots(pats)
anypats = anypats or _anypats(pats)
pm = _buildmatch(pats, '$')
if include:
im = _buildmatch(_normalize(include, 'glob', root, cwd), '(?:/|$)')
if exclude:
em = _buildmatch(_normalize(exclude, 'glob', root, cwd), '(?:/|$)')
if patterns:
if include:
if exclude:
m = lambda f: im(f) and not em(f) and pm(f)
else:
m = lambda f: im(f) and pm(f)
else:
if exclude:
m = lambda f: not em(f) and pm(f)
else:
m = pm
else:
if include:
if exclude:
m = lambda f: im(f) and not em(f)
else:
m = im
else:
if exclude:
m = lambda f: not em(f)
else:
m = lambda f: True
_match.__init__(self, root, cwd, roots, m, anypats)
Matt Mackall
match: refactor patkind...
r8568
def patkind(pat):
Matt Mackall
match: move util match functions over
r8570 return _patsplit(pat, None)[0]
def _patsplit(pat, default):
"""Split a string into an optional pattern kind prefix and the
actual pattern."""
Matt Mackall
match: optimize _patsplit
r8579 if ':' in pat:
pat, val = pat.split(':', 1)
if pat in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre'):
return pat, val
Matt Mackall
match: move util match functions over
r8570 return default, pat
Matt Mackall
match: remove head and tail args from _globre
r8582 def _globre(pat):
Matt Mackall
match: move util match functions over
r8570 "convert a glob pattern into a regexp"
i, n = 0, len(pat)
res = ''
group = 0
def peek(): return i < n and pat[i]
while i < n:
c = pat[i]
i = i+1
if c == '*':
if peek() == '*':
i += 1
res += '.*'
else:
res += '[^/]*'
elif c == '?':
res += '.'
elif c == '[':
j = i
if j < n and pat[j] in '!]':
j += 1
while j < n and pat[j] != ']':
j += 1
if j >= n:
res += '\\['
else:
stuff = pat[i:j].replace('\\','\\\\')
i = j + 1
if stuff[0] == '!':
stuff = '^' + stuff[1:]
elif stuff[0] == '^':
stuff = '\\' + stuff
res = '%s[%s]' % (res, stuff)
elif c == '{':
group += 1
res += '(?:'
elif c == '}' and group:
res += ')'
group -= 1
elif c == ',' and group:
res += '|'
elif c == '\\':
p = peek()
if p:
i += 1
res += re.escape(p)
else:
res += re.escape(c)
else:
res += re.escape(c)
Matt Mackall
match: remove head and tail args from _globre
r8582 return res
Matt Mackall
match: move util match functions over
r8570
Matt Mackall
match: unnest functions in _matcher
r8574 def _regex(kind, name, tail):
'''convert a pattern into a regular expression'''
if not name:
return ''
if kind == 're':
return name
elif kind == 'path':
return '^' + re.escape(name) + '(?:/|$)'
elif kind == 'relglob':
Matt Mackall
match: remove head and tail args from _globre
r8582 return '(?:|.*/)' + _globre(name) + tail
Matt Mackall
match: unnest functions in _matcher
r8574 elif kind == 'relpath':
return re.escape(name) + '(?:/|$)'
elif kind == 'relre':
if name.startswith('^'):
return name
return '.*' + name
Matt Mackall
match: remove head and tail args from _globre
r8582 return _globre(name) + tail
Matt Mackall
match: unnest functions in _matcher
r8574
Matt Mackall
match: rename _matchfn to _buildmatch
r8580 def _buildmatch(pats, tail):
Matt Mackall
match: unnest functions in _matcher
r8574 """build a matching function from a set of patterns"""
try:
pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats])
if len(pat) > 20000:
raise OverflowError()
return re.compile(pat).match
except OverflowError:
# We're using a Python with a tiny regex engine and we
# made it explode, so we'll divide the pattern list in two
# until it works
l = len(pats)
if l < 2:
raise
Matt Mackall
match: rename _matchfn to _buildmatch
r8580 a, b = _buildmatch(pats[:l//2], tail), _buildmatch(pats[l//2:], tail)
Matt Mackall
match: unnest functions in _matcher
r8574 return lambda s: a(s) or b(s)
except re.error:
for k, p in pats:
try:
re.compile('(?:%s)' % _regex(k, p, tail))
except re.error:
raise util.Abort("invalid pattern (%s): %s" % (k, p))
raise util.Abort("invalid pattern")
def _globprefix(pat):
'''return the non-glob prefix of a path, e.g. foo/* -> foo'''
root = []
for p in pat.split('/'):
Matt Mackall
match: optimize _globprefix
r8575 if '[' in p or '{' in p or '*' in p or '?' in p:
break
Matt Mackall
match: unnest functions in _matcher
r8574 root.append(p)
return '/'.join(root) or '.'
Matt Mackall
match: tweak some names
r8578 def _normalize(names, default, root, cwd):
Matt Mackall
match: unnest functions in _matcher
r8574 pats = []
for kind, name in [_patsplit(p, default) for p in names]:
if kind in ('glob', 'relpath'):
Matt Mackall
match: tweak some names
r8578 name = util.canonpath(root, cwd, name)
Matt Mackall
match: unnest functions in _matcher
r8574 elif kind in ('relglob', 'path'):
name = util.normpath(name)
pats.append((kind, name))
Matt Mackall
match: split up _normalizepats
r8576 return pats
Matt Mackall
match: unnest functions in _matcher
r8574
Matt Mackall
match: split up _normalizepats
r8576 def _roots(patterns):
r = []
for kind, name in patterns:
Matt Mackall
match: unnest functions in _matcher
r8574 if kind == 'glob':
Matt Mackall
match: split up _normalizepats
r8576 r.append(_globprefix(name))
Matt Mackall
match: unnest functions in _matcher
r8574 elif kind in ('relpath', 'path'):
Matt Mackall
match: split up _normalizepats
r8576 r.append(name or '.')
Matt Mackall
match: unnest functions in _matcher
r8574 elif kind == 'relglob':
Matt Mackall
match: split up _normalizepats
r8576 r.append('.')
return r
def _anypats(patterns):
for kind, name in patterns:
if kind in ('glob', 're', 'relglob', 'relre'):
return True