Commit 8705602c authored by Jim Fulton's avatar Jim Fulton

Python 2.7 support!!!

And holy crap, uncommented some tests that I'd commented early while
working on asyncio and forgot to uncomment before checking in.
parent 48defe9f
......@@ -2,6 +2,8 @@ language: python
sudo: false
matrix:
include:
- os: linux
python: 2.7
- os: linux
python: 3.4
- os: linux
......
......@@ -21,14 +21,31 @@ if sys.version_info < (2, 7):
print("This version of ZEO requires Python 2.7 or higher")
sys.exit(0)
if (3, 0) < sys.version_info < (3, 3):
print("This version of ZEO requires Python 3.3 or higher")
if (3, 0) < sys.version_info < (3, 4):
print("This version of ZEO requires Python 3.4 or higher")
sys.exit(0)
install_requires = [
'ZODB >= 5.0.0a1',
'six',
'transaction >= 1.6.0',
'persistent >= 4.1.0',
'zc.lockfile',
'ZConfig',
'zdaemon',
'zope.interface',
]
tests_require = ['zope.testing', 'manuel', 'random2', 'mock']
if sys.version_info[:2] < (3, ):
install_requires.extend(('futures', 'trollius'))
classifiers = """\
Intended Audience :: Developers
License :: OSI Approved :: Zope Public License
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
......@@ -92,8 +109,6 @@ def alltests():
_unittests_only(suite, mod.test_suite())
return suite
tests_require = ['zope.testing', 'manuel', 'random2', 'mock']
long_description = (
open('README.rst').read()
+ '\n' +
......@@ -114,16 +129,7 @@ setup(name="ZEO",
test_suite="__main__.alltests", # to support "setup.py test"
tests_require = tests_require,
extras_require = dict(test=tests_require),
install_requires = [
'ZODB >= 5.0.0a1',
'six',
'transaction >= 1.6.0',
'persistent >= 4.1.0',
'zc.lockfile',
'ZConfig',
'zdaemon',
'zope.interface',
],
install_requires = install_requires,
zip_safe = False,
entry_points = """
[console_scripts]
......
......@@ -19,7 +19,6 @@ file storage or Berkeley storage.
TODO: Need some basic access control-- a declaration of the methods
exported for invocation by the server.
"""
import asyncio
import codecs
import itertools
import logging
......
from struct import unpack
import asyncio
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
import logging
from struct import unpack
from .marshal import encoder
......
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
from ZEO.Exceptions import ClientDisconnected
from ZODB.ConflictResolution import ResolvedSerial
import asyncio
import concurrent.futures
import logging
import random
......@@ -251,7 +257,7 @@ class Protocol(base.Protocol):
self.heartbeat_handle = self.loop.call_later(
self.heartbeat_interval, self.heartbeat)
class Client:
class Client(object):
"""asyncio low-level ZEO client interface
"""
......@@ -589,7 +595,7 @@ class Client:
else:
return protocol.read_only
class ClientRunner:
class ClientRunner(object):
def set_options(self, addrs, wrapper, cache, storage_key, read_only,
timeout=30, disconnect_poll=1,
......@@ -608,7 +614,9 @@ class ClientRunner:
from concurrent.futures import Future
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()
call_soon_threadsafe(meth, result, *args)
return self.wait_for_result(result, timeout)
......@@ -624,8 +632,8 @@ class ClientRunner:
else:
raise
def call(self, method, *args, timeout=None):
return self.__call(self.call_threadsafe, method, args, timeout=timeout)
def call(self, method, *args, **kw):
return self.__call(self.call_threadsafe, method, args, **kw)
def call_future(self, method, *args):
# for tests
......@@ -702,8 +710,8 @@ class ClientThread(ClientRunner):
self.thread = threading.Thread(
target=self.run,
name="%s zeo client networking thread" % client.__name__,
daemon=True,
)
self.thread.setDaemon(True)
self.started = threading.Event()
self.thread.start()
self.started.wait()
......@@ -741,13 +749,13 @@ class ClientThread(ClientRunner):
def close(self):
if not self.closed:
self.closed = True
super().close()
super(ClientThread, self).close()
self.loop.call_soon_threadsafe(self.loop.stop)
self.thread.join(9)
if self.exception:
raise self.exception
class Promise:
class Promise(object):
"""Lightweight future with a partial promise API.
These are lighweight because they call callbacks synchronously
......
......@@ -41,8 +41,13 @@ with:
in ZEO.StorageServer.
"""
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
import asyncio
import asyncore
import socket
import threading
......
import asyncio
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
import json
import logging
import os
......@@ -62,7 +68,8 @@ class ServerProtocol(base.Protocol):
self.close()
else:
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.zeo_storage.notify_connected(self)
else:
......@@ -142,7 +149,7 @@ def new_connection(loop, addr, socket, zeo_storage):
cr = loop.create_connection((lambda : protocol), sock=socket)
asyncio.async(cr, loop=loop)
class Delay:
class Delay(object):
"""Used to delay response to client for synchronous calls.
When a synchronous call is made and the original handler returns
......@@ -203,7 +210,7 @@ class MTDelay(Delay):
self.protocol.call_soon_threadsafe(Delay.error, self, exc_info)
class Acceptor:
class Acceptor(object):
def __init__(self, storage_server, addr, ssl):
self.storage_server = storage_server
......
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
class Loop:
class Loop(object):
protocol = transport = None
......@@ -75,14 +87,14 @@ class Loop:
def stop(self):
self.stopped = True
class Handle:
class Handle(object):
cancelled = False
def cancel(self):
self.cancelled = True
class Transport:
class Transport(object):
capacity = 1 << 64
paused = False
......@@ -127,7 +139,7 @@ class Transport:
def get_extra_info(self, name):
return self.extra[name]
class AsyncRPC:
class AsyncRPC(object):
"""Adapt an asyncio API to an RPC to help hysterical tests
"""
def __init__(self, api):
......@@ -136,7 +148,7 @@ class AsyncRPC:
def __getattr__(self, name):
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,
**kw):
......@@ -152,7 +164,7 @@ class ClientRunner:
def start(self, wait=True):
pass
def call(self, method, *args, timeout=None):
def call(self, method, *args, **kw):
return getattr(self, method)(*args)
async = async_iter = call
......
from .._compat import PY3
if PY3:
import asyncio
else:
import trollius as asyncio
from zope.testing import setupstack
from concurrent.futures import Future
from unittest import mock
import mock
from ZODB.POSException import ReadOnlyError
import asyncio
import collections
import logging
import pdb
......@@ -27,7 +33,8 @@ class Base(object):
def unsized(self, data, unpickle=False):
result = []
while data:
size, message, *data = data
size, message = data[:2]
data = data[2:]
self.assertEqual(struct.unpack(">I", size)[0], len(message))
if unpickle:
message = decode(message)
......@@ -622,7 +629,7 @@ class ClientTests(Base, setupstack.TestCase, ClientRunner):
def test_ClientDisconnected_on_call_timeout(self):
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')
client.ready = False
self.assertRaises(ClientDisconnected, self.call, 'foo')
......@@ -686,7 +693,7 @@ class ClientTests(Base, setupstack.TestCase, ClientRunner):
self.assertTrue(handle.cancelled)
class MemoryCache:
class MemoryCache(object):
def __init__(self):
# { oid -> [(start, end, data)] }
......@@ -837,7 +844,7 @@ def response(*data):
def sized(message):
return struct.pack(">I", len(message)) + message
class Logging:
class Logging(object):
def __init__(self, level=logging.ERROR):
self.level = level
......
......@@ -24,8 +24,8 @@ A current client should be able to connect to a old server:
>>> import ZEO, ZODB.blob, transaction
>>> db = ZEO.DB(addr, client='client', blob_dir='blobs')
>>> wait_connected(db.storage)
>>> db.storage.protocol_version
b'Z4'
>>> str(db.storage.protocol_version.decode('ascii'))
'Z4'
>>> conn = db.open()
>>> conn.root().x = 0
......@@ -105,8 +105,8 @@ Note that we'll have to pull some hijinks:
>>> ZEO.asyncio.client.Protocol.protocols = [b'Z4']
>>> db = ZEO.DB(addr, client='client', blob_dir='blobs')
>>> db.storage.protocol_version
b'Z4'
>>> str(db.storage.protocol_version.decode('ascii'))
'Z4'
>>> wait_connected(db.storage)
>>> conn = db.open()
>>> conn.root().x = 0
......
......@@ -471,7 +471,7 @@ class ZRPCConnectionTests(ZEO.tests.ConnectionTests.CommonSetupTearDown):
handler = InstalledHandler('ZEO.asyncio.client')
import ZODB.POSException
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:
handler.clear()
......@@ -684,15 +684,7 @@ class BlobAdaptedFileStorageTests(FullGenericTests, CommonBlobTests):
check_data(server_filename)
# If we remove it from the cache and call loadBlob, it should
# come back. We can do this in many threads. We'll instrument
# 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)
# come back. We can do this in many threads.
ZODB.blob.remove_committed(filename)
returns = []
......@@ -1510,7 +1502,7 @@ def can_use_empty_string_for_local_host_on_client():
"""
slow_test_classes = [
#BlobAdaptedFileStorageTests, BlobWritableCacheTests,
BlobAdaptedFileStorageTests, BlobWritableCacheTests,
MappingStorageTests, DemoStorageTests,
FileStorageTests, FileStorageSSLTests,
FileStorageHexTests, FileStorageClientHexTests,
......
......@@ -218,13 +218,13 @@ We start a transaction and vote, this leads to getting the lock.
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
>>> tid1 = start_trans(zs1)
>>> zs1.vote(tid1) # doctest: +ELLIPSIS
ZEO.StorageServer DEBUG
(test-addr-1) ('1') lock: transactions waiting: 0
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
a delay will be returned:
......@@ -233,7 +233,7 @@ a delay will be returned:
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
>>> tid2 = start_trans(zs2)
>>> delay = zs2.vote(tid2)
ZEO.StorageServer DEBUG
......@@ -296,55 +296,55 @@ increased, so does the logging level:
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer DEBUG
(test-addr-10) ('1') queue lock: transactions waiting: 2
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer DEBUG
(test-addr-11) ('1') queue lock: transactions waiting: 3
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer WARNING
(test-addr-12) ('1') queue lock: transactions waiting: 4
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer WARNING
(test-addr-13) ('1') queue lock: transactions waiting: 5
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer WARNING
(test-addr-14) ('1') queue lock: transactions waiting: 6
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer WARNING
(test-addr-15) ('1') queue lock: transactions waiting: 7
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer WARNING
(test-addr-16) ('1') queue lock: transactions waiting: 8
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer WARNING
(test-addr-17) ('1') queue lock: transactions waiting: 9
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
ZEO.StorageServer CRITICAL
(test-addr-18) ('1') queue lock: transactions waiting: 10
......@@ -475,7 +475,7 @@ ZEOStorage as closed and see if trying to get a lock cleans it up:
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
>>> tid1 = start_trans(zs1)
>>> zs1.vote(tid1) # doctest: +ELLIPSIS
ZEO.StorageServer DEBUG
......@@ -491,7 +491,7 @@ ZEOStorage as closed and see if trying to get a lock cleans it up:
ZEO.asyncio.base INFO
Connected server protocol
ZEO.asyncio.server INFO
received handshake b'Z5'
received handshake 'Z5'
>>> tid2 = start_trans(zs2)
>>> zs2.vote(tid2) # doctest: +ELLIPSIS
ZEO.StorageServer DEBUG
......
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