Skip to content

Commit e69167c

Browse files
authored
Use latexmk if Sphinx > 1.6 (#5656)
Use latexmk if Sphinx > 1.6
2 parents 2fa1989 + 204c020 commit e69167c

File tree

7 files changed

+73
-36
lines changed

7 files changed

+73
-36
lines changed

docs/guides/feature-flags.rst

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ or disable one or more of these featured flags for a particular project.
1212
Available Flags
1313
---------------
1414

15-
``USE_PDF_LATEXMK``: :featureflags:`USE_PDF_LATEXMK`
16-
1715
``USE_SPHINX_LATEST``: :featureflags:`USE_SPHINX_LATEST`
1816

1917
``ALLOW_DEPRECATED_WEBHOOKS``: :featureflags:`ALLOW_DEPRECATED_WEBHOOKS`

docs/guides/pdf-non-ascii-languages.rst

-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
Build PDF format for non-ASCII languages
22
========================================
33

4-
5-
.. warning::
6-
7-
To be able to follow this guide and build PDF with this method,
8-
you need to ask the Read the Docs core team to enable ``USE_PDF_LATEXMK`` :doc:`feature flag </guides/feature-flags>` in your project.
9-
Please, `open an issue`_ in our repository asking for this, and wait for one of the core team to enable it.
10-
114
.. _open an issue: https://github.com/rtfd/readthedocs.org/issues/new
125

136
Sphinx offers different `LaTeX engines`_ that support Unicode characters and non-ASCII languages,

readthedocs/doc_builder/backends/sphinx.py

+35-4
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,6 @@ def get_config_params(self):
146146
'dont_overwrite_sphinx_context': self.project.has_feature(
147147
Feature.DONT_OVERWRITE_SPHINX_CONTEXT,
148148
),
149-
'use_pdf_latexmk': self.project.has_feature(
150-
Feature.USE_PDF_LATEXMK,
151-
),
152149
}
153150

154151
finalize_sphinx_context_data.send(
@@ -227,6 +224,38 @@ def build(self):
227224
)
228225
return cmd_ret.successful
229226

227+
def venv_sphinx_supports_latexmk(self):
228+
"""
229+
Check if ``sphinx`` from the user's venv supports ``latexmk``.
230+
231+
If the version of ``sphinx`` is greater or equal to 1.6.1 it returns
232+
``True`` and ``False`` otherwise.
233+
234+
See: https://www.sphinx-doc.org/en/master/changes.html#release-1-6-1-released-may-16-2017
235+
"""
236+
237+
command = [
238+
self.python_env.venv_bin(filename='python'),
239+
'-c',
240+
(
241+
'"'
242+
'import sys; '
243+
'import sphinx; '
244+
'sys.exit(0 if sphinx.version_info >= (1, 6, 1) else 1)'
245+
'"'
246+
),
247+
]
248+
249+
cmd_ret = self.run(
250+
*command,
251+
bin_path=self.python_env.venv_bin(),
252+
cwd=self.project.checkout_path(self.version.slug),
253+
escape_command=False, # used on DockerBuildCommand
254+
shell=True, # used on BuildCommand
255+
record=False,
256+
)
257+
return cmd_ret.exit_code == 0
258+
230259

231260
class HtmlBuilder(BaseSphinx):
232261
type = 'sphinx'
@@ -390,7 +419,9 @@ def build(self):
390419
raise BuildEnvironmentError('No TeX files were found')
391420

392421
# Run LaTeX -> PDF conversions
393-
if self.project.has_feature(Feature.USE_PDF_LATEXMK):
422+
# Build PDF with ``latexmk`` if Sphinx supports it, otherwise fallback
423+
# to ``pdflatex`` to support old versions
424+
if self.venv_sphinx_supports_latexmk():
394425
return self._build_latexmk(cwd, latex_cwd)
395426

396427
return self._build_pdflatex(tex_files, latex_cwd)

readthedocs/doc_builder/environments.py

+34-17
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class BuildCommand(BuildCommandResultMixin):
8181
:param build_env: build environment to use to execute commands
8282
:param bin_path: binary path to add to PATH resolution
8383
:param description: a more grokable description of the command being run
84+
:param kwargs: allow to subclass this class and extend it
8485
"""
8586

8687
def __init__(
@@ -95,6 +96,7 @@ def __init__(
9596
bin_path=None,
9697
description=None,
9798
record_as_success=False,
99+
**kwargs,
98100
):
99101
self.command = command
100102
self.shell = shell
@@ -164,8 +166,13 @@ def run(self):
164166
environment['PATH'] = ':'.join(env_paths)
165167

166168
try:
169+
# When using ``shell=True`` the command should be flatten
170+
command = self.command
171+
if self.shell:
172+
command = self.get_command()
173+
167174
proc = subprocess.Popen(
168-
self.command,
175+
command,
169176
shell=self.shell,
170177
# This is done here for local builds, but not for docker,
171178
# as we want docker to expand inside the container
@@ -294,14 +301,20 @@ class DockerBuildCommand(BuildCommand):
294301
Build command to execute in docker container
295302
"""
296303

