Merge branch '3.x'

This commit is contained in:
Takeshi KOMIYA 2021-01-08 01:23:17 +09:00
commit 5460ea103b
22 changed files with 131 additions and 53 deletions

29
CHANGES
View File

@ -81,6 +81,12 @@ Features added
* #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 * #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()` for :meth:`Sphinx.add_js_file()` and :meth:`Sphinx.add_css_file()`
* #6241: html: Allow to add JS/CSS files to the specific page when an extension
calls ``app.add_js_file()`` or ``app.add_css_file()`` on
:event:`html-page-context` event
* #8649: imgconverter: Skip availability check if builder supports the image
type
* #6241: mathjax: Include mathjax.js only on the document using equations
* #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
@ -102,7 +108,7 @@ Bugs fixed
Testing Testing
-------- --------
Release 3.4.2 (in development) Release 3.4.4 (in development)
============================== ==============================
Dependencies Dependencies
@ -120,15 +126,30 @@ Features added
Bugs fixed Bugs fixed
---------- ----------
Testing
--------
Release 3.4.3 (released Jan 08, 2021)
=====================================
Bugs fixed
----------
* #8655: autodoc: Failed to generate document if target module contains an
object that raises an exception on ``hasattr()``
Release 3.4.2 (released Jan 04, 2021)
=====================================
Bugs fixed
----------
* #8164: autodoc: Classes that inherit mocked class are not documented * #8164: autodoc: Classes that inherit mocked class are not documented
* #8602: autodoc: The ``autodoc-process-docstring`` event is emitted to the * #8602: autodoc: The ``autodoc-process-docstring`` event is emitted to the
non-datadescriptors unexpectedly non-datadescriptors unexpectedly
* #8616: autodoc: AttributeError is raised on non-class object is passed to * #8616: autodoc: AttributeError is raised on non-class object is passed to
autoclass directive autoclass directive
Testing
--------
Release 3.4.1 (released Dec 25, 2020) Release 3.4.1 (released Dec 25, 2020)
===================================== =====================================

View File

@ -376,6 +376,9 @@ Here is a more detailed list of these events.
You can return a string from the handler, it will then replace You can return a string from the handler, it will then replace
``'page.html'`` as the HTML template for this page. ``'page.html'`` as the HTML template for this page.
.. note:: You can install JS/CSS files for the specific page via
:meth:`Sphinx.add_js_file` and :meth:`Sphinx.add_css_file` since v3.5.0.
.. versionadded:: 0.4 .. versionadded:: 0.4
.. versionchanged:: 1.3 .. versionchanged:: 1.3

View File

@ -1003,7 +1003,14 @@ that use Sphinx's HTMLWriter class.
'https://example.com/css/custom.css', 'https://example.com/css/custom.css',
('print.css', {'media': 'print'})] ('print.css', {'media': 'print'})]
As a special attribute, *priority* can be set as an integer to load the CSS
file earlier or lazier step. For more information, refer
:meth:`Sphinx.add_css_files()`.
.. versionadded:: 1.8 .. versionadded:: 1.8
.. versionchanged:: 3.5
Support priority attribute
.. confval:: html_js_files .. confval:: html_js_files
@ -1019,7 +1026,14 @@ that use Sphinx's HTMLWriter class.
'https://example.com/scripts/custom.js', 'https://example.com/scripts/custom.js',
('custom.js', {'async': 'async'})] ('custom.js', {'async': 'async'})]
As a special attribute, *priority* can be set as an integer to load the CSS
file earlier or lazier step. For more information, refer
:meth:`Sphinx.add_css_files()`.
.. versionadded:: 1.8 .. versionadded:: 1.8
.. versionchanged:: 3.5
Support priority attribute
.. confval:: html_static_path .. confval:: html_static_path

View File

