Sphinx: Extension template doesn't have access to updated HTML context?

Created on 1 Feb 2020  路  8Comments  路  Source: sphinx-doc/sphinx

I am developing an extension that includes some Javascript that is configurable based on the user's configuration input. The user should configure the extension with a key in conf.py, and then the value for this key needs to be inserted into a javascript file in the _static directory of the extension. Here's what I'm doing:

First I've defined a function that updates the context based on a config value.

def add_to_context(app, pagename, templatename, context, doctree):
    context['copybutton_skip_text'] = app.config['copybutton_skip_text']

Then I've added a new configuration key and connected the function to update
the HTML context:

def setup(app):
    app.add_config_value("copybutton_skip_text", ">>> ", "html")
    app.connect("html-page-context", add_to_context)

Finally, in my extension, I have a "templatable" javascript file (say, myjavascript.js_t). Inside I am trying to insert the variable like so

var skipText = "{{ copybutton_skip_text }}";

However, in the final rendered js file (myjavascript.js), the result is
always empty (e.g., var skipText = "") no matter how the value is configured.

I can confirm that default HTML context keys are inserted properly (e.g. docstitle)
so it seems like the HTML context update step isn't in effect when the extension JS file is being run through the template system.

Any idea why this would be? Am I doing things in the incorrect order or something?

If it's helpful, here's the PR where I am trying this out: https://github.com/choldgraf/sphinx-copybutton/pull/58

bug html

All 8 comments

Sadly, html-page-context event is only used to HTML pages (as it named). So it can't inject template variables on generating static files.
How about using add_js_file(body='...') instead?

Ahh, I see. So just to make sure that I understand:

While you can use templating w/ static files using _t at the end of the filename, Jinja will only have access to the default template variables that Sphinx provides. Anything that is added manually by updating context in an html-page-context callback will only be available upon generating HTML pages, and not static files.

Is that right? If so I will update https://github.com/sphinx-doc/sphinx/pull/7058 with this information, because it wasn't intuitive to me that extra context variables would work with HTML files but not with static files (I know it technically says "html" in the event, but I suspect many people just equate "html" with "web stuff" and don't assume it literally only means .html files)

As far as I understand:

  • Multiple HTML pages can be generated from a single template. For each instance, there can be differences in the given context. The file names of the generated files are different from the template file name.
  • Only a single file can be created from each template in _static. There is only one "global" context. The file name of the generated file is the same as the template file, except the trailing _t is stripped.

Given that, it makes sense that stuff from the html-page-context is only available in the first case, right?

Also, it doesn't really matter whether the file suffix is .html or whatever, I think the location matters: If a template is in templates_path it is of the first category, if it's in html_static_path, it's of the second.

@mgeier that's a good description above it that makes sense to me, if it's correct :-)

To that extent, I guess the way to achieve the behavior that I want (where a variable is inserted into the static file template) is to use the html_context dictionary on conf.py, and that it isn't possible to do this with variables that are directly encoded in conf.py?

Unless there is another way to update the html context other than within the html-page-context event?

ah I think I figured it out - to update the global HTML context the right time to do this is to define a function like this:

def add_to_context(app, config):
    # Update the global context
    config.html_context.update({'copybutton_skip_text': config.copybutton_skip_text})

and to attach this to the config-inited event. I just tried it and was successfully
able to update my global HTML context in a way that propagates to static files 馃帀

If y'all agree this sounds correct, I'll update that docs PR to explain how to do this so other folks don't have to discover this over several days like I did haha

I can't remember why I added templating feature to copying static files. But I feel it is strange why "static" files are generating "dynamically"? First of all, it seems no document for this feature.

I prefer to use explicitly sphinx.util.fileutil:copy_asset_file() from extensions to generate such file. So -0 for adding it to tips.

@tk0mmiya - it seemed reasonable to me to allow for templatizing static files, especially for people who want configurability on the javascript or CSS side. That said, I have never heard of the function that you describe...how would you use it to let people control javascript behavior based on user configuration? (apologies if this is a stupid question, but I'm coming from the perspective of someone who is still a relative newbie, and trying to find where there are holes in the documentation). I don't really mind what the solution is (so long as it's relatively simple), but I just want to make sure it's documented and discoverable

@tk0miya I think it makes very much sense to use templates in the "static" files in case of a Sphinx theme, where the theme options can be used in the templates.

Using it in Sphinx extensions (as opposed to Sphinx themes) is probably more of a hack, since the user configuration option app.config.html_static_path has to be manipulated by the extension's setup code in order to make this work.

AFAIK, there is no separate API to manipulate the "static path" from a Sphinx extension, or is there?

The function sphinx.util.fileutil:copy_asset_file() sounds really interesting, but how exactly would this be used?
Would this be used in builder-inited? Or in env-updated? Or in html-collect-pages? Or somewhere else?
And how do we get the "context"?

I guess the example we are talking about is the file copybutton.js_t in the directory https://github.com/choldgraf/sphinx-copybutton/tree/master/sphinx_copybutton/_static.

Could this be done without manipulating app.config.html_static_path in https://github.com/choldgraf/sphinx-copybutton/blob/3018dd7bfcc950b0e6f4debfc0e8e139b54de042/sphinx_copybutton/__init__.py#L8?

Was this page helpful?
0 / 5 - 0 ratings