[Zope-Checkins] CVS: Zope3/lib/python/Zope/Publisher - BaseRequest.py:1.1.2.1 BaseResponse.py:1.1.2.1 Exceptions.py:1.1.2.1 IPublication.py:1.1.2.1 Publish.py:1.1.2.1 __init__.py:1.1.2.1
Shane Hathaway
shane@digicool.com
Wed, 14 Nov 2001 16:44:22 -0500
Update of /cvs-repository/Zope3/lib/python/Zope/Publisher
In directory cvs.zope.org:/tmp/cvs-serv5855
Added Files:
Tag: Zope-3x-branch
BaseRequest.py BaseResponse.py Exceptions.py IPublication.py
Publish.py __init__.py
Log Message:
First part of the more interface-driven publisher.
=== Added File Zope3/lib/python/Zope/Publisher/BaseRequest.py ===
__version__='$Revision: 1.1.2.1 $'[11:-2]
from urllib import quote
from cgi import escape
from types import StringType
class RequestContainer:
# TODO: add security assertion declaring this to be public
def __init__(self, request):
self.REQUEST = request
_marker=[]
class BaseRequest:
"""Represents a publishing request.
This object provides access to request data. Request data may
vary depending on the protocol used.
Request objects are created by the object publisher and will be
passed to published objects through the argument name, REQUEST.
The request object is a mapping object that represents a
collection of variable to value mappings.
"""
# TODO: add security assertion declaring this to be public
_file = None # The request input stream
common = {} # Common request data
# _held contains objects kept until the request is closed,
# such as the database connection closer.
_held = ()
_auth = None # Unverified credentials presented by the user.
# URL is a string built up during traversal and url_quoted.
# It does not include names built from publication.getDefault().
URL = ''
to_traverse = () # A sequence containing the names to traverse, reversed.
steps = () # A sequence of names built up during traversal.
quoted_steps = () # Same as steps but quoted.
# PARENTS contains the objects traversed, innermost parents
# first and the root object last.
PARENTS = ()
PUBLISHED = None # Object traversed to
AUTHENTICATED_USER = None # Authorized user object
AUTHENTICATION_PATH = '' # Unquoted path to parent of user folder
def __init__(self, file, mapping, response):
"""
The constructor should not raise errors.
processInputs() should perform the unsafe initialization.
"""
self._file = file
self.steps = []
self.quoted_steps = []
self.other = {}
self.other.update(mapping)
self.response = response
def close(self):
self.other.clear()
self._held = None # Release held objects
def processInputs(self):
"""Do any input processing that could raise errors
"""
def __len__(self):
return 1
_key_handlers = {}
def getRequest(self):
return self
_key_handlers['REQUEST'] = getRequest
def getURL(self):
return self.URL
_key_handlers['URL'] = getURL
def getBody(self):
body = self.BODY
if body is None:
p = self._file.tell()
self._file.seek(0)
body = self._file.read()
self._file.seek(p)
self.BODY = body
return body
_key_handlers['BODY'] = getBody
def getBodyFile(self):
return self._file
_key_handlers['BODYFILE'] = getBodyFile
def __getitem__(self, key,
default=_marker, # Any special internal marker will do
):
"""Get a variable value
Return a value for the required variable name.
The value will be looked up from one of the request data
categories. The search order is environment variables,
other variables, form data, and then cookies.
"""
h = self._key_handlers.get(key, None)
if h is not None:
return h(self)
v=self.other.get(key, _marker)
if v is not _marker: return v
v=self.common.get(key, default)
if v is not _marker: return v
raise KeyError, key
def get(self, key, default=None):
return self.__getitem__(key, default)
def has_key(self,key):
return self.get(key, _marker) is not _marker
def keys(self):
keys = {}
#keys.update(self.common)
keys.update(self.other)
return keys.keys()
def items(self):
result = []
get = self.get
for k in self.keys():
result.append((k, get(k)))
return result
def values(self):
result = []
get = self.get
for k in self.keys():
result.append(get(k))
return result
def __str__(self):
L1 = self.items()
L1.sort()
return "\n".join(map(lambda item: "%s:\t%s" % item, L1))
__repr__=__str__
def _splitPath(self, path):
# Split and clean up the path.
if path[:1] == '/': path = path[1:]
if path[-1:] == '/': path = path[:-1]
clean = []
for item in path.split('/'):
if not item or item == '.':
continue
elif item == '..':
del clean[-1]
else: clean.append(item)
return clean
## def _getDefaultView(self, object, m_name='_browser_default'):
## """
## returns (object, add_steps)
## """
## psteps = None
## f = getattr(object, m_name, None)
## if f is not None:
## v = f(self)
## if v is not None:
## if type(v) is StringType:
## # One more traversal step.
## return object, (v,)
## else:
## # Arbitrary additional traversal.
## o, add_steps = v
## if add_steps or o is not object:
## return o, add_steps
## m = self._request_method
## if m == 'GET' or m == 'POST':
## default = 'index_html'
## else:
## default = m
## if default and getattr(object, default, None) is not None:
## return object, (default,)
## return object, ()
def changeTraversalStack(self, names):
"""
Private.
names should be in reverse.
"""
self.to_traverse = names
def setAuthenticatedUser(self, user):
self.AUTHENTICATED_USER = user
self.AUTHENTICATION_PATH = '/'.join(self.steps)
def traverse(self, publication, object, path_str):
"""
Traverses to an object and returns it.
Private.
"""
added_default = 0
to_traverse = self._splitPath(path_str)
if hasattr(object, '__of__'):
# Try to bind the top-level object to the request.
object = object.__of__(RequestContainer(self))
parents = []
parents.append(object)
steps = self.steps
self.quoted_steps = quoted_steps = map(quote, steps)
to_traverse.reverse()
self.to_traverse = to_traverse
prev_object = None
while 1:
if object is not prev_object:
# Invoke hooks (but not more than once).
publication.invokeHooks(self, object)
# A hook may have called changeTraversalStack().
to_traverse = self.to_traverse
prev_object = object
if to_traverse:
# Traverse to the next step.
entry_name = to_traverse.pop()
if entry_name:
qstep = quote(entry_name)
quoted_steps.append(qstep)
if not added_default:
# Build up the URL to the object, but not
# to the default view.
self.URL = '%s/%s' % (self.URL, qstep)
subobject = publication.traverseName(
self, object, entry_name)
publication.checkAuthorization(
self, object, entry_name, subobject)
object = subobject
parents.append(object)
steps.append(entry_name)
elif not added_default:
# Traverse to the default view, if any.
added_default = 1
object, add_steps = publication.getDefault(self, object)
if add_steps:
to_traverse.extend(add_steps)
else:
# Finished traversal.
break
if added_default:
self.response.setBase(self.URL)
parents.reverse()
self.PARENTS = parents
self.PUBLISHED = object
return object
def supports_retry(self):
return 0
def _hold(self, object):
"""
Holds a reference to an object to delay its destruction until mine
"""
self._held = self._held + (object,)
=== Added File Zope3/lib/python/Zope/Publisher/BaseResponse.py ===
'''Response Output formatter
$Id: BaseResponse.py,v 1.1.2.1 2001/11/14 21:44:21 shane Exp $'''
__version__='$Revision: 1.1.2.1 $'[11:-2]
from zExceptions import Unauthorized
class BaseResponse:
"""Base Response Class
What should be here?
"""
debug_mode=None
#_auth=None
#_error_format='text/plain'
def __init__(self, outstream, body='', headers=None,
status=None, cookies=None):
self.outstream = outstream
self.body = body
if headers is None:
headers = {}
self.headers = headers
self.status = status
if cookies is None:
cookies = {}
self.cookies = cookies
def setDebugMode(self, d):
self.debug_mode = d
def setStatus(self, status, reason=None):
self.status = status
def setHeader(self, name, value):
self.headers[name] = value
__setitem__ = setHeader
def outputBody(self):
"""
Output the response body.
"""
self.outstream.write(str(self))
def setBody(self, body):
self.body = body
def getStatus(self):
'Returns the current HTTP status code as an integer. '
return self.status
def setCookie(self,name,value,**kw):
'''
Sets an HTTP cookie on the browser
The response will include an HTTP header that sets a cookie on
cookie-enabled browsers with a key "name" and value
"value". This overwrites any previously set value for the
cookie in the Response object.
'''
cookies=self.cookies
if cookies.has_key(name):
cookie=cookies[name]
else: cookie=cookies[name]={}
for k, v in kw.items():
cookie[k]=v
cookie['value']=value
def getHeader(self, name):
'''
Gets a header value
Returns the value associated with a HTTP return header, or
"None" if no such header has been set in the response
yet.
'''
return self.headers.get(name, None)
def __getitem__(self, name):
'Gets the value of an output header'
return self.headers[name]
def getBody(self):
'Returns a string representing the currently set body.'
return self.body
def __str__(self):
return str(self.body)
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, `self.body`)
def flush(self):
pass
def write(self,data):
"""
Implements the notional stream output interface.
HTML data may be returned using a stream-oriented interface.
This allows the browser to display partial results while
computation of a response to proceed.
The published object should first set any output headers or
cookies on the response object.
Note that published objects must not generate any errors
after beginning stream-oriented output.
"""
self.body = self.body + data
## def exception(self, fatal=0, info=None):
## """Handle an exception.
## The fatal argument indicates whether the error is fatal.
## The info argument, if given should be a tuple with an
## error type, value, and traceback.
## """
## def notFoundError(self, v=''):
## """Generate an error indicating that an object was not found.
## """
## raise 'Not Found', v
## def debugError(self, v=''):
## """Raise an error with debigging info and in debugging mode"""
## raise 'Debug Error', v
## def badRequestError(self, v=''):
## """Raise an error indicating something wrong with the request"""
## raise 'Bad Request', v
## def forbiddenError(self, v=''):
## """Raise an error indicating that the request cannot be done"""
## raise 'Forbidden', v
## def unauthorized(self):
## """Raise an eror indicating that the user was not authizated
## Make sure to generate an appropriate challenge, as appropriate.
## """
## raise Unauthorized
=== Added File Zope3/lib/python/Zope/Publisher/Exceptions.py ===
from Zope.zExceptions import Unauthorized
class PublishingException (Exception):
"""
"""
class NotFound (PublishingException):
def __init__(self, ob, name):
self.ob = ob
self.name = name
def getOb(self):
return self.ob
def getName(self):
return self.name
class DebugError (PublishingException):
def __init__(self, s):
self.s = s
class BadRequest (PublishingException):
def __init__(self, s):
self.s = s
class Redirect (PublishingException):
def __init__(self, location):
self.location = location
class Retry (PublishingException):
"""Raise this to retry a request
"""
def __init__(self, t=None, v=None, tb=None):
self._args = t, v, tb
def reraise(self):
t, v, tb = self._args
if t is None: t=Retry
if tb is None: raise t, v
try: raise t, v, tb
finally: tb=None
=== Added File Zope3/lib/python/Zope/Publisher/IPublication.py ===
from Interface import Interface
class IPublication (Interface):
"""
Object publication framework.
"""
# The order of the hooks mostly corresponds with the order in which
# they are invoked.
def getDebugMode(request):
"""
Returns the debug mode setting.
"""
def preTraversal(request):
"""
Pre-traversal hook.
"""
def getApplication(request):
"""
Returns the object where traversal should commence.
"""
def invokeHooks(request, object):
"""
Invokes any traversal hooks associated with the object.
"""
def traverseName(request, ob, name):
"""
Traverses to the next object.
"""
def checkAuthorization(request, ob, name, subob):
"""
Performs idenitification, authentication, and authorization.
Should call request.setAuthenticatedUser().
"""
def getDefault(request, ob):
"""
Allows a default view to be added to traversal.
Returns (ob, steps_reversed).
"""
def postTraversal(request, ob):
"""
Post-traversal hook.
"""
def publish(request, ob):
"""
Publishes the object, usually by calling it.
"""
def postPublish(request):
"""
Post-publishing hook (if publishing was successful).
"""
def handleException(request, t, v, tb):
"""
Either:
- sets the body of request.response,
- raises a Retry exception, or
- throws another exception, which is a Bad Thing.
"""
=== Added File Zope3/lib/python/Zope/Publisher/Publish.py ===
__doc__="""Python Object Publisher -- Publish Python objects on web servers
$Id: Publish.py,v 1.1.2.1 2001/11/14 21:44:21 shane Exp $"""
__version__='$Revision: 1.1.2.1 $'[11:-2]
import sys, os
def traverseAndPublish(publication, request):
## (bobo_before, bobo_after, object, realm, debug_mode, err_hook,
## validated_hook, transactions_manager)= get_module_info(module_name)
parents = None
try:
request.processInputs()
response = request.response
if debug_mode:
response.setDebugMode(debug_mode)
publication.preTraversal(request)
#if transactions_manager: transactions_manager.begin()
object = publication.getApplication()
path_str = request.get('PATH_INFO', '').strip()
object = request.traverse(publication, object, path_str)
publication.postTraversal(request, object)
result = publication.publish(request, object)
## if transactions_manager:
## transactions_manager.recordMetaData(object, request)
## 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)
publication.postPublish(request)
## if transactions_manager: transactions_manager.commit()
return response
except:
try:
t, v, tb = sys.exc_info()
try:
publication.handleException(request, t, v, tb)
except Retry:
# The exception handler requested a retry.
if not request.supports_retry():
# Can't retry. Restore the original exception.
raise t, v, tb
newrequest = request.retry()
request.close() # Free resources held by the request.
try:
tb = None
return traverseAndPublish(publication, newrequest)
finally:
newrequest.close()
finally:
del tb # Avoid circular ref.
def publishRequest(publication, request):
to_raise = None
try:
try:
response = traverseAndPublish(publication, request)
except SystemExit, v:
to_raise = sys.exc_info()
publication.handleException(
request, to_raise[0], to_raise[1], to_raise[2])
except:
to_raise = sys.exc_info()
publication.handleException(
request, to_raise[0], to_raise[1], to_raise[2])
response.outputBody()
finally:
if request is not None:
request.close()
if to_raise:
try:
raise to_raise[0], to_raise[1], to_raise[2]
finally:
to_raise = None
return response.getStatus()
##def publish_module(module_name,
## stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr,
## environ=os.environ, debug=0, request=None, response=None):
## must_die=0
## status=200
## after_list=[None]
## 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)
## 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 type(v) is type(()) 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 response:
## outputBody=getattr(response, 'outputBody', None)
## if outputBody is not None:
## outputBody()
## else:
## 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]()
## 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
## return status
##_l=allocate_lock()
##def get_module_info(module_name, modules={},
## acquire=_l.acquire,
## release=_l.release,
## ):
## if modules.has_key(module_name): return modules[module_name]
## if module_name[-4:]=='.cgi': module_name=module_name[:-4]
## acquire()
## tb=None
## try:
## try:
## module=__import__(module_name, globals(), globals(), ('__doc__',))
## realm=module_name
## # Let the app specify a realm
## if hasattr(module,'__bobo_realm__'):
## realm=module.__bobo_realm__
## elif os.environ.has_key('Z_REALM'):
## realm=os.environ['Z_REALM']
## elif os.environ.has_key('BOBO_REALM'):
## realm=os.environ['BOBO_REALM']
## else: realm=module_name
## # Check for debug mode
## debug_mode=None
## if hasattr(module,'__bobo_debug_mode__'):
## debug_mode=not not module.__bobo_debug_mode__
## else:
## z1 = os.environ.get('Z_DEBUG_MODE','')
## z2 = os.environ.get('BOBO_DEBUG_MODE','')
## if z1.lower() in ('yes','y') or z1.isdigit():
## debug_mode = 1
## elif z2.lower() in ('yes','y') or z2.isdigit():
## debug_mode = 1
## if hasattr(module,'__bobo_before__'):
## bobo_before=module.__bobo_before__
## else: bobo_before=None
## if hasattr(module,'__bobo_after__'): bobo_after=module.__bobo_after__
## else: bobo_after=None
## if hasattr(module,'bobo_application'):
## object=module.bobo_application
## elif hasattr(module,'web_objects'):
## object=module.web_objects
## else: object=module
## error_hook=getattr(module,'zpublisher_exception_hook', None)
## validated_hook=getattr(module,'zpublisher_validated_hook', None)
## transactions_manager=getattr(
## module,'zpublisher_transactions_manager', None)
## if not transactions_manager:
## try: get_transaction()
## except: pass
## else:
## # Create a default transactions manager for use
## # by software that uses ZPublisher and ZODB but
## # not the rest of Zope.
## transactions_manager = DefaultTransactionsManager()
## info= (bobo_before, bobo_after, object, realm, debug_mode,
## error_hook, validated_hook, transactions_manager)
## modules[module_name]=modules[module_name+'.cgi']=info
## return info
## except:
## t,v,tb=sys.exc_info()
## v=str(v)
## raise ImportError, (t, v), tb
## finally:
## tb=None
## release()
##class DefaultTransactionsManager:
## def begin(self): get_transaction().begin()
## def commit(self): get_transaction().commit()
## def abort(self): get_transaction().abort()
## def recordMetaData(self, object, request):
## # Is this code needed?
## request_get = request.get
## T=get_transaction()
## T.note(request_get('PATH_INFO'))
## auth_user=request_get('AUTHENTICATED_USER',None)
## if auth_user is not None:
## T.setUser(auth_user, request_get('AUTHENTICATION_PATH'))
### ZPublisher profiler support
### ---------------------------
##if os.environ.get('PROFILE_PUBLISHER', None):
## import profile, pstats
## _pfile=os.environ['PROFILE_PUBLISHER']
## _plock=allocate_lock()
## _pfunc=publish_module
## _pstat=None
## def pm(module_name, stdin, stdout, stderr,
## environ, debug, request, response):
## try:
## r=_pfunc(module_name, stdin=stdin, stdout=stdout,
## stderr=stderr, environ=environ, debug=debug,
## request=request, response=response)
## except: r=None
## sys._pr_=r
## def publish_module(module_name, stdin=sys.stdin, stdout=sys.stdout,
## stderr=sys.stderr, environ=os.environ, debug=0,
## request=None, response=None):
## global _pstat
## _plock.acquire()
## try:
## if request is not None:
## path_info=request.get('PATH_INFO')
## else: path_info=environ.get('PATH_INFO')
## if path_info[-14:]=='manage_profile':
## return _pfunc(module_name, stdin=stdin, stdout=stdout,
## stderr=stderr, environ=environ, debug=debug,
## request=request, response=response)
## pobj=profile.Profile()
## pobj.runcall(pm, module_name, stdin, stdout, stderr,
## environ, debug, request, response)
## result=sys._pr_
## pobj.create_stats()
## if _pstat is None:
## _pstat=sys._ps_=pstats.Stats(pobj)
## else: _pstat.add(pobj)
## finally:
## _plock.release()
## if result is None:
## try:
## error=sys.exc_info()
## file=open(_pfile, 'w')
## file.write(
## "See the url "
## "http://www.python.org/doc/current/lib/module-profile.html"
## "\n for information on interpreting profiler statistics.\n\n"
## )
## sys.stdout=file
## _pstat.strip_dirs().sort_stats('cumulative').print_stats(250)
## _pstat.strip_dirs().sort_stats('time').print_stats(250)
## file.flush()
## file.close()
## except: pass
## raise error[0], error[1], error[2]
## return result
=== Added File Zope3/lib/python/Zope/Publisher/__init__.py ===
"""
Notes:
provideExceptionHandler(BrowserPublish, Redirect, HTTPRedirector)
"""