D657: revset: move weight information to predicate

quark (Jun Wu) phabricator at mercurial-scm.org
Thu Sep 7 16:16:43 UTC 2017


quark created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Previously revset weight is hardcoded and cannot be modified. This patch
  moves it to predicate so newly registered revsets could define their weight
  to properly give static optimization some hint.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D657

AFFECTED FILES
  mercurial/registrar.py
  mercurial/revset.py
  mercurial/revsetlang.py

CHANGE DETAILS

diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -49,6 +49,8 @@
 
 keywords = {'and', 'or', 'not'}
 
+symbols = {}
+
 _quoteletters = {'"', "'"}
 _simpleopletters = set(pycompat.iterbytestr("()[]#:=,-|&+!~^%"))
 
@@ -441,21 +443,7 @@
     elif op == 'func':
         f = getsymbol(x[1])
         wa, ta = _optimize(x[2])
-        if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
-                 'keyword', 'outgoing', 'user', 'destination'):
-            w = 10 # slow
-        elif f in ('modifies', 'adds', 'removes'):
-            w = 30 # slower
-        elif f == "contains":
-            w = 100 # very slow
-        elif f == "ancestor":
-            w = 0.5
-        elif f in ('reverse', 'limit', 'first', 'wdir', '_intlist'):
-            w = 0
-        elif f == "sort":
-            w = 10 # assume most sorts look at changelog
-        else:
-            w = 1
+        w = getattr(symbols.get(f), '_weight', 1)
         return w + wa, (op, x[1], ta)
     raise ValueError('invalid operator %r' % op)
 
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -253,7 +253,7 @@
 #   repo - current repository instance
 #   subset - of revisions to be examined
 #   x - argument in tree form
-symbols = {}
+symbols = revsetlang.symbols
 
 # symbols which can't be used for a DoS attack for any given input
 # (e.g. those which accept regexes as plain strings shouldn't be included)
@@ -276,7 +276,7 @@
         sourceset = getset(repo, fullreposet(repo), x)
     return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
 
- at predicate('adds(pattern)', safe=True)
+ at predicate('adds(pattern)', safe=True, weight=30)
 def adds(repo, subset, x):
     """Changesets that add a file matching pattern.
 
@@ -288,7 +288,7 @@
     pat = getstring(x, _("adds requires a pattern"))
     return checkstatus(repo, subset, pat, 1)
 
- at predicate('ancestor(*changeset)', safe=True)
+ at predicate('ancestor(*changeset)', safe=True, weight=0.5)
 def ancestor(repo, subset, x):
     """A greatest common ancestor of the changesets.
 
@@ -394,7 +394,7 @@
         ps.add(r)
     return subset & ps
 
- at predicate('author(string)', safe=True)
+ at predicate('author(string)', safe=True, weight=10)
 def author(repo, subset, x):
     """Alias for ``user(string)``.
     """
@@ -462,7 +462,7 @@
     bms -= {node.nullrev}
     return subset & bms
 
- at predicate('branch(string or set)', safe=True)
+ at predicate('branch(string or set)', safe=True, weight=10)
 def branch(repo, subset, x):
     """
     All changesets belonging to the given branch or the branches of the given
@@ -595,16 +595,16 @@
     cs = _children(repo, subset, s)
     return subset & cs
 
- at predicate('closed()', safe=True)
+ at predicate('closed()', safe=True, weight=10)
 def closed(repo, subset, x):
     """Changeset is closed.
     """
     # i18n: "closed" is a keyword
     getargs(x, 0, 0, _("closed takes no arguments"))
     return subset.filter(lambda r: repo[r].closesbranch(),
                          condrepr='<branch closed>')
 
- at predicate('contains(pattern)')
+ at predicate('contains(pattern)', weight=100)
 def contains(repo, subset, x):
     """The revision's manifest contains a file matching pattern (but might not
     modify it). See :hg:`help patterns` for information about file patterns.
@@ -654,7 +654,7 @@
     return subset.filter(lambda r: _matchvalue(r),
                          condrepr=('<converted %r>', rev))
 
- at predicate('date(interval)', safe=True)
+ at predicate('date(interval)', safe=True, weight=10)
 def date(repo, subset, x):
     """Changesets within the interval, see :hg:`help dates`.
     """
@@ -664,7 +664,7 @@
     return subset.filter(lambda x: dm(repo[x].date()[0]),
                          condrepr=('<date %r>', ds))
 
- at predicate('desc(string)', safe=True)
+ at predicate('desc(string)', safe=True, weight=10)
 def desc(repo, subset, x):
     """Search commit message for string. The match is case-insensitive.
 
@@ -722,7 +722,7 @@
     # Like ``descendants(set)`` but follows only the first parents.
     return _descendants(repo, subset, x, followfirst=True)
 
- at predicate('destination([set])', safe=True)
+ at predicate('destination([set])', safe=True, weight=10)
 def destination(repo, subset, x):
     """Changesets that were created by a graft, transplant or rebase operation,
     with the given revisions specified as the source.  Omitting the optional set
@@ -891,7 +891,7 @@
 
     return subset & s
 
- at predicate('first(set, [n])', safe=True, takeorder=True)
+ at predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
 def first(repo, subset, x, order):
     """An alias for limit().
     """
@@ -1012,7 +1012,7 @@
     getargs(x, 0, 0, _("all takes no arguments"))
     return subset & spanset(repo)  # drop "null" if any
 
- at predicate('grep(regex)')
+ at predicate('grep(regex)', weight=10)
 def grep(repo, subset, x):
     """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
     to ensure special escape characters are handled correctly. Unlike
@@ -1097,7 +1097,7 @@
                                    'exclude=%r, default=%r, rev=%r>',
                                    pats, inc, exc, default, rev))
 
- at predicate('file(pattern)', safe=True)
+ at predicate('file(pattern)', safe=True, weight=10)
 def hasfile(repo, subset, x):
     """Changesets affecting files matched by pattern.
 
@@ -1139,7 +1139,7 @@
     hiddenrevs = repoview.filterrevs(repo, 'visible')
     return subset & hiddenrevs
 
- at predicate('keyword(string)', safe=True)
+ at predicate('keyword(string)', safe=True, weight=10)
 def keyword(repo, subset, x):
     """Search commit message, user name, and names of changed files for
     string. The match is case-insensitive.
@@ -1157,7 +1157,7 @@
 
     return subset.filter(matches, condrepr=('<keyword %r>', kw))
 
- at predicate('limit(set[, n[, offset]])', safe=True, takeorder=True)
+ at predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
 def limit(repo, subset, x, order):
     """First n members of set, defaulting to 1, starting from offset.
     """
@@ -1259,7 +1259,7 @@
         pass
     return baseset(datarepr=('<min %r, %r>', subset, os))
 
- at predicate('modifies(pattern)', safe=True)
+ at predicate('modifies(pattern)', safe=True, weight=30)
 def modifies(repo, subset, x):
     """Changesets modifying files matched by pattern.
 
@@ -1403,7 +1403,7 @@
     # some optimizations from the fact this is a baseset.
     return subset & o
 
- at predicate('outgoing([path])', safe=False)
+ at predicate('outgoing([path])', safe=False, weight=10)
 def outgoing(repo, subset, x):
     """Changesets not found in the specified destination repository, or the
     default push location.
@@ -1654,7 +1654,7 @@
             return baseset([r])
     return baseset()
 
- at predicate('removes(pattern)', safe=True)
+ at predicate('removes(pattern)', safe=True, weight=30)
 def removes(repo, subset, x):
     """Changesets which remove files matching pattern.
 
@@ -1794,7 +1794,7 @@
 
     return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
 
- at predicate('reverse(set)', safe=True, takeorder=True)
+ at predicate('reverse(set)', safe=True, takeorder=True, weight=0)
 def reverse(repo, subset, x, order):
     """Reverse order of set.
     """
@@ -1862,7 +1862,8 @@
 
     return args['set'], keyflags, opts
 
- at predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
+ at predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
+           weight=10)
 def sort(repo, subset, x, order):
     """Sort set by keys. The default sort order is ascending, specify a key
     as ``-key`` to sort in descending order.
@@ -2033,16 +2034,16 @@
     return subset & orphan
 
 
- at predicate('user(string)', safe=True)
+ at predicate('user(string)', safe=True, weight=10)
 def user(repo, subset, x):
     """User name contains string. The match is case-insensitive.
 
     Pattern matching is supported for `string`. See
     :hg:`help revisions.patterns`.
     """
     return author(repo, subset, x)
 
- at predicate('wdir()', safe=True)
+ at predicate('wdir()', safe=True, weight=0)
 def wdir(repo, subset, x):
     """Working directory. (EXPERIMENTAL)"""
     # i18n: "wdir" is a keyword
@@ -2097,7 +2098,7 @@
     return baseset([r for r in ls if r in s])
 
 # for internal use
- at predicate('_intlist', safe=True, takeorder=True)
+ at predicate('_intlist', safe=True, takeorder=True, weight=0)
 def _intlist(repo, subset, x, order):
     if order == followorder:
         # slow path to take the subset order
diff --git a/mercurial/registrar.py b/mercurial/registrar.py
--- a/mercurial/registrar.py
+++ b/mercurial/registrar.py
@@ -166,6 +166,10 @@
     Optional argument 'takeorder' indicates whether a predicate function
     takes ordering policy as the last argument.
 
+    Optional argument 'weight' indicates the estimated run-time cost, useful
+    for static optimization. Higher weight means more expensive. For example,
+    'contains' has a weight of 100.
+
     'revsetpredicate' instance in example above can be used to
     decorate multiple functions.
 
@@ -178,9 +182,10 @@
     _getname = _funcregistrarbase._parsefuncdecl
     _docformat = "``%s``\n    %s"
 
-    def _extrasetup(self, name, func, safe=False, takeorder=False):
+    def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1):
         func._safe = safe
         func._takeorder = takeorder
+        func._weight = weight
 
 class filesetpredicate(_funcregistrarbase):
     """Decorator to register fileset predicate



To: quark, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list