comparison mercurial/wireproto.py @ 36609:abc3b9801563

wireproto: allow wire protocol commands to declare transport support Currently, wire protocol commands are exposed on all transports. Some wire protocol commands are only supported or sensical on some transports. In the future, new wire protocol commands may only be available on new transports and legacy wire protocol commands may not be available to newer transports. This commit introduces a mechanism to allow @wireprotocommand to declare transports for which they should not be available. The mechanism for determining if a wire protocol command is available for a given transport instance has been taught to take this knowledge into account. To help implement this feature, we add a dict to wireprototypes declaring all wire transports and their metadata. There's probably room to refactor the constants used to identify the wire protocols. But that can be in another commit. Differential Revision: https://phab.mercurial-scm.org/D2483
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 02 Mar 2018 09:47:37 -0500
parents 1c16324fdf05
children 6906547c8476
comparison
equal deleted inserted replaced
36608:1151c731686e 36609:abc3b9801563
590 590
591 return compengines 591 return compengines
592 592
593 class commandentry(object): 593 class commandentry(object):
594 """Represents a declared wire protocol command.""" 594 """Represents a declared wire protocol command."""
595 def __init__(self, func, args=''): 595 def __init__(self, func, args='', transports=None):
596 self.func = func 596 self.func = func
597 self.args = args 597 self.args = args
598 self.transports = transports or set()
598 599
599 def _merge(self, func, args): 600 def _merge(self, func, args):
600 """Merge this instance with an incoming 2-tuple. 601 """Merge this instance with an incoming 2-tuple.
601 602
602 This is called when a caller using the old 2-tuple API attempts 603 This is called when a caller using the old 2-tuple API attempts
603 to replace an instance. The incoming values are merged with 604 to replace an instance. The incoming values are merged with
604 data not captured by the 2-tuple and a new instance containing 605 data not captured by the 2-tuple and a new instance containing
605 the union of the two objects is returned. 606 the union of the two objects is returned.
606 """ 607 """
607 return commandentry(func, args=args) 608 return commandentry(func, args=args, transports=set(self.transports))
608 609
609 # Old code treats instances as 2-tuples. So expose that interface. 610 # Old code treats instances as 2-tuples. So expose that interface.
610 def __iter__(self): 611 def __iter__(self):
611 yield self.func 612 yield self.func
612 yield self.args 613 yield self.args
638 # doing this aren't aware of the new API that uses objects to store 639 # doing this aren't aware of the new API that uses objects to store
639 # command entries, we automatically merge old state with new. 640 # command entries, we automatically merge old state with new.
640 if k in self: 641 if k in self:
641 v = self[k]._merge(v[0], v[1]) 642 v = self[k]._merge(v[0], v[1])
642 else: 643 else:
643 v = commandentry(v[0], args=v[1]) 644 # Use default values from @wireprotocommand.
645 v = commandentry(v[0], args=v[1],
646 transports=set(wireprototypes.TRANSPORTS))
644 else: 647 else:
645 raise ValueError('command entries must be commandentry instances ' 648 raise ValueError('command entries must be commandentry instances '
646 'or 2-tuples') 649 'or 2-tuples')
647 650
648 return super(commanddict, self).__setitem__(k, v) 651 return super(commanddict, self).__setitem__(k, v)
649 652
650 def commandavailable(self, command, proto): 653 def commandavailable(self, command, proto):
651 """Determine if a command is available for the requested protocol.""" 654 """Determine if a command is available for the requested protocol."""
652 # For now, commands are available for all protocols. So do a simple 655 assert proto.name in wireprototypes.TRANSPORTS
653 # membership test. 656
654 return command in self 657 entry = self.get(command)
658
659 if not entry:
660 return False
661
662 if proto.name not in entry.transports:
663 return False
664
665 return True
666
667 # Constants specifying which transports a wire protocol command should be
668 # available on. For use with @wireprotocommand.
669 POLICY_ALL = 'all'
670 POLICY_V1_ONLY = 'v1-only'
671 POLICY_V2_ONLY = 'v2-only'
655 672
656 commands = commanddict() 673 commands = commanddict()
657 674
658 def wireprotocommand(name, args=''): 675 def wireprotocommand(name, args='', transportpolicy=POLICY_ALL):
659 """Decorator to declare a wire protocol command. 676 """Decorator to declare a wire protocol command.
660 677
661 ``name`` is the name of the wire protocol command being provided. 678 ``name`` is the name of the wire protocol command being provided.
662 679
663 ``args`` is a space-delimited list of named arguments that the command 680 ``args`` is a space-delimited list of named arguments that the command
664 accepts. ``*`` is a special value that says to accept all arguments. 681 accepts. ``*`` is a special value that says to accept all arguments.
682
683 ``transportpolicy`` is a POLICY_* constant denoting which transports
684 this wire protocol command should be exposed to. By default, commands
685 are exposed to all wire protocol transports.
665 """ 686 """
687 if transportpolicy == POLICY_ALL:
688 transports = set(wireprototypes.TRANSPORTS)
689 elif transportpolicy == POLICY_V1_ONLY:
690 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
691 if v['version'] == 1}
692 elif transportpolicy == POLICY_V2_ONLY:
693 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
694 if v['version'] == 2}
695 else:
696 raise error.Abort(_('invalid transport policy value: %s') %
697 transportpolicy)
698
666 def register(func): 699 def register(func):
667 commands[name] = commandentry(func, args=args) 700 commands[name] = commandentry(func, args=args, transports=transports)
668 return func 701 return func
669 return register 702 return register
670 703
671 @wireprotocommand('batch', 'cmds *') 704 @wireprotocommand('batch', 'cmds *')
672 def batch(repo, proto, cmds, others): 705 def batch(repo, proto, cmds, others):