@ -917,9 +917,11 @@ class Sphinx:
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 in order of *priority* (ascending). The filename template will include in order of *priority* (ascending). The filename
must be relative to the HTML static path , or a full URI with scheme. must be relative to the HTML static path , or a full URI with scheme.
If the keyword argument ``body`` is given, its value will be added If the priority of JavaScript file is the same as others, the JavaScript
between the ``<script>`` tags. Extra keyword arguments are included as files will be included in order of the registration. If the keyword
attributes of the ``<script>`` tag. argument ``body`` is given, its value will be added between the
``<script>`` tags. Extra keyword arguments are included as attributes of
the ``<script>`` tag.
Example:: Example::
@ -944,6 +946,9 @@ class Sphinx:
* - 800 * - 800
- default priority for :confval:`html_js_files` - default priority for :confval:`html_js_files`
A JavaScript file can be added to the specific HTML page when on extension
calls this method on :event:`html-page-context` event.
.. versionadded:: 0.5 .. versionadded:: 0.5
.. versionchanged:: 1.8 .. versionchanged:: 1.8
@ -951,7 +956,7 @@ class Sphinx:
And it allows keyword arguments as attributes of script tag. And it allows keyword arguments as attributes of script tag.
.. versionchanged:: 3.5 .. versionchanged:: 3.5
Take priority argument. Take priority argument. Allow to add a JavaScript file to the specific page.
""" """
self.registry.add_js_file(filename, priority=priority, **kwargs) self.registry.add_js_file(filename, priority=priority, **kwargs)
if hasattr(self.builder, 'add_js_file'): if hasattr(self.builder, 'add_js_file'):
@ -962,8 +967,10 @@ class Sphinx:
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 in order of *priority* (ascending). The filename must be will include in order of *priority* (ascending). The filename must be
relative to the HTML static path, or a full URI with scheme. The relative to the HTML static path, or a full URI with scheme. If the
eyword arguments are also accepted for attributes of ``<link>`` tag. priority of CSS file is the same as others, the CSS files will be
included in order of the registration. The keyword arguments are also
accepted for attributes of ``<link>`` tag.
Example:: Example::
@ -990,6 +997,9 @@ class Sphinx:
* - 800 * - 800
- default priority for :confval:`html_css_files` - default priority for :confval:`html_css_files`
A CSS file can be added to the specific HTML page when on extension calls
this method on :event:`html-page-context` event.
.. versionadded:: 1.0 .. versionadded:: 1.0
.. versionchanged:: 1.6 .. versionchanged:: 1.6
@ -1004,7 +1014,7 @@ class Sphinx:
And it allows keyword arguments as attributes of link tag. And it allows keyword arguments as attributes of link tag.
.. versionchanged:: 3.5 .. versionchanged:: 3.5
Take priority argument. Take priority argument. Allow to add a CSS file to the specific page.
""" """
logger.debug('[app] adding stylesheet: %r', filename) logger.debug('[app] adding stylesheet: %r', filename)
self.registry.add_css_files(filename, priority=priority, **kwargs) self.registry.add_css_files(filename, priority=priority, **kwargs)

View File

