[PATCH 6 of 6] fileset: extract language processing part to new module (API)
Yuya Nishihara
yuya at tcha.org
Thu Aug 2 09:26:06 EDT 2018
# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1532242245 -32400
# Sun Jul 22 15:50:45 2018 +0900
# Node ID e6e56b590c20c3a026a304f569e4e1edfd3b1b6a
# Parent 7b744a18768440cdca7662db7dc6179c1abd6613
fileset: extract language processing part to new module (API)
I'll add a couple more functions that work on parsed tree.
% wc -l mercurial/fileset*.py
559 mercurial/fileset.py
135 mercurial/filesetlang.py
694 total
diff --git a/hgext/lfs/__init__.py b/hgext/lfs/__init__.py
--- a/hgext/lfs/__init__.py
+++ b/hgext/lfs/__init__.py
@@ -136,7 +136,7 @@ from mercurial import (
exchange,
extensions,
filelog,
- fileset,
+ filesetlang,
hg,
localrepo,
minifileset,
@@ -261,7 +261,7 @@ def _trackedmatcher(repo):
# deprecated config: lfs.threshold
threshold = repo.ui.configbytes('lfs', 'threshold')
if threshold:
- fileset.parse(trackspec) # make sure syntax errors are confined
+ filesetlang.parse(trackspec) # make sure syntax errors are confined
trackspec = "(%s) | size('>%d')" % (trackspec, threshold)
return minifileset.compile(trackspec)
@@ -361,7 +361,7 @@ def extsetup(ui):
def lfsfileset(mctx, x):
"""File that uses LFS storage."""
# i18n: "lfs" is a keyword
- fileset.getargs(x, 0, 0, _("lfs takes no arguments"))
+ filesetlang.getargs(x, 0, 0, _("lfs takes no arguments"))
ctx = mctx.ctx
def lfsfilep(f):
return wrapper.pointerfromctx(ctx, f, removed=True) is not None
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -48,7 +48,7 @@ from . import (
exchange,
extensions,
filemerge,
- fileset,
+ filesetlang,
formatter,
hg,
httppeer,
@@ -916,13 +916,13 @@ def debugfileset(ui, repo, expr, **opts)
raise error.Abort(_('invalid stage name: %s') % n)
showalways.update(opts['show_stage'])
- tree = fileset.parse(expr)
+ tree = filesetlang.parse(expr)
for n, f in stages:
tree = f(tree)
if n in showalways:
if opts['show_stage'] or n != 'parsed':
ui.write(("* %s:\n") % n)
- ui.write(fileset.prettyformat(tree), "\n")
+ ui.write(filesetlang.prettyformat(tree), "\n")
files = set()
if opts['all_files']:
diff --git a/mercurial/fileset.py b/mercurial/fileset.py
--- a/mercurial/fileset.py
+++ b/mercurial/fileset.py
@@ -13,9 +13,9 @@ import re
from .i18n import _
from . import (
error,
+ filesetlang,
match as matchmod,
merge,
- parser,
pycompat,
registrar,
scmutil,
@@ -25,120 +25,12 @@ from .utils import (
stringutil,
)
-elements = {
- # token-type: binding-strength, primary, prefix, infix, suffix
- "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
- ":": (15, None, None, ("kindpat", 15), None),
- "-": (5, None, ("negate", 19), ("minus", 5), None),
- "not": (10, None, ("not", 10), None, None),
- "!": (10, None, ("not", 10), None, None),
- "and": (5, None, None, ("and", 5), None),
- "&": (5, None, None, ("and", 5), None),
- "or": (4, None, None, ("or", 4), None),
- "|": (4, None, None, ("or", 4), None),
- "+": (4, None, None, ("or", 4), None),
- ",": (2, None, None, ("list", 2), None),
- ")": (0, None, None, None, None),
- "symbol": (0, "symbol", None, None, None),
- "string": (0, "string", None, None, None),
- "end": (0, None, None, None, None),
-}
-
-keywords = {'and', 'or', 'not'}
-
-globchars = ".*{}[]?/\\_"
-
-def tokenize(program):
- pos, l = 0, len(program)
- program = pycompat.bytestr(program)
- while pos < l:
- c = program[pos]
- if c.isspace(): # skip inter-token whitespace
- pass
- elif c in "(),-:|&+!": # handle simple operators
- yield (c, None, pos)
- elif (c in '"\'' or c == 'r' and
- program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
- if c == 'r':
- pos += 1
- c = program[pos]
- decode = lambda x: x
- else:
- decode = parser.unescapestr
- pos += 1
- s = pos
- while pos < l: # find closing quote
- d = program[pos]
- if d == '\\': # skip over escaped characters
- pos += 2
- continue
- if d == c:
- yield ('string', decode(program[s:pos]), s)
- break
- pos += 1
- else:
- raise error.ParseError(_("unterminated string"), s)
- elif c.isalnum() or c in globchars or ord(c) > 127:
- # gather up a symbol/keyword
- s = pos
- pos += 1
- while pos < l: # find end of symbol
- d = program[pos]
- if not (d.isalnum() or d in globchars or ord(d) > 127):
- break
- pos += 1
- sym = program[s:pos]
- if sym in keywords: # operator keywords
- yield (sym, None, s)
- else:
- yield ('symbol', sym, s)
- pos -= 1
- else:
- raise error.ParseError(_("syntax error"), pos)
- pos += 1
- yield ('end', None, pos)
-
-def parse(expr):
- p = parser.parser(elements)
- tree, pos = p.parse(tokenize(expr))
- if pos != len(expr):
- raise error.ParseError(_("invalid token"), pos)
- return parser.simplifyinfixops(tree, {'list', 'or'})
-
-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 _getkindpat(x, y, allkinds, err):
- kind = getsymbol(x)
- pat = getstring(y, err)
- if kind not in allkinds:
- raise error.ParseError(_("invalid pattern kind: %s") % kind)
- return '%s:%s' % (kind, pat)
-
-def getpattern(x, allkinds, err):
- if x and x[0] == 'kindpat':
- return _getkindpat(x[1], x[2], allkinds, err)
- return getstring(x, err)
-
-def getlist(x):
- if not x:
- return []
- if x[0] == 'list':
- return list(x[1:])
- return [x]
-
-def getargs(x, min, max, err):
- l = getlist(x)
- if len(l) < min or len(l) > max:
- raise error.ParseError(err)
- return l
+# helpers for processing parsed tree
+getsymbol = filesetlang.getsymbol
+getstring = filesetlang.getstring
+_getkindpat = filesetlang.getkindpat
+getpattern = filesetlang.getpattern
+getargs = filesetlang.getargs
def getmatch(mctx, x):
if not x:
@@ -192,7 +84,7 @@ def func(mctx, a, b):
# with:
# mctx - current matchctx instance
# x - argument in tree form
-symbols = {}
+symbols = filesetlang.symbols
# filesets using matchctx.status()
_statuscallers = set()
@@ -635,7 +527,7 @@ def _intree(funcs, tree):
def match(ctx, expr, badfn=None):
"""Create a matcher for a single fileset expression"""
- tree = parse(expr)
+ tree = filesetlang.parse(expr)
mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
return getmatch(mctx, tree)
@@ -653,9 +545,6 @@ def _buildstatus(ctx, tree, basectx=None
else:
return None
-def prettyformat(tree):
- return parser.prettyformat(tree, ('string', 'symbol'))
-
def loadpredicate(ui, extname, registrarobj):
"""Load fileset predicates from specified registrarobj
"""
diff --git a/mercurial/fileset.py b/mercurial/filesetlang.py
copy from mercurial/fileset.py
copy to mercurial/filesetlang.py
--- a/mercurial/fileset.py
+++ b/mercurial/filesetlang.py
@@ -1,4 +1,4 @@
-# fileset.py - file set queries for mercurial
+# filesetlang.py - parser, tokenizer and utility for file set language
#
# Copyright 2010 Matt Mackall <mpm at selenic.com>
#
@@ -7,22 +7,11 @@
from __future__ import absolute_import
-import errno
-import re
-
from .i18n import _
from . import (
error,
- match as matchmod,
- merge,
parser,
pycompat,
- registrar,
- scmutil,
- util,
-)
-from .utils import (
- stringutil,
)
elements = {
@@ -46,6 +35,8 @@ elements = {
keywords = {'and', 'or', 'not'}
+symbols = {}
+
globchars = ".*{}[]?/\\_"
def tokenize(program):
@@ -115,7 +106,7 @@ def getstring(x, err):
return x[1]
raise error.ParseError(err)
-def _getkindpat(x, y, allkinds, err):
+def getkindpat(x, y, allkinds, err):
kind = getsymbol(x)
pat = getstring(y, err)
if kind not in allkinds:
@@ -124,7 +115,7 @@ def _getkindpat(x, y, allkinds, err):
def getpattern(x, allkinds, err):
if x and x[0] == 'kindpat':
- return _getkindpat(x[1], x[2], allkinds, err)
+ return getkindpat(x[1], x[2], allkinds, err)
return getstring(x, err)
def getlist(x):
@@ -140,532 +131,5 @@ def getargs(x, min, max, err):
raise error.ParseError(err)
return l
-def getmatch(mctx, x):
- if not x:
- raise error.ParseError(_("missing argument"))
- return methods[x[0]](mctx, *x[1:])
-
-def stringmatch(mctx, x):
- return mctx.matcher([x])
-
-def kindpatmatch(mctx, x, y):
- return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
- _("pattern must be a string")))
-
-def andmatch(mctx, x, y):
- xm = getmatch(mctx, x)
- ym = getmatch(mctx, y)
- return matchmod.intersectmatchers(xm, ym)
-
-def ormatch(mctx, *xs):
- ms = [getmatch(mctx, x) for x in xs]
- return matchmod.unionmatcher(ms)
-
-def notmatch(mctx, x):
- m = getmatch(mctx, x)
- return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
-
-def minusmatch(mctx, x, y):
- xm = getmatch(mctx, x)
- ym = getmatch(mctx, y)
- return matchmod.differencematcher(xm, ym)
-
-def negatematch(mctx, x):
- raise error.ParseError(_("can't use negate operator in this context"))
-
-def listmatch(mctx, *xs):
- raise error.ParseError(_("can't use a list in this context"),
- hint=_('see hg help "filesets.x or y"'))
-
-def func(mctx, a, b):
- funcname = getsymbol(a)
- if funcname in symbols:
- return symbols[funcname](mctx, b)
-
- keep = lambda fn: getattr(fn, '__doc__', None) is not None
-
- syms = [s for (s, fn) in symbols.items() if keep(fn)]
- raise error.UnknownIdentifier(funcname, syms)
-
-# symbols are callable like:
-# fun(mctx, x)
-# with:
-# mctx - current matchctx instance
-# x - argument in tree form
-symbols = {}
-
-# filesets using matchctx.status()
-_statuscallers = set()
-
-predicate = registrar.filesetpredicate()
-
- at predicate('modified()', callstatus=True)
-def modified(mctx, x):
- """File that is modified according to :hg:`status`.
- """
- # i18n: "modified" is a keyword
- getargs(x, 0, 0, _("modified takes no arguments"))
- s = set(mctx.status().modified)
- return mctx.predicate(s.__contains__, predrepr='modified')
-
- at predicate('added()', callstatus=True)
-def added(mctx, x):
- """File that is added according to :hg:`status`.
- """
- # i18n: "added" is a keyword
- getargs(x, 0, 0, _("added takes no arguments"))
- s = set(mctx.status().added)
- return mctx.predicate(s.__contains__, predrepr='added')
-
- at predicate('removed()', callstatus=True)
-def removed(mctx, x):
- """File that is removed according to :hg:`status`.
- """
- # i18n: "removed" is a keyword
- getargs(x, 0, 0, _("removed takes no arguments"))
- s = set(mctx.status().removed)
- return mctx.predicate(s.__contains__, predrepr='removed')
-
- at predicate('deleted()', callstatus=True)
-def deleted(mctx, x):
- """Alias for ``missing()``.
- """
- # i18n: "deleted" is a keyword
- getargs(x, 0, 0, _("deleted takes no arguments"))
- s = set(mctx.status().deleted)
- return mctx.predicate(s.__contains__, predrepr='deleted')
-
- at predicate('missing()', callstatus=True)
-def missing(mctx, x):
- """File that is missing according to :hg:`status`.
- """
- # i18n: "missing" is a keyword
- getargs(x, 0, 0, _("missing takes no arguments"))
- s = set(mctx.status().deleted)
- return mctx.predicate(s.__contains__, predrepr='deleted')
-
- at predicate('unknown()', callstatus=True)
-def unknown(mctx, x):
- """File that is unknown according to :hg:`status`."""
- # i18n: "unknown" is a keyword
- getargs(x, 0, 0, _("unknown takes no arguments"))
- s = set(mctx.status().unknown)
- return mctx.predicate(s.__contains__, predrepr='unknown')
-
- at predicate('ignored()', callstatus=True)
-def ignored(mctx, x):
- """File that is ignored according to :hg:`status`."""
- # i18n: "ignored" is a keyword
- getargs(x, 0, 0, _("ignored takes no arguments"))
- s = set(mctx.status().ignored)
- return mctx.predicate(s.__contains__, predrepr='ignored')
-
- at predicate('clean()', callstatus=True)
-def clean(mctx, x):
- """File that is clean according to :hg:`status`.
- """
- # i18n: "clean" is a keyword
- getargs(x, 0, 0, _("clean takes no arguments"))
- s = set(mctx.status().clean)
- return mctx.predicate(s.__contains__, predrepr='clean')
-
- at predicate('tracked()')
-def tracked(mctx, x):
- """File that is under Mercurial control."""
- # i18n: "tracked" is a keyword
- getargs(x, 0, 0, _("tracked takes no arguments"))
- return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
-
- at predicate('binary()')
-def binary(mctx, x):
- """File that appears to be binary (contains NUL bytes).
- """
- # i18n: "binary" is a keyword
- getargs(x, 0, 0, _("binary takes no arguments"))
- return mctx.fpredicate(lambda fctx: fctx.isbinary(),
- predrepr='binary', cache=True)
-
- at predicate('exec()')
-def exec_(mctx, x):
- """File that is marked as executable.
- """
- # i18n: "exec" is a keyword
- getargs(x, 0, 0, _("exec takes no arguments"))
- ctx = mctx.ctx
- return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
-
- at predicate('symlink()')
-def symlink(mctx, x):
- """File that is marked as a symlink.
- """
- # i18n: "symlink" is a keyword
- getargs(x, 0, 0, _("symlink takes no arguments"))
- ctx = mctx.ctx
- return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
-
- at predicate('resolved()')
-def resolved(mctx, x):
- """File that is marked resolved according to :hg:`resolve -l`.
- """
- # i18n: "resolved" is a keyword
- getargs(x, 0, 0, _("resolved takes no arguments"))
- if mctx.ctx.rev() is not None:
- return mctx.never()
- ms = merge.mergestate.read(mctx.ctx.repo())
- return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
- predrepr='resolved')
-
- at predicate('unresolved()')
-def unresolved(mctx, x):
- """File that is marked unresolved according to :hg:`resolve -l`.
- """
- # i18n: "unresolved" is a keyword
- getargs(x, 0, 0, _("unresolved takes no arguments"))
- if mctx.ctx.rev() is not None:
- return mctx.never()
- ms = merge.mergestate.read(mctx.ctx.repo())
- return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
- predrepr='unresolved')
-
- at predicate('hgignore()')
-def hgignore(mctx, x):
- """File that matches the active .hgignore pattern.
- """
- # i18n: "hgignore" is a keyword
- getargs(x, 0, 0, _("hgignore takes no arguments"))
- return mctx.ctx.repo().dirstate._ignore
-
- at predicate('portable()')
-def portable(mctx, x):
- """File that has a portable name. (This doesn't include filenames with case
- collisions.)
- """
- # i18n: "portable" is a keyword
- getargs(x, 0, 0, _("portable takes no arguments"))
- return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
- predrepr='portable')
-
- at predicate('grep(regex)')
-def grep(mctx, x):
- """File contains the given regular expression.
- """
- try:
- # i18n: "grep" is a keyword
- r = re.compile(getstring(x, _("grep requires a pattern")))
- except re.error as e:
- raise error.ParseError(_('invalid match pattern: %s') %
- stringutil.forcebytestr(e))
- return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
- predrepr=('grep(%r)', r.pattern), cache=True)
-
-def _sizetomax(s):
- try:
- s = s.strip().lower()
- for k, v in util._sizeunits:
- if s.endswith(k):
- # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
- n = s[:-len(k)]
- inc = 1.0
- if "." in n:
- inc /= 10 ** len(n.split(".")[1])
- return int((float(n) + inc) * v) - 1
- # no extension, this is a precise value
- return int(s)
- except ValueError:
- raise error.ParseError(_("couldn't parse size: %s") % s)
-
-def sizematcher(expr):
- """Return a function(size) -> bool from the ``size()`` expression"""
- expr = expr.strip()
- if '-' in expr: # do we have a range?
- a, b = expr.split('-', 1)
- a = util.sizetoint(a)
- b = util.sizetoint(b)
- return lambda x: x >= a and x <= b
- elif expr.startswith("<="):
- a = util.sizetoint(expr[2:])
- return lambda x: x <= a
- elif expr.startswith("<"):
- a = util.sizetoint(expr[1:])
- return lambda x: x < a
- elif expr.startswith(">="):
- a = util.sizetoint(expr[2:])
- return lambda x: x >= a
- elif expr.startswith(">"):
- a = util.sizetoint(expr[1:])
- return lambda x: x > a
- else:
- a = util.sizetoint(expr)
- b = _sizetomax(expr)
- return lambda x: x >= a and x <= b
-
- at predicate('size(expression)')
-def size(mctx, x):
- """File size matches the given expression. Examples:
-
- - size('1k') - files from 1024 to 2047 bytes
- - size('< 20k') - files less than 20480 bytes
- - size('>= .5MB') - files at least 524288 bytes
- - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
- """
- # i18n: "size" is a keyword
- expr = getstring(x, _("size requires an expression"))
- m = sizematcher(expr)
- return mctx.fpredicate(lambda fctx: m(fctx.size()),
- predrepr=('size(%r)', expr), cache=True)
-
- at predicate('encoding(name)')
-def encoding(mctx, x):
- """File can be successfully decoded with the given character
- encoding. May not be useful for encodings other than ASCII and
- UTF-8.
- """
-
- # i18n: "encoding" is a keyword
- enc = getstring(x, _("encoding requires an encoding name"))
-
- def encp(fctx):
- d = fctx.data()
- try:
- d.decode(pycompat.sysstr(enc))
- return True
- except LookupError:
- raise error.Abort(_("unknown encoding '%s'") % enc)
- except UnicodeDecodeError:
- return False
-
- return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
-
- at predicate('eol(style)')
-def eol(mctx, x):
- """File contains newlines of the given style (dos, unix, mac). Binary
- files are excluded, files with mixed line endings match multiple
- styles.
- """
-
- # i18n: "eol" is a keyword
- enc = getstring(x, _("eol requires a style name"))
-
- def eolp(fctx):
- if fctx.isbinary():
- return False
- d = fctx.data()
- if (enc == 'dos' or enc == 'win') and '\r\n' in d:
- return True
- elif enc == 'unix' and re.search('(?<!\r)\n', d):
- return True
- elif enc == 'mac' and re.search('\r(?!\n)', d):
- return True
- return False
- return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
-
- at predicate('copied()')
-def copied(mctx, x):
- """File that is recorded as being copied.
- """
- # i18n: "copied" is a keyword
- getargs(x, 0, 0, _("copied takes no arguments"))
- def copiedp(fctx):
- p = fctx.parents()
- return p and p[0].path() != fctx.path()
- return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
-
- at predicate('revs(revs, pattern)')
-def revs(mctx, x):
- """Evaluate set in the specified revisions. If the revset match multiple
- revs, this will return file matching pattern in any of the revision.
- """
- # i18n: "revs" is a keyword
- r, x = getargs(x, 2, 2, _("revs takes two arguments"))
- # i18n: "revs" is a keyword
- revspec = getstring(r, _("first argument to revs must be a revision"))
- repo = mctx.ctx.repo()
- revs = scmutil.revrange(repo, [revspec])
-
- matchers = []
- for r in revs:
- ctx = repo[r]
- matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
- if not matchers:
- return mctx.never()
- if len(matchers) == 1:
- return matchers[0]
- return matchmod.unionmatcher(matchers)
-
- at predicate('status(base, rev, pattern)')
-def status(mctx, x):
- """Evaluate predicate using status change between ``base`` and
- ``rev``. Examples:
-
- - ``status(3, 7, added())`` - matches files added from "3" to "7"
- """
- repo = mctx.ctx.repo()
- # i18n: "status" is a keyword
- b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
- # i18n: "status" is a keyword
- baseerr = _("first argument to status must be a revision")
- baserevspec = getstring(b, baseerr)
- if not baserevspec:
- raise error.ParseError(baseerr)
- reverr = _("second argument to status must be a revision")
- revspec = getstring(r, reverr)
- if not revspec:
- raise error.ParseError(reverr)
- basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
- return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
-
- at predicate('subrepo([pattern])')
-def subrepo(mctx, x):
- """Subrepositories whose paths match the given pattern.
- """
- # i18n: "subrepo" is a keyword
- getargs(x, 0, 1, _("subrepo takes at most one argument"))
- ctx = mctx.ctx
- sstate = ctx.substate
- if x:
- pat = getpattern(x, matchmod.allpatternkinds,
- # i18n: "subrepo" is a keyword
- _("subrepo requires a pattern or no arguments"))
- fast = not matchmod.patkind(pat)
- if fast:
- def m(s):
- return (s == pat)
- else:
- m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
- return mctx.predicate(lambda f: f in sstate and m(f),
- predrepr=('subrepo(%r)', pat))
- else:
- return mctx.predicate(sstate.__contains__, predrepr='subrepo')
-
-methods = {
- 'string': stringmatch,
- 'symbol': stringmatch,
- 'kindpat': kindpatmatch,
- 'and': andmatch,
- 'or': ormatch,
- 'minus': minusmatch,
- 'negate': negatematch,
- 'list': listmatch,
- 'group': getmatch,
- 'not': notmatch,
- 'func': func,
-}
-
-class matchctx(object):
- def __init__(self, ctx, status=None, badfn=None):
- self.ctx = ctx
- self._status = status
- self._badfn = badfn
-
- def status(self):
- return self._status
-
- def matcher(self, patterns):
- return self.ctx.match(patterns, badfn=self._badfn)
-
- def predicate(self, predfn, predrepr=None, cache=False):
- """Create a matcher to select files by predfn(filename)"""
- if cache:
- predfn = util.cachefunc(predfn)
- repo = self.ctx.repo()
- return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
- predrepr=predrepr, badfn=self._badfn)
-
- def fpredicate(self, predfn, predrepr=None, cache=False):
- """Create a matcher to select files by predfn(fctx) at the current
- revision
-
- Missing files are ignored.
- """
- ctx = self.ctx
- if ctx.rev() is None:
- def fctxpredfn(f):
- try:
- fctx = ctx[f]
- except error.LookupError:
- return False
- try:
- fctx.audit()
- except error.Abort:
- return False
- try:
- return predfn(fctx)
- except (IOError, OSError) as e:
- # open()-ing a directory fails with EACCES on Windows
- if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
- errno.EISDIR):
- return False
- raise
- else:
- def fctxpredfn(f):
- try:
- fctx = ctx[f]
- except error.LookupError:
- return False
- return predfn(fctx)
- return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
-
- def never(self):
- """Create a matcher to select nothing"""
- repo = self.ctx.repo()
- return matchmod.nevermatcher(repo.root, repo.getcwd(),
- badfn=self._badfn)
-
- def switch(self, ctx, status=None):
- return matchctx(ctx, status, self._badfn)
-
-# filesets using matchctx.switch()
-_switchcallers = [
- 'revs',
- 'status',
-]
-
-def _intree(funcs, tree):
- if isinstance(tree, tuple):
- if tree[0] == 'func' and tree[1][0] == 'symbol':
- if tree[1][1] in funcs:
- return True
- if tree[1][1] in _switchcallers:
- # arguments won't be evaluated in the current context
- return False
- for s in tree[1:]:
- if _intree(funcs, s):
- return True
- return False
-
-def match(ctx, expr, badfn=None):
- """Create a matcher for a single fileset expression"""
- tree = parse(expr)
- mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
- return getmatch(mctx, tree)
-
-def _buildstatus(ctx, tree, basectx=None):
- # do we need status info?
-
- if _intree(_statuscallers, tree):
- unknown = _intree(['unknown'], tree)
- ignored = _intree(['ignored'], tree)
-
- if basectx is None:
- basectx = ctx.p1()
- return basectx.status(ctx, listunknown=unknown, listignored=ignored,
- listclean=True)
- else:
- return None
-
def prettyformat(tree):
return parser.prettyformat(tree, ('string', 'symbol'))
-
-def loadpredicate(ui, extname, registrarobj):
- """Load fileset predicates from specified registrarobj
- """
- for name, func in registrarobj._table.iteritems():
- symbols[name] = func
- if func._callstatus:
- _statuscallers.add(name)
-
-# load built-in predicates explicitly to setup _statuscallers
-loadpredicate(None, None, predicate)
-
-# tell hggettext to extract docstrings from these functions:
-i18nfunctions = symbols.values()
diff --git a/mercurial/minifileset.py b/mercurial/minifileset.py
--- a/mercurial/minifileset.py
+++ b/mercurial/minifileset.py
@@ -11,12 +11,13 @@ from .i18n import _
from . import (
error,
fileset,
+ filesetlang,
pycompat,
)
def _sizep(x):
# i18n: "size" is a keyword
- expr = fileset.getstring(x, _("size requires an expression"))
+ expr = filesetlang.getstring(x, _("size requires an expression"))
return fileset.sizematcher(expr)
def _compile(tree):
@@ -24,7 +25,7 @@ def _compile(tree):
raise error.ParseError(_("missing argument"))
op = tree[0]
if op in {'symbol', 'string', 'kindpat'}:
- name = fileset.getpattern(tree, {'path'}, _('invalid file pattern'))
+ name = filesetlang.getpattern(tree, {'path'}, _('invalid file pattern'))
if name.startswith('**'): # file extension test, ex. "**.tar.gz"
ext = name[2:]
for c in pycompat.bytestr(ext):
@@ -57,7 +58,7 @@ def _compile(tree):
'size': lambda n, s: _sizep(tree[2])(s),
}
- name = fileset.getsymbol(tree[1])
+ name = filesetlang.getsymbol(tree[1])
if name in symbols:
return symbols[name]
@@ -87,5 +88,5 @@ def compile(text):
files whose name ends with ".zip", and all files under "bin" in the repo
root except for "bin/README".
"""
- tree = fileset.parse(text)
+ tree = filesetlang.parse(text)
return _compile(tree)
More information about the Mercurial-devel
mailing list