Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.1 (or 2.0) feature idea: import blocks #256

Open
sqlalchemy-bot opened this issue Mar 12, 2016 · 8 comments
Open

1.1 (or 2.0) feature idea: import blocks #256

sqlalchemy-bot opened this issue Mar 12, 2016 · 8 comments

Comments

@sqlalchemy-bot
Copy link

Migrated issue, originally created by jvanasco (@jvanasco)

I don't know if I'd be able to pull this off, but wanted to share publicly for feedback.

I propose a new feature to "import" a block.

It might be invoked like:

<%block import="namespace.function"></%block>
<%block file="file.mako" name="function"></%block>
<%block_import file="file.mako" name="function"></%block>

Invoking this would (re)declare the block as a local function, generating the same code as if you copy/pasted the block between source files.

why?

if you do a large project with template inheritance, you inevitably end up having a lot of blocks that you'd like to re-use across components.

the implementation details for blocks/defs make it hard to recreate this behavior.

  • you can call a block like a def, but it then works like a def
  • you can include a file to place a block, but it's not over-writeable .

an example would be setting up a "header" on a site template. if you wanted to split out the "header" into it's own page, the current way would be...

library.mako:

 <%block name="header">library header</%block>

page.mako:

 <%block name="header">page header</%block>

site-template.mako:

<%namespace name="t_library" file="library.mako"/>
<%block name="header">${t_library.header()}</%block>

that's a bit messy.

if the library.mako file looks like this:

<%block name="header">
	library header
	<%block name="sub_header">library subheader</%block>
</%block>

then site-template would need to define a header block with a sub_header block for an override to happen from "page" -- but then to get nesting right one would need to do....

library

<%def name="_header()">
    library header
</%def>
<%def name="_subheader()">
    library subheader
</%def>
<%block name="header">
    ${_header()}
    <%block name="sub_header">${_subheader()}</%block>
</%block>

site-template

<%block name="header">
    ${t_library._header()}
    <%block name="sub_header">${t_library._subheader()}</%block>
</%block>

this gets increasingly complex.

so my proposal would be to allow for a block that is defined in one namespace, to be "imported" into a new namespace (vs being called as a regular def)

@sqlalchemy-bot
Copy link
Author

Changes by jvanasco (@jvanasco):

  • edited description

1 similar comment
@sqlalchemy-bot
Copy link
Author

Changes by jvanasco (@jvanasco):

  • edited description

@sqlalchemy-bot
Copy link
Author

Michael Bayer (@zzzeek) wrote:

when i first made the <%include> tag I considered the option to have a version that's a so-called "server side" include; instead of invoking the related template at runtime, it would pull it in at compile time. the concepts are straight from JSP which you can see here: http://stackoverflow.com/a/14763794/34549.

is this essentially a server side include?

@sqlalchemy-bot
Copy link
Author

jvanasco (@jvanasco) wrote:

Yes. Their server-side-include variation of a "compile-time" inclusion is essentially what I'm talking about (the format <%@ include file="header.html" %>) I'm not talking about the "on request" server side includes.

The main goal is to be able define blocks in a namespace file, and import them as-is.

Consider a namespace file that defines this:

## namespace.mako
<%block name="header">
	HEADER
	<%block name="sub_header">SUBHEADER</%block>
</%block>	

The current Mako implementation doesn't let you recycle that into another template as inhertitable blocks.
The current implementation requires block names be defined in the parent template.

So my goal is that writing something like this:

## parent.mako
<%namespace name="t_headers" file="namespace.mako"/>
<%block name="header" source="t_headers.header"></%block>	

Would import the code at compile-time from the namespace, and be equivalent to writing:

## parent.mako
<%block name="header">
	HEADER
	<%block name="sub_header">SUBHEADER</%block>
</%block>	

The workaround right now, is to make the header components separate, and then declare them on the parent files:

## namespace.mako
<%block name="content__header">
	HEADER
</%block>	
<%block name="content__subheader">
	subheader
</%block>	
<%block name="header">
	<%doc>
		This is only a reference implementation. 
		It can be executed as a "def" to print the header components, but is usesless as a "block"
	</%doc>
	${content__header()}
	<%block name="sub_header">${content__subheader()}</%block>