@ -481,6 +481,10 @@ class StandaloneHTMLBuilder(Builder):
rellinks.append((indexname, indexcls.localname, rellinks.append((indexname, indexcls.localname,
'', indexcls.shortname)) '', indexcls.shortname))
# back up script_files and css_files to allow adding JS/CSS files to a specific page.
self._script_files = list(self.script_files)
self._css_files = list(self.css_files)
self.globalcontext = { self.globalcontext = {
'embedded': self.embedded, 'embedded': self.embedded,
'project': self.config.project, 'project': self.config.project,
@ -1014,6 +1018,10 @@ class StandaloneHTMLBuilder(Builder):
self.add_sidebars(pagename, ctx) self.add_sidebars(pagename, ctx)
ctx.update(addctx) ctx.update(addctx)
# revert script_files and css_files
self.script_files[:] = self._script_files
self.css_files[:] = self.css_files
self.update_page_context(pagename, templatename, ctx, event_arg) self.update_page_context(pagename, templatename, ctx, event_arg)
newtmpl = self.app.emit_firstresult('html-page-context', pagename, newtmpl = self.app.emit_firstresult('html-page-context', pagename,
templatename, ctx, event_arg) templatename, ctx, event_arg)

View File

@ -9,7 +9,7 @@
""" """
import re import re
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, cast from typing import TYPE_CHECKING, Any, Dict, Generic, List, Tuple, TypeVar, cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Node from docutils.nodes import Node
@ -31,6 +31,8 @@ if TYPE_CHECKING:
nl_escape_re = re.compile(r'\\\n') nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(.)') strip_backslash_re = re.compile(r'\\(.)')
T = TypeVar('T')
def optional_int(argument: str) -> int: def optional_int(argument: str) -> int:
""" """
@ -45,7 +47,7 @@ def optional_int(argument: str) -> int:
return value return value
class ObjectDescription(SphinxDirective): class ObjectDescription(SphinxDirective, Generic[T]):
""" """
Directive to describe a class, function or similar object. Not used Directive to describe a class, function or similar object. Not used
directly, but subclassed (in domain-specific directives) to add custom directly, but subclassed (in domain-specific directives) to add custom
@ -95,7 +97,7 @@ class ObjectDescription(SphinxDirective):
else: else:
return [line.strip() for line in lines] return [line.strip() for line in lines]
def handle_signature(self, sig: str, signode: desc_signature) -> Any: def handle_signature(self, sig: str, signode: desc_signature) -> T:
""" """
Parse the signature *sig* into individual nodes and append them to Parse the signature *sig* into individual nodes and append them to
*signode*. If ValueError is raised, parsing is aborted and the whole *signode*. If ValueError is raised, parsing is aborted and the whole
@ -107,7 +109,7 @@ class ObjectDescription(SphinxDirective):
""" """
raise ValueError raise ValueError
def add_target_and_index(self, name: Any, sig: str, signode: desc_signature) -> None: def add_target_and_index(self, name: T, sig: str, signode: desc_signature) -> None:
""" """
Add cross-reference IDs and entries to self.indexnode, if applicable. Add cross-reference IDs and entries to self.indexnode, if applicable.
@ -171,7 +173,7 @@ class ObjectDescription(SphinxDirective):
if self.domain: if self.domain:
node['classes'].append(self.domain) node['classes'].append(self.domain)
self.names = [] # type: List[Any] self.names = [] # type: List[T]
signatures = self.get_signatures() signatures = self.get_signatures()
for i, sig in enumerate(signatures): for i, sig in enumerate(signatures):
# add a signature node for each signature in the current unit # add a signature node for each signature in the current unit

View File

@ -3099,7 +3099,7 @@ def _make_phony_error_name() -> ASTNestedName:
return ASTNestedName([ASTIdentifier("PhonyNameDueToError")], rooted=False) return ASTNestedName([ASTIdentifier("PhonyNameDueToError")], rooted=False)
class CObject(ObjectDescription): class CObject(ObjectDescription[ASTDeclaration]):
""" """
Description of a C language object. Description of a C language object.
""" """

View File

@ -6670,7 +6670,7 @@ def _make_phony_error_name() -> ASTNestedName:
return ASTNestedName([nne], [False], rooted=False) return ASTNestedName([nne], [False], rooted=False)
class CPPObject(ObjectDescription): class CPPObject(ObjectDescription[ASTDeclaration]):
"""Description of a C++ language object.""" """Description of a C++ language object."""
doc_field_types = [ doc_field_types = [

View File

@ -32,7 +32,7 @@ from sphinx.util.nodes import make_id, make_refnode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class JSObject(ObjectDescription): class JSObject(ObjectDescription[Tuple[str, str]]):
""" """
Description of a JavaScript object. Description of a JavaScript object.
""" """

View File

@ -136,7 +136,10 @@ class MathDomain(Domain):
def get_objects(self) -> List: def get_objects(self) -> List:
return [] return []
def has_equations(self) -> bool: def has_equations(self, docname: str = None) -> bool:
if docname:
return self.data['has_equations'].get(docname, False)
else:
return any(self.data['has_equations'].values()) return any(self.data['has_equations'].values())

View File

@ -333,7 +333,7 @@ class PyTypedField(PyXrefMixin, TypedField):
return super().make_xref(rolename, domain, target, innernode, contnode, env) return super().make_xref(rolename, domain, target, innernode, contnode, env)
class PyObject(ObjectDescription): class PyObject(ObjectDescription[Tuple[str, str]]):
""" """
Description of a general Python object. Description of a general Python object.

View File

@ -31,7 +31,7 @@ logger = logging.getLogger(__name__)
dir_sig_re = re.compile(r'\.\. (.+?)::(.*)$') dir_sig_re = re.compile(r'\.\. (.+?)::(.*)$')
class ReSTMarkup(ObjectDescription): class ReSTMarkup(ObjectDescription[str]):
""" """
Description of generic reST markup. Description of generic reST markup.
""" """

View File

@ -46,7 +46,7 @@ option_desc_re = re.compile(r'((?:/|--|-|\+)?[^\s=]+)(=?\s*.*)')
token_re = re.compile(r'`(\w+)`', re.U) token_re = re.compile(r'`(\w+)`', re.U)
class GenericObject(ObjectDescription): class GenericObject(ObjectDescription[str]):
""" """
A generic x-ref directive registered with Sphinx.add_object_type(). A generic x-ref directive registered with Sphinx.add_object_type().
""" """
@ -176,7 +176,7 @@ class Target(SphinxDirective):
return self.name + '-' + name return self.name + '-' + name
class Cmdoption(ObjectDescription): class Cmdoption(ObjectDescription[str]):
""" """
Description of a command-line option (.. option). Description of a command-line option (.. option).
""" """

View File

@ -153,7 +153,10 @@ def mock(modnames: List[str]) -> Generator[None, None, None]:
def ismock(subject: Any) -> bool: def ismock(subject: Any) -> bool:
"""Check if the object is mocked.""" """Check if the object is mocked."""
# check the object has '__sphinx_mock__' attribute # check the object has '__sphinx_mock__' attribute
if not hasattr(subject, '__sphinx_mock__'): try:
if safe_getattr(subject, '__sphinx_mock__', None) is None:
return False
except AttributeError:
return False return False
# check the object is mocked module # check the object is mocked module

View File

@ -17,14 +17,16 @@ from docutils import nodes
import sphinx import sphinx
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.domains.math import MathDomain from sphinx.domains.math import MathDomain
from sphinx.environment import BuildEnvironment
from sphinx.errors import ExtensionError from sphinx.errors import ExtensionError
from sphinx.locale import _ from sphinx.locale import _
from sphinx.util.math import get_node_equation_number from sphinx.util.math import get_node_equation_number
from sphinx.writers.html import HTMLTranslator from sphinx.writers.html import HTMLTranslator
# more information for mathjax secure url is here:
# https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
MATHJAX_URL = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'
def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None: def html_visit_math(self: HTMLTranslator, node: nodes.math) -> None:
self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight')) self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight'))
@ -66,25 +68,25 @@ def html_visit_displaymath(self: HTMLTranslator, node: nodes.math_block) -> None
raise nodes.SkipNode raise nodes.SkipNode
def install_mathjax(app: Sphinx, env: BuildEnvironment) -> None: def install_mathjax(app: Sphinx, pagename: str, templatename: str, context: Dict,
event_arg: Any) -> None:
if app.builder.format != 'html' or app.builder.math_renderer_name != 'mathjax': # type: ignore # NOQA if app.builder.format != 'html' or app.builder.math_renderer_name != 'mathjax': # type: ignore # NOQA
return return
if not app.config.mathjax_path: if not app.config.mathjax_path:
raise ExtensionError('mathjax_path config value must be set for the ' raise ExtensionError('mathjax_path config value must be set for the '
'mathjax extension to work') 'mathjax extension to work')
builder = cast(StandaloneHTMLBuilder, app.builder) domain = cast(MathDomain, app.env.get_domain('math'))
domain = cast(MathDomain, env.get_domain('math')) if domain.has_equations(pagename):
if domain.has_equations():
# Enable mathjax only if equations exists # Enable mathjax only if equations exists
options = {'async': 'async'} options = {'async': 'async'}
if app.config.mathjax_options: if app.config.mathjax_options:
options.update(app.config.mathjax_options) options.update(app.config.mathjax_options)
builder.add_js_file(app.config.mathjax_path, **options) app.add_js_file(app.config.mathjax_path, **options) # type: ignore
if app.config.mathjax_config: if app.config.mathjax_config:
body = "MathJax.Hub.Config(%s)" % json.dumps(app.config.mathjax_config) body = "MathJax.Hub.Config(%s)" % json.dumps(app.config.mathjax_config)
builder.add_js_file(None, type="text/x-mathjax-config", body=body) app.add_js_file(None, type="text/x-mathjax-config", body=body)
def setup(app: Sphinx) -> Dict[str, Any]: def setup(app: Sphinx) -> Dict[str, Any]:
@ -92,15 +94,11 @@ def setup(app: Sphinx) -> Dict[str, Any]:
(html_visit_math, None), (html_visit_math, None),
(html_visit_displaymath, None)) (html_visit_displaymath, None))
# more information for mathjax secure url is here: app.add_config_value('mathjax_path', MATHJAX_URL, 'html')
# https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
app.add_config_value('mathjax_path',
'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
'html')
app.add_config_value('mathjax_options', {}, 'html') app.add_config_value('mathjax_options', {}, 'html')
app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html') app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html')
app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html') app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html')
app.add_config_value('mathjax_config', None, 'html') app.add_config_value('mathjax_config', None, 'html')
app.connect('env-updated', install_mathjax) app.connect('html-page-context', install_mathjax)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True} return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@ -197,15 +197,15 @@ class ImageConverter(BaseImageConverter):
def match(self, node: nodes.image) -> bool: def match(self, node: nodes.image) -> bool:
if not self.app.builder.supported_image_types: if not self.app.builder.supported_image_types:
return False return False
elif set(node['candidates']) & set(self.app.builder.supported_image_types):
# builder supports the image; no need to convert
return False
elif self.available is None: elif self.available is None:
# store the value to the class variable to share it during the build # store the value to the class variable to share it during the build
self.__class__.available = self.is_available() self.__class__.available = self.is_available()
if not self.available: if not self.available:
return False return False
elif set(node['candidates']) & set(self.app.builder.supported_image_types):
# builder supports the image; no need to convert
return False
else: else:
rule = self.get_conversion_rule(node) rule = self.get_conversion_rule(node)
if rule: if rule:

