zope.interface is superior to the abc module. Let's port to it.
As part of this, we add tests for interface conformance for
classes implementing the interface.
durin42 |
hg-reviewers |
zope.interface is superior to the abc module. Let's port to it.
As part of this, we add tests for interface conformance for
classes implementing the interface.
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
Path | Packages | |||
---|---|---|---|---|
M | mercurial/wireprotoserver.py (12 lines) | |||
M | mercurial/wireprototypes.py (31 lines) | |||
M | tests/test-check-interfaces.py (21 lines) |
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net> | ||||
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||||
# | # | ||||
# This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
import contextlib | import contextlib | ||||
import struct | import struct | ||||
import sys | import sys | ||||
import threading | import threading | ||||
from .i18n import _ | from .i18n import _ | ||||
from .thirdparty.zope import ( | |||||
interface as zi, | |||||
) | |||||
from . import ( | from . import ( | ||||
encoding, | encoding, | ||||
error, | error, | ||||
hook, | hook, | ||||
pycompat, | pycompat, | ||||
util, | util, | ||||
wireproto, | wireproto, | ||||
wireprotoframing, | wireprotoframing, | ||||
v = req.headers.get(b'%s-%d' % (headerprefix, i)) | v = req.headers.get(b'%s-%d' % (headerprefix, i)) | ||||
if v is None: | if v is None: | ||||
break | break | ||||
chunks.append(pycompat.bytesurl(v)) | chunks.append(pycompat.bytesurl(v)) | ||||
i += 1 | i += 1 | ||||
return ''.join(chunks) | return ''.join(chunks) | ||||
class httpv1protocolhandler(wireprototypes.baseprotocolhandler): | @zi.implementer(wireprototypes.baseprotocolhandler) | ||||
class httpv1protocolhandler(object): | |||||
def __init__(self, req, ui, checkperm): | def __init__(self, req, ui, checkperm): | ||||
self._req = req | self._req = req | ||||
self._ui = ui | self._ui = ui | ||||
self._checkperm = checkperm | self._checkperm = checkperm | ||||
@property | @property | ||||
def name(self): | def name(self): | ||||
return 'http-v1' | return 'http-v1' | ||||
# Maps API name to metadata so custom API can be registered. | # Maps API name to metadata so custom API can be registered. | ||||
API_HANDLERS = { | API_HANDLERS = { | ||||
HTTPV2: { | HTTPV2: { | ||||
'config': ('experimental', 'web.api.http-v2'), | 'config': ('experimental', 'web.api.http-v2'), | ||||
'handler': _handlehttpv2request, | 'handler': _handlehttpv2request, | ||||
}, | }, | ||||
} | } | ||||
class httpv2protocolhandler(wireprototypes.baseprotocolhandler): | @zi.implementer(wireprototypes.baseprotocolhandler) | ||||
class httpv2protocolhandler(object): | |||||
def __init__(self, req, ui, args=None): | def __init__(self, req, ui, args=None): | ||||
self._req = req | self._req = req | ||||
self._ui = ui | self._ui = ui | ||||
self._args = args | self._args = args | ||||
@property | @property | ||||
def name(self): | def name(self): | ||||
return HTTPV2 | return HTTPV2 | ||||
fout.flush() | fout.flush() | ||||
def _sshv1respondooberror(fout, ferr, rsp): | def _sshv1respondooberror(fout, ferr, rsp): | ||||
ferr.write(b'%s\n-\n' % rsp) | ferr.write(b'%s\n-\n' % rsp) | ||||
ferr.flush() | ferr.flush() | ||||
fout.write(b'\n') | fout.write(b'\n') | ||||
fout.flush() | fout.flush() | ||||
class sshv1protocolhandler(wireprototypes.baseprotocolhandler): | @zi.implementer(wireprototypes.baseprotocolhandler) | ||||
class sshv1protocolhandler(object): | |||||
"""Handler for requests services via version 1 of SSH protocol.""" | """Handler for requests services via version 1 of SSH protocol.""" | ||||
def __init__(self, ui, fin, fout): | def __init__(self, ui, fin, fout): | ||||
self._ui = ui | self._ui = ui | ||||
self._fin = fin | self._fin = fin | ||||
self._fout = fout | self._fout = fout | ||||
@property | @property | ||||
def name(self): | def name(self): |
# Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com> | # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com> | ||||
# | # | ||||
# This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
from __future__ import absolute_import | from __future__ import absolute_import | ||||
import abc | from .thirdparty.zope import ( | ||||
interface as zi, | |||||
) | |||||
# Names of the SSH protocol implementations. | # Names of the SSH protocol implementations. | ||||
SSHV1 = 'ssh-v1' | SSHV1 = 'ssh-v1' | ||||
# These are advertised over the wire. Increment the counters at the end | # These are advertised over the wire. Increment the counters at the end | ||||
# to reflect BC breakages. | # to reflect BC breakages. | ||||
SSHV2 = 'exp-ssh-v2-0001' | SSHV2 = 'exp-ssh-v2-0001' | ||||
HTTPV2 = 'exp-http-v2-0001' | HTTPV2 = 'exp-http-v2-0001' | ||||
Accepts a generator containing chunks of data to be sent to the client. | Accepts a generator containing chunks of data to be sent to the client. | ||||
Like ``streamres``, but sends an uncompressed data for "version 1" clients | Like ``streamres``, but sends an uncompressed data for "version 1" clients | ||||
using the application/mercurial-0.1 media type. | using the application/mercurial-0.1 media type. | ||||
""" | """ | ||||
def __init__(self, gen=None): | def __init__(self, gen=None): | ||||
self.gen = gen | self.gen = gen | ||||
class baseprotocolhandler(object): | class baseprotocolhandler(zi.Interface): | ||||
"""Abstract base class for wire protocol handlers. | """Abstract base class for wire protocol handlers. | ||||
A wire protocol handler serves as an interface between protocol command | A wire protocol handler serves as an interface between protocol command | ||||
handlers and the wire protocol transport layer. Protocol handlers provide | handlers and the wire protocol transport layer. Protocol handlers provide | ||||
methods to read command arguments, redirect stdio for the duration of | methods to read command arguments, redirect stdio for the duration of | ||||
the request, handle response types, etc. | the request, handle response types, etc. | ||||
""" | """ | ||||
__metaclass__ = abc.ABCMeta | name = zi.Attribute( | ||||
@abc.abstractproperty | |||||
def name(self): | |||||
"""The name of the protocol implementation. | """The name of the protocol implementation. | ||||
Used for uniquely identifying the transport type. | Used for uniquely identifying the transport type. | ||||
""" | """) | ||||
@abc.abstractmethod | def getargs(args): | ||||
def getargs(self, args): | |||||
"""return the value for arguments in <args> | """return the value for arguments in <args> | ||||
returns a list of values (same order as <args>)""" | returns a list of values (same order as <args>)""" | ||||
@abc.abstractmethod | def forwardpayload(fp): | ||||
def forwardpayload(self, fp): | |||||
"""Read the raw payload and forward to a file. | """Read the raw payload and forward to a file. | ||||
The payload is read in full before the function returns. | The payload is read in full before the function returns. | ||||
""" | """ | ||||
@abc.abstractmethod | def mayberedirectstdio(): | ||||
def mayberedirectstdio(self): | |||||
"""Context manager to possibly redirect stdio. | """Context manager to possibly redirect stdio. | ||||
The context manager yields a file-object like object that receives | The context manager yields a file-object like object that receives | ||||
stdout and stderr output when the context manager is active. Or it | stdout and stderr output when the context manager is active. Or it | ||||
yields ``None`` if no I/O redirection occurs. | yields ``None`` if no I/O redirection occurs. | ||||
The intent of this context manager is to capture stdio output | The intent of this context manager is to capture stdio output | ||||
so it may be sent in the response. Some transports support streaming | so it may be sent in the response. Some transports support streaming | ||||
stdio to the client in real time. For these transports, stdio output | stdio to the client in real time. For these transports, stdio output | ||||
won't be captured. | won't be captured. | ||||
""" | """ | ||||
@abc.abstractmethod | def client(): | ||||
def client(self): | |||||
"""Returns a string representation of this client (as bytes).""" | """Returns a string representation of this client (as bytes).""" | ||||
@abc.abstractmethod | def addcapabilities(repo, caps): | ||||
def addcapabilities(self, repo, caps): | |||||
"""Adds advertised capabilities specific to this protocol. | """Adds advertised capabilities specific to this protocol. | ||||
Receives the list of capabilities collected so far. | Receives the list of capabilities collected so far. | ||||
Returns a list of capabilities. The passed in argument can be returned. | Returns a list of capabilities. The passed in argument can be returned. | ||||
""" | """ | ||||
@abc.abstractmethod | def checkperm(perm): | ||||
def checkperm(self, perm): | |||||
"""Validate that the client has permissions to perform a request. | """Validate that the client has permissions to perform a request. | ||||
The argument is the permission required to proceed. If the client | The argument is the permission required to proceed. If the client | ||||
doesn't have that permission, the exception should raise or abort | doesn't have that permission, the exception should raise or abort | ||||
in a protocol specific manner. | in a protocol specific manner. | ||||
""" | """ |
bundlerepo, | bundlerepo, | ||||
httppeer, | httppeer, | ||||
localrepo, | localrepo, | ||||
repository, | repository, | ||||
sshpeer, | sshpeer, | ||||
statichttprepo, | statichttprepo, | ||||
ui as uimod, | ui as uimod, | ||||
unionrepo, | unionrepo, | ||||
wireprotoserver, | |||||
wireprototypes, | |||||
) | ) | ||||
rootdir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) | rootdir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..')) | ||||
def checkobject(o): | def checkobject(o): | ||||
"""Verify a constructed object conforms to interface rules. | """Verify a constructed object conforms to interface rules. | ||||
An object must have __abstractmethods__ defined. | An object must have __abstractmethods__ defined. | ||||
checkobject(statichttprepo.statichttppeer(dummyrepo())) | checkobject(statichttprepo.statichttppeer(dummyrepo())) | ||||
checkobject(unionrepo.unionpeer(dummyrepo())) | checkobject(unionrepo.unionpeer(dummyrepo())) | ||||
ziverify.verifyClass(repository.completelocalrepository, | ziverify.verifyClass(repository.completelocalrepository, | ||||
localrepo.localrepository) | localrepo.localrepository) | ||||
repo = localrepo.localrepository(ui, rootdir) | repo = localrepo.localrepository(ui, rootdir) | ||||
checkzobject(repo) | checkzobject(repo) | ||||
ziverify.verifyClass(wireprototypes.baseprotocolhandler, | |||||
wireprotoserver.sshv1protocolhandler) | |||||
ziverify.verifyClass(wireprototypes.baseprotocolhandler, | |||||
wireprotoserver.sshv2protocolhandler) | |||||
ziverify.verifyClass(wireprototypes.baseprotocolhandler, | |||||
wireprotoserver.httpv1protocolhandler) | |||||
ziverify.verifyClass(wireprototypes.baseprotocolhandler, | |||||
wireprotoserver.httpv2protocolhandler) | |||||
sshv1 = wireprotoserver.sshv1protocolhandler(None, None, None) | |||||
checkzobject(sshv1) | |||||
sshv2 = wireprotoserver.sshv2protocolhandler(None, None, None) | |||||
checkzobject(sshv2) | |||||
httpv1 = wireprotoserver.httpv1protocolhandler(None, None, None) | |||||
checkzobject(httpv1) | |||||
httpv2 = wireprotoserver.httpv2protocolhandler(None, None) | |||||
checkzobject(httpv2) | |||||
main() | main() |