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 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(wireprototypes.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 wireprototypes.TRANSPORTS - - entry = self.get(command) - - if not entry: - return False - - if proto.name not in entry.transports: - return False - - return True - # For version 1 transports. -commands = commanddict() +commands = wireprototypes.commanddict() # For version 2 transports. -commandsv2 = commanddict() +commandsv2 = wireprototypes.commanddict() def wireprotocommand(name, args=None, permission='push'): """Decorator to declare a wire protocol command. @@ -297,9 +219,8 @@ if name in commands: raise error.ProgrammingError('%s command already registered ' 'for version 1' % name) - commands[name] = commandentry(func, args=args, - transports=transports, - permission=permission) + commands[name] = wireprototypes.commandentry( + func, args=args, transports=transports, permission=permission) return func return register 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/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