Commit 9a7a13bf authored by Hanno Schlichting's avatar Hanno Schlichting

Add direct support for ConflictError and TransientError retry logic.

This avoids the `repoze.retry` dependency for WSGIPublisher.
parent 683d6ce9
...@@ -16,6 +16,9 @@ Bugs Fixed ...@@ -16,6 +16,9 @@ Bugs Fixed
Features Added Features Added
++++++++++++++ ++++++++++++++
- Add support for ConflictError and TransientError retry logic directly
into WSGIPublisher.
- Add support for raising HTTPOK and HTTPRedirection exceptions and - Add support for raising HTTPOK and HTTPRedirection exceptions and
have them result in successful transactions. have them result in successful transactions.
......
...@@ -26,7 +26,6 @@ five.globalrequest==1.0 ...@@ -26,7 +26,6 @@ five.globalrequest==1.0
mechanize==0.2.5 mechanize==0.2.5
persistent==4.2.1 persistent==4.2.1
pytz==2016.6.1 pytz==2016.6.1
repoze.retry==1.4
six==1.10.0 six==1.10.0
sourcecodegen==0.6.14 sourcecodegen==0.6.14
transaction==1.6.1 transaction==1.6.1
......
...@@ -62,7 +62,6 @@ setup( ...@@ -62,7 +62,6 @@ setup(
'ZConfig >= 2.9.2', 'ZConfig >= 2.9.2',
'ZODB', 'ZODB',
'five.globalrequest', 'five.globalrequest',
'repoze.retry',
'setuptools', 'setuptools',
'six', 'six',
'sourcecodegen', 'sourcecodegen',
......
...@@ -21,11 +21,13 @@ import time ...@@ -21,11 +21,13 @@ import time
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager from AccessControl.SecurityManagement import noSecurityManager
import transaction import transaction
from transaction.interfaces import TransientError
from zExceptions import ( from zExceptions import (
HTTPOk, HTTPOk,
HTTPRedirection, HTTPRedirection,
Unauthorized, Unauthorized,
) )
from ZODB.POSException import ConflictError
from zope.event import notify from zope.event import notify
from zope.security.management import newInteraction, endInteraction from zope.security.management import newInteraction, endInteraction
from zope.publisher.skinnable import setDefaultSkin from zope.publisher.skinnable import setDefaultSkin
...@@ -197,7 +199,7 @@ def transaction_pubevents(request, tm=transaction.manager): ...@@ -197,7 +199,7 @@ def transaction_pubevents(request, tm=transaction.manager):
tm.begin() tm.begin()
notify(pubevents.PubStart(request)) notify(pubevents.PubStart(request))
try: try:
yield tm yield None
except (HTTPOk, HTTPRedirection) as exc: except (HTTPOk, HTTPRedirection) as exc:
ok_exception = exc ok_exception = exc
...@@ -259,6 +261,32 @@ def publish(request, module_info): ...@@ -259,6 +261,32 @@ def publish(request, module_info):
return response return response
def _publish_wsgi(request, response, module_info, start_response, stdout,
_publish=publish):
try:
with transaction_pubevents(request):
response = _publish(request, module_info)
except Unauthorized:
response._unauthorized()
except HTTPRedirection as exc:
# TODO: HTTPOk is only handled by the httpexceptions
# middleware, maybe it should be handled here.
response.redirect(exc)
# Start the WSGI server response
status, headers = response.finalize()
start_response(status, headers)
if (isinstance(response.body, IOBase) or
IUnboundStreamIterator.providedBy(response.body)):
result = response.body
else:
# If somebody used response.write, that data will be in the
# stdout StringIO, so we put that before the body.
result = (stdout.getvalue(), response.body)
return (response, result)
def publish_module(environ, start_response, def publish_module(environ, start_response,
_publish=publish, # only for testing _publish=publish, # only for testing
_response=None, _response=None,
...@@ -266,7 +294,8 @@ def publish_module(environ, start_response, ...@@ -266,7 +294,8 @@ def publish_module(environ, start_response,
_request=None, _request=None,
_request_factory=WSGIRequest, _request_factory=WSGIRequest,
_module_name='Zope2'): _module_name='Zope2'):
status = 200 module_info = get_module_info(_module_name)
result = ()
with closing(StringIO()) as stdout, closing(StringIO()) as stderr: with closing(StringIO()) as stdout, closing(StringIO()) as stderr:
response = (_response if _response is not None else response = (_response if _response is not None else
...@@ -277,31 +306,26 @@ def publish_module(environ, start_response, ...@@ -277,31 +306,26 @@ def publish_module(environ, start_response,
request = (_request if _request is not None else request = (_request if _request is not None else
_request_factory(environ['wsgi.input'], environ, response)) _request_factory(environ['wsgi.input'], environ, response))
with closing(request) as request: for i in range(getattr(request, 'retry_max_count', 3) + 1):
try: try:
with transaction_pubevents(request): response, result = _publish_wsgi(
response = _publish(request, get_module_info(_module_name)) request, response,
except Unauthorized: module_info, start_response,
response._unauthorized() stdout, _publish=_publish)
except HTTPRedirection as exc: break
# TODO: HTTPOk is only handled by the httpexceptions except (ConflictError, TransientError) as exc:
# middleware, maybe it should be handled here. if request.supports_retry():
response.redirect(exc) new_request = request.retry()
request.close()
# Start the WSGI server response request = new_request
status, headers = response.finalize() response = new_request.response
start_response(status, headers) else:
raise
if (isinstance(response.body, IOBase) or finally:
IUnboundStreamIterator.providedBy(response.body)): request.close()
result = response.body
else: for func in response.after_list:
# If somebody used response.write, that data will be in the func()
# stdout StringIO, so we put that before the body.
result = (stdout.getvalue(), response.body)
for func in response.after_list:
func()
# Return the result body iterable. # Return the result body iterable.
return result return result
...@@ -12,8 +12,6 @@ ...@@ -12,8 +12,6 @@
############################################################################## ##############################################################################
import unittest import unittest
import transaction
from ZPublisher.WSGIPublisher import get_module_info from ZPublisher.WSGIPublisher import get_module_info
......
...@@ -5,7 +5,6 @@ zope_conf = %(here)s/wsgi.conf ...@@ -5,7 +5,6 @@ zope_conf = %(here)s/wsgi.conf
[pipeline:main] [pipeline:main]
pipeline = pipeline =
egg:Zope2#httpexceptions egg:Zope2#httpexceptions
egg:repoze.retry#retry
zope zope
[server:main] [server:main]
......
...@@ -23,7 +23,6 @@ Products.ZCatalog = 4.0a2 ...@@ -23,7 +23,6 @@ Products.ZCatalog = 4.0a2
Products.ZCTextIndex = 4.0 Products.ZCTextIndex = 4.0
pytz = 2016.6.1 pytz = 2016.6.1
Record = 3.1 Record = 3.1
repoze.retry = 1.4
RestrictedPython = 3.6.0 RestrictedPython = 3.6.0
six = 1.10.0 six = 1.10.0
sourcecodegen = 0.6.14 sourcecodegen = 0.6.14
......
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