Commit 1ce6fd78 authored by Jim Fulton's avatar Jim Fulton Committed by GitHub

Merge pull request #41 from zopefoundation/py2

Python 2 support
parents 29bf7bc0 9b8cfe9e
...@@ -2,6 +2,8 @@ language: python ...@@ -2,6 +2,8 @@ language: python
sudo: false sudo: false
matrix: matrix:
include: include:
- os: linux
python: 2.7
- os: linux - os: linux
python: 3.4 python: 3.4
- os: linux - os: linux
......
...@@ -21,12 +21,12 @@ if sys.version_info < (2, 7): ...@@ -21,12 +21,12 @@ if sys.version_info < (2, 7):
print("This version of ZEO requires Python 2.7 or higher") print("This version of ZEO requires Python 2.7 or higher")
sys.exit(0) sys.exit(0)
if (3, 0) < sys.version_info < (3, 3): if (3, 0) < sys.version_info < (3, 4):
print("This version of ZEO requires Python 3.3 or higher") print("This version of ZEO requires Python 3.4 or higher")
sys.exit(0) sys.exit(0)
install_requires = [ install_requires = [
'ZODB >= 5.0.0a1', 'ZODB >= 5.0.0a5',
'six', 'six',
'transaction >= 1.6.0', 'transaction >= 1.6.0',
'persistent >= 4.1.0', 'persistent >= 4.1.0',
...@@ -38,12 +38,14 @@ install_requires = [ ...@@ -38,12 +38,14 @@ install_requires = [
tests_require = ['zope.testing', 'manuel', 'random2', 'mock'] tests_require = ['zope.testing', 'manuel', 'random2', 'mock']
if sys.version_info >= (3, 5): if sys.version_info[:2] < (3, ):
install_requires.append('uvloop') install_requires.extend(('futures', 'trollius'))
classifiers = """\ classifiers = """\
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: Zope Public License License :: OSI Approved :: Zope Public License
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
......
...@@ -19,7 +19,6 @@ file storage or Berkeley storage. ...@@ -19,7 +19,6 @@ file storage or Berkeley storage.
TODO: Need some basic access control-- a declaration of the methods TODO: Need some basic access control-- a declaration of the methods
exported for invocation by the server. exported for invocation by the server.
""" """
import asyncio
import codecs import codecs
import itertools import itertools
import logging import logging
......
import sys #
if sys.version_info >= (3, 5):
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
from struct import unpack from .._compat import PY3
import asyncio
if PY3:
import asyncio
else:
import trollius as asyncio
import logging import logging
import socket import socket
from struct import unpack
import sys import sys
from .marshal import encoder from .marshal import encoder
......
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
from ZEO.Exceptions import ClientDisconnected from ZEO.Exceptions import ClientDisconnected
from ZODB.ConflictResolution import ResolvedSerial from ZODB.ConflictResolution import ResolvedSerial
import asyncio
import concurrent.futures import concurrent.futures
import logging import logging
import random import random
...@@ -251,7 +257,7 @@ class Protocol(base.Protocol): ...@@ -251,7 +257,7 @@ class Protocol(base.Protocol):
self.heartbeat_handle = self.loop.call_later( self.heartbeat_handle = self.loop.call_later(
self.heartbeat_interval, self.heartbeat) self.heartbeat_interval, self.heartbeat)
class Client: class Client(object):
"""asyncio low-level ZEO client interface """asyncio low-level ZEO client interface
""" """
...@@ -589,7 +595,7 @@ class Client: ...@@ -589,7 +595,7 @@ class Client:
else: else:
return protocol.read_only return protocol.read_only
class ClientRunner: class ClientRunner(object):
def set_options(self, addrs, wrapper, cache, storage_key, read_only, def set_options(self, addrs, wrapper, cache, storage_key, read_only,
timeout=30, disconnect_poll=1, timeout=30, disconnect_poll=1,
...@@ -608,7 +614,9 @@ class ClientRunner: ...@@ -608,7 +614,9 @@ class ClientRunner:
from concurrent.futures import Future from concurrent.futures import Future
call_soon_threadsafe = loop.call_soon_threadsafe call_soon_threadsafe = loop.call_soon_threadsafe
def call(meth, *args, timeout=None): def call(meth, *args, **kw):
timeout = kw.pop('timeout', None)
assert not kw
result = Future() result = Future()
call_soon_threadsafe(meth, result, *args) call_soon_threadsafe(meth, result, *args)
return self.wait_for_result(result, timeout) return self.wait_for_result(result, timeout)
...@@ -624,8 +632,8 @@ class ClientRunner: ...@@ -624,8 +632,8 @@ class ClientRunner:
else: else:
raise raise
def call(self, method, *args, timeout=None): def call(self, method, *args, **kw):
return self.__call(self.call_threadsafe, method, args, timeout=timeout) return self.__call(self.call_threadsafe, method, args, **kw)
def call_future(self, method, *args): def call_future(self, method, *args):
# for tests # for tests
...@@ -702,8 +710,8 @@ class ClientThread(ClientRunner): ...@@ -702,8 +710,8 @@ class ClientThread(ClientRunner):
self.thread = threading.Thread( self.thread = threading.Thread(
target=self.run, target=self.run,
name="%s zeo client networking thread" % client.__name__, name="%s zeo client networking thread" % client.__name__,
daemon=True,
) )
self.thread.setDaemon(True)
self.started = threading.Event() self.started = threading.Event()
self.thread.start() self.thread.start()
self.started.wait() self.started.wait()
...@@ -715,7 +723,6 @@ class ClientThread(ClientRunner): ...@@ -715,7 +723,6 @@ class ClientThread(ClientRunner):
loop = None loop = None
try: try:
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
self.setup_delegation(loop) self.setup_delegation(loop)
self.started.set() self.started.set()
loop.run_forever() loop.run_forever()
...@@ -741,13 +748,13 @@ class ClientThread(ClientRunner): ...@@ -741,13 +748,13 @@ class ClientThread(ClientRunner):
def close(self): def close(self):
if not self.closed: if not self.closed:
self.closed = True self.closed = True
super().close() super(ClientThread, self).close()
self.loop.call_soon_threadsafe(self.loop.stop) self.loop.call_soon_threadsafe(self.loop.stop)
self.thread.join(9) self.thread.join(9)
if self.exception: if self.exception:
raise self.exception raise self.exception
class Promise: class Promise(object):
"""Lightweight future with a partial promise API. """Lightweight future with a partial promise API.
These are lighweight because they call callbacks synchronously These are lighweight because they call callbacks synchronously
......
...@@ -41,8 +41,13 @@ with: ...@@ -41,8 +41,13 @@ with:
in ZEO.StorageServer. in ZEO.StorageServer.
""" """
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
import asyncio
import asyncore import asyncore
import socket import socket
import threading import threading
......
import asyncio from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
import json import json
import logging import logging
import os import os
...@@ -62,7 +68,8 @@ class ServerProtocol(base.Protocol): ...@@ -62,7 +68,8 @@ class ServerProtocol(base.Protocol):
self.close() self.close()
else: else:
if protocol_version in self.protocols: if protocol_version in self.protocols:
logger.info("received handshake %r" % protocol_version) logger.info("received handshake %r" %
str(protocol_version.decode('ascii')))
self.protocol_version = protocol_version self.protocol_version = protocol_version
self.zeo_storage.notify_connected(self) self.zeo_storage.notify_connected(self)
else: else:
...@@ -142,7 +149,7 @@ def new_connection(loop, addr, socket, zeo_storage): ...@@ -142,7 +149,7 @@ def new_connection(loop, addr, socket, zeo_storage):
cr = loop.create_connection((lambda : protocol), sock=socket) cr = loop.create_connection((lambda : protocol), sock=socket)
asyncio.async(cr, loop=loop) asyncio.async(cr, loop=loop)
class Delay: class Delay(object):
"""Used to delay response to client for synchronous calls. """Used to delay response to client for synchronous calls.
When a synchronous call is made and the original handler returns When a synchronous call is made and the original handler returns
...@@ -202,36 +209,33 @@ class MTDelay(Delay): ...@@ -202,36 +209,33 @@ class MTDelay(Delay):
self.protocol.call_soon_threadsafe(Delay.error, self, exc_info) self.protocol.call_soon_threadsafe(Delay.error, self, exc_info)
class Acceptor: class Acceptor(object):
def __init__(self, storage_server, addr, ssl): def __init__(self, storage_server, addr, ssl):
self.storage_server = storage_server self.storage_server = storage_server
self.addr = addr self.addr = addr
self.ssl_context = ssl self.ssl_context = ssl
self.event_loop = loop = asyncio.new_event_loop() self.event_loop = loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
if isinstance(addr, tuple): if isinstance(addr, tuple):
cr = loop.create_server(self.factory, addr[0], addr[1], cr = loop.create_server(self.factory, addr[0], addr[1],
reuse_address=True, ssl=ssl) reuse_address=True, ssl=ssl)
else: else:
cr = loop.create_unix_server(self.factory, addr, ssl=ssl) cr = loop.create_unix_server(self.factory, addr, ssl=ssl)
f = asyncio.async(cr) f = asyncio.async(cr, loop=loop)
server = loop.run_until_complete(f)
@f.add_done_callback self.server = server
def listenting(f): if isinstance(addr, tuple) and addr[1] == 0:
server = f.result() addrs = [s.getsockname() for s in server.sockets]
self.server = server addrs = [a for a in addrs if len(a) == len(addr)]
if isinstance(addr, tuple) and addr[1] == 0: if addrs:
addrs = [s.getsockname() for s in server.sockets] self.addr = addrs[0]
addrs = [a for a in addrs if len(a) == len(addr)] else:
if addrs: self.addr = server.sockets[0].getsockname()[:len(addr)]
self.addr = addrs[0]
else:
self.addr = server.sockets[0].getsockname()[:len(addr)]
logger.info("listening on %s", str(addr))
loop.run_until_complete(f) logger.info("listening on %s", str(addr))
def factory(self): def factory(self):
try: try:
......
import asyncio from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
try:
ConnectionRefusedError
except NameError:
class ConnectionRefusedError(OSError):
pass
import pprint import pprint
class Loop: class Loop(object):
protocol = transport = None protocol = transport = None
...@@ -75,14 +87,14 @@ class Loop: ...@@ -75,14 +87,14 @@ class Loop:
def stop(self): def stop(self):
self.stopped = True self.stopped = True
class Handle: class Handle(object):
cancelled = False cancelled = False
def cancel(self): def cancel(self):
self.cancelled = True self.cancelled = True
class Transport: class Transport(object):
capacity = 1 << 64 capacity = 1 << 64
paused = False paused = False
...@@ -127,7 +139,7 @@ class Transport: ...@@ -127,7 +139,7 @@ class Transport:
def get_extra_info(self, name): def get_extra_info(self, name):
return self.extra[name] return self.extra[name]
class AsyncRPC: class AsyncRPC(object):
"""Adapt an asyncio API to an RPC to help hysterical tests """Adapt an asyncio API to an RPC to help hysterical tests
""" """
def __init__(self, api): def __init__(self, api):
...@@ -136,7 +148,7 @@ class AsyncRPC: ...@@ -136,7 +148,7 @@ class AsyncRPC:
def __getattr__(self, name): def __getattr__(self, name):
return lambda *a, **kw: self.api.call(name, *a, **kw) return lambda *a, **kw: self.api.call(name, *a, **kw)
class ClientRunner: class ClientRunner(object):
def __init__(self, addr, client, cache, storage, read_only, timeout, def __init__(self, addr, client, cache, storage, read_only, timeout,
**kw): **kw):
...@@ -152,7 +164,7 @@ class ClientRunner: ...@@ -152,7 +164,7 @@ class ClientRunner:
def start(self, wait=True): def start(self, wait=True):
pass pass
def call(self, method, *args, timeout=None): def call(self, method, *args, **kw):
return getattr(self, method)(*args) return getattr(self, method)(*args)
async = async_iter = call async = async_iter = call
......
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
from zope.testing import setupstack from zope.testing import setupstack
from concurrent.futures import Future from concurrent.futures import Future
from unittest import mock import mock
from ZODB.POSException import ReadOnlyError from ZODB.POSException import ReadOnlyError
import asyncio
import collections import collections
import logging import logging
import pdb import pdb
...@@ -27,7 +33,8 @@ class Base(object): ...@@ -27,7 +33,8 @@ class Base(object):
def unsized(self, data, unpickle=False): def unsized(self, data, unpickle=False):
result = [] result = []
while data: while data:
size, message, *data = data size, message = data[:2]
data = data[2:]
self.assertEqual(struct.unpack(">I", size)[0], len(message)) self.assertEqual(struct.unpack(">I", size)[0], len(message))
if unpickle: if unpickle:
message = decode(message) message = decode(message)
...@@ -622,7 +629,7 @@ class ClientTests(Base, setupstack.TestCase, ClientRunner): ...@@ -622,7 +629,7 @@ class ClientTests(Base, setupstack.TestCase, ClientRunner):
def test_ClientDisconnected_on_call_timeout(self): def test_ClientDisconnected_on_call_timeout(self):
wrapper, cache, loop, client, protocol, transport = self.start() wrapper, cache, loop, client, protocol, transport = self.start()
self.wait_for_result = super().wait_for_result self.wait_for_result = super(ClientTests, self).wait_for_result
self.assertRaises(ClientDisconnected, self.call, 'foo') self.assertRaises(ClientDisconnected, self.call, 'foo')
client.ready = False client.ready = False
self.assertRaises(ClientDisconnected, self.call, 'foo') self.assertRaises(ClientDisconnected, self.call, 'foo')
...@@ -686,7 +693,7 @@ class ClientTests(Base, setupstack.TestCase, ClientRunner): ...@@ -686,7 +693,7 @@ class ClientTests(Base, setupstack.TestCase, ClientRunner):
self.assertTrue(handle.cancelled) self.assertTrue(handle.cancelled)
class MemoryCache: class MemoryCache(object):
def __init__(self): def __init__(self):
# { oid -> [(start, end, data)] } # { oid -> [(start, end, data)] }
...@@ -837,7 +844,7 @@ def response(*data): ...@@ -837,7 +844,7 @@ def response(*data):
def sized(message): def sized(message):
return struct.pack(">I", len(message)) + message return struct.pack(">I", len(message)) + message
class Logging: class Logging(object):
def __init__(self, level=logging.ERROR): def __init__(self, level=logging.ERROR):
self.level = level self.level = level
......
...@@ -175,7 +175,7 @@ def stop_runner(thread, config, qin, qout, stop_timeout=9, pid=None): ...@@ -175,7 +175,7 @@ def stop_runner(thread, config, qin, qout, stop_timeout=9, pid=None):
def start_zeo_server(storage_conf=None, zeo_conf=None, port=None, keep=False, def start_zeo_server(storage_conf=None, zeo_conf=None, port=None, keep=False,
path='Data.fs', protocol=None, blob_dir=None, path='Data.fs', protocol=None, blob_dir=None,
suicide=True, debug=False, suicide=True, debug=False,
threaded=False, start_timeout=150, name=None, threaded=False, start_timeout=33, name=None,
): ):
"""Start a ZEO server in a separate process. """Start a ZEO server in a separate process.
...@@ -231,7 +231,15 @@ def start_zeo_server(storage_conf=None, zeo_conf=None, port=None, keep=False, ...@@ -231,7 +231,15 @@ def start_zeo_server(storage_conf=None, zeo_conf=None, port=None, keep=False,
) )
thread.daemon = True thread.daemon = True
thread.start() thread.start()
addr = qout.get(timeout=start_timeout) try:
addr = qout.get(timeout=start_timeout)
except Exception:
whine("SERVER FAILED TO START")
if thread.is_alive():
whine("Server thread/process is still running")
elif not threaded:
whine("Exit status", thread.exitcode)
raise
def stop(stop_timeout=99): def stop(stop_timeout=99):
stop_runner(thread, tmpfile, qin, qout, stop_timeout) stop_runner(thread, tmpfile, qin, qout, stop_timeout)
...@@ -423,3 +431,6 @@ def debug_logging(logger='ZEO', stream='stderr', level=logging.DEBUG): ...@@ -423,3 +431,6 @@ def debug_logging(logger='ZEO', stream='stderr', level=logging.DEBUG):
return stop return stop
def whine(*message):
print(*message, file=sys.stderr)
sys.stderr.flush()
...@@ -24,8 +24,8 @@ A current client should be able to connect to a old server: ...@@ -24,8 +24,8 @@ A current client should be able to connect to a old server:
>>> import ZEO, ZODB.blob, transaction >>> import ZEO, ZODB.blob, transaction
>>> db = ZEO.DB(addr, client='client', blob_dir='blobs') >>> db = ZEO.DB(addr, client='client', blob_dir='blobs')
>>> wait_connected(db.storage) >>> wait_connected(db.storage)
>>> db.storage.protocol_version >>> str(db.storage.protocol_version.decode('ascii'))
b'Z4' 'Z4'
>>> conn = db.open() >>> conn = db.open()
>>> conn.root().x = 0 >>> conn.root().x = 0
...@@ -103,9 +103,15 @@ A current client should be able to connect to a old server: ...@@ -103,9 +103,15 @@ A current client should be able to connect to a old server:
# Note that we'll have to pull some hijinks: # Note that we'll have to pull some hijinks:
# >>> import ZEO.asyncio.client # >>> db = ZEO.DB(addr, client='client', blob_dir='blobs')
# >>> old_protocols = ZEO.asyncio.client.Protocol.protocols # >>> str(db.storage.protocol_version.decode('ascii'))
# >>> ZEO.asyncio.client.Protocol.protocols = [b'Z4'] # 'Z4'
# >>> wait_connected(db.storage)
# >>> conn = db.open()
# >>> conn.root().x = 0
# >>> transaction.commit()
# >>> len(db.history(conn.root()._p_oid, 99))
# 2
# >>> db = ZEO.DB(addr, client='client', blob_dir='blobs') # >>> db = ZEO.DB(addr, client='client', blob_dir='blobs')
# >>> db.storage.protocol_version # >>> db.storage.protocol_version
......
...@@ -20,7 +20,7 @@ from ZODB.config import storageFromString ...@@ -20,7 +20,7 @@ from ZODB.config import storageFromString
from .forker import start_zeo_server from .forker import start_zeo_server
class ZEOConfigTest(setupstack.TestCase): class ZEOConfigTestBase(setupstack.TestCase):
setUp = setupstack.setUpDirectory setUp = setupstack.setUpDirectory
...@@ -86,6 +86,8 @@ class ZEOConfigTest(setupstack.TestCase): ...@@ -86,6 +86,8 @@ class ZEOConfigTest(setupstack.TestCase):
self.assertEqual(client.__name__, self.assertEqual(client.__name__,
name if name is not None else str(client._addr)) name if name is not None else str(client._addr))
class ZEOConfigTest(ZEOConfigTestBase):
def test_default_zeo_config(self, **client_settings): def test_default_zeo_config(self, **client_settings):
addr, stop = self.start_server() addr, stop = self.start_server()
......
...@@ -172,7 +172,7 @@ class GenericTestBase( ...@@ -172,7 +172,7 @@ class GenericTestBase(
self._storage.registerDB(DummyDB()) self._storage.registerDB(DummyDB())
def getZEOConfig(self): def getZEOConfig(self):
return forker.ZEOConfig(('', get_port(self))) return forker.ZEOConfig(('127.0.0.1', 0))
def _wrap_client(self, client): def _wrap_client(self, client):
return client return client
...@@ -475,7 +475,7 @@ class ZRPCConnectionTests(ZEO.tests.ConnectionTests.CommonSetupTearDown): ...@@ -475,7 +475,7 @@ class ZRPCConnectionTests(ZEO.tests.ConnectionTests.CommonSetupTearDown):
handler = InstalledHandler('ZEO.asyncio.client') handler = InstalledHandler('ZEO.asyncio.client')
import ZODB.POSException import ZODB.POSException
self.assertRaises(TypeError, self._storage.history, z64, None) self.assertRaises(TypeError, self._storage.history, z64, None)
self.assertTrue(" from server: builtins.TypeError" in str(handler)) self.assertTrue(re.search(" from server: .*TypeError", str(handler)))
# POSKeyErrors and ConflictErrors aren't logged: # POSKeyErrors and ConflictErrors aren't logged:
handler.clear() handler.clear()
...@@ -557,10 +557,9 @@ class CommonBlobTests: ...@@ -557,10 +557,9 @@ class CommonBlobTests:
t = transaction.Transaction() t = transaction.Transaction()
try: try:
self._storage.tpc_begin(t) self._storage.tpc_begin(t)
r1 = self._storage.storeBlob(oid, ZERO, data, tfname, '', t) self._storage.storeBlob(oid, ZERO, data, tfname, '', t)
r2 = self._storage.tpc_vote(t) self._storage.tpc_vote(t)
revid = handle_serials(oid, r1, r2) revid = self._storage.tpc_finish(t)
self._storage.tpc_finish(t)
except: except:
self._storage.tpc_abort(t) self._storage.tpc_abort(t)
raise raise
...@@ -598,10 +597,9 @@ class CommonBlobTests: ...@@ -598,10 +597,9 @@ class CommonBlobTests:
t = transaction.Transaction() t = transaction.Transaction()
try: try:
self._storage.tpc_begin(t) self._storage.tpc_begin(t)
r1 = self._storage.storeBlob(oid, ZERO, data, tfname, '', t) self._storage.storeBlob(oid, ZERO, data, tfname, '', t)
r2 = self._storage.tpc_vote(t) self._storage.tpc_vote(t)
serial = handle_serials(oid, r1, r2) serial = self._storage.tpc_finish(t)
self._storage.tpc_finish(t)
except: except:
self._storage.tpc_abort(t) self._storage.tpc_abort(t)
raise raise
...@@ -665,10 +663,9 @@ class BlobAdaptedFileStorageTests(FullGenericTests, CommonBlobTests): ...@@ -665,10 +663,9 @@ class BlobAdaptedFileStorageTests(FullGenericTests, CommonBlobTests):
t = transaction.Transaction() t = transaction.Transaction()
try: try:
self._storage.tpc_begin(t) self._storage.tpc_begin(t)
r1 = self._storage.storeBlob(oid, ZERO, data, tfname, '', t) self._storage.storeBlob(oid, ZERO, data, tfname, '', t)
r2 = self._storage.tpc_vote(t) self._storage.tpc_vote(t)
revid = handle_serials(oid, r1, r2) revid = self._storage.tpc_finish(t)
self._storage.tpc_finish(t)
except: except:
self._storage.tpc_abort(t) self._storage.tpc_abort(t)
raise raise
...@@ -690,15 +687,7 @@ class BlobAdaptedFileStorageTests(FullGenericTests, CommonBlobTests): ...@@ -690,15 +687,7 @@ class BlobAdaptedFileStorageTests(FullGenericTests, CommonBlobTests):
check_data(server_filename) check_data(server_filename)
# If we remove it from the cache and call loadBlob, it should # If we remove it from the cache and call loadBlob, it should
# come back. We can do this in many threads. We'll instrument # come back. We can do this in many threads.
# the method that is used to request data from teh server to
# verify that it is only called once.
sendBlob_org = ZEO.ServerStub.StorageServer.sendBlob
calls = []
def sendBlob(self, oid, serial):
calls.append((oid, serial))
sendBlob_org(self, oid, serial)
ZODB.blob.remove_committed(filename) ZODB.blob.remove_committed(filename)
returns = [] returns = []
...@@ -1515,7 +1504,7 @@ def can_use_empty_string_for_local_host_on_client(): ...@@ -1515,7 +1504,7 @@ def can_use_empty_string_for_local_host_on_client():
""" """
slow_test_classes = [ slow_test_classes = [
#BlobAdaptedFileStorageTests, BlobWritableCacheTests, BlobAdaptedFileStorageTests, BlobWritableCacheTests,
MappingStorageTests, DemoStorageTests, MappingStorageTests, DemoStorageTests,
FileStorageTests, FileStorageSSLTests, FileStorageTests, FileStorageSSLTests,
FileStorageHexTests, FileStorageClientHexTests, FileStorageHexTests, FileStorageClientHexTests,
......
...@@ -94,8 +94,7 @@ raised to the client: ...@@ -94,8 +94,7 @@ raised to the client:
>>> zs1.tpc_abort('0') # doctest: +ELLIPSIS >>> zs1.tpc_abort('0') # doctest: +ELLIPSIS
Error raised in delayed method Error raised in delayed method
Traceback (most recent call last): Traceback (most recent call last):
... ...ConflictError: ...
ZODB.POSException.ConflictError: ...
error 1 database conflict error ... error 1 database conflict error ...
The transaction is aborted by the server: The transaction is aborted by the server:
...@@ -227,13 +226,13 @@ We start a transaction and vote, this leads to getting the lock. ...@@ -227,13 +226,13 @@ We start a transaction and vote, this leads to getting the lock.
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
>>> tid1 = start_trans(zs1) >>> tid1 = start_trans(zs1)
>>> resolved1 = zs1.vote(tid1) # doctest: +ELLIPSIS >>> resolved1 = zs1.vote(tid1) # doctest: +ELLIPSIS
ZEO.StorageServer DEBUG ZEO.StorageServer DEBUG
(test-addr-1) ('1') lock: transactions waiting: 0 (test-addr-1) ('1') lock: transactions waiting: 0
ZEO.StorageServer BLATHER ZEO.StorageServer BLATHER
(test-addr-1) Preparing to commit transaction: 1 objects, 108 bytes (test-addr-1) Preparing to commit transaction: 1 objects, ... bytes
If another client tried to vote, it's lock request will be queued and If another client tried to vote, it's lock request will be queued and
a delay will be returned: a delay will be returned:
...@@ -242,7 +241,7 @@ a delay will be returned: ...@@ -242,7 +241,7 @@ a delay will be returned:
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
>>> tid2 = start_trans(zs2) >>> tid2 = start_trans(zs2)
>>> delay = zs2.vote(tid2) >>> delay = zs2.vote(tid2)
ZEO.StorageServer DEBUG ZEO.StorageServer DEBUG
...@@ -305,55 +304,55 @@ increased, so does the logging level: ...@@ -305,55 +304,55 @@ increased, so does the logging level:
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer DEBUG ZEO.StorageServer DEBUG
(test-addr-10) ('1') queue lock: transactions waiting: 2 (test-addr-10) ('1') queue lock: transactions waiting: 2
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer DEBUG ZEO.StorageServer DEBUG
(test-addr-11) ('1') queue lock: transactions waiting: 3 (test-addr-11) ('1') queue lock: transactions waiting: 3
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer WARNING ZEO.StorageServer WARNING
(test-addr-12) ('1') queue lock: transactions waiting: 4 (test-addr-12) ('1') queue lock: transactions waiting: 4
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer WARNING ZEO.StorageServer WARNING
(test-addr-13) ('1') queue lock: transactions waiting: 5 (test-addr-13) ('1') queue lock: transactions waiting: 5
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer WARNING ZEO.StorageServer WARNING
(test-addr-14) ('1') queue lock: transactions waiting: 6 (test-addr-14) ('1') queue lock: transactions waiting: 6
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer WARNING ZEO.StorageServer WARNING
(test-addr-15) ('1') queue lock: transactions waiting: 7 (test-addr-15) ('1') queue lock: transactions waiting: 7
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer WARNING ZEO.StorageServer WARNING
(test-addr-16) ('1') queue lock: transactions waiting: 8 (test-addr-16) ('1') queue lock: transactions waiting: 8
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer WARNING ZEO.StorageServer WARNING
(test-addr-17) ('1') queue lock: transactions waiting: 9 (test-addr-17) ('1') queue lock: transactions waiting: 9
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
ZEO.StorageServer CRITICAL ZEO.StorageServer CRITICAL
(test-addr-18) ('1') queue lock: transactions waiting: 10 (test-addr-18) ('1') queue lock: transactions waiting: 10
...@@ -484,7 +483,7 @@ ZEOStorage as closed and see if trying to get a lock cleans it up: ...@@ -484,7 +483,7 @@ ZEOStorage as closed and see if trying to get a lock cleans it up:
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
>>> tid1 = start_trans(zs1) >>> tid1 = start_trans(zs1)
>>> resolved1 = zs1.vote(tid1) # doctest: +ELLIPSIS >>> resolved1 = zs1.vote(tid1) # doctest: +ELLIPSIS
ZEO.StorageServer DEBUG ZEO.StorageServer DEBUG
...@@ -500,7 +499,7 @@ ZEOStorage as closed and see if trying to get a lock cleans it up: ...@@ -500,7 +499,7 @@ ZEOStorage as closed and see if trying to get a lock cleans it up:
ZEO.asyncio.base INFO ZEO.asyncio.base INFO
Connected server protocol Connected server protocol
ZEO.asyncio.server INFO ZEO.asyncio.server INFO
received handshake b'Z5' received handshake 'Z5'
>>> tid2 = start_trans(zs2) >>> tid2 = start_trans(zs2)
>>> resolved2 = zs2.vote(tid2) # doctest: +ELLIPSIS >>> resolved2 = zs2.vote(tid2) # doctest: +ELLIPSIS
ZEO.StorageServer DEBUG ZEO.StorageServer DEBUG
......
from .._compat import PY3
import mock import mock
import os import os
import ssl import ssl
...@@ -7,7 +9,7 @@ from ZODB.config import storageFromString ...@@ -7,7 +9,7 @@ from ZODB.config import storageFromString
from ..Exceptions import ClientDisconnected from ..Exceptions import ClientDisconnected
from .. import runzeo from .. import runzeo
from .testConfig import ZEOConfigTest from .testConfig import ZEOConfigTestBase
here = os.path.dirname(__file__) here = os.path.dirname(__file__)
server_cert = os.path.join(here, 'server.pem') server_cert = os.path.join(here, 'server.pem')
...@@ -17,7 +19,7 @@ serverpw_key = os.path.join(here, 'serverpw_key.pem') ...@@ -17,7 +19,7 @@ serverpw_key = os.path.join(here, 'serverpw_key.pem')
client_cert = os.path.join(here, 'client.pem') client_cert = os.path.join(here, 'client.pem')
client_key = os.path.join(here, 'client_key.pem') client_key = os.path.join(here, 'client_key.pem')
class SSLConfigTest(ZEOConfigTest): class SSLConfigTest(ZEOConfigTestBase):
def test_ssl_basic(self): def test_ssl_basic(self):
# This shows that configuring ssl has an actual effect on connections. # This shows that configuring ssl has an actual effect on connections.
...@@ -112,8 +114,13 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -112,8 +114,13 @@ class SSLConfigTest(ZEOConfigTest):
) )
stop() stop()
@mock.patch(('asyncio' if PY3 else 'trollius') + '.async')
@mock.patch(('asyncio' if PY3 else 'trollius') + '.set_event_loop')
@mock.patch(('asyncio' if PY3 else 'trollius') + '.new_event_loop')
class SSLConfigTestMockiavellian(ZEOConfigTestBase):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
def test_ssl_mockiavellian_server_no_ssl(self, factory): def test_ssl_mockiavellian_server_no_ssl(self, factory, *_):
server = create_server() server = create_server()
self.assertFalse(factory.called) self.assertFalse(factory.called)
self.assertEqual(server.acceptor.ssl_context, None) self.assertEqual(server.acceptor.ssl_context, None)
...@@ -134,13 +141,13 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -134,13 +141,13 @@ class SSLConfigTest(ZEOConfigTest):
self.assertEqual(context.check_hostname, check_hostname) self.assertEqual(context.check_hostname, check_hostname)
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
def test_ssl_mockiavellian_server_ssl_no_auth(self, factory): def test_ssl_mockiavellian_server_ssl_no_auth(self, factory, *_):
with self.assertRaises(SystemExit): with self.assertRaises(SystemExit):
# auth is required # auth is required
create_server(certificate=server_cert, key=server_key) create_server(certificate=server_cert, key=server_key)
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
def test_ssl_mockiavellian_server_ssl_auth_file(self, factory): def test_ssl_mockiavellian_server_ssl_auth_file(self, factory, *_):
server = create_server( server = create_server(
certificate=server_cert, key=server_key, authenticate=__file__) certificate=server_cert, key=server_key, authenticate=__file__)
context = server.acceptor.ssl_context context = server.acceptor.ssl_context
...@@ -148,7 +155,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -148,7 +155,7 @@ class SSLConfigTest(ZEOConfigTest):
server.close() server.close()
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
def test_ssl_mockiavellian_server_ssl_auth_dir(self, factory): def test_ssl_mockiavellian_server_ssl_auth_dir(self, factory, *_):
server = create_server( server = create_server(
certificate=server_cert, key=server_key, authenticate=here) certificate=server_cert, key=server_key, authenticate=here)
context = server.acceptor.ssl_context context = server.acceptor.ssl_context
...@@ -156,7 +163,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -156,7 +163,7 @@ class SSLConfigTest(ZEOConfigTest):
server.close() server.close()
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
def test_ssl_mockiavellian_server_ssl_pw(self, factory): def test_ssl_mockiavellian_server_ssl_pw(self, factory, *_):
server = create_server( server = create_server(
certificate=server_cert, certificate=server_cert,
key=server_key, key=server_key,
...@@ -170,7 +177,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -170,7 +177,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_no_ssl(self, ClientStorage, factory): def test_ssl_mockiavellian_client_no_ssl(self, ClientStorage, factory, *_):
client = ssl_client() client = ssl_client()
self.assertFalse('ssl' in ClientStorage.call_args[1]) self.assertFalse('ssl' in ClientStorage.call_args[1])
self.assertFalse('ssl_server_hostname' in ClientStorage.call_args[1]) self.assertFalse('ssl_server_hostname' in ClientStorage.call_args[1])
...@@ -178,7 +185,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -178,7 +185,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_server_signed( def test_ssl_mockiavellian_client_server_signed(
self, ClientStorage, factory self, ClientStorage, factory, *_
): ):
client = ssl_client(certificate=client_cert, key=client_key) client = ssl_client(certificate=client_cert, key=client_key)
context = ClientStorage.call_args[1]['ssl'] context = ClientStorage.call_args[1]['ssl']
...@@ -191,7 +198,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -191,7 +198,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_auth_dir( def test_ssl_mockiavellian_client_auth_dir(
self, ClientStorage, factory self, ClientStorage, factory, *_
): ):
client = ssl_client( client = ssl_client(
certificate=client_cert, key=client_key, authenticate=here) certificate=client_cert, key=client_key, authenticate=here)
...@@ -207,7 +214,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -207,7 +214,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_auth_file( def test_ssl_mockiavellian_client_auth_file(
self, ClientStorage, factory self, ClientStorage, factory, *_
): ):
client = ssl_client( client = ssl_client(
certificate=client_cert, key=client_key, authenticate=server_cert) certificate=client_cert, key=client_key, authenticate=server_cert)
...@@ -223,7 +230,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -223,7 +230,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_pw( def test_ssl_mockiavellian_client_pw(
self, ClientStorage, factory self, ClientStorage, factory, *_
): ):
client = ssl_client( client = ssl_client(
certificate=client_cert, key=client_key, certificate=client_cert, key=client_key,
...@@ -241,7 +248,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -241,7 +248,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_server_hostname( def test_ssl_mockiavellian_client_server_hostname(
self, ClientStorage, factory self, ClientStorage, factory, *_
): ):
client = ssl_client( client = ssl_client(
certificate=client_cert, key=client_key, authenticate=server_cert, certificate=client_cert, key=client_key, authenticate=server_cert,
...@@ -258,7 +265,7 @@ class SSLConfigTest(ZEOConfigTest): ...@@ -258,7 +265,7 @@ class SSLConfigTest(ZEOConfigTest):
@mock.patch('ssl.create_default_context') @mock.patch('ssl.create_default_context')
@mock.patch('ZEO.ClientStorage.ClientStorage') @mock.patch('ZEO.ClientStorage.ClientStorage')
def test_ssl_mockiavellian_client_check_hostname( def test_ssl_mockiavellian_client_check_hostname(
self, ClientStorage, factory self, ClientStorage, factory, *_
): ):
client = ssl_client( client = ssl_client(
certificate=client_cert, key=client_key, authenticate=server_cert, certificate=client_cert, key=client_key, authenticate=server_cert,
...@@ -320,7 +327,10 @@ pwfunc = lambda : '1234' ...@@ -320,7 +327,10 @@ pwfunc = lambda : '1234'
def test_suite(): def test_suite():
return unittest.makeSuite(SSLConfigTest) return unittest.TestSuite((
unittest.makeSuite(SSLConfigTest),
unittest.makeSuite(SSLConfigTestMockiavellian),
))
# Helpers for other tests: # Helpers for other tests:
......
[tox] [tox]
envlist = envlist =
py34,py35,simple py27,py34,py35,simple
[testenv] [testenv]
commands = commands =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment