Patchwork D1998: wireproto: define and use types for wire protocol commands

login
register
mail settings
Submitter phabricator
Date Feb. 7, 2018, 10:41 p.m.
Message ID <ea9264375590dbad3197173817b63b72@localhost.localdomain>
Download mbox | patch
Permalink /patch/27439/
State Not Applicable
Headers show

Comments

phabricator - Feb. 7, 2018, 10:41 p.m.
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGef683a0fd21f: wireproto: define and use types for wire protocol commands (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D1998?vs=5137&id=5305

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

AFFECTED FILES
  mercurial/wireproto.py

CHANGE DETAILS




To: indygreg, #hg-reviewers, durin42
Cc: durin42, mercurial-devel

Patch

diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -634,8 +634,64 @@ 
 
     return compengines
 
-# list of commands
-commands = {}
+class commandentry(object):
+    """Represents a declared wire protocol command."""
+    def __init__(self, func, args=''):
+        self.func = func
+        self.args = args
+
+    def _merge(self, func, args):
+        """Merge this instance with an incoming 2-tuple.
+
+        This is called when a caller using the old 2-tuple API attempts
+        to replace an instance. The incoming values are merged with
+        data not captured by the 2-tuple and a new instance containing
+        the union of the two objects is returned.
+        """
+        return commandentry(func, args)
+
+    # Old code treats instances as 2-tuples. So expose that interface.
+    def __iter__(self):
+        yield self.func
+        yield self.args
+
+    def __getitem__(self, i):
+        if i == 0:
+            return self.func
+        elif i == 1:
+            return self.args
+        else:
+            raise IndexError('can only access elements 0 and 1')
+
+class commanddict(dict):
+    """Container for registered wire protocol commands.
+
+    It behaves like a dict. But __setitem__ is overwritten to allow silent
+    coercion of values from 2-tuples for API compatibility.
+    """
+    def __setitem__(self, k, v):
+        if isinstance(v, commandentry):
+            pass
+        # Cast 2-tuples to commandentry instances.
+        elif isinstance(v, tuple):
+            if len(v) != 2:
+                raise ValueError('command tuples must have exactly 2 elements')
+
+            # It is common for extensions to wrap wire protocol commands via
+            # e.g. ``wireproto.commands[x] = (newfn, args)``. Because callers
+            # doing this aren't aware of the new API that uses objects to store
+            # command entries, we automatically merge old state with new.
+            if k in self:
+                v = self[k]._merge(v[0], v[1])
+            else:
+                v = commandentry(v[0], v[1])
+        else:
+            raise ValueError('command entries must be commandentry instances '
+                             'or 2-tuples')
+
+        return super(commanddict, self).__setitem__(k, v)
+
+commands = commanddict()
 
 def wireprotocommand(name, args=''):
     """Decorator to declare a wire protocol command.
@@ -646,7 +702,7 @@ 
     accepts. ``*`` is a special value that says to accept all arguments.
     """
     def register(func):
-        commands[name] = (func, args)
+        commands[name] = commandentry(func, args)
         return func
     return register