Commit 563ad818 authored by Evan Simpson's avatar Evan Simpson

Made namespace handling saner.

parent 9bebe884
...@@ -129,26 +129,29 @@ class HTMLFile(DocumentTemplate.HTMLFile,MethodObject.Method,): ...@@ -129,26 +129,29 @@ class HTMLFile(DocumentTemplate.HTMLFile,MethodObject.Method,):
defaultBindings = {'name_context': 'context', defaultBindings = {'name_context': 'context',
'name_container': 'container', 'name_container': 'container',
'name_m_self': 'self', 'name_m_self': 'self',
'name_ns': '_', 'name_ns': 'caller_namespace',
'name_subpath': 'traverse_subpath'} 'name_subpath': 'traverse_subpath'}
from Shared.DC.Scripts.Bindings import Bindings from Shared.DC.Scripts.Bindings import Bindings
from Acquisition import Explicit from Acquisition import Explicit
from DocumentTemplate.DT_String import _marker, DTReturn, render_blocks from DocumentTemplate.DT_String import _marker, DTReturn, render_blocks
from DocumentTemplate.DT_Util import TemplateDict, InstanceDict from DocumentTemplate.DT_Util import TemplateDict, InstanceDict
from AccessControl import getSecurityManager
class DTMLFile(Bindings, Explicit, HTMLFile): class DTMLFile(Bindings, Explicit, HTMLFile):
"HTMLFile with bindings and support for __render_with_namespace__" "HTMLFile with bindings and support for __render_with_namespace__"
class func_code: pass func_code = None
func_code=func_code() func_defaults = None
func_code.co_varnames=()
func_code.co_argcount=0
_Bindings_ns_class = TemplateDict _Bindings_ns_class = TemplateDict
# By default, we want to look up names in our container.
_Bindings_client = 'container'
def __init__(self, name, _prefix=None, **kw): def __init__(self, name, _prefix=None, **kw):
self.ZBindings_edit(defaultBindings) self.ZBindings_edit(defaultBindings)
self._setFuncSignature()
apply(DTMLFile.inheritedAttribute('__init__'), apply(DTMLFile.inheritedAttribute('__init__'),
(self, name, _prefix), kw) (self, name, _prefix), kw)
...@@ -156,57 +159,59 @@ class DTMLFile(Bindings, Explicit, HTMLFile): ...@@ -156,57 +159,59 @@ class DTMLFile(Bindings, Explicit, HTMLFile):
# Cook if we haven't already # Cook if we haven't already
self._cook_check() self._cook_check()
# Get our namespace # Get our caller's namespace, and set up our own.
name_ns = self.getBindingAssignments().getAssignedName('name_ns') cns = bound_data['caller_namespace']
ns = bound_data[name_ns] ns = self._Bindings_ns_class()
push = ns._push push = ns._push
ns.validate = None ns.validate = None
# Check for excessive recursion
level = ns.level
if level > 200: raise SystemError, (
'infinite recursion in document template')
ns.level = level + 1
req = None req = None
kw_bind = kw kw_bind = kw
if level == 0: if cns:
# If this is the starting namespace, get the REQUEST. # Someone called us.
try: push(cns)
req = self.aq_acquire('REQUEST') ns.level = cns.level + 1
except: pass # Get their bindings. Copy the request reference
else:
# Get last set of bindings. Copy the request reference
# forward, and include older keyword arguments in the # forward, and include older keyword arguments in the
# current 'keyword_args' binding. # current 'keyword_args' binding.
try: try:
last_bound = ns[('current bindings',)] last_bound = ns[('current bindings',)]
req = last_bound.get('REQUEST', None) last_req = last_bound.get('REQUEST', None)
if last_req:
bound_data['REQUEST'] = last_req
old_kw = last_bound['keyword_args'] old_kw = last_bound['keyword_args']
if old_kw: if old_kw:
kw_bind = old_kw.copy() kw_bind = old_kw.copy()
kw_bind.update(kw) kw_bind.update(kw)
except: pass except: pass
# Add 'REQUEST' and 'keyword_args' bound names. else:
if req: # We're first, so get the REQUEST.
try:
req = self.aq_acquire('REQUEST')
except: pass
bound_data['REQUEST'] = req bound_data['REQUEST'] = req
# Bind 'keyword_args' to the complete set of keyword arguments.
bound_data['keyword_args'] = kw_bind bound_data['keyword_args'] = kw_bind
# Push globals, initialized variables, REQUEST (if any), # Push globals, initialized variables, REQUEST (if any),
# and keyword arguments onto the namespace stack, followed # and keyword arguments onto the namespace stack
# by the the container and the bound names.
for nsitem in (self.globals, self._vars, req, kw): for nsitem in (self.globals, self._vars, req, kw):
if nsitem: if nsitem:
push(nsitem) push(nsitem)
# This causes dtml files to bypass their context unless they # Push the 'container' (default), 'context', or nothing.
# explicitly use it through the 'context' name binding. bind_to = self._Bindings_client
push(InstanceDict(self._getContainer(), ns)) if bind_to in ('container', 'client'):
push(InstanceDict(bound_data[bind_to], ns))
# Push the name bindings, and a reference to grab later.
push(bound_data) push(bound_data)
push({('current bindings',): bound_data}) push({('current bindings',): bound_data})
security = getSecurityManager()
security.addContext(self)
try: try:
value = self.ZDocumentTemplate_beforeRender(ns, _marker) value = self.ZDocumentTemplate_beforeRender(ns, _marker)
if value is _marker: if value is _marker:
...@@ -217,10 +222,10 @@ class DTMLFile(Bindings, Explicit, HTMLFile): ...@@ -217,10 +222,10 @@ class DTMLFile(Bindings, Explicit, HTMLFile):
else: else:
return value return value
finally: finally:
# Clear the namespace security.removeContext(self)
# Clear the namespace, breaking circular references.
while len(ns): ns._pop() while len(ns): ns._pop()
from Shared.DC.Scripts.Signature import _setFuncSignature
...@@ -217,14 +217,23 @@ class Bindings: ...@@ -217,14 +217,23 @@ class Bindings:
__ac_permissions__ = ( __ac_permissions__ = (
('View management screens', ('getBindingAssignments',)), ('View management screens', ('getBindingAssignments',)),
('Change bindings', ('ZBindings_edit')), ('Change bindings', ('ZBindings_edit', 'ZBindings_setClient')),
) )
_Bindings_client = None
def ZBindings_edit(self, mapping): def ZBindings_edit(self, mapping):
names = self._setupBindings(mapping) names = self._setupBindings(mapping)
self._prepareBindCode() self._prepareBindCode()
self._editedBindings() self._editedBindings()
def ZBindings_setClient(self, clientname):
'''Name the binding to be used as the "client".
This is used by classes such as DTMLFile that want to
choose an object on which to operate by default.'''
self._Bindings_client = str(clientname)
def _editedBindings(self): def _editedBindings(self):
# Override to receive notification when the bindings are edited. # Override to receive notification when the bindings are edited.
pass pass
...@@ -305,13 +314,10 @@ class Bindings: ...@@ -305,13 +314,10 @@ class Bindings:
names = self.getBindingAssignments() names = self.getBindingAssignments()
assigned_name = names.getAssignedName('name_ns') assigned_name = names.getAssignedName('name_ns')
caller_namespace = kw.get(assigned_name, None) caller_namespace = kw.get(assigned_name, None)
# Create a local namespace. if caller_namespace is None:
my_namespace = self._Bindings_ns_class() # Create an empty namespace.
if caller_namespace is not None: return self._Bindings_ns_class()
# Include the caller's namespace. return caller_namespace
my_namespace._push(caller_namespace)
my_namespace.level = caller_namespace.level
return my_namespace
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
'''Calls the script. '''Calls the script.
......
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