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,79 +201,85 @@ class WSGIResponse(HTTPResponse): ...@@ -199,79 +201,85 @@ 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)) request.processInputs()
newInteraction() response = request.response
try:
request.processInputs()
response = request.response
if bobo_after is not None:
response.after_list += (bobo_after,)
if debug_mode:
response.debug_mode = debug_mode
if realm and not request.get('REMOTE_USER', None):
response.realm = realm
if bobo_before is not None:
bobo_before()
# Get the path list. if bobo_after is not None:
# According to RFC1738 a trailing space in the path is valid. response.after_list += (bobo_after,)
path = request.get('PATH_INFO')
request['PARENTS'] = [object] if debug_mode:
response.debug_mode = debug_mode
if transactions_manager: if realm and not request.get('REMOTE_USER', None):
transactions_manager.begin() response.realm = realm
object = request.traverse(path, validated_hook=validated_hook) noSecurityManager()
notify(pubevents.PubAfterTraversal(request)) if bobo_before is not None:
bobo_before()
if transactions_manager: # Get the path list.
recordMetaData(object, request) # According to RFC1738 a trailing space in the path is valid.
path = request.get('PATH_INFO')
request['PARENTS'] = [obj]
ok_exception = None obj = request.traverse(path, validated_hook=validated_hook)
try: notify(pubevents.PubAfterTraversal(request))
result = mapply(object, recordMetaData(obj, request)
request.args,
request,
call_object,
1,
missing_name,
dont_publish_class,
request,
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:
response.setBody(result)
notify(pubevents.PubBeforeCommit(request)) result = mapply(obj,
request.args,
if transactions_manager: request,
transactions_manager.commit() call_object,
notify(pubevents.PubSuccess(request)) 1,
missing_name,
if ok_exception: dont_publish_class,
raise ok_exception request,
bind=1)
finally: if result is not response:
endInteraction() response.setBody(result)
return response return response
...@@ -288,59 +296,44 @@ def publish_module(environ, start_response, ...@@ -288,59 +296,44 @@ def publish_module(environ, start_response,
transactions_manager = module_info[7] transactions_manager = module_info[7]
status = 200 status = 200
stdout = StringIO()
stderr = StringIO()
if _response is None:
response = _response_factory(stdout=stdout, stderr=stderr)
else:
response = _response
response._http_version = environ['SERVER_PROTOCOL'].split('/')[1]
response._server_version = environ.get('SERVER_SOFTWARE')
if _request is None:
request = _request_factory(environ['wsgi.input'], environ, response)
else:
request = _request
setDefaultSkin(request)
try: with closing(StringIO()) as stdout, closing(StringIO()) as stderr:
try: if _response is None:
response = _publish(request, module_info) response = _response_factory(stdout=stdout, stderr=stderr)
except Exception: else:
response = _response
response._http_version = environ['SERVER_PROTOCOL'].split('/')[1]
response._server_version = environ.get('SERVER_SOFTWARE')
if _request is None:
request = _request_factory(
environ['wsgi.input'], environ, response)
else:
request = _request
with closing(request) as request:
try: try:
exc_info = sys.exc_info() with transaction_pubevents(transactions_manager, request):
notify(pubevents.PubBeforeAbort( response = _publish(request, module_info)
request, exc_info, request.supports_retry())) except Unauthorized:
response._unauthorized()
if transactions_manager: except HTTPRedirection as exc:
transactions_manager.abort() # TODO: HTTPOk is only handled by the httpexceptions
# middleware, maybe it should be handled here.
notify(pubevents.PubFailure( response.redirect(exc)
request, exc_info, request.supports_retry()))
finally: # Start the WSGI server response
del exc_info status, headers = response.finalize()
raise start_response(status, headers)
except Unauthorized:
response._unauthorized() body = response.body
except HTTPRedirection as exc: if (isinstance(body, IOBase) or
response.redirect(exc) IUnboundStreamIterator.providedBy(body)):
result = body
# Start the WSGI server response else:
status, headers = response.finalize() # If somebody used response.write, that data will be in the
start_response(status, headers) # stdout StringIO, so we put that before the body.
result = (stdout.getvalue(), response.body)
body = response.body
if isinstance(body, IOBase) or IUnboundStreamIterator.providedBy(body):
result = 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)
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