API/ENH: Styler template arguments #15649

Closed
chris-b1 opened this Issue Mar 10, 2017 · 9 comments

Comments

Projects
None yet
2 participants
Contributor

chris-b1 commented Mar 10, 2017

Usecase - I have a Styler subclass with a new template that I want to pass some arbitrary stuff into - which currently there isn't a great way to do (right now I'm overriding and re-implementing .render()).

Open to suggestions on the api, but I was thinking of adding template_args argument that would be passed to the template.

from pandas.formats.style import Styler
from jinja2 import Template

class MyStyler(Styler):
    template = Template("""
        .... omitted
        {{ my_value_1 }}
        """)

MyStyler(df, template_args={'my_value_1': '...'}).render()

xref #11610 (master tracker)

pandas 0.19.2

chris-b1 added the IO HTML label Mar 10, 2017

Contributor

TomAugspurger commented Mar 10, 2017

Are you familiar with Jinja's template inheritence? I wonder if we inserted some {% block ... %} in good places, it'd be easier for people to extend it and insert their own?

Contributor

TomAugspurger commented Mar 10, 2017

Oh, are these template_args known when instantiating the data, or at render-time?

I think django gets around that problem by providing a get_context_data() method that subclasses can override. It returns a dict that gets sent to the template engine.

Contributor

chris-b1 commented Mar 10, 2017 edited

In my case the template_args are known at render time.

I was actually just looking at how jinja does inheritance, - some {% bock ... %} extension points would also be good, right now I'm copy-pasting the whole template and adding what I need, which of course isn't great.

Contributor

TomAugspurger commented Mar 13, 2017

@chris-b1 can you take a look at https://github.com/TomAugspurger/pandas/commit/aafba92e65389662be5265687f38f1cc04c4f79e and let me know if that would be sufficient? I moved it into it's own template file to make things easier to work on.

I also added **kwargs to render. I think that'll be a bit nicer than passing template_args to your Style subclass.

I'll add some tests and submit a PR, but it could be a bit.

Contributor

chris-b1 commented Mar 13, 2017

@TomAugspurger thanks! Made one inline comment on the blocks.

My only question / thing needing docs would be how to actually extend that template. I understand the template syntax ({% extends 'html.tpl' %} ...), but it's not clear to me how I would create a jinja2 Environment containing both my template and the base?

Contributor

TomAugspurger commented Mar 13, 2017

My only question / thing needing docs would be how to actually extend that template.

I'll look into this. I think the idea is to use a PrefixLoader or ChoiceLoader.

class MyStyler(Styler):
    my_loader = FileSystemLoader("templates/")
    loader = ChoiceLoader([my_loader, Styler.loader])
    template_name = 'mytemplate.tpl'

then if you make a file templates/mytemplate.tpl, you should be able to {% extends "html.tpl" %}. I'll have a couple fixes to push first, but I think that'll work...

Contributor

TomAugspurger commented Apr 3, 2017

@chris-b1 http://nbviewer.jupyter.org/gist/anonymous/c84f30b41609fef7fd1c06b9fd748729 has an example (using my branch)

I'll get things cleaned up and submitted as a PR this week.

Contributor

chris-b1 commented Apr 3, 2017 edited

Very nice! Might be nice to wrap the subclassing into some kind of factory function to lessen the jinja2 knowledge required? Roughly

def make_styler_subclass(template_folder, visible_template_name):
    loader = ChoiceLoader([
        FileSystemLoader(template_folder),
        PackageLoader("pandas.formats")
    ])
    class MyStyler(pd.formats.style.Styler):
        env = Environment(loader=loader)
        template = env.get_template(visible_template_name)
    return MyStyler
Contributor

TomAugspurger commented May 31, 2017

Closed by #15649 (I think solved everything, just forget to close it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment