D3396: wireproto: move command registration types to wireprototypes

2018-04-18 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG352932a11905: wireproto: move command registration types to 
wireprototypes (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3396?vs=8367&id=8385

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

AFFECTED FILES
  mercurial/wireproto.py
  mercurial/wireprototypes.py
  mercurial/wireprotov2server.py

CHANGE DETAILS

diff --git a/mercurial/wireprotov2server.py b/mercurial/wireprotov2server.py
--- a/mercurial/wireprotov2server.py
+++ b/mercurial/wireprotov2server.py
@@ -438,7 +438,7 @@
 raise error.ProgrammingError('%s command already registered '
  'for version 2' % name)
 
-wireproto.commandsv2[name] = wireproto.commandentry(
+wireproto.commandsv2[name] = wireprototypes.commandentry(
 func, args=args, transports=transports, permission=permission)
 
 return func
diff --git a/mercurial/wireprototypes.py b/mercurial/wireprototypes.py
--- a/mercurial/wireprototypes.py
+++ b/mercurial/wireprototypes.py
@@ -241,3 +241,81 @@
 doesn't have that permission, the exception should raise or abort
 in a protocol specific manner.
 """
+
+class commandentry(object):
+"""Represents a declared wire protocol command."""
+def __init__(self, func, args='', transports=None,
+ permission='push'):
+self.func = func
+self.args = args
+self.transports = transports or set()
+self.permission = permission
+
+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=args, transports=set(self.transports),
+permission=self.permission)
+
+# 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:
+# Use default values from @wireprotocommand.
+v = commandentry(v[0], args=v[1],
+ transports=set(TRANSPORTS),
+ permission='push')
+else:
+raise ValueError('command entries must be commandentry instances '
+ 'or 2-tuples')
+
+return super(commanddict, self).__setitem__(k, v)
+
+def commandavailable(self, command, proto):
+"""Determine if a command is available for the requested protocol."""
+assert proto.name in TRANSPORTS
+
+entry = self.get(command)
+
+if not entry:
+return False
+
+if proto.name not in entry.transports:
+return False
+
+return True
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -173,89 +173,11 @@
 
 return compengines
 
-class commandentry(object):
-"""Represents a declared wire protocol command."""
-def __init__(self, func, args='', transports=None,
- permission='push'):
-self.func = func
-self.args = args
-self.transports = transports or set()
-self.permission = permission
-
-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

D3396: wireproto: move command registration types to wireprototypes

2018-04-16 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  These are shared across wire protocol implementations. wireprototypes
  is our module for common code.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/wireproto.py
  mercurial/wireprototypes.py
  mercurial/wireprotov2server.py

CHANGE DETAILS

diff --git a/mercurial/wireprotov2server.py b/mercurial/wireprotov2server.py
--- a/mercurial/wireprotov2server.py
+++ b/mercurial/wireprotov2server.py
@@ -438,7 +438,7 @@
 raise error.ProgrammingError('%s command already registered '
  'for version 2' % name)
 
-wireproto.commandsv2[name] = wireproto.commandentry(
+wireproto.commandsv2[name] = wireprototypes.commandentry(
 func, args=args, transports=transports, permission=permission)
 
 return func
diff --git a/mercurial/wireprototypes.py b/mercurial/wireprototypes.py
--- a/mercurial/wireprototypes.py
+++ b/mercurial/wireprototypes.py
@@ -241,3 +241,81 @@
 doesn't have that permission, the exception should raise or abort
 in a protocol specific manner.
 """
+
+class commandentry(object):
+"""Represents a declared wire protocol command."""
+def __init__(self, func, args='', transports=None,
+ permission='push'):
+self.func = func
+self.args = args
+self.transports = transports or set()
+self.permission = permission
+
+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=args, transports=set(self.transports),
+permission=self.permission)
+
+# 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:
+# Use default values from @wireprotocommand.
+v = commandentry(v[0], args=v[1],
+ transports=set(TRANSPORTS),
+ permission='push')
+else:
+raise ValueError('command entries must be commandentry instances '
+ 'or 2-tuples')
+
+return super(commanddict, self).__setitem__(k, v)
+
+def commandavailable(self, command, proto):
+"""Determine if a command is available for the requested protocol."""
+assert proto.name in TRANSPORTS
+
+entry = self.get(command)
+
+if not entry:
+return False
+
+if proto.name not in entry.transports:
+return False
+
+return True
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -173,89 +173,11 @@
 
 return compengines
 
-class commandentry(object):
-"""Represents a declared wire protocol command."""
-def __init__(self, func, args='', transports=None,
- permission='push'):
-self.func = func
-self.args = args
-self.transports = transports or set()
-self.permission = permission
-
-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 uni