As Plone developers, a lot of the problems we have when writing code and templates are only revealed with cryptic, sometimes misleading error messages from somewhere way down the stack from their underlying cause, if at all. When an error is raised, by some template rendering, Zope does provide some useful traceback information specifying the template with line numbers and expressions and whatnot. But why shouldn’t we be able to access this information without raising an error? For example, to diagnose security or redirection problems that aren’t necessarily obvious even with extra logging & verbose security enabled.
The functions provided below allow the developer to gather this kind of feedback and output wherever he or she wishes, without having to provide any arguments that might not always be easy to get from the current part of source code. They work under Zope 2.7 but are untested under other versions. If you do try them out on other versions, please report back in the comments if they do work!
import sys
from Products.CMFCore.utils import getToolByName
from zope.tales.tales import TALESTracebackSupplement
def get_current_template_position():
""" If called from a stack frame which has been called from template evaluation,
returns a tuple of template filename, line number, column number and
TALES expression closest in the stack to the caller. Otherwise, returns None.
"""
i = 0
curframe = sys._getframe(i)
while True:
locals = curframe.f_locals
globals = curframe.f_globals
if '__traceback_supplement__' in locals:
# Use the supplement defined in the function.
tbs = locals.get('__traceback_supplement__')
elif '__traceback_supplement__' in globals:
# Use the supplement defined in the module.
# This is used by Scripts (Python).
tbs = globals.get('__traceback_supplement__')
else:
tbs = None
if tbs is not None:
factory = tbs[0]
args = tbs[1:]
try:
supp = factory(*args)
except:
continue
if type(supp) is TALESTracebackSupplement:
return (supp.context, supp.source_url, supp.line, supp.column, supp.expression)
i=i+1
try:
curframe = sys._getframe(i)
if curframe is None:
return None
except:
return None
def dump_current_template_position(context=None, return_string=False):
""" When called, attempts to print to the console the URL of the current request, the
authenticated user, the currently executing template file, the line and column
currently being evaluated in the file and the expression being evaluated.
Will not print if called from a stack frame which has been called from template
evaluation. May not print if called from a .cpy or .vpy file, depending on
permissions to 'print'.
Wherever possible, this function should be called with the 'context' arguement
specified.
If the optional argument 'return_string' is set to True, the function returns the
message that would be output, rather than printing.
"""
tpos = get_current_template_position()
if tpos is not None:
(ctx, template, line, col, expr) = tpos
url = 'Unknown'
if context is not None:
try:
request = hasattr(context, 'request') and context.request or context.REQUEST
url = request.get('ACTUAL_URL')
except AttributeError:
pass
if url == 'Unknown':
try:
request = hasattr(ctx, 'request') and ctx.request or ctx.REQUEST
url = request.get('ACTUAL_URL')
except AttributeError:
pass
member = 'Unknown'
if context is not None:
try:
mtool = getToolByName(context, 'portal_membership')
member = mtool.getAuthenticatedMember()
except AttributeError:
pass
if member == 'Unknown':
try:
mtool = getToolByName(ctx, 'portal_membership')
member = mtool.getAuthenticatedMember()
except AttributeError:
pass
output = "\tURL: %s\n\tAuth'd as: %s\n\tFile: %s\n\tLine: %s\n\tColumn: %s\n\tExpression: %s" % (url, member, template, line, col, expr)
if return_string:
return output
print output
This may also be called from templates, provided the template has sufficient permissions to call the module it lies in:
<tal:block tal:define="dummy python:modules['myproject.app.utils'].dump_current_template_position(context)" />
Or, as it is mostly used, from code called by templates, simply by importing the function(s) as necessary and calling them with options of your choice. If calling from
.cpy
or
.vpy
files, the print command may not work properly, so the
dump_current_template_position
function may be called with the optional argument
return_string
set to
True
and then the result may be logged or printed using alternate methods.