</%block>

## parent.mako
<%namespace name="t_headers" file="namespace.mako"/>
<%block name="header">
	${t_headers.content__header()}
	<%block name="sub_header">${t_headers.content__subheader()}</%block>
</%block>	

@sqlalchemy-bot
Copy link
Author

Michael Bayer (@zzzeek) wrote:

all the mechanics of namespaces are fully at runtime. getting a namespace to mean something at compile time would be an entirely new set of features. I'd rather name it something else. but also I'm not really sold on server side includes anyway since Python itself has no similar concept, and neither do any modern template languages I'm familiar with. You don't see them used in modern JSP either.

@sqlalchemy-bot
Copy link
Author

jvanasco (@jvanasco) wrote:

"namespace" is just used as an example.

The proposal that i've been digging through the internals to possibly implement isn't directly for global "compile time" SSIs. (i'm just going to use the phrase compile time include because that's the specific variant we're talking about. a lot of SSIs are run-time).

I am only focused on being able to import a fully defined inheritance block from another file as-is. This only relates to compile-time is because of Mako's implementation details on how it handles the <%block> template tags. In other templating systems, this could (potentially) be handled at runtime.

I roughly solved my problem with a preprocessor that parses a custom tag. It's not an ideal solution because the preprocessor isn't aware of the TemplateLookup, and I had to kludge that in.

I think it could be a valuable feature to future versions if TemplateLookup supported a preprocessor variant that can pass the TemplateLookup object. That could allow processors to be more aware of the Mako environment. If you think it's useful, I'd be willing to do a PR for something like this:
• add kwarg preprocessor_accepts_templatelookup to TemplateLookup, Template, Lexer init; handle accordingly in Lexer
• new method TemplateLookup.get_filepath(self, url)

This looks to be related to the compile time concept (though it may be entirely different); doing some testing I encountered this implementation detail:

Given:

#parent.mako
<%block name="header">
    [SITE.header]
    <%block name="header_sub">[SITE.header_sub]</%block>
</%block>

A child can overwrite header or header_sub.

However, a child can not declare a blocks with addressable blocks:

# child.mako
<%inherit file="parent.mako"/>
<%block name="header">
    overwrites parent.mako#header
    <%block name="header_sub">will not render</%block>
    <%block name="header_new_block">but this will render</%block>
</%block>

The header_sub block won't render in child.mako, or anything that inherits it.

We have a lot of C > inherits > B > inherits A. Somehow we never encountered this before.

@sqlalchemy-bot
Copy link
Author

Michael Bayer (@zzzeek) wrote:

However, a child can not declare a blocks with addressable blocks:

OK this is how this works. You can make that header_sub render if you just did this:

#!

    <%block name="header">
        overwrites parent.mako#header
        ${header_sub()}
        <%block name="header_sub">will not render</%block>
        <%block name="header_new_block">but this will render</%block>
    </%block>

which is like yuk, but there's a reason, which is because the blocks were meant to be cascaded like this:

#!

    <%block name="header">
        overwrites parent.mako#header
        ${parent.header()}
        <%block name="header_sub">will not render</%block>
        <%block name="header_new_block">but this will render</%block>
    </%block>

where above, you get:

#!

    overwrites parent.mako#header
        
    [SITE.header]
    will not render

        
        but this will render

I'm not sure how to have it both ways unless we added more flags and switches to <%block>.

@sqlalchemy-bot
Copy link
Author

jvanasco (@jvanasco) wrote:

which is like yuk, but there's a reason, which is because the blocks were meant to be cascaded like this

Wow. That IS weird, but I understand why it's necessary now. In any event, it works - so no complaints here.

If I could figure out a way to describe this for the docs, I'd submit a PR. One day...

A (better) way to write that and preserve the whitespace would be...

<%block name="header">
    overwrites parent.mako#header
    <%block name="header_sub">will not render</%block>${header_sub()}
    <%block name="header_new_block">but this will render</%block>
</%block>

The call to ${header_sub()} could go before of after the block definition to render the same whitespace.

@bourke bourke added this to Deep Freeze in Mako prioritization Nov 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Development

No branches or pull requests

1 participant