View File

@ -6,6 +6,7 @@ Test Math
math math
page page
nomath
.. math:: a^2+b^2=c^2 .. math:: a^2+b^2=c^2

View File

View File

@ -4,7 +4,9 @@ version = '1.4.4'
html_static_path = ['static', 'subdir'] html_static_path = ['static', 'subdir']
html_extra_path = ['extra', 'subdir'] html_extra_path = ['extra', 'subdir']
html_css_files = ['css/style.css', html_css_files = ['css/style.css',
('https://example.com/custom.css', {'title': 'title', 'media': 'print'})] ('https://example.com/custom.css',
{'title': 'title', 'media': 'print', 'priority': 400})]
html_js_files = ['js/custom.js', html_js_files = ['js/custom.js',
('https://example.com/script.js', {'async': 'async'})] ('https://example.com/script.js',
{'async': 'async', 'priority': 400})]
exclude_patterns = ['**/_build', '**/.htpasswd'] exclude_patterns = ['**/_build', '**/.htpasswd']

View File

@ -1220,15 +1220,15 @@ def test_assets_order(app):
# css_files # css_files
expected = ['_static/early.css', '_static/pygments.css', '_static/alabaster.css', expected = ['_static/early.css', '_static/pygments.css', '_static/alabaster.css',
'_static/normal.css', '_static/late.css', '_static/css/style.css', 'https://example.com/custom.css', '_static/normal.css', '_static/late.css',
'https://example.com/custom.css', '_static/lazy.css'] '_static/css/style.css', '_static/lazy.css']
pattern = '.*'.join('href="%s"' % f for f in expected) pattern = '.*'.join('href="%s"' % f for f in expected)
assert re.search(pattern, content, re.S) assert re.search(pattern, content, re.S)
# js_files # js_files
expected = ['_static/early.js', '_static/jquery.js', '_static/underscore.js', expected = ['_static/early.js', '_static/jquery.js', '_static/underscore.js',
'_static/doctools.js', '_static/normal.js', '_static/late.js', '_static/doctools.js', 'https://example.com/script.js', '_static/normal.js',
'_static/js/custom.js', 'https://example.com/script.js', '_static/lazy.js'] '_static/late.js', '_static/js/custom.js', '_static/lazy.js']
pattern = '.*'.join('src="%s"' % f for f in expected) pattern = '.*'.join('src="%s"' % f for f in expected)
assert re.search(pattern, content, re.S) assert re.search(pattern, content, re.S)