297-
def run(self):
304+
def __init__(self, *args, escape_command=True, **kwargs):
298305
"""
299-
Execute command in existing Docker container.
306+
Override default to extend behavior.
300307
301-
:param cmd_input: input to pass to command in STDIN
302-
:type cmd_input: str
303-
:param combine_output: combine STDERR into STDOUT
308+
:param escape_command: whether escape special chars the command before
309+
executing it in the container. This should only be disabled on
310+
trusted or internal commands.
311+
:type escape_command: bool
304312
"""
313+
self.escape_command = escape_command
314+
super(DockerBuildCommand, self).__init__(*args, **kwargs)
315+
316+
def run(self):
317+
"""Execute command in existing Docker container."""
305318
log.info(
306319
"Running in container %s: '%s' [%s]",
307320
self.build_env.container_id,
@@ -350,13 +363,15 @@ def run(self):
350363

351364
def get_wrapped_command(self):
352365
"""
353-
Escape special bash characters in command to wrap in shell.
366+
Wrap command in a shell and optionally escape special bash characters.
354367
355368
In order to set the current working path inside a docker container, we
356-
need to wrap the command in a shell call manually. Some characters will
357-
be interpreted as shell characters without escaping, such as: ``pip
358-
install requests<0.8``. This escapes a good majority of those
359-
characters.
369+
need to wrap the command in a shell call manually.
370+
371+
Some characters will be interpreted as shell characters without
372+
escaping, such as: ``pip install requests<0.8``. When passing
373+
``escape_command=True`` in the init method this escapes a good majority
374+
of those characters.
360375
"""
361376
bash_escape_re = re.compile(
362377
r"([\t\ \!\"\#\$\&\'\(\)\*\:\;\<\>\?\@"
@@ -365,16 +380,18 @@ def get_wrapped_command(self):
365380
prefix = ''
366381
if self.bin_path:
367382
prefix += 'PATH={}:$PATH '.format(self.bin_path)
383+
384+
command = (
385+
' '.join([
386+
bash_escape_re.sub(r'\\\1', part) if self.escape_command else part
387+
for part in self.command
388+
])
389+
)
368390
return (
369391
"/bin/sh -c 'cd {cwd} && {prefix}{cmd}'".format(
370392
cwd=self.cwd,
371393
prefix=prefix,
372-
cmd=(
373-
' '.join([
374-
bash_escape_re.sub(r'\\\1', part)
375-
for part in self.command
376-
])
377-
),
394+
cmd=command,
378395
)
379396
)
380397

readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl

-2
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ if 'extensions' in globals():
141141
else:
142142
extensions = ["readthedocs_ext.readthedocs"]
143143

144-
{% if use_pdf_latexmk %}
145144
project_language = '{{ project.language }}'
146145

147146
# User's Sphinx configurations
@@ -173,4 +172,3 @@ if chinese:
173172
latex_elements = latex_elements_user or latex_elements_rtd
174173
elif japanese:
175174
latex_engine = latex_engine_user or 'platex'
176-
{% endif %}

readthedocs/projects/models.py

-2
Original file line numberDiff line numberDiff line change
@@ -1358,12 +1358,10 @@ def add_features(sender, **kwargs):
13581358
DONT_SHALLOW_CLONE = 'dont_shallow_clone'
13591359
USE_TESTING_BUILD_IMAGE = 'use_testing_build_image'
13601360
SHARE_SPHINX_DOCTREE = 'share_sphinx_doctree'
1361-
USE_PDF_LATEXMK = 'use_pdf_latexmk'
13621361
DEFAULT_TO_MKDOCS_0_17_3 = 'default_to_mkdocs_0_17_3'
13631362

13641363
FEATURES = (
13651364
(USE_SPHINX_LATEST, _('Use latest version of Sphinx')),
1366-
(USE_PDF_LATEXMK, _('Use latexmk to build the PDF')),
13671365
(ALLOW_DEPRECATED_WEBHOOKS, _('Allow deprecated webhook views')),
13681366
(PIP_ALWAYS_UPGRADE, _('Always run pip install --upgrade')),
13691367
(SKIP_SUBMODULES, _('Skip git submodule checkout')),

readthedocs/rtd_tests/tests/test_builds.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ def test_build_pdf_latex_failures(self, load_config):
220220
returns = [
221221
((b'', b''), 0), # sphinx-build html
222222
((b'', b''), 0), # sphinx-build pdf
223+
((b'', b''), 1), # sphinx version check
223224
((b'', b''), 1), # latex
224225
((b'', b''), 0), # makeindex
225226
((b'', b''), 0), # latex
@@ -236,7 +237,7 @@ def test_build_pdf_latex_failures(self, load_config):
236237

237238
with build_env:
238239
task.build_docs()
239-
self.assertEqual(self.mocks.popen.call_count, 7)
240+
self.assertEqual(self.mocks.popen.call_count, 8)
240241
self.assertTrue(build_env.failed)
241242

242243
@mock.patch('readthedocs.doc_builder.config.load_config')
@@ -271,6 +272,7 @@ def test_build_pdf_latex_not_failure(self, load_config):
271272
returns = [
272273
((b'', b''), 0), # sphinx-build html
273274
((b'', b''), 0), # sphinx-build pdf
275+
((b'', b''), 1), # sphinx version check
274276
((b'Output written on foo.pdf', b''), 1), # latex
275277
((b'', b''), 0), # makeindex
276278
((b'', b''), 0), # latex
@@ -287,7 +289,7 @@ def test_build_pdf_latex_not_failure(self, load_config):
287289

288290
with build_env:
289291
task.build_docs()
290-
self.assertEqual(self.mocks.popen.call_count, 7)
292+
self.assertEqual(self.mocks.popen.call_count, 8)
291293
self.assertTrue(build_env.successful)
292294

293295
@mock.patch('readthedocs.projects.tasks.api_v2')

0 commit comments

Comments
 (0)