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