View File

@ -15,6 +15,7 @@ import warnings
import pytest import pytest
from docutils import nodes from docutils import nodes
from sphinx.ext.mathjax import MATHJAX_URL
from sphinx.testing.util import assert_node from sphinx.testing.util import assert_node
@ -224,6 +225,18 @@ def test_mathjax_config(app, status, warning):
'</script>' in content) '</script>' in content)
@pytest.mark.sphinx('html', testroot='ext-math',
confoverrides={'extensions': ['sphinx.ext.mathjax']})
def test_mathjax_is_installed_only_if_document_having_math(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').read_text()
assert MATHJAX_URL in content
content = (app.outdir / 'nomath.html').read_text()
assert MATHJAX_URL not in content
@pytest.mark.sphinx('html', testroot='basic', @pytest.mark.sphinx('html', testroot='basic',
confoverrides={'extensions': ['sphinx.ext.mathjax']}) confoverrides={'extensions': ['sphinx.ext.mathjax']})
def test_mathjax_is_not_installed_if_no_equations(app, status, warning): def test_mathjax_is_not_installed_if_no_equations(app, status, warning):

View File

@ -4,7 +4,7 @@ Release checklist
for stable releases for stable releases
------------------- -------------------
* open https://github.com/sphinx-doc/sphinx/actions?query=branch:X.Y.x and all tests has passed * open "https://github.com/sphinx-doc/sphinx/actions?query=branch:X.Y.x" and all tests has passed
* Run ``git fetch; git status`` and check nothing changed * Run ``git fetch; git status`` and check nothing changed
* ``python utils/bump_version.py X.Y.Z`` * ``python utils/bump_version.py X.Y.Z``
* Check diff by ``git diff`` * Check diff by ``git diff``
@ -28,7 +28,7 @@ for stable releases
for first beta releases for first beta releases
----------------------- -----------------------
* open https://github.com/sphinx-doc/sphinx/actions?query=branch:master and all tests has passed * open "https://github.com/sphinx-doc/sphinx/actions?query=branch:master" and all tests has passed
* Run ``git fetch; git status`` and check nothing changed * Run ``git fetch; git status`` and check nothing changed
* Run ``python setup.py extract_messages`` * Run ``python setup.py extract_messages``
* Run ``(cd sphinx/locale; tx push -s)`` * Run ``(cd sphinx/locale; tx push -s)``
@ -58,7 +58,7 @@ for first beta releases
for other beta releases for other beta releases
----------------------- -----------------------
* open https://github.com/sphinx-doc/sphinx/actions?query=branch:X.x and all tests has passed * open "https://github.com/sphinx-doc/sphinx/actions?query=branch:X.x" and all tests has passed
* Run ``git fetch; git status`` and check nothing changed * Run ``git fetch; git status`` and check nothing changed
* ``python utils/bump_version.py X.Y.0bN`` * ``python utils/bump_version.py X.Y.0bN``
* Check diff by ``git diff`` * Check diff by ``git diff``
@ -81,7 +81,7 @@ for other beta releases
for major releases for major releases
------------------ ------------------
* open https://github.com/sphinx-doc/sphinx/actions?query=branch:X.x and all tests has passed * open "https://github.com/sphinx-doc/sphinx/actions?query=branch:X.x" and all tests has passed
* Run ``git fetch; git status`` and check nothing changed * Run ``git fetch; git status`` and check nothing changed
* Run ``(cd sphinx/locale; tx pull -a -f)`` * Run ``(cd sphinx/locale; tx pull -a -f)``
* Run ``python setup.py compile_catalog`` * Run ``python setup.py compile_catalog``