Patchwork D3068: repository: port peer interfaces to zope.interface

login
register
mail settings
Submitter phabricator
Date April 4, 2018, 1:40 p.m.
Message ID <1b7590ae2553e75c082d3b528370738a@localhost.localdomain>
Download mbox | patch
Permalink /patch/30254/
State Not Applicable
Headers show

Comments

phabricator - April 4, 2018, 1:40 p.m.
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG39f7d4ee8bcd: repository: port peer interfaces to zope.interface (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D3068?vs=7611&id=7620

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

AFFECTED FILES
  mercurial/httppeer.py
  mercurial/repository.py
  mercurial/sshpeer.py
  mercurial/wireproto.py
  tests/test-check-interfaces.py
  tests/test-check-interfaces.py.out

CHANGE DETAILS




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

Patch

diff --git a/tests/test-check-interfaces.py.out b/tests/test-check-interfaces.py.out
--- a/tests/test-check-interfaces.py.out
+++ b/tests/test-check-interfaces.py.out
@@ -1,2 +1,2 @@ 
-public attributes not in abstract interface: badpeer.badattribute
-public attributes not in abstract interface: badpeer.badmethod
+public attribute not declared in interfaces: badpeer.badattribute
+public attribute not declared in interfaces: badpeer.badmethod
diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py
--- a/tests/test-check-interfaces.py
+++ b/tests/test-check-interfaces.py
@@ -25,35 +25,6 @@ 
 
 rootdir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
 
-def checkobject(o):
-    """Verify a constructed object conforms to interface rules.
-
-    An object must have __abstractmethods__ defined.
-
-    All "public" attributes of the object (attributes not prefixed with
-    an underscore) must be in __abstractmethods__ or appear on a base class
-    with __abstractmethods__.
-    """
-    name = o.__class__.__name__
-
-    allowed = set()
-    for cls in o.__class__.__mro__:
-        if not getattr(cls, '__abstractmethods__', set()):
-            continue
-
-        allowed |= cls.__abstractmethods__
-        allowed |= {a for a in dir(cls) if not a.startswith('_')}
-
-    if not allowed:
-        print('%s does not have abstract methods' % name)
-        return
-
-    public = {a for a in dir(o) if not a.startswith('_')}
-
-    for attr in sorted(public - allowed):
-        print('public attributes not in abstract interface: %s.%s' % (
-            name, attr))
-
 def checkzobject(o):
     """Verify an object with a zope interface."""
     ifaces = zi.providedBy(o)
@@ -108,16 +79,34 @@ 
     # Needed so we can open a local repo with obsstore without a warning.
     ui.setconfig('experimental', 'evolution.createmarkers', True)
 
-    checkobject(badpeer())
-    checkobject(httppeer.httppeer(None, None, None, dummyopener()))
-    checkobject(localrepo.localpeer(dummyrepo()))
-    checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(),
-                                  dummypipe(), None, None))
-    checkobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(),
-                                  dummypipe(), None, None))
-    checkobject(bundlerepo.bundlepeer(dummyrepo()))
-    checkobject(statichttprepo.statichttppeer(dummyrepo()))
-    checkobject(unionrepo.unionpeer(dummyrepo()))
+    checkzobject(badpeer())
+
+    ziverify.verifyClass(repository.ipeerbaselegacycommands,
+                         httppeer.httppeer)
+    checkzobject(httppeer.httppeer(None, None, None, dummyopener()))
+
+    ziverify.verifyClass(repository.ipeerbase,
+                         localrepo.localpeer)
+    checkzobject(localrepo.localpeer(dummyrepo()))
+
+    ziverify.verifyClass(repository.ipeerbaselegacycommands,
+                         sshpeer.sshv1peer)
+    checkzobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(),
+                                   dummypipe(), None, None))
+
+    ziverify.verifyClass(repository.ipeerbaselegacycommands,
+                         sshpeer.sshv2peer)
+    checkzobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(),
+                                   dummypipe(), None, None))
+
+    ziverify.verifyClass(repository.ipeerbase, bundlerepo.bundlepeer)
+    checkzobject(bundlerepo.bundlepeer(dummyrepo()))
+
+    ziverify.verifyClass(repository.ipeerbase, statichttprepo.statichttppeer)
+    checkzobject(statichttprepo.statichttppeer(dummyrepo()))
+
+    ziverify.verifyClass(repository.ipeerbase, unionrepo.unionpeer)
+    checkzobject(unionrepo.unionpeer(dummyrepo()))
 
     ziverify.verifyClass(repository.completelocalrepository,
                          localrepo.localrepository)
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -192,7 +192,7 @@ 
     See also httppeer.py and sshpeer.py for protocol-specific
     implementations of this interface.
     """
-    # Begin of basewirepeer interface.
+    # Begin of ipeercommands interface.
 
     def iterbatch(self):
         return remoteiterbatcher(self)
@@ -353,9 +353,9 @@ 
             ret = bundle2.getunbundler(self.ui, stream)
         return ret
 
-    # End of basewirepeer interface.
+    # End of ipeercommands interface.
 
-    # Begin of baselegacywirepeer interface.
+    # Begin of ipeerlegacycommands interface.
 
     def branches(self, nodes):
         n = encodelist(nodes)
@@ -391,7 +391,7 @@ 
                                    bases=bases, heads=heads)
         return changegroupmod.cg1unpacker(f, 'UN')
 
-    # End of baselegacywirepeer interface.
+    # End of ipeerlegacycommands interface.
 
     def _submitbatch(self, req):
         """run batch request <req> on the server
diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py
--- a/mercurial/sshpeer.py
+++ b/mercurial/sshpeer.py
@@ -377,7 +377,7 @@ 
         'batch',
     }
 
-    # Begin of _basepeer interface.
+    # Begin of ipeerconnection interface.
 
     @util.propertycache
     def ui(self):
@@ -398,14 +398,14 @@ 
     def close(self):
         pass
 
-    # End of _basepeer interface.
+    # End of ipeerconnection interface.
 
-    # Begin of _basewirecommands interface.
+    # Begin of ipeercommands interface.
 
     def capabilities(self):
         return self._caps
 
-    # End of _basewirecommands interface.
+    # End of ipeercommands interface.
 
     def _readerr(self):
         _forwardoutput(self.ui, self._pipee)
diff --git a/mercurial/repository.py b/mercurial/repository.py
--- a/mercurial/repository.py
+++ b/mercurial/repository.py
@@ -7,33 +7,26 @@ 
 
 from __future__ import absolute_import
 
-import abc
-
 from .i18n import _
 from .thirdparty.zope import (
     interface as zi,
 )
 from . import (
     error,
 )
 
-class _basepeer(object):
+class ipeerconnection(zi.Interface):
     """Represents a "connection" to a repository.
 
     This is the base interface for representing a connection to a repository.
     It holds basic properties and methods applicable to all peer types.
 
     This is not a complete interface definition and should not be used
     outside of this module.
     """
-    __metaclass__ = abc.ABCMeta
+    ui = zi.Attribute("""ui.ui instance""")
 
-    @abc.abstractproperty
-    def ui(self):
-        """ui.ui instance."""
-
-    @abc.abstractmethod
-    def url(self):
+    def url():
         """Returns a URL string representing this peer.
 
         Currently, implementations expose the raw URL used to construct the
@@ -45,103 +38,89 @@ 
         value.
         """
 
-    @abc.abstractmethod
-    def local(self):
+    def local():
         """Returns a local repository instance.
 
         If the peer represents a local repository, returns an object that
         can be used to interface with it. Otherwise returns ``None``.
         """
 
-    @abc.abstractmethod
-    def peer(self):
+    def peer():
         """Returns an object conforming to this interface.
 
         Most implementations will ``return self``.
         """
 
-    @abc.abstractmethod
-    def canpush(self):
+    def canpush():
         """Returns a boolean indicating if this peer can be pushed to."""
 
-    @abc.abstractmethod
-    def close(self):
+    def close():
         """Close the connection to this peer.
 
         This is called when the peer will no longer be used. Resources
         associated with the peer should be cleaned up.
         """
 
-class _basewirecommands(object):
+class ipeercommands(zi.Interface):
     """Client-side interface for communicating over the wire protocol.
 
     This interface is used as a gateway to the Mercurial wire protocol.
     methods commonly call wire protocol commands of the same name.
     """
-    __metaclass__ = abc.ABCMeta
 
-    @abc.abstractmethod
-    def branchmap(self):
+    def branchmap():
         """Obtain heads in named branches.
 
         Returns a dict mapping branch name to an iterable of nodes that are
         heads on that branch.
         """
 
-    @abc.abstractmethod
-    def capabilities(self):
+    def capabilities():
         """Obtain capabilities of the peer.
 
         Returns a set of string capabilities.
         """
 
-    @abc.abstractmethod
-    def debugwireargs(self, one, two, three=None, four=None, five=None):
+    def debugwireargs(one, two, three=None, four=None, five=None):
         """Used to facilitate debugging of arguments passed over the wire."""
 
-    @abc.abstractmethod
-    def getbundle(self, source, **kwargs):
+    def getbundle(source, **kwargs):
         """Obtain remote repository data as a bundle.
 
         This command is how the bulk of repository data is transferred from
         the peer to the local repository
 
         Returns a generator of bundle data.
         """
 
-    @abc.abstractmethod
-    def heads(self):
+    def heads():
         """Determine all known head revisions in the peer.
 
         Returns an iterable of binary nodes.
         """
 
-    @abc.abstractmethod
-    def known(self, nodes):
+    def known(nodes):
         """Determine whether multiple nodes are known.
 
         Accepts an iterable of nodes whose presence to check for.
 
         Returns an iterable of booleans indicating of the corresponding node
         at that index is known to the peer.
         """
 
-    @abc.abstractmethod
-    def listkeys(self, namespace):
+    def listkeys(namespace):
         """Obtain all keys in a pushkey namespace.
 
         Returns an iterable of key names.
         """
 
