Skip to content

Allow restricting the use of template data as magical globals #231

Open
@sqlalchemy-bot

Description

@sqlalchemy-bot

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions