readthedocs / readthedocs/sphinx-autoapi
Extendable templates?
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 494
- Forks
- 147
- Avg merge
- 9m
- Merged PRs (30d)
- 1
Description
In my local python/module.rst I only need to override one block of the default template.
{% extends "python/module.rst" %}
{% block functions scoped %}
{% endblock %}
That fails, somewhat expectedly, with an RecursionError. My idea for a workaround was to inject a custom filter to make the path for the extend absolute.
def _autoapi_prepare_jinja_env(jinja_env):
jinja_env.filters["base_template"] = lambda value: f'{autoapi.settings.TEMPLATE_DIR}/{value}'
autoapi_prepare_jinja_env = _autoapi_prepare_jinja_env
and then make the child template
{% extends "python/module.rst" | base_template %}
{% block functions scoped %}
{% endblock %}
That however fails since jinja's FileSystemLoader (or Environment?) always appends given paths as relative to its preset basedir(s). So then I hacked the jinja env further, including a new loader that has the filesystem root as the last search path.
autoapi_template_dir = '/my/local/template_dir'
def _autoapi_prepare_jinja_env(jinja_env):
import jinja2
tpl_dir = autoapi.settings.TEMPLATE_DIR
jinja_env = jinja_env.overlay(loader=jinja2.FileSystemLoader([tpl_dir, autoapi_template_dir, '/']))
jinja_env.filters["base_template"] = lambda value: f'{tpl_dir}/{value}'
Jinja still doesn't find the base template with the absolute path though, so I used a custom loader. Also the overlay doesn't actually use MyLoader.
autoapi_template_dir = this_dir / '_templates' / 'autoapi'
import jinja2
from os.path import join, exists, getmtime
class MyLoader(jinja2.FileSystemLoader):
def get_source(self, environment, template):
try:
return super().get_source(environment, template)
except jinja2.TemplateNotFound:
path = template
print(f"LOAD {template}")
mtime = getmtime(template)
with file(template) as f:
source = f.read().decode('utf-8')
return source, template, lambda: mtime == getmtime(template)
def _autoapi_prepare_jinja_env(jinja_env):
tpl_dir = autoapi.settings.TEMPLATE_DIR
jinja_env.filters["base_template"] = lambda value: f'{tpl_dir}/{value}'
jinja_env.loader = MyLoader([tpl_dir, autoapi_template_dir])
This "works" as in I get no error.
Problem is AFAICT my custom get_source is only called once. For index.rst.
So bottom line I'm looking for either the problem with my workaround, or how I could generally make this workflow possible in autoapi, less hackish.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the recursion with the local python/module.rst override and inspect _autoapi_prepare_jinja_env, the Jinja Environment/FileSystemLoader setup, and the index.rst load path. Define done as allowing a local template to extend the default template and override one block without recursion, while correctly loading inherited templates without a custom loader hack.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- documentation
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100