Merge branch '3.x'

This commit is contained in:
Takeshi KOMIYA 2021-01-03 00:20:27 +09:00
commit a9c7dd7037
6 changed files with 97 additions and 27 deletions

View File

@ -76,6 +76,8 @@ Features added
value of the variable if docstring contains ``:meta hide-value:`` in value of the variable if docstring contains ``:meta hide-value:`` in
info-field-list info-field-list
* #8619: html: kbd role generates customizable HTML tags for compound keys * #8619: html: kbd role generates customizable HTML tags for compound keys
* #8634: html: Allow to change the order of JS/CSS via ``priority`` parameter
for :meth:`Sphinx.add_js_file()` and :meth:`Sphinx.add_css_file()`
* #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright` * #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright`
Bugs fixed Bugs fixed

View File

@ -304,7 +304,7 @@ The following variables available in the templates:
.. data:: modules .. data:: modules
List containing names of "public" modules in the package. Only available for List containing names of "public" modules in the package. Only available for
modules that are packages. modules that are packages and the ``recursive`` option is on.
.. versionadded:: 3.1 .. versionadded:: 3.1

View File

@ -911,14 +911,14 @@ class Sphinx:
""" """
self.registry.add_post_transform(transform) self.registry.add_post_transform(transform)
def add_js_file(self, filename: str, **kwargs: str) -> None: def add_js_file(self, filename: str, priority: int = 500, **kwargs: Any) -> None:
"""Register a JavaScript file to include in the HTML output. """Register a JavaScript file to include in the HTML output.
Add *filename* to the list of JavaScript files that the default HTML Add *filename* to the list of JavaScript files that the default HTML
template will include. The filename must be relative to the HTML template will include in order of *priority* (ascending). The filename
static path , or a full URI with scheme. If the keyword argument must be relative to the HTML static path , or a full URI with scheme.
``body`` is given, its value will be added between the If the keyword argument ``body`` is given, its value will be added
``<script>`` tags. Extra keyword arguments are included as between the ``<script>`` tags. Extra keyword arguments are included as
attributes of the ``<script>`` tag. attributes of the ``<script>`` tag.
Example:: Example::
@ -932,23 +932,38 @@ class Sphinx:
app.add_js_file(None, body="var myVariable = 'foo';") app.add_js_file(None, body="var myVariable = 'foo';")
# => <script>var myVariable = 'foo';</script> # => <script>var myVariable = 'foo';</script>
.. list-table:: priority range for JavaScript files
:widths: 20,80
* - Priority
- Main purpose in Sphinx
* - 200
- default priority for built-in JavaScript files
* - 500
- default priority for extensions
* - 800
- default priority for :confval:`html_js_files`
.. versionadded:: 0.5 .. versionadded:: 0.5
.. versionchanged:: 1.8 .. versionchanged:: 1.8
Renamed from ``app.add_javascript()``. Renamed from ``app.add_javascript()``.
And it allows keyword arguments as attributes of script tag. And it allows keyword arguments as attributes of script tag.
"""
self.registry.add_js_file(filename, **kwargs)
if hasattr(self.builder, 'add_js_file'):
self.builder.add_js_file(filename, **kwargs) # type: ignore
def add_css_file(self, filename: str, **kwargs: str) -> None: .. versionchanged:: 3.5
Take priority argument.
"""
self.registry.add_js_file(filename, priority=priority, **kwargs)
if hasattr(self.builder, 'add_js_file'):
self.builder.add_js_file(filename, priority=priority, **kwargs) # type: ignore
def add_css_file(self, filename: str, priority: int = 500, **kwargs: Any) -> None:
"""Register a stylesheet to include in the HTML output. """Register a stylesheet to include in the HTML output.
Add *filename* to the list of CSS files that the default HTML template Add *filename* to the list of CSS files that the default HTML template
will include. The filename must be relative to the HTML static path, will include in order of *priority* (ascending). The filename must be
or a full URI with scheme. The keyword arguments are also accepted for relative to the HTML static path, or a full URI with scheme. The
attributes of ``<link>`` tag. eyword arguments are also accepted for attributes of ``<link>`` tag.
Example:: Example::
@ -963,6 +978,16 @@ class Sphinx:
# => <link rel="alternate stylesheet" href="_static/fancy.css" # => <link rel="alternate stylesheet" href="_static/fancy.css"
# type="text/css" title="fancy" /> # type="text/css" title="fancy" />
.. list-table:: priority range for CSS files
:widths: 20,80
* - Priority
- Main purpose in Sphinx
* - 500
- default priority for extensions
* - 800
- default priority for :confval:`html_css_files`
.. versionadded:: 1.0 .. versionadded:: 1.0
.. versionchanged:: 1.6 .. versionchanged:: 1.6
@ -975,11 +1000,14 @@ class Sphinx:
.. versionchanged:: 1.8 .. versionchanged:: 1.8
Renamed from ``app.add_stylesheet()``. Renamed from ``app.add_stylesheet()``.
And it allows keyword arguments as attributes of link tag. And it allows keyword arguments as attributes of link tag.
.. versionchanged:: 3.5
Take priority argument.
""" """
logger.debug('[app] adding stylesheet: %r', filename) logger.debug('[app] adding stylesheet: %r', filename)
self.registry.add_css_files(filename, **kwargs) self.registry.add_css_files(filename, priority=priority, **kwargs)
if hasattr(self.builder, 'add_css_file'): if hasattr(self.builder, 'add_css_file'):
self.builder.add_css_file(filename, **kwargs) # type: ignore self.builder.add_css_file(filename, priority=priority, **kwargs) # type: ignore
def add_latex_package(self, packagename: str, options: str = None, def add_latex_package(self, packagename: str, options: str = None,
after_hyperref: bool = False) -> None: after_hyperref: bool = False) -> None:

View File

@ -84,10 +84,13 @@ class Stylesheet(str):
attributes = None # type: Dict[str, str] attributes = None # type: Dict[str, str]
filename = None # type: str filename = None # type: str
priority = None # type: int
def __new__(cls, filename: str, *args: str, **attributes: str) -> "Stylesheet": def __new__(cls, filename: str, *args: str, priority: int = 500, **attributes: Any
) -> "Stylesheet":
self = str.__new__(cls, filename) # type: ignore self = str.__new__(cls, filename) # type: ignore
self.filename = filename self.filename = filename
self.priority = priority
self.attributes = attributes self.attributes = attributes
self.attributes.setdefault('rel', 'stylesheet') self.attributes.setdefault('rel', 'stylesheet')
self.attributes.setdefault('type', 'text/css') self.attributes.setdefault('type', 'text/css')
@ -107,10 +110,12 @@ class JavaScript(str):
attributes = None # type: Dict[str, str] attributes = None # type: Dict[str, str]
filename = None # type: str filename = None # type: str
priority = None # type: int
def __new__(cls, filename: str, **attributes: str) -> "JavaScript": def __new__(cls, filename: str, priority: int = 500, **attributes: str) -> "JavaScript":
self = str.__new__(cls, filename) # type: ignore self = str.__new__(cls, filename) # type: ignore
self.filename = filename self.filename = filename
self.priority = priority
self.attributes = attributes self.attributes = attributes
return self return self
@ -284,29 +289,31 @@ class StandaloneHTMLBuilder(Builder):
self.add_css_file(filename, **attrs) self.add_css_file(filename, **attrs)
for filename, attrs in self.get_builder_config('css_files', 'html'): for filename, attrs in self.get_builder_config('css_files', 'html'):
attrs.setdefault('priority', 800) # User's CSSs are loaded after extensions'
self.add_css_file(filename, **attrs) self.add_css_file(filename, **attrs)
def add_css_file(self, filename: str, **kwargs: str) -> None: def add_css_file(self, filename: str, **kwargs: Any) -> None:
if '://' not in filename: if '://' not in filename:
filename = posixpath.join('_static', filename) filename = posixpath.join('_static', filename)
self.css_files.append(Stylesheet(filename, **kwargs)) # type: ignore self.css_files.append(Stylesheet(filename, **kwargs)) # type: ignore
def init_js_files(self) -> None: def init_js_files(self) -> None:
self.add_js_file('jquery.js') self.add_js_file('jquery.js', priority=200)
self.add_js_file('underscore.js') self.add_js_file('underscore.js', priority=200)
self.add_js_file('doctools.js') self.add_js_file('doctools.js', priority=200)
for filename, attrs in self.app.registry.js_files: for filename, attrs in self.app.registry.js_files:
self.add_js_file(filename, **attrs) self.add_js_file(filename, **attrs)
for filename, attrs in self.get_builder_config('js_files', 'html'): for filename, attrs in self.get_builder_config('js_files', 'html'):
attrs.setdefault('priority', 800) # User's JSs are loaded after extensions'
self.add_js_file(filename, **attrs) self.add_js_file(filename, **attrs)
if self.config.language and self._get_translations_js(): if self.config.language and self._get_translations_js():
self.add_js_file('translations.js') self.add_js_file('translations.js')
def add_js_file(self, filename: str, **kwargs: str) -> None: def add_js_file(self, filename: str, **kwargs: Any) -> None:
if filename and '://' not in filename: if filename and '://' not in filename:
filename = posixpath.join('_static', filename) filename = posixpath.join('_static', filename)
@ -1009,6 +1016,10 @@ class StandaloneHTMLBuilder(Builder):
if newtmpl: if newtmpl:
templatename = newtmpl templatename = newtmpl
# sort JS/CSS before rendering HTML
ctx['script_files'].sort(key=lambda js: js.priority)
ctx['css_files'].sort(key=lambda js: js.priority)
try: try:
output = self.templates.render(templatename, ctx) output = self.templates.render(templatename, ctx)
except UnicodeError: except UnicodeError:

View File

@ -60,7 +60,7 @@ class SphinxComponentRegistry:
self.documenters = {} # type: Dict[str, Type[Documenter]] self.documenters = {} # type: Dict[str, Type[Documenter]]
#: css_files; a list of tuple of filename and attributes #: css_files; a list of tuple of filename and attributes
self.css_files = [] # type: List[Tuple[str, Dict[str, str]]] self.css_files = [] # type: List[Tuple[str, Dict[str, Any]]]
#: domains; a dict of domain name -> domain class #: domains; a dict of domain name -> domain class
self.domains = {} # type: Dict[str, Type[Domain]] self.domains = {} # type: Dict[str, Type[Domain]]
@ -91,7 +91,7 @@ class SphinxComponentRegistry:
self.html_block_math_renderers = {} # type: Dict[str, Tuple[Callable, Callable]] self.html_block_math_renderers = {} # type: Dict[str, Tuple[Callable, Callable]]
#: js_files; list of JS paths or URLs #: js_files; list of JS paths or URLs
self.js_files = [] # type: List[Tuple[str, Dict[str, str]]] self.js_files = [] # type: List[Tuple[str, Dict[str, Any]]]
#: LaTeX packages; list of package names and its options #: LaTeX packages; list of package names and its options
self.latex_packages = [] # type: List[Tuple[str, str]] self.latex_packages = [] # type: List[Tuple[str, str]]
@ -358,10 +358,10 @@ class SphinxComponentRegistry:
attrgetter: Callable[[Any, str, Any], Any]) -> None: attrgetter: Callable[[Any, str, Any], Any]) -> None:
self.autodoc_attrgettrs[typ] = attrgetter self.autodoc_attrgettrs[typ] = attrgetter
def add_css_files(self, filename: str, **attributes: str) -> None: def add_css_files(self, filename: str, **attributes: Any) -> None:
self.css_files.append((filename, attributes)) self.css_files.append((filename, attributes))
def add_js_file(self, filename: str, **attributes: str) -> None: def add_js_file(self, filename: str, **attributes: Any) -> None:
logger.debug('[app] adding js_file: %r, %r', filename, attributes) logger.debug('[app] adding js_file: %r, %r', filename, attributes)
self.js_files.append((filename, attributes)) self.js_files.append((filename, attributes))

View File

@ -1204,6 +1204,35 @@ def test_html_assets(app):
'</script>' in content) '</script>' in content)
@pytest.mark.sphinx('html', testroot='html_assets')
def test_assets_order(app):
app.add_css_file('normal.css')
app.add_css_file('early.css', priority=100)
app.add_css_file('late.css', priority=750)
app.add_css_file('lazy.css', priority=900)
app.add_js_file('normal.js')
app.add_js_file('early.js', priority=100)
app.add_js_file('late.js', priority=750)
app.add_js_file('lazy.js', priority=900)
app.builder.build_all()
content = (app.outdir / 'index.html').read_text()
# css_files
expected = ['_static/pygments.css', '_static/alabaster.css', '_static/early.css',
'_static/normal.css', '_static/late.css', '_static/css/style.css',
'https://example.com/custom.css', '_static/lazy.css']
pattern = '.*'.join('href="%s"' % f for f in expected)
assert re.search(pattern, content, re.S)
# js_files
expected = ['_static/early.js', '_static/jquery.js', '_static/underscore.js',
'_static/doctools.js', '_static/normal.js', '_static/late.js',
'_static/js/custom.js', 'https://example.com/script.js', '_static/lazy.js']
pattern = '.*'.join('src="%s"' % f for f in expected)
assert re.search(pattern, content, re.S)
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_copy_source': False}) @pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_copy_source': False})
def test_html_copy_source(app): def test_html_copy_source(app):
app.builder.build_all() app.builder.build_all()