Commit df4bd2cd authored by Jens Vagelpohl's avatar Jens Vagelpohl Committed by GitHub

Merge pull request #182 from zopefoundation/python38-and-39

#165 more accurate asyncio.CancelledError handling
parents afaf0eb9 0e70fd26
...@@ -27,6 +27,14 @@ jobs: ...@@ -27,6 +27,14 @@ jobs:
- ["3.7", "py37-mtacceptor-msgpack1"] - ["3.7", "py37-mtacceptor-msgpack1"]
- ["3.7", "py37-uvloop"] - ["3.7", "py37-uvloop"]
- ["3.7", "py37-zodbmaster"] - ["3.7", "py37-zodbmaster"]
- ["3.8", "py38"]
- ["3.8", "py38-mtacceptor"]
- ["3.8", "py38-mtacceptor-msgpack1"]
- ["3.8", "py38-uvloop"]
- ["3.9", "py39"]
- ["3.9", "py39-mtacceptor"]
- ["3.9", "py39-mtacceptor-msgpack1"]
- ["3.9", "py39-uvloop"]
- ["pypy2", "pypy"] - ["pypy2", "pypy"]
- ["pypy3", "pypy3"] - ["pypy3", "pypy3"]
- ["pypy3", "pypy3-mtacceptor"] - ["pypy3", "pypy3-mtacceptor"]
......
Changelog Changelog
========= =========
5.2.4 (unreleased) 5.3.0 (unreleased)
------------------ ------------------
- Add support for Python 3.8 and Python 3.9.
- Add more accurate error handling for ``asyncio.CancelledError``.
See `issue 165 <https://github.com/zopefoundation/ZEO/issues/165>`_.
- Fix bug related to blobs stored by ``ZEO`` - Fix bug related to blobs stored by ``ZEO``
`#150 <https://github.com/zopefoundation/ZEO/issues/150>`_. `#150 <https://github.com/zopefoundation/ZEO/issues/150>`_.
...@@ -26,7 +31,6 @@ Changelog ...@@ -26,7 +31,6 @@ Changelog
- Improve log message when client cache is out of sync with server. - Improve log message when client cache is out of sync with server.
See `issue 142 <https://github.com/zopefoundation/ZEO/issues/142>`_. See `issue 142 <https://github.com/zopefoundation/ZEO/issues/142>`_.
5.2.2 (2020-08-11) 5.2.2 (2020-08-11)
------------------ ------------------
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
version = '5.2.4.dev0' version = '5.3.0.dev0'
from setuptools import setup, find_packages from setuptools import setup, find_packages
import os import os
...@@ -48,6 +48,8 @@ Programming Language :: Python :: 3 ...@@ -48,6 +48,8 @@ Programming Language :: Python :: 3
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: PyPy
Topic :: Database Topic :: Database
......
...@@ -122,19 +122,25 @@ class Protocol(base.Protocol): ...@@ -122,19 +122,25 @@ class Protocol(base.Protocol):
cr = self.loop.create_unix_connection( cr = self.loop.create_unix_connection(
self.protocol_factory, self.addr, ssl=self.ssl) self.protocol_factory, self.addr, ssl=self.ssl)
# an ``asyncio.Future``
self._connecting = cr = asyncio.ensure_future(cr, loop=self.loop) self._connecting = cr = asyncio.ensure_future(cr, loop=self.loop)
@cr.add_done_callback @cr.add_done_callback
def done_connecting(future): def done_connecting(future):
if future.exception() is not None: if future.cancelled():
logger.info("Connection to %r failed, retrying, %s", logger.info("Connection to %r cancelled", self.addr)
self.addr, future.exception()) elif future.exception() is not None:
# keep trying logger.info("Connection to %r failed, %s",
if not self.closed: (self.addr, future.exception()))
self.loop.call_later( else: return
self.connect_poll + local_random.random(), # keep trying
self.connect, if not self.closed:
) logger.info("retry connecting %r", self.addr)
self.loop.call_later(
self.connect_poll + local_random.random(),
self.connect,
)
def connection_made(self, transport): def connection_made(self, transport):
super(Protocol, self).connection_made(transport) super(Protocol, self).connection_made(transport)
...@@ -482,10 +488,10 @@ class Client(object): ...@@ -482,10 +488,10 @@ class Client(object):
self.verify_invalidation_queue = [] # See comment in init :( self.verify_invalidation_queue = [] # See comment in init :(
protocol = self.protocol protocol = self.protocol
if server_tid is None:
server_tid = yield protocol.fut('lastTransaction')
try: try:
if server_tid is None:
server_tid = yield protocol.fut('lastTransaction')
cache = self.cache cache = self.cache
if cache: if cache:
cache_tid = cache.getLastTid() cache_tid = cache.getLastTid()
......
...@@ -251,12 +251,18 @@ class ZEOServer(object): ...@@ -251,12 +251,18 @@ class ZEOServer(object):
def loop_forever(self): def loop_forever(self):
if self.options.testing_exit_immediately: if self.options.testing_exit_immediately:
print("testing exit immediately") print("testing exit immediately")
sys.stdout.flush() # ensure truthful output order
else: else:
self.server.loop() self.server.loop()
def close_server(self): def close_server(self):
if self.server is not None: if self.server is not None:
self.server.close() self.server.close()
if getattr(self.options, "testing_exit_immediately", False):
acceptor = self.server.acceptor
if hasattr(acceptor, "event_loop"):
# usually, this happens automatically - but not for testing
acceptor.event_loop.close()
def handle_sigterm(self): def handle_sigterm(self):
log("terminated by SIGTERM") log("terminated by SIGTERM")
......
...@@ -1221,7 +1221,7 @@ def client_asyncore_thread_has_name(): ...@@ -1221,7 +1221,7 @@ def client_asyncore_thread_has_name():
""" """
def runzeo_without_configfile(): def runzeo_without_configfile():
""" r"""
>>> with open('runzeo', 'w') as r: >>> with open('runzeo', 'w') as r:
... _ = r.write(''' ... _ = r.write('''
... import sys ... import sys
...@@ -1231,10 +1231,13 @@ def runzeo_without_configfile(): ...@@ -1231,10 +1231,13 @@ def runzeo_without_configfile():
... ''' % sys.path) ... ''' % sys.path)
>>> import subprocess, re >>> import subprocess, re
>>> print(re.sub(br'\d\d+|[:]', b'', subprocess.Popen( >>> proc = subprocess.Popen(
... [sys.executable, 'runzeo', '-a:0', '-ft', '--test'], ... [sys.executable, 'runzeo', '-a:0', '-ft', '--test'],
... stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ... stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
... ).stdout.read()).decode('ascii')) ... )
>>> proc.wait()
0
>>> print(re.sub(br'\d\d+|[:]', b'', proc.stdout.read()).decode('ascii'))
... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
------ ------
--T INFO ZEO.runzeo () opening storage '1' using FileStorage --T INFO ZEO.runzeo () opening storage '1' using FileStorage
...@@ -1242,9 +1245,10 @@ def runzeo_without_configfile(): ...@@ -1242,9 +1245,10 @@ def runzeo_without_configfile():
--T INFO ZEO.StorageServer StorageServer created RW with storages 1RWt --T INFO ZEO.StorageServer StorageServer created RW with storages 1RWt
------ ------
--T INFO ZEO.asyncio... listening on ... --T INFO ZEO.asyncio... listening on ...
testing exit immediately
------ ------
--T INFO ZEO.StorageServer closing storage '1' --T INFO ZEO.StorageServer closing storage '1'
testing exit immediately >>> proc.stdout.close()
""" """
def close_client_storage_w_invalidations(): def close_client_storage_w_invalidations():
......
...@@ -4,6 +4,8 @@ envlist = ...@@ -4,6 +4,8 @@ envlist =
py35 py35
py36 py36
py37 py37
py38
py39
pypy pypy
pypy3 pypy3
simple simple
......
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