Mercurial > public > mercurial-scm > hg
diff mercurial/revsetlang.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | ddb174511f1b |
children | 687b865b95ad |
line wrap: on
line diff
--- a/mercurial/revsetlang.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/revsetlang.py Sun Oct 06 09:45:02 2019 -0400 @@ -18,9 +18,7 @@ smartset, util, ) -from .utils import ( - stringutil, -) +from .utils import stringutil elements = { # token-type: binding-strength, primary, prefix, infix, suffix @@ -31,10 +29,20 @@ "~": (18, None, None, ("ancestor", 18), None), "^": (18, None, None, ("parent", 18), "parentpost"), "-": (5, None, ("negate", 19), ("minus", 5), None), - "::": (17, "dagrangeall", ("dagrangepre", 17), ("dagrange", 17), - "dagrangepost"), - "..": (17, "dagrangeall", ("dagrangepre", 17), ("dagrange", 17), - "dagrangepost"), + "::": ( + 17, + "dagrangeall", + ("dagrangepre", 17), + ("dagrange", 17), + "dagrangepost", + ), + "..": ( + 17, + "dagrangeall", + ("dagrangepre", 17), + ("dagrange", 17), + "dagrangepost", + ), ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"), "not": (10, None, ("not", 10), None, None), "!": (10, None, ("not", 10), None, None), @@ -61,14 +69,18 @@ _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%")) # default set of valid characters for the initial letter of symbols -_syminitletters = set(pycompat.iterbytestr( - pycompat.sysbytes(string.ascii_letters) + - pycompat.sysbytes(string.digits) + - '._@')) | set(map(pycompat.bytechr, pycompat.xrange(128, 256))) +_syminitletters = set( + pycompat.iterbytestr( + pycompat.sysbytes(string.ascii_letters) + + pycompat.sysbytes(string.digits) + + '._@' + ) +) | set(map(pycompat.bytechr, pycompat.xrange(128, 256))) # default set of valid characters for non-initial letters of symbols _symletters = _syminitletters | set(pycompat.iterbytestr('-/')) + def tokenize(program, lookup=None, syminitletters=None, symletters=None): ''' Parse a revset statement into a stream of tokens @@ -91,8 +103,9 @@ ''' if not isinstance(program, bytes): - raise error.ProgrammingError('revset statement must be bytes, got %r' - % program) + raise error.ProgrammingError( + 'revset statement must be bytes, got %r' % program + ) program = pycompat.bytestr(program) if syminitletters is None: syminitletters = _syminitletters @@ -117,21 +130,30 @@ pos, l = 0, len(program) while pos < l: c = program[pos] - if c.isspace(): # skip inter-token whitespace + if c.isspace(): # skip inter-token whitespace pass - elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully + elif ( + c == ':' and program[pos : pos + 2] == '::' + ): # look ahead carefully yield ('::', None, pos) - pos += 1 # skip ahead - elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully + pos += 1 # skip ahead + elif ( + c == '.' and program[pos : pos + 2] == '..' + ): # look ahead carefully yield ('..', None, pos) - pos += 1 # skip ahead - elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully + pos += 1 # skip ahead + elif ( + c == '#' and program[pos : pos + 2] == '##' + ): # look ahead carefully yield ('##', None, pos) - pos += 1 # skip ahead - elif c in _simpleopletters: # handle simple operators + pos += 1 # skip ahead + elif c in _simpleopletters: # handle simple operators yield (c, None, pos) - elif (c in _quoteletters or c == 'r' and - program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings + elif ( + c in _quoteletters + or c == 'r' + and program[pos : pos + 2] in ("r'", 'r"') + ): # handle quoted strings if c == 'r': pos += 1 c = program[pos] @@ -140,9 +162,9 @@ decode = parser.unescapestr pos += 1 s = pos - while pos < l: # find closing quote + while pos < l: # find closing quote d = program[pos] - if d == '\\': # skip over escaped characters + if d == '\\': # skip over escaped characters pos += 2 continue if d == c: @@ -155,16 +177,16 @@ elif c in syminitletters: s = pos pos += 1 - while pos < l: # find end of symbol + while pos < l: # find end of symbol d = program[pos] if d not in symletters: break - if d == '.' and program[pos - 1] == '.': # special case for .. + if d == '.' and program[pos - 1] == '.': # special case for .. pos -= 1 break pos += 1 sym = program[s:pos] - if sym in keywords: # operator keywords + if sym in keywords: # operator keywords yield (sym, None, s) elif '-' in sym: # some jerk gave us foo-bar-baz, try to check if it's a symbol @@ -175,36 +197,41 @@ # looks like an expression parts = sym.split('-') for p in parts[:-1]: - if p: # possible consecutive - + if p: # possible consecutive - yield ('symbol', p, s) s += len(p) yield ('-', None, s) s += 1 - if parts[-1]: # possible trailing - + if parts[-1]: # possible trailing - yield ('symbol', parts[-1], s) else: yield ('symbol', sym, s) pos -= 1 else: - raise error.ParseError(_("syntax error in revset '%s'") % - program, pos) + raise error.ParseError( + _("syntax error in revset '%s'") % program, pos + ) pos += 1 yield ('end', None, pos) + # helpers _notset = object() + def getsymbol(x): if x and x[0] == 'symbol': return x[1] raise error.ParseError(_('not a symbol')) + def getstring(x, err): if x and (x[0] == 'string' or x[0] == 'symbol'): return x[1] raise error.ParseError(err) + def getinteger(x, err, default=_notset): if not x and default is not _notset: return default @@ -213,12 +240,14 @@ except ValueError: raise error.ParseError(err) + def getboolean(x, err): value = stringutil.parsebool(getsymbol(x)) if value is not None: return value raise error.ParseError(err) + def getlist(x): if not x: return [] @@ -226,6 +255,7 @@ return list(x[1:]) return [x] + def getrange(x, err): if not x: raise error.ParseError(err) @@ -240,6 +270,7 @@ return None, None raise error.ParseError(err) + def getintrange(x, err1, err2, deffirst=_notset, deflast=_notset): """Get [first, last] integer range (both inclusive) from a parsed tree @@ -252,19 +283,28 @@ a, b = getrange(x, err1) return getinteger(a, err2, deffirst), getinteger(b, err2, deflast) + def getargs(x, min, max, err): l = getlist(x) if len(l) < min or (max >= 0 and len(l) > max): raise error.ParseError(err) return l + def getargsdict(x, funcname, keys): - return parser.buildargsdict(getlist(x), funcname, parser.splitargspec(keys), - keyvaluenode='keyvalue', keynode='symbol') + return parser.buildargsdict( + getlist(x), + funcname, + parser.splitargspec(keys), + keyvaluenode='keyvalue', + keynode='symbol', + ) + # cache of {spec: raw parsed tree} built internally _treecache = {} + def _cachedtree(spec): # thread safe because parse() is reentrant and dict.__setitem__() is atomic tree = _treecache.get(spec) @@ -272,6 +312,7 @@ _treecache[spec] = tree = parse(spec) return tree + def _build(tmplspec, *repls): """Create raw parsed tree from a template revset statement @@ -281,6 +322,7 @@ template = _cachedtree(tmplspec) return parser.buildtree(template, ('symbol', '_'), *repls) + def _match(patspec, tree): """Test if a tree matches the given pattern statement; return the matches @@ -290,12 +332,15 @@ >>> _match(b'f(_)', parse(b'f(1, 2)')) """ pattern = _cachedtree(patspec) - return parser.matchtree(pattern, tree, ('symbol', '_'), - {'keyvalue', 'list'}) + return parser.matchtree( + pattern, tree, ('symbol', '_'), {'keyvalue', 'list'} + ) + def _matchonly(revs, bases): return _match('ancestors(_) and not ancestors(_)', ('and', revs, bases)) + def _fixops(x): """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be handled well by our simple top-down parser""" @@ -325,6 +370,7 @@ return (op,) + tuple(_fixops(y) for y in x[1:]) + def _analyze(x): if x is None: return x @@ -353,8 +399,15 @@ return (op, _analyze(x[1])) elif op == 'group': return _analyze(x[1]) - elif op in {'and', 'dagrange', 'range', 'parent', 'ancestor', 'relation', - 'subscript'}: + elif op in { + 'and', + 'dagrange', + 'range', + 'parent', + 'ancestor', + 'relation', + 'subscript', + }: ta = _analyze(x[1]) tb = _analyze(x[2]) return (op, ta, tb) @@ -371,6 +424,7 @@ return (op, x[1], _analyze(x[2])) raise ValueError('invalid operator %r' % op) + def analyze(x): """Transform raw parsed tree to evaluatable tree which can be fed to optimize() or getset() @@ -380,13 +434,14 @@ """ return _analyze(x) + def _optimize(x): if x is None: return 0, x op = x[0] if op in ('string', 'symbol', 'smartset'): - return 0.5, x # single revisions are small + return 0.5, x # single revisions are small elif op == 'and': wa, ta = _optimize(x[1]) wb, tb = _optimize(x[2]) @@ -412,6 +467,7 @@ # fast path for machine-generated expression, that is likely to have # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()' ws, ts, ss = [], [], [] + def flushss(): if not ss: return @@ -424,6 +480,7 @@ ws.append(w) ts.append(t) del ss[:] + for y in getlist(x[1]): w, t = _optimize(y) if t is not None and (t[0] == 'string' or t[0] == 'symbol'): @@ -434,7 +491,7 @@ ts.append(t) flushss() if len(ts) == 1: - return ws[0], ts[0] # 'or' operation is fully optimized out + return ws[0], ts[0] # 'or' operation is fully optimized out return max(ws), (op, ('list',) + tuple(ts)) elif op == 'not': # Optimize not public() to _notpublic() because we have a fast version @@ -478,6 +535,7 @@ return w + wa, (op, x[1], ta) raise ValueError('invalid operator %r' % op) + def optimize(tree): """Optimize evaluatable tree @@ -486,10 +544,12 @@ _weight, newtree = _optimize(tree) return newtree + # the set of valid characters for the initial letter of symbols in # alias declarations and definitions _aliassyminitletters = _syminitletters | {'$'} + def _parsewith(spec, lookup=None, syminitletters=None): """Generate a parse tree of given spec with given tokenizing options @@ -507,14 +567,17 @@ if lookup and spec.startswith('revset(') and spec.endswith(')'): lookup = None p = parser.parser(elements) - tree, pos = p.parse(tokenize(spec, lookup=lookup, - syminitletters=syminitletters)) + tree, pos = p.parse( + tokenize(spec, lookup=lookup, syminitletters=syminitletters) + ) if pos != len(spec): raise error.ParseError(_('invalid token'), pos) return _fixops(parser.simplifyinfixops(tree, ('list', 'or'))) + class _aliasrules(parser.basealiasrules): """Parsing and expansion rule set of revset aliases""" + _section = _('revset alias') @staticmethod @@ -532,6 +595,7 @@ if tree[0] == 'func' and tree[1][0] == 'symbol': return tree[1][1], getlist(tree[2]) + def expandaliases(tree, aliases, warn=None): """Expand aliases in a tree, aliases is a list of (name, value) tuples""" aliases = _aliasrules.buildmap(aliases) @@ -544,11 +608,15 @@ alias.warned = True return tree + def foldconcat(tree): """Fold elements to be concatenated by `##` """ - if (not isinstance(tree, tuple) - or tree[0] in ('string', 'symbol', 'smartset')): + if not isinstance(tree, tuple) or tree[0] in ( + 'string', + 'symbol', + 'smartset', + ): return tree if tree[0] == '_concat': pending = [tree] @@ -566,6 +634,7 @@ else: return tuple(foldconcat(t) for t in tree) + def parse(spec, lookup=None): try: return _parsewith(spec, lookup=lookup) @@ -581,6 +650,7 @@ inst.hint = spec + '\n' + ' ' * (loc + 1) + '^ ' + _('here') raise + def _quote(s): r"""Quote a value in order to make it safe for the revset engine. @@ -595,6 +665,7 @@ """ return "'%s'" % stringutil.escapestr(pycompat.bytestr(s)) + def _formatargtype(c, arg): if c == 'd': return '_rev(%d)' % int(arg) @@ -603,7 +674,7 @@ elif c == 'r': if not isinstance(arg, bytes): raise TypeError - parse(arg) # make sure syntax errors are confined + parse(arg) # make sure syntax errors are confined return '(%s)' % arg elif c == 'n': return _quote(node.hex(arg)) @@ -614,6 +685,7 @@ raise TypeError raise error.ParseError(_('unexpected revspec format character %s') % c) + def _formatlistexp(s, t): l = len(s) if l == 0: @@ -635,6 +707,7 @@ m = l // 2 return '(%s or %s)' % (_formatlistexp(s[:m], t), _formatlistexp(s[m:], t)) + def _formatintlist(data): try: l = len(data) @@ -646,14 +719,17 @@ except (TypeError, ValueError): raise error.ParseError(_('invalid argument for revspec')) + def _formatparamexp(args, t): return ', '.join(_formatargtype(t, a) for a in args) + _formatlistfuncs = { 'l': _formatlistexp, 'p': _formatparamexp, } + def formatspec(expr, *args): ''' This is a convenience function for using revsets internally, and @@ -704,6 +780,7 @@ raise error.ProgrammingError("unknown revspec item type: %r" % t) return b''.join(ret) + def spectree(expr, *args): """similar to formatspec but return a parsed and optimized tree""" parsed = _parseargs(expr, args) @@ -726,6 +803,7 @@ tree = optimize(tree) return tree + def _parseargs(expr, args): """parse the expression and replace all inexpensive args @@ -763,7 +841,7 @@ if f: # a list of some type, might be expensive, do not replace pos += 1 - islist = (d == 'l') + islist = d == 'l' try: d = expr[pos] except IndexError: @@ -794,15 +872,18 @@ pass return ret + def prettyformat(tree): return parser.prettyformat(tree, ('string', 'symbol')) + def depth(tree): if isinstance(tree, tuple): return max(map(depth, tree)) + 1 else: return 0 + def funcsused(tree): if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'): return set() @@ -814,12 +895,15 @@ funcs.add(tree[1][1]) return funcs + _hashre = util.re.compile('[0-9a-fA-F]{1,40}$') + def _ishashlikesymbol(symbol): """returns true if the symbol looks like a hash""" return _hashre.match(symbol) + def gethashlikesymbols(tree): """returns the list of symbols of the tree that look like hashes