revset: add extra data to filteredset for better inspection
authorYuya Nishihara <yuya@tcha.org>
Sat, 13 Feb 2016 19:25:11 +0900
changeset 28423 0d79d91ba7e3
parent 28422 e2c6092ad422
child 28424 534f968d33e5
revset: add extra data to filteredset for better inspection A filteredset is heavily used, but it cannot provide a printable information how given set is filtered because a condition is an arbitrary callable object. This patch adds an optional "condrepr" object that is used only by repr(). To minimize the maintaining/runtime overhead of "condrepr", its type is overloaded as follows: type example -------- --------------------------------- tuple ('<not %r>', other) str '<branch closed>' callable lambda: '<branch %r>' % sorted(b) object other
mercurial/revset.py
tests/test-revset.t
--- a/mercurial/revset.py	Tue Mar 01 10:18:47 2016 +0000
+++ b/mercurial/revset.py	Sat Feb 13 19:25:11 2016 +0900
@@ -2730,6 +2730,29 @@
             funcs.add(tree[1][1])
         return funcs
 
+def _formatsetrepr(r):
+    """Format an optional printable representation of a set
+
+    ========  =================================
+    type(r)   example
+    ========  =================================
+    tuple     ('<not %r>', other)
+    str       '<branch closed>'
+    callable  lambda: '<branch %r>' % sorted(b)
+    object    other
+    ========  =================================
+    """
+    if r is None:
+        return ''
+    elif isinstance(r, tuple):
+        return r[0] % r[1:]
+    elif isinstance(r, str):
+        return r
+    elif callable(r):
+        return r()
+    else:
+        return repr(r)
+
 class abstractsmartset(object):
 
     def __nonzero__(self):
@@ -2810,7 +2833,7 @@
         This is part of the mandatory API for smartset."""
         if isinstance(other, fullreposet):
             return self
-        return self.filter(other.__contains__, cache=False)
+        return self.filter(other.__contains__, condrepr=other, cache=False)
 
     def __add__(self, other):
         """Returns a new object with the union of the two collections.
@@ -2823,19 +2846,21 @@
 
         This is part of the mandatory API for smartset."""
         c = other.__contains__
-        return self.filter(lambda r: not c(r), cache=False)
-
-    def filter(self, condition, cache=True):
+        return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
+                           cache=False)
+
+    def filter(self, condition, condrepr=None, cache=True):
         """Returns this smartset filtered by condition as a new smartset.
 
         `condition` is a callable which takes a revision number and returns a
-        boolean.
+        boolean. Optional `condrepr` provides a printable representation of
+        the given `condition`.
 
         This is part of the mandatory API for smartset."""
         # builtin cannot be cached. but do not needs to
         if cache and util.safehasattr(condition, 'func_code'):
             condition = util.cachefunc(condition)
-        return filteredset(self, condition)
+        return filteredset(self, condition, condrepr)
 
 class baseset(abstractsmartset):
     """Basic data structure that represents a revset and contains the basic
@@ -2939,13 +2964,16 @@
     the subset and contains a function which tests for membership in the
     revset
     """
-    def __init__(self, subset, condition=lambda x: True):
+    def __init__(self, subset, condition=lambda x: True, condrepr=None):
         """
         condition: a function that decide whether a revision in the subset
                    belongs to the revset or not.
+        condrepr: a tuple of (format, obj, ...), a function or an object that
+                  provides a printable representation of the given condition.
         """
         self._subset = subset
         self._condition = condition
+        self._condrepr = condrepr
 
     def __contains__(self, x):
         return x in self._subset and self._condition(x)
@@ -3025,7 +3053,11 @@
             return x
 
     def __repr__(self):
-        return '<%s %r>' % (type(self).__name__, self._subset)
+        xs = [repr(self._subset)]
+        s = _formatsetrepr(self._condrepr)
+        if s:
+            xs.append(s)
+        return '<%s %s>' % (type(self).__name__, ', '.join(xs))
 
 def _iterordered(ascending, iter1, iter2):
     """produce an ordered iteration from two iterators with the same order
--- a/tests/test-revset.t	Tue Mar 01 10:18:47 2016 +0000
+++ b/tests/test-revset.t	Sat Feb 13 19:25:11 2016 +0900
@@ -169,7 +169,9 @@
     ('symbol', 'a'))
   * set:
   <filteredset
-    <baseset [1]>>
+    <baseset [1]>,
+    <not
+      <baseset [0]>>>
   1
   $ try _a_b_c_
   ('symbol', '_a_b_c_')
@@ -182,7 +184,9 @@
     ('symbol', 'a'))
   * set:
   <filteredset
-    <baseset [6]>>
+    <baseset [6]>,
+    <not
+      <baseset [0]>>>
   6
   $ try .a.b.c.
   ('symbol', '.a.b.c.')
@@ -195,7 +199,9 @@
     ('symbol', 'a'))
   * set:
   <filteredset
-    <baseset [7]>>
+    <baseset [7]>,
+    <not
+      <baseset [0]>>>
   7
 
 names that should be caught by fallback mechanism
@@ -278,7 +284,9 @@
     ('symbol', 'a'))
   * set:
   <filteredset
-    <baseset [4]>>
+    <baseset [4]>,
+    <not
+      <baseset [0]>>>
   4
 
   $ log '1 or 2'