summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES1
-rw-r--r--sphinx/ext/autosummary/generate.py40
-rw-r--r--sphinx/util/template.py38
3 files changed, 64 insertions, 15 deletions
diff --git a/CHANGES b/CHANGES
index ad544bf12..b7eff7554 100644
--- a/CHANGES
+++ b/CHANGES
@@ -30,6 +30,7 @@ Features added
caption to the toctree
* #248, #6040: autosummary: Add ``:recursive:`` option to autosummary directive
to generate stub files recursively
+* #7535: sphinx-autogen: crashes when custom template uses inheritance
* #7481: html theme: Add right margin to footnote/citation labels
* #7482: html theme: CSS spacing for code blocks with captions and line numbers
* #7443: html theme: Add new options :confval:`globaltoc_collapse` and
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 0449837ff..4d680acd6 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -25,25 +25,27 @@ import pydoc
import re
import sys
import warnings
+from os import path
from typing import Any, Callable, Dict, List, NamedTuple, Set, Tuple
-from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
+from jinja2 import TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment
import sphinx.locale
from sphinx import __display_version__
from sphinx import package_dir
from sphinx.builders import Builder
+from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.ext.autodoc import Documenter
from sphinx.ext.autosummary import import_by_name, get_documenter
-from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.locale import __
from sphinx.registry import SphinxComponentRegistry
from sphinx.util import logging
from sphinx.util import rst
from sphinx.util.inspect import safe_getattr
from sphinx.util.osutil import ensuredir
+from sphinx.util.template import SphinxTemplateLoader
if False:
# For type annotation
@@ -59,6 +61,7 @@ class DummyApplication:
def __init__(self) -> None:
self.registry = SphinxComponentRegistry()
self.messagelog = [] # type: List[str]
+ self.translator = None
self.verbosity = 0
self._warncount = 0
self.warningiserror = False
@@ -67,6 +70,21 @@ class DummyApplication:
pass
+class DummyBuilder:
+ """Dummy builder class for sphinx-autogen command."""
+
+ def __init__(self, app: DummyApplication, template_path: str) -> None:
+ if template_path:
+ templates_path = [path.abspath(template_path)]
+ else:
+ templates_path = []
+
+ self.app = app
+ self.srcdir = "/"
+ self.config = Config(overrides={'templates_path': templates_path})
+ self.config.init_values()
+
+
AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
('path', str),
('template', str),
@@ -110,16 +128,10 @@ class AutosummaryRenderer:
"""A helper class for rendering."""
def __init__(self, builder: Builder, template_dir: str) -> None:
- loader = None # type: BaseLoader
- template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
- if builder is None:
- if template_dir:
- template_dirs.insert(0, template_dir)
- loader = FileSystemLoader(template_dirs)
- else:
- # allow the user to override the templates
- loader = BuiltinTemplateLoader()
- loader.init(builder, dirs=template_dirs)
+ system_templates_path = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')]
+ loader = SphinxTemplateLoader(builder.srcdir,
+ builder.config.templates_path,
+ system_templates_path)
self.env = SandboxedEnvironment(loader=loader)
self.env.filters['escape'] = rst.escape
@@ -514,9 +526,9 @@ def main(argv: List[str] = sys.argv[1:]) -> None:
logging.setup(app, sys.stdout, sys.stderr) # type: ignore
setup_documenters(app)
args = get_parser().parse_args(argv)
+ builder = DummyBuilder(app, args.templates)
generate_autosummary_docs(args.source_file, args.output_dir,
- '.' + args.suffix,
- template_dir=args.templates,
+ '.' + args.suffix, builder=builder, # type: ignore
imported_members=args.imported_members,
app=app)
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 1337f407c..2449a60a1 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -10,8 +10,11 @@
import os
from functools import partial
-from typing import Dict, List, Union
+from os import path
+from typing import Callable, Dict, List, Tuple, Union
+from jinja2 import TemplateNotFound
+from jinja2.environment import Environment
from jinja2.loaders import BaseLoader
from jinja2.sandbox import SandboxedEnvironment
@@ -94,3 +97,36 @@ class ReSTRenderer(SphinxRenderer):
self.env.filters['e'] = rst.escape
self.env.filters['escape'] = rst.escape
self.env.filters['heading'] = rst.heading
+
+
+class SphinxTemplateLoader(BaseLoader):
+ """A loader supporting template inheritance"""
+
+ def __init__(self, confdir: str, templates_paths: List[str],
+ system_templates_paths: List[str]) -> None:
+ self.loaders = []
+ self.sysloaders = []
+
+ for templates_path in templates_paths:
+ loader = SphinxFileSystemLoader(path.join(confdir, templates_path))
+ self.loaders.append(loader)
+
+ for templates_path in system_templates_paths:
+ loader = SphinxFileSystemLoader(templates_path)
+ self.loaders.append(loader)
+ self.sysloaders.append(loader)
+
+ def get_source(self, environment: Environment, template: str) -> Tuple[str, str, Callable]:
+ if template.startswith('!'):
+ # search a template from ``system_templates_paths``
+ loaders = self.sysloaders
+ template = template[1:]
+ else:
+ loaders = self.loaders
+
+ for loader in loaders:
+ try:
+ return loader.get_source(environment, template)
+ except TemplateNotFound:
+ pass
+ raise TemplateNotFound(template)