mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0'
This commit is contained in:
commit
d47f38e22d
30
CHANGES
30
CHANGES
@ -42,6 +42,36 @@ Incompatible changes
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
* ``sphinx.io.FiletypeNotFoundError``
|
||||
* ``sphinx.io.get_filetype()``
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
* #6910: inheritance_diagram: Make the background of diagrams transparent
|
||||
* #6446: duration: Add ``sphinx.ext.durations`` to inspect which documents slow
|
||||
down the build
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #6925: html: Remove redundant type="text/javascript" from <script> elements
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
Release 2.3.1 (in development)
|
||||
==============================
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
|
2
doc/_themes/sphinx13/layout.html
vendored
2
doc/_themes/sphinx13/layout.html
vendored
@ -30,7 +30,7 @@
|
||||
.related { display: none; }
|
||||
{% endif %}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
// intelligent scrolling of the sidebar content
|
||||
$(window).scroll(function() {
|
||||
var sb = $('.sphinxsidebarwrapper');
|
||||
|
@ -26,6 +26,16 @@ The following is a list of deprecated interfaces.
|
||||
- (will be) Removed
|
||||
- Alternatives
|
||||
|
||||
* - ``sphinx.io.FiletypeNotFoundError``
|
||||
- 2.4
|
||||
- 4.0
|
||||
- ``sphinx.errors.FiletypeNotFoundError``
|
||||
|
||||
* - ``sphinx.io.get_filetype()``
|
||||
- 2.4
|
||||
- 4.0
|
||||
- ``sphinx.util.get_filetype()``
|
||||
|
||||
* - ``sphinx.builders.gettext.POHEADER``
|
||||
- 2.3
|
||||
- 4.0
|
||||
|
@ -89,7 +89,7 @@ Google Analytics
|
||||
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'XXX account number XXX']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
@ -101,7 +101,7 @@ Google Analytics
|
||||
<div class="footer">This page uses <a href="https://analytics.google.com/">
|
||||
Google Analytics</a> to collect statistics. You can disable it by blocking
|
||||
the JavaScript coming from www.google-analytics.com.
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
(function() {
|
||||
var ga = document.createElement('script');
|
||||
ga.src = ('https:' == document.location.protocol ?
|
||||
@ -132,7 +132,6 @@ Google Search
|
||||
(function() {
|
||||
var cx = '......';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
|
11
doc/usage/extensions/duration.rst
Normal file
11
doc/usage/extensions/duration.rst
Normal file
@ -0,0 +1,11 @@
|
||||
:mod:`sphinx.ext.duration` -- Measure durations of Sphinx processing
|
||||
====================================================================
|
||||
|
||||
.. module:: sphinx.ext.duration
|
||||
:synopsis: Measure durations of Sphinx processing
|
||||
|
||||
.. versionadded:: 2.3
|
||||
|
||||
This extension measures durations of Sphinx processing and show its
|
||||
result at end of the build. It is useful for inspecting what document
|
||||
is slowly built.
|
@ -23,6 +23,7 @@ These extensions are built in and can be activated by respective entries in the
|
||||
autosummary
|
||||
coverage
|
||||
doctest
|
||||
duration
|
||||
extlinks
|
||||
githubpages
|
||||
graphviz
|
||||
|
2
setup.py
2
setup.py
@ -47,7 +47,7 @@ extras_require = {
|
||||
'html5lib',
|
||||
'flake8>=3.5.0',
|
||||
'flake8-import-order',
|
||||
'mypy>=0.750',
|
||||
'mypy>=0.761',
|
||||
'docutils-stubs',
|
||||
],
|
||||
}
|
||||
|
@ -117,7 +117,6 @@ class JavaScript(str):
|
||||
self = str.__new__(cls, filename) # type: ignore
|
||||
self.filename = filename
|
||||
self.attributes = attributes
|
||||
self.attributes.setdefault('type', 'text/javascript')
|
||||
|
||||
return self
|
||||
|
||||
@ -1098,7 +1097,6 @@ def setup_js_tag_helper(app: Sphinx, pagename: str, templatexname: str,
|
||||
attrs.append('src="%s"' % pathto(js.filename, resource=True))
|
||||
else:
|
||||
# str value (old styled)
|
||||
attrs.append('type="text/javascript"')
|
||||
attrs.append('src="%s"' % pathto(js, resource=True))
|
||||
return '<script %s>%s</script>' % (' '.join(attrs), body)
|
||||
|
||||
|
@ -126,3 +126,8 @@ class PycodeError(Exception):
|
||||
class NoUri(Exception):
|
||||
"""Raised by builder.get_relative_uri() if there is no URI available."""
|
||||
pass
|
||||
|
||||
|
||||
class FiletypeNotFoundError(Exception):
|
||||
"Raised by get_filetype() if a filename matches no source suffix."
|
||||
pass
|
||||
|
96
sphinx/ext/duration.py
Normal file
96
sphinx/ext/duration.py
Normal file
@ -0,0 +1,96 @@
|
||||
"""
|
||||
sphinx.ext.duration
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Measure durations of Sphinx processing.
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from itertools import islice
|
||||
from operator import itemgetter
|
||||
from typing import cast
|
||||
from typing import Dict, List
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.domains import Domain
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DurationDomain(Domain):
|
||||
"""A domain for durations of Sphinx processing."""
|
||||
name = 'duration'
|
||||
|
||||
@property
|
||||
def reading_durations(self) -> Dict[str, timedelta]:
|
||||
return self.data.setdefault('reading_durations', {})
|
||||
|
||||
def note_reading_duration(self, duration: timedelta):
|
||||
self.reading_durations[self.env.docname] = duration
|
||||
|
||||
def clear(self) -> None:
|
||||
self.reading_durations.clear()
|
||||
|
||||
def clear_doc(self, docname: str) -> None:
|
||||
self.reading_durations.pop(docname, None)
|
||||
|
||||
def merge_domaindata(self, docnames: List[str], otherdata: Dict[str, timedelta]) -> None:
|
||||
for docname, duration in otherdata.items():
|
||||
if docname in docnames:
|
||||
self.reading_durations[docname] = duration
|
||||
|
||||
|
||||
def on_builder_inited(app: Sphinx) -> None:
|
||||
"""Initialize DurationDomain on bootstrap.
|
||||
|
||||
This clears results of last build.
|
||||
"""
|
||||
domain = cast(DurationDomain, app.env.get_domain('duration'))
|
||||
domain.clear()
|
||||
|
||||
|
||||
def on_source_read(app: Sphinx, docname: str, content: List[str]) -> None:
|
||||
"""Start to measure reading duration."""
|
||||
app.env.temp_data['started_at'] = datetime.now()
|
||||
|
||||
|
||||
def on_doctree_read(app: Sphinx, doctree: nodes.document) -> None:
|
||||
"""Record a reading duration."""
|
||||
started_at = app.env.temp_data.get('started_at')
|
||||
duration = datetime.now() - started_at
|
||||
domain = cast(DurationDomain, app.env.get_domain('duration'))
|
||||
domain.note_reading_duration(duration)
|
||||
|
||||
|
||||
def on_build_finished(app: Sphinx, error):
|
||||
"""Display duration ranking on current build."""
|
||||
domain = cast(DurationDomain, app.env.get_domain('duration'))
|
||||
durations = sorted(domain.reading_durations.items(), key=itemgetter(1), reverse=True)
|
||||
if not durations:
|
||||
return
|
||||
|
||||
logger.info('')
|
||||
logger.info(__('====================== slowest reading durations ======================='))
|
||||
for docname, d in islice(durations, 5):
|
||||
logger.info('%d.%03d %s', d.seconds, d.microseconds / 1000, docname)
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_domain(DurationDomain)
|
||||
app.connect('builder-inited', on_builder_inited)
|
||||
app.connect('source-read', on_source_read)
|
||||
app.connect('doctree-read', on_doctree_read)
|
||||
app.connect('build-finished', on_build_finished)
|
||||
|
||||
return {
|
||||
'version': 'builtin',
|
||||
'parallel_read_safe': True,
|
||||
'parallel_write_safe': True,
|
||||
}
|
27
sphinx/io.py
27
sphinx/io.py
@ -18,7 +18,8 @@ from docutils.readers import standalone
|
||||
from docutils.transforms.references import DanglingReferences
|
||||
from docutils.writers import UnfilteredWriter
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.errors import FiletypeNotFoundError
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||
from sphinx.transforms import (
|
||||
AutoIndexUpgrader, DoctreeReadEvent, FigureAligner, SphinxTransformer
|
||||
)
|
||||
@ -26,7 +27,7 @@ from sphinx.transforms.i18n import (
|
||||
PreserveTranslatableMessages, Locale, RemoveTranslatableInline,
|
||||
)
|
||||
from sphinx.transforms.references import SphinxDomains
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import logging, get_filetype
|
||||
from sphinx.util import UnicodeDecodeErrorHandler
|
||||
from sphinx.util.docutils import LoggingReporter
|
||||
from sphinx.versioning import UIDTransform
|
||||
@ -192,20 +193,6 @@ class SphinxFileInput(FileInput):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class FiletypeNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_filetype(source_suffix, filename):
|
||||
# type: (Dict[str, str], str) -> str
|
||||
for suffix, filetype in source_suffix.items():
|
||||
if filename.endswith(suffix):
|
||||
# If default filetype (None), considered as restructuredtext.
|
||||
return filetype or 'restructuredtext'
|
||||
else:
|
||||
raise FiletypeNotFoundError
|
||||
|
||||
|
||||
def read_doc(app, env, filename):
|
||||
# type: (Sphinx, BuildEnvironment, str) -> nodes.document
|
||||
"""Parse a document and convert to doctree."""
|
||||
@ -249,3 +236,11 @@ def read_doc(app, env, filename):
|
||||
|
||||
pub.publish()
|
||||
return pub.document
|
||||
|
||||
|
||||
deprecated_alias('sphinx.io',
|
||||
{
|
||||
'FiletypeNotFoundError': FiletypeNotFoundError,
|
||||
'get_filetype': get_filetype,
|
||||
},
|
||||
RemovedInSphinx40Warning)
|
||||
|
@ -129,7 +129,7 @@ class TokenProcessor:
|
||||
def __init__(self, buffers: List[str]) -> None:
|
||||
lines = iter(buffers)
|
||||
self.buffers = buffers
|
||||
self.tokens = tokenize.generate_tokens(lambda: next(lines)) # type: ignore
|
||||
self.tokens = tokenize.generate_tokens(lambda: next(lines))
|
||||
self.current = None # type: Token
|
||||
self.previous = None # type: Token
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
{% block extrahead %}
|
||||
{{ super() }}
|
||||
{% if not embedded and collapse_index %}
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
DOCUMENTATION_OPTIONS.COLLAPSE_INDEX = true;
|
||||
</script>
|
||||
{% endif %}
|
||||
|
@ -87,7 +87,7 @@
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro script() %}
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="{{ pathto('', 1) }}" src="{{ pathto('_static/documentation_options.js', 1) }}"></script>
|
||||
<script id="documentation_options" data-url_root="{{ pathto('', 1) }}" src="{{ pathto('_static/documentation_options.js', 1) }}"></script>
|
||||
{%- for js in script_files %}
|
||||
{{ js_tag(js) }}
|
||||
{%- endfor %}
|
||||
|
@ -11,16 +11,16 @@
|
||||
{% set title = _('Search') %}
|
||||
{%- block scripts %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript" src="{{ pathto('_static/searchtools.js', 1) }}"></script>
|
||||
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
|
||||
{%- endblock %}
|
||||
{% block extrahead %}
|
||||
<script type="text/javascript" src="{{ pathto('searchindex.js', 1) }}" defer></script>
|
||||
<script src="{{ pathto('searchindex.js', 1) }}" defer></script>
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<h1 id="search-documentation">{{ _('Search') }}</h1>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<script type="text/javascript">$('#fallback').hide();</script>
|
||||
<script>$('#fallback').hide();</script>
|
||||
<p>
|
||||
{% trans %}Please activate JavaScript to enable the search
|
||||
functionality.{% endtrans %}
|
||||
|
@ -17,5 +17,5 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
{%- endif %}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
{%- block scripts %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript" src="{{ pathto('_static/bizstyle.js', 1) }}"></script>
|
||||
<script src="{{ pathto('_static/bizstyle.js', 1) }}"></script>
|
||||
{%- endblock %}
|
||||
|
||||
{# put the sidebar before the body #}
|
||||
@ -26,6 +26,6 @@
|
||||
{%- block extrahead %}
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<!--[if lt IE 9]>
|
||||
<script type="text/javascript" src="_static/css3-mediaqueries.js"></script>
|
||||
<script src="_static/css3-mediaqueries.js"></script>
|
||||
<![endif]-->
|
||||
{%- endblock %}
|
||||
|
@ -12,6 +12,6 @@
|
||||
{%- block scripts %}
|
||||
{{ super() }}
|
||||
{% if theme_collapsiblesidebar|tobool %}
|
||||
<script type="text/javascript" src="{{ pathto('_static/sidebar.js', 1) }}"></script>
|
||||
<script src="{{ pathto('_static/sidebar.js', 1) }}"></script>
|
||||
{% endif %}
|
||||
{%- endblock %}
|
||||
|
@ -15,7 +15,7 @@
|
||||
{%- endblock %}
|
||||
{%- block scripts %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript" src="{{ pathto('_static/theme_extras.js', 1) }}"></script>
|
||||
<script src="{{ pathto('_static/theme_extras.js', 1) }}"></script>
|
||||
{%- endblock %}
|
||||
{# do not display relbars #}
|
||||
{% block relbar1 %}{% endblock %}
|
||||
|
@ -22,7 +22,7 @@ from sphinx.config import Config
|
||||
from sphinx.domains.std import make_glossary_term, split_term_classifiers
|
||||
from sphinx.locale import __, init as init_locale
|
||||
from sphinx.transforms import SphinxTransform
|
||||
from sphinx.util import split_index_msg, logging
|
||||
from sphinx.util import split_index_msg, logging, get_filetype
|
||||
from sphinx.util.i18n import docname_to_domain
|
||||
from sphinx.util.nodes import (
|
||||
LITERAL_TYPE_NODES, IMAGE_TYPE_NODES, NodeMatcher,
|
||||
@ -61,7 +61,8 @@ def publish_msgstr(app: "Sphinx", source: str, source_path: str, source_line: in
|
||||
from sphinx.io import SphinxI18nReader
|
||||
reader = SphinxI18nReader()
|
||||
reader.setup(app)
|
||||
parser = app.registry.create_source_parser(app, 'restructuredtext')
|
||||
filetype = get_filetype(config.source_suffix, source_path)
|
||||
parser = app.registry.create_source_parser(app, filetype)
|
||||
doc = reader.read(
|
||||
source=StringInput(source=source,
|
||||
source_path="%s:%s:<translated>" % (source_path, source_line)),
|
||||
|
@ -29,7 +29,9 @@ from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, S
|
||||
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.errors import PycodeError, SphinxParallelError, ExtensionError
|
||||
from sphinx.errors import (
|
||||
PycodeError, SphinxParallelError, ExtensionError, FiletypeNotFoundError
|
||||
)
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.console import strip_colors, colorize, bold, term_width_line # type: ignore
|
||||
@ -117,6 +119,16 @@ def get_matching_docs(dirname: str, suffixes: List[str],
|
||||
break
|
||||
|
||||
|
||||
def get_filetype(source_suffix, filename):
|
||||
# type: (Dict[str, str], str) -> str
|
||||
for suffix, filetype in source_suffix.items():
|
||||
if filename.endswith(suffix):
|
||||
# If default filetype (None), considered as restructuredtext.
|
||||
return filetype or 'restructuredtext'
|
||||
else:
|
||||
raise FiletypeNotFoundError
|
||||
|
||||
|
||||
class FilenameUniqDict(dict):
|
||||
"""
|
||||
A dictionary that automatically generates unique names for its keys,
|
||||
|
@ -1216,8 +1216,8 @@ def test_html_assets(app):
|
||||
'href="https://example.com/custom.css" />' in content)
|
||||
|
||||
# html_js_files
|
||||
assert '<script type="text/javascript" src="_static/js/custom.js"></script>' in content
|
||||
assert ('<script async="async" type="text/javascript" src="https://example.com/script.js">'
|
||||
assert '<script src="_static/js/custom.js"></script>' in content
|
||||
assert ('<script async="async" src="https://example.com/script.js">'
|
||||
'</script>' in content)
|
||||
|
||||
|
||||
|
21
tests/test_ext_duration.py
Normal file
21
tests/test_ext_duration.py
Normal file
@ -0,0 +1,21 @@
|
||||
"""
|
||||
test_ext_duration
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Test sphinx.ext.duration extension.
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='basic',
|
||||
confoverrides={'extensions': ['sphinx.ext.duration']})
|
||||
def test_githubpages(app, status, warning):
|
||||
app.build()
|
||||
|
||||
assert 'slowest reading durations' in status.getvalue()
|
||||
assert re.search('\\d+\\.\\d{3} index\n', status.getvalue())
|
@ -70,7 +70,7 @@ def test_mathjax_options(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
assert ('<script async="async" integrity="sha384-0123456789" type="text/javascript" '
|
||||
assert ('<script async="async" integrity="sha384-0123456789" '
|
||||
'src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?'
|
||||
'config=TeX-AMS-MML_HTMLorMML"></script>' in content)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user