Commit 10dbbaee authored by Hanno Schlichting's avatar Hanno Schlichting

Use context managers to simplify the publish event and transaction logic.

Also drive by fix for ZopePageTemplate.
parent eefbc5a0
...@@ -174,7 +174,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable, ...@@ -174,7 +174,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
source_dot_xml = Src() source_dot_xml = Src()
security.declareProtected(change_page_templates, 'pt_editAction') security.declareProtected(change_page_templates, 'pt_editAction')
def pt_editAction(self, REQUEST, title, text, content_type, expand): def pt_editAction(self, REQUEST, title, text, content_type, expand=0):
"""Change the title and document.""" """Change the title and document."""
if self.wl_isLocked(): if self.wl_isLocked():
......
...@@ -12,11 +12,13 @@ ...@@ -12,11 +12,13 @@
############################################################################## ##############################################################################
""" Python Object Publisher -- Publish Python objects on web servers """ Python Object Publisher -- Publish Python objects on web servers
""" """
from contextlib import contextmanager, closing
from cStringIO import StringIO from cStringIO import StringIO
import sys import sys
from thread import allocate_lock from thread import allocate_lock
import time import time
from AccessControl.SecurityManagement import noSecurityManager
import transaction import transaction
from zExceptions import ( from zExceptions import (
HTTPOk, HTTPOk,
...@@ -199,19 +201,49 @@ class WSGIResponse(HTTPResponse): ...@@ -199,19 +201,49 @@ class WSGIResponse(HTTPResponse):
raise NotImplementedError raise NotImplementedError
@contextmanager
def transaction_pubevents(tm, request):
ok_exception = None
try:
setDefaultSkin(request)
newInteraction()
tm.begin()
notify(pubevents.PubStart(request))
try:
yield tm
except (HTTPOk, HTTPRedirection) as exc:
ok_exception = exc
notify(pubevents.PubBeforeCommit(request))
if tm.isDoomed():
tm.abort()
else:
tm.commit()
notify(pubevents.PubSuccess(request))
except Exception:
exc_info = sys.exc_info()
notify(pubevents.PubBeforeAbort(
request, exc_info, request.supports_retry()))
tm.abort()
notify(pubevents.PubFailure(
request, exc_info, request.supports_retry()))
raise
finally:
endInteraction()
if ok_exception is not None:
raise ok_exception
def publish(request, module_info): def publish(request, module_info):
(bobo_before, (bobo_before,
bobo_after, bobo_after,
object, obj,
realm, realm,
debug_mode, debug_mode,
err_hook, err_hook,
validated_hook, validated_hook,
transactions_manager) = module_info transactions_manager) = module_info
notify(pubevents.PubStart(request))
newInteraction()
try:
request.processInputs() request.processInputs()
response = request.response response = request.response
...@@ -224,27 +256,20 @@ def publish(request, module_info): ...@@ -224,27 +256,20 @@ def publish(request, module_info):
if realm and not request.get('REMOTE_USER', None): if realm and not request.get('REMOTE_USER', None):
response.realm = realm response.realm = realm
noSecurityManager()
if bobo_before is not None: if bobo_before is not None:
bobo_before() bobo_before()
# Get the path list. # Get the path list.
# According to RFC1738 a trailing space in the path is valid. # According to RFC1738 a trailing space in the path is valid.
path = request.get('PATH_INFO') path = request.get('PATH_INFO')
request['PARENTS'] = [obj]
request['PARENTS'] = [object] obj = request.traverse(path, validated_hook=validated_hook)
if transactions_manager:
transactions_manager.begin()
object = request.traverse(path, validated_hook=validated_hook)
notify(pubevents.PubAfterTraversal(request)) notify(pubevents.PubAfterTraversal(request))
recordMetaData(obj, request)
if transactions_manager: result = mapply(obj,
recordMetaData(object, request)
ok_exception = None
try:
result = mapply(object,
request.args, request.args,
request, request,
call_object, call_object,
...@@ -253,26 +278,9 @@ def publish(request, module_info): ...@@ -253,26 +278,9 @@ def publish(request, module_info):
dont_publish_class, dont_publish_class,
request, request,
bind=1) bind=1)
except (HTTPOk, HTTPRedirection) as exc:
# 2xx and 3xx responses raised as exceptions are considered
# successful.
ok_exception = exc
else:
if result is not response: if result is not response:
response.setBody(result) response.setBody(result)
notify(pubevents.PubBeforeCommit(request))
if transactions_manager:
transactions_manager.commit()
notify(pubevents.PubSuccess(request))
if ok_exception:
raise ok_exception
finally:
endInteraction()
return response return response
...@@ -288,8 +296,8 @@ def publish_module(environ, start_response, ...@@ -288,8 +296,8 @@ def publish_module(environ, start_response,
transactions_manager = module_info[7] transactions_manager = module_info[7]
status = 200 status = 200
stdout = StringIO()
stderr = StringIO() with closing(StringIO()) as stdout, closing(StringIO()) as stderr:
if _response is None: if _response is None:
response = _response_factory(stdout=stdout, stderr=stderr) response = _response_factory(stdout=stdout, stderr=stderr)
else: else:
...@@ -298,32 +306,20 @@ def publish_module(environ, start_response, ...@@ -298,32 +306,20 @@ def publish_module(environ, start_response,
response._server_version = environ.get('SERVER_SOFTWARE') response._server_version = environ.get('SERVER_SOFTWARE')
if _request is None: if _request is None:
request = _request_factory(environ['wsgi.input'], environ, response) request = _request_factory(
environ['wsgi.input'], environ, response)
else: else:
request = _request request = _request
setDefaultSkin(request) with closing(request) as request:
try:
try: try:
with transaction_pubevents(transactions_manager, request):
response = _publish(request, module_info) response = _publish(request, module_info)
except Exception:
try:
exc_info = sys.exc_info()
notify(pubevents.PubBeforeAbort(
request, exc_info, request.supports_retry()))
if transactions_manager:
transactions_manager.abort()
notify(pubevents.PubFailure(
request, exc_info, request.supports_retry()))
finally:
del exc_info
raise
except Unauthorized: except Unauthorized:
response._unauthorized() response._unauthorized()
except HTTPRedirection as exc: except HTTPRedirection as exc:
# TODO: HTTPOk is only handled by the httpexceptions
# middleware, maybe it should be handled here.
response.redirect(exc) response.redirect(exc)
# Start the WSGI server response # Start the WSGI server response
...@@ -331,17 +327,14 @@ def publish_module(environ, start_response, ...@@ -331,17 +327,14 @@ def publish_module(environ, start_response,
start_response(status, headers) start_response(status, headers)
body = response.body body = response.body
if (isinstance(body, IOBase) or
if isinstance(body, IOBase) or IUnboundStreamIterator.providedBy(body): IUnboundStreamIterator.providedBy(body)):
result = body result = body
else: else:
# If somebody used response.write, that data will be in the # If somebody used response.write, that data will be in the
# stdout StringIO, so we put that before the body. # stdout StringIO, so we put that before the body.
result = (stdout.getvalue(), response.body) result = (stdout.getvalue(), response.body)
request.close() # this aborts the transaction!
stdout.close()
for func in response.after_list: for func in response.after_list:
func() func()
......
...@@ -155,7 +155,6 @@ def startup(): ...@@ -155,7 +155,6 @@ def startup():
Zope2.zpublisher_transactions_manager = transaction.manager Zope2.zpublisher_transactions_manager = transaction.manager
Zope2.zpublisher_exception_hook = zpublisher_exception_hook Zope2.zpublisher_exception_hook = zpublisher_exception_hook
Zope2.zpublisher_validated_hook = validated_hook Zope2.zpublisher_validated_hook = validated_hook
Zope2.__bobo_before__ = noSecurityManager
def validated_hook(request, user): def validated_hook(request, user):
......
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