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 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: %sntAuth'd as: %sntFile: %sntLine: %sntColumn: %sntExpression: %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.