diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | doc/_themes/sphinx13/layout.html | 2 | ||||
-rw-r--r-- | doc/builders.rst | 71 | ||||
-rw-r--r-- | doc/conf.py | 2 | ||||
-rw-r--r-- | doc/extdev/index.rst | 1 | ||||
-rw-r--r-- | doc/extdev/utils.rst | 19 | ||||
-rw-r--r-- | doc/intl.rst | 8 | ||||
-rw-r--r-- | doc/latex.rst | 69 | ||||
-rw-r--r-- | doc/man/sphinx-apidoc.rst | 6 | ||||
-rw-r--r-- | doc/usage/restructuredtext/directives.rst | 6 | ||||
-rw-r--r-- | sphinx/application.py | 4 | ||||
-rw-r--r-- | sphinx/builders/__init__.py | 4 | ||||
-rw-r--r-- | sphinx/builders/gettext.py | 5 | ||||
-rw-r--r-- | sphinx/directives/__init__.py | 12 | ||||
-rw-r--r-- | sphinx/directives/code.py | 21 | ||||
-rw-r--r-- | sphinx/directives/other.py | 56 | ||||
-rw-r--r-- | sphinx/directives/patches.py | 6 | ||||
-rw-r--r-- | sphinx/environment/__init__.py | 4 | ||||
-rw-r--r-- | sphinx/ext/autosummary/__init__.py | 2 | ||||
-rw-r--r-- | sphinx/ext/doctest.py | 4 | ||||
-rw-r--r-- | sphinx/ext/imgconverter.py | 25 | ||||
-rw-r--r-- | sphinx/testing/util.py | 3 | ||||
-rw-r--r-- | sphinx/transforms/__init__.py | 15 | ||||
-rw-r--r-- | sphinx/util/docutils.py | 23 | ||||
-rw-r--r-- | sphinx/util/i18n.py | 6 | ||||
-rw-r--r-- | sphinx/util/osutil.py | 9 |
26 files changed, 225 insertions, 161 deletions
@@ -93,6 +93,9 @@ Bugs fixed * #4769: autodoc loses the first staticmethod parameter * #4790: autosummary: too wide two column tables in PDF builds * #4795: Latex customization via ``_templates/longtable.tex_t`` is broken +* #4789: imgconverter: confused by convert.exe of Windows +* #4783: On windows, Sphinx crashed when drives of srcdir and outdir are + different Testing -------- diff --git a/doc/_themes/sphinx13/layout.html b/doc/_themes/sphinx13/layout.html index 597b6261f..8967d8265 100644 --- a/doc/_themes/sphinx13/layout.html +++ b/doc/_themes/sphinx13/layout.html @@ -70,7 +70,7 @@ <div class="pageheader"> <ul> <li><a href="{{ pathto('index') }}">Home</a></li> - <li><a href="{{ pathto('install') }}">Get it</a></li> + <li><a href="{{ pathto('usage/installation') }}">Get it</a></li> <li><a href="{{ pathto('contents') }}">Docs</a></li> <li><a href="{{ pathto('develop') }}">Extend/Develop</a></li> </ul> diff --git a/doc/builders.rst b/doc/builders.rst index ffaa1e14f..6ab6227fa 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -155,36 +155,30 @@ The builder's "name" must be given to the **-b** command-line option of configuration values that customize the output of this builder, see the chapter :ref:`latex-options` for details. + The produced LaTeX file uses several LaTeX packages that may not be present + in a "minimal" TeX distribution installation. For example, on Ubuntu, the + following packages need to be installed for successful PDF builds: + + * texlive-latex-recommended + * texlive-fonts-recommended + * texlive-latex-extra + * latexmk (for ``make latexpdf`` on GNU/Linux and MacOS X) + * latex-xcolor (old Ubuntu) + * texlive-luatex, texlive-xetex (see :confval:`latex_engine`) + + The testing of Sphinx LaTeX is done on Ubuntu trusty with the above + mentioned packages, which are from a TeXLive 2013 snapshot dated + February 2014. + + .. versionchanged:: 1.6 + Formerly, testing had been done on Ubuntu precise (TeXLive 2009). + .. note:: - The produced LaTeX file uses several LaTeX packages that may not be - present in a "minimal" TeX distribution installation. For example, on - Ubuntu, the following packages need to be installed for successful PDF - builds: - - * texlive-latex-recommended - * texlive-fonts-recommended - * texlive-latex-extra - * latexmk (for ``make latexpdf``) - - Sphinx will use ``xcolor.sty`` if present: recent Ubuntu distributions - have ``xcolor.sty`` included in latex-recommended, earlier ones have it - in latex-xcolor. Unicode engines will need texlive-luatex or - texlive-xetex. - - The testing of Sphinx LaTeX is done on Ubuntu trusty with the above - mentioned packages, which are from a TeXLive 2013 snapshot dated - February 2014. - - .. versionchanged:: 1.6 - Formerly, testing had been done for some years on Ubuntu precise - (based on TeXLive 2009). - .. versionchanged:: 1.6 - Use of ``latexmk`` for ``make latexpdf`` on GNU/Linux and Mac OS X - - Since 1.6, ``make latexpdf`` (or - ``make -C "<builddir>/latex"`` after a ``sphinx-build`` run) uses - ``latexmk`` (not on Windows). + Since 1.6, ``make latexpdf`` uses ``latexmk`` (not on Windows). This + makes sure the needed number of runs is automatically executed to get + the cross-references, bookmarks, indices, and tables of contents right. + One can pass to ``latexmk`` options via the ``LATEXMKOPTS`` Makefile variable. For example: @@ -192,13 +186,22 @@ The builder's "name" must be given to the **-b** command-line option of make latexpdf LATEXMKOPTS="-silent" - reduces console output to a minimum. Also, if ``latexmk`` version is - 4.52b or higher (Jan 17) and ``xelatex`` is the :confval:`latex_engine`, - then ``LATEXMKOPTS="-xelatex"`` will speed up PDF builds. + reduces console output to a minimum. + + Also, if ``latexmk`` version is 4.52b or higher (Jan 17) + ``LATEXMKOPTS="-xelatex"`` will speed up PDF builds via XeLateX in case + of numerous graphics inclusions. + + .. code-block:: console + + make latexpdf LATEXMKOPTS="-xelatex" + + To pass options directly to the ``(pdf|xe|lua)latex`` executable, use + variable ``LATEXOPTS``. + + .. code-block:: console - To pass options directly to the - ``(pdf|xe|lua)latex`` executable, use variable ``LATEXOPTS`` (for example - ``LATEXOPTS="--interaction=nonstopmode"``). + make latexpdf LATEXOPTS="--interaction=nonstopmode" .. autoattribute:: name diff --git a/doc/conf.py b/doc/conf.py index 49f0930b6..18d82b223 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -39,7 +39,7 @@ epub_uid = 'web-site' epub_scheme = 'url' epub_identifier = epub_publisher epub_pre_files = [('index.xhtml', 'Welcome')] -epub_post_files = [('install.xhtml', 'Installing Sphinx'), +epub_post_files = [('usage/installation.xhtml', 'Installing Sphinx'), ('develop.xhtml', 'Sphinx development')] epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js', '_static/jquery.js', '_static/searchtools.js', diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index 78414cd09..2dba7a430 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -94,6 +94,7 @@ APIs used for writing extensions nodes logging i18n + utils Deprecated APIs --------------- diff --git a/doc/extdev/utils.rst b/doc/extdev/utils.rst new file mode 100644 index 000000000..6ec3f1b46 --- /dev/null +++ b/doc/extdev/utils.rst @@ -0,0 +1,19 @@ +Utilities +========= + +Sphinx provides utility classes and functions to develop extensions. + +Base classes for components +--------------------------- + +These base classes are useful to allow your extensions to obtain Sphinx +components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily. + +.. note:: The subclasses of them might not work with bare docutils because they + are strongly coupled with Sphinx. + +.. autoclass:: sphinx.transforms.SphinxTransform + :members: + +.. autoclass:: sphinx.util.docutils.SphinxDirective + :members: diff --git a/doc/intl.rst b/doc/intl.rst index ac4f47079..75263e6e4 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -207,7 +207,7 @@ easy to fetch and push translations. $ pip install transifex-client - .. seealso:: `Transifex Client v0.8 — Transifex documentation`_ + .. seealso:: `Transifex Client documentation`_ #. Create your transifex_ account and create new project for your document @@ -305,7 +305,7 @@ Contributing to Sphinx reference translation The recommended way for new contributors to translate Sphinx reference is to join the translation team on Transifex. -There is `sphinx translation page`_ for Sphinx-1.3 documentation. +There is `sphinx translation page`_ for Sphinx (master) documentation. 1. Login to transifex_ service. 2. Go to `sphinx translation page`_. @@ -325,5 +325,5 @@ There is `sphinx translation page`_ for Sphinx-1.3 documentation. .. _`transifex-client`: https://pypi.python.org/pypi/transifex-client .. _`sphinx-intl`: https://pypi.python.org/pypi/sphinx-intl .. _Transifex: https://www.transifex.com/ -.. _`sphinx translation page`: https://www.transifex.com/sphinx-doc/sphinx-doc-1_3/ -.. _`Transifex Client v0.8 — Transifex documentation`: https://docs.transifex.com/client/introduction/ +.. _`sphinx translation page`: https://www.transifex.com/sphinx-doc/sphinx-doc/ +.. _`Transifex Client documentation`: http://docs.transifex.com/developer/client/ diff --git a/doc/latex.rst b/doc/latex.rst index f8e275020..b50f49377 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -8,8 +8,8 @@ LaTeX customization .. module:: latex :synopsis: LaTeX specifics. -The *latex* target does not benefit from pre-prepared themes like the -*html* target does (see :doc:`theming`). +For details of the LaTeX/PDF builder command line invocation, refer to +:py:class:`~sphinx.builders.latex.LaTeXBuilder`. .. raw:: latex @@ -34,8 +34,10 @@ The *latex* target does not benefit from pre-prepared themes like the Basic customization ------------------- -It is achieved via usage of the -:ref:`latex-options` as described in :doc:`config`. For example:: +The *latex* target does not benefit from prepared themes. + +Basic customization is obtained via usage of the :ref:`latex-options`. For +example:: # inside conf.py latex_engine = 'xelatex' @@ -69,7 +71,7 @@ repertory, and get LaTeX to import it at run time:: # or, if the \ProvidesPackage LaTeX macro is used in a file mystyle.sty 'preamble': r'\usepackage{mystyle}', -It is needed to set appropriately :confval:`latex_additional_files`, for +It is then needed to set appropriately :confval:`latex_additional_files`, for example:: latex_additional_files = ["mystyle.sty"] @@ -79,11 +81,14 @@ example:: The LaTeX style file options ---------------------------- +Additional customization is possible via LaTeX options of the Sphinx style +file. + The sphinxsetup interface ~~~~~~~~~~~~~~~~~~~~~~~~~ The ``'sphinxsetup'`` key of :confval:`latex_elements` provides a convenient -interface to the package options of the Sphinx style file:: +interface:: latex_elements = { 'sphinxsetup': 'key1=value1, key2=value2, ...', @@ -103,40 +108,39 @@ inside the document preamble, like this:: .. versionadded:: 1.5 -It is possible to insert further uses of the ``\sphinxsetup`` LaTeX macro -directly into the body of the document, via the help of the :rst:dir:`raw` -directive. This is what is done for this documentation, for local styling -of this chapter in the PDF output:: +.. hint:: - .. raw:: latex + It is possible to insert further uses of the ``\sphinxsetup`` LaTeX macro + directly into the body of the document, via the help of the :rst:dir:`raw` + directive. Here is how this present chapter in PDF is styled:: - \begingroup - \sphinxsetup{% - verbatimwithframe=false, - VerbatimColor={named}{OldLace}, - TitleColor={named}{DarkGoldenrod}, - hintBorderColor={named}{LightCoral}, - attentionborder=3pt, - attentionBorderColor={named}{Crimson}, - attentionBgColor={named}{FloralWhite}, - noteborder=2pt, - noteBorderColor={named}{Olive}, - cautionborder=3pt, - cautionBorderColor={named}{Cyan}, - cautionBgColor={named}{LightCyan}} + .. raw:: latex -at the start of the chapter and:: + \begingroup + \sphinxsetup{% + verbatimwithframe=false, + VerbatimColor={named}{OldLace}, + TitleColor={named}{DarkGoldenrod}, + hintBorderColor={named}{LightCoral}, + attentionborder=3pt, + attentionBorderColor={named}{Crimson}, + attentionBgColor={named}{FloralWhite}, + noteborder=2pt, + noteBorderColor={named}{Olive}, + cautionborder=3pt, + cautionBorderColor={named}{Cyan}, + cautionBgColor={named}{LightCyan}} - .. raw:: latex + at the start of the chapter and:: - \endgroup + .. raw:: latex -at its end. + \endgroup -.. note:: + at its end. - The colors above are made available via the ``svgnames`` option of - the "xcolor" package:: + The colors used in the above are provided by the ``svgnames`` option of the + "xcolor" package:: latex_elements = { 'passoptionstopackages': r'\PassOptionsToPackage{svgnames}{xcolor}', @@ -465,7 +469,6 @@ Miscellany .. versionchanged:: 1.5 formerly, use of *fncychap* with other styles than ``Bjarne`` was dysfunctional. -- check file :file:`sphinx.sty` for more... .. hint:: diff --git a/doc/man/sphinx-apidoc.rst b/doc/man/sphinx-apidoc.rst index 8f75d6f7c..97135d98e 100644 --- a/doc/man/sphinx-apidoc.rst +++ b/doc/man/sphinx-apidoc.rst @@ -15,8 +15,10 @@ style of other automatic API documentation tools. *MODULE_PATH* is the path to a Python package to document, and *OUTPUT_PATH* is the directory where the generated sources are placed. Any *EXCLUDE_PATTERN*\s -given are `fnmatch-style <fnmatch>`_ file and/or directory patterns that will -be excluded from generation. +given are `fnmatch-style`_ file and/or directory patterns that will be excluded +from generation. + +.. _fnmatch-style: https://docs.python.org/3/library/fnmatch.html .. warning:: diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst index 1a41dbef1..a99ccdbf5 100644 --- a/doc/usage/restructuredtext/directives.rst +++ b/doc/usage/restructuredtext/directives.rst @@ -596,7 +596,8 @@ __ http://pygments.org/docs/lexers/ ``start-after`` is given as a string option, only lines that follow the first line containing that string are included. If ``end-before`` is given as a string option, only lines that precede the first lines containing that - string are included. + string are included. The ``start-at`` and ``end-at`` options behave in a + similar way, but the lines containing the matched string are included. With lines selected using ``start-after`` it is still possible to use ``lines``, the first allowed line having by convention the line number @@ -638,6 +639,9 @@ __ http://pygments.org/docs/lexers/ Added the ``diff``, ``lineno-match``, ``caption``, ``name``, and ``dedent`` options. + .. versionchanged:: 1.5 + Added the ``start-at``, and ``end-at`` options. + .. versionchanged:: 1.6 With both ``start-after`` and ``lines`` in use, the first line as per ``start-after`` is considered to be with line number ``1`` for ``lines``. diff --git a/sphinx/application.py b/sphinx/application.py index d6d66c925..c8e06245c 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -43,7 +43,7 @@ from sphinx.util.build_phase import BuildPhase from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import directive_helper from sphinx.util.i18n import find_catalog_source_files -from sphinx.util.osutil import abspath, ensuredir +from sphinx.util.osutil import abspath, ensuredir, relpath from sphinx.util.tags import Tags if False: @@ -351,7 +351,7 @@ class Sphinx(object): if self.statuscode == 0 and self.builder.epilog: logger.info('') logger.info(self.builder.epilog % { - 'outdir': path.relpath(self.outdir), + 'outdir': relpath(self.outdir), 'project': self.config.project }) except Exception as err: diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index d5b60a0a9..fb171dcde 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -23,7 +23,7 @@ from sphinx.util import i18n, import_object, logging, status_iterator from sphinx.util.build_phase import BuildPhase from sphinx.util.console import bold # type: ignore from sphinx.util.i18n import find_catalog -from sphinx.util.osutil import SEP, ensuredir, relative_uri +from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \ parallel_available @@ -242,7 +242,7 @@ class Builder(object): def cat2relpath(cat): # type: (CatalogInfo) -> unicode - return path.relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP) + return relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP) logger.info(bold(__('building [mo]: ')) + message) for catalog in status_iterator(catalogs, __('writing output... '), "darkgreen", diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index c8fbb9c32..b43dcfb3b 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -27,7 +27,7 @@ from sphinx.util import split_index_msg, logging, status_iterator from sphinx.util.console import bold # type: ignore from sphinx.util.i18n import find_catalog from sphinx.util.nodes import extract_messages, traverse_translatable_index -from sphinx.util.osutil import safe_relpath, ensuredir, canon_path +from sphinx.util.osutil import relpath, ensuredir, canon_path from sphinx.util.tags import Tags if False: @@ -286,8 +286,7 @@ class MessageCatalogBuilder(I18nBuilder): if self.config.gettext_location: # generate "#: file1:line1\n#: file2:line2 ..." output.write("#: %s\n" % "\n#: ".join( # type: ignore - "%s:%s" % (canon_path( - safe_relpath(source, self.outdir)), line) + "%s:%s" % (canon_path(relpath(source, self.outdir)), line) for source, line, _ in positions)) if self.config.gettext_uuid: # generate "# uuid1\n# uuid2\n ..." diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index dc51810d3..81978b722 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -16,6 +16,7 @@ from docutils.parsers.rst import Directive, directives, roles from sphinx import addnodes from sphinx.util.docfields import DocFieldTransformer +from sphinx.util.docutils import SphinxDirective # import all directives sphinx provides from sphinx.directives.code import ( # noqa @@ -33,6 +34,7 @@ if False: # For type annotation from typing import Any, Dict, List # NOQA from sphinx.application import Sphinx # NOQA + from sphinx.config import Config # NOQA from sphinx.environment import BuildEnvironment # NOQA @@ -41,7 +43,7 @@ nl_escape_re = re.compile(r'\\\n') strip_backslash_re = re.compile(r'\\(.)') -class ObjectDescription(Directive): +class ObjectDescription(SphinxDirective): """ Directive to describe a class, function or similar object. Not used directly, but subclassed (in domain-specific directives) to add custom @@ -135,7 +137,6 @@ class ObjectDescription(Directive): self.domain, self.objtype = self.name.split(':', 1) else: self.domain, self.objtype = '', self.name - self.env = self.state.document.settings.env # type: BuildEnvironment self.indexnode = addnodes.index(entries=[]) node = addnodes.desc() @@ -187,7 +188,7 @@ class ObjectDescription(Directive): DescDirective = ObjectDescription -class DefaultRole(Directive): +class DefaultRole(SphinxDirective): """ Set the default interpreted text role. Overridden from docutils. """ @@ -212,7 +213,7 @@ class DefaultRole(Directive): line=self.lineno) return messages + [error] roles._roles[''] = role - self.state.document.settings.env.temp_data['default_role'] = role_name + self.env.temp_data['default_role'] = role_name return messages @@ -229,7 +230,6 @@ class DefaultDomain(Directive): def run(self): # type: () -> List[nodes.Node] - env = self.state.document.settings.env domain_name = self.arguments[0].lower() # if domain_name not in env.domains: # # try searching by label @@ -237,7 +237,7 @@ class DefaultDomain(Directive): # if domain.label.lower() == domain_name: # domain_name = domain.name # break - env.temp_data['default_domain'] = env.domains.get(domain_name) + self.env.temp_data['default_domain'] = self.env.domains.get(domain_name) return [] diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 8690fca0f..1c294ec7b 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -12,7 +12,7 @@ import sys from difflib import unified_diff from docutils import nodes -from docutils.parsers.rst import Directive, directives +from docutils.parsers.rst import directives from docutils.statemachine import ViewList from six import text_type @@ -20,6 +20,7 @@ from sphinx import addnodes from sphinx.locale import __ from sphinx.util import logging from sphinx.util import parselinenos +from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import set_source_info if False: @@ -31,7 +32,7 @@ if False: logger = logging.getLogger(__name__) -class Highlight(Directive): +class Highlight(SphinxDirective): """ Directive to set the highlighting language for code blocks, as well as the threshold for line numbers. @@ -77,7 +78,7 @@ def dedent_lines(lines, dedent, location=None): def container_wrapper(directive, literal_node, caption): - # type: (Directive, nodes.Node, unicode) -> nodes.container + # type: (SphinxDirective, nodes.Node, unicode) -> nodes.container container_node = nodes.container('', literal_block=True, classes=['literal-block-wrapper']) parsed = nodes.Element() @@ -95,7 +96,7 @@ def container_wrapper(directive, literal_node, caption): return container_node -class CodeBlock(Directive): +class CodeBlock(SphinxDirective): """ Directive for a code block with special highlighting or line numbering settings. @@ -372,7 +373,7 @@ class LiteralIncludeReader(object): return lines -class LiteralInclude(Directive): +class LiteralInclude(SphinxDirective): """ Like ``.. include:: :literal:``, but only warns if the include file is not found, and does not raise errors. Also has several options for @@ -412,19 +413,17 @@ class LiteralInclude(Directive): if not document.settings.file_insertion_enabled: return [document.reporter.warning('File insertion disabled', line=self.lineno)] - env = document.settings.env - # convert options['diff'] to absolute path if 'diff' in self.options: - _, path = env.relfn2path(self.options['diff']) + _, path = self.env.relfn2path(self.options['diff']) self.options['diff'] = path try: location = self.state_machine.get_source_and_line(self.lineno) - rel_filename, filename = env.relfn2path(self.arguments[0]) - env.note_dependency(rel_filename) + rel_filename, filename = self.env.relfn2path(self.arguments[0]) + self.env.note_dependency(rel_filename) - reader = LiteralIncludeReader(filename, self.options, env.config) + reader = LiteralIncludeReader(filename, self.options, self.config) text, lines = reader.read(location=location) retnode = nodes.literal_block(text, text, source=filename) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 252e6522a..450999435 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -8,7 +8,7 @@ """ from docutils import nodes -from docutils.parsers.rst import Directive, directives +from docutils.parsers.rst import directives from docutils.parsers.rst.directives.admonitions import BaseAdmonition from docutils.parsers.rst.directives.misc import Class from docutils.parsers.rst.directives.misc import Include as BaseInclude @@ -18,6 +18,7 @@ from sphinx import addnodes, locale from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning from sphinx.locale import _ from sphinx.util import url_re, docname_join +from sphinx.util.docutils import SphinxDirective from sphinx.util.matching import patfilter from sphinx.util.nodes import explicit_title_re, set_source_info, \ process_index_entry @@ -49,7 +50,7 @@ def int_or_nothing(argument): return int(argument) -class TocTree(Directive): +class TocTree(SphinxDirective): """ Directive to notify Sphinx about the hierarchical structure of the docs, and to include a table-of-contents like tree in the current document. @@ -72,8 +73,7 @@ class TocTree(Directive): def run(self): # type: () -> List[nodes.Node] - env = self.state.document.settings.env - suffixes = env.config.source_suffix + suffixes = self.config.source_suffix glob = 'glob' in self.options ret = [] @@ -81,17 +81,17 @@ class TocTree(Directive): # and title may be None if the document's title is to be used entries = [] # type: List[Tuple[unicode, unicode]] includefiles = [] - all_docnames = env.found_docs.copy() + all_docnames = self.env.found_docs.copy() # don't add the currently visited file in catch-all patterns - all_docnames.remove(env.docname) + all_docnames.remove(self.env.docname) for entry in self.content: if not entry: continue # look for explicit titles ("Some Title <document>") explicit = explicit_title_re.match(entry) if glob and ('*' in entry or '?' in entry or '[' in entry) and not explicit: - patname = docname_join(env.docname, entry) - docnames = sorted(patfilter(all_docnames, patname)) + patname = docname_join(self.env.docname, entry) + docnames = sorted(patfilter(all_docnames, patname)) # type: ignore for docname in docnames: all_docnames.remove(docname) # don't include it again entries.append((None, docname)) @@ -114,20 +114,20 @@ class TocTree(Directive): docname = docname[:-len(suffix)] break # absolutize filenames - docname = docname_join(env.docname, docname) + docname = docname_join(self.env.docname, docname) if url_re.match(ref) or ref == 'self': entries.append((title, ref)) - elif docname not in env.found_docs: + elif docname not in self.env.found_docs: ret.append(self.state.document.reporter.warning( 'toctree contains reference to nonexisting ' 'document %r' % docname, line=self.lineno)) - env.note_reread() + self.env.note_reread() else: all_docnames.discard(docname) entries.append((title, docname)) includefiles.append(docname) subnode = addnodes.toctree() - subnode['parent'] = env.docname + subnode['parent'] = self.env.docname # entries contains all entries (self references, external links etc.) if 'reversed' in self.options: entries.reverse() @@ -149,7 +149,7 @@ class TocTree(Directive): return ret -class Author(Directive): +class Author(SphinxDirective): """ Directive to give the name of the author of the current document or section. Shown in the output only if the show_authors option is on. @@ -162,8 +162,7 @@ class Author(Directive): def run(self): # type: () -> List[nodes.Node] - env = self.state.document.settings.env - if not env.config.show_authors: + if not self.config.show_authors: return [] para = nodes.paragraph(translatable=False) emph = nodes.emphasis() @@ -183,7 +182,7 @@ class Author(Directive): return [para] + messages -class Index(Directive): +class Index(SphinxDirective): """ Directive to add entries to the index. """ @@ -196,8 +195,7 @@ class Index(Directive): def run(self): # type: () -> List[nodes.Node] arguments = self.arguments[0].split('\n') - env = self.state.document.settings.env - targetid = 'index-%s' % env.new_serialno('index') + targetid = 'index-%s' % self.env.new_serialno('index') targetnode = nodes.target('', '', ids=[targetid]) self.state.document.note_explicit_target(targetnode) indexnode = addnodes.index() @@ -209,7 +207,7 @@ class Index(Directive): return [indexnode, targetnode] -class VersionChange(Directive): +class VersionChange(SphinxDirective): """ Directive to describe a change/addition/deprecation in a specific version. """ @@ -252,9 +250,8 @@ class VersionChange(Directive): classes=['versionmodified']), translatable=False) node.append(para) - env = self.state.document.settings.env # XXX should record node.source as well - env.note_versionchange(node['type'], node['version'], node, node.line) + self.env.note_versionchange(node['type'], node['version'], node, node.line) return [node] + messages @@ -265,7 +262,7 @@ class SeeAlso(BaseAdmonition): node_class = addnodes.seealso -class TabularColumns(Directive): +class TabularColumns(SphinxDirective): """ Directive to give an explicit tabulary column definition to LaTeX. """ @@ -283,7 +280,7 @@ class TabularColumns(Directive): return [node] -class Centered(Directive): +class Centered(SphinxDirective): """ Directive to create a centered line of bold text. """ @@ -304,7 +301,7 @@ class Centered(Directive): return [subnode] + messages -class Acks(Directive): +class Acks(SphinxDirective): """ Directive for a list of names. """ @@ -326,7 +323,7 @@ class Acks(Directive): return [node] -class HList(Directive): +class HList(SphinxDirective): """ Directive for a list that gets compacted horizontally. """ @@ -363,7 +360,7 @@ class HList(Directive): return [newnode] -class Only(Directive): +class Only(SphinxDirective): """ Directive to only include text if the given tag(s) are enabled. """ @@ -421,7 +418,7 @@ class Only(Directive): self.state.memo.section_level = surrounding_section_level -class Include(BaseInclude): +class Include(BaseInclude, SphinxDirective): """ Like the standard "Include" directive, but interprets absolute paths "correctly", i.e. relative to source directory. @@ -429,14 +426,13 @@ class Include(BaseInclude): def run(self): # type: () -> List[nodes.Node] - env = self.state.document.settings.env if self.arguments[0].startswith('<') and \ self.arguments[0].endswith('>'): # docutils "standard" includes, do not do path processing return BaseInclude.run(self) - rel_filename, filename = env.relfn2path(self.arguments[0]) + rel_filename, filename = self.env.relfn2path(self.arguments[0]) self.arguments[0] = filename - env.note_included(filename) + self.env.note_included(filename) return BaseInclude.run(self) diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py index 14f0ca17e..00be5584d 100644 --- a/sphinx/directives/patches.py +++ b/sphinx/directives/patches.py @@ -12,6 +12,7 @@ from docutils.parsers.rst import directives from docutils.parsers.rst.directives import images, html, tables from sphinx import addnodes +from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import set_source_info if False: @@ -44,16 +45,15 @@ class Figure(images.Figure): return [figure_node] -class Meta(html.Meta): +class Meta(html.Meta, SphinxDirective): def run(self): # type: () -> List[nodes.Node] - env = self.state.document.settings.env result = html.Meta.run(self) for node in result: if (isinstance(node, nodes.pending) and isinstance(node.details['nodes'][0], html.MetaBody.meta)): meta = node.details['nodes'][0] - meta.source = env.doc2path(env.docname) + meta.source = self.env.doc2path(self.env.docname) meta.line = self.lineno meta.rawcontent = meta['content'] diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 1a8dd6a85..b69a5fd32 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -38,7 +38,7 @@ from sphinx.util.docutils import sphinx_domains, WarningStream from sphinx.util.i18n import find_catalog_files from sphinx.util.matching import compile_matchers from sphinx.util.nodes import is_translatable -from sphinx.util.osutil import SEP, ensuredir +from sphinx.util.osutil import SEP, ensuredir, relpath from sphinx.util.websupport import is_commentable if False: @@ -354,7 +354,7 @@ class BuildEnvironment(object): *filename* should be absolute or relative to the source directory. """ if filename.startswith(self.srcdir): - filename = os.path.relpath(filename, self.srcdir) + filename = relpath(filename, self.srcdir) for suffix in self.config.source_suffix: if filename.endswith(suffix): return filename[:-len(suffix)] diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index be76d8572..80a2b4e63 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -91,7 +91,7 @@ if False: logger = logging.getLogger(__name__) -periods_re = re.compile('\.(?:\s+)') +periods_re = re.compile(r'\.(?:\s+)') # -- autosummary_toc node ------------------------------------------------------ diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 255a38f6d..b86775d5e 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -30,7 +30,7 @@ from sphinx.locale import __ from sphinx.util import force_decode, logging from sphinx.util.console import bold # type: ignore from sphinx.util.nodes import set_source_info -from sphinx.util.osutil import fs_encoding +from sphinx.util.osutil import fs_encoding, relpath if False: # For type annotation @@ -372,7 +372,7 @@ Doctest summary """Try to get the file which actually contains the doctest, not the filename of the document it's included in.""" try: - filename = path.relpath(node.source, self.env.srcdir)\ + filename = relpath(node.source, self.env.srcdir)\ .rsplit(':docstring of ', maxsplit=1)[0] except Exception: filename = self.env.doc2path(docname, base=None) diff --git a/sphinx/ext/imgconverter.py b/sphinx/ext/imgconverter.py index 8546593a7..fe086b1fe 100644 --- a/sphinx/ext/imgconverter.py +++ b/sphinx/ext/imgconverter.py @@ -8,6 +8,7 @@ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import locale import subprocess from sphinx.errors import ExtensionError @@ -38,17 +39,29 @@ class ImagemagickConverter(ImageConverter): try: args = [self.config.image_converter, '-version'] logger.debug('Invoking %r ...', args) - ret = subprocess.call(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - if ret == 0: - return True - else: - return False + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except (OSError, IOError): logger.warning(__('convert command %r cannot be run.' 'check the image_converter setting'), self.config.image_converter) return False + try: + stdout, stderr = p.communicate() + except (OSError, IOError) as err: + if err.errno not in (EPIPE, EINVAL): + raise + stdout, stderr = p.stdout.read(), p.stderr.read() + p.wait() + if p.returncode != 0: + encoding = locale.getpreferredencoding() + logger.warning(__('convert exited with error:\n' + '[stderr]\n%s\n[stdout]\n%s'), + stderr.decode(encoding), stdout.decode(encoding)) + return False + + return True + def convert(self, _from, _to): # type: (unicode, unicode) -> bool """Converts the image to expected one.""" @@ -61,7 +74,7 @@ class ImagemagickConverter(ImageConverter): self.config.image_converter_args + [_from, _to]) logger.debug('Invoking %r ...', args) - p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as err: if err.errno != ENOENT: # No such file or directory raise diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py index e55007d80..24f5267b8 100644 --- a/sphinx/testing/util.py +++ b/sphinx/testing/util.py @@ -23,6 +23,7 @@ from sphinx.builders.latex import LaTeXBuilder from sphinx.ext.autodoc import AutoDirective from sphinx.pycode import ModuleAnalyzer from sphinx.testing.path import path +from sphinx.util.osutil import relpath if False: # For type annotation @@ -201,7 +202,7 @@ def find_files(root, suffix=None): dirpath = path(dirpath) for f in [f for f in files if not suffix or f.endswith(suffix)]: # type: ignore fpath = dirpath / f - yield os.path.relpath(fpath, root) + yield relpath(fpath, root) def strip_escseq(text): diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index d82b02149..b32b317bb 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -44,35 +44,28 @@ default_substitutions = set([ class SphinxTransform(Transform): - """ - A base class of Transforms. + """A base class of Transforms. Compared with ``docutils.transforms.Transform``, this class improves accessibility to Sphinx APIs. - - The subclasses can access following objects and functions: - - self.app - The application object (:class:`sphinx.application.Sphinx`) - self.config - The config object (:class:`sphinx.config.Config`) - self.env - The environment object (:class:`sphinx.environment.BuildEnvironment`) """ @property def app(self): # type: () -> Sphinx + """Reference to the :class:`.Sphinx` object.""" return self.document.settings.env.app @property def env(self): # type: () -> BuildEnvironment + """Reference to the :class:`.BuildEnvironment` object.""" return self.document.settings.env @property def config(self): # type: () -> Config + """Reference to the :class:`.Config` object.""" return self.document.settings.env.config diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 949bec3b4..747b24565 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -20,7 +20,7 @@ from distutils.version import LooseVersion import docutils from docutils import nodes from docutils.languages import get_language -from docutils.parsers.rst import directives, roles, convert_directive_function +from docutils.parsers.rst import Directive, directives, roles, convert_directive_function from docutils.statemachine import StateMachine from docutils.utils import Reporter @@ -36,6 +36,7 @@ if False: # For type annotation from typing import Any, Callable, Generator, Iterator, List, Set, Tuple # NOQA from docutils.statemachine import State, ViewList # NOQA + from sphinx.config import Config # NOQA from sphinx.environment import BuildEnvironment # NOQA from sphinx.io import SphinxFileInput # NOQA @@ -273,6 +274,26 @@ def switch_source_input(state, content): state.memo.reporter.get_source_and_line = get_source_and_line +class SphinxDirective(Directive): + """A base class for Directives. + + Compared with ``docutils.parsers.rst.Directive``, this class improves + accessibility to Sphinx APIs. + """ + + @property + def env(self): + # type: () -> BuildEnvironment + """Reference to the :class:`.BuildEnvironment` object.""" + return self.state.document.settings.env + + @property + def config(self): + # type: () -> Config + """Reference to the :class:`.Config` object.""" + return self.env.config + + # cache a vanilla instance of nodes.document # Used in new_document() function __document_cache__ = None # type: nodes.document diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index 9b6270bdf..d18889756 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -23,7 +23,7 @@ from babel.messages.pofile import read_po from sphinx.errors import SphinxError from sphinx.locale import __ from sphinx.util import logging -from sphinx.util.osutil import SEP, walk +from sphinx.util.osutil import SEP, relpath, walk logger = logging.getLogger(__name__) @@ -97,7 +97,7 @@ def find_catalog_files(docname, srcdir, locale_dirs, lang, compaction): domain = find_catalog(docname, compaction) files = [gettext.find(domain, path.join(srcdir, dir_), [lang]) # type: ignore for dir_ in locale_dirs] - files = [path.relpath(f, srcdir) for f in files if f] # type: ignore + files = [relpath(f, srcdir) for f in files if f] # type: ignore return files # type: ignore @@ -138,7 +138,7 @@ def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact filenames = [f for f in filenames if f.endswith('.po')] for filename in filenames: base = path.splitext(filename)[0] - domain = path.relpath(path.join(dirpath, base), base_dir) + domain = relpath(path.join(dirpath, base), base_dir) if gettext_compact and path.sep in domain: domain = path.split(domain)[0] domain = domain.replace(path.sep, SEP) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 21464bbe6..986171293 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -210,14 +210,21 @@ def ustrftime(format, *args): return r.encode().decode('unicode-escape') -def safe_relpath(path, start=None): +def relpath(path, start=os.curdir): # type: (unicode, unicode) -> unicode + """Return a relative filepath to *path* either from the current directory or + from an optional *start* directory. + + This is an alternative of ``os.path.relpath()``. This returns original path + if *path* and *start* are on different drives (for Windows platform). + """ try: return os.path.relpath(path, start) except ValueError: return path +safe_relpath = relpath # for compatibility fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() # type: unicode |