# $Id: request.py 2053 2006-03-02 16:58:25Z zzzeek $
# request.py - handles component calls for Myghty templates
# Copyright (C) 2004, 2005 Michael Bayer mike_mp@zzzcomputing.com
# Original Perl code and documentation copyright (c) 1998-2003 by Jonathan Swartz.
#
# This module is part of Myghty and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
#
"""The request package and its primary class, Request, provide the "m" object in Myghty templates.
The Request is instantiated by the Interpreter and handles component execution state as well
as buffered state.
It provides the interface to all actions taken by components, such as writing output,
calling other components, calling subrequests, and controllling request state. Also
provides the programmatic interface to key objects such as caches and sessions.
"""
from myghty.util import *
import myghty.buffer
from myghty import exception
import sys, string, re, StringIO, types, os, time
import posixpath as unixpath
from myghty.container import CreationAbortedError
import myghty.escapes as escapes
import myghty.csource as csource
# current instance of Request for this thread.
# this is flaky as the request module seems to get reimported when it is
# first imported within a generated component
reqinstance = ThreadLocal()
__all__ = ['Request', 'instance', 'threadlocal']
class Request(object):
"""request object, calls components and directs output. also is the
primary programmatic interface presented to a template's environment."""
def __init__(self,
interpreter,
component = None,
request_impl = None,
max_recursion = 32,
auto_flush = False,
use_dhandlers = True,
dont_auto_flush_filters = False,
resolver_context = 'request',
parent_request = None,
request_depth = 0,
request_path = None,
raise_error = False,
**params
):
"""init is called internally by the Interpreter."""
self.component = component
self.auto_flush = auto_flush
self.executed = False
self.interpreter = interpreter
self.execution_stack = []
self.max_recursion = max_recursion
self.dont_auto_flush_filters = dont_auto_flush_filters
self.use_dhandlers = use_dhandlers
self.raise_error = raise_error
self.parent_request = parent_request
self._attributes = {}
self.request_depth = request_depth
self.current_csource = None
self.starttime = time.time()
self.comphash = {}
self.compcaches = {}
self.request_path = request_path
self.resolver_context = resolver_context
if parent_request is not None:
self.root_request = parent_request.root_request
self.declined_components = parent_request.declined_components
self.dhandler_path = parent_request.dhandler_path
else:
self.declined_components = {}
self.root_request = self
self.dhandler_path = None
if request_impl:
self.request_impl = request_impl
else:
self.request_impl = DefaultRequestImpl(**params)
self.buffer = None
notes = property(lambda self: self.attributes, doc="""A synonym for m.attributes""")
root_request_path = property(lambda self:self.root_request.request_path, doc="""\
The URI sent to the ultimate root request of this Request.
""")
root_request_args = property(lambda self:self.root_request.request_args, doc="""\
The request argument dictionary sent to the ultimate root request of this Request.
""")
def _parentattr(self):
if self.parent_request is not None:
return self.parent_request.attributes
else:
return None
attributes = property(lambda self:InheritedDict(self._attributes, lambda: self._parentattr()), doc="""\
A dictionary where arbitrary attributes can be stored and retrieved. Inherits data from its
parent request, if any.
""")
def is_subrequest(self):
"""returns True if this request is a subrequest."""
return self.parent_request is not None
def clone(self, **params):
"""creates a copy of this Request. Normally used to create subrequests."""
if not params.has_key('request_impl'):
params['request_impl'] = self.request_impl.clone(**params)
clone = ConstructorClone(self, **params)
return clone.clone()
class StackFrame:
"""data object representing the currently executing component context.
gets pushed and popped to/from the execution_stack instance variable.
"""
def __init__(self, request, component, args, base_component, content, is_call_self):
self.component = component
self.args = args
self.base_component = base_component
self.content = content
self.is_call_self = is_call_self
self.content_args = None
# set up an indirect filter function that is passed to output
# buffers, etc. allows the filtering function can be dynamically
# reset to a no-op, in the case that the component calls call_self
# or cache_self, which should disable the final output filtering
if isinstance(component, myghty.component.Component) and component.has_filter():
def filt(f):
return component.filter(f, m = request, ARGS = args, **request.global_args)
self._filter = filt
self.filter = lambda f: self._filter(f)
def reset_filter(self):
self._filter = lambda f: f
def out(self, string):
"""a synonym for write"""
self.write(string)
def write(self, string):
"""writes textual content to the current output buffer."""
self.buffer.write(string)
def flush_buffer(self):
"""flushes the current output buffer to its destination."""
b = self.buffer
while (b and not b.ignore_flush):
b.flush()
b = b.parent
def clear_buffer(self):
"""clears all content from the current output buffer."""
b = self.buffer
while (b):
b.truncate(0)
b = b.parent
def push_buffer(self, buf, ignore_flush = False, ignore_clear = False, filter = None):
"""pushes a new output buffer onto the request's buffer stack. This buffer, when flushed,
will transfer its content to the next buffer in the buffer stack."""
buffer = myghty.buffer.HierarchicalBuffer(buf, parent = self.buffer, ignore_flush = ignore_flush, ignore_clear = ignore_clear, filter = filter)
self.buffer = buffer
return buffer
def pop_buffer(self):
"""pops a buffer off the buffer stack. The buffer is not flushed."""
buffer = self.buffer
if not buffer:
self.buffer = None
return None
elif buffer.parent:
self.buffer = buffer.parent
return buffer
def clear_buffer_stack(self):
"""clears all output buffers. The request's buffer is set to None."""
self.buffer = None
def execute(self):
"""executes this request after constructed. This is the first method called,
and can only be called once per request."""
if self.executed:
self._raise_error("Can only call execute() once per request")
self.executed = True
if reqinstance.exists():
existing_request = reqinstance.get()
else:
existing_request = None
reqinstance.put(self)
if self.parent_request is not None:
# if there is a parent request, push their buffer onto our stack
self.