-    @abc.abstractmethod
-    def lookup(self, key):
+    def lookup(key):
         """Resolve a value to a known revision.
 
         Returns a binary node of the resolved revision on success.
         """
 
-    @abc.abstractmethod
-    def pushkey(self, namespace, key, old, new):
+    def pushkey(namespace, key, old, new):
         """Set a value using the ``pushkey`` protocol.
 
         Arguments correspond to the pushkey namespace and key to operate on and
@@ -151,68 +130,58 @@ 
         namespace.
         """
 
-    @abc.abstractmethod
-    def stream_out(self):
+    def stream_out():
         """Obtain streaming clone data.
 
         Successful result should be a generator of data chunks.
         """
 
-    @abc.abstractmethod
-    def unbundle(self, bundle, heads, url):
+    def unbundle(bundle, heads, url):
         """Transfer repository data to the peer.
 
         This is how the bulk of data during a push is transferred.
 
         Returns the integer number of heads added to the peer.
         """
 
-class _baselegacywirecommands(object):
+class ipeerlegacycommands(zi.Interface):
     """Interface for implementing support for legacy wire protocol commands.
 
     Wire protocol commands transition to legacy status when they are no longer
     used by modern clients. To facilitate identifying which commands are
     legacy, the interfaces are split.
     """
-    __metaclass__ = abc.ABCMeta
 
-    @abc.abstractmethod
-    def between(self, pairs):
+    def between(pairs):
         """Obtain nodes between pairs of nodes.
 
         ``pairs`` is an iterable of node pairs.
 
         Returns an iterable of iterables of nodes corresponding to each
         requested pair.
         """
 
-    @abc.abstractmethod
-    def branches(self, nodes):
+    def branches(nodes):
         """Obtain ancestor changesets of specific nodes back to a branch point.
 
         For each requested node, the peer finds the first ancestor node that is
         a DAG root or is a merge.
 
         Returns an iterable of iterables with the resolved values for each node.
         """
 
-    @abc.abstractmethod
-    def changegroup(self, nodes, kind):
+    def changegroup(nodes, kind):
         """Obtain a changegroup with data for descendants of specified nodes."""
 
-    @abc.abstractmethod
-    def changegroupsubset(self, bases, heads, kind):
+    def changegroupsubset(bases, heads, kind):
         pass
 
-class peer(_basepeer, _basewirecommands):
-    """Unified interface and base class for peer repositories.
+class ipeerbase(ipeerconnection, ipeercommands):
+    """Unified interface for peer repositories.
 
-    All peer instances must inherit from this class and conform to its
-    interface.
+    All peer instances must conform to this interface.
     """
-
-    @abc.abstractmethod
-    def iterbatch(self):
+    def iterbatch():
         """Obtain an object to be used for multiple method calls.
 
         Various operations call several methods on peer instances. If each
@@ -236,14 +205,29 @@ 
         calls. However, they must all support this API.
         """
 
-    def capable(self, name):
+    def capable(name):
         """Determine support for a named capability.
 
         Returns ``False`` if capability not supported.
 
         Returns ``True`` if boolean capability is supported. Returns a string
         if capability support is non-boolean.
         """
+
+    def requirecap(name, purpose):
+        """Require a capability to be present.
+
+        Raises a ``CapabilityError`` if the capability isn't present.
+        """
+
+class ipeerbaselegacycommands(ipeerbase, ipeerlegacycommands):
+    """Unified peer interface that supports legacy commands."""
+
+@zi.implementer(ipeerbase)
+class peer(object):
+    """Base class for peer repositories."""
+
+    def capable(self, name):
         caps = self.capabilities()
         if name in caps:
             return True
@@ -256,18 +240,15 @@ 
         return False
 
     def requirecap(self, name, purpose):
-        """Require a capability to be present.
-
-        Raises a ``CapabilityError`` if the capability isn't present.
-        """
         if self.capable(name):
             return
 
         raise error.CapabilityError(
             _('cannot %s; remote repository does not support the %r '
               'capability') % (purpose, name))
 
-class legacypeer(peer, _baselegacywirecommands):
+@zi.implementer(ipeerbaselegacycommands)
+class legacypeer(peer):
     """peer but with support for legacy wire protocol commands."""
 
 class completelocalrepository(zi.Interface):
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -184,7 +184,7 @@ 
                 % (util.timer() - start, ret.code))
         return ret
 
-    # Begin of _basepeer interface.
+    # Begin of ipeerconnection interface.
 
     @util.propertycache
     def ui(self):
@@ -205,17 +205,17 @@ 
     def close(self):
         pass
 
-    # End of _basepeer interface.
+    # End of ipeerconnection interface.
 
-    # Begin of _basewirepeer interface.
+    # Begin of ipeercommands interface.
 
     def capabilities(self):
         # self._fetchcaps() should have been called as part of peer
         # handshake. So self._caps should always be set.
         assert self._caps is not None
         return self._caps
 
-    # End of _basewirepeer interface.
+    # End of ipeercommands interface.
 
     # look up capabilities only when needed