0

I am programming a Bokeh Server Application with templates. I have added some custom CSS and JS files. But when I make some changes the files are reloaded from the cache and I cannot see the new changes. One solution is to add a variable at the end of the URL in order to load them from scratch just if there were some changes. Bokeh resources work like that.

I have added the resources as in this example so far

{% extends base %}

<!-- goes in head -->
{% block preamble %}
    <link href="app/static/css/custom.min.css" rel="stylesheet">
    <script type="text/javascript" src="app/static/js/custom.min.js"></script>
{% endblock %}

<!-- goes in body -->
{% block contents %}
    <div> {{ embed(roots.scatter) }} </div>
    <div> {{ embed(roots.line) }} </div>
{% endblock %}

Is there a builtin way to add these hashes? The result I would like to have is:

<link href="app/static/css/custom.min.css?v=f5ee62ee57a37d94c35e8f7be218e11fc63df459" rel="stylesheet">

This hash string must be added before the page is loaded to make it work well

I found this class, but it seems just for Bokeh resources

ChesuCR
  • 8,522
  • 4
  • 37
  • 89

1 Answers1

1

Bokeh uses Jinja2 templates but it doesn't provide any access to template variables. One of possible workarounds is to create a custom template class:

from hashlib import md5

from jinja2 import Template

from bokeh.io import save
from bokeh.models import Div


class ExtendedTemplate(Template):
    def render(self, *args, **kwargs):
        if 'with_hash' not in kwargs:
            kwargs['with_hash'] = with_hash
        return super().render(*args, **kwargs)


t = ExtendedTemplate("""\
<link href="{{ with_hash('app/static/css/custom.min.css') }}" rel="stylesheet">
""")


def with_hash(s):
    with open(s, 'rb') as f:
        return f'{s}?v={md5(f.read()).hexdigest()}'


save(Div(), template=t)

Note that this particular code assumes that the CSS file's path is accessible from its current working directory.

Eugene Pakhomov
  • 6,298
  • 3
  • 23
  • 44
  • Your example is just to create a standalone HTML right? As I am programming a "Bokeh Server Application", I use `curdoc()` to work with the current document. Can your solution be used in that case? I use `add_root(model, setter=None)` to add objects to the document, but it does not have the argument "template" – ChesuCR May 14 '20 at 16:10
  • What I found is that I can send the full link or script header from python using these [template variables](https://docs.bokeh.org/en/latest/docs/reference/document.html?highlight=template_variables#bokeh.document.document.Document.template_variables). But I cannot use them inside the attributes like this ``, it just works with the whole node – ChesuCR May 14 '20 at 16:18
  • Okay, it worked! And I had to add this in order to use my custom template written in an external file: `ExtendedTemplate(open('app/templates/index.html').read()` – ChesuCR May 14 '20 at 23:43