panel icon indicating copy to clipboard operation
panel copied to clipboard

UndefinedError while saving a panel object to html file

Open Davide-sd opened this issue 10 months ago • 4 comments

ALL software version info

OS: Ubuntu 22.04 Python: 3.10.12 Panel: 1.4.0 Bokeh: 3.4.0

Description of expected behavior and the observed behavior

I developed a package that creates interactive plots. Considering the great number of examples in the documentation, these are the steps I use to build it (on a headless server):

  1. create and populate a panel template.
  2. Save the template to an html file.
  3. Load the html file with selenium, take a screenshot and save it.
  4. Include the screenshot in the docs.

With panel 1.4.0 I'm getting an error in step 2, saving the template to an html file. Previous versions worked fine.

Complete, minimal, self-contained example code that reproduces the issue

from bokeh.plotting import figure, show
from bokeh.io import output_notebook
import numpy as np
import panel as pn

x = np.linspace(-np.pi, np.pi)
y = np.cos(x)
f = figure()
f.line(x, y)

p = pn.template.BootstrapTemplate(
    title="Hello World",
    sidebar=["# Hello Sidebar", "This is text for the *sidebar*"],
    main=[pn.pane.panel(f)],
)
p.save("test_panel.html")

Stack traceback and/or browser JavaScript console output

---------------------------------------------------------------------------
UndefinedError                            Traceback (most recent call last)
Cell In[1], line 17
     11 p = pn.template.BootstrapTemplate(
     12     title="Hello World",
     13     sidebar=["# Hello Sidebar", "This is text for the *sidebar*"],
     14     main=[pn.pane.panel(f)],
     15 )
     16 # p.servable().show()
---> 17 p.save("test_panel.html")

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/template/base.py:473, in BaseTemplate.save(self, filename, title, resources, embed, max_states, max_opts, embed_json, json_prefix, save_path, load_path)
    470 if embed:
    471     raise ValueError("Embedding is not yet supported on Template.")
--> 473 return save(
    474     self, filename, title, resources, self.template,
    475     self._render_variables, embed, max_states, max_opts,
    476     embed_json, json_prefix, save_path, load_path
    477 )

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/io/save.py:290, in save(panel, filename, title, resources, template, template_variables, embed, max_states, max_opts, embed_json, json_prefix, save_path, load_path, progress, embed_states, as_png, **kwargs)
    288 # Set resource mode
    289 with set_resource_mode(resources):
--> 290     html = file_html(doc, resources, title, **kwargs)
    291 if hasattr(filename, 'write'):
    292     if isinstance(filename, io.BytesIO):

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/io/save.py:164, in file_html(models, resources, title, template, template_variables, theme, _always_new)
    162 title = _title_from_models(models_seq, title)
    163 bundle = bundle_resources(models_seq, resources)
--> 164 return html_page_for_render_items(
    165     bundle, docs_json, render_items, title=title, template=template,
    166     template_variables=template_variables
    167 )

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/bokeh/embed/elements.py:150, in html_page_for_render_items(bundle, docs_json, render_items, title, template, template_variables)
    147 elif isinstance(template, str):
    148     template = get_env().from_string("{% extends base %}\n" + template)
--> 150 html = template.render(context)
    151 return html

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/jinja2/environment.py:1301, in Template.render(self, *args, **kwargs)
   1299     return self.environment.concat(self.root_render_func(ctx))  # type: ignore
   1300 except Exception:
-> 1301     self.environment.handle_exception()

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/jinja2/environment.py:936, in Environment.handle_exception(self, source)
    931 """Exception handling helper.  This is used internally to either raise
    932 rewritten exceptions or return a rendered traceback for the template.
    933 """
    934 from .debug import rewrite_traceback_stack
--> 936 raise rewrite_traceback_stack(source=source)

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/template/bootstrap/bootstrap.html:1, in top-level template code()
----> 1 {% extends "base/base.html" %}
      2 
      3 {% block preamble %}

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/template/base/base.html:1, in top-level template code()
----> 1 {% extends "base.html" %}
      2 
      3 <!-- goes in body -->

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/_templates/base.html:20, in top-level template code()
     18 <!DOCTYPE html>
     19 <html lang="en" {{ html_attrs | default("", true) }}>
---> 20   {% block head %}
     21   <head>
     22   {% block inner_head %}

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/_templates/base.html:22, in block 'head'()
     20 {% block head %}
     21 <head>
---> 22 {% block inner_head %}
     23   <meta charset="utf-8">
     24   <title>{% block title %}{{ title | e if title else "Panel App" }}{% endblock %}</title>

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/_templates/base.html:29, in block 'inner_head'()
     27   {% if manifest_url %}<link rel="manifest" href="{{ manifest_url }}">{% endif %}
     28   {% if theme_name == "dark"%}<style>html { background-color: #121212 }</style>{% endif %}
---> 29 {%  block preamble -%}{%- endblock %}
     30 {%  block resources %}
     31   <style>

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/panel/template/bootstrap/bootstrap.html:6, in block 'preamble'()
      4 {{ super() }}
      5 <!-- Template JS -->
----> 6 {% for src in template_resources['js'].values() %}
      7 <script src="{{ src }}"></script>
      8 {% endfor %}

File ~/Documents/Development/envs/plot/lib/python3.10/site-packages/jinja2/environment.py:466, in Environment.getitem(self, obj, argument)
    464 """Get an item or attribute of an object but prefer the item."""
    465 try:
--> 466     return obj[argument]
    467 except (AttributeError, TypeError, LookupError):
    468     if isinstance(argument, str):

UndefinedError: 'template_resources' is undefined

Screenshots or screencasts of the bug in action

  • [ ] I may be interested in making a pull request to address this

Davide-sd avatar Apr 02 '24 12:04 Davide-sd

I am having the same issue in my application. I was able to reproduce with the code example above. If I set the panel version to 1.3.8 the sample runs without error. Any version >= 1.4.0 reproduces the issue.

rahedges avatar Apr 27 '24 19:04 rahedges

Can confirm it is an issue with MaterialTemplate on Panel 1.4.2

itsgifnotjiff avatar May 15 '24 14:05 itsgifnotjiff

Confirmed also here. An active issue with on Panel 1.4.2. Okay if downloaded to 1.3.8. Would love to have this bug fixed.

oxfordroadmap avatar Jun 07 '24 03:06 oxfordroadmap

The issue seemed to be resolved if the following one-line code is restored from panel/template/base.py at line number 146 @philippjfr

    self._render_variables['template_resources'] = self.resolve_resources()

I have noticed the commit here: https://github.com/holoviz/panel/commit/d6f5117b06da92c127585b29b03da9b71c6260ab

oxfordroadmap avatar Jun 07 '24 08:06 oxfordroadmap

Can confirm it is an issue with MaterialTemplate on Panel 1.4.5

I tried with ressources='CDN' and ressources='INLINE'.

itsgifnotjiff avatar Aug 09 '24 15:08 itsgifnotjiff