mako icon indicating copy to clipboard operation
mako copied to clipboard

Allow restricting the use of template data as magical globals

Open sqlalchemy-bot opened this issue 10 years ago • 2 comments

Migrated issue, originally created by eevee (@eevee)

For big projects with big collections of big templates, having a set of super-global variables that aren't documented and might vary by caller starts to get really thorny and confusing. There are a few features that partially help this problem:

  • strict_undefined makes sure you don't use anything you don't pass in, but it only applies when some function is actually called (and doesn't apply at all for Python-land functions that use the context). Slightly better than Python semantics, sure, but linters are easier to come by for Python than for Mako.
  • <%page args> provides some documentation and enforces that some minimum set of arguments are provided, which is great. But any extra variables are still added to the context (even though they also go into pageargs), and I think (not sure?) that even the explicitly named arguments are still context globals.

I don't see how to fix this outside of Mako without doing something really gross, but on the other hand Mako tries really hard not to be too opinionated.

I think the least-intrusive, most-helpful thing would be to add an argument to <%page> (opaque, maybe?) that does two things: shadows the context data when a function declared in that template is called, and doesn't populate the context with extra kwargs when body is rendered directly. So this would fail:

mako.template.Template(filename='a.mako').render(request=...)

# a.mako
<%namespace name="lib" file="lib.mako" />
${lib.print_link("hello world")}

# lib.mako
<%page opaque />

<%def name="print_link(title)">
<a href="${request.route_url(...)}">${title}</a>
</%def>

...because request doesn't exist inside print_link.

Then you could opt in on a per-template basis; for example, keep the default behavior for templates that render entire pages (which is reasonable), but use opaque for shared code where the callers aren't all immediately obvious.

Doesn't cover the case of a Python module imported as a namespace, but at least in that case it's plainly obvious when you're relying on non-arguments.

I think I could implement this, if the idea is okay.

sqlalchemy-bot avatar Aug 11 '14 21:08 sqlalchemy-bot

Michael Bayer (@zzzeek) wrote:

OK.... im sort of grokking it. you just want <%page> to block anything that isn't passed in directly, is that it? propagate_context=False maybe?

sqlalchemy-bot avatar Aug 11 '14 22:08 sqlalchemy-bot

eevee (@eevee) wrote:

Yeah, but opt-in (so as to not break everyone's existing templates), with an argument on <%page> within the template itself.

sqlalchemy-bot avatar Aug 11 '14 22:08 sqlalchemy-bot