[Zope-Checkins]
SVN: Zope/branches/publication-refactor2/lib/python/ZPublisher/
commit the merge from the old publication-refactor branch.
Michael Kerrin
michael.kerrin at openapp.biz
Fri Apr 21 12:38:40 EDT 2006
Log message for revision 67229:
commit the merge from the old publication-refactor branch.
Changed:
U Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseRequest.py
U Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseResponse.py
U Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPRequest.py
U Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPResponse.py
A Zope/branches/publication-refactor2/lib/python/ZPublisher/Publication.py
U Zope/branches/publication-refactor2/lib/python/ZPublisher/Publish.py
U Zope/branches/publication-refactor2/lib/python/ZPublisher/Test.py
U Zope/branches/publication-refactor2/lib/python/ZPublisher/tests/testPublish.py
-=-
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseRequest.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseRequest.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseRequest.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -14,13 +14,13 @@
$Id$
"""
-from urllib import quote
+
import xmlrpc
+from urllib import quote
+from zope.publisher.interfaces import NotFound
+
from zExceptions import Forbidden
-from zope.event import notify
-from zope.app.publication.interfaces import EndRequestEvent
-
UNSPECIFIED_ROLES=''
try:
@@ -80,12 +80,23 @@
"""
if other is None: other=kw
else: other.update(kw)
- self.other=other
+ self.other = other
+ self.environ = {}
+ # Publication will be overriden by ZPublisher.Publish,
+ # publish(), we assume that by default it's the "Zope2"
+ # module. This is done so that tests using BaseRequest
+ # directly don't break.
+ from ZPublisher.Publication import get_publication
+ self.publication = get_publication(module_name="Zope2")
+ def setPublication(self, publication):
+ self.publication = publication
+
def close(self):
self.other.clear()
- self._held=None
- notify(EndRequestEvent(None, self))
+ self._held = None
+ self.publication.endRequest(self, None)
+ self.publication = None
def processInputs(self):
"""Do any input processing that could raise errors
@@ -155,7 +166,7 @@
def __contains__(self, key):
return self.has_key(key)
-
+
def keys(self):
keys = {}
keys.update(self.common)
@@ -219,40 +230,19 @@
if method=='GET' or method=='POST' and not isinstance(response,
xmlrpc.Response):
- # Probably a browser
- no_acquire_flag=0
# index_html is still the default method, only any object can
# override it by implementing its own __browser_default__ method
method = 'index_html'
- elif self.maybe_webdav_client:
- # Probably a WebDAV client.
- no_acquire_flag=1
- else:
- no_acquire_flag=0
URL=request['URL']
- parents=request['PARENTS']
- object=parents[-1]
+ parents = request['PARENTS']
+ object = parents[-1]
del parents[:]
- roles = getRoles(None, None, object, UNSPECIFIED_ROLES)
-
- # if the top object has a __bobo_traverse__ method, then use it
- # to possibly traverse to an alternate top-level object.
- if hasattr(object,'__bobo_traverse__'):
- try:
- object=object.__bobo_traverse__(request)
- roles = getRoles(None, None, object, UNSPECIFIED_ROLES)
- except: pass
-
if not path and not method:
return response.forbiddenError(self['URL'])
- # Traverse the URL to find the object:
- if hasattr(object, '__of__'):
- # Try to bind the top-level object to the request
- # This is how you get 'self.REQUEST'
- object=object.__of__(RequestContainer(REQUEST=request))
+ roles = getRoles(None, None, object, UNSPECIFIED_ROLES)
parents.append(object)
steps=self.steps
@@ -270,9 +260,7 @@
# We build parents in the wrong order, so we
# need to make sure we reverse it when we're doe.
while 1:
- bpth = getattr(object, '__before_publishing_traverse__', None)
- if bpth is not None:
- bpth(object, self)
+ self.publication.callTraversalHooks(self, object)
path = request.path = request['TraversalRequestNameStack']
# Check for method:
@@ -318,72 +306,16 @@
"Object name begins with an underscore at: %s" % URL)
else: return response.forbiddenError(entry_name)
- if hasattr(object,'__bobo_traverse__'):
- try:
- subobject=object.__bobo_traverse__(request,entry_name)
- if type(subobject) is type(()) and len(subobject) > 1:
- # Add additional parents into the path
- parents[-1:] = list(subobject[:-1])
- object, subobject = subobject[-2:]
- except (AttributeError, KeyError):
- if debug_mode:
- return response.debugError(
- "Cannot locate object at: %s" % URL)
- else:
- return response.notFoundError(URL)
- else:
- try:
- # Note - no_acquire_flag is necessary to support
- # things like DAV. We have to make sure
- # that the target object is not acquired
- # if the request_method is other than GET
- # or POST. Otherwise, you could never use
- # PUT to add a new object named 'test' if
- # an object 'test' existed above it in the
- # heirarchy -- you'd always get the
- # existing object :(
+ try:
+ subobject = self.publication.traverseName(
+ self, object, entry_name)
+ except NotFound:
+ if debug_mode:
+ return response.debugError(
+ "Cannot locate object at: %s" % URL)
+ else:
+ return response.notFoundError(URL)
- if (no_acquire_flag and len(path) == 0 and
- hasattr(object, 'aq_base')):
- if hasattr(object.aq_base, entry_name):
- subobject=getattr(object, entry_name)
- else: raise AttributeError, entry_name
- else: subobject=getattr(object, entry_name)
- except AttributeError:
- got=1
- try: subobject=object[entry_name]
- except (KeyError, IndexError,
- TypeError, AttributeError):
- if debug_mode:
- return response.debugError(
- "Cannot locate object at: %s" % URL)
- else:
- return response.notFoundError(URL)
-
- # Ensure that the object has a docstring, or that the parent
- # object has a pseudo-docstring for the object. Objects that
- # have an empty or missing docstring are not published.
- doc = getattr(subobject, '__doc__', None)
- if doc is None:
- doc = getattr(object, '%s__doc__' % entry_name, None)
- if not doc:
- return response.debugError(
- "The object at %s has an empty or missing " \
- "docstring. Objects must have a docstring to be " \
- "published." % URL
- )
-
- # Hack for security: in Python 2.2.2, most built-in types
- # gained docstrings that they didn't have before. That caused
- # certain mutable types (dicts, lists) to become publishable
- # when they shouldn't be. The following check makes sure that
- # the right thing happens in both 2.2.2+ and earlier versions.
-
- if not typeCheck(subobject):
- return response.debugError(
- "The object at %s is not publishable." % URL
- )
-
roles = getRoles(
object, (not got) and entry_name or None, subobject,
roles)
@@ -395,7 +327,7 @@
steps.append(entry_name)
finally:
parents.reverse()
-
+
# After traversal post traversal hooks aren't available anymore
del self._post_traverse
@@ -481,7 +413,7 @@
def post_traverse(self, f, args=()):
"""Add a callable object and argument tuple to be post-traversed.
-
+
If traversal and authentication succeed, each post-traversal
pair is processed in the order in which they were added.
Each argument tuple is passed to its callable. If a callable
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseResponse.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseResponse.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/BaseResponse.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -54,6 +54,10 @@
"""Output the response body"""
self.stdout.write(str(self))
+ def setResult(self, result):
+ """IResponse"""
+ self.setBody(result)
+
def setBody(self, body):
self.body = body
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPRequest.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPRequest.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPRequest.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -239,6 +239,8 @@
return self._client_addr
def __init__(self, stdin, environ, response, clean=0):
+ # Call base class __init__.
+ BaseRequest.__init__(self, RESPONSE=response)
self._orig_env=environ
# Avoid the overhead of scrubbing the environment in the
# case of request cloning for traversal purposes. If the
@@ -256,7 +258,7 @@
have_env=environ.has_key
get_env=environ.get
self.response=response
- other=self.other={'RESPONSE': response}
+ other = self.other
self.form={}
self.taintedform={}
self.steps=[]
@@ -1070,6 +1072,8 @@
clone['PARENTS']=[self['PARENTS'][-1]]
return clone
+ # XXX This is called getHeader in Response, and in Zope3 request.
+ # We should probably rename this getHeader and deprecate this.
def get_header(self, name, default=None):
"""Return the named HTTP header, or an optional default
argument or None if the header is not found. Note that
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPResponse.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPResponse.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/HTTPResponse.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -180,6 +180,10 @@
self.stdout = stdout
self.stderr = stderr
+ def internalError(self):
+ 'See IPublisherResponse'
+ self.setStatus(500, u"The engines can't take any more, Jim!")
+
def retry(self):
"""Return a response object to be used in a retry attempt
"""
Copied: Zope/branches/publication-refactor2/lib/python/ZPublisher/Publication.py (from rev 67221, Zope/branches/publication-refactor/lib/python/ZPublisher/Publication.py)
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/Publish.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/Publish.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/Publish.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -60,67 +60,72 @@
_default_realm = realm
def publish(request, module_name, after_list, debug=0,
- # Optimize:
+ # Optimize (now unused).
call_object=call_object,
missing_name=missing_name,
dont_publish_class=dont_publish_class,
mapply=mapply,
):
- (bobo_before, bobo_after, object, realm, debug_mode, err_hook,
- validated_hook, transactions_manager)= get_module_info(module_name)
+ # We assume the publication object returned is the one in
+ # ZPublisher.Publication here so we don't bother using accessors
+ # and poke directly into the variables.
+ from ZPublisher.Publication import get_publication
+ publication = get_publication(module_name)
+ request.setPublication(publication)
- parents=None
- response=None
+ # BBB: bobo_after hooks are called from 'publish_module_standard'
+ if publication.bobo_after:
+ after_list[0] = bobo_after
+ parents = None
+ response = None
+
try:
request.processInputs()
- request_get=request.get
- response=request.response
+ request_get = request.get
+ response = request.response
# First check for "cancel" redirect:
if request_get('SUBMIT','').strip().lower()=='cancel':
- cancel=request_get('CANCEL_ACTION','')
+ # XXX Deprecate this, the Zope 2+3 publication won't support it.
+ cancel = request_get('CANCEL_ACTION','')
if cancel:
raise Redirect, cancel
- after_list[0]=bobo_after
- if debug_mode:
- response.debug_mode=debug_mode
- if realm and not request.get('REMOTE_USER',None):
- response.realm=realm
+ if publication.debug_mode:
+ response.debug_mode = publication.debug_mode
+ if publication.realm and not request.get('REMOTE_USER',None):
+ response.realm = publication.realm
+ validated_hook = publication.validated_hook
- if bobo_before is not None:
- bobo_before()
+ # Do any pre-traversal setup, like starting a new transaction.
+ publication.beforeTraversal(request)
# Get the path list.
# According to RFC1738 a trailing space in the path is valid.
path=request_get('PATH_INFO')
- request['PARENTS']=parents=[object]
+ request['PARENTS'] = parents = [publication.root]
- if transactions_manager:
- transactions_manager.begin()
+ # Traverse to the requested path.
+ object = request.traverse(path, validated_hook=validated_hook)
- object=request.traverse(path, validated_hook=validated_hook)
+ # After traversal hook, usually annotate transaction
+ publication.afterTraversal(request, object)
- if transactions_manager:
- transactions_manager.recordMetaData(object, request)
+ # Now call the traversed object.
+ result = publication.callObject(request, object)
- result=mapply(object, request.args, request,
- call_object,1,
- missing_name,
- dont_publish_class,
- request, bind=1)
-
if result is not response:
response.setBody(result)
- if transactions_manager:
- transactions_manager.commit()
+ # Call any afterCall hooks, usually commits the transaction
+ publication.afterCall(request, object)
return response
+
except:
# DM: provide nicer error message for FTP
@@ -135,40 +140,24 @@
getattr(cl,'__name__',cl), val,
debug_mode and compact_traceback()[-1] or ''))
- if err_hook is not None:
- if parents:
- parents=parents[0]
- try:
- try:
- return err_hook(parents, request,
- sys.exc_info()[0],
- sys.exc_info()[1],
- sys.exc_info()[2],
- )
- except Retry:
- if not request.supports_retry():
- return err_hook(parents, request,
- sys.exc_info()[0],
- sys.exc_info()[1],
- sys.exc_info()[2],
- )
- finally:
- if transactions_manager:
- transactions_manager.abort()
+ if parents:
+ parents = parents[0]
- # Only reachable if Retry is raised and request supports retry.
- newrequest=request.retry()
- request.close() # Free resources held by the request.
- try:
- return publish(newrequest, module_name, after_list, debug)
- finally:
- newrequest.close()
+ err_handled = publication.handleException(
+ parents, request, sys.exc_info(),
+ retry_allowed=request.supports_retry())
- else:
- if transactions_manager:
- transactions_manager.abort()
- raise
+ # XXX What if 'err_hook' returns None?
+ if err_handled is not None:
+ return err_handled
+ # Only reachable if Retry is raised and request supports retry.
+ newrequest = request.retry()
+ request.close() # Free resources held by the request.
+ try:
+ return publish(newrequest, module_name, after_list, debug)
+ finally:
+ newrequest.close()
def publish_module_standard(module_name,
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr,
@@ -177,47 +166,52 @@
status=200
after_list=[None]
try:
- try:
- if response is None:
- response=Response(stdout=stdout, stderr=stderr)
- else:
- stdout=response.stdout
+ if response is None:
+ response=Response(stdout=stdout, stderr=stderr)
+ else:
+ stdout=response.stdout
- if request is None:
- request=Request(stdin, environ, response)
+ if request is None:
+ request=Request(stdin, environ, response)
- # make sure that the request we hand over has the
- # default layer/skin set on it; subsequent code that
- # wants to look up views will likely depend on it
- setDefaultSkin(request)
+ # We assume the publication object returned is the one in
+ # ZPublisher.Publication here so we don't bother using accessors
+ # and poke directly into the variables.
+ from ZPublisher.Publication import get_publication
+ publication = get_publication(module_name)
+ request.setPublication(publication)
- response = publish(request, module_name, after_list, debug=debug)
- except SystemExit, v:
- must_die=sys.exc_info()
- request.response.exception(must_die)
- except ImportError, v:
- if isinstance(v, tuple) and len(v)==3: must_die=v
- elif hasattr(sys, 'exc_info'): must_die=sys.exc_info()
- else: must_die = SystemExit, v, sys.exc_info()[2]
- request.response.exception(1, v)
- except:
- request.response.exception()
- status=response.getStatus()
+ # make sure that the request we hand over has the
+ # default layer/skin set on it; subsequent code that
+ # wants to look up views will likely depend on it
+ setDefaultSkin(request)
- if response:
- outputBody=getattr(response, 'outputBody', None)
- if outputBody is not None:
- outputBody()
- else:
- response=str(response)
- if response: stdout.write(response)
+ from zope.publisher.publish import publish as publish3
+ publish3(request)
+ except SystemExit, v:
+ must_die=sys.exc_info()
+ request.response.exception(must_die)
+ except ImportError, v:
+ if isinstance(v, tuple) and len(v)==3: must_die=v
+ elif hasattr(sys, 'exc_info'): must_die=sys.exc_info()
+ else: must_die = SystemExit, v, sys.exc_info()[2]
+ request.response.exception(1, v)
+ except:
+ request.response.exception()
+ status=response.getStatus()
+ if request.response:
+ outputBody=getattr(request.response, 'outputBody', None)
+ if outputBody is not None:
+ outputBody()
+ else:
+ response=str(request.response)
+ if response:
+ stdout.write(response)
+
# The module defined a post-access function, call it
if after_list[0] is not None: after_list[0]()
- finally:
- if request is not None: request.close()
-
if must_die:
# Try to turn exception value into an exit code.
try:
@@ -328,7 +322,7 @@
def install_profiling(filename):
global _pfile
_pfile = filename
-
+
def pm(module_name, stdin, stdout, stderr,
environ, debug, request, response):
try:
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/Test.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/Test.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/Test.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -180,45 +180,44 @@
after_list=[None]
from Response import Response
from Request import Request
- from Publish import publish
+ from Publication import get_publication
+ # from Publish import publish
+ from zope.publisher.publish import publish
try:
- try:
- if response is None:
- response=Response(stdout=stdout, stderr=stderr)
- else:
- stdout=response.stdout
- if request is None:
- request=Request(stdin, environ, response)
- # make sure that the request we hand over has the
- # default layer/skin set on it; subsequent code that
- # wants to look up views will likely depend on it
- from zope.app.publication.browser import setDefaultSkin
- setDefaultSkin(request)
+ if response is None:
+ response=Response(stdout=stdout, stderr=stderr)
+ else:
+ stdout=response.stdout
+ if request is None:
+ request=Request(stdin, environ, response)
+ # make sure that the request we hand over has the
+ # default layer/skin set on it; subsequent code that
+ # wants to look up views will likely depend on it
+ from zope.app.publication.browser import setDefaultSkin
+ setDefaultSkin(request)
+ request.setPublication(get_publication())
+ for k, v in extra.items(): request[k]=v
+ response = request.response
+ publish(request) #, module_name, after_list, debug=debug)
+ except SystemExit, v:
+ must_die=sys.exc_info()
+ response.exception(must_die)
+ except ImportError, v:
+ if isinstance(v, TupleType) and len(v)==3: must_die=v
+ else: must_die=sys.exc_info()
+ response.exception(1, v)
+ except:
+ if debug:
+ raise
+ response.exception()
+ status=response.getStatus()
+ if response:
+ response=str(response)
+ if response: stdout.write(response)
- for k, v in extra.items(): request[k]=v
- response = publish(request, module_name, after_list, debug=debug)
- except SystemExit, v:
- must_die=sys.exc_info()
- response.exception(must_die)
- except ImportError, v:
- if isinstance(v, TupleType) and len(v)==3: must_die=v
- else: must_die=sys.exc_info()
- response.exception(1, v)
- except:
- if debug:
- raise
- response.exception()
- status=response.getStatus()
- if response:
- response=str(response)
- if response: stdout.write(response)
+ # The module defined a post-access function, call it
+ if after_list[0] is not None: after_list[0]()
- # The module defined a post-access function, call it
- if after_list[0] is not None: after_list[0]()
-
- finally:
- if request is not None: request.close()
-
if must_die:
try: raise must_die[0], must_die[1], must_die[2]
finally: must_die=None
Modified: Zope/branches/publication-refactor2/lib/python/ZPublisher/tests/testPublish.py
===================================================================
--- Zope/branches/publication-refactor2/lib/python/ZPublisher/tests/testPublish.py 2006-04-21 16:37:33 UTC (rev 67228)
+++ Zope/branches/publication-refactor2/lib/python/ZPublisher/tests/testPublish.py 2006-04-21 16:38:39 UTC (rev 67229)
@@ -79,9 +79,21 @@
"""Mock Response to replace ZPublisher.HTTPResponse.HTTPResponse.
"""
+ def internalError(self):
+ 'See IPublisherResponse'
+ self.setStatus(500, u"The engines can't take any more, Jim!")
+
+ def setStatus(self, status, reason):
+ self.status = status
+
def setBody(self, a):
pass
+ def exception(self, fatal = 0, info = None):
+ pass
+
+ setResult = setBody
+
class Request:
"""Mock Request to replace ZPublisher.HTTPRequest.HTTPRequest.
"""
@@ -90,7 +102,12 @@
def __init__(self):
self.response = Response()
+ from ZPublisher.Publication import get_publication
+ self.publication = get_publication('ZPublisher.tests.testPublish')
+ def setPublication(self, publication):
+ self.publication = publication
+
def processInputs(self):
pass
@@ -100,7 +117,7 @@
def __setitem__(self, name, value):
pass
- def traverse(self, path, validated_hook):
+ def traverse(self, path, response = None, validated_hook = None):
return Object()
def close(self):
@@ -112,6 +129,8 @@
def supports_retry(self):
return self.retry_count < self.retry_max_count
+ supportsRetry = supports_retry
+
def retry(self):
self.retry_count += 1
r = self.__class__()
@@ -127,14 +146,14 @@
Tests to ensure that the ZPublisher correctly manages the ZODB
transaction boundaries.
- >>> from ZPublisher.Publish import publish
+ >>> from zope.publisher.publish import publish
ZPublisher will commit the transaction after it has made a
rendering of the object.
>>> tracer.reset()
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request)
>>> tracer.showTracedPath()
begin
__call__
@@ -150,7 +169,7 @@
>>> tracer.reset()
>>> tracer.exceptions['__call__'] = [ValueError]
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request)
>>> tracer.showTracedPath()
begin
__call__
@@ -166,7 +185,7 @@
>>> tracer.exceptions['__call__'] = [ValueError]
>>> tracer.exceptions['zpublisher_exception_hook'] = [ValueError]
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request, False)
Traceback (most recent call last):
...
ValueError
@@ -187,7 +206,7 @@
>>> tracer.reset()
>>> tracer.exceptions['__call__'] = [ConflictError]
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request)
>>> tracer.showTracedPath()
begin
__call__
@@ -204,7 +223,7 @@
>>> tracer.reset()
>>> tracer.exceptions['commit'] = [ConflictError]
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request)
>>> tracer.showTracedPath()
begin
__call__
@@ -222,7 +241,7 @@
>>> tracer.exceptions['__call__'] = [ConflictError, ConflictError,
... ConflictError, ConflictError]
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request, False)
Traceback (most recent call last):
...
ConflictError: database conflict error
@@ -251,7 +270,7 @@
>>> tracer.exceptions['__call__'] = [ValueError]
>>> tracer.exceptions['zpublisher_exception_hook'] = [ConflictError]
>>> request = Request()
- >>> response = publish(request, module_name, after_list)
+ >>> response = publish(request, False)
Traceback (most recent call last):
...
ConflictError: database conflict error
More information about the Zope-Checkins
mailing list