diff --git a/.travis.yml b/.travis.yml index c468867ae..45bf4bb40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,12 +17,19 @@ env: matrix: - DOCUTILS=0.11 - DOCUTILS=0.12 +addons: + apt: + packages: + - graphviz + - texlive-latex-recommended + - texlive-latex-extra + - texlive-fonts-recommended + - texlive-fonts-extra install: - pip install -U pip - pip install docutils==$DOCUTILS - pip install -r test-reqs.txt before_script: flake8 script: - - - if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then make test-async; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then make style-check test-async; fi - if [[ $TRAVIS_PYTHON_VERSION != '3.5' ]]; then make test; fi diff --git a/CHANGES b/CHANGES index 2e925c761..c71261032 100644 --- a/CHANGES +++ b/CHANGES @@ -4,8 +4,28 @@ Release 1.5 (in development) Incompatible changes -------------------- -* LaTeX package fancybox is not longer a dependency of sphinx.sty +* latex, package fancybox is not longer a dependency of sphinx.sty * Use ``'locales'`` as a default value of `locale_dirs` +* latex, package ifthen is not any longer a dependency of sphinx.sty +* latex, style file does not modify fancyvrb's Verbatim (also available as + OriginalVerbatim) but uses sphinxVerbatim for name of custom wrapper. +* latex, package newfloat is no longer a dependency of sphinx.sty (ref #2660; + it was shipped with Sphinx since 1.3.4). +* latex, literal blocks in tables do not use OriginalVerbatim but + sphinxVerbatimintable which handles captions and wraps lines(ref #2704). +* latex, replace ``pt`` by TeX equivalent ``bp`` if found in ``width`` or + ``height`` attribute of an image. +* latex, if ``width`` or ``height`` attribute of an image is given with no unit, + use ``px`` rather than ignore it. +* latex: Separate stylesheets of pygments to independent .sty file +* #2454: The filename of sourcelink is now changed. The value of + `html_sourcelink_suffix` will be appended to the original filename (like + ``index.rst.txt``). +* ``sphinx.util.copy_static_entry()`` is now deprecated. + Use ``sphinx.util.fileutil.copy_asset()`` instead. +* ``sphinx.util.osutil.filecopy()`` skips copying if the file has not been changed + (ref: #2510, #2753) + Features added -------------- @@ -17,20 +37,141 @@ Features added * #2463, #2516: Add keywords of "meta" directive to search index * ``:maxdepth:`` option of toctree affects ``secnumdepth`` (ref: #2547) * #2575: Now ``sphinx.ext.graphviz`` allows ``:align:`` option +* Show warnings if unknown key is specified to `latex_elements` +* Show warnings if no domains match with `primary_domain` (ref: #2001) +* C++, show warnings when the kind of role is misleading for the kind + of target it refers to (e.g., using the `class` role for a function). +* latex, writer abstracts more of text styling into customizable macros, e.g. + the ``visit_emphasis`` will output ``\sphinxstyleemphasis`` rather than + ``\emph`` (which may be in use elsewhere or in an added LaTeX package). See + list at end of ``sphinx.sty`` (ref: #2686) +* latex, public names for environments and parameters used by note, warning, + and other admonition types, allowing full customizability from the + ``'preamble'`` key or an input file (ref: feature request #2674, #2685) +* latex, better computes column widths of some tables (as a result, there will + be slight changes as tables now correctly fill the line width; ref: #2708) +* latex, sphinxVerbatim environment is more easily customizable (ref: #2704). + In addition to already existing VerbatimColor and VerbatimBorderColor: + + - two lengths ``\sphinxverbatimsep`` and ``\sphinxverbatimborder``, + - booleans ``\ifsphinxverbatimwithframe`` and ``\ifsphinxverbatimwrapslines``. +* latex, captions for literal blocks inside tables are handled, and long code + lines wrapped to fit table cell (ref: #2704) +* #2597: Show warning messages as darkred +* latex, allow image dimensions using px unit (default is 96px=1in) +* Show warnings if invalid dimension units found +* #2650: Add ``--pdb`` option to setup.py command +* latex, make the use of ``\small`` for code listings customizable (ref #2721) +* #2663: Add ``--warning-is-error`` option to setup.py command +* Show warnings if deprecated latex options are used +* Add sphinx.config.ENUM to check the config values is in candidates +* math: Add hyperlink marker to each equations in HTML output + Bugs fixed ---------- +* #2707: (latex) the column width is badly computed for tabular + Documentation ------------- -Release 1.4.3 (in development) +Release 1.4.5 (in development) ============================== +Incompatible changes +-------------------- + +* latex, inclusion of non-inline images from image directive resulted in + non-coherent whitespaces depending on original image width; new behaviour + by necessity differs from earlier one in some cases. (ref: #2672) +* latex, use of ``\includegraphics`` to refer to Sphinx custom variant is + deprecated; in future it will revert to original LaTeX macro, custom one + already has alternative name ``\sphinxincludegraphics``. + +Features added +-------------- + +* new config option ``latex_keep_old_macro_names``, defaults to True. If False, + lets macros (for text styling) be defined only with ``\sphinx``-prefixed names. +* latex writer allows user customization of "shadowed" boxes (topics), via + three length variables. +* woff-format web font files now supported by the epub builder. + Bugs fixed ---------- +* #2676: (latex) Error with verbatim text in captions since Sphinx 1.4.4 +* #2629: memoir class crashes LaTeX. Fixed ``by latex_keep_old_macro_names=False`` (ref 2675) +* #2684: `sphinx.ext.intersphinx` crashes with six-1.4.1 +* #2679: ``float`` package needed for ``'figure_align': 'H'`` latex option +* #2671: image directive may lead to inconsistent spacing in pdf +* #2705: ``toctree`` generates empty bullet_list if ``:titlesonly:`` specified +* #2479: `sphinx.ext.viewcode` uses python2 highlighter by default +* #2700: HtmlHelp builder has hard coded index.html +* latex, since 1.4.4 inline literal text is followed by spurious space +* #2722: C++, fix id generation for var/member declarations to include namespaces. +* latex, images (from image directive) in lists or quoted blocks did not obey + indentation (fixed together with #2671) +* #2733: since Sphinx-1.4.4 ``make latexpdf`` generates lots of hyperref warnings +* #2731: `sphinx.ext.autodoc` does not access propertymethods which raises any + exceptions +* #2666: C++, properly look up nested names involving constructors. +* #2579: Could not refer a label including both spaces and colons via + `sphinx.ext.intersphinx` +* #2718: Sphinx crashes if the document file is not readable +* #2699: hyperlinks in help HTMLs are broken if `html_file_suffix` is set +* #2723: extra spaces in latex pdf output from multirow cell +* #2735: latexpdf ``Underfull \hbox (badness 10000)`` warnings from title page +* #2667: latex crashes if resized images appeared in section title + + +Release 1.4.4 (released Jun 12, 2016) +===================================== + +Bugs fixed +---------- + +* #2630: Latex sphinx.sty Notice Enviroment formatting problem +* #2632: Warning directives fail in quote environment latex build +* #2633: Sphinx crashes with old styled indices +* Fix a ``\begin{\minipage}`` typo in sphinx.sty from 1.4.2 (ref: 68becb1) +* #2622: Latex produces empty pages after title and table of contents +* #2640: 1.4.2 LaTeX crashes if code-block inside warning directive +* Let LaTeX use straight quotes also in inline code (ref #2627) +* #2351: latex crashes if enumerated lists are placed on footnotes +* #2646: latex crashes if math contains twice empty lines +* #2480: `sphinx.ext.autodoc`: memory addresses were shown +* latex: allow code-blocks appearing inside lists and quotes at maximal nesting + depth (ref #777, #2624, #2651) +* #2635: Latex code directives produce inconsistent frames based on viewing + resolution +* #2639: Sphinx now bundles iftex.sty +* Failed to build PDF with framed.sty 0.95 +* Sphinx now bundles needspace.sty + + +Release 1.4.3 (released Jun 5, 2016) +==================================== + +Bugs fixed +---------- + +* #2530: got "Counter too large" error on building PDF if large numbered + footnotes existed in admonitions +* ``width`` option of figure directive does not work if ``align`` option specified at same time (ref: #2595) +* #2590: The ``inputenc`` package breaks compiling under lualatex and xelatex +* #2540: date on latex front page use different font +* Suppress "document isn't included in any toctree" warning if the document is included (ref: #2603) +* #2614: Some tables in PDF output will end up shifted if user sets non zero + \parindent in preamble +* #2602: URL redirection breaks the hyperlinks generated by `sphinx.ext.intersphinx` +* #2613: Show warnings if merged extensions are loaded +* #2619: make sure amstext LaTeX package always loaded (ref: d657225, 488ee52, + 9d82cad and #2615) +* #2593: latex crashes if any figures in the table + Release 1.4.2 (released May 29, 2016) ===================================== @@ -69,7 +210,8 @@ Bugs fixed * #2397: Setup shorthandoff for turkish documents * #2447: VerbatimBorderColor wrongly used also for captions of PDF * #2456: C++, fix crash related to document merging (e.g., singlehtml and Latex builders). -* #2446: latex(pdf) sets local tables of contents (or more generally topic nodes) in unbreakable boxes, causes overflow at bottom +* #2446: latex(pdf) sets local tables of contents (or more generally topic + nodes) in unbreakable boxes, causes overflow at bottom * #2476: Omit MathJax markers if :nowrap: is given * #2465: latex builder fails in case no caption option is provided to toctree directive * Sphinx crashes if self referenced toctree found @@ -278,7 +420,8 @@ Bugs fixed is highlighted as Python 3 (which is mostly a superset of Python 2) if possible. To get the old behavior back, add ``highlight_language = "python"`` to conf.py. * #2329: Refresh environment forcely if source directory has changed. -* #2331: Fix code-blocks are filled by block in dvi; remove ``xcdraw`` option from xcolor package +* #2331: Fix code-blocks are filled by block in dvi; remove ``xcdraw`` option from + xcolor package * Fix the confval type checker emits warnings if unicode is given to confvals which expects string value * #2360: Fix numref in LaTeX output is broken * #2361: Fix additional paragraphs inside the "compound" directive are indented diff --git a/Makefile b/Makefile index 02854840e..3b9581033 100644 --- a/Makefile +++ b/Makefile @@ -33,12 +33,15 @@ all: clean-pyc clean-backupfiles style-check test style-check: @$(PYTHON) utils/check_sources.py $(DONT_CHECK) . -clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated +clean: clean-pyc clean-pycache clean-patchfiles clean-backupfiles clean-generated clean-testfiles clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + +clean-pycache: + find . -name __pycache__ -exec rm -rf {} + + clean-patchfiles: find . -name '*.orig' -exec rm -f {} + find . -name '*.rej' -exec rm -f {} + @@ -50,6 +53,10 @@ clean-backupfiles: clean-generated: rm -f utils/*3.py* +clean-testfiles: + rm -rf tests/build + rm -rf .tox/ + pylint: @pylint --rcfile utils/pylintrc sphinx diff --git a/README.rst b/README.rst index 391d0bd1a..384e2cc6a 100644 --- a/README.rst +++ b/README.rst @@ -33,6 +33,12 @@ Install from cloned source as editable:: pip install -e . +Release signatures +================== + +Releases are signed with `498D6B9E `_ + + Reading the docs ================ diff --git a/doc/_static/bookcover.png b/doc/_static/bookcover.png index 5b521b67b..0a8167fcc 100644 Binary files a/doc/_static/bookcover.png and b/doc/_static/bookcover.png differ diff --git a/doc/_static/pocoo.png b/doc/_static/pocoo.png index 67663b976..1648bb8d7 100644 Binary files a/doc/_static/pocoo.png and b/doc/_static/pocoo.png differ diff --git a/doc/_static/sphinx.png b/doc/_static/sphinx.png index 4bd9c7536..0a103cd3e 100644 Binary files a/doc/_static/sphinx.png and b/doc/_static/sphinx.png differ diff --git a/doc/_themes/sphinx13/static/bodybg.png b/doc/_themes/sphinx13/static/bodybg.png index ebe92f669..6f667b99e 100644 Binary files a/doc/_themes/sphinx13/static/bodybg.png and b/doc/_themes/sphinx13/static/bodybg.png differ diff --git a/doc/_themes/sphinx13/static/footerbg.png b/doc/_themes/sphinx13/static/footerbg.png index df783e2c7..d1bcb009b 100644 Binary files a/doc/_themes/sphinx13/static/footerbg.png and b/doc/_themes/sphinx13/static/footerbg.png differ diff --git a/doc/_themes/sphinx13/static/headerbg.png b/doc/_themes/sphinx13/static/headerbg.png index 22830f99e..522504964 100644 Binary files a/doc/_themes/sphinx13/static/headerbg.png and b/doc/_themes/sphinx13/static/headerbg.png differ diff --git a/doc/_themes/sphinx13/static/listitem.png b/doc/_themes/sphinx13/static/listitem.png index e45715f91..f7f814d00 100644 Binary files a/doc/_themes/sphinx13/static/listitem.png and b/doc/_themes/sphinx13/static/listitem.png differ diff --git a/doc/_themes/sphinx13/static/relbg.png b/doc/_themes/sphinx13/static/relbg.png index 2006af7d2..68a9b77eb 100644 Binary files a/doc/_themes/sphinx13/static/relbg.png and b/doc/_themes/sphinx13/static/relbg.png differ diff --git a/doc/_themes/sphinx13/static/sphinxheader.png b/doc/_themes/sphinx13/static/sphinxheader.png index 12988c3d1..845da4ab9 100644 Binary files a/doc/_themes/sphinx13/static/sphinxheader.png and b/doc/_themes/sphinx13/static/sphinxheader.png differ diff --git a/doc/config.rst b/doc/config.rst index f0a290a23..a71555974 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -56,7 +56,7 @@ General configuration .. confval:: extensions - A list of strings that are module names of Sphinx extensions. These can be + A list of strings that are module names of :ref:`extensions`. These can be extensions coming with Sphinx (named ``sphinx.ext.*``) or custom ones. Note that you can extend :data:`sys.path` within the conf file if your @@ -870,6 +870,13 @@ that use Sphinx's HTMLWriter class. .. versionadded:: 0.6 +.. confval:: html_sourcelink_suffix + + Suffix to be appended to source links (see :confval:`html_show_sourcelink`), + unless they have this suffix already. Default is ``'.txt'``. + + .. versionadded:: 1.5 + .. confval:: html_use_opensearch If nonempty, an `OpenSearch `_ description file will be @@ -1460,7 +1467,7 @@ the `Dublin Core metadata `_. Options for LaTeX output ------------------------ -These options influence LaTeX output. +These options influence LaTeX output. See further :doc:`latex`. .. confval:: latex_documents @@ -1568,6 +1575,21 @@ These options influence LaTeX output. value selected the ``'inline'`` display. For backwards compatibility, ``True`` is still accepted. +.. confval:: latex_keep_old_macro_names + + If ``True`` (default) the ``\strong``, ``\code``, ``\bfcode``, ``\email``, + ``\tablecontinued``, ``\titleref``, ``\menuselection``, ``\accelerator``, + ``\crossref``, ``\termref``, and ``\optional`` text styling macros are + pre-defined by Sphinx and may be user-customized by some + ``\renewcommand``'s inserted either via ``'preamble'`` key or :dudir:`raw + ` directive. If ``False``, only ``\sphinxstrong``, + etc... macros are defined (and may be redefined by user). Setting to + ``False`` may help solve macro name conflicts caused by user-added latex + packages. + + .. versionadded:: 1.4.5 + + .. confval:: latex_elements .. versionadded:: 0.5 @@ -1586,6 +1608,15 @@ These options influence LaTeX output. ``'pointsize'`` Point size option of the document class (``'10pt'``, ``'11pt'`` or ``'12pt'``), default ``'10pt'``. + ``'pxunit'`` + the value of the ``px`` when used in image attributes ``width`` and + ``height``. The default value is ``'49336sp'`` which achieves + ``96px=1in`` (``1in = 72.27*65536 = 4736286.72sp``, and all dimensions + in TeX are internally integer multiples of ``sp``). To obtain for + example ``100px=1in``, one can use ``'0.01in'`` but it is more precise + to use ``'47363sp'``. To obtain ``72px=1in``, use ``'1bp'``. + + .. versionadded:: 1.5 ``'babel'`` "babel" package inclusion, default ``'\\usepackage{babel}'``. ``'fontpkg'`` @@ -1608,7 +1639,7 @@ These options influence LaTeX output. .. versionadded:: 1.4 ``'preamble'`` - Additional preamble content, default empty. + Additional preamble content, default empty. See :doc:`latex`. ``'figure_align'`` Latex figure float alignment, default 'htbp' (here, top, bottom, page). Whenever an image doesn't fit into the current page, it will be @@ -1627,7 +1658,7 @@ These options influence LaTeX output. ``'\\usepackage[utf8]{inputenc}'`` when using pdflatex. Otherwise unset. - .. versionchanged:: 1.5 + .. versionchanged:: 1.4.3 Previously ``'\\usepackage[utf8]{inputenc}'`` was used for all compilers. ``'cmappkg'`` diff --git a/doc/contents.rst b/doc/contents.rst index a51910b81..79a10493d 100644 --- a/doc/contents.rst +++ b/doc/contents.rst @@ -1,3 +1,4 @@ + .. _contents: Sphinx documentation contents @@ -17,6 +18,7 @@ Sphinx documentation contents intl theming templating + latex extensions extdev/index websupport diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst index 3d4f66a9a..818b86007 100644 --- a/doc/ext/doctest.rst +++ b/doc/ext/doctest.rst @@ -182,7 +182,7 @@ The doctest extension uses the following configuration values: .. confval:: doctest_default_flags By default, these options are enabled: - + - ``ELLIPSIS``, allowing you to put ellipses in the expected output that match anything in the actual output; - ``IGNORE_EXCEPTION_DETAIL``, causing everything following the leftmost diff --git a/doc/ext/example_google.py b/doc/ext/example_google.py index 81a312cfd..c3ba273b8 100644 --- a/doc/ext/example_google.py +++ b/doc/ext/example_google.py @@ -43,6 +43,39 @@ on the first line, separated by a colon. """ +def function_with_types_in_docstring(param1, param2): + """Example function with types documented in the docstring. + + `PEP 484`_ type annotations are supported. If attribute, parameter, and + return types are annotated according to `PEP 484`_, they do not need to be + included in the docstring: + + Args: + param1 (int): The first parameter. + param2 (str): The second parameter. + + Returns: + bool: The return value. True for success, False otherwise. + + .. _PEP 484: + https://www.python.org/dev/peps/pep-0484/ + + """ + + +def function_with_pep484_type_annotations(param1: int, param2: str) -> bool: + """Example function with PEP 484 type annotations. + + Args: + param1: The first parameter. + param2: The second parameter. + + Returns: + The return value. True for success, False otherwise. + + """ + + def module_level_function(param1, param2=None, *args, **kwargs): """This is an example of a module level function. @@ -50,9 +83,6 @@ def module_level_function(param1, param2=None, *args, **kwargs): of each parameter is required. The type and description of each parameter is optional, but should be included if not obvious. - Parameter types -- if given -- should be specified according to - `PEP 484`_, though `PEP 484`_ conformance isn't required or enforced. - If \*args or \*\*kwargs are accepted, they should be listed as ``*args`` and ``**kwargs``. @@ -67,7 +97,7 @@ def module_level_function(param1, param2=None, *args, **kwargs): Args: param1 (int): The first parameter. - param2 (Optional[str]): The second parameter. Defaults to None. + param2 (:obj:`str`, optional): The second parameter. Defaults to None. Second line of description should be indented. *args: Variable length argument list. **kwargs: Arbitrary keyword arguments. @@ -94,10 +124,6 @@ def module_level_function(param1, param2=None, *args, **kwargs): that are relevant to the interface. ValueError: If `param2` is equal to `param1`. - - .. _PEP 484: - https://www.python.org/dev/peps/pep-0484/ - """ if param1 == param2: raise ValueError('param1 may not be equal to param2') @@ -139,7 +165,7 @@ class ExampleError(Exception): Args: msg (str): Human readable string describing the exception. - code (Optional[int]): Error code. + code (:obj:`int`, optional): Error code. Attributes: msg (str): Human readable string describing the exception. @@ -163,16 +189,9 @@ class ExampleClass(object): Properties created with the ``@property`` decorator should be documented in the property's getter method. - Attribute and property types -- if given -- should be specified according - to `PEP 484`_, though `PEP 484`_ conformance isn't required or enforced. - Attributes: attr1 (str): Description of `attr1`. - attr2 (Optional[int]): Description of `attr2`. - - - .. _PEP 484: - https://www.python.org/dev/peps/pep-0484/ + attr2 (:obj:`int`, optional): Description of `attr2`. """ @@ -190,20 +209,20 @@ class ExampleClass(object): Args: param1 (str): Description of `param1`. - param2 (Optional[int]): Description of `param2`. Multiple + param2 (:obj:`int`, optional): Description of `param2`. Multiple lines are supported. - param3 (List[str]): Description of `param3`. + param3 (:obj:`list` of :obj:`str`): Description of `param3`. """ self.attr1 = param1 self.attr2 = param2 self.attr3 = param3 #: Doc comment *inline* with attribute - #: List[str]: Doc comment *before* attribute, with type specified + #: list of str: Doc comment *before* attribute, with type specified self.attr4 = ['attr4'] self.attr5 = None - """Optional[str]: Docstring *after* attribute, with type specified.""" + """str: Docstring *after* attribute, with type specified.""" @property def readonly_property(self): @@ -212,8 +231,8 @@ class ExampleClass(object): @property def readwrite_property(self): - """List[str]: Properties with both a getter and setter should only - be documented in their getter method. + """:obj:`list` of :obj:`str`: Properties with both a getter and setter + should only be documented in their getter method. If the setter method contains notable behavior, it should be mentioned here. diff --git a/doc/ext/example_numpy.py b/doc/ext/example_numpy.py index bb91cac82..0e71008e1 100644 --- a/doc/ext/example_numpy.py +++ b/doc/ext/example_numpy.py @@ -37,6 +37,7 @@ module_level_variable1 : int one convention to document module level variables and be consistent with it. + .. _NumPy Documentation HOWTO: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt @@ -52,6 +53,52 @@ on the first line, separated by a colon. """ +def function_with_types_in_docstring(param1, param2): + """Example function with types documented in the docstring. + + `PEP 484`_ type annotations are supported. If attribute, parameter, and + return types are annotated according to `PEP 484`_, they do not need to be + included in the docstring: + + Parameters + ---------- + param1 : int + The first parameter. + param2 : str + The second parameter. + + Returns + ------- + bool + True if successful, False otherwise. + + .. _PEP 484: + https://www.python.org/dev/peps/pep-0484/ + + """ + + +def function_with_pep484_type_annotations(param1: int, param2: str) -> bool: + """Example function with PEP 484 type annotations. + + The return type must be duplicated in the docstring to comply + with the NumPy docstring style. + + Parameters + ---------- + param1 + The first parameter. + param2 + The second parameter. + + Returns + ------- + bool + True if successful, False otherwise. + + """ + + def module_level_function(param1, param2=None, *args, **kwargs): """This is an example of a module level function. @@ -59,9 +106,6 @@ def module_level_function(param1, param2=None, *args, **kwargs): The name of each parameter is required. The type and description of each parameter is optional, but should be included if not obvious. - Parameter types -- if given -- should be specified according to - `PEP 484`_, though `PEP 484`_ conformance isn't required or enforced. - If \*args or \*\*kwargs are accepted, they should be listed as ``*args`` and ``**kwargs``. @@ -81,7 +125,7 @@ def module_level_function(param1, param2=None, *args, **kwargs): ---------- param1 : int The first parameter. - param2 : Optional[str] + param2 : :obj:`str`, optional The second parameter. *args Variable length argument list. @@ -113,10 +157,6 @@ def module_level_function(param1, param2=None, *args, **kwargs): ValueError If `param2` is equal to `param1`. - - .. _PEP 484: - https://www.python.org/dev/peps/pep-0484/ - """ if param1 == param2: raise ValueError('param1 may not be equal to param2') @@ -166,7 +206,7 @@ class ExampleError(Exception): ---------- msg : str Human readable string describing the exception. - code : Optional[int] + code : :obj:`int`, optional Numeric error code. Attributes @@ -194,20 +234,13 @@ class ExampleClass(object): Properties created with the ``@property`` decorator should be documented in the property's getter method. - Attribute and property types -- if given -- should be specified according - to `PEP 484`_, though `PEP 484`_ conformance isn't required or enforced. - Attributes ---------- attr1 : str Description of `attr1`. - attr2 : Optional[int] + attr2 : :obj:`int`, optional Description of `attr2`. - - .. _PEP 484: - https://www.python.org/dev/peps/pep-0484/ - """ def __init__(self, param1, param2, param3): @@ -227,10 +260,10 @@ class ExampleClass(object): ---------- param1 : str Description of `param1`. - param2 : List[str] + param2 : :obj:`list` of :obj:`str` Description of `param2`. Multiple lines are supported. - param3 : Optional[int] + param3 : :obj:`int`, optional Description of `param3`. """ @@ -238,11 +271,11 @@ class ExampleClass(object): self.attr2 = param2 self.attr3 = param3 #: Doc comment *inline* with attribute - #: List[str]: Doc comment *before* attribute, with type specified + #: list of str: Doc comment *before* attribute, with type specified self.attr4 = ["attr4"] self.attr5 = None - """Optional[str]: Docstring *after* attribute, with type specified.""" + """str: Docstring *after* attribute, with type specified.""" @property def readonly_property(self): @@ -251,8 +284,8 @@ class ExampleClass(object): @property def readwrite_property(self): - """List[str]: Properties with both a getter and setter should only - be documented in their getter method. + """:obj:`list` of :obj:`str`: Properties with both a getter and setter + should only be documented in their getter method. If the setter method contains notable behavior, it should be mentioned here. diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst index dd8d5aa99..bd287aa49 100644 --- a/doc/ext/inheritance.rst +++ b/doc/ext/inheritance.rst @@ -40,7 +40,7 @@ It adds this directive: included. .. versionchanged:: 1.5 - Added ``caption`` option + Added ``caption`` option New config values are: diff --git a/doc/ext/napoleon.rst b/doc/ext/napoleon.rst index 2f2fb4376..ea3e4042f 100644 --- a/doc/ext/napoleon.rst +++ b/doc/ext/napoleon.rst @@ -186,11 +186,60 @@ not be mixed. Choose one style for your project and be consistent with it. * :ref:`example_google` * :ref:`example_numpy` - For Python type annotations, see `PEP 484`_. + +Type Annotations +---------------- + +`PEP 484`_ introduced a standard way to express types in Python code. +This is an alternative to expressing types directly in docstrings. +One benefit of expressing types according to `PEP 484`_ is that +type checkers and IDEs can take advantage of them for static code +analysis. + +Google style with Python 3 type annotations:: + + def func(arg1: int, arg2: str) -> bool: + """Summary line. + + Extended description of function. + + Args: + arg1: Description of arg1 + arg2: Description of arg2 + + Returns: + Description of return value + + """ + return True + +Google style with types in docstrings:: + + def func(arg1, arg2): + """Summary line. + + Extended description of function. + + Args: + arg1 (int): Description of arg1 + arg2 (str): Description of arg2 + + Returns: + bool: Description of return value + + """ + return True + +.. Note:: + `Python 2/3 compatible annotations`_ aren't currently + supported by Sphinx and won't show up in the docs. .. _PEP 484: https://www.python.org/dev/peps/pep-0484/ +.. _Python 2/3 compatible annotations: + https://www.python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code + Configuration ============= @@ -208,6 +257,7 @@ enabled in `conf.py`:: # Napoleon settings napoleon_google_docstring = True napoleon_numpy_docstring = True + napoleon_include_init_with_doc = False napoleon_include_private_with_doc = False napoleon_include_special_with_doc = True napoleon_use_admonition_for_examples = False @@ -234,6 +284,23 @@ enabled in `conf.py`:: True to parse `NumPy style`_ docstrings. False to disable support for NumPy style docstrings. *Defaults to True.* +.. confval:: napoleon_include_init_with_doc + + True to list ``__init___`` docstrings separately from the class + docstring. False to fall back to Sphinx's default behavior, which + considers the ``__init___`` docstring as part of the class + documentation. *Defaults to False.* + + **If True**:: + + def __init__(self): + \"\"\" + This will be included in the docs because it has a docstring + \"\"\" + + def __init__(self): + # This will NOT be included in the docs + .. confval:: napoleon_include_private_with_doc True to include private members (like ``_membername``) with docstrings diff --git a/doc/install.rst b/doc/install.rst index 0ecd73609..a2aeaf9cb 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -135,7 +135,7 @@ install. .. note:: ``pip`` has been contained in the Python official installation after version - of Python-3.4.0 or Python-2.7.9. + of Python-3.4.0 or Python-2.7.9. Installing Sphinx with pip diff --git a/doc/installpython.jpg b/doc/installpython.jpg index b0e458e40..fff4630ae 100644 Binary files a/doc/installpython.jpg and b/doc/installpython.jpg differ diff --git a/doc/latex.rst b/doc/latex.rst new file mode 100644 index 000000000..826a715c6 --- /dev/null +++ b/doc/latex.rst @@ -0,0 +1,151 @@ +.. highlightlang:: python + +.. _latex: + +LaTeX customization +=================== + +.. module:: latex + :synopsis: LaTeX specifics. + +The *latex* target does not (yet) benefit from pre-prepared themes like the +*html* target does (see :doc:`theming`). + +There are two principal means of setting up customization: + +#. usage of the :ref:`latex-options` as described in :doc:`config`, particularly the + various keys of :confval:`latex_elements`, to modify the loaded packages, + for example:: + + 'fontpkg': '\\usepackage{times}', # can load other font + 'fncychap': '\\usepackage[Bjarne]{fncychap}', # can use other option + + .. tip:: + + It is not mandatory to load *fncychap*. Naturally, without it and in + absence of further customizations, the chapter headings will revert to + LaTeX's default for the *report* class. + +#. usage of LaTeX ``\renewcommand``, ``\renewenvironment``, ``\setlength``, + ``\definecolor`` to modify the defaults from package file :file:`sphinx.sty` + and class files :file:`sphinxhowto.cls` and :file:`sphinxmanual.cls`. If such + definitions are few, they can be located inside the ``'preamble'`` key of + :confval:`latex_elements`. In case of many it may prove more convenient to + assemble them into a specialized file :file:`customizedmacros.tex` and use:: + + 'preamble': '\\makeatletter\\input{customizedmacros.tex}\\makeatother', + + More advanced LaTeX users will set up a style file + :file:`customizedmacros.sty`, which can then be loaded via:: + + 'preamble': '\\usepackage{customizedmacros}', + + The :ref:`build configuration file ` file will then have its variable + :confval:`latex_additional_files` appropriately configured, for example:: + + latex_additional_files = ["customizedmacros.sty"] + + Such *LaTeX Sphinx theme* files could possibly be contributed in the + future by advanced users for wider use. + +Let us illustrate here what can be modified by the second method. + +- text styling commands (they have one argument): ``\sphinx`` with + ```` being one of ``strong``, ``bfcode``, ``email``, ``tablecontinued``, + ``titleref``, ``menuselection``, ``accelerator``, ``crossref``, ``termref``, + ``optional``. By default and for backwards compatibility the ``\sphinx`` + expands to ``\`` hence the user can choose to customize rather the latter + (the non-prefixed macros will be left undefined if option + :confval:`latex_keep_old_macro_names` is set to ``False`` in :file:`conf.py`.) + + .. versionchanged:: 1.4.5 + use of ``\sphinx`` prefixed macro names to limit possibilities of conflict + with user added packages. The LaTeX writer uses always the prefixed names. +- more text styling commands: ``\sphinxstyle`` with ```` one of + ``indexentry``, ``indexextra``, ``indexpageref``, ``topictitle``, + ``sidebartitle``, ``othertitle``, ``sidebarsubtitle``, ``thead``, + ``emphasis``, ``literalemphasis``, ``strong``, ``literalstrong``, + ``abbreviation``, ``literalintitle``. + + .. versionadded:: 1.5 + earlier, the LaTeX writer used hard-coded ``\texttt``, ``\emph``, etc... +- parameters for paragraph level environments: with ```` one of + :dudir:`warning`, :dudir:`caution`, :dudir:`attention`, + :dudir:`danger`, :dudir:`error`, the colours + *sphinxbordercolor* and *sphinxbgcolor* can be + re-defined using ``\definecolor`` command. The + ``\sphinxborder`` is a command (not a LaTeX length) which + specifies the thickness of the frame (default ``1pt``) and can be + ``\renewcommand`` 'd. The same applies with ```` one of + :dudir:`note`, :dudir:`hint`, :dudir:`important`, :dudir:`tip`, but + the background colour is not implemented by the default environments + and the top and bottom rule thickness default is ``0.5pt``. + + .. versionchanged:: 1.5 + customizability of the parameters for each type of admonition. +- paragraph level environments: for each admonition as in the previous item, the + used environment is named ``sphinx``. They may be ``\renewenvironment`` + 'd individually, and must then be defined with one argument (it is the heading + of the notice, for example ``Warning:`` for :dudir:`warning` directive, if + English is the document language). Their default definitions use either the + *sphinxheavybox* (for the first listed directives) or the *sphinxlightbox* + environments, configured to use the parameters (colours, border thickness) + specific to each type, as mentioned in the previous item. + + .. versionchanged:: 1.5 + use of public environment names, separate customizability of the parameters. +- the :dudir:`contents` directive (with ``:local:`` option) and the + :dudir:`topic` directive are implemented by environment ``sphinxShadowBox``. + Its default definition obeys three LaTeX lengths (not commands) as parameters: + ``\sphinxshadowsep`` (distance from contents), ``\sphinxshadowsize`` (width of + lateral shadow), ``\sphinxshadowrule`` (thickness of the frame). + + .. versionchanged:: 1.5 + use of public names for the three lengths. The environment itself was + redefined to allow page breaks at release 1.4.2. +- the literal blocks (:rst:dir:`code-block` directives, etc ...), are + implemented using ``sphinxVerbatim`` environment which is a wrapper of + ``Verbatim`` environment from package ``fancyvrb.sty``. It adds the handling + of the top caption and the wrapping of long lines, and a frame which allows + pagebreaks. The LaTeX lengths (not commands) ``\sphinxverbatimsep`` and + ``\sphinxverbatimborder`` customize the framing. Inside tables the used + environment is ``sphinxVerbatimintable`` (it does not draw a frame, but + allows a caption). + + .. versionchanged:: 1.5 + ``Verbatim`` keeps exact same meaning as in ``fancyvrb.sty`` (meaning + which is the one of ``OriginalVerbatim`` too), and custom one is called + ``sphinxVerbatim``. Also, earlier version of Sphinx used + ``OriginalVerbatim`` inside tables (captions were lost, long code lines + were not wrapped), they now use ``sphinxVerbatimintable``. + .. versionadded:: 1.5 + the two customizable lengths, the ``sphinxVerbatimintable``. +- by default the Sphinx style file ``sphinx.sty`` includes the command + ``\fvset{fontsize=\small}`` as part of its configuration of + ``fancyvrb.sty``. The user may override this for example via + ``\fvset{fontsize=auto}`` which will use for listings the ambient + font size. Refer to ``fancyvrb.sty``'s documentation for further keys. + + .. versionadded:: 1.5 + formerly, the use of ``\small`` for code listings was not customizable. +- miscellaneous colours: *TitleColor*, *InnerLinkColor*, *OuterLinkColor*, + *VerbatimColor* (this is a background colour), *VerbatimBorderColor*. +- the ``\sphinxAtStartFootnote`` is inserted between footnote numbers and their + texts, by default it does ``\mbox{ }``. +- use ``\sphinxSetHeaderFamily`` to set the font used by headings + (default is ``\sffamily\bfseries``). + + .. versionadded:: 1.5 +- the section, subsection, ... headings are set using *titlesec*'s + ``\titleformat`` command. +- for the ``'manual'`` class, the chapter headings can be customized using + *fncychap*'s commands ``\ChNameVar``, ``\ChNumVar``, ``\ChTitleVar``. Or, if + the loading of this package has been removed from ``'fncychap'`` key, one can + use the *titlesec* ``\titleformat`` command. + +.. note:: + + It is impossible to revert or prevent the loading of a package that results + from a ``\usepackage`` executed from inside the :file:`sphinx.sty` style + file. Sphinx aims at loading as few packages as are really needed for its + default design. diff --git a/doc/more.png b/doc/more.png index a27a0fcba..97553a8b7 100644 Binary files a/doc/more.png and b/doc/more.png differ diff --git a/doc/pythonorg.png b/doc/pythonorg.png index 32f0787d1..cf9ccbbdb 100644 Binary files a/doc/pythonorg.png and b/doc/pythonorg.png differ diff --git a/doc/rest.rst b/doc/rest.rst index 293b2ea02..7b2b92ddc 100644 --- a/doc/rest.rst +++ b/doc/rest.rst @@ -363,8 +363,9 @@ directory on building (e.g. the ``_static`` directory for HTML output.) Interpretation of image size options (``width`` and ``height``) is as follows: if the size has no unit or the unit is pixels, the given size will only be -respected for output channels that support pixels (i.e. not in LaTeX output). -Other units (like ``pt`` for points) will be used for HTML and LaTeX output. +respected for output channels that support pixels. Other units (like ``pt`` +for points) will be used for HTML and LaTeX output (the latter replaces ``pt`` +by ``bp`` as this is the TeX unit such that ``72bp=1in``). Sphinx extends the standard docutils behavior by allowing an asterisk for the extension:: @@ -386,6 +387,9 @@ Note that image file names should not contain spaces. .. versionchanged:: 0.6 Image paths can now be absolute. +.. versionchanged:: 1.5 + latex target supports pixels (default is ``96px=1in``). + Footnotes --------- diff --git a/doc/themes/agogo.png b/doc/themes/agogo.png index 453a1f7dc..5a09cb96c 100644 Binary files a/doc/themes/agogo.png and b/doc/themes/agogo.png differ diff --git a/doc/themes/alabaster.png b/doc/themes/alabaster.png index 6e02c35ca..4a49c1ad0 100644 Binary files a/doc/themes/alabaster.png and b/doc/themes/alabaster.png differ diff --git a/doc/themes/bizstyle.png b/doc/themes/bizstyle.png index 4deae9a79..e19fb6b34 100644 Binary files a/doc/themes/bizstyle.png and b/doc/themes/bizstyle.png differ diff --git a/doc/themes/classic.png b/doc/themes/classic.png index 6989ebe9b..3b3c9cbd8 100644 Binary files a/doc/themes/classic.png and b/doc/themes/classic.png differ diff --git a/doc/themes/fullsize/agogo.png b/doc/themes/fullsize/agogo.png index bfdba3a17..106a16cea 100644 Binary files a/doc/themes/fullsize/agogo.png and b/doc/themes/fullsize/agogo.png differ diff --git a/doc/themes/fullsize/alabaster.png b/doc/themes/fullsize/alabaster.png index 3e026c999..5eca20912 100644 Binary files a/doc/themes/fullsize/alabaster.png and b/doc/themes/fullsize/alabaster.png differ diff --git a/doc/themes/fullsize/bizstyle.png b/doc/themes/fullsize/bizstyle.png index d917e2ff2..586064765 100644 Binary files a/doc/themes/fullsize/bizstyle.png and b/doc/themes/fullsize/bizstyle.png differ diff --git a/doc/themes/fullsize/classic.png b/doc/themes/fullsize/classic.png index 9c00f6899..269dab22f 100644 Binary files a/doc/themes/fullsize/classic.png and b/doc/themes/fullsize/classic.png differ diff --git a/doc/themes/fullsize/haiku.png b/doc/themes/fullsize/haiku.png index 8d807f4e1..707d2bfec 100644 Binary files a/doc/themes/fullsize/haiku.png and b/doc/themes/fullsize/haiku.png differ diff --git a/doc/themes/fullsize/nature.png b/doc/themes/fullsize/nature.png index 02d8743b3..00730c0a5 100644 Binary files a/doc/themes/fullsize/nature.png and b/doc/themes/fullsize/nature.png differ diff --git a/doc/themes/fullsize/pyramid.png b/doc/themes/fullsize/pyramid.png index 961cb896c..3b9d04d13 100644 Binary files a/doc/themes/fullsize/pyramid.png and b/doc/themes/fullsize/pyramid.png differ diff --git a/doc/themes/fullsize/scrolls.png b/doc/themes/fullsize/scrolls.png index 4e5c45f21..8a1c1faf5 100644 Binary files a/doc/themes/fullsize/scrolls.png and b/doc/themes/fullsize/scrolls.png differ diff --git a/doc/themes/fullsize/sphinx_rtd_theme.png b/doc/themes/fullsize/sphinx_rtd_theme.png index 4a3d74c88..95cff4ccd 100644 Binary files a/doc/themes/fullsize/sphinx_rtd_theme.png and b/doc/themes/fullsize/sphinx_rtd_theme.png differ diff --git a/doc/themes/fullsize/sphinxdoc.png b/doc/themes/fullsize/sphinxdoc.png index b74633452..eb498e3e8 100644 Binary files a/doc/themes/fullsize/sphinxdoc.png and b/doc/themes/fullsize/sphinxdoc.png differ diff --git a/doc/themes/fullsize/traditional.png b/doc/themes/fullsize/traditional.png index da69efe12..07ad00875 100644 Binary files a/doc/themes/fullsize/traditional.png and b/doc/themes/fullsize/traditional.png differ diff --git a/doc/themes/haiku.png b/doc/themes/haiku.png index 78a2570c4..4530debb9 100644 Binary files a/doc/themes/haiku.png and b/doc/themes/haiku.png differ diff --git a/doc/themes/nature.png b/doc/themes/nature.png index cbe773d5c..ad39b32b7 100644 Binary files a/doc/themes/nature.png and b/doc/themes/nature.png differ diff --git a/doc/themes/pyramid.png b/doc/themes/pyramid.png index eb13cd5f2..72749dd6b 100644 Binary files a/doc/themes/pyramid.png and b/doc/themes/pyramid.png differ diff --git a/doc/themes/scrolls.png b/doc/themes/scrolls.png index 30ccc8d49..1a117379f 100644 Binary files a/doc/themes/scrolls.png and b/doc/themes/scrolls.png differ diff --git a/doc/themes/sphinx_rtd_theme.png b/doc/themes/sphinx_rtd_theme.png index e13f52b04..7c3b7ae05 100644 Binary files a/doc/themes/sphinx_rtd_theme.png and b/doc/themes/sphinx_rtd_theme.png differ diff --git a/doc/themes/sphinxdoc.png b/doc/themes/sphinxdoc.png index 31512d8d8..587363e61 100644 Binary files a/doc/themes/sphinxdoc.png and b/doc/themes/sphinxdoc.png differ diff --git a/doc/themes/traditional.png b/doc/themes/traditional.png index 5ff44f869..9820fd0ea 100644 Binary files a/doc/themes/traditional.png and b/doc/themes/traditional.png differ diff --git a/doc/theming.rst b/doc/theming.rst index df0755f1f..ee45df52d 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -81,6 +81,8 @@ that has to return the directory with themes in it:: Builtin themes -------------- +.. cssclass:: longtable + +--------------------+--------------------+ | **Theme overview** | | +--------------------+--------------------+ diff --git a/doc/translation.png b/doc/translation.png index 347e287f6..11f3d02cd 100644 Binary files a/doc/translation.png and b/doc/translation.png differ diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 385746f72..bced21ade 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -300,12 +300,17 @@ features of intersphinx. More topics to be covered ------------------------- -- Other extensions (math, viewcode, doctest) +- :doc:`Other extensions `: + + * :doc:`ext/math`, + * :doc:`ext/viewcode`, + * :doc:`ext/doctest`, + * ... - Static files -- Selecting a theme -- Templating +- :doc:`Selecting a theme ` +- :ref:`Templating ` - Using extensions -- Writing extensions +- :ref:`Writing extensions ` .. rubric:: Footnotes diff --git a/setup.cfg b/setup.cfg index 2d2f3b535..be78d68d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,5 +25,5 @@ universal = 1 [flake8] max-line-length=95 -ignore=E113,E116,E221,E226,E241,E251 -exclude=ez_setup.py,utils/*,tests/*,build/*,sphinx/search/*,sphinx/pycode/pgen2/* +ignore=E113,E116,E221,E226,E241,E251,E901 +exclude=tests/*,build/*,sphinx/search/*,sphinx/pycode/pgen2/* diff --git a/setup.py b/setup.py index 3d792b2dc..c1aa346ba 100644 --- a/setup.py +++ b/setup.py @@ -64,6 +64,7 @@ extras_require = { 'nose', 'mock', # it would be better for 'test:python_version in "2.6,2.7"' 'simplejson', # better: 'test:platform_python_implementation=="PyPy"' + 'html5lib', ], } @@ -137,11 +138,8 @@ else: domain + '.js')) for js_file, (locale, po_file) in zip(js_files, po_files): - infile = open(po_file, 'r') - try: + with open(po_file, 'r') as infile: catalog = read_po(infile, locale) - finally: - infile.close() if catalog.fuzzy and not self.use_fuzzy: continue @@ -158,8 +156,7 @@ else: msgid = msgid[0] jscatalog[msgid] = message.string - outfile = open(js_file, 'wb') - try: + with open(js_file, 'wb') as outfile: outfile.write('Documentation.addTranslations(') dump(dict( messages=jscatalog, @@ -167,8 +164,6 @@ else: locale=str(catalog.locale) ), outfile, sort_keys=True) outfile.write(');') - finally: - outfile.close() cmdclass['compile_catalog'] = compile_catalog_plusjs diff --git a/sphinx/application.py b/sphinx/application.py index e82719d77..8a23cddd7 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -32,9 +32,8 @@ from sphinx.roles import XRefRole from sphinx.config import Config from sphinx.errors import SphinxError, SphinxWarning, ExtensionError, \ VersionRequirementError, ConfigError -from sphinx.domains import ObjType, BUILTIN_DOMAINS +from sphinx.domains import ObjType from sphinx.domains.std import GenericObject, Target, StandardDomain -from sphinx.builders import BUILTIN_BUILDERS from sphinx.environment import BuildEnvironment from sphinx.io import SphinxStandaloneReader from sphinx.util import pycompat # noqa: imported for side-effects @@ -42,7 +41,7 @@ from sphinx.util import import_object from sphinx.util.tags import Tags from sphinx.util.osutil import ENOENT from sphinx.util.logging import is_suppressed_warning -from sphinx.util.console import bold, lightgray, darkgray, darkgreen, \ +from sphinx.util.console import bold, lightgray, darkgray, darkred, darkgreen, \ term_width_line from sphinx.util.i18n import find_catalog_source_files @@ -65,10 +64,39 @@ events = { 'html-page-context': 'pagename, context, doctree or None', 'build-finished': 'exception', } +builtin_extensions = ( + 'sphinx.builders.applehelp', + 'sphinx.builders.changes', + 'sphinx.builders.epub', + 'sphinx.builders.epub3', + 'sphinx.builders.devhelp', + 'sphinx.builders.dummy', + 'sphinx.builders.gettext', + 'sphinx.builders.html', + 'sphinx.builders.htmlhelp', + 'sphinx.builders.latex', + 'sphinx.builders.linkcheck', + 'sphinx.builders.manpage', + 'sphinx.builders.qthelp', + 'sphinx.builders.texinfo', + 'sphinx.builders.text', + 'sphinx.builders.websupport', + 'sphinx.builders.xml', + 'sphinx.domains.c', + 'sphinx.domains.cpp', + 'sphinx.domains.javascript', + 'sphinx.domains.python', + 'sphinx.domains.rst', + 'sphinx.domains.std', +) CONFIG_FILENAME = 'conf.py' ENV_PICKLE_FILENAME = 'environment.pickle' +# list of deprecated extensions. Keys are extension name. +# Values are Sphinx version that merge the extension. +EXTENSION_BLACKLIST = {"sphinxjp.themecore": "1.2"} + class Sphinx(object): @@ -83,9 +111,9 @@ class Sphinx(object): self._additional_source_parsers = {} self._listeners = {} self._setting_up_extension = ['?'] - self.domains = BUILTIN_DOMAINS.copy() + self.domains = {} self.buildername = buildername - self.builderclasses = BUILTIN_BUILDERS.copy() + self.builderclasses = {} self.builder = None self.env = None self.enumerable_nodes = {} @@ -148,6 +176,10 @@ class Sphinx(object): if self.confdir is None: self.confdir = self.srcdir + # load all built-in extension modules + for extension in builtin_extensions: + self.setup_extension(extension) + # extension loading support for alabaster theme # self.config.html_theme is not set from conf.py at here # for now, sphinx always load a 'alabaster' extension. @@ -188,6 +220,10 @@ class Sphinx(object): 'version %s and therefore cannot be built with the ' 'loaded version (%s).' % (extname, needs_ver, has_ver)) + # check primary_domain if requested + if self.config.primary_domain and self.config.primary_domain not in self.domains: + self.warn('primary_domain %r not found, ignored.' % self.config.primary_domain) + # set up translation infrastructure self._init_i18n() # check all configuration values for permissible types @@ -235,8 +271,8 @@ class Sphinx(object): def _init_env(self, freshenv): if freshenv: - self.env = BuildEnvironment(self.srcdir, self.doctreedir, - self.config) + self.env = BuildEnvironment(self.srcdir, self.doctreedir, self.config) + self.env.set_warnfunc(self.warn) self.env.find_files(self.config) for domain in self.domains.keys(): self.env.domains[domain] = self.domains[domain](self.env) @@ -245,6 +281,7 @@ class Sphinx(object): self.info(bold('loading pickled environment... '), nonl=True) self.env = BuildEnvironment.frompickle( self.srcdir, self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME)) + self.env.set_warnfunc(self.warn) self.env.domains = {} for domain in self.domains.keys(): # this can raise if the data version doesn't fit @@ -257,8 +294,6 @@ class Sphinx(object): self.info('failed: %s' % err) return self._init_env(freshenv=True) - self.env.set_warnfunc(self.warn) - def _init_builder(self, buildername): if buildername is None: print('No builder selected, using default: html', file=self._status) @@ -267,11 +302,6 @@ class Sphinx(object): raise SphinxError('Builder name %s not registered' % buildername) builderclass = self.builderclasses[buildername] - if isinstance(builderclass, tuple): - # builtin builder - mod, cls = builderclass - builderclass = getattr( - __import__('sphinx.builders.' + mod, None, None, [cls]), cls) self.builder = builderclass(self) self.emit('builder-inited') @@ -326,7 +356,8 @@ class Sphinx(object): wfile.flush() self.messagelog.append(message) - def warn(self, message, location=None, prefix='WARNING: ', type=None, subtype=None): + def warn(self, message, location=None, prefix='WARNING: ', + type=None, subtype=None, colorfunc=darkred): """Emit a warning. If *location* is given, it should either be a tuple of (docname, lineno) @@ -356,7 +387,7 @@ class Sphinx(object): if self.warningiserror: raise SphinxWarning(warntext) self._warncount += 1 - self._log(warntext, self._warning, True) + self._log(colorfunc(warntext), self._warning, True) def info(self, message='', nonl=False): """Emit an informational message. @@ -460,6 +491,11 @@ class Sphinx(object): self.debug('[app] setting up extension: %r', extension) if extension in self._extensions: return + if extension in EXTENSION_BLACKLIST: + self.warn('the extension %r was already merged with Sphinx since version %s; ' + 'this extension is ignored.' % ( + extension, EXTENSION_BLACKLIST[extension])) + return self._setting_up_extension.append(extension) try: mod = __import__(extension, None, None, ['setup']) @@ -557,13 +593,9 @@ class Sphinx(object): raise ExtensionError('Builder class %s has no "name" attribute' % builder) if builder.name in self.builderclasses: - if isinstance(self.builderclasses[builder.name], tuple): - raise ExtensionError('Builder %r is a builtin builder' % - builder.name) - else: - raise ExtensionError( - 'Builder %r already exists (in module %s)' % ( - builder.name, self.builderclasses[builder.name].__module__)) + raise ExtensionError( + 'Builder %r already exists (in module %s)' % ( + builder.name, self.builderclasses[builder.name].__module__)) self.builderclasses[builder.name] = builder def add_config_value(self, name, default, rebuild, types=()): diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 8863050ba..fe0c9c665 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -451,29 +451,3 @@ class Builder(object): except AttributeError: optname = '%s_%s' % (default, option) return getattr(self.config, optname) - -BUILTIN_BUILDERS = { - 'dummy': ('dummy', 'DummyBuilder'), - 'html': ('html', 'StandaloneHTMLBuilder'), - 'dirhtml': ('html', 'DirectoryHTMLBuilder'), - 'singlehtml': ('html', 'SingleFileHTMLBuilder'), - 'pickle': ('html', 'PickleHTMLBuilder'), - 'json': ('html', 'JSONHTMLBuilder'), - 'web': ('html', 'PickleHTMLBuilder'), - 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'), - 'devhelp': ('devhelp', 'DevhelpBuilder'), - 'qthelp': ('qthelp', 'QtHelpBuilder'), - 'applehelp': ('applehelp', 'AppleHelpBuilder'), - 'epub': ('epub', 'EpubBuilder'), - 'epub3': ('epub3', 'Epub3Builder'), - 'latex': ('latex', 'LaTeXBuilder'), - 'text': ('text', 'TextBuilder'), - 'man': ('manpage', 'ManualPageBuilder'), - 'texinfo': ('texinfo', 'TexinfoBuilder'), - 'changes': ('changes', 'ChangesBuilder'), - 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), - 'websupport': ('websupport', 'WebSupportBuilder'), - 'gettext': ('gettext', 'MessageCatalogBuilder'), - 'xml': ('xml', 'XMLBuilder'), - 'pseudoxml': ('xml', 'PseudoXMLBuilder'), -} diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py index d3ad861dc..e8a476f60 100644 --- a/sphinx/builders/applehelp.py +++ b/sphinx/builders/applehelp.py @@ -13,14 +13,16 @@ from __future__ import print_function import codecs import pipes -from os import path +from os import path, environ +import shlex from sphinx.builders.html import StandaloneHTMLBuilder -from sphinx.util import copy_static_entry -from sphinx.util.osutil import copyfile, ensuredir +from sphinx.config import string_classes +from sphinx.util.osutil import copyfile, ensuredir, make_filename from sphinx.util.console import bold +from sphinx.util.fileutil import copy_asset from sphinx.util.pycompat import htmlescape -from sphinx.util.matching import compile_matchers +from sphinx.util.matching import Matcher from sphinx.errors import SphinxError import plistlib @@ -84,6 +86,7 @@ class AppleHelpBuilder(StandaloneHTMLBuilder): super(AppleHelpBuilder, self).init() # the output files for HTML help must be .html only self.out_suffix = '.html' + self.link_suffix = '.html' if self.config.applehelp_bundle_id is None: raise SphinxError('You must set applehelp_bundle_id before ' @@ -104,17 +107,15 @@ class AppleHelpBuilder(StandaloneHTMLBuilder): self.finish_tasks.add_task(self.build_helpbook) def copy_localized_files(self): - source_dir = path.join(self.confdir, - self.config.applehelp_locale + '.lproj') + source_dir = path.join(self.confdir, self.config.applehelp_locale + '.lproj') target_dir = self.outdir if path.isdir(source_dir): self.info(bold('copying localized files... '), nonl=True) - ctx = self.globalcontext.copy() - matchers = compile_matchers(self.config.exclude_patterns) - copy_static_entry(source_dir, target_dir, self, ctx, - exclude_matchers=matchers) + excluded = Matcher(self.config.exclude_patterns + ['**/.*']) + copy_asset(source_dir, target_dir, excluded, + context=self.globalcontext, renderer=self.templates) self.info('done') @@ -254,3 +255,35 @@ class AppleHelpBuilder(StandaloneHTMLBuilder): raise AppleHelpCodeSigningFailed(output) else: self.info('done') + + +def setup(app): + app.setup_extension('sphinx.builders.html') + app.add_builder(AppleHelpBuilder) + + app.add_config_value('applehelp_bundle_name', + lambda self: make_filename(self.project), 'applehelp') + app.add_config_value('applehelp_bundle_id', None, 'applehelp', string_classes) + app.add_config_value('applehelp_dev_region', 'en-us', 'applehelp') + app.add_config_value('applehelp_bundle_version', '1', 'applehelp') + app.add_config_value('applehelp_icon', None, 'applehelp', string_classes) + app.add_config_value('applehelp_kb_product', + lambda self: '%s-%s' % (make_filename(self.project), self.release), + 'applehelp') + app.add_config_value('applehelp_kb_url', None, 'applehelp', string_classes) + app.add_config_value('applehelp_remote_url', None, 'applehelp', string_classes) + app.add_config_value('applehelp_index_anchors', False, 'applehelp', string_classes) + app.add_config_value('applehelp_min_term_length', None, 'applehelp', string_classes) + app.add_config_value('applehelp_stopwords', + lambda self: self.language or 'en', 'applehelp') + app.add_config_value('applehelp_locale', lambda self: self.language or 'en', 'applehelp') + app.add_config_value('applehelp_title', lambda self: self.project + ' Help', 'applehelp') + app.add_config_value('applehelp_codesign_identity', + lambda self: environ.get('CODE_SIGN_IDENTITY', None), + 'applehelp'), + app.add_config_value('applehelp_codesign_flags', + lambda self: shlex.split(environ.get('OTHER_CODE_SIGN_FLAGS', '')), + 'applehelp'), + app.add_config_value('applehelp_indexer_path', '/usr/bin/hiutil', 'applehelp') + app.add_config_value('applehelp_codesign_path', '/usr/bin/codesign', 'applehelp') + app.add_config_value('applehelp_disable_external_tools', False, None) diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py index ed9edc403..1bccb67d9 100644 --- a/sphinx/builders/changes.py +++ b/sphinx/builders/changes.py @@ -15,12 +15,12 @@ from os import path from six import iteritems from sphinx import package_dir -from sphinx.util import copy_static_entry from sphinx.locale import _ from sphinx.theming import Theme from sphinx.builders import Builder from sphinx.util.osutil import ensuredir, os_path from sphinx.util.console import bold +from sphinx.util.fileutil import copy_asset_file from sphinx.util.pycompat import htmlescape @@ -138,12 +138,10 @@ class ChangesBuilder(Builder): f.write(self.templates.render('changes/rstsource.html', ctx)) themectx = dict(('theme_' + key, val) for (key, val) in iteritems(self.theme.get_options({}))) - copy_static_entry(path.join(package_dir, 'themes', 'default', - 'static', 'default.css_t'), - self.outdir, self, themectx) - copy_static_entry(path.join(package_dir, 'themes', 'basic', - 'static', 'basic.css'), - self.outdir, self) + copy_asset_file(path.join(package_dir, 'themes', 'default', 'static', 'default.css_t'), + self.outdir, context=themectx, renderer=self.templates) + copy_asset_file(path.join(package_dir, 'themes', 'basic', 'static', 'basic.css'), + self.outdir) def hl(self, text, version): text = htmlescape(text) @@ -154,3 +152,7 @@ class ChangesBuilder(Builder): def finish(self): pass + + +def setup(app): + app.add_builder(ChangesBuilder) diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py index eb3e997d8..0f88e9f38 100644 --- a/sphinx/builders/devhelp.py +++ b/sphinx/builders/devhelp.py @@ -18,6 +18,7 @@ from os import path from docutils import nodes from sphinx import addnodes +from sphinx.util.osutil import make_filename from sphinx.builders.html import StandaloneHTMLBuilder try: @@ -59,6 +60,7 @@ class DevhelpBuilder(StandaloneHTMLBuilder): def init(self): StandaloneHTMLBuilder.init(self) self.out_suffix = '.html' + self.link_suffix = '.html' def handle_finish(self): self.build_devhelp(self.outdir, self.config.devhelp_basename) @@ -129,3 +131,10 @@ class DevhelpBuilder(StandaloneHTMLBuilder): # Dump the XML file with comp_open(path.join(outdir, outname + '.devhelp'), 'w') as f: tree.write(f, 'utf-8') + + +def setup(app): + app.setup_extension('sphinx.builders.html') + app.add_builder(DevhelpBuilder) + + app.add_config_value('devhelp_basename', lambda self: make_filename(self.project), None) diff --git a/sphinx/builders/dummy.py b/sphinx/builders/dummy.py index 75b834c2b..b119d9687 100644 --- a/sphinx/builders/dummy.py +++ b/sphinx/builders/dummy.py @@ -34,3 +34,7 @@ class DummyBuilder(Builder): def finish(self): pass + + +def setup(app): + app.add_builder(DummyBuilder) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 349574ae0..e46d2cba0 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -29,7 +29,7 @@ from docutils import nodes from sphinx import addnodes from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.util.i18n import format_date -from sphinx.util.osutil import ensuredir, copyfile, EEXIST +from sphinx.util.osutil import ensuredir, copyfile, make_filename, EEXIST from sphinx.util.smartypants import sphinx_smarty_pants as ssp from sphinx.util.console import brown @@ -152,6 +152,7 @@ MEDIA_TYPES = { '.jpeg': 'image/jpeg', '.otf': 'application/x-font-otf', '.ttf': 'application/x-font-ttf', + '.woff': 'application/font-woff', } VECTOR_GRAPHICS_EXTENSIONS = ('.svg',) @@ -760,3 +761,33 @@ class EpubBuilder(StandaloneHTMLBuilder): fp = path.join(outdir, file) epub.write(fp, file, zipfile.ZIP_DEFLATED) epub.close() + + +def setup(app): + app.setup_extension('sphinx.builders.html') + app.add_builder(EpubBuilder) + + # config values + app.add_config_value('epub_basename', lambda self: make_filename(self.project), None) + app.add_config_value('epub_theme', 'epub', 'html') + app.add_config_value('epub_theme_options', {}, 'html') + app.add_config_value('epub_title', lambda self: self.html_title, 'html') + app.add_config_value('epub_author', 'unknown', 'html') + app.add_config_value('epub_language', lambda self: self.language or 'en', 'html') + app.add_config_value('epub_publisher', 'unknown', 'html') + app.add_config_value('epub_copyright', lambda self: self.copyright, 'html') + app.add_config_value('epub_identifier', 'unknown', 'html') + app.add_config_value('epub_scheme', 'unknown', 'html') + app.add_config_value('epub_uid', 'unknown', 'env') + app.add_config_value('epub_cover', (), 'env') + app.add_config_value('epub_guide', (), 'env') + app.add_config_value('epub_pre_files', [], 'env') + app.add_config_value('epub_post_files', [], 'env') + app.add_config_value('epub_exclude_files', [], 'env') + app.add_config_value('epub_tocdepth', 3, 'env') + app.add_config_value('epub_tocdup', True, 'env') + app.add_config_value('epub_tocscope', 'default', 'env') + app.add_config_value('epub_fix_images', False, 'env') + app.add_config_value('epub_max_image_width', 0, 'env') + app.add_config_value('epub_show_urls', 'inline', 'html') + app.add_config_value('epub_use_index', lambda self: self.html_use_index, 'html') diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index b243486f6..57ed1abbe 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -13,6 +13,7 @@ import codecs from os import path +from sphinx.config import string_classes from sphinx.builders.epub import EpubBuilder @@ -209,3 +210,12 @@ class Epub3Builder(EpubBuilder): # Add nav.xhtml to epub file self.files.append(outname) + + +def setup(app): + app.setup_extension('sphinx.builders.epub') + app.add_builder(Epub3Builder) + + app.add_config_value('epub3_description', '', 'epub3', string_classes) + app.add_config_value('epub3_contributor', 'unknown', 'epub3', string_classes) + app.add_config_value('epub3_page_progression_direction', 'ltr', 'epub3', string_classes) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index c8f4dab4f..aa9400575 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -135,7 +135,7 @@ tzdelta = datetime.fromtimestamp(timestamp) - \ source_date_epoch = getenv('SOURCE_DATE_EPOCH') if source_date_epoch is not None: timestamp = float(source_date_epoch) - tzdelta = 0 + tzdelta = timedelta(0) class LocalTimeZone(tzinfo): @@ -233,3 +233,13 @@ class MessageCatalogBuilder(I18nBuilder): replace('"', r'\"'). \ replace('\n', '\\n"\n"') pofile.write('msgid "%s"\nmsgstr ""\n\n' % message) + + +def setup(app): + app.add_builder(MessageCatalogBuilder) + + app.add_config_value('gettext_compact', True, 'gettext') + app.add_config_value('gettext_location', True, 'gettext') + app.add_config_value('gettext_uuid', False, 'gettext') + app.add_config_value('gettext_auto_build', True, 'env') + app.add_config_value('gettext_additional_targets', [], 'env') diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 63ccdd66c..462f5f00a 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -27,13 +27,15 @@ from docutils.frontend import OptionParser from docutils.readers.doctree import Reader as DoctreeReader from sphinx import package_dir, __display_version__ -from sphinx.util import jsonimpl, copy_static_entry, copy_extra_entry +from sphinx.util import jsonimpl from sphinx.util.i18n import format_date from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \ movefile, copyfile from sphinx.util.nodes import inline_all_toctrees -from sphinx.util.matching import patmatch, compile_matchers -from sphinx.locale import _ +from sphinx.util.fileutil import copy_asset +from sphinx.util.matching import patmatch, Matcher, DOTFILES +from sphinx.config import string_classes +from sphinx.locale import _, l_ from sphinx.search import js_index from sphinx.theming import Theme from sphinx.builders import Builder @@ -339,6 +341,7 @@ class StandaloneHTMLBuilder(Builder): show_sphinx = self.config.html_show_sphinx, has_source = self.config.html_copy_source, show_source = self.config.html_show_sourcelink, + sourcelink_suffix = self.config.html_sourcelink_suffix, file_suffix = self.out_suffix, script_files = self.script_files, language = self.config.language, @@ -402,15 +405,21 @@ class StandaloneHTMLBuilder(Builder): # title rendered as HTML title = self.env.longtitles.get(docname) title = title and self.render_partial(title)['title'] or '' + + # Suffix for the document + source_suffix = path.splitext(self.env.doc2path(docname))[1] + # the name for the copied source - sourcename = self.config.html_copy_source and docname + '.txt' or '' + if self.config.html_copy_source: + sourcename = docname + source_suffix + if source_suffix != self.config.html_sourcelink_suffix: + sourcename += self.config.html_sourcelink_suffix + else: + sourcename = '' # metadata for the document meta = self.env.metadata.get(docname) - # Suffix for the document - source_suffix = '.' + self.env.doc2path(docname).split('.')[-1] - # local TOC and global TOC tree self_toc = self.env.get_toc_for(docname, self) toc = self.render_partial(self_toc)['fragment'] @@ -581,9 +590,8 @@ class StandaloneHTMLBuilder(Builder): self.info(bold('copying static files... '), nonl=True) ensuredir(path.join(self.outdir, '_static')) # first, create pygments style file - f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w') - f.write(self.highlighter.get_stylesheet()) - f.close() + with open(path.join(self.outdir, '_static', 'pygments.css'), 'w') as f: + f.write(self.highlighter.get_stylesheet()) # then, copy translations JavaScript file if self.config.language is not None: jsfile = self._get_translations_js() @@ -605,21 +613,19 @@ class StandaloneHTMLBuilder(Builder): # then, copy over theme-supplied static files if self.theme: - themeentries = [path.join(themepath, 'static') - for themepath in self.theme.get_dirchain()[::-1]] - for entry in themeentries: - copy_static_entry(entry, path.join(self.outdir, '_static'), - self, ctx) + for theme_path in self.theme.get_dirchain()[::-1]: + entry = path.join(theme_path, 'static') + copy_asset(entry, path.join(self.outdir, '_static'), excluded=DOTFILES, + context=ctx, renderer=self.templates) # then, copy over all user-supplied static files - staticentries = [path.join(self.confdir, spath) - for spath in self.config.html_static_path] - matchers = compile_matchers(self.config.exclude_patterns) - for entry in staticentries: + excluded = Matcher(self.config.exclude_patterns + ["**/.*"]) + for static_path in self.config.html_static_path: + entry = path.join(self.confdir, static_path) if not path.exists(entry): self.warn('html_static_path entry %r does not exist' % entry) continue - copy_static_entry(entry, path.join(self.outdir, '_static'), self, - ctx, exclude_matchers=matchers) + copy_asset(entry, path.join(self.outdir, '_static'), excluded, + context=ctx, renderer=self.templates) # copy logo and favicon files if not already in static path if self.config.html_logo: logobase = path.basename(self.config.html_logo) @@ -642,14 +648,15 @@ class StandaloneHTMLBuilder(Builder): def copy_extra_files(self): # copy html_extra_path files self.info(bold('copying extra files... '), nonl=True) - extraentries = [path.join(self.confdir, epath) - for epath in self.config.html_extra_path] - matchers = compile_matchers(self.config.exclude_patterns) - for entry in extraentries: + excluded = Matcher(self.config.exclude_patterns) + + for extra_path in self.config.html_extra_path: + entry = path.join(self.confdir, extra_path) if not path.exists(entry): self.warn('html_extra_path entry %r does not exist' % entry) continue - copy_extra_entry(entry, self.outdir, matchers) + + copy_asset(entry, self.outdir, excluded) self.info('done') def write_buildinfo(self): @@ -712,7 +719,12 @@ class StandaloneHTMLBuilder(Builder): def index_page(self, pagename, doctree, title): # only index pages with title if self.indexer is not None and title: - self.indexer.feed(pagename, title, doctree) + filename = self.env.doc2path(pagename, base=None) + try: + self.indexer.feed(pagename, filename, title, doctree) + except TypeError: + # fallback for old search-adapters + self.indexer.feed(pagename, title, doctree) def _get_local_toctree(self, docname, collapse=True, **kwds): if 'includehidden' not in kwds: @@ -1056,6 +1068,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): self.theme = None # no theme necessary self.templates = None # no template bridge necessary self.init_translator_class() + self.init_templates() self.init_highlighter() def get_target_uri(self, docname, typ=None): @@ -1148,3 +1161,52 @@ class JSONHTMLBuilder(SerializingHTMLBuilder): def init(self): SerializingHTMLBuilder.init(self) + + +def setup(app): + # builders + app.add_builder(StandaloneHTMLBuilder) + app.add_builder(DirectoryHTMLBuilder) + app.add_builder(SingleFileHTMLBuilder) + app.add_builder(PickleHTMLBuilder) + app.add_builder(JSONHTMLBuilder) + + # config values + app.add_config_value('html_theme', 'alabaster', 'html') + app.add_config_value('html_theme_path', [], 'html') + app.add_config_value('html_theme_options', {}, 'html') + app.add_config_value('html_title', + lambda self: l_('%s %s documentation') % (self.project, self.release), + 'html', string_classes) + app.add_config_value('html_short_title', lambda self: self.html_title, 'html') + app.add_config_value('html_style', None, 'html', string_classes) + app.add_config_value('html_logo', None, 'html', string_classes) + app.add_config_value('html_favicon', None, 'html', string_classes) + app.add_config_value('html_static_path', [], 'html') + app.add_config_value('html_extra_path', [], 'html') + app.add_config_value('html_last_updated_fmt', None, 'html', string_classes) + app.add_config_value('html_use_smartypants', True, 'html') + app.add_config_value('html_translator_class', None, 'html', string_classes) + app.add_config_value('html_sidebars', {}, 'html') + app.add_config_value('html_additional_pages', {}, 'html') + app.add_config_value('html_use_modindex', True, 'html') # deprecated + app.add_config_value('html_domain_indices', True, 'html', [list]) + app.add_config_value('html_add_permalinks', u'\u00B6', 'html') + app.add_config_value('html_use_index', True, 'html') + app.add_config_value('html_split_index', False, 'html') + app.add_config_value('html_copy_source', True, 'html') + app.add_config_value('html_show_sourcelink', True, 'html') + app.add_config_value('html_sourcelink_suffix', '.txt', 'html') + app.add_config_value('html_use_opensearch', '', 'html') + app.add_config_value('html_file_suffix', None, 'html', string_classes) + app.add_config_value('html_link_suffix', None, 'html', string_classes) + app.add_config_value('html_show_copyright', True, 'html') + app.add_config_value('html_show_sphinx', True, 'html') + app.add_config_value('html_context', {}, 'html') + app.add_config_value('html_output_encoding', 'utf-8', 'html') + app.add_config_value('html_compact_lists', True, 'html') + app.add_config_value('html_secnumber_suffix', '. ', 'html') + app.add_config_value('html_search_language', None, 'html', string_classes) + app.add_config_value('html_search_options', {}, 'html') + app.add_config_value('html_search_scorer', '', None) + app.add_config_value('html_scaled_image_link', True, 'html') diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index b1a5d7dda..cdb51b2c6 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -18,6 +18,7 @@ from os import path from docutils import nodes from sphinx import addnodes +from sphinx.util.osutil import make_filename from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.util.pycompat import htmlescape @@ -63,7 +64,7 @@ Binary Index=No Compiled file=%(outname)s.chm Contents file=%(outname)s.hhc Default Window=%(outname)s -Default topic=index.html +Default topic=%(master_doc)s Display compile progress=No Full text search stop list file=%(outname)s.stp Full-text search=Yes @@ -73,7 +74,7 @@ Title=%(title)s [WINDOWS] %(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\ -"index.html","index.html",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 +"%(master_doc)s","%(master_doc)s",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 [FILES] ''' @@ -183,6 +184,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): StandaloneHTMLBuilder.init(self) # the output files for HTML help must be .html only self.out_suffix = '.html' + self.link_suffix = '.html' # determine the correct locale setting locale = chm_locales.get(self.config.language) if locale is not None: @@ -204,11 +206,14 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): self.info('writing project file...') with self.open_file(outdir, outname+'.hhp') as f: - f.write(project_template % {'outname': outname, - 'title': self.config.html_title, - 'version': self.config.version, - 'project': self.config.project, - 'lcid': self.lcid}) + f.write(project_template % { + 'outname': outname, + 'title': self.config.html_title, + 'version': self.config.version, + 'project': self.config.project, + 'lcid': self.lcid, + 'master_doc': self.config.master_doc + self.out_suffix + }) if not outdir.endswith(os.sep): outdir += os.sep olen = len(outdir) @@ -225,7 +230,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): f.write(contents_header) # special books f.write('
  • ' + object_sitemap % (self.config.html_short_title, - 'index.html')) + self.config.master_doc + self.out_suffix)) for indexname, indexcls, content, collapse in self.domain_indices: f.write('
  • ' + object_sitemap % (indexcls.localname, '%s.html' % indexname)) @@ -292,3 +297,10 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): for title, (refs, subitems, key_) in group: write_index(title, refs, subitems) f.write('\n') + + +def setup(app): + app.setup_extension('sphinx.builders.html') + app.add_builder(HTMLHelpBuilder) + + app.add_config_value('htmlhelp_basename', lambda self: make_filename(self.project), None) diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index ac26e33c9..f82e7ade0 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -11,7 +11,6 @@ import os from os import path -import warnings from six import iteritems from docutils import nodes @@ -19,14 +18,15 @@ from docutils.io import FileOutput from docutils.utils import new_document from docutils.frontend import OptionParser -from sphinx import package_dir, addnodes +from sphinx import package_dir, addnodes, highlighting from sphinx.util import texescape +from sphinx.config import string_classes from sphinx.errors import SphinxError from sphinx.locale import _ from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.util.nodes import inline_all_toctrees -from sphinx.util.osutil import SEP, copyfile +from sphinx.util.osutil import SEP, copyfile, make_filename from sphinx.util.console import bold, darkgreen from sphinx.writers.latex import LaTeXWriter @@ -44,21 +44,6 @@ class LaTeXBuilder(Builder): self.docnames = [] self.document_data = [] texescape.init() - self.check_options() - - def check_options(self): - if self.config.latex_toplevel_sectioning not in (None, 'part', 'chapter', 'section'): - self.warn('invalid latex_toplevel_sectioning, ignored: %s' % - self.config.latex_top_sectionlevel) - self.config.latex_top_sectionlevel = None - - if self.config.latex_use_parts: - warnings.warn('latex_use_parts will be removed at Sphinx-1.5. ' - 'Use latex_toplevel_sectioning instead.', - DeprecationWarning) - - if self.config.latex_toplevel_sectioning: - self.warn('latex_use_parts conflicts with latex_toplevel_sectioning, ignored.') def get_outdated_docs(self): return 'all documents' # for now @@ -92,6 +77,16 @@ class LaTeXBuilder(Builder): docname = docname[:-5] self.titles.append((docname, entry[2])) + def write_stylesheet(self): + highlighter = highlighting.PygmentsBridge( + 'latex', self.config.pygments_style, self.config.trim_doctest_flags) + stylesheet = path.join(self.outdir, 'sphinxhighlight.sty') + with open(stylesheet, 'w') as f: + f.write('\\NeedsTeXFormat{LaTeX2e}[1995/12/01]\n') + f.write('\\ProvidesPackage{sphinxhighlight}' + '[2016/05/29 stylesheet for highlighting with pygments]\n\n') + f.write(highlighter.get_stylesheet()) + def write(self, *ignored): docwriter = LaTeXWriter(self) docsettings = OptionParser( @@ -100,6 +95,7 @@ class LaTeXBuilder(Builder): read_config_files=True).get_default_values() self.init_document_data() + self.write_stylesheet() for entry in self.document_data: docname, targetname, title, author, docclass = entry[:5] @@ -222,3 +218,72 @@ class LaTeXBuilder(Builder): elif not path.isfile(logotarget): copyfile(path.join(self.confdir, self.config.latex_logo), logotarget) self.info('done') + + +def validate_config_values(app): + if app.config.latex_toplevel_sectioning not in (None, 'part', 'chapter', 'section'): + app.warn('invalid latex_toplevel_sectioning, ignored: %s' % + app.config.latex_toplevel_sectioning) + app.config.latex_toplevel_sectioning = None + + if app.config.latex_use_parts: + if app.config.latex_toplevel_sectioning: + app.warn('latex_use_parts conflicts with latex_toplevel_sectioning, ignored.') + else: + app.warn('latex_use_parts is deprecated. Use latex_toplevel_sectioning instead.') + app.config.latex_toplevel_sectioning = 'parts' + + if app.config.latex_use_modindex is not True: # changed by user + app.warn('latex_use_modeindex is deprecated. Use latex_domain_indices instead.') + + if app.config.latex_preamble: + if app.config.latex_elements.get('preamble'): + app.warn("latex_preamble conflicts with latex_elements['preamble'], ignored.") + else: + app.warn("latex_preamble is deprecated. Use latex_elements['preamble'] instead.") + app.config.latex_elements['preamble'] = app.config.latex_preamble + + if app.config.latex_paper_size != 'letter': + if app.config.latex_elements.get('papersize'): + app.warn("latex_paper_size conflicts with latex_elements['papersize'], ignored.") + else: + app.warn("latex_paper_size is deprecated. " + "Use latex_elements['papersize'] instead.") + if app.config.latex_paper_size: + app.config.latex_elements['papersize'] = app.config.latex_paper_size + 'paper' + + if app.config.latex_font_size != '10pt': + if app.config.latex_elements.get('pointsize'): + app.warn("latex_font_size conflicts with latex_elements['pointsize'], ignored.") + else: + app.warn("latex_font_size is deprecated. Use latex_elements['pointsize'] instead.") + app.config.latex_elements['pointsize'] = app.config.latex_font_size + + +def setup(app): + app.add_builder(LaTeXBuilder) + app.connect('builder-inited', validate_config_values) + + app.add_config_value('latex_documents', + lambda self: [(self.master_doc, make_filename(self.project) + '.tex', + self.project, '', 'manual')], + None) + app.add_config_value('latex_logo', None, None, string_classes) + app.add_config_value('latex_appendices', [], None) + app.add_config_value('latex_keep_old_macro_names', True, None) + # now deprecated - use latex_toplevel_sectioning + app.add_config_value('latex_use_parts', False, None) + app.add_config_value('latex_toplevel_sectioning', None, None, [str]) + app.add_config_value('latex_use_modindex', True, None) # deprecated + app.add_config_value('latex_domain_indices', True, None, [list]) + app.add_config_value('latex_show_urls', 'no', None) + app.add_config_value('latex_show_pagerefs', False, None) + # paper_size and font_size are still separate values + # so that you can give them easily on the command line + app.add_config_value('latex_paper_size', 'letter', None) + app.add_config_value('latex_font_size', '10pt', None) + app.add_config_value('latex_elements', {}, None) + app.add_config_value('latex_additional_files', [], None) + app.add_config_value('latex_docclass', {}, None) + # now deprecated - use latex_elements + app.add_config_value('latex_preamble', '', None) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 7c30597a8..4764f6947 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -300,3 +300,13 @@ class CheckExternalLinksBuilder(Builder): def finish(self): for worker in self.workers: self.wqueue.put((None, None, None), False) + + +def setup(app): + app.add_builder(CheckExternalLinksBuilder) + + app.add_config_value('linkcheck_ignore', [], None) + app.add_config_value('linkcheck_retries', 1, None) + app.add_config_value('linkcheck_timeout', None, None, [int]) + app.add_config_value('linkcheck_workers', 5, None) + app.add_config_value('linkcheck_anchors', True, None) diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py index a2e75f9f7..248ed40b2 100644 --- a/sphinx/builders/manpage.py +++ b/sphinx/builders/manpage.py @@ -19,6 +19,7 @@ from sphinx import addnodes from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.util.nodes import inline_all_toctrees +from sphinx.util.osutil import make_filename from sphinx.util.console import bold, darkgreen from sphinx.writers.manpage import ManualPageWriter @@ -88,3 +89,13 @@ class ManualPageBuilder(Builder): def finish(self): pass + + +def setup(app): + app.add_builder(ManualPageBuilder) + + app.add_config_value('man_pages', + lambda self: [(self.master_doc, make_filename(self.project).lower(), + '%s %s' % (self.project, self.release), [], 1)], + None) + app.add_config_value('man_show_urls', False, None) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 4139c92c6..d55ea3cc7 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -21,6 +21,7 @@ from docutils import nodes from sphinx import addnodes from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.util import force_decode +from sphinx.util.osutil import make_filename from sphinx.util.pycompat import htmlescape @@ -111,6 +112,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder): StandaloneHTMLBuilder.init(self) # the output files for HTML help must be .html only self.out_suffix = '.html' + self.link_suffix = '.html' # self.config.html_style = 'traditional.css' def handle_finish(self): @@ -290,3 +292,10 @@ class QtHelpBuilder(StandaloneHTMLBuilder): keywords.extend(self.build_keywords(subitem[0], subitem[1], [])) return keywords + + +def setup(app): + app.setup_extension('sphinx.builders.html') + app.add_builder(QtHelpBuilder) + + app.add_config_value('qthelp_basename', lambda self: make_filename(self.project), None) diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index 8c4bd2419..f070840b6 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -22,7 +22,7 @@ from sphinx.locale import _ from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.util.nodes import inline_all_toctrees -from sphinx.util.osutil import SEP, copyfile +from sphinx.util.osutil import SEP, copyfile, make_filename from sphinx.util.console import bold, darkgreen from sphinx.writers.texinfo import TexinfoWriter @@ -225,3 +225,20 @@ class TexinfoBuilder(Builder): except (IOError, OSError) as err: self.warn("error writing file %s: %s" % (fn, err)) self.info(' done') + + +def setup(app): + app.add_builder(TexinfoBuilder) + + app.add_config_value('texinfo_documents', + lambda self: [(self.master_doc, make_filename(self.project).lower(), + self.project, '', make_filename(self.project), + 'The %s reference manual.' % + make_filename(self.project), + 'Python')], + None) + app.add_config_value('texinfo_appendices', [], None) + app.add_config_value('texinfo_elements', {}, None) + app.add_config_value('texinfo_domain_indices', True, None, [list]) + app.add_config_value('texinfo_show_urls', 'footnote', None) + app.add_config_value('texinfo_no_detailmenu', False, None) diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py index 202ec20db..2daf8b043 100644 --- a/sphinx/builders/text.py +++ b/sphinx/builders/text.py @@ -67,3 +67,10 @@ class TextBuilder(Builder): def finish(self): pass + + +def setup(app): + app.add_builder(TextBuilder) + + app.add_config_value('text_sectionchars', '*=-~"+`', 'env') + app.add_config_value('text_newlines', 'unix', 'env') diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 843b0899e..d8ff5ad8d 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -165,3 +165,7 @@ class WebSupportBuilder(PickleHTMLBuilder): def dump_search_index(self): self.indexer.finish_indexing() + + +def setup(app): + app.add_builder(WebSupportBuilder) diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py index 589e8a63a..e0e33312c 100644 --- a/sphinx/builders/xml.py +++ b/sphinx/builders/xml.py @@ -95,3 +95,10 @@ class PseudoXMLBuilder(XMLBuilder): out_suffix = '.pseudoxml' _writer_class = PseudoXMLWriter + + +def setup(app): + app.add_builder(XMLBuilder) + app.add_builder(PseudoXMLBuilder) + + app.add_config_value('xml_pretty', True, 'env') diff --git a/sphinx/config.py b/sphinx/config.py index ef57334fe..13921aac8 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -10,14 +10,13 @@ """ import re -from os import path, environ, getenv -import shlex +from os import path, getenv from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types from sphinx.errors import ConfigError from sphinx.locale import l_ -from sphinx.util.osutil import make_filename, cd +from sphinx.util.osutil import cd from sphinx.util.pycompat import execfile_, NoneType from sphinx.util.i18n import format_date @@ -29,10 +28,25 @@ if PY3: CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?" CONFIG_EXIT_ERROR = "The configuration file (or one of the modules it imports) " \ "called sys.exit()" +CONFIG_ENUM_WARNING = "The config value `{name}` has to be a one of {candidates}, " \ + "but `{current}` is given." CONFIG_TYPE_WARNING = "The config value `{name}' has type `{current.__name__}', " \ "defaults to `{default.__name__}.'" +class ENUM: + """represents the config value should be a one of candidates. + + Example: + app.add_config_value('latex_show_urls', 'no', ENUM('no', 'footnote', 'inline')) + """ + def __init__(self, *candidates): + self.candidates = candidates + + def match(self, value): + return value in self.candidates + + string_classes = [text_type] if PY2: string_classes.append(binary_type) # => [str, unicode] @@ -94,181 +108,6 @@ class Config(object): 'table': l_('Table %s'), 'code-block': l_('Listing %s')}, 'env'), - - # HTML options - html_theme = ('alabaster', 'html'), - html_theme_path = ([], 'html'), - html_theme_options = ({}, 'html'), - html_title = (lambda self: l_('%s %s documentation') % - (self.project, self.release), - 'html', string_classes), - html_short_title = (lambda self: self.html_title, 'html'), - html_style = (None, 'html', string_classes), - html_logo = (None, 'html', string_classes), - html_favicon = (None, 'html', string_classes), - html_static_path = ([], 'html'), - html_extra_path = ([], 'html'), - # the real default is locale-dependent - html_last_updated_fmt = (None, 'html', string_classes), - html_use_smartypants = (True, 'html'), - html_translator_class = (None, 'html', string_classes), - html_sidebars = ({}, 'html'), - html_additional_pages = ({}, 'html'), - html_use_modindex = (True, 'html'), # deprecated - html_domain_indices = (True, 'html', [list]), - html_add_permalinks = (u'\u00B6', 'html'), - html_use_index = (True, 'html'), - html_split_index = (False, 'html'), - html_copy_source = (True, 'html'), - html_show_sourcelink = (True, 'html'), - html_use_opensearch = ('', 'html'), - html_file_suffix = (None, 'html', string_classes), - html_link_suffix = (None, 'html', string_classes), - html_show_copyright = (True, 'html'), - html_show_sphinx = (True, 'html'), - html_context = ({}, 'html'), - html_output_encoding = ('utf-8', 'html'), - html_compact_lists = (True, 'html'), - html_secnumber_suffix = ('. ', 'html'), - html_search_language = (None, 'html', string_classes), - html_search_options = ({}, 'html'), - html_search_scorer = ('', None), - html_scaled_image_link = (True, 'html'), - - # HTML help only options - htmlhelp_basename = (lambda self: make_filename(self.project), None), - - # Qt help only options - qthelp_basename = (lambda self: make_filename(self.project), None), - - # Devhelp only options - devhelp_basename = (lambda self: make_filename(self.project), None), - - # Apple help options - applehelp_bundle_name = (lambda self: make_filename(self.project), - 'applehelp'), - applehelp_bundle_id = (None, 'applehelp', string_classes), - applehelp_dev_region = ('en-us', 'applehelp'), - applehelp_bundle_version = ('1', 'applehelp'), - applehelp_icon = (None, 'applehelp', string_classes), - applehelp_kb_product = (lambda self: '%s-%s' % - (make_filename(self.project), self.release), - 'applehelp'), - applehelp_kb_url = (None, 'applehelp', string_classes), - applehelp_remote_url = (None, 'applehelp', string_classes), - applehelp_index_anchors = (False, 'applehelp', string_classes), - applehelp_min_term_length = (None, 'applehelp', string_classes), - applehelp_stopwords = (lambda self: self.language or 'en', 'applehelp'), - applehelp_locale = (lambda self: self.language or 'en', 'applehelp'), - applehelp_title = (lambda self: self.project + ' Help', 'applehelp'), - applehelp_codesign_identity = (lambda self: - environ.get('CODE_SIGN_IDENTITY', None), - 'applehelp'), - applehelp_codesign_flags = (lambda self: - shlex.split( - environ.get('OTHER_CODE_SIGN_FLAGS', - '')), - 'applehelp'), - applehelp_indexer_path = ('/usr/bin/hiutil', 'applehelp'), - applehelp_codesign_path = ('/usr/bin/codesign', 'applehelp'), - applehelp_disable_external_tools = (False, None), - - # Epub options - epub_basename = (lambda self: make_filename(self.project), None), - epub_theme = ('epub', 'html'), - epub_theme_options = ({}, 'html'), - epub_title = (lambda self: self.html_title, 'html'), - epub3_description = ('', 'epub3', string_classes), - epub_author = ('unknown', 'html'), - epub3_contributor = ('unknown', 'epub3', string_classes), - epub_language = (lambda self: self.language or 'en', 'html'), - epub_publisher = ('unknown', 'html'), - epub_copyright = (lambda self: self.copyright, 'html'), - epub_identifier = ('unknown', 'html'), - epub_scheme = ('unknown', 'html'), - epub_uid = ('unknown', 'env'), - epub_cover = ((), 'env'), - epub_guide = ((), 'env'), - epub_pre_files = ([], 'env'), - epub_post_files = ([], 'env'), - epub_exclude_files = ([], 'env'), - epub_tocdepth = (3, 'env'), - epub_tocdup = (True, 'env'), - epub_tocscope = ('default', 'env'), - epub_fix_images = (False, 'env'), - epub_max_image_width = (0, 'env'), - epub_show_urls = ('inline', 'html'), - epub_use_index = (lambda self: self.html_use_index, 'html'), - epub3_page_progression_direction = ('ltr', 'epub3', string_classes), - - # LaTeX options - latex_documents = (lambda self: [(self.master_doc, - make_filename(self.project) + '.tex', - self.project, - '', 'manual')], - None), - latex_logo = (None, None, string_classes), - latex_appendices = ([], None), - # now deprecated - use latex_toplevel_sectioning - latex_use_parts = (False, None), - latex_toplevel_sectioning = (None, None, [str]), - latex_use_modindex = (True, None), # deprecated - latex_domain_indices = (True, None, [list]), - latex_show_urls = ('no', None), - latex_show_pagerefs = (False, None), - # paper_size and font_size are still separate values - # so that you can give them easily on the command line - latex_paper_size = ('letter', None), - latex_font_size = ('10pt', None), - latex_elements = ({}, None), - latex_additional_files = ([], None), - latex_docclass = ({}, None), - # now deprecated - use latex_elements - latex_preamble = ('', None), - - # text options - text_sectionchars = ('*=-~"+`', 'env'), - text_newlines = ('unix', 'env'), - - # manpage options - man_pages = (lambda self: [(self.master_doc, - make_filename(self.project).lower(), - '%s %s' % (self.project, self.release), - [], 1)], - None), - man_show_urls = (False, None), - - # Texinfo options - texinfo_documents = (lambda self: [(self.master_doc, - make_filename(self.project).lower(), - self.project, '', - make_filename(self.project), - 'The %s reference manual.' % - make_filename(self.project), - 'Python')], - None), - texinfo_appendices = ([], None), - texinfo_elements = ({}, None), - texinfo_domain_indices = (True, None, [list]), - texinfo_show_urls = ('footnote', None), - texinfo_no_detailmenu = (False, None), - - # linkcheck options - linkcheck_ignore = ([], None), - linkcheck_retries = (1, None), - linkcheck_timeout = (None, None, [int]), - linkcheck_workers = (5, None), - linkcheck_anchors = (True, None), - - # gettext options - gettext_compact = (True, 'gettext'), - gettext_location = (True, 'gettext'), - gettext_uuid = (False, 'gettext'), - gettext_auto_build = (True, 'env'), - gettext_additional_targets = ([], 'env'), - - # XML options - xml_pretty = (True, 'env'), ) def __init__(self, dirname, filename, overrides, tags): @@ -326,19 +165,24 @@ class Config(object): if default is None and not permitted: continue # neither inferrable nor expliclitly permitted types current = self[name] - if type(current) is type(default): - continue - if type(current) in permitted: - continue + if isinstance(permitted, ENUM): + if not permitted.match(current): + warn(CONFIG_ENUM_WARNING.format( + name=name, current=current, candidates=permitted.candidates)) + else: + if type(current) is type(default): + continue + if type(current) in permitted: + continue - common_bases = (set(type(current).__bases__ + (type(current),)) & - set(type(default).__bases__)) - common_bases.discard(object) - if common_bases: - continue # at least we share a non-trivial base class + common_bases = (set(type(current).__bases__ + (type(current),)) & + set(type(default).__bases__)) + common_bases.discard(object) + if common_bases: + continue # at least we share a non-trivial base class - warn(CONFIG_TYPE_WARNING.format( - name=name, current=type(current), default=type(default))) + warn(CONFIG_TYPE_WARNING.format( + name=name, current=type(current), default=type(default))) def check_unicode(self, warn): # check all string values for non-ASCII characters in bytestrings, diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 51294570c..bab2f9f54 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -405,6 +405,7 @@ class Include(BaseInclude): return BaseInclude.run(self) rel_filename, filename = env.relfn2path(self.arguments[0]) self.arguments[0] = filename + env.note_included(filename) return BaseInclude.run(self) diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index e4d397efe..c67abc207 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -273,20 +273,3 @@ class Domain(object): if primary: return type.lname return _('%s %s') % (self.label, type.lname) - - -from sphinx.domains.c import CDomain # noqa -from sphinx.domains.cpp import CPPDomain # noqa -from sphinx.domains.std import StandardDomain # noqa -from sphinx.domains.python import PythonDomain # noqa -from sphinx.domains.javascript import JavaScriptDomain # noqa -from sphinx.domains.rst import ReSTDomain # noqa - -BUILTIN_DOMAINS = { - 'std': StandardDomain, - 'py': PythonDomain, - 'c': CDomain, - 'cpp': CPPDomain, - 'js': JavaScriptDomain, - 'rst': ReSTDomain, -} diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index c7fd0681e..43e869dbc 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -279,6 +279,9 @@ class CDomain(Domain): typ, target, node, contnode): # strip pointer asterisk target = target.rstrip(' *') + # becase TypedField can generate xrefs + if target in CObject.stopwords: + return contnode if target not in self.data['objects']: return None obj = self.data['objects'][target] @@ -299,3 +302,7 @@ class CDomain(Domain): def get_objects(self): for refname, (docname, type) in list(self.data['objects'].items()): yield (refname, refname, type, docname, 'c.' + refname, 1) + + +def setup(app): + app.add_domain(CDomain) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 7d41d6570..9a3d10a3f 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1977,7 +1977,7 @@ class ASTTypeWithInit(ASTBase): def get_id_v2(self, objectType=None, symbol=None): if objectType == 'member': - return symbol.declaration.name.get_id_v2() + return symbol.get_full_nested_name().get_id_v2() else: return self.type.get_id_v2() @@ -2660,6 +2660,10 @@ class Symbol(object): if symbol is None: # TODO: maybe search without template args return None + # We have now matched part of a nested name, and need to match more + # so even if we should matchSelf before, we definitely shouldn't + # even more. (see also issue #2666) + matchSelf = False parentSymbol = symbol assert False # should have returned in the loop @@ -3007,10 +3011,9 @@ class DefinitionParser(object): self.fail('Expected ")" after "..." in ' 'parameters_and_qualifiers.') break - if paramMode == 'function': - arg = self._parse_type_with_init(outer=None, named='single') - else: - arg = self._parse_type(named=False) + # note: it seems that function arguments can always sbe named, + # even in function pointers and similar. + arg = self._parse_type_with_init(outer=None, named='single') # TODO: parse default parameters # TODO: didn't we just do that? args.append(ASTFunctinoParameter(arg)) @@ -3734,7 +3737,7 @@ class CPPObject(ObjectDescription): id_v1 = None id_v2 = ast.get_id_v2() # store them in reverse order, so the newest is first - ids = [id_v2, id_v1] + ids = [id_v2, id_v1] newestId = ids[0] assert newestId # shouldn't be None @@ -3755,8 +3758,14 @@ class CPPObject(ObjectDescription): else: # print("[CPP] non-unique name:", name) pass - for id in ids: - if id: # is None when the element didn't exist in that version + # always add the newest id + assert newestId + signode['ids'].append(newestId) + # only add compatibility ids when there are no conflicts + for id in ids[1:]: + if not id: # is None when the element didn't exist in that version + continue + if id not in self.state.document.ids: signode['ids'].append(id) signode['first'] = (not self.names) # hmm, what is this abound? self.state.document.note_explicit_target(signode) @@ -3800,6 +3809,15 @@ class CPPObject(ObjectDescription): self.describe_signature(signode, ast) return ast + def before_content(self): + lastSymbol = self.env.ref_context['cpp:last_symbol'] + assert lastSymbol + self.oldParentSymbol = self.env.ref_context['cpp:parent_symbol'] + self.env.ref_context['cpp:parent_symbol'] = lastSymbol + + def after_content(self): + self.env.ref_context['cpp:parent_symbol'] = self.oldParentSymbol + class CPPTypeObject(CPPObject): def get_index_text(self, name): @@ -3838,15 +3856,6 @@ class CPPClassObject(CPPObject): def get_index_text(self, name): return _('%s (C++ class)') % name - def before_content(self): - lastSymbol = self.env.ref_context['cpp:last_symbol'] - assert lastSymbol - self.oldParentSymbol = self.env.ref_context['cpp:parent_symbol'] - self.env.ref_context['cpp:parent_symbol'] = lastSymbol - - def after_content(self): - self.env.ref_context['cpp:parent_symbol'] = self.oldParentSymbol - def parse_definition(self, parser): return parser.parse_declaration("class") @@ -3858,15 +3867,6 @@ class CPPEnumObject(CPPObject): def get_index_text(self, name): return _('%s (C++ enum)') % name - def before_content(self): - lastSymbol = self.env.ref_context['cpp:last_symbol'] - assert lastSymbol - self.oldParentSymbol = self.env.ref_context['cpp:parent_symbol'] - self.env.ref_context['cpp:parent_symbol'] = lastSymbol - - def after_content(self): - self.env.ref_context['cpp:parent_symbol'] = self.oldParentSymbol - def parse_definition(self, parser): ast = parser.parse_declaration("enum") # self.objtype is set by ObjectDescription in run() @@ -4123,6 +4123,29 @@ class CPPDomain(Domain): matchSelf=True) if s is None or s.declaration is None: return None, None + + if typ.startswith('cpp:'): + typ = typ[4:] + if typ == 'func': + typ = 'function' + declTyp = s.declaration.objectType + + def checkType(): + if typ == 'any': + return True + if declTyp == 'templateParam': + return True + if typ == 'var' or typ == 'member': + return declTyp in ['var', 'member'] + if typ in ['enum', 'enumerator', 'function', 'class']: + return declTyp == typ + if typ == 'type': + return declTyp in ['enum', 'class', 'function', 'type'] + print("Type is %s" % typ) + assert False + if not checkType(): + warner.warn("cpp:%s targets a %s." % (typ, s.declaration.objectType)) + declaration = s.declaration fullNestedName = s.get_full_nested_name() name = text_type(fullNestedName).lstrip(':') @@ -4162,3 +4185,7 @@ class CPPDomain(Domain): docname = symbol.docname newestId = symbol.declaration.get_newest_id() yield (name, name, objectType, docname, newestId, 1) + + +def setup(app): + app.add_domain(CPPDomain) diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index b5f64022a..ade6e4224 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -234,3 +234,7 @@ class JavaScriptDomain(Domain): for refname, (docname, type) in list(self.data['objects'].items()): yield refname, refname, type, docname, \ refname.replace('$', '_S_'), 1 + + +def setup(app): + app.add_domain(JavaScriptDomain) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 1639d8288..9a9bcef16 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -771,3 +771,7 @@ class PythonDomain(Domain): for refname, (docname, type) in iteritems(self.data['objects']): if type != 'module': # modules are already handled yield (refname, refname, type, docname, refname, 1) + + +def setup(app): + app.add_domain(PythonDomain) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index b11c9450b..526ae18a7 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -156,3 +156,7 @@ class ReSTDomain(Domain): def get_objects(self): for (typ, name), docname in iteritems(self.data['objects']): yield name, name, typ, docname, typ + '-' + name, 1 + + +def setup(app): + app.add_domain(ReSTDomain) diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 997d95ba3..001d9f6d9 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -14,7 +14,6 @@ import unicodedata from six import iteritems from docutils import nodes -from docutils.nodes import fully_normalize_name from docutils.parsers.rst import directives from docutils.statemachine import ViewList @@ -585,106 +584,119 @@ class StandardDomain(Domain): newnode.append(innernode) return newnode - def resolve_xref(self, env, fromdocname, builder, - typ, target, node, contnode): + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if typ == 'ref': - if node['refexplicit']: - # reference to anonymous label; the reference uses - # the supplied link caption - docname, labelid = self.data['anonlabels'].get(target, ('', '')) - sectname = node.astext() - else: - # reference to named label; the final node will - # contain the section name after the label - docname, labelid, sectname = self.data['labels'].get(target, - ('', '', '')) - if not docname: - return None - - return self.build_reference_node(fromdocname, builder, - docname, labelid, sectname, 'ref') + resolver = self._resolve_ref_xref elif typ == 'numref': + resolver = self._resolve_numref_xref + elif typ == 'keyword': + resolver = self._resolve_keyword_xref + elif typ == 'option': + resolver = self._resolve_option_xref + else: + resolver = self._resolve_obj_xref + + return resolver(env, fromdocname, builder, typ, target, node, contnode) + + def _resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode): + if node['refexplicit']: + # reference to anonymous label; the reference uses + # the supplied link caption docname, labelid = self.data['anonlabels'].get(target, ('', '')) - if not docname: - return None + sectname = node.astext() + else: + # reference to named label; the final node will + # contain the section name after the label + docname, labelid, sectname = self.data['labels'].get(target, + ('', '', '')) + if not docname: + return None - if env.config.numfig is False: - env.warn(fromdocname, 'numfig is disabled. :numref: is ignored.', - lineno=node.line) - return contnode + return self.build_reference_node(fromdocname, builder, + docname, labelid, sectname, 'ref') - target_node = env.get_doctree(docname).ids.get(labelid) - figtype = self.get_figtype(target_node) - if figtype is None: - return None + def _resolve_numref_xref(self, env, fromdocname, builder, typ, target, node, contnode): + docname, labelid = self.data['anonlabels'].get(target, ('', '')) + if not docname: + return None - try: - figure_id = target_node['ids'][0] - fignumber = env.toc_fignumbers[docname][figtype][figure_id] - except (KeyError, IndexError): - # target_node is found, but fignumber is not assigned. - # Maybe it is defined in orphaned document. - env.warn(fromdocname, "no number is assigned for %s: %s" % (figtype, labelid), - lineno=node.line) - return contnode + if env.config.numfig is False: + env.warn_node('numfig is disabled. :numref: is ignored.', node) + return contnode - title = contnode.astext() - if target == fully_normalize_name(title): + target_node = env.get_doctree(docname).ids.get(labelid) + figtype = self.get_figtype(target_node) + if figtype is None: + return None + + try: + figure_id = target_node['ids'][0] + fignumber = env.toc_fignumbers[docname][figtype][figure_id] + except (KeyError, IndexError): + # target_node is found, but fignumber is not assigned. + # Maybe it is defined in orphaned document. + env.warn_node("no number is assigned for %s: %s" % (figtype, labelid), node) + return contnode + + try: + if node['refexplicit']: + title = contnode.astext() + else: title = env.config.numfig_format.get(figtype, '') - try: - newtitle = title % '.'.join(map(str, fignumber)) - except TypeError: - env.warn(fromdocname, 'invalid numfig_format: %s' % title, - lineno=node.line) - return None + newtitle = title % '.'.join(map(str, fignumber)) + except TypeError: + env.warn_node('invalid numfig_format: %s' % title, node) + return None - return self.build_reference_node(fromdocname, builder, - docname, labelid, newtitle, 'numref', - nodeclass=addnodes.number_reference, - title=title) - elif typ == 'keyword': - # keywords are oddballs: they are referenced by named labels - docname, labelid, _ = self.data['labels'].get(target, ('', '', '')) - if not docname: - return None - return make_refnode(builder, fromdocname, docname, - labelid, contnode) - elif typ == 'option': - progname = node.get('std:program') - target = target.strip() - docname, labelid = self.data['progoptions'].get((progname, target), ('', '')) - if not docname: - commands = [] - while ws_re.search(target): - subcommand, target = ws_re.split(target, 1) - commands.append(subcommand) - progname = "-".join(commands) + return self.build_reference_node(fromdocname, builder, + docname, labelid, newtitle, 'numref', + nodeclass=addnodes.number_reference, + title=title) - docname, labelid = self.data['progoptions'].get((progname, target), - ('', '')) - if docname: - break - else: - return None + def _resolve_keyword_xref(self, env, fromdocname, builder, typ, target, node, contnode): + # keywords are oddballs: they are referenced by named labels + docname, labelid, _ = self.data['labels'].get(target, ('', '', '')) + if not docname: + return None + return make_refnode(builder, fromdocname, docname, + labelid, contnode) - return make_refnode(builder, fromdocname, docname, - labelid, contnode) - else: - objtypes = self.objtypes_for_role(typ) or [] - for objtype in objtypes: - if (objtype, target) in self.data['objects']: - docname, labelid = self.data['objects'][objtype, target] + def _resolve_option_xref(self, env, fromdocname, builder, typ, target, node, contnode): + progname = node.get('std:program') + target = target.strip() + docname, labelid = self.data['progoptions'].get((progname, target), ('', '')) + if not docname: + commands = [] + while ws_re.search(target): + subcommand, target = ws_re.split(target, 1) + commands.append(subcommand) + progname = "-".join(commands) + + docname, labelid = self.data['progoptions'].get((progname, target), + ('', '')) + if docname: break else: - docname, labelid = '', '' - if not docname: return None - return make_refnode(builder, fromdocname, docname, - labelid, contnode) - def resolve_any_xref(self, env, fromdocname, builder, target, - node, contnode): + return make_refnode(builder, fromdocname, docname, + labelid, contnode) + + def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode): + objtypes = self.objtypes_for_role(typ) or [] + for objtype in objtypes: + if (objtype, target) in self.data['objects']: + docname, labelid = self.data['objects'][objtype, target] + break + else: + docname, labelid = '', '' + if not docname: + return None + return make_refnode(builder, fromdocname, docname, + labelid, contnode) + + def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): results = [] ltarget = target.lower() # :ref: lowercases its target automatically for role in ('ref', 'option'): # do not try "keyword" @@ -755,3 +767,7 @@ class StandardDomain(Domain): else: figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None)) return figtype + + +def setup(app): + app.add_domain(StandardDomain) diff --git a/sphinx/environment.py b/sphinx/environment.py index 92064f911..e31ebea47 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -17,6 +17,7 @@ import types import bisect import codecs import string +import fnmatch import unicodedata from os import path from glob import glob @@ -75,7 +76,7 @@ default_settings = { # or changed to properly invalidate pickle files. # # NOTE: increase base version by 2 to have distinct numbers for Py2 and 3 -ENV_VERSION = 48 + (sys.version_info[0] - 2) +ENV_VERSION = 49 + (sys.version_info[0] - 2) dummy_reporter = Reporter('', 4, 4) @@ -169,6 +170,7 @@ class BuildEnvironment: # contains all read docnames self.dependencies = {} # docname -> set of dependent file # names, relative to documentation root + self.included = set() # docnames included from other documents self.reread_always = set() # docnames to re-read unconditionally on # next build @@ -328,6 +330,20 @@ class BuildEnvironment: domain.merge_domaindata(docnames, other.domaindata[domainname]) app.emit('env-merge-info', self, docnames, other) + def path2doc(self, filename): + """Return the docname for the filename if the file is document. + + *filename* should be absolute or relative to the source directory. + """ + if filename.startswith(self.srcdir): + filename = filename[len(self.srcdir) + 1:] + for suffix in self.config.source_suffix: + if fnmatch.fnmatch(filename, '*' + suffix): + return filename[:-len(suffix)] + else: + # the file does not have docname + return None + def doc2path(self, docname, base=True, suffix=None): """Return the filename for the document name. @@ -387,8 +403,13 @@ class BuildEnvironment: config.html_extra_path + ['**/_sources', '.#*', '**/.#*', '*.lproj/**'] ) - self.found_docs = set(get_matching_docs( - self.srcdir, config.source_suffix, exclude_matchers=matchers)) + self.found_docs = set() + for docname in get_matching_docs(self.srcdir, config.source_suffix, + exclude_matchers=matchers): + if os.access(self.doc2path(docname), os.R_OK): + self.found_docs.add(docname) + else: + self.warn(docname, "document not readable. Ignored.") # add catalog mo file dependency for docname in self.found_docs: @@ -820,6 +841,15 @@ class BuildEnvironment: """ self.dependencies.setdefault(self.docname, set()).add(filename) + def note_included(self, filename): + """Add *filename* as a included from other document. + + This means the document is not orphaned. + + *filename* should be absolute or relative to the source directory. + """ + self.included.add(self.path2doc(filename)) + def note_reread(self): """Add the current document to the list of documents that will automatically be re-read at the next build. @@ -1413,7 +1443,10 @@ class BuildEnvironment: # nodes with length 1 don't have any children anyway if len(toplevel) > 1: subtrees = toplevel.traverse(addnodes.toctree) - toplevel[1][:] = subtrees + if subtrees: + toplevel[1][:] = subtrees + else: + toplevel.pop(1) # resolve all sub-toctrees for subtocnode in toc.traverse(addnodes.toctree): if not (subtocnode.get('hidden', False) and @@ -1459,6 +1492,9 @@ class BuildEnvironment: _toctree_add_classes(newnode, 1) self._toctree_prune(newnode, 1, prune and maxdepth or 0, collapse) + if len(newnode[-1]) == 0: # No titles found + return None + # set the target paths in the toctrees (they are not known at TOC # generation time) for refnode in newnode.traverse(nodes.reference): @@ -1488,9 +1524,9 @@ class BuildEnvironment: typ, target, node, contnode) # really hardwired reference types elif typ == 'any': - newnode = self._resolve_any_reference(builder, node, contnode) + newnode = self._resolve_any_reference(builder, refdoc, node, contnode) elif typ == 'doc': - newnode = self._resolve_doc_reference(builder, node, contnode) + newnode = self._resolve_doc_reference(builder, refdoc, node, contnode) elif typ == 'citation': newnode = self._resolve_citation(builder, refdoc, node, contnode) # no new node found? try the missing-reference event @@ -1538,10 +1574,10 @@ class BuildEnvironment: msg = '%r reference target not found: %%(target)s' % typ self.warn_node(msg % {'target': target}, node, type='ref', subtype=typ) - def _resolve_doc_reference(self, builder, node, contnode): + def _resolve_doc_reference(self, builder, refdoc, node, contnode): # directly reference to document by source name; # can be absolute or relative - docname = docname_join(node['refdoc'], node['reftarget']) + docname = docname_join(refdoc, node['reftarget']) if docname in self.all_docs: if node['refexplicit']: # reference with explicit title @@ -1551,7 +1587,7 @@ class BuildEnvironment: innernode = nodes.inline(caption, caption) innernode['classes'].append('doc') newnode = nodes.reference('', '', internal=True) - newnode['refuri'] = builder.get_relative_uri(node['refdoc'], docname) + newnode['refuri'] = builder.get_relative_uri(refdoc, docname) newnode.append(innernode) return newnode @@ -1574,13 +1610,12 @@ class BuildEnvironment: # transforms.CitationReference.apply. del node['ids'][:] - def _resolve_any_reference(self, builder, node, contnode): + def _resolve_any_reference(self, builder, refdoc, node, contnode): """Resolve reference generated by the "any" role.""" - refdoc = node['refdoc'] target = node['reftarget'] results = [] # first, try resolving as :doc: - doc_ref = self._resolve_doc_reference(builder, node, contnode) + doc_ref = self._resolve_doc_reference(builder, refdoc, node, contnode) if doc_ref: results.append(('doc', doc_ref)) # next, do the standard domain (makes this a priority) @@ -1931,6 +1966,9 @@ class BuildEnvironment: if docname == self.config.master_doc: # the master file is not included anywhere ;) continue + if docname in self.included: + # the document is included from other documents + continue if 'orphan' in self.metadata[docname]: continue self.warn(docname, 'document isn\'t included in any toctree') diff --git a/sphinx/errors.py b/sphinx/errors.py index 8d695c190..5fb77a135 100644 --- a/sphinx/errors.py +++ b/sphinx/errors.py @@ -67,7 +67,10 @@ class PycodeError(Exception): return res -class SphinxParallelError(Exception): +class SphinxParallelError(SphinxError): + + category = 'Sphinx parallel build error' + def __init__(self, orig_exc, traceback): self.orig_exc = orig_exc self.traceback = traceback diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index f8e402be3..927429086 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -22,6 +22,7 @@ from six import text_type from docutils import nodes import sphinx +from sphinx.locale import _ from sphinx.errors import SphinxError, ExtensionError from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.osutil import ensuredir, ENOENT, cd @@ -253,7 +254,9 @@ def html_visit_displaymath(self, node): self.body.append(self.starttag(node, 'div', CLASS='math')) self.body.append('

    ') if node['number']: - self.body.append('(%s)' % node['number']) + self.body.append('(%s)' % node['number']) + self.add_permalink_ref(node, _('Permalink to this code')) + self.body.append('') if fname is None: # something failed -- use text-only as a bad substitute self.body.append('%s

    \n' % diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 8e6573e8a..c43b8ae6a 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -34,7 +34,8 @@ from os import path import re from six import iteritems, string_types -from six.moves.urllib import parse, request +from six.moves.urllib import request +from six.moves.urllib.parse import urlsplit, urlunsplit from docutils import nodes from docutils.utils import relative_path @@ -105,7 +106,7 @@ def read_inventory_v2(f, uri, join, bufsize=16*1024): for line in split_lines(read_chunks()): # be careful to handle names with embedded spaces correctly - m = re.match(r'(?x)(.+?)\s+(\S*:\S*)\s+(\S+)\s+(\S+)\s+(.*)', + m = re.match(r'(?x)(.+?)\s+(\S*:\S*)\s+(-?\d+)\s+(\S+)\s+(.*)', line.rstrip()) if not m: continue @@ -145,7 +146,7 @@ def _strip_basic_auth(url): :rtype: ``tuple`` """ - url_parts = parse.urlsplit(url) + url_parts = urlsplit(url) username = url_parts.username password = url_parts.password frags = list(url_parts) @@ -154,7 +155,7 @@ def _strip_basic_auth(url): frags[1] = "%s:%s" % (url_parts.hostname, url_parts.port) else: frags[1] = url_parts.hostname - url = parse.urlunsplit(frags) + url = urlunsplit(frags) return (url, username, password) @@ -208,12 +209,12 @@ def _get_safe_url(url): url, username, _ = _strip_basic_auth(url) if username is not None: # case: url contained basic auth creds; obscure password - url_parts = parse.urlsplit(url) + url_parts = urlsplit(url) safe_netloc = '{0}@{1}'.format(username, url_parts.hostname) # replace original netloc w/ obscured version frags = list(url_parts) frags[1] = safe_netloc - safe_url = parse.urlunsplit(frags) + safe_url = urlunsplit(frags) return safe_url @@ -237,6 +238,13 @@ def fetch_inventory(app, uri, inv): '%s: %s' % (inv, err.__class__, err)) return try: + if hasattr(f, 'geturl'): + newuri = f.geturl() + if newuri.endswith("/" + INVENTORY_FILENAME): + newuri = newuri[:-len(INVENTORY_FILENAME) - 1] + if uri != newuri and uri != newuri + "/": + app.info('intersphinx inventory has moved: %s -> %s' % (uri, newuri)) + uri = newuri line = f.readline().rstrip().decode('utf-8') try: if line == '# Sphinx inventory version 1': diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py index f36e12fed..9fb790e1b 100644 --- a/sphinx/ext/jsmath.py +++ b/sphinx/ext/jsmath.py @@ -13,6 +13,7 @@ from docutils import nodes import sphinx +from sphinx.locale import _ from sphinx.application import ExtensionError from sphinx.ext.mathbase import setup_math as mathbase_setup @@ -34,8 +35,9 @@ def html_visit_displaymath(self, node): if i == 0: # necessary to e.g. set the id property correctly if node['number']: - self.body.append('(%s)' % - node['number']) + self.body.append('(%s)' % node['number']) + self.add_permalink_ref(node, _('Permalink to this code')) + self.body.append('') self.body.append(self.starttag(node, 'div', CLASS='math')) else: # but only once! diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index 13a560c0a..ddcec492c 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -58,7 +58,7 @@ def wrap_displaymath(math, label, numbering): begin = r'\begin{align*}%s\!\begin{aligned}' % labeldef end = r'\end{aligned}\end{align*}' for part in parts: - equations.append('%s\\\\\n' % part) + equations.append('%s\\\\\n' % part.strip()) return '%s\n%s%s' % (begin, ''.join(equations), end) diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index bdaab55e0..420e2b312 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -14,6 +14,7 @@ from docutils import nodes import sphinx +from sphinx.locale import _ from sphinx.errors import ExtensionError from sphinx.ext.mathbase import setup_math as mathbase_setup @@ -35,7 +36,9 @@ def html_visit_displaymath(self, node): # necessary to e.g. set the id property correctly if node['number']: - self.body.append('(%s)' % node['number']) + self.body.append('(%s)' % node['number']) + self.add_permalink_ref(node, _('Permalink to this code')) + self.body.append('') self.body.append(self.builder.config.mathjax_display[0]) parts = [prt for prt in node['latex'].split('\n\n') if prt.strip()] if len(parts) > 1: # Add alignment if there are more than 1 equation diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index 9dedc15d7..651355c57 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -51,16 +51,17 @@ class Config(object): Attributes ---------- - napoleon_google_docstring : bool, defaults to True + napoleon_google_docstring : :obj:`bool` (Defaults to True) True to parse `Google style`_ docstrings. False to disable support for Google style docstrings. - napoleon_numpy_docstring : bool, defaults to True + napoleon_numpy_docstring : :obj:`bool` (Defaults to True) True to parse `NumPy style`_ docstrings. False to disable support for NumPy style docstrings. - napoleon_include_init_with_doc : bool, defaults to False - True to include init methods (i.e. ``__init___``) with - docstrings in the documentation. False to fall back to Sphinx's - default behavior. + napoleon_include_init_with_doc : :obj:`bool` (Defaults to False) + True to list ``__init___`` docstrings separately from the class + docstring. False to fall back to Sphinx's default behavior, which + considers the ``__init___`` docstring as part of the class + documentation. **If True**:: @@ -72,7 +73,7 @@ class Config(object): def __init__(self): # This will NOT be included in the docs - napoleon_include_private_with_doc : bool, defaults to False + napoleon_include_private_with_doc : :obj:`bool` (Defaults to False) True to include private members (like ``_membername``) with docstrings in the documentation. False to fall back to Sphinx's default behavior. @@ -88,7 +89,7 @@ class Config(object): # This will NOT be included in the docs pass - napoleon_include_special_with_doc : bool, defaults to False + napoleon_include_special_with_doc : :obj:`bool` (Defaults to False) True to include special members (like ``__membername__``) with docstrings in the documentation. False to fall back to Sphinx's default behavior. @@ -105,7 +106,7 @@ class Config(object): # This will NOT be included in the docs return unicode(self.__class__.__name__) - napoleon_use_admonition_for_examples : bool, defaults to False + napoleon_use_admonition_for_examples : :obj:`bool` (Defaults to False) True to use the ``.. admonition::`` directive for the **Example** and **Examples** sections. False to use the ``.. rubric::`` directive instead. One may look better than the other depending on what HTML @@ -129,7 +130,7 @@ class Config(object): This is just a quick example - napoleon_use_admonition_for_notes : bool, defaults to False + napoleon_use_admonition_for_notes : :obj:`bool` (Defaults to False) True to use the ``.. admonition::`` directive for **Notes** sections. False to use the ``.. rubric::`` directive instead. @@ -142,7 +143,7 @@ class Config(object): -------- :attr:`napoleon_use_admonition_for_examples` - napoleon_use_admonition_for_references : bool, defaults to False + napoleon_use_admonition_for_references : :obj:`bool` (Defaults to False) True to use the ``.. admonition::`` directive for **References** sections. False to use the ``.. rubric::`` directive instead. @@ -150,7 +151,7 @@ class Config(object): -------- :attr:`napoleon_use_admonition_for_examples` - napoleon_use_ivar : bool, defaults to False + napoleon_use_ivar : :obj:`bool` (Defaults to False) True to use the ``:ivar:`` role for instance variables. False to use the ``.. attribute::`` directive instead. @@ -174,7 +175,7 @@ class Config(object): Description of `attr1` - napoleon_use_param : bool, defaults to True + napoleon_use_param : :obj:`bool` (Defaults to True) True to use a ``:param:`` role for each function parameter. False to use a single ``:parameters:`` role for all the parameters. @@ -201,21 +202,22 @@ class Config(object): * **arg2** (*int, optional*) -- Description of `arg2`, defaults to 0 - napoleon_use_keyword : bool, defaults to True + napoleon_use_keyword : :obj:`bool` (Defaults to True) True to use a ``:keyword:`` role for each function keyword argument. False to use a single ``:keyword arguments:`` role for all the keywords. - This behaves similarly to :attr:`napoleon_use_param`. Note unlike docutils, - ``:keyword:`` and ``:param:`` will not be treated the same way - there will - be a separate "Keyword Arguments" section, rendered in the same fashion as - "Parameters" section (type links created if possible) + This behaves similarly to :attr:`napoleon_use_param`. Note unlike + docutils, ``:keyword:`` and ``:param:`` will not be treated the same + way - there will be a separate "Keyword Arguments" section, rendered + in the same fashion as "Parameters" section (type links created if + possible) See Also -------- :attr:`napoleon_use_param` - napoleon_use_rtype : bool, defaults to True + napoleon_use_rtype : :obj:`bool` (Defaults to True) True to use the ``:rtype:`` role for the return type. False to output the return type inline with the description. @@ -295,20 +297,23 @@ def setup(app): def _patch_python_domain(): - import sphinx.domains.python - from sphinx.domains.python import PyTypedField - import sphinx.locale - l_ = sphinx.locale.lazy_gettext - for doc_field in sphinx.domains.python.PyObject.doc_field_types: - if doc_field.name == 'parameter': - doc_field.names = ('param', 'parameter', 'arg', 'argument') - break - sphinx.domains.python.PyObject.doc_field_types.append( - PyTypedField('keyword', label=l_('Keyword Arguments'), - names=('keyword', 'kwarg', 'kwparam'), - typerolename='obj', typenames=('paramtype', 'kwtype'), - can_collapse=True), - ) + try: + from sphinx.domains.python import PyTypedField + except ImportError: + pass + else: + import sphinx.domains.python + import sphinx.locale + l_ = sphinx.locale.lazy_gettext + for doc_field in sphinx.domains.python.PyObject.doc_field_types: + if doc_field.name == 'parameter': + doc_field.names = ('param', 'parameter', 'arg', 'argument') + break + sphinx.domains.python.PyObject.doc_field_types.append( + PyTypedField('keyword', label=l_('Keyword Arguments'), + names=('keyword', 'kwarg', 'kwparam'), + typerolename='obj', typenames=('paramtype', 'kwtype'), + can_collapse=True)) def _process_docstring(app, what, name, obj, options, lines): diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index c12b75e6d..c3d5bb3d0 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -26,6 +26,7 @@ _directive_regex = re.compile(r'\.\. \S+::') _google_section_regex = re.compile(r'^(\s|\w)+:\s*$') _google_typed_arg_regex = re.compile(r'\s*(.+?)\s*\(\s*(.+?)\s*\)') _numpy_section_regex = re.compile(r'^[=\-`:\'"~^_*+#<>]{2,}\s*$') +_single_colon_regex = re.compile(r'(? indent: + desc = [''] + desc + else: + desc = ['', desc[0]] + self._indent(desc_block, 4) + return desc + def _format_admonition(self, admonition, lines): lines = self._strip_empty(lines) if len(lines) == 1: @@ -333,6 +344,22 @@ class GoogleDocstring(UnicodeMixin): else: return [prefix] + def _format_docutils_params(self, fields, field_role='param', + type_role='type'): + lines = [] + for _name, _type, _desc in fields: + _desc = self._strip_empty(_desc) + if any(_desc): + _desc = self._fix_field_desc(_desc) + field = ':%s %s: ' % (field_role, _name) + lines.extend(self._format_block(field, _desc)) + else: + lines.append(':%s %s:' % (field_role, _name)) + + if _type: + lines.append(':%s %s: %s' % (type_role, _name, _type)) + return lines + [''] + def _format_field(self, _name, _type, _desc): _desc = self._strip_empty(_desc) has_desc = any(_desc) @@ -354,9 +381,11 @@ class GoogleDocstring(UnicodeMixin): field = '' if has_desc: - if self._is_list(_desc): - return [field, ''] + _desc - return [field + _desc[0]] + _desc[1:] + _desc = self._fix_field_desc(_desc) + if _desc[0]: + return [field + _desc[0]] + _desc[1:] + else: + return [field] + _desc else: return [field] @@ -393,6 +422,12 @@ class GoogleDocstring(UnicodeMixin): return i return len(line) + def _get_initial_indent(self, lines): + for line in lines: + if line: + return self._get_indent(line) + return 0 + def _get_min_indent(self, lines): min_indent = None for line in lines: @@ -529,11 +564,10 @@ class GoogleDocstring(UnicodeMixin): def _parse_keyword_arguments_section(self, section): fields = self._consume_fields() if self._config.napoleon_use_keyword: - return self._generate_docutils_params( + return self._format_docutils_params( fields, field_role="keyword", - type_role="kwtype" - ) + type_role="kwtype") else: return self._format_fields('Keyword Arguments', fields) @@ -560,26 +594,10 @@ class GoogleDocstring(UnicodeMixin): def _parse_parameters_section(self, section): fields = self._consume_fields() if self._config.napoleon_use_param: - return self._generate_docutils_params(fields) + return self._format_docutils_params(fields) else: return self._format_fields('Parameters', fields) - def _generate_docutils_params(self, fields, field_role='param', type_role='type'): - lines = [] - for _name, _type, _desc in fields: - _desc = self._strip_empty(_desc) - if any(_desc): - if self._is_list(_desc): - _desc = [''] + _desc - field = ':%s %s: ' % (field_role, _name) - lines.extend(self._format_block(field, _desc)) - else: - lines.append(':%s %s:' % (field_role, _name)) - - if _type: - lines.append(':%s %s: %s' % (type_role, _name, _type)) - return lines + [''] - def _parse_raises_section(self, section): fields = self._consume_fields(parse_type=False, prefer_type=True) field_type = ':raises:' @@ -678,11 +696,12 @@ class GoogleDocstring(UnicodeMixin): if found_colon: after_colon.append(source) else: - if (i % 2) == 0 and ":" in source: + m = _single_colon_regex.search(source) + if (i % 2) == 0 and m: found_colon = True - before, colon, after = source.partition(":") - before_colon.append(before) - after_colon.append(after) + colon = source[m.start(): m.end()] + before_colon.append(source[:m.start()]) + after_colon.append(source[m.end():]) else: before_colon.append(source) @@ -715,31 +734,28 @@ class NumpyDocstring(GoogleDocstring): Parameters ---------- - docstring : str or List[str] + docstring : :obj:`str` or :obj:`list` of :obj:`str` The docstring to parse, given either as a string or split into individual lines. - config : Optional[sphinx.ext.napoleon.Config or sphinx.config.Config] + config: :obj:`sphinx.ext.napoleon.Config` or :obj:`sphinx.config.Config` The configuration settings to use. If not given, defaults to the config object on `app`; or if `app` is not given defaults to the - a new `sphinx.ext.napoleon.Config` object. + a new :class:`sphinx.ext.napoleon.Config` object. - See Also - -------- - :class:`sphinx.ext.napoleon.Config` Other Parameters ---------------- - app : Optional[sphinx.application.Sphinx] + app : :class:`sphinx.application.Sphinx`, optional Application object representing the Sphinx process. - what : Optional[str] + what : :obj:`str`, optional A string specifying the type of the object to which the docstring belongs. Valid values: "module", "class", "exception", "function", "method", "attribute". - name : Optional[str] + name : :obj:`str`, optional The fully qualified name of the object. obj : module, class, exception, function, method, or attribute The object to which the docstring belongs. - options : Optional[sphinx.ext.autodoc.Options] + options : :class:`sphinx.ext.autodoc.Options`, optional The options given to the directive: an object with attributes inherited_members, undoc_members, show_inheritance and noindex that are True if the flag option of same name was given to the auto @@ -801,7 +817,7 @@ class NumpyDocstring(GoogleDocstring): Returns ------- - List[str] + :obj:`list` of :obj:`str` The lines of the docstring in a list. """ diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 89e7a9965..a071c5533 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -139,8 +139,8 @@ def collect_pages(app): # construct a page name for the highlighted source pagename = '_modules/' + modname.replace('.', '/') # highlight the source using the builder's highlighter - if env.config.highlight_language == 'python3': - lexer = 'python3' + if env.config.highlight_language in ('python3', 'default'): + lexer = env.config.highlight_language else: lexer = 'python' highlighted = highlighter.highlight_block(code, lexer, linenos=False) diff --git a/sphinx/io.py b/sphinx/io.py index 8d9970e80..36ac7bf98 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -15,7 +15,7 @@ from six import string_types, text_type from sphinx.transforms import ApplySourceWorkaround, ExtraTranslatableNodes, Locale, \ CitationReferences, DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, \ - AutoNumbering, SortIds, RemoveTranslatableInline + AutoNumbering, AutoIndexUpgrader, SortIds, RemoveTranslatableInline from sphinx.util import import_object, split_docinfo @@ -59,7 +59,7 @@ class SphinxStandaloneReader(SphinxBaseReader): """ transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, - AutoNumbering, SortIds, RemoveTranslatableInline] + AutoNumbering, AutoIndexUpgrader, SortIds, RemoveTranslatableInline] class SphinxI18nReader(SphinxBaseReader): @@ -112,10 +112,9 @@ class SphinxFileInput(FileInput): return data.decode(self.encoding, 'sphinx') # py2: decoding def read(self): - def get_parser_type(docname): - path = self.env.doc2path(docname) + def get_parser_type(source_path): for suffix in self.env.config.source_parsers: - if path.endswith(suffix): + if source_path.endswith(suffix): parser_class = self.env.config.source_parsers[suffix] if isinstance(parser_class, string_types): parser_class = import_object(parser_class, 'source parser') @@ -129,7 +128,7 @@ class SphinxFileInput(FileInput): self.app.emit('source-read', self.env.docname, arg) data = arg[0] docinfo, data = split_docinfo(data) - if 'restructuredtext' in get_parser_type(self.env.docname): + if 'restructuredtext' in get_parser_type(self.source_path): if self.env.config.rst_epilog: data = data + '\n' + self.env.config.rst_epilog + '\n' if self.env.config.rst_prolog: diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 1fd2bd626..5aa47cb61 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -359,6 +359,12 @@ latex_documents = [ # # latex_appendices = [] +# It false, will not define \strong, \code, \titleref, \crossref ... but only +# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added +# packages. +# +# latex_keep_old_macro_names = True + # If false, no module index is generated. # # latex_domain_indices = True @@ -559,8 +565,8 @@ help: \t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" \t@echo " latexpdf to make LaTeX files and run them through pdflatex" \t@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" -\t@echo " lualatexpdf to make LaTeX files and run them through pdflatex" -\t@echo " xelatexpdf to make LaTeX files and run them through pdflatex" +\t@echo " lualatexpdf to make LaTeX files and run them through lualatex" +\t@echo " xelatexpdf to make LaTeX files and run them through xelatex" \t@echo " text to make text files" \t@echo " man to make manual pages" \t@echo " texinfo to make Texinfo files" diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 0d1f864b3..6e21b2f5f 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -226,11 +226,13 @@ class IndexBuilder(object): def __init__(self, env, lang, options, scoring): self.env = env - # filename -> title + # docname -> title self._titles = {} - # stemmed word -> set(filenames) + # docname -> filename + self._filenames = {} + # stemmed word -> set(docname) self._mapping = {} - # stemmed words in titles -> set(filenames) + # stemmed words in titles -> set(docname) self._title_mapping = {} # word -> stemmed word self._stem_cache = {} @@ -338,15 +340,16 @@ class IndexBuilder(object): def freeze(self): """Create a usable data structure for serializing.""" - filenames, titles = zip(*sorted(self._titles.items())) - fn2index = dict((f, i) for (i, f) in enumerate(filenames)) + docnames, titles = zip(*sorted(self._titles.items())) + filenames = [self._filenames.get(docname) for docname in docnames] + fn2index = dict((f, i) for (i, f) in enumerate(docnames)) terms, title_terms = self.get_terms(fn2index) objects = self.get_objects(fn2index) # populates _objtypes objtypes = dict((v, k[0] + ':' + k[1]) for (k, v) in iteritems(self._objtypes)) objnames = self._objnames - return dict(filenames=filenames, titles=titles, terms=terms, + return dict(docnames=docnames, filenames=filenames, titles=titles, terms=terms, objects=objects, objtypes=objtypes, objnames=objnames, titleterms=title_terms, envversion=self.env.version) @@ -365,9 +368,11 @@ class IndexBuilder(object): for wordnames in itervalues(self._title_mapping): wordnames.intersection_update(filenames) - def feed(self, filename, title, doctree): + def feed(self, docname, filename, title, doctree): """Feed a doctree to the index.""" - self._titles[filename] = title + self._titles[docname] = title + self._filenames[docname] = filename + visitor = WordCollector(doctree, self.lang) doctree.walk(visitor) @@ -383,9 +388,9 @@ class IndexBuilder(object): for word in visitor.found_title_words: stemmed_word = stem(word) if _filter(stemmed_word): - self._title_mapping.setdefault(stemmed_word, set()).add(filename) + self._title_mapping.setdefault(stemmed_word, set()).add(docname) elif _filter(word): # stemmer must not remove words from search index - self._title_mapping.setdefault(word, set()).add(filename) + self._title_mapping.setdefault(word, set()).add(docname) for word in visitor.found_words: stemmed_word = stem(word) @@ -393,8 +398,7 @@ class IndexBuilder(object): if not _filter(stemmed_word) and _filter(word): stemmed_word = word if stemmed_word not in self._title_mapping and _filter(stemmed_word): - self._mapping.setdefault(stemmed_word, set()).add(filename) - + self._mapping.setdefault(stemmed_word, set()).add(docname) def context_for_searchtool(self): return dict( diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py index 128c1415c..7df8bd6e8 100644 --- a/sphinx/setup_command.py +++ b/sphinx/setup_command.py @@ -15,6 +15,8 @@ from __future__ import print_function import sys import os +import traceback + from distutils.cmd import Command from distutils.errors import DistutilsOptionError, DistutilsExecError @@ -71,6 +73,7 @@ class BuildDoc(Command): ('build-dir=', None, 'Build directory'), ('config-dir=', 'c', 'Location of the configuration directory'), ('builder=', 'b', 'The builder to use. Defaults to "html"'), + ('warning-is-error', 'W', 'Turn warning into errors'), ('project=', None, 'The documented project\'s name'), ('version=', None, 'The short X.Y version'), ('release=', None, 'The full version, including alpha/beta/rc tags'), @@ -78,13 +81,17 @@ class BuildDoc(Command): 'replacement for |today|'), ('link-index', 'i', 'Link index.html to the master doc'), ('copyright', None, 'The copyright string'), + ('pdb', None, 'Start pdb on exception'), ] - boolean_options = ['fresh-env', 'all-files', 'link-index'] + boolean_options = ['fresh-env', 'all-files', 'warning-is-error', + 'link-index'] def initialize_options(self): self.fresh_env = self.all_files = False + self.pdb = False self.source_dir = self.build_dir = None self.builder = 'html' + self.warning_is_error = False self.project = '' self.version = '' self.release = '' @@ -158,7 +165,8 @@ class BuildDoc(Command): app = Sphinx(self.source_dir, self.config_dir, self.builder_target_dir, self.doctree_dir, self.builder, confoverrides, status_stream, - freshenv=self.fresh_env) + freshenv=self.fresh_env, + warningiserror=self.warning_is_error) try: app.build(force_all=self.all_files) @@ -166,13 +174,20 @@ class BuildDoc(Command): raise DistutilsExecError( 'caused by %s builder.' % app.builder.name) except Exception as err: - from docutils.utils import SystemMessage - if isinstance(err, SystemMessage): - print(darkred('reST markup error:'), file=sys.stderr) - print(err.args[0].encode('ascii', 'backslashreplace'), + if self.pdb: + import pdb + print(darkred('Exception occurred while building, starting debugger:'), file=sys.stderr) + traceback.print_exc() + pdb.post_mortem(sys.exc_info()[2]) else: - raise + from docutils.utils import SystemMessage + if isinstance(err, SystemMessage): + print(darkred('reST markup error:'), file=sys.stderr) + print(err.args[0].encode('ascii', 'backslashreplace'), + file=sys.stderr) + else: + raise if self.link_index: src = app.config.master_doc + app.builder.out_suffix diff --git a/sphinx/templates/latex/content.tex_t b/sphinx/templates/latex/content.tex_t new file mode 100644 index 000000000..fb5b1decd --- /dev/null +++ b/sphinx/templates/latex/content.tex_t @@ -0,0 +1,42 @@ +%% Generated by Sphinx. +\def\sphinxdocclass{<%= docclass %>} +\newif\ifsphinxKeepOldNames <%= keepoldnames %> +\documentclass[<%= papersize %>,<%= pointsize %><%= classoptions %>]{<%= wrapperclass %>} +\ifdefined\pdfpxdimen + \let\sphinxpxdimen\pdfpxdimen\else\newdimen\sphinxpxdimen +\fi \sphinxpxdimen=<%= pxunit %>\relax +\usepackage{iftex} +<%= passoptionstopackages %> +<%= inputenc %> +<%= utf8extra %> +<%= cmappkg %> +<%= fontenc %> +<%= amsmath %> +<%= babel %> +<%= fontpkg %> +<%= fncychap %> +<%= longtable %> +\usepackage{sphinx} +\usepackage{multirow} +\usepackage{eqparbox} +<%= usepackages %> +<%= contentsname %> +<%= numfig_format %> +<%= pageautorefname %> +<%= tocdepth %> +<%= secnumdepth %> +<%= preamble %> + +\title{<%= title %>} +\date{<%= date %>} +\release{<%= release %>} +\author{<%= author %>} +\newcommand{\sphinxlogo}{<%= logo %>} +\renewcommand{\releasename}{<%= releasename %>} +<%= makeindex %> +<%= body %> +<%= footer %> +<%= indices %> +\renewcommand{\indexname}{<%= indexname %>} +<%= printindex %> +\end{document} diff --git a/sphinx/texinputs/Makefile b/sphinx/texinputs/Makefile index d748006cc..c0676e3ee 100644 --- a/sphinx/texinputs/Makefile +++ b/sphinx/texinputs/Makefile @@ -3,6 +3,7 @@ ALLDOCS = $(basename $(wildcard *.tex)) ALLPDF = $(addsuffix .pdf,$(ALLDOCS)) ALLDVI = $(addsuffix .dvi,$(ALLDOCS)) +ALLPS = $(addsuffix .ps,$(ALLDOCS)) # Prefix for archive names ARCHIVEPRREFIX = @@ -18,8 +19,7 @@ MAKEINDEX = makeindex all: $(ALLPDF) all-pdf: $(ALLPDF) all-dvi: $(ALLDVI) -all-ps: all-dvi - for f in *.dvi; do dvips $$f; done +all-ps: $(ALLPS) all-pdf-ja: for f in *.pdf *.png *.gif *.jpg *.jpeg; do extractbb $$f; done @@ -70,6 +70,9 @@ xz: tar $(PDFLATEX) $(LATEXOPTS) '$<' $(PDFLATEX) $(LATEXOPTS) '$<' +%.ps: %.dvi + dvips '$<' + clean: rm -f *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla *.ps *.tar *.tar.gz *.tar.bz2 *.tar.xz $(ALLPDF) $(ALLDVI) diff --git a/sphinx/texinputs/iftex.sty b/sphinx/texinputs/iftex.sty new file mode 100755 index 000000000..765146644 --- /dev/null +++ b/sphinx/texinputs/iftex.sty @@ -0,0 +1,97 @@ +%% +%% This is file `iftex.sty', + +%% +%% __________________________________ +%% Copyright © 2010–2013 Persian TeX Group +%% +%% License information appended. +%% +%% +\csname iftexloaded\endcsname +\let\iftexloaded\endinput +\expandafter\ifx\csname ProvidesPackage\endcsname\relax\else + \ProvidesPackage{iftex} + [2013/04/04 v0.2 Provides if(tex) conditional for PDFTeX, XeTeX, and LuaTeX] +\fi +\def\RequirePDFTeX{% + \ifPDFTeX\else + \begingroup + \errorcontextlines=-1\relax + \newlinechar=10\relax + \errmessage{^^J + ********************************************^^J + * PDFTeX is required to compile this document.^^J + * Sorry!^^J + ********************************************}% + \endgroup + \fi} +\def\RequireXeTeX{% + \ifXeTeX\else + \begingroup + \errorcontextlines=-1\relax + \newlinechar=10\relax + \errmessage{^^J + ********************************************^^J + * XeTeX is required to compile this document.^^J + * Sorry!^^J + ********************************************}% + \endgroup + \fi} +\def\RequireLuaTeX{% + \ifLuaTeX\else + \begingroup + \errorcontextlines=-1\relax + \newlinechar=10\relax + \errmessage{^^J + ********************************************^^J + * LuaTeX is required to compile this document.^^J + * Sorry!^^J + ********************************************}% + \endgroup + \fi} +\expandafter\ifx\csname ifPDFTeX\endcsname\relax\else + \expandafter\endinput +\fi +\expandafter\ifx\csname ifXeTeX\endcsname\relax\else + \expandafter\endinput +\fi +\expandafter\ifx\csname ifLuaTeX\endcsname\relax\else + \expandafter\endinput +\fi +\newif\ifPDFTeX +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname pdfmatch\endcsname\relax + \PDFTeXfalse +\else + \PDFTeXtrue +\fi +\newif\ifXeTeX +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname XeTeXinterchartoks\endcsname\relax + \XeTeXfalse +\else + \XeTeXtrue +\fi +\newif\ifLuaTeX +\begingroup\expandafter\expandafter\expandafter\endgroup +\expandafter\ifx\csname directlua\endcsname\relax + \LuaTeXfalse +\else + \LuaTeXtrue +\fi +%% +%% Copyright © 2010–2013 by Persian TeX Group +%% +%% Distributable under the LaTeX Project Public License, +%% version 1.3c or higher (your choice). The latest version of +%% this license is at: http://www.latex-project.org/lppl.txt +%% +%% This work is "maintained" (as per LPPL maintenance status) +%% by Persian TeX Group. +%% +%% +%% +%% +%% +%% End of file `iftex.sty'. diff --git a/sphinx/texinputs/needspace.sty b/sphinx/texinputs/needspace.sty new file mode 100644 index 000000000..113d87216 --- /dev/null +++ b/sphinx/texinputs/needspace.sty @@ -0,0 +1,35 @@ + +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{needspace}[2010/09/12 v1.3d reserve vertical space] + +\newcommand{\needspace}[1]{% + \begingroup + \setlength{\dimen@}{#1}% + \vskip\z@\@plus\dimen@ + \penalty -100\vskip\z@\@plus -\dimen@ + \vskip\dimen@ + \penalty 9999% + \vskip -\dimen@ + \vskip\z@skip % hide the previous |\vskip| from |\addvspace| + \endgroup +} + +\newcommand{\Needspace}{\@ifstar{\@sneedsp@}{\@needsp@}} + +\newcommand{\@sneedsp@}[1]{\par \penalty-100\begingroup + \setlength{\dimen@}{#1}% + \dimen@ii\pagegoal \advance\dimen@ii-\pagetotal + \ifdim \dimen@>\dimen@ii + \break + \fi\endgroup} + +\newcommand{\@needsp@}[1]{\par \penalty-100\begingroup + \setlength{\dimen@}{#1}% + \dimen@ii\pagegoal \advance\dimen@ii-\pagetotal + \ifdim \dimen@>\dimen@ii + \ifdim \dimen@ii>\z@ + \vfil + \fi + \break + \fi\endgroup} + diff --git a/sphinx/texinputs/newfloat.sty b/sphinx/texinputs/newfloat.sty deleted file mode 100644 index 47ac5e568..000000000 --- a/sphinx/texinputs/newfloat.sty +++ /dev/null @@ -1,737 +0,0 @@ -%% -%% This is file `newfloat.sty', -%% generated with the docstrip utility. -%% -%% The original source files were: -%% -%% newfloat.dtx (with options: `package') -%% -%% Copyright (C) 1994-2016 Axel Sommerfeldt (axel.sommerfeldt@f-m.fm) -%% -%% http://sourceforge.net/projects/latex-caption/ -%% -%% -------------------------------------------------------------------------- -%% -%% This work may be distributed and/or modified under the -%% conditions of the LaTeX Project Public License, either version 1.3 -%% of this license or (at your option) any later version. -%% The latest version of this license is in -%% http://www.latex-project.org/lppl.txt -%% and version 1.3 or later is part of all distributions of LaTeX -%% version 2003/12/01 or later. -%% -%% This work has the LPPL maintenance status "maintained". -%% -%% This Current Maintainer of this work is Axel Sommerfeldt. -%% -%% This work consists of the files -%% CHANGELOG, README, SUMMARY, caption.ins, -%% caption.dtx, caption2.dtx, caption3.dtx, -%% bicaption.dtx, ltcaption.dtx, subcaption.dtx, -%% newfloat.dtx, and totalcount.dtx -%% the derived files -%% caption.sty, caption2.sty, caption3.sty, -%% bicaption.sty, ltcaption.sty, subcaption.sty, -%% newfloat.sty, and totalcount.sty -%% and the user manuals -%% caption-deu.tex, caption-eng.tex, and caption-rus.tex. -%% -\NeedsTeXFormat{LaTeX2e}[1994/12/01] -\def\caption@tempa$Id: #1 #2 #3-#4-#5 #6${% - \def\caption@tempa{#3/#4/#5 }\def\caption@tempb{#2 }} -\caption@tempa $Id: newfloat.dtx 109 2015-09-17 09:29:07Z sommerfeldt $ -\ProvidesPackage{newfloat}[\caption@tempa v1.1-\caption@tempb Defining new floating environments (AR)] -\newcommand*\newfloat@Info[1]{% - \PackageInfo{newfloat}{#1}} -\newcommand*\newfloat@InfoNoLine[1]{% - \newfloat@Info{#1\@gobble}} -\newcommand*\newfloat@Error[1]{% - \PackageError{newfloat}{#1}\newfloat@eh} -\newcommand*\newfloat@eh{% - If you do not understand this error, please take a closer look\MessageBreak - at the documentation of the `newfloat' package.\MessageBreak\@ehc} -\RequirePackage{keyval}[1997/11/10] -\newcommand*\newfloat@def[2]{% - \newfloat@ifundefined{#1}{% - \@namedef{#1}{#2}}} -\newcommand*\newfloat@let[2]{% - \newfloat@ifundefined{#1}{% - \expandafter\let\csname #1\endcsname#2}} -\newcommand*\newfloat@ifundefined[2]{% - \@ifundefined{#1}{#2}{% - \newfloat@Info{% - \expandafter\string\csname#1\endcsname\space is already defined}}} -\newcommand*\DeclareFloatingEnvironment{% - \@testopt\@DeclareFloatingEnvironment{}} -\@onlypreamble\DeclareFloatingEnvironment -\def\@DeclareFloatingEnvironment[#1]#2{% - \newfloat@Info{New float `#2' with options `#1'}% - \newfloat@ifundefined{c@#2}{\newcounter{#2}}% - \ifdefined\c@float@type % from float package - \expandafter\edef\csname ftype@#2\endcsname{\the\value{float@type}}% - \addtocounter{float@type}{\value{float@type}}% - \else\ifdefined\c@newflo@tctr % from memoir document class - \expandafter\edef\csname ftype@#2\endcsname{\the\c@newflo@tctr}% - \advance\c@newflo@tctr \c@newflo@tctr - \else - \ifdefined\newfloat@ftype \else - \newcount\newfloat@ftype - \newfloat@ftype=8\relax - \fi - \expandafter\xdef\csname ftype@#2\endcsname{\the\newfloat@ftype}% - \advance\newfloat@ftype\newfloat@ftype - \fi\fi - \newfloat@Info{float type `#2'=\@nameuse{ftype@#2}}% - \newfloat@def{fnum@#2}% - {\@nameuse{#2name}\nobreakspace\@nameuse{the#2}\@nameuse{autodot}}% - \newfloat@capitalize\newfloat@Type{#2}% - \newfloat@let{#2name}{\newfloat@Type}% - \newfloat@def{fleg#2}{\@nameuse{#2name}}% legend naming (memoir) - \newfloat@ifundefined{flegtoc#2}{\@namedef{flegtoc#2}##1{}}% - \ifcsname @tufte@float\endcsname - \newenvironment{#2}[1][htbp]% - {\begin{@tufte@float}[##1]{#2}{}}% - {\end{@tufte@float}}% - \newenvironment{#2*}[1][htbp]% - {\begin{@tufte@float}[##1]{#2}{star}}% - {\end{@tufte@float}}% - \else - \newenvironment{#2}{\@float{#2}}{\end@float}% - \newenvironment{#2*}{\@dblfloat{#2}}{\end@dblfloat}% - \fi - \newfloat@def{listof#2}{\newfloat@listof{#2}}% - \newfloat@def{listof#2s}{\@nameuse{listof#2}}% - \newfloat@def{listof#2es}{\@nameuse{listof#2s}}% - \newfloat@def{newfloat@listof#2@hook}{}% - \ifdefined\l@figure - \newfloat@let{l@#2}{\l@figure}% - \else - \newfloat@def{l@#2}{\@dottedtocline{1}{1.5em}{2.3em}}% - \fi - \edef\newfloat@tempa{List of \newfloat@Type s}% - \newfloat@let{list#2name}{\newfloat@tempa}% - \expandafter\let\csname fst@#2\endcsname\@undefined - \newfloat@ifundefined{fps@#2}{\newfloat@setplacement{#2}{tbp}}% - \newfloat@ifundefined{ext@#2}{\newfloat@setfileext{#2}{lo#2}}% - \newfloat@setoptions*{#2}{#1}% - \@expandtwoargs\newfloat@announce{#2}{\@nameuse{ext@#2}}% - \@ifnextchar[\newfloat@DFE@setname\relax} -\@onlypreamble\@DeclareFloatingEnvironment -\def\newfloat@DFE@setname[#1]{% - \KV@@newfloat@name{#1}% - \@ifnextchar[\newfloat@DFE@setlistname\relax} -\@onlypreamble\newfloat@DFE@setname -\def\newfloat@DFE@setlistname[#1]{% - \KV@@newfloat@listname{#1}} -\@onlypreamble\newfloat@DFE@setlistname -\newcommand*\newfloat@capitalize[2]{% - \edef\newfloat@tempa{\gdef\noexpand#1{\@car#2\@nil}}% - \uppercase\expandafter{\newfloat@tempa}% - \edef\newfloat@tempa{% - \noexpand\g@addto@macro\noexpand#1{\@cdr#2\@nil}}% - \newfloat@tempa} -\newcommand*\newfloat@listof[1]{% - \@expandtwoargs\newfloat@list@of{#1}{\@nameuse{ext@#1}}} -\newcommand*\newfloat@list@of[2]{% - \begingroup - \expandafter\let\expandafter\listfigurename\csname list#1name\endcsname - \def\ext@figure{#2}% - \let\newfloat@starttoc\@starttoc - \def\@starttoc##1{\newfloat@starttoc{#2}}% - \let\newfloat@listoftoc\listoftoc - \def\listoftoc##1{\newfloat@listoftoc{#2}}% - \@nameuse{newfloat@listof#1@hook}% - \listoffigures - \endgroup} -\newcommand*\newfloat@setoptions{% - \@ifstar - {\newfloat@@setoptions\@firstofone}% - {\newfloat@@setoptions\@gobble}} -\newcommand*\newfloat@@setoptions[3]{% - \let\newfloat@within@value\@undefined - \let\newfloat@chapterlistsgaps@value\@undefined - #1{\KV@@newfloat@within\newfloat@within@default}% set default value for new floats - \def\newfloat@type{#2}% - \setkeys{@newfloat}{#3}% - \ifx\newfloat@within@value\@undefined \else - \newfloat@setoption{within}\newfloat@within@value - \fi - \ifx\newfloat@chapterlistsgaps@value\@undefined \else - \newfloat@setoption{chapterlistsgaps}\newfloat@chapterlistsgaps@value - \fi} -\newcommand*\newfloat@within@default{% - \ifcsname c@chapter\endcsname chapter\else none\fi} -\@onlypreamble\newfloat@within@default -\newcommand*\newfloat@setoption[1]{% - \edef\caption@tempa{\noexpand\@nameuse{newfloat@set#1}{\newfloat@type}}% - \caption@tempa} -\newcommand*\newfloat@setfileext[2]{% - \@namedef{ext@#1}{#2}} -\define@key{@newfloat}{fileext}{% - \newfloat@setoption{fileext}{#1}} -\newcommand*\newfloat@setlistname[2]{% - \@namedef{list#1name}{#2}} -\define@key{@newfloat}{listname}{% - \newfloat@setoption{listname}{#1}} -\newcommand*\newfloat@setname[2]{% - \newfloat@@setname{#1}{#2}% - \begingroup - \ifcsname languagename\endcsname - \ifcsname captions\languagename\endcsname - \expandafter\g@addto@macro\csname captions\languagename\endcsname - {\newfloat@@setname{#1}{#2}}% - \fi - \fi - \endgroup} -%%\AtBeginDocument{\let\newfloat@setname\newfloat@@setname} -\newcommand*\newfloat@@setname[2]{% - \@namedef{#1name}{#2}} -\define@key{@newfloat}{name}{% - \newfloat@setoption{name}{#1}} -\newcommand*\newfloat@setplacement[2]{% - \@namedef{fps@#1}{#2}} -\define@key{@newfloat}{placement}{% - \newfloat@setoption{placement}{#1}} -\newcommand*\newfloat@setwithin[2]{% - \ifcsname c@chapter\endcsname - \@removefromreset{#1}{chapter}% - \fi - \@removefromreset{#1}{section}% - \edef\@tempa{#2}% - \ifx\@tempa\@empty - \def\@tempa{none}% - \fi - \def\@tempb{none}% - \ifx\@tempa\@tempb - \ifcsname c@chapter\endcsname - \@chapterlistsgap@off{#1}% - \fi - \newfloat@@setwithin{#1}{}{}% - \else - \def\@tempb{chapter}% - \ifx\@tempa\@tempb - \@addtoreset{#1}{chapter}% - \@chapterlistsgap@on{#1}% - \newfloat@@setwithin{#1}{\ifnum\c@chapter>\z@ \thechapter.\fi}{\theHchapter.}% - \else - \def\@tempb{section}% - \ifx\@tempa\@tempb - \@addtoreset{#1}{section}% - \ifcsname c@chapter\endcsname - \@addtoreset{#1}{chapter}% - \@chapterlistsgap@on{#1}% - \newfloat@@setwithin{#1}{\thesection.}{\theHsection.}% - \else - \newfloat@@setwithin{#1}{\ifnum\c@section>\z@ \thesection.\fi}{\theHsection.}% - \fi - \else - \newfloat@Error{Invalid value `#2' for option `within'}% - \fi - \fi - \fi} -\newcommand*\newfloat@@setwithin[3]{% - \global\@namedef{the#1}{#2\arabic{#1}}% - \global\@namedef{theH#1}{#3\arabic{#1}}} -\define@key{@newfloat}{within}{% - \def\newfloat@within@value{#1}} -\newcommand*\newfloat@setwithout[1]{% - \newfloat@setwithin{#1}{none}} -\define@key{@newfloat}{without}[]{% - \def\newfloat@within@value{none}} -\newcommand*\newfloat@setchapterlistsgaps[2]{% - \edef\@tempa{#2}% - \def\@tempb{off}% - \ifx\@tempa\@tempb - \@chapterlistsgap@off{#1}% - \else - \def\@tempb{on}% - \ifx\@tempa\@tempb - \@chapterlistsgap@on{#1}% - \else - \newfloat@Error{Invalid value `#2' for option `chapterlistsgaps'}% - \fi - \fi} -\define@key{@newfloat}{chapterlistsgaps}{% - \def\newfloat@chapterlistsgaps@value{#1}} -\providecommand*\@removefromreset[2]{{% - \expandafter\let\csname c@#1\endcsname\@removefromreset - \def\@elt##1{% - \expandafter\ifx\csname c@##1\endcsname\@removefromreset - \else - \noexpand\@elt{##1}% - \fi}% - \expandafter\xdef\csname cl@#2\endcsname{% - \csname cl@#2\endcsname}}} -\newcommand*\newfloat@announce[2]{% - \@cons\newfloat@list{{#1}}% - \@cons\newfloat@@list{{#1}}% - \newfloat@ifundefined{newfloat@ext@#2}{% - \@namedef{newfloat@ext@#2}{#1}% - \ifcsname c@lofdepth\endcsname - \newfloat@ifundefined{c@#2depth}{% - \newcounter{#2depth}% - \setcounter{#2depth}{1}}% - \fi - \ifcsname addtotoclist\endcsname - \addtotoclist[float]{#2}% - \newfloat@def{listof#2name}{\@nameuse{list#1name}}% - \fi - }% - \ifcsname contentsuse\endcsname - \contentsuse{#1}{#2}% - \fi - \newfloat@hook{#1}} -\@onlypreamble\newfloat@announce -\newcommand*\newfloat@@list{} -\newcommand*\SetupFloatingEnvironment[1]{% - \newfloat@addtolist{#1}% - \newfloat@setoptions{#1}} -\newcommand\ForEachFloatingEnvironment{% - \@ifstar - {\@ForEachFloatingEnvironment\@gobble}% - {\@ForEachFloatingEnvironment\@iden}} -\newcommand\@ForEachFloatingEnvironment[2]{% - \def\@elt##1{#2}% - \newfloat@list - \let\@elt\relax - #1{\newfloat@addtohook{#2}}} -\providecommand\newfloat@addtohook[1]{% - \toks@=\expandafter{\newfloat@hook{##1}#1}% - \edef\@tempa{\def\noexpand\newfloat@hook####1{\the\toks@}}% - \@tempa} -\providecommand*\newfloat@hook[1]{} -\newcommand\PrepareListOf[1]{% - \expandafter\g@addto@macro\csname newfloat@listof#1@hook\endcsname} -\@onlypreamble\PrepareListOf -\newcommand*\newfloat@list{} -\newcommand*\newfloat@addtolist[1]{% - \newfloat@ifinlist{#1}{}{% - \ifcsname ext@#1\endcsname - \@cons\newfloat@list{{#1}}% - \@namedef{newfloat@ext@\@nameuse{ext@#1}}{#1}% - \newfloat@let{@ifchapterlistsgap@#1}{\@iden}% - \else - \newfloat@Error{`#1' does not seem to be a floating environment}% - \fi}} -\newcommand*\newfloat@ifinlist[1]{% - \let\next\@secondoftwo - \begingroup - \expandafter\let\csname c@#1\endcsname\newfloat@ifinlist - \def\@elt##1{% - \expandafter\ifx\csname c@##1\endcsname\newfloat@ifinlist - \global\let\next\@firstoftwo - \fi}% - \newfloat@list - \endgroup - \next} -\ifcsname ext@figure\endcsname - \newfloat@addtolist{figure} -\fi -\ifcsname ext@table\endcsname - \newfloat@addtolist{table} -\fi -\ifcsname @chapter\endcsname - \providecommand*\@chapterlistsgap{10\p@}% - \providecommand*\@addchapterlistsgap[2]{% - \@nameuse{@ifchapterlistsgap@#1}{% if switched on - \@@addchapterlistsgap{#1}{#2}}} - \providecommand*\@@addchapterlistsgap[2]{% - \@ifundefined{@addchapterlistsgap@#2}{% only once per extension - \@namedef{@addchapterlistsgap@#2}{#1}% - \@@@addchapterlistsgap{#2}}{}} - \providecommand*\@@@addchapterlistsgap[1]{% - \ifdim \@chapterlistsgap>\z@ - \addtocontents{#1}{\protect\addvspace{\@chapterlistsgap}}% - \fi} - \providecommand*\@addchapterlistsgaps{% - \begingroup - \def\@elt##1{% - \@expandtwoargs\@addchapterlistsgap{##1}{\@nameuse{ext@##1}}}% - \newfloat@list - \endgroup} - \providecommand*\@chapterlistsgap@off[1]{% - \expandafter\let\csname @ifchapterlistsgap@#1\endcsname\@gobble - \ifcsname unsettoc\endcsname - \@expandtwoargs\unsettoc{\@nameuse{ext@#1}}{chapteratlist}% - \fi} - \providecommand*\@chapterlistsgap@on[1]{% - \expandafter\let\csname @ifchapterlistsgap@#1\endcsname\@iden - \ifcsname setuptoc\endcsname - \@expandtwoargs\setuptoc{\@nameuse{ext@#1}}{chapteratlist}% - \fi} -\fi -\define@key{newfloat}{chapterlistsgap}{% - \renewcommand*\@chapterlistsgap{#1}} -\define@key{newfloat}{within}{% - \def\newfloat@within@default{#1}% set new default value - \def\@elt##1{\newfloat@setwithin{##1}{#1}}% - \newfloat@list - \let\@elt\relax} -\define@key{newfloat}{without}[]{% - \KV@newfloat@within{none}} -\def\@elt#1{% - \define@key{newfloat}{#1name}{% - \newfloat@setname{#1}{##1}}% - \define@key{newfloat}{list#1name}{% - \newfloat@setname{list#1}{##1}}% - \define@key{newfloat}{#1within}{% - \newfloat@setwithin{#1}{##1}}% - \define@key{newfloat}{#1without}[]{% - \newfloat@setwithout{#1}}% -}% -\newfloat@list -\let\@elt\relax -\define@key{newfloat}{planb}[true]{% - \def\@tempa{#1}% - \def\@tempb{false}% - \ifx\@tempa\@tempb - \let\newfloat@ifplanb\@gobble - \else - \def\@tempb{true}% - \ifx\@tempa\@tempb - \let\newfloat@ifplanb\@iden - \else - \newfloat@Error{Invalid value `#1' for option `planb'}% - \fi - \fi} -\define@key{newfloat}{planb-fileext}{% - \newfloat@Info{Setting Plan B file extension to `#1'} - \xdef\newfloat@addtocontents@ext{#1}} - -\let\@tempc\relax -\@expandtwoargs\setkeys{newfloat}{planb,\@ptionlist{\@currname.\@currext}}% -\AtEndOfPackage{\let\@unprocessedoptions\relax} -\newcommand*\newfloatsetup{\setkeys{newfloat}} -\newcommand\newfloat@replace@chapter[2]{% - \begingroup - \let\if@twocolumn\iffalse - \let\if@mainmatter\iffalse - \let\if@thema\iffalse - \def\@tempa[##1]##2{#1}% - \ifx\@tempa\@chapter - \gdef\@chapter[##1]##2{#2}% - \global\let\newfloat@replace@chapter\@gobbletwo - \else\ifx\@tempa\Hy@org@chapter - \gdef\Hy@org@chapter[##1]##2{#2}% - \global\let\newfloat@replace@chapter\@gobbletwo - \fi\fi - \endgroup} -\ifcsname @chapter\endcsname \else - \let\newfloat@replace@chapter\@gobbletwo -\fi -\newfloat@replace@chapter{% - \ifnum \c@secnumdepth >\m@ne - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}#1}% - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \chaptermark{#1}% - \addtocontents{lof}{\protect\addvspace{10\p@}}% - \addtocontents{lot}{\protect\addvspace{10\p@}}% - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi -}{% - \ifnum \c@secnumdepth >\m@ne - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}#1}% - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \chaptermark{#1}% - \@addchapterlistsgaps - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi} -\newfloat@replace@chapter{% - \ifnum \c@secnumdepth >\m@ne - \if@mainmatter - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}#1}% - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \chaptermark{#1}% - \addtocontents{lof}{\protect\addvspace{10\p@}}% - \addtocontents{lot}{\protect\addvspace{10\p@}}% - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi -}{% - \ifnum \c@secnumdepth >\m@ne - \if@mainmatter - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}#1}% - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \chaptermark{#1}% - \@addchapterlistsgaps - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi} -\newfloat@replace@chapter{% - \refstepcounter{chapter}% - \ifnum\c@secnumdepth<\z@ \let\@secnumber\@empty - \else \let\@secnumber\thechapter \fi - \typeout{\chaptername\space\@secnumber}% - \def\@toclevel{0}% - \ifx\chaptername\appendixname \@tocwriteb\tocappendix{chapter}{#2}% - \else \@tocwriteb\tocchapter{chapter}{#2}\fi - \chaptermark{#1}% - \addtocontents{lof}{\protect\addvspace{10\p@}}% - \addtocontents{lot}{\protect\addvspace{10\p@}}% - \@makechapterhead{#2}\@afterheading -}{% - \refstepcounter{chapter}% - \ifnum\c@secnumdepth<\z@ \let\@secnumber\@empty - \else \let\@secnumber\thechapter \fi - \typeout{\chaptername\space\@secnumber}% - \def\@toclevel{0}% - \ifx\chaptername\appendixname \@tocwriteb\tocappendix{chapter}{#2}% - \else \@tocwriteb\tocchapter{chapter}{#2}\fi - \chaptermark{#1}% - \@addchapterlistsgaps - \@makechapterhead{#2}\@afterheading} -\@ifpackageloaded{tocbasic}{% - \let\newfloat@replace@chapter\@gobbletwo}{} -\ifcsname insertchapterspace\endcsname - \renewcommand*\insertchapterspace{\@addchapterlistsgaps} - \let\newfloat@replace@chapter\@gobbletwo -\fi -\newfloat@replace@chapter{% - \ifnum \c@secnumdepth >\m@ne - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}\toc@font0 #1}% - \else - \addcontentsline{toc}{chapter}{\toc@font0 #1}% - \fi - \chaptermark{#1}% - \addtocontents{lof}{\protect\addvspace{10\p@}}% - \addtocontents{lot}{\protect\addvspace{10\p@}}% - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi -}{% - \ifnum \c@secnumdepth >\m@ne - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}\toc@font0 #1}% - \else - \addcontentsline{toc}{chapter}{\toc@font0 #1}% - \fi - \chaptermark{#1}% - \@addchapterlistsgaps - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi} - % boek(3).cls [2004/06/07 v2.1a NTG LaTeX document class] -\newfloat@replace@chapter{% - \ifnum \c@secnumdepth >\m@ne - \if@mainmatter - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}\toc@font0 #1}% - \else - \addcontentsline{toc}{chapter}{\toc@font0 #1}% - \fi - \else - \addcontentsline{toc}{chapter}{\toc@font0 #1}% - \fi - \chaptermark{#1}% - \addtocontents{lof}{\protect\addvspace{10\p@}}% - \addtocontents{lot}{\protect\addvspace{10\p@}}% - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi -}{% - \ifnum \c@secnumdepth >\m@ne - \if@mainmatter - \refstepcounter{chapter}% - \typeout{\@chapapp\space\thechapter.}% - \addcontentsline{toc}{chapter}% - {\protect\numberline{\thechapter}\toc@font0 #1}% - \else - \addcontentsline{toc}{chapter}{\toc@font0 #1}% - \fi - \else - \addcontentsline{toc}{chapter}{\toc@font0 #1}% - \fi - \chaptermark{#1}% - \@addchapterlistsgaps - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi} -\newfloat@replace@chapter{% - \ifnum \c@secnumdepth >\m@ne - \if@mainmatter - \refstepcounter{chapter}% - \typeout{\chaptername\space\thechapter.} - \if@thema - \ifx\@shortauthor\@empty - \addcontentsline{toc}{chapter}{% - \protect\numberline{\thechapter.}#1}% - \else - \addcontentsline{toc}{chapter}{% - \protect\numberline{\thechapter.}% - \@shortauthor\hfill\mbox{}\vskip\normallineskip #1}% - \fi - \else - \addcontentsline{toc}{chapter}{% - \protect\numberline{\thechapter.}#1}% - \fi - \else - \addcontentsline{toc}{chapter}{#1} - \fi - \else - \addcontentsline{toc}{chapter}{#1} - \fi - \chaptermark{#1} - \addtocontents{lof}{\protect\addvspace{10pt}} - \addtocontents{lot}{\protect\addvspace{10pt}} - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}] - \else - \@makechapterhead{#2} - \@afterheading - \fi -}{% - \ifnum \c@secnumdepth >\m@ne - \if@mainmatter - \refstepcounter{chapter}% - \typeout{\chaptername\space\thechapter.}% - \if@thema - \ifx\@shortauthor\@empty - \addcontentsline{toc}{chapter}{% - \protect\numberline{\thechapter.}#1}% - \else - \addcontentsline{toc}{chapter}{% - \protect\numberline{\thechapter.}% - \@shortauthor\hfill\mbox{}\vskip\normallineskip #1}% - \fi - \else - \addcontentsline{toc}{chapter}{% - \protect\numberline{\thechapter.}#1}% - \fi - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \else - \addcontentsline{toc}{chapter}{#1}% - \fi - \chaptermark{#1}% - \@addchapterlistsgaps - \if@twocolumn - \@topnewpage[\@makechapterhead{#2}]% - \else - \@makechapterhead{#2}% - \@afterheading - \fi} -\ifx\newfloat@replace@chapter\@gobbletwo \else - \newfloat@InfoNoLine{% - Unsupported document class, or \noexpand\@chapter\MessageBreak - was already redefined by another package} - \newfloat@InfoNoLine{\string\@chapter\space=\space\meaning\@chapter} - \newfloat@InfoNoLine{\string\Hy@org@chapter\space=\space\meaning\Hy@org@chapter} - \newfloat@ifplanb{% - \newfloat@InfoNoLine{Trying Plan B..}% - \let\newfloat@addtocontents@ORI\addtocontents - \long\def\addtocontents#1#2{% - \newfloat@addtocontents{#1}{#2}#2\addvspace\newfloat@nil}% - \long\def\newfloat@addtocontents#1#2#3\addvspace#4\newfloat@nil{% - \def\newfloat@tempa{#4}% - \ifx\newfloat@tempa\@empty - \newfloat@addtocontents@ORI{#1}{#2}% - \else - \ifx\newfloat@addtocontents@ext\@undefined - \newfloat@Info{Setting Plan B file extension to `#1'...}% - \xdef\newfloat@addtocontents@ext{#1}% - \fi - \edef\newfloat@tempa{#1}% - \ifx\newfloat@tempa\newfloat@addtocontents@ext - \begingroup - \let\addtocontents\newfloat@addtocontents@ORI - \@addchapterlistsgaps - \endgroup - \fi - \fi}} -\fi -\newcommand\newfloat@ForEachNew[2][newfloat@@list]{% - \AtBeginDocument{% - \ifcsname#1\endcsname - \def\@elt##1{#2}% - \newfloat@@list - \let\@elt\relax - \fi}}% -\@onlypreamble\newfloat@ForEachNew -%% \begin{macrocode} -\newfloat@ForEachNew[float@exts]{% - \@nameuse{@ifchapterlistsgap@#1}{% if switched on - \let\float@do=\relax - \edef\@tempa{% - \noexpand\float@exts{\the\float@exts\float@do{\@nameuse{ext@#1}}}}% - \@tempa}} -\newfloat@ForEachNew[FP@floatBegin]{% - \newcounter{FP@#1C}% - \newenvironment{FP#1}{\FP@floatBegin{#1}}{\FP@floatEnd}} -\providecommand*\ext@lstlisting{lol}% -\newfloat@ForEachNew[@rotfloat]{% - \newenvironment{sideways#1}{\@rotfloat{#1}}{\end@rotfloat}% - \newenvironment{sideways#1*}{\@rotdblfloat{#1}}{\end@rotdblfloat}} -\newcommand*\newfloat@For@SC[2]{% - \def#1{b}% = \sidecaptionvpos{#2}{b} (v1.6) - \newenvironment{SC#2}% - {\SC@float[#1]{#2}}{\endSC@float}% - \newenvironment{SC#2*}% - {\SC@dblfloat[#1]{#2}}{\endSC@dblfloat}} -\@onlypreamble\newfloat@For@SC -\newfloat@ForEachNew[SC@float]{% - \expandafter\newfloat@For@SC\csname SC@#1@vpos\endcsname{#1}} -\newfloat@ForEachNew[wrapfloat]{% - \newenvironment{wrap#1}{\wrapfloat{#1}}{\endwrapfloat}} -\endinput -%% -%% End of file `newfloat.sty'. diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 956924e99..e612cf5ef 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -6,18 +6,34 @@ % \NeedsTeXFormat{LaTeX2e}[1995/12/01] -\ProvidesPackage{sphinx}[2010/01/15 LaTeX package (Sphinx markup)] +\ProvidesPackage{sphinx}[2016/06/10 LaTeX package (Sphinx markup)] +% this is the \ltx@ifundefined of ltxcmds.sty, which is loaded by +% hyperref.sty, but we need it before, and initial ltxcmds.sty +% as in TL2009/Debian had wrong definition. +\newcommand{\spx@ifundefined}[1]{% + \ifcsname #1\endcsname + \expandafter\ifx\csname #1\endcsname\relax + \expandafter\expandafter\expandafter\@firstoftwo + \else + \expandafter\expandafter\expandafter\@secondoftwo + \fi + \else + \expandafter\@firstoftwo + \fi +} + +\RequirePackage{graphicx} \@ifclassloaded{memoir}{}{\RequirePackage{fancyhdr}} +% for \text macro and \iffirstchoice@ conditional even if amsmath not loaded +\RequirePackage{amstext} \RequirePackage{textcomp} \RequirePackage{titlesec} \RequirePackage{tabulary} \RequirePackage{makeidx} % For framing code-blocks and warning type notices, and shadowing topics \RequirePackage{framed} -\newif\ifSphinx@inframed % flag set if we are in a framed environment -\RequirePackage{ifthen} % The xcolor package draws better fcolorboxes around verbatim code \IfFileExists{xcolor.sty}{ \RequirePackage{xcolor} @@ -26,12 +42,16 @@ } % For highlighted code. \RequirePackage{fancyvrb} +\fvset{fontsize=\small} % For table captions. \RequirePackage{threeparttable} % Handle footnotes in tables. \RequirePackage{footnote} \makesavenoteenv{tabulary} -% For floating figures in the text. +% For the H specifier. Do not \restylefloat{figure}, it breaks Sphinx code +% for allowing figures in tables. +\RequirePackage{float} +% For floating figures in the text. Better to load after float. \RequirePackage{wrapfig} % Separate paragraphs by space by default. \RequirePackage{parskip} @@ -61,20 +81,20 @@ \newcount\pdfoutput\pdfoutput=0 \fi -\RequirePackage{graphicx} - % for PDF output, use colors and maximal compression -\newif\ifsphinxpdfoutput\sphinxpdfoutputfalse -\ifx\pdfoutput\undefined\else\ifcase\pdfoutput +\newif\ifsphinxpdfoutput % used in \maketitle +\ifx\pdfoutput\undefined\else + \ifnum\pdfoutput=\z@ \let\py@NormalColor\relax \let\py@TitleColor\relax -\else + \else \sphinxpdfoutputtrue \input{pdfcolor} \def\py@NormalColor{\color[rgb]{0.0,0.0,0.0}} \def\py@TitleColor{\color{TitleColor}} \pdfcompresslevel=9 -\fi\fi + \fi +\fi % XeLaTeX can do colors, too \ifx\XeTeXrevision\undefined\else @@ -109,9 +129,10 @@ % Use this to set the font family for headers and other decor: \newcommand{\py@HeaderFamily}{\sffamily\bfseries} +\newcommand{\sphinxSetHeaderFamily}[1]{\renewcommand{\py@HeaderFamily}{#1}} % Redefine the 'normal' header/footer style when using "fancyhdr" package: -\@ifundefined{fancyhf}{}{ +\spx@ifundefined{fancyhf}{}{ % Use \pagestyle{normal} as the primary pagestyle for text. \fancypagestyle{normal}{ \fancyhf{} @@ -122,9 +143,8 @@ \renewcommand{\headrulewidth}{0.4pt} \renewcommand{\footrulewidth}{0.4pt} % define chaptermark with \@chappos when \@chappos is available for Japanese - \ifx\@chappos\undefined\else - \def\chaptermark##1{\markboth{\@chapapp\space\thechapter\space\@chappos\space ##1}{}} - \fi + \spx@ifundefined{@chappos}{} + {\def\chaptermark##1{\markboth{\@chapapp\space\thechapter\space\@chappos\space ##1}{}}} } % Update the plain style so we get the page number & footer line, % but not a chapter or section title. This is to keep the first @@ -138,28 +158,45 @@ } % Some custom font markup commands. -% -\newcommand{\strong}[1]{{\textbf{#1}}} -\newcommand{\code}[1]{\texttt{#1}} -\newcommand{\bfcode}[1]{\code{\bfseries#1}} -\newcommand{\email}[1]{\textsf{#1}} -\newcommand{\tablecontinued}[1]{\textsf{#1}} -\newcommand{\titleref}[1]{\emph{#1}} -\newcommand{\menuselection}[1]{\emph{#1}} -\newcommand{\accelerator}[1]{\underline{#1}} -\newcommand{\crossref}[1]{\emph{#1}} -\newcommand{\termref}[1]{\emph{#1}} +% *** the macros without \sphinx prefix are still defined at bottom of file *** +\newcommand{\sphinxstrong}[1]{{\textbf{#1}}} +% let \sphinxcode and \sphinxbfcode use straight quotes. \@noligs patched by upquote, +% but needs protection in "moving arguments" such as for captions. +% Use \scantokens to handle e.g. \item[{\sphinxcode{'fontenc'}}] +\DeclareRobustCommand{\sphinxcode}[1]{{\@noligs\scantokens{\texttt{#1}\relax}}} +\newcommand{\sphinxbfcode}[1]{\sphinxcode{\bfseries#1}} +\newcommand{\sphinxemail}[1]{\textsf{#1}} +\newcommand{\sphinxtablecontinued}[1]{\textsf{#1}} +\newcommand{\sphinxtitleref}[1]{\emph{#1}} +\newcommand{\sphinxmenuselection}[1]{\emph{#1}} +\newcommand{\sphinxaccelerator}[1]{\underline{#1}} +\newcommand{\sphinxcrossref}[1]{\emph{#1}} +\newcommand{\sphinxtermref}[1]{\emph{#1}} +% miscellaneous related to footnotes \newcommand*{\sphinxAtStartFootnote}{\mbox{ }} +% Support large numbered footnotes in minipage (cf. admonitions) +\def\thempfootnote{\arabic{mpfootnote}} -% Redefine the Verbatim environment to allow border and background colors -% and to handle the top caption in a non separable by pagebreak way. -% The original environment is still used for verbatims within tables. -\let\OriginalVerbatim=\Verbatim -\let\endOriginalVerbatim=\endVerbatim +% Preparations for sphinxVerbatim environment, which is a wrapper of fancyvrb +% Verbatim with framing allowing pagebreaks, with border and background colors +% and possibly also a top caption, non separable by pagebreak. -\newcommand\Sphinx@colorbox [2]{% -% #1 will be \fcolorbox or, for first part of frame: \Sphinx@fcolorbox +% For maintaining compatibility with Sphinx < 1.5, we define and use these +% when (unmodified) Verbatim will be needed. But Sphinx >= 1.5 does not modify +% original Verbatim anyhow. +\let\OriginalVerbatim \Verbatim +\let\endOriginalVerbatim\endVerbatim + +\newif\ifspx@inframed % flag set if we are already in a framed environment +\newdimen\sphinxverbatimsep \sphinxverbatimsep \fboxsep % default 3pt +\newdimen\sphinxverbatimborder\sphinxverbatimborder\fboxrule % default 0.4pt +\newif\ifsphinxverbatimwithframe \sphinxverbatimwithframetrue +\newif\ifsphinxverbatimwrapslines \sphinxverbatimwrapslinestrue +% if forced use of minipage encapsulation is needed (e.g. table cells) +\newif\ifsphinxverbatimwithminipage \sphinxverbatimwithminipagefalse +\newcommand\spx@colorbox [2]{% +% #1 will be \fcolorbox or, for first part of frame: \spx@fcolorbox % let the framing obey the current indentation (adapted from framed.sty's code). \hskip\@totalleftmargin \hskip-\fboxsep\hskip-\fboxrule @@ -168,100 +205,102 @@ \hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth } % use of \color@b@x here is compatible with both xcolor.sty and color.sty -\def\Sphinx@fcolorbox #1#2% - {\color@b@x {\fboxsep\z@\color{#1}\Sphinx@VerbatimFBox}{\color{#2}}}% +\def\spx@fcolorbox #1#2% + {\color@b@x {\fboxsep\z@\color{#1}\spx@VerbatimFBox}{\color{#2}}}% -% The title is specified from outside as macro \SphinxVerbatimTitle. -% \SphinxVerbatimTitle is reset to empty after each use of Verbatim. -\newcommand*\SphinxVerbatimTitle {} +% The title (caption) is specified from outside as macro \sphinxVerbatimTitle. +% \sphinxVerbatimTitle is reset to empty after each use of Verbatim. +\newcommand*\sphinxVerbatimTitle {} +% This box to typeset the caption before framed.sty multiple passes for framing. +\newbox\spx@VerbatimTitleBox % Holder macro for labels of literal blocks. Set-up by LaTeX writer. -\newcommand*\SphinxLiteralBlockLabel {} -\newcommand*\SphinxSetupCaptionForVerbatim [2] +\newcommand*\sphinxLiteralBlockLabel {} +\newcommand*\sphinxSetupCaptionForVerbatim [1] {% - \needspace{\literalblockneedspace}% -% insert a \label via \SphinxLiteralBlockLabel + \needspace{\sphinxliteralblockneedspace}% +% insert a \label via \sphinxLiteralBlockLabel % reset to normal the color for the literal block caption % the caption inserts \abovecaptionskip whitespace above itself (usually 10pt) % there is also \belowcaptionskip but it is usually zero, hence the \smallskip - \def\SphinxVerbatimTitle - {\py@NormalColor\captionof{#1}{\SphinxLiteralBlockLabel #2}\smallskip }% + \def\sphinxVerbatimTitle + {\py@NormalColor + \captionof{literalblock}{\sphinxLiteralBlockLabel #1}\smallskip }% } % Inspired and adapted from framed.sty's \CustomFBox with extra handling -% of a non separable by pagebreak caption, and controlled counter stepping. -\newif\ifSphinx@myfirstframedpass - -\long\def\Sphinx@VerbatimFBox#1{% +% of a non separable by pagebreak caption. +\long\def\spx@VerbatimFBox#1{% \leavevmode \begingroup - % framed.sty does some measuring but this macro adds possibly a caption - % use amsmath conditional to inhibit the caption counter stepping after - % first pass - \ifSphinx@myfirstframedpass\else\firstchoice@false\fi \setbox\@tempboxa\hbox{\kern\fboxsep{#1}\kern\fboxsep}% \hbox {\lower\dimexpr\fboxrule+\fboxsep+\dp\@tempboxa \hbox{% - \vbox{\ifx\SphinxVerbatimTitle\empty\else + \vbox{\ifvoid\spx@VerbatimTitleBox\else % add the caption in a centered way above possibly indented frame % hide its width from framed.sty's measuring step % note that the caption brings \abovecaptionskip top vertical space \moveright\dimexpr\fboxrule+.5\wd\@tempboxa - \hb@xt@\z@{\hss\begin{minipage}{\wd\@tempboxa}% - \SphinxVerbatimTitle - \end{minipage}\hss}\fi - \hrule\@height\fboxrule\relax - \hbox{\vrule\@width\fboxrule\relax + \hb@xt@\z@{\hss\unhcopy\spx@VerbatimTitleBox\hss}\fi + % draw frame border _latest_ to avoid pdf viewer issue + \kern\fboxrule + \hbox{\kern\fboxrule \vbox{\vskip\fboxsep\copy\@tempboxa\vskip\fboxsep}% - \vrule\@width\fboxrule\relax}% - \hrule\@height\fboxrule\relax}% + \kern-\wd\@tempboxa\kern-\fboxrule + \vrule\@width\fboxrule + \kern\wd\@tempboxa + \vrule\@width\fboxrule}% + \kern-\dimexpr\fboxsep+\ht\@tempboxa+\dp\@tempboxa + +\fboxsep+\fboxrule\relax + \hrule\@height\fboxrule + \kern\dimexpr\fboxsep+\ht\@tempboxa+\dp\@tempboxa+\fboxsep\relax + \hrule\@height\fboxrule}% }}% \endgroup - \global\Sphinx@myfirstframedpassfalse } % For linebreaks inside Verbatim environment from package fancyvrb. -\newbox\Sphinxcontinuationbox -\newbox\Sphinxvisiblespacebox +\newbox\sphinxcontinuationbox +\newbox\sphinxvisiblespacebox % These are user customizable e.g. from latex_elements's preamble key. % Use of \textvisiblespace for compatibility with XeTeX/LuaTeX/fontspec. -\newcommand*\Sphinxvisiblespace {\textcolor{red}{\textvisiblespace}} -\newcommand*\Sphinxcontinuationsymbol {\textcolor{red}{\llap{\tiny$\m@th\hookrightarrow$}}} -\newcommand*\Sphinxcontinuationindent {3ex } -\newcommand*\Sphinxafterbreak {\kern\Sphinxcontinuationindent\copy\Sphinxcontinuationbox} +\newcommand*\sphinxvisiblespace {\textcolor{red}{\textvisiblespace}} +\newcommand*\sphinxcontinuationsymbol {\textcolor{red}{\llap{\tiny$\m@th\hookrightarrow$}}} +\newcommand*\sphinxcontinuationindent {3ex } +\newcommand*\sphinxafterbreak {\kern\sphinxcontinuationindent\copy\sphinxcontinuationbox} % Take advantage of the already applied Pygments mark-up to insert % potential linebreaks for TeX processing. % {, <, #, %, $, ' and ": go to next line. % _, }, ^, &, >, - and ~: stay at end of broken line. % Use of \textquotesingle for straight quote. -\newcommand*\Sphinxbreaksatspecials {% - \def\PYGZus{\discretionary{\char`\_}{\Sphinxafterbreak}{\char`\_}}% - \def\PYGZob{\discretionary{}{\Sphinxafterbreak\char`\{}{\char`\{}}% - \def\PYGZcb{\discretionary{\char`\}}{\Sphinxafterbreak}{\char`\}}}% - \def\PYGZca{\discretionary{\char`\^}{\Sphinxafterbreak}{\char`\^}}% - \def\PYGZam{\discretionary{\char`\&}{\Sphinxafterbreak}{\char`\&}}% - \def\PYGZlt{\discretionary{}{\Sphinxafterbreak\char`\<}{\char`\<}}% - \def\PYGZgt{\discretionary{\char`\>}{\Sphinxafterbreak}{\char`\>}}% - \def\PYGZsh{\discretionary{}{\Sphinxafterbreak\char`\#}{\char`\#}}% - \def\PYGZpc{\discretionary{}{\Sphinxafterbreak\char`\%}{\char`\%}}% - \def\PYGZdl{\discretionary{}{\Sphinxafterbreak\char`\$}{\char`\$}}% - \def\PYGZhy{\discretionary{\char`\-}{\Sphinxafterbreak}{\char`\-}}% - \def\PYGZsq{\discretionary{}{\Sphinxafterbreak\textquotesingle}{\textquotesingle}}% - \def\PYGZdq{\discretionary{}{\Sphinxafterbreak\char`\"}{\char`\"}}% - \def\PYGZti{\discretionary{\char`\~}{\Sphinxafterbreak}{\char`\~}}% +\newcommand*\sphinxbreaksatspecials {% + \def\PYGZus{\discretionary{\char`\_}{\sphinxafterbreak}{\char`\_}}% + \def\PYGZob{\discretionary{}{\sphinxafterbreak\char`\{}{\char`\{}}% + \def\PYGZcb{\discretionary{\char`\}}{\sphinxafterbreak}{\char`\}}}% + \def\PYGZca{\discretionary{\char`\^}{\sphinxafterbreak}{\char`\^}}% + \def\PYGZam{\discretionary{\char`\&}{\sphinxafterbreak}{\char`\&}}% + \def\PYGZlt{\discretionary{}{\sphinxafterbreak\char`\<}{\char`\<}}% + \def\PYGZgt{\discretionary{\char`\>}{\sphinxafterbreak}{\char`\>}}% + \def\PYGZsh{\discretionary{}{\sphinxafterbreak\char`\#}{\char`\#}}% + \def\PYGZpc{\discretionary{}{\sphinxafterbreak\char`\%}{\char`\%}}% + \def\PYGZdl{\discretionary{}{\sphinxafterbreak\char`\$}{\char`\$}}% + \def\PYGZhy{\discretionary{\char`\-}{\sphinxafterbreak}{\char`\-}}% + \def\PYGZsq{\discretionary{}{\sphinxafterbreak\textquotesingle}{\textquotesingle}}% + \def\PYGZdq{\discretionary{}{\sphinxafterbreak\char`\"}{\char`\"}}% + \def\PYGZti{\discretionary{\char`\~}{\sphinxafterbreak}{\char`\~}}% } % Some characters . , ; ? ! / are not pygmentized. % This macro makes them "active" and they will insert potential linebreaks -\newcommand*\Sphinxbreaksatpunct {% - \lccode`\~`\.\lowercase{\def~}{\discretionary{\char`\.}{\Sphinxafterbreak}{\char`\.}}% - \lccode`\~`\,\lowercase{\def~}{\discretionary{\char`\,}{\Sphinxafterbreak}{\char`\,}}% - \lccode`\~`\;\lowercase{\def~}{\discretionary{\char`\;}{\Sphinxafterbreak}{\char`\;}}% - \lccode`\~`\:\lowercase{\def~}{\discretionary{\char`\:}{\Sphinxafterbreak}{\char`\:}}% - \lccode`\~`\?\lowercase{\def~}{\discretionary{\char`\?}{\Sphinxafterbreak}{\char`\?}}% - \lccode`\~`\!\lowercase{\def~}{\discretionary{\char`\!}{\Sphinxafterbreak}{\char`\!}}% - \lccode`\~`\/\lowercase{\def~}{\discretionary{\char`\/}{\Sphinxafterbreak}{\char`\/}}% +\newcommand*\sphinxbreaksatpunct {% + \lccode`\~`\.\lowercase{\def~}{\discretionary{\char`\.}{\sphinxafterbreak}{\char`\.}}% + \lccode`\~`\,\lowercase{\def~}{\discretionary{\char`\,}{\sphinxafterbreak}{\char`\,}}% + \lccode`\~`\;\lowercase{\def~}{\discretionary{\char`\;}{\sphinxafterbreak}{\char`\;}}% + \lccode`\~`\:\lowercase{\def~}{\discretionary{\char`\:}{\sphinxafterbreak}{\char`\:}}% + \lccode`\~`\?\lowercase{\def~}{\discretionary{\char`\?}{\sphinxafterbreak}{\char`\?}}% + \lccode`\~`\!\lowercase{\def~}{\discretionary{\char`\!}{\sphinxafterbreak}{\char`\!}}% + \lccode`\~`\/\lowercase{\def~}{\discretionary{\char`\/}{\sphinxafterbreak}{\char`\/}}% \catcode`\.\active \catcode`\,\active \catcode`\;\active @@ -272,42 +311,55 @@ \lccode`\~`\~ } -\renewcommand{\Verbatim}[1][1]{% +% needed to create wrapper environments of fancyvrb's Verbatim +\newcommand*{\sphinxVerbatimEnvironment}{\gdef\FV@EnvironName{sphinxVerbatim}} +% Sphinx <1.5 optional argument was in fact mandatory. It is now really +% optional and handled by original Verbatim. +\newenvironment{sphinxVerbatim}{% % quit horizontal mode if we are still in a paragraph \par % list starts new par, but we don't want it to be set apart vertically \parskip\z@skip % first, let's check if there is a caption - \ifx\SphinxVerbatimTitle\empty + \ifx\sphinxVerbatimTitle\empty \addvspace\z@% counteract possible previous negative skip (French lists!) \smallskip % there was no caption. Check if nevertheless a label was set. - \ifx\SphinxLiteralBlockLabel\empty\else + \ifx\sphinxLiteralBlockLabel\empty\else % we require some space to be sure hyperlink target from \phantomsection % will not be separated from upcoming verbatim by a page break - \needspace{\literalblockwithoutcaptionneedspace}% - \phantomsection\SphinxLiteralBlockLabel + \needspace{\sphinxliteralblockwithoutcaptionneedspace}% + \phantomsection\sphinxLiteralBlockLabel \fi + \setbox\spx@VerbatimTitleBox\box\voidb@x + \else + % non-empty \sphinxVerbatimTitle has label inside it (in case there is one) + \setbox\spx@VerbatimTitleBox + \hbox{\begin{minipage}{\linewidth}% + \sphinxVerbatimTitle + \end{minipage}}% \fi - % non-empty \SphinxVerbatimTitle has label inside it (in case there is one) + \fboxsep\sphinxverbatimsep \fboxrule\sphinxverbatimborder + % setting borderwidth to zero is simplest for no-frame effect with same pagebreaks + \ifsphinxverbatimwithframe\else\fboxrule\z@\fi % Customize framed.sty \MakeFramed to glue caption to literal block - \global\Sphinx@myfirstframedpasstrue - % via \Sphinx@fcolorbox, will use \Sphinx@VerbatimFBox which inserts title - \def\FrameCommand {\Sphinx@colorbox\Sphinx@fcolorbox }% + % via \spx@fcolorbox, will use \spx@VerbatimFBox which inserts title + \def\FrameCommand {\spx@colorbox\spx@fcolorbox }% \let\FirstFrameCommand\FrameCommand % for mid pages and last page portion of (long) split frame: - \def\MidFrameCommand{\Sphinx@colorbox\fcolorbox }% + \def\MidFrameCommand{\spx@colorbox\fcolorbox }% \let\LastFrameCommand\MidFrameCommand + \ifsphinxverbatimwrapslines % fancyvrb's Verbatim puts each input line in (unbreakable) horizontal boxes. % This customization wraps each line from the input in a \vtop, thus % allowing it to wrap and display on two or more lines in the latex output. % - The codeline counter will be increased only once. % - The wrapped material will not break across pages, it is impossible % to achieve this without extensive rewrite of fancyvrb. - % - The (not used in Sphinx) obeytabs option to Verbatim is + % - The (not used in sphinx) obeytabs option to Verbatim is % broken by this change (showtabs and tabspace work). - \sbox\Sphinxcontinuationbox {\Sphinxcontinuationsymbol}% - \sbox\Sphinxvisiblespacebox {\FV@SetupFont\Sphinxvisiblespace}% + \sbox\sphinxcontinuationbox {\sphinxcontinuationsymbol}% + \sbox\sphinxvisiblespacebox {\FV@SetupFont\sphinxvisiblespace}% \def\FancyVerbFormatLine ##1{\hsize\linewidth \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@ \doublehyphendemerits\z@\finalhyphendemerits\z@ @@ -318,74 +370,100 @@ % Stretch/shrink are however usually zero for typewriter font. \def\FV@Space {% \nobreak\hskip\z@ plus\fontdimen3\font minus\fontdimen4\font - \discretionary{\copy\Sphinxvisiblespacebox}{\Sphinxafterbreak} + \discretionary{\copy\sphinxvisiblespacebox}{\sphinxafterbreak} {\kern\fontdimen2\font}% }% % Allow breaks at special characters using \PYG... macros. - \Sphinxbreaksatspecials + \sphinxbreaksatspecials + % Breaks at punctuation characters . , ; ? ! and / (needs catcode activation) + \def\FancyVerbCodes{\sphinxbreaksatpunct}% + \fi % end of conditional code for wrapping long code lines + % go around fancyvrb's check of \@currenvir + \let\VerbatimEnvironment\sphinxVerbatimEnvironment + % go around fancyvrb's check of current list depth + \def\@toodeep {\advance\@listdepth\@ne}% % The list environment is needed to control perfectly the vertical space. % Note: \OuterFrameSep used by framed.sty is later set to \topsep hence 0pt. % - if caption: vertical space above caption = (\abovecaptionskip + D) with % D = \baselineskip-\FrameHeightAdjust, and then \smallskip above frame. % - if no caption: (\smallskip + D) above frame. By default D=6pt. - \list{}{% - \setlength\parskip{0pt}% - \setlength\itemsep{0ex}% - \setlength\topsep{0ex}% - \setlength\parsep{0pt}% let's not forget this one! - \setlength\partopsep{0pt}% - \setlength\leftmargin{0pt}% - }% - \item - % use a minipage if we are already inside a framed environment - \relax\ifSphinx@inframed\noindent\begin{\minipage}{\linewidth}\fi + % Use trivlist rather than list to avoid possible "too deeply nested" error. + \itemsep \z@skip + \topsep \z@skip + \partopsep \z@skip% trivlist will set \parsep to \parskip = zero (see above) + % \leftmargin will be set to zero by trivlist + \rightmargin\z@ + \parindent \z@% becomes \itemindent. Default zero, but perhaps overwritten. + \trivlist\item\relax + \ifsphinxverbatimwithminipage\spx@inframedtrue\fi + % use a minipage if we are already inside a framed environment + \ifspx@inframed\noindent\begin{minipage}{\linewidth}\fi \MakeFramed {% adapted over from framed.sty's snugshade environment - \advance\hsize-\width\@totalleftmargin\z@\linewidth\hsize - \@setminipage }% - \small + \advance\hsize-\width\@totalleftmargin\z@\linewidth\hsize\@setminipage + }% % For grid placement from \strut's in \FancyVerbFormatLine \lineskip\z@skip - % Breaks at punctuation characters . , ; ? ! and / need catcode=\active - \OriginalVerbatim[#1,codes*=\Sphinxbreaksatpunct]% + % will fetch its optional arguments if any + \OriginalVerbatim } -\renewcommand{\endVerbatim}{% +{% \endOriginalVerbatim - \par\unskip\@minipagefalse\endMakeFramed - \ifSphinx@inframed\end{minipage}\fi - \endlist - % LaTeX environments always revert local changes on exit, here e.g. \parskip + \par\unskip\@minipagefalse\endMakeFramed % from framed.sty snugshade + \ifspx@inframed\end{minipage}\fi + \endtrivlist } +\newenvironment {sphinxVerbatimNoFrame} + {\sphinxverbatimwithframefalse + % needed for fancyvrb as literal code will end in \end{sphinxVerbatimNoFrame} + \def\sphinxVerbatimEnvironment{\gdef\FV@EnvironName{sphinxVerbatimNoFrame}}% + \begin{sphinxVerbatim}} + {\end{sphinxVerbatim}} +\newenvironment {sphinxVerbatimintable} + {% don't use a frame if in a table cell + \sphinxverbatimwithframefalse + \sphinxverbatimwithminipagetrue + % counteract longtable redefinition of caption + \let\caption\sphinxfigcaption + % reduce above caption space if in a table cell + \abovecaptionskip\smallskipamount + \def\sphinxVerbatimEnvironment{\gdef\FV@EnvironName{sphinxVerbatimintable}}% + \begin{sphinxVerbatim}} + {\end{sphinxVerbatim}} % define macro to frame contents and add shadow on right and bottom -\def\Sphinx@shadowsep {5\p@} % \p@ means "pt " -\def\Sphinx@shadowsize {4\p@} -\def\Sphinx@shadowrule {\fboxrule} -\long\def\Sphinx@ShadowFBox#1{% +% use public names for customizable lengths +\newlength\sphinxshadowsep \setlength\sphinxshadowsep {5pt} +\newlength\sphinxshadowsize \setlength\sphinxshadowsize {4pt} +\newlength\sphinxshadowrule +% this uses \fboxrule value at loading time of sphinx.sty (0.4pt normally) +\setlength\sphinxshadowrule {\fboxrule} + +\long\def\spx@ShadowFBox#1{% \leavevmode\begingroup % first we frame the box #1 \setbox\@tempboxa - \hbox{\vrule\@width\Sphinx@shadowrule - \vbox{\hrule\@height\Sphinx@shadowrule - \kern\Sphinx@shadowsep - \hbox{\kern\Sphinx@shadowsep #1\kern\Sphinx@shadowsep}% - \kern\Sphinx@shadowsep - \hrule\@height\Sphinx@shadowrule}% - \vrule\@width\Sphinx@shadowrule}% + \hbox{\vrule\@width\sphinxshadowrule + \vbox{\hrule\@height\sphinxshadowrule + \kern\sphinxshadowsep + \hbox{\kern\sphinxshadowsep #1\kern\sphinxshadowsep}% + \kern\sphinxshadowsep + \hrule\@height\sphinxshadowrule}% + \vrule\@width\sphinxshadowrule}% % Now we add the shadow, like \shadowbox from fancybox.sty would do - \dimen@\dimexpr.5\Sphinx@shadowrule+\Sphinx@shadowsize\relax + \dimen@\dimexpr.5\sphinxshadowrule+\sphinxshadowsize\relax \hbox{\vbox{\offinterlineskip - \hbox{\copy\@tempboxa\kern-.5\Sphinx@shadowrule + \hbox{\copy\@tempboxa\kern-.5\sphinxshadowrule % add shadow on right side - \lower\Sphinx@shadowsize + \lower\sphinxshadowsize \hbox{\vrule\@height\ht\@tempboxa \@width\dimen@}% }% \kern-\dimen@ % shift back vertically to bottom of frame % and add shadow at bottom - \moveright\Sphinx@shadowsize + \moveright\sphinxshadowsize \vbox{\hrule\@width\wd\@tempboxa \@height\dimen@}% }% % move left by the size of right shadow so shadow adds no width - \kern-\Sphinx@shadowsize + \kern-\sphinxshadowsize }% \endgroup } @@ -394,10 +472,10 @@ % works well inside Lists and Quote-like environments % produced by ``topic'' directive (or local contents) % could nest if LaTeX writer authorized it -\newenvironment{SphinxShadowBox} - {\def\FrameCommand {\Sphinx@ShadowFBox }% +\newenvironment{sphinxShadowBox} + {\def\FrameCommand {\spx@ShadowFBox }% % configure framed.sty not to add extra vertical spacing - \OuterFrameSep \z@skip + \spx@ifundefined{OuterFrameSep}{}{\OuterFrameSep\z@skip}% % the \trivlist will add the vertical spacing on top and bottom which is % typical of center environment as used in Sphinx <= 1.4.1 % the \noindent has the effet of an extra blank line on top, to @@ -406,8 +484,8 @@ \def\FrameHeightAdjust {\baselineskip}% \trivlist\item\noindent % use a minipage if we are already inside a framed environment - \ifSphinx@inframed\begin{minipage}{\linewidth}\fi - \MakeFramed {\Sphinx@inframedtrue + \ifspx@inframed\begin{minipage}{\linewidth}\fi + \MakeFramed {\spx@inframedtrue % framed.sty puts into "\width" the added width (=2shadowsep+2shadowrule) % adjust \hsize to what the contents must use \advance\hsize-\width @@ -433,7 +511,7 @@ \fi \@minipagefalse \endMakeFramed - \ifSphinx@inframed\end{minipage}\fi + \ifspx@inframed\end{minipage}\fi \endtrivlist } @@ -474,25 +552,25 @@ }{\end{list}} % \optional is used for ``[, arg]``, i.e. desc_optional nodes. -\newcommand{\optional}[1]{% +\newcommand{\sphinxoptional}[1]{% {\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}} \newlength{\py@argswidth} \newcommand{\py@sigparams}[2]{% - \parbox[t]{\py@argswidth}{#1\code{)}#2}} + \parbox[t]{\py@argswidth}{#1\sphinxcode{)}#2}} \newcommand{\pysigline}[1]{\item[#1]\nopagebreak} \newcommand{\pysiglinewithargsret}[3]{% - \settowidth{\py@argswidth}{#1\code{(}}% + \settowidth{\py@argswidth}{#1\sphinxcode{(}}% \addtolength{\py@argswidth}{-2\py@argswidth}% \addtolength{\py@argswidth}{\linewidth}% - \item[#1\code{(}\py@sigparams{#2}{#3}]} + \item[#1\sphinxcode{(}\py@sigparams{#2}{#3}]} % Production lists % \newenvironment{productionlist}{ -% \def\optional##1{{\Large[}##1{\Large]}} - \def\production##1##2{\\\code{##1}&::=&\code{##2}} - \def\productioncont##1{\\& &\code{##1}} +% \def\sphinxoptional##1{{\Large[}##1{\Large]}} + \def\production##1##2{\\\sphinxcode{##1}&::=&\sphinxcode{##2}} + \def\productioncont##1{\\& &\sphinxcode{##1}} \parindent=2em \indent \setlength{\LTpre}{0pt} @@ -503,29 +581,79 @@ } % Notices / Admonitions -% +% Some are quite plain +\newenvironment{sphinxlightbox}{% + \par\allowbreak + \noindent{\color{spx@notice@bordercolor}% + \rule{\linewidth}{\spx@notice@border}}\par\nobreak + {\parskip\z@skip\noindent}% + } + {% + \par + % counteract previous possible negative skip (French lists!): + % (we can't cancel that any earlier \vskip introduced a potential pagebreak) + \ifdim\lastskip<\z@\vskip-\lastskip\fi + \nobreak\vbox{\noindent\kern\@totalleftmargin + {\color{spx@notice@bordercolor}% + \rule[\dimexpr.4\baselineskip-\spx@notice@border\relax] + {\linewidth}{\spx@notice@border}}\hss}\allowbreak + }% end of sphinxlightbox environment definition +% may be renewenvironment'd by user for complete customization +\newenvironment{sphinxnote}[1] + {\begin{sphinxlightbox}\sphinxstrong{#1} }{\end{sphinxlightbox}} +\newenvironment{sphinxhint}[1] + {\begin{sphinxlightbox}\sphinxstrong{#1} }{\end{sphinxlightbox}} +\newenvironment{sphinximportant}[1] + {\begin{sphinxlightbox}\sphinxstrong{#1} }{\end{sphinxlightbox}} +\newenvironment{sphinxtip}[1] + {\begin{sphinxlightbox}\sphinxstrong{#1} }{\end{sphinxlightbox}} +% or user may just customize the bordercolor and the border width +% re-use \definecolor if change needed, and \renewcommand for rule width +\definecolor{sphinxnotebordercolor}{rgb}{0,0,0} +\definecolor{sphinxhintbordercolor}{rgb}{0,0,0} +\definecolor{sphinximportantbordercolor}{rgb}{0,0,0} +\definecolor{sphinxtipbordercolor}{rgb}{0,0,0} +\newcommand{\sphinxnoteborder}{0.5pt} +\newcommand{\sphinxhintborder}{0.5pt} +\newcommand{\sphinximportantborder}{0.5pt} +\newcommand{\sphinxtipborder}{0.5pt} +% these are needed for common handling by notice environment of lightbox +% and heavybox but they are currently not used by lightbox environment +\definecolor{sphinxnotebgcolor}{rgb}{1,1,1} +\definecolor{sphinxhintbgcolor}{rgb}{1,1,1} +\definecolor{sphinximportantbgcolor}{rgb}{1,1,1} +\definecolor{sphinxtipbgcolor}{rgb}{1,1,1} + +% Others get more distinction +\newdimen\spx@notice@border % Code adapted from framed.sty's "snugshade" environment. % Nesting works (inner frames do not allow page breaks). -\newcommand{\py@heavybox}{\par - \setlength{\FrameRule}{\p@}% 1pt +\newenvironment{sphinxheavybox}{\par + \setlength{\FrameRule}{\spx@notice@border}% \setlength{\FrameSep}{\dimexpr.6\baselineskip-\FrameRule\relax} % configure framed.sty's parameters to obtain same vertical spacing % as for "light" boxes. We need for this to manually insert parskip glue and % revert a skip done by framed before the frame. - \setlength{\OuterFrameSep}{0pt} + \spx@ifundefined{OuterFrameSep}{}{\OuterFrameSep\z@skip}% \vspace{\FrameHeightAdjust} % copied/adapted from framed.sty's snugshade \def\FrameCommand##1{\hskip\@totalleftmargin - \fboxsep\FrameSep \fboxrule\FrameRule\fbox{##1}% + \fboxsep\FrameSep \fboxrule\FrameRule + \fcolorbox{spx@notice@bordercolor}{spx@notice@bgcolor}{##1}% \hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth}% % use a minipage if we are already inside a framed environment - \ifSphinx@inframed + \ifspx@inframed \noindent\begin{minipage}{\linewidth} \else - \vspace{\parskip} + % handle case where notice is first thing in a list item (or is quoted) + \if@inlabel + \noindent\par\vspace{-\baselineskip} + \else + \vspace{\parskip} + \fi \fi - \MakeFramed {\Sphinx@inframedtrue + \MakeFramed {\spx@inframedtrue \advance\hsize-\width \@totalleftmargin\z@ \linewidth\hsize % minipage initialization copied from LaTeX source code. \@pboxswfalse @@ -536,58 +664,66 @@ \@minipagerestore \@setminipage }% } -\newcommand{\py@endheavybox}{% + {% \par\unskip % handles footnotes \ifvoid\@mpfootins\else \vskip\skip\@mpfootins\normalcolor\footnoterule\unvbox\@mpfootins \fi - \@minipagefalse\endMakeFramed - \ifSphinx@inframed\end{minipage}\fi + \@minipagefalse + \endMakeFramed + \ifspx@inframed\end{minipage}\fi % arrange for similar spacing below frame as for "light" boxes. \vskip .4\baselineskip - } + }% end of sphinxheavybox environment definition +% may be renewenvironment'd by user for complete customization +\newenvironment{sphinxwarning}[1] + {\begin{sphinxheavybox}\sphinxstrong{#1} }{\end{sphinxheavybox}} +\newenvironment{sphinxcaution}[1] + {\begin{sphinxheavybox}\sphinxstrong{#1} }{\end{sphinxheavybox}} +\newenvironment{sphinxattention}[1] + {\begin{sphinxheavybox}\sphinxstrong{#1} }{\end{sphinxheavybox}} +\newenvironment{sphinxdanger}[1] + {\begin{sphinxheavybox}\sphinxstrong{#1} }{\end{sphinxheavybox}} +\newenvironment{sphinxerror}[1] + {\begin{sphinxheavybox}\sphinxstrong{#1} }{\end{sphinxheavybox}} +% or just re-do \definecolor for colours, \renewcommand for frame width +\definecolor{sphinxwarningbordercolor}{rgb}{0,0,0} +\definecolor{sphinxcautionbordercolor}{rgb}{0,0,0} +\definecolor{sphinxattentionbordercolor}{rgb}{0,0,0} +\definecolor{sphinxdangerbordercolor}{rgb}{0,0,0} +\definecolor{sphinxerrorbordercolor}{rgb}{0,0,0} +\definecolor{sphinxwarningbgcolor}{rgb}{1,1,1} +\definecolor{sphinxcautionbgcolor}{rgb}{1,1,1} +\definecolor{sphinxattentionbgcolor}{rgb}{1,1,1} +\definecolor{sphinxdangerbgcolor}{rgb}{1,1,1} +\definecolor{sphinxerrorbgcolor}{rgb}{1,1,1} +\newcommand{\sphinxwarningborder}{1pt} +\newcommand{\sphinxcautionborder}{1pt} +\newcommand{\sphinxattentionborder}{1pt} +\newcommand{\sphinxdangerborder}{1pt} +\newcommand{\sphinxerrorborder}{1pt} -\newcommand{\py@lightbox}{% - \par\allowbreak - \noindent\rule{\linewidth}{0.5pt}\par\nobreak - {\parskip\z@skip\noindent}% - } -\newcommand{\py@endlightbox}{% - \par - % counteract previous possible negative skip (French lists!): - % (we can't cancel that any earlier \vskip introduced a potential pagebreak) - \ifdim\lastskip<\z@\vskip-\lastskip\fi - \nobreak\vbox{\noindent\rule[.4\baselineskip]{\linewidth}{0.5pt}}\allowbreak - } +% the \colorlet of xcolor (if at all loaded) is overkill for our use case +\newcommand{\sphinxcolorlet}[2] + {\expandafter\let\csname\@backslashchar color@#1\expandafter\endcsname + \csname\@backslashchar color@#2\endcsname } -% Some are quite plain: -\newcommand{\py@noticestart@note}{\py@lightbox} -\newcommand{\py@noticeend@note}{\py@endlightbox} -\newcommand{\py@noticestart@hint}{\py@lightbox} -\newcommand{\py@noticeend@hint}{\py@endlightbox} -\newcommand{\py@noticestart@important}{\py@lightbox} -\newcommand{\py@noticeend@important}{\py@endlightbox} -\newcommand{\py@noticestart@tip}{\py@lightbox} -\newcommand{\py@noticeend@tip}{\py@endlightbox} - -% Others gets more visible distinction: -\newcommand{\py@noticestart@warning}{\py@heavybox} -\newcommand{\py@noticeend@warning}{\py@endheavybox} -\newcommand{\py@noticestart@caution}{\py@heavybox} -\newcommand{\py@noticeend@caution}{\py@endheavybox} -\newcommand{\py@noticestart@attention}{\py@heavybox} -\newcommand{\py@noticeend@attention}{\py@endheavybox} -\newcommand{\py@noticestart@danger}{\py@heavybox} -\newcommand{\py@noticeend@danger}{\py@endheavybox} -\newcommand{\py@noticestart@error}{\py@heavybox} -\newcommand{\py@noticeend@error}{\py@endheavybox} - -\newenvironment{notice}[2]{ - \def\py@noticetype{#1} - \csname py@noticestart@#1\endcsname - \strong{#2} -}{\csname py@noticeend@\py@noticetype\endcsname} +% the main dispatch for all types of notices +\newenvironment{sphinxadmonition}{\begin{notice}}{\end{notice}} +% use of ``notice'' is for backwards compatibility and will be removed in +% future release; sphinxadmonition environment will be defined directly. +\newenvironment{notice}[2]{% #1=type, #2=heading + % can't use #1 directly in definition of end part + \def\spx@noticetype {#1}% + % set parameters of heavybox/lightbox + \sphinxcolorlet{spx@notice@bordercolor}{sphinx#1bordercolor}% + \sphinxcolorlet{spx@notice@bgcolor}{sphinx#1bgcolor}% + \setlength\spx@notice@border {\dimexpr\csname sphinx#1border\endcsname\relax}% + % start specific environment, passing the heading as argument + \begin{sphinx#1}{#2}} + % in end part, need to go around a LaTeX's "feature" + {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} % Allow the release number to be specified independently of the % \date{}. This allows the date to reflect the document's date and @@ -616,7 +752,7 @@ % This sets up the fancy chapter headings that make the documents look % at least a little better than the usual LaTeX output. % -\@ifundefined{ChTitleVar}{}{ +\spx@ifundefined{ChTitleVar}{}{ \ChNameVar{\raggedleft\normalsize\py@HeaderFamily} \ChNumVar{\raggedleft \bfseries\Large\py@HeaderFamily} \ChTitleVar{\raggedleft \textrm{\Huge\py@HeaderFamily}} @@ -656,7 +792,7 @@ % The following is stuff copied from docutils' latex writer. % -\newcommand{\optionlistlabel}[1]{\bf #1 \hfill} +\newcommand{\optionlistlabel}[1]{\normalfont\bfseries #1 \hfill}% \bf deprecated \newenvironment{optionlist}[1] {\begin{list}{} {\setlength{\labelwidth}{#1} @@ -678,26 +814,29 @@ \raggedright} {\end{list}} -% Redefine includgraphics for avoiding images larger than the screen size -% If the size is not specified. +% Re-define \includegraphics to resize images larger than the line width +% if the size is not specified. +% Warning: future version of Sphinx will not modify original \includegraphics, +% Below custom code will be direct definition of \sphinxincludegraphics, with +% \py@Oldincludegraphics replaced by direct use of original \includegraphics. \let\py@Oldincludegraphics\includegraphics - -\newbox\image@box% -\newdimen\image@width% -\renewcommand\includegraphics[2][\@empty]{% - \ifx#1\@empty% - \setbox\image@box=\hbox{\py@Oldincludegraphics{#2}}% - \image@width\wd\image@box% - \ifdim \image@width>\linewidth% - \setbox\image@box=\hbox{\py@Oldincludegraphics[width=\linewidth]{#2}}% - \box\image@box% - \else% - \py@Oldincludegraphics{#2}% - \fi% - \else% +\newbox\spx@image@box +\renewcommand*{\includegraphics}[2][\@empty]{% + \ifx\@empty #1% attention, #1 could be bb.., bad if first after \ifx + \setbox\spx@image@box=\hbox{\py@Oldincludegraphics{#2}}% + \ifdim \wd\spx@image@box>\linewidth + \py@Oldincludegraphics[width=\linewidth]{#2}% + \else + \leavevmode\box\spx@image@box + \fi + \else \py@Oldincludegraphics[#1]{#2}% - \fi% + \fi } +% Writer will put \sphinxincludegraphics in LaTeX source, and with this, +% documents which used their own modified \includegraphics will compile +% as before. But see warning above. +\newcommand*{\sphinxincludegraphics}{\includegraphics} % to make pdf with correct encoded bookmarks in Japanese % this should precede the hyperref package @@ -745,12 +884,12 @@ \fi% } -\providecommand*{\DUprovidelength}[2]{ - \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{} +\providecommand*{\DUprovidelength}[2]{% + \ifdefined#1\else\newlength{#1}\setlength{#1}{#2}\fi } \DUprovidelength{\DUlineblockindent}{2.5em} -\ifthenelse{\isundefined{\DUlineblock}}{ +\ifdefined\DUlineblock\else \newenvironment{DUlineblock}[1]{% \list{}{\setlength{\partopsep}{\parskip} \addtolength{\partopsep}{\baselineskip} @@ -761,8 +900,7 @@ \raggedright } {\endlist} -}{} - +\fi % From footmisc.sty: allows footnotes in titles \let\FN@sf@@footnote\footnote @@ -813,18 +951,79 @@ } \fi -% Define literal-block environment -\RequirePackage{newfloat} -\DeclareFloatingEnvironment{literal-block} -\ifx\thechapter\undefined - \SetupFloatingEnvironment{literal-block}{within=section,placement=h} -\else - \SetupFloatingEnvironment{literal-block}{within=chapter,placement=h} -\fi -\SetupFloatingEnvironment{literal-block}{name=List} +% for captions of literal blocks +\newcounter{literalblock} +\spx@ifundefined{c@chapter} + {\@addtoreset{literalblock}{section} + \def\theliteralblock {\ifnum\c@section>\z@ \thesection.\fi\arabic{literalblock}} + \def\theHliteralblock {\theHsection.\arabic{literalblock}}} + {\@addtoreset{literalblock}{chapter} + \def\theliteralblock {\ifnum\c@chapter>\z@ \thechapter.\fi\arabic{literalblock}} + \def\theHliteralblock {\theHchapter.\arabic{literalblock}}} +% at start of caption title +\newcommand*{\fnum@literalblock}{\literalblockname\nobreakspace\theliteralblock} +% this will be overwritten in document preamble by Babel translation +\newcommand*{\literalblockname}{Listing } +% file extension needed for \caption's good functioning, the file is created +% only if a \listof{literalblock}{foo} command is encountered, which is +% analogous to \listoffigures, but for the code listings (foo = chosen title.) +\newcommand*{\ext@literalblock}{lol} + % control caption around literal-block \RequirePackage{capt-of} \RequirePackage{needspace} % if the left page space is less than \literalblockneedspace, insert page-break -\newcommand{\literalblockneedspace}{5\baselineskip} -\newcommand{\literalblockwithoutcaptionneedspace}{1.5\baselineskip} +\newcommand{\sphinxliteralblockneedspace}{5\baselineskip} +\newcommand{\sphinxliteralblockwithoutcaptionneedspace}{1.5\baselineskip} + +% figure in table +\newenvironment{sphinxfigure-in-table}[1][\linewidth]{% + \def\@captype{figure}% + \begin{minipage}{#1}% +}{\end{minipage}} +% store original \caption macro for use with figures in longtable and tabulary +\AtBeginDocument{\let\spx@originalcaption\caption} +\newcommand*\sphinxfigcaption + {\ifx\equation$%$% this is trick to identify tabulary first pass + \firstchoice@false\else\firstchoice@true\fi + \spx@originalcaption } + +% by default, also define macros with the no-prefix names +\ifsphinxKeepOldNames + \typeout{** (sphinx) defining (legacy) text style macros without \string\sphinx\space prefix} + \typeout{** if clashes with packages, set latex_keep_old_macro_names=False in conf.py} + \@for\@tempa:=strong,bfcode,email,tablecontinued,titleref,% + menuselection,accelerator,crossref,termref,optional\do +{% first, check if command with no prefix already exists + \expandafter\newcommand\csname\@tempa\endcsname{}% + % if no error give it the meaning defined so far with \sphinx prefix + \expandafter\let\csname\@tempa\expandafter\endcsname + \csname sphinx\@tempa\endcsname + % redefine the \sphinx prefixed macro to expand to non-prefixed one + \expandafter\def\csname sphinx\@tempa\expandafter\endcsname + \expandafter{\csname\@tempa\endcsname}% +} + % robustified case needs special treatment + \newcommand\code{}\let\code\relax + \DeclareRobustCommand{\code}[1]{{\@noligs\scantokens{\texttt{#1}\relax}}} + \def\sphinxcode{\code}% +\fi + +% additional customizable styling +\newcommand*{\sphinxstyleindexentry}{\texttt} +\newcommand{\sphinxstyleindexextra}[1]{ \emph{(#1)}} +\newcommand*{\sphinxstyleindexpageref}{, \pageref} +\newcommand{\sphinxstyletopictitle}[1]{\textbf{#1}\par\medskip} +\let\sphinxstylesidebartitle\sphinxstyletopictitle +\newcommand*{\sphinxstyleothertitle}{\textbf} +\newcommand{\sphinxstylesidebarsubtitle}[1]{~\\\textbf{#1} \smallskip} +\newcommand*{\sphinxstylethead}{\textsf} +\newcommand*{\sphinxstyleemphasis}{\emph} +\newcommand{\sphinxstyleliteralemphasis}[1]{\emph{\texttt{#1}}} +\newcommand*{\sphinxstylestrong}{\textbf} +\newcommand{\sphinxstyleliteralstrong}[1]{\textbf{\texttt{#1}}} +\newcommand*{\sphinxstyleabbreviation}{\textsc} +\newcommand*{\sphinxstyleliteralintitle}{\texttt} + +% stylesheet for highlighting with pygments +\RequirePackage{sphinxhighlight} diff --git a/sphinx/texinputs/sphinxhowto.cls b/sphinx/texinputs/sphinxhowto.cls index 8607ef8c4..8d5c59232 100644 --- a/sphinx/texinputs/sphinxhowto.cls +++ b/sphinx/texinputs/sphinxhowto.cls @@ -35,30 +35,31 @@ % Change the title page to look a bit better, and fit in with the fncychap % ``Bjarne'' style a bit better. % -\renewcommand{\maketitle}{ - \rule{\textwidth}{1pt} +\renewcommand{\maketitle}{% + \noindent\rule{\textwidth}{1pt}\ifsphinxpdfoutput\newline\null\fi\par \ifsphinxpdfoutput \begingroup % These \defs are required to deal with multi-line authors; it % changes \\ to ', ' (comma-space), making it pass muster for % generating document info in the PDF file. - \def\\{, } - \def\and{and } + \def\\{, }% + \def\and{and }% \pdfinfo{ /Author (\@author) /Title (\@title) - } + }% \endgroup \fi \begin{flushright} - \sphinxlogo% - {\rm\Huge\py@HeaderFamily \@title} \par - {\em\large\py@HeaderFamily \py@release\releaseinfo} \par + \sphinxlogo + \py@HeaderFamily + {\Huge \@title }\par + {\itshape\large \py@release \releaseinfo}\par \vspace{25pt} - {\Large\py@HeaderFamily + {\Large \begin{tabular}[t]{c} \@author - \end{tabular}} \par + \end{tabular}}\par \vspace{25pt} \@date \par \py@authoraddress \par diff --git a/sphinx/texinputs/sphinxmanual.cls b/sphinx/texinputs/sphinxmanual.cls index b576b673e..f20449449 100644 --- a/sphinx/texinputs/sphinxmanual.cls +++ b/sphinx/texinputs/sphinxmanual.cls @@ -43,26 +43,27 @@ \begin{titlepage}% \let\footnotesize\small \let\footnoterule\relax - \rule{\textwidth}{1pt}% + \noindent\rule{\textwidth}{1pt}\ifsphinxpdfoutput\newline\null\fi\par \ifsphinxpdfoutput \begingroup % These \defs are required to deal with multi-line authors; it % changes \\ to ', ' (comma-space), making it pass muster for % generating document info in the PDF file. - \def\\{, } - \def\and{and } + \def\\{, }% + \def\and{and }% \pdfinfo{ /Author (\@author) /Title (\@title) - } + }% \endgroup \fi \begin{flushright}% - \sphinxlogo% - {\rm\Huge\py@HeaderFamily \@title \par}% - {\em\LARGE\py@HeaderFamily \py@release\releaseinfo \par} + \sphinxlogo + \py@HeaderFamily + {\Huge \@title \par} + {\itshape\LARGE \py@release\releaseinfo \par} \vfill - {\LARGE\py@HeaderFamily + {\LARGE \begin{tabular}[t]{c} \@author \end{tabular} @@ -76,52 +77,27 @@ \end{flushright}%\par \@thanks \end{titlepage}% - \cleardoublepage% \setcounter{footnote}{0}% \let\thanks\relax\let\maketitle\relax %\gdef\@thanks{}\gdef\@author{}\gdef\@title{} } - -% Catch the end of the {abstract} environment, but here make sure the abstract -% is followed by a blank page if the 'openright' option is used. -% -\let\py@OldEndAbstract=\endabstract -\renewcommand{\endabstract}{ - \if@openright - \ifodd\value{page} - \typeout{Adding blank page after the abstract.} - \vfil\pagebreak - \fi - \fi - \py@OldEndAbstract -} - -% This wraps the \tableofcontents macro with all the magic to get the spacing -% right and have the right number of pages if the 'openright' option has been -% used. This eliminates a fair amount of crud in the individual document files. -% \let\py@OldTableofcontents=\tableofcontents \renewcommand{\tableofcontents}{% + % before resetting page counter, let's do the right thing. + \if@openright\cleardoublepage\else\clearpage\fi \pagenumbering{roman}% - \setcounter{page}{1}% - \pagebreak% \pagestyle{plain}% - {% - \parskip = 0mm% - \py@OldTableofcontents% - \if@openright% - \ifodd\value{page}% - \typeout{Adding blank page after the table of contents.}% - \pagebreak\hspace{0pt}% - \fi% - \fi% - \cleardoublepage% - }% + \begingroup + \parskip \z@skip + \py@OldTableofcontents + \endgroup + % before resetting page counter, let's do the right thing. + \if@openright\cleardoublepage\else\clearpage\fi \pagenumbering{arabic}% - \@ifundefined{fancyhf}{}{\pagestyle{normal}}% + \ifdefined\fancyhf\pagestyle{normal}\fi } -\pagenumbering{alph} +\pagenumbering{alph}% avoid hyperref "duplicate destination" warnings % This is needed to get the width of the section # area wide enough in the % library reference. Doing it here keeps it the same for all the manuals. @@ -134,7 +110,7 @@ % For a report document class this environment is a chapter. \let\py@OldThebibliography=\thebibliography \renewcommand{\thebibliography}[1]{ - \cleardoublepage + \if@openright\cleardoublepage\else\clearpage\fi \phantomsection \py@OldThebibliography{1} \addcontentsline{toc}{chapter}{\bibname} @@ -146,7 +122,7 @@ \@ifclassloaded{memoir}{}{ \let\py@OldTheindex=\theindex \renewcommand{\theindex}{ - \cleardoublepage + \if@openright\cleardoublepage\else\clearpage\fi \phantomsection \py@OldTheindex \addcontentsline{toc}{chapter}{\indexname} diff --git a/sphinx/texinputs/tabulary.sty b/sphinx/texinputs/tabulary.sty index 11fdf7428..03e07ec2a 100644 --- a/sphinx/texinputs/tabulary.sty +++ b/sphinx/texinputs/tabulary.sty @@ -14,7 +14,7 @@ %% \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{tabulary} - [2008/12/01 v0.9 tabulary package (DPC)] + [2014/06/11 v0.10 tabulary package (DPC) + footnote patch (sphinx)] \RequirePackage{array} \catcode`\Z=14 \DeclareOption{debugshow}{\catcode`\Z=9\relax} @@ -354,6 +354,7 @@ Z \message{^^JTotal:\the\@tempdima^^J}% \expandafter\let\expandafter\color\expandafter\relax \expandafter\let\expandafter\CT@column@color\expandafter\relax \expandafter\let\expandafter\CT@row@color\expandafter\relax + \expandafter\let\expandafter\CT@cell@color\expandafter\relax \@mkpream{#1}} \let\TY@@mkpream\@mkpream \def\TY@classz{% @@ -396,6 +397,7 @@ Z \message{^^JTotal:\the\@tempdima^^J}% \CT@setup \CT@column@color \CT@row@color + \CT@cell@color \CT@do@color \endgroup \@tempdima\ht\z@ diff --git a/sphinx/themes/agogo/static/bgfooter.png b/sphinx/themes/agogo/static/bgfooter.png index 9ce5bdd90..b7c7cadd4 100644 Binary files a/sphinx/themes/agogo/static/bgfooter.png and b/sphinx/themes/agogo/static/bgfooter.png differ diff --git a/sphinx/themes/agogo/static/bgtop.png b/sphinx/themes/agogo/static/bgtop.png index a0d4709ba..05740880f 100644 Binary files a/sphinx/themes/agogo/static/bgtop.png and b/sphinx/themes/agogo/static/bgtop.png differ diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html index 1afc4a0bf..f8ff477c7 100644 --- a/sphinx/themes/basic/layout.html +++ b/sphinx/themes/basic/layout.html @@ -91,7 +91,8 @@ VERSION: '{{ release|e }}', COLLAPSE_INDEX: false, FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}', - HAS_SOURCE: {{ has_source|lower }} + HAS_SOURCE: {{ has_source|lower }}, + SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}' }; {%- for scriptfile in script_files %} diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index cdc703b09..585e992a5 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -585,6 +585,16 @@ span.eqno { float: right; } +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + /* -- printout stylesheet --------------------------------------------------- */ @media print { diff --git a/sphinx/themes/basic/static/comment-bright.png b/sphinx/themes/basic/static/comment-bright.png index 551517b8c..15e27edb1 100644 Binary files a/sphinx/themes/basic/static/comment-bright.png and b/sphinx/themes/basic/static/comment-bright.png differ diff --git a/sphinx/themes/basic/static/comment-close.png b/sphinx/themes/basic/static/comment-close.png index 09b54be46..4d91bcf57 100644 Binary files a/sphinx/themes/basic/static/comment-close.png and b/sphinx/themes/basic/static/comment-close.png differ diff --git a/sphinx/themes/basic/static/comment.png b/sphinx/themes/basic/static/comment.png index 92feb52b8..dfbc0cbd5 100644 Binary files a/sphinx/themes/basic/static/comment.png and b/sphinx/themes/basic/static/comment.png differ diff --git a/sphinx/themes/basic/static/down-pressed.png b/sphinx/themes/basic/static/down-pressed.png index 7c30d004b..5756c8cad 100644 Binary files a/sphinx/themes/basic/static/down-pressed.png and b/sphinx/themes/basic/static/down-pressed.png differ diff --git a/sphinx/themes/basic/static/down.png b/sphinx/themes/basic/static/down.png index f48098a43..1b3bdad2c 100644 Binary files a/sphinx/themes/basic/static/down.png and b/sphinx/themes/basic/static/down.png differ diff --git a/sphinx/themes/basic/static/file.png b/sphinx/themes/basic/static/file.png index 254c60bfb..a858a410e 100644 Binary files a/sphinx/themes/basic/static/file.png and b/sphinx/themes/basic/static/file.png differ diff --git a/sphinx/themes/basic/static/minus.png b/sphinx/themes/basic/static/minus.png index 0f22b16b0..d96755fda 100644 Binary files a/sphinx/themes/basic/static/minus.png and b/sphinx/themes/basic/static/minus.png differ diff --git a/sphinx/themes/basic/static/plus.png b/sphinx/themes/basic/static/plus.png index 0cfe084cf..7107cec93 100644 Binary files a/sphinx/themes/basic/static/plus.png and b/sphinx/themes/basic/static/plus.png differ diff --git a/sphinx/themes/basic/static/searchtools.js_t b/sphinx/themes/basic/static/searchtools.js_t index af0e21a37..cf899ef85 100644 --- a/sphinx/themes/basic/static/searchtools.js_t +++ b/sphinx/themes/basic/static/searchtools.js_t @@ -260,7 +260,8 @@ var Search = { displayNextItem(); }); } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { - $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt', + var suffix = DOCUMENTATION_OPTIONS.SOURCELINK_SUFFIX; + $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[5] + (item[5].endsWith(suffix) ? '' : suffix), dataType: "text", complete: function(jqxhr, textstatus) { var data = jqxhr.responseText; @@ -299,6 +300,7 @@ var Search = { */ performObjectSearch : function(object, otherterms) { var filenames = this._index.filenames; + var docnames = this._index.docnames; var objects = this._index.objects; var objnames = this._index.objnames; var titles = this._index.titles; @@ -352,7 +354,7 @@ var Search = { } else { score += Scorer.objPrioDefault; } - results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]); + results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]); } } } @@ -364,6 +366,7 @@ var Search = { * search for full-text terms in the index */ performTermsSearch : function(searchterms, excluded, terms, titleterms) { + var docnames = this._index.docnames; var filenames = this._index.filenames; var titles = this._index.titles; @@ -438,7 +441,7 @@ var Search = { // select one (max) score for the file. // for better ranking, we should calculate ranking by using words statistics like basic tf-idf... var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]})); - results.push([filenames[file], titles[file], '', null, score]); + results.push([docnames[file], titles[file], '', null, score, filenames[file]]); } } return results; diff --git a/sphinx/themes/basic/static/up-pressed.png b/sphinx/themes/basic/static/up-pressed.png index 99e721096..acee3b68e 100644 Binary files a/sphinx/themes/basic/static/up-pressed.png and b/sphinx/themes/basic/static/up-pressed.png differ diff --git a/sphinx/themes/basic/static/up.png b/sphinx/themes/basic/static/up.png index 26de002e8..2a940a7da 100644 Binary files a/sphinx/themes/basic/static/up.png and b/sphinx/themes/basic/static/up.png differ diff --git a/sphinx/themes/bizstyle/static/background_b01.png b/sphinx/themes/bizstyle/static/background_b01.png index d262745b4..353f26dde 100644 Binary files a/sphinx/themes/bizstyle/static/background_b01.png and b/sphinx/themes/bizstyle/static/background_b01.png differ diff --git a/sphinx/themes/haiku/static/alert_info_32.png b/sphinx/themes/haiku/static/alert_info_32.png index 05b4fe898..ea4d1baf7 100644 Binary files a/sphinx/themes/haiku/static/alert_info_32.png and b/sphinx/themes/haiku/static/alert_info_32.png differ diff --git a/sphinx/themes/haiku/static/alert_warning_32.png b/sphinx/themes/haiku/static/alert_warning_32.png index f13611cde..a687c3dca 100644 Binary files a/sphinx/themes/haiku/static/alert_warning_32.png and b/sphinx/themes/haiku/static/alert_warning_32.png differ diff --git a/sphinx/themes/haiku/static/bg-page.png b/sphinx/themes/haiku/static/bg-page.png index 0103ee1a7..fe0a6dc89 100644 Binary files a/sphinx/themes/haiku/static/bg-page.png and b/sphinx/themes/haiku/static/bg-page.png differ diff --git a/sphinx/themes/haiku/static/bullet_orange.png b/sphinx/themes/haiku/static/bullet_orange.png index ad5d02f34..1cb8097ce 100644 Binary files a/sphinx/themes/haiku/static/bullet_orange.png and b/sphinx/themes/haiku/static/bullet_orange.png differ diff --git a/sphinx/themes/pyramid/static/dialog-note.png b/sphinx/themes/pyramid/static/dialog-note.png index 708682114..5a6336d11 100644 Binary files a/sphinx/themes/pyramid/static/dialog-note.png and b/sphinx/themes/pyramid/static/dialog-note.png differ diff --git a/sphinx/themes/pyramid/static/dialog-seealso.png b/sphinx/themes/pyramid/static/dialog-seealso.png index a27a0fcba..97553a8b7 100644 Binary files a/sphinx/themes/pyramid/static/dialog-seealso.png and b/sphinx/themes/pyramid/static/dialog-seealso.png differ diff --git a/sphinx/themes/pyramid/static/dialog-todo.png b/sphinx/themes/pyramid/static/dialog-todo.png index 9caa91e8a..cfbc28088 100644 Binary files a/sphinx/themes/pyramid/static/dialog-todo.png and b/sphinx/themes/pyramid/static/dialog-todo.png differ diff --git a/sphinx/themes/pyramid/static/dialog-topic.png b/sphinx/themes/pyramid/static/dialog-topic.png index 2ac57475c..a75afeaaf 100644 Binary files a/sphinx/themes/pyramid/static/dialog-topic.png and b/sphinx/themes/pyramid/static/dialog-topic.png differ diff --git a/sphinx/themes/pyramid/static/dialog-warning.png b/sphinx/themes/pyramid/static/dialog-warning.png index 4f598b12b..8bb7d8d35 100644 Binary files a/sphinx/themes/pyramid/static/dialog-warning.png and b/sphinx/themes/pyramid/static/dialog-warning.png differ diff --git a/sphinx/themes/pyramid/static/headerbg.png b/sphinx/themes/pyramid/static/headerbg.png index 0596f2020..e1051af48 100644 Binary files a/sphinx/themes/pyramid/static/headerbg.png and b/sphinx/themes/pyramid/static/headerbg.png differ diff --git a/sphinx/themes/pyramid/static/middlebg.png b/sphinx/themes/pyramid/static/middlebg.png index b3a89f4e5..5ee55db25 100644 Binary files a/sphinx/themes/pyramid/static/middlebg.png and b/sphinx/themes/pyramid/static/middlebg.png differ diff --git a/sphinx/themes/scrolls/static/darkmetal.png b/sphinx/themes/scrolls/static/darkmetal.png index 3ed486d5c..49c82f301 100644 Binary files a/sphinx/themes/scrolls/static/darkmetal.png and b/sphinx/themes/scrolls/static/darkmetal.png differ diff --git a/sphinx/themes/scrolls/static/headerbg.png b/sphinx/themes/scrolls/static/headerbg.png index 0c5b3657c..ef15cc09c 100644 Binary files a/sphinx/themes/scrolls/static/headerbg.png and b/sphinx/themes/scrolls/static/headerbg.png differ diff --git a/sphinx/themes/scrolls/static/logo.png b/sphinx/themes/scrolls/static/logo.png index 3dc573e0f..354aded6b 100644 Binary files a/sphinx/themes/scrolls/static/logo.png and b/sphinx/themes/scrolls/static/logo.png differ diff --git a/sphinx/themes/scrolls/static/metal.png b/sphinx/themes/scrolls/static/metal.png index e51010b5f..c29cd9eca 100644 Binary files a/sphinx/themes/scrolls/static/metal.png and b/sphinx/themes/scrolls/static/metal.png differ diff --git a/sphinx/themes/scrolls/static/navigation.png b/sphinx/themes/scrolls/static/navigation.png index 5be5b3183..89c447a29 100644 Binary files a/sphinx/themes/scrolls/static/navigation.png and b/sphinx/themes/scrolls/static/navigation.png differ diff --git a/sphinx/themes/scrolls/static/watermark.png b/sphinx/themes/scrolls/static/watermark.png index 903a96edb..d71dc4bbe 100644 Binary files a/sphinx/themes/scrolls/static/watermark.png and b/sphinx/themes/scrolls/static/watermark.png differ diff --git a/sphinx/themes/scrolls/static/watermark_blur.png b/sphinx/themes/scrolls/static/watermark_blur.png index 022690062..9fc0b6d31 100644 Binary files a/sphinx/themes/scrolls/static/watermark_blur.png and b/sphinx/themes/scrolls/static/watermark_blur.png differ diff --git a/sphinx/themes/sphinxdoc/static/contents.png b/sphinx/themes/sphinxdoc/static/contents.png index 7fb82154a..6c59aa1f9 100644 Binary files a/sphinx/themes/sphinxdoc/static/contents.png and b/sphinx/themes/sphinxdoc/static/contents.png differ diff --git a/sphinx/themes/sphinxdoc/static/navigation.png b/sphinx/themes/sphinxdoc/static/navigation.png index 1081dc143..fda6cd29e 100644 Binary files a/sphinx/themes/sphinxdoc/static/navigation.png and b/sphinx/themes/sphinxdoc/static/navigation.png differ diff --git a/sphinx/theming.py b/sphinx/theming.py index 539184115..42e4448db 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -133,9 +133,8 @@ class Theme(object): dirname = path.dirname(name) if not path.isdir(path.join(self.themedir, dirname)): os.makedirs(path.join(self.themedir, dirname)) - fp = open(path.join(self.themedir, name), 'wb') - fp.write(tinfo.read(name)) - fp.close() + with open(path.join(self.themedir, name), 'wb') as fp: + fp.write(tinfo.read(name)) self.themeconf = configparser.RawConfigParser() self.themeconf.read(path.join(self.themedir, THEMECONF)) diff --git a/sphinx/transforms.py b/sphinx/transforms.py index abeab7dab..cb4a5779b 100644 --- a/sphinx/transforms.py +++ b/sphinx/transforms.py @@ -170,6 +170,24 @@ class ApplySourceWorkaround(Transform): apply_source_workaround(n) +class AutoIndexUpgrader(Transform): + """ + Detect old style; 4 column based indices and automatically upgrade to new style. + """ + default_priority = 210 + + def apply(self): + env = self.document.settings.env + for node in self.document.traverse(addnodes.index): + if 'entries' in node and any(len(entry) == 4 for entry in node['entries']): + msg = ('4 column based index found. ' + 'It might be a bug of extensions you use: %r' % node['entries']) + env.warn_node(msg, node) + for i, entry in enumerate(node['entries']): + if len(entry) == 4: + node['entries'][i] = entry + (None,) + + class ExtraTranslatableNodes(Transform): """ make nodes translatable diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 0c823fb1a..a0dababc8 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -18,7 +18,7 @@ import posixpath import traceback import unicodedata from os import path -from codecs import open, BOM_UTF8 +from codecs import BOM_UTF8 from collections import deque from six import iteritems, text_type, binary_type @@ -32,6 +32,7 @@ import jinja2 import sphinx from sphinx.errors import PycodeError, SphinxParallelError, ExtensionError from sphinx.util.console import strip_colors +from sphinx.util.fileutil import copy_asset_file from sphinx.util.osutil import fs_encoding # import other utilities; partly for backwards compatibility, so don't @@ -148,7 +149,7 @@ class FilenameUniqDict(dict): def copy_static_entry(source, targetdir, builder, context={}, exclude_matchers=(), level=0): - """Copy a HTML builder static_path entry from source to targetdir. + """[DEPRECATED] Copy a HTML builder static_path entry from source to targetdir. Handles all possible cases of files, directories and subdirectories. """ @@ -158,16 +159,7 @@ def copy_static_entry(source, targetdir, builder, context={}, if matcher(relpath): return if path.isfile(source): - target = path.join(targetdir, path.basename(source)) - if source.lower().endswith('_t') and builder.templates: - # templated! - fsrc = open(source, 'r', encoding='utf-8') - fdst = open(target[:-2], 'w', encoding='utf-8') - fdst.write(builder.templates.render_string(fsrc.read(), context)) - fsrc.close() - fdst.close() - else: - copyfile(source, target) + copy_asset_file(source, targetdir, context, builder.templates) elif path.isdir(source): if not path.isdir(targetdir): os.mkdir(targetdir) @@ -181,37 +173,6 @@ def copy_static_entry(source, targetdir, builder, context={}, builder, context, level=level+1, exclude_matchers=exclude_matchers) - -def copy_extra_entry(source, targetdir, exclude_matchers=()): - """Copy a HTML builder extra_path entry from source to targetdir. - - Handles all possible cases of files, directories and subdirectories. - """ - def excluded(path): - relpath = relative_path(os.path.dirname(source), path) - return any(matcher(relpath) for matcher in exclude_matchers) - - def copy_extra_file(source_, targetdir_): - if not excluded(source_): - target = path.join(targetdir_, os.path.basename(source_)) - copyfile(source_, target) - - if os.path.isfile(source): - copy_extra_file(source, targetdir) - return - - for root, dirs, files in os.walk(source): - reltargetdir = os.path.join(targetdir, relative_path(source, root)) - for dir in dirs[:]: - if excluded(os.path.join(root, dir)): - dirs.remove(dir) - else: - target = os.path.join(reltargetdir, dir) - if not path.exists(target): - os.mkdir(target) - for file in files: - copy_extra_file(os.path.join(root, file), reltargetdir) - _DEBUG_HEADER = '''\ # Sphinx version: %s # Python version: %s (%s) diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py new file mode 100644 index 000000000..d11ae9f1b --- /dev/null +++ b/sphinx/util/fileutil.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.fileutil + ~~~~~~~~~~~~~~~~~~~~ + + File utility functions for Sphinx. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import os +import codecs +import posixpath +from docutils.utils import relative_path +from sphinx.util.osutil import copyfile, ensuredir, walk + + +def copy_asset_file(source, destination, context=None, renderer=None): + """Copy an asset file to destination. + + On copying, it expands the template variables if context argument is given and + the asset is a template file. + + :param source: The path to source file + :param destination: The path to destination file or directory + :param context: The template variables. If not given, template files are simply copied + :param renderer: The template engine. If not given, SphinxRenderer is used by default + """ + if not os.path.exists(source): + return + + if os.path.exists(destination) and os.path.isdir(destination): + # Use source filename if destination points a directory + destination = os.path.join(destination, os.path.basename(source)) + + if source.lower().endswith('_t') and context: + if renderer is None: + from sphinx.util.template import SphinxRenderer + renderer = SphinxRenderer() + + with codecs.open(source, 'r', encoding='utf-8') as fsrc: + with codecs.open(destination[:-2], 'w', encoding='utf-8') as fdst: + fdst.write(renderer.render_string(fsrc.read(), context)) + else: + copyfile(source, destination) + + +def copy_asset(source, destination, excluded=lambda path: False, context=None, renderer=None): + """Copy asset files to destination recursively. + + On copying, it expands the template variables if context argument is given and + the asset is a template file. + + :param source: The path to source file or directory + :param destination: The path to destination directory + :param excluded: The matcher to determine the given path should be copied or not + :param context: The template variables. If not given, template files are simply copied + :param renderer: The template engine. If not given, SphinxRenderer is used by default + """ + if not os.path.exists(source): + return + + ensuredir(destination) + if os.path.isfile(source): + copy_asset_file(source, destination, context, renderer) + return + + for root, dirs, files in walk(source): + reldir = relative_path(source, root) + for dir in dirs[:]: + if excluded(posixpath.join(reldir, dir)): + dirs.remove(dir) + else: + ensuredir(posixpath.join(destination, reldir, dir)) + + for filename in files: + if not excluded(posixpath.join(reldir, filename)): + copy_asset_file(posixpath.join(root, filename), + posixpath.join(destination, reldir), + context, renderer) diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 5dc2008c8..1c6e4e151 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -20,7 +20,7 @@ from sphinx.util import force_decode # relatively import this module inspect = __import__('inspect') -memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>$)') +memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)') if PY3: @@ -108,6 +108,10 @@ def safe_getattr(obj, name, *defargs): try: return getattr(obj, name, *defargs) except Exception: + # sometimes accessing a property raises an exception (e.g. + # NotImplementedError), so let's try to read the attribute directly + if name in obj.__dict__: + return obj.__dict__[name] # this is a catch-all for all the weird things that some modules do # with attribute access if defargs: diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py index 91fda6378..fc7750be9 100644 --- a/sphinx/util/matching.py +++ b/sphinx/util/matching.py @@ -62,6 +62,27 @@ def compile_matchers(patterns): return [re.compile(_translate_pattern(pat)).match for pat in patterns] +class Matcher(object): + """A pattern matcher for Multiple shell-style glob patterns. + + Note: this modifies the patterns to work with copy_asset(). + For example, "**/index.rst" matches with "index.rst" + """ + + def __init__(self, patterns): + expanded = [pat[3:] for pat in patterns if pat.startswith('**/')] + self.patterns = compile_matchers(patterns + expanded) + + def __call__(self, string): + return self.match(string) + + def match(self, string): + return any(pat(string) for pat in self.patterns) + + +DOTFILES = Matcher(['**/.*']) + + _pat_cache = {} diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 585dc6104..0de6d8472 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -17,6 +17,7 @@ import time import errno import locale import shutil +import filecmp from os import path import contextlib @@ -141,13 +142,16 @@ def copytimes(source, dest): def copyfile(source, dest): - """Copy a file and its modification times, if possible.""" - shutil.copyfile(source, dest) - try: - # don't do full copystat because the source may be read-only - copytimes(source, dest) - except OSError: - pass + """Copy a file and its modification times, if possible. + + Note: ``copyfile`` skips copying if the file has not been changed""" + if not path.exists(dest) or not filecmp.cmp(source, dest): + shutil.copyfile(source, dest) + try: + # don't do full copystat because the source may be read-only + copytimes(source, dest) + except OSError: + pass no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') diff --git a/sphinx/util/template.py b/sphinx/util/template.py new file mode 100644 index 000000000..b89a4c960 --- /dev/null +++ b/sphinx/util/template.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.template + ~~~~~~~~~~~~~~~~~~~~ + + Templates utility functions for Sphinx. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +from jinja2.sandbox import SandboxedEnvironment + +from sphinx import package_dir +from sphinx.jinja2glue import SphinxFileSystemLoader + + +class BaseRenderer(object): + def __init__(self, loader=None): + self.env = SandboxedEnvironment(loader=loader) + self.env.filters['repr'] = repr + + def render(self, template_name, context): + return self.env.get_template(template_name).render(context) + + def render_string(self, source, context): + return self.env.from_string(source).render(context) + + +class FileRenderer(BaseRenderer): + def __init__(self, search_path): + loader = SphinxFileSystemLoader(search_path) + super(FileRenderer, self).__init__(loader) + + @classmethod + def render_from_file(cls, filename, context): + dirname = os.path.dirname(filename) + basename = os.path.basename(filename) + return cls(dirname).render(basename, context) + + +class SphinxRenderer(FileRenderer): + def __init__(self): + super(SphinxRenderer, self).__init__(os.path.join(package_dir, 'templates')) + + +class LaTeXRenderer(SphinxRenderer): + def __init__(self): + super(LaTeXRenderer, self).__init__() + + # use JSP/eRuby like tagging instead because curly bracket; the default + # tagging of jinja2 is not good for LaTeX sources. + self.env.variable_start_string = '<%=' + self.env.variable_end_string = '%>' + self.env.block_start_string = '<%' + self.env.block_end_string = '%>' diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index 844a3b468..80b5a3535 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -34,19 +34,20 @@ class BaseSearch(object): """ pass - def feed(self, pagename, title, doctree): + def feed(self, pagename, filename, title, doctree): """Called by the builder to add a doctree to the index. Converts the `doctree` to text and passes it to :meth:`add_document`. You probably won't want to override this unless you need access to the `doctree`. Override :meth:`add_document` instead. :param pagename: the name of the page to be indexed + :param filename: the name of the original source file :param title: the title of the page to be indexed :param doctree: is the docutils doctree representation of the page """ - self.add_document(pagename, title, doctree.astext()) + self.add_document(pagename, filename, title, doctree.astext()) - def add_document(self, pagename, title, text): + def add_document(self, pagename, filename, title, text): """Called by :meth:`feed` to add a document to the search index. This method should should do everything necessary to add a single document to the search index. @@ -59,6 +60,7 @@ class BaseSearch(object): query. :param pagename: the name of the page being indexed + :param filename: the name of the original source file :param title: the page's title :param text: the full text of the page """ diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py index 9e990b1cf..4d0db9553 100644 --- a/sphinx/websupport/search/nullsearch.py +++ b/sphinx/websupport/search/nullsearch.py @@ -17,7 +17,7 @@ class NullSearch(BaseSearch): """A search adapter that does nothing. Used when no search adapter is specified. """ - def feed(self, pagename, title, doctree): + def feed(self, pagename, filename, title, doctree): pass def query(self, q): diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 4b0769f50..f31dc4d52 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -44,7 +44,7 @@ class WhooshSearch(BaseSearch): def finish_indexing(self): self.index_writer.commit() - def add_document(self, pagename, title, text): + def add_document(self, pagename, filename, title, text): self.index_writer.add_document(path=text_type(pagename), title=title, text=text) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index c72aff7b1..85014ef2a 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -28,42 +28,10 @@ from sphinx.locale import admonitionlabels, _ from sphinx.util import split_into from sphinx.util.i18n import format_date from sphinx.util.nodes import clean_astext, traverse_parent +from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_escape_map, tex_replace_map from sphinx.util.smartypants import educate_quotes_latex -HEADER = r'''%% Generated by Sphinx. -\def\sphinxdocclass{%(docclass)s} -\documentclass[%(papersize)s,%(pointsize)s%(classoptions)s]{%(wrapperclass)s} -\usepackage{iftex} -%(passoptionstopackages)s -%(inputenc)s -%(utf8extra)s -%(cmappkg)s -%(fontenc)s -%(amsmath)s -%(babel)s -%(fontpkg)s -%(fncychap)s -%(longtable)s -\usepackage{sphinx} -\usepackage{multirow} -\usepackage{eqparbox} -%(usepackages)s -%(contentsname)s -%(numfig_format)s -%(pageautorefname)s -%(tocdepth)s -%(secnumdepth)s -%(preamble)s - -\title{%(title)s} -\date{%(date)s} -\release{%(release)s} -\author{%(author)s} -\newcommand{\sphinxlogo}{%(logo)s} -\renewcommand{\releasename}{%(releasename)s} -%(makeindex)s -''' BEGIN_DOC = r''' \begin{document} @@ -72,12 +40,8 @@ BEGIN_DOC = r''' %(tableofcontents)s ''' -FOOTER = r''' -\renewcommand{\indexname}{%(indexname)s} -%(printindex)s -\end{document} -''' +DEFAULT_TEMPLATE = 'latex/content.tex_t' URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:') SECNUMDEPTH = 3 @@ -266,29 +230,28 @@ class Table(object): self.longtable = False -def width_to_latex_length(length_str): - """Convert `length_str` with rst length to LaTeX length. - - This function is copied from docutils' latex writer - """ - match = re.match('(\d*\.?\d*)\s*(\S*)', length_str) - if not match: - return length_str - value, unit = match.groups()[:2] - if unit in ('', 'pt'): - length_str = '%sbp' % value # convert to 'bp' - # percentage: relate to current line width - elif unit == '%': - length_str = '%.3f\\linewidth' % (float(value)/100.0) - - return length_str - - def escape_abbr(text): """Adjust spacing after abbreviations.""" return re.sub('\.(?=\s|$)', '.\\@', text) +def rstdim_to_latexdim(width_str): + """Convert `width_str` with rst length to LaTeX length.""" + match = re.match('^(\d*\.?\d*)\s*(\S*)$', width_str) + if not match: + raise ValueError + res = width_str + amount, unit = match.groups()[:2] + float(amount) # validate amount is float + if unit in ('', "px"): + res = "%s\\sphinxpxdimen" % amount + elif unit == 'pt': + res = '%sbp' % amount # convert to 'bp' + elif unit == "%": + res = "%.3f\\linewidth" % (float(amount) / 100.0) + return res + + class LaTeXTranslator(nodes.NodeVisitor): sectionnames = ["part", "chapter", "section", "subsection", "subsubsection", "paragraph", "subparagraph"] @@ -298,15 +261,16 @@ class LaTeXTranslator(nodes.NodeVisitor): default_elements = { 'papersize': 'letterpaper', 'pointsize': '10pt', + 'pxunit': '49336sp', 'classoptions': '', 'extraclassoptions': '', 'passoptionstopackages': '', 'inputenc': ('\\ifPDFTeX\n' ' \\usepackage[utf8]{inputenc}\n' - '\\else\\fi'), + '\\fi'), 'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n' ' \\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' - '\\else\\fi'), + '\\fi'), 'cmappkg': '\\usepackage{cmap}', 'fontenc': '\\usepackage[T1]{fontenc}', 'amsmath': '\\usepackage{amsmath,amssymb,amstext}', @@ -371,29 +335,24 @@ class LaTeXTranslator(nodes.NodeVisitor): if document.settings.docclass == 'howto': self.top_sectionlevel = 2 else: - if builder.config.latex_use_parts: - self.top_sectionlevel = 0 - else: - self.top_sectionlevel = 1 + self.top_sectionlevel = 1 # sort out some elements - papersize = builder.config.latex_paper_size + 'paper' - if papersize == 'paper': # e.g. command line "-D latex_paper_size=" - papersize = 'letterpaper' - self.elements = self.default_elements.copy() self.elements.update({ 'wrapperclass': self.format_docclass(document.settings.docclass), - 'papersize': papersize, - 'pointsize': builder.config.latex_font_size, # if empty, the title is set to the first section title 'title': document.settings.title, 'release': builder.config.release, 'author': document.settings.author, 'releasename': _('Release'), - 'preamble': builder.config.latex_preamble, 'indexname': _('Index'), }) + # set-up boolean for sphinx.sty + if builder.config.latex_keep_old_macro_names: + self.elements['keepoldnames'] = '\\sphinxKeepOldNamestrue' + else: + self.elements['keepoldnames'] = '\\sphinxKeepOldNamesfalse' if document.settings.docclass == 'howto': docclass = builder.config.latex_docclass.get('howto', 'article') else: @@ -405,7 +364,8 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), language=builder.config.language) if builder.config.latex_logo: - self.elements['logo'] = '\\includegraphics{%s}\\par' % \ + # no need for \\noindent here, used in flushright + self.elements['logo'] = '\\sphinxincludegraphics{%s}\\par' % \ path.basename(builder.config.latex_logo) # setup babel self.babel = ExtBabel(builder.config.language) @@ -458,6 +418,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements['contentsname'] = \ self.babel_renewcommand('\\contentsname', document.settings.contentsname) # allow the user to override them all + self.check_latex_elements() self.elements.update(builder.config.latex_elements) if self.elements['extraclassoptions']: self.elements['classoptions'] += ',' + \ @@ -505,6 +466,12 @@ class LaTeXTranslator(nodes.NodeVisitor): def pop_hyperlink_ids(self, figtype): return self.next_hyperlink_ids.pop(figtype, set()) + def check_latex_elements(self): + for key in self.builder.config.latex_elements: + if key not in self.elements: + msg = _("Unknown configure key: latex_elements[%r] is ignored.") + self.builder.warn(msg % key) + def restrict_footnote(self, node): if self.footnote_restricted is False: self.footnote_restricted = node @@ -526,12 +493,11 @@ class LaTeXTranslator(nodes.NodeVisitor): return docclass def astext(self): - return (HEADER % self.elements + - self.highlighter.get_stylesheet() + - u''.join(self.body) + - '\n' + self.elements['footer'] + '\n' + - self.generate_indices() + - FOOTER % self.elements) + self.elements.update({ + 'body': u''.join(self.body), + 'indices': self.generate_indices() + }) + return LaTeXRenderer().render(DEFAULT_TEMPLATE, self.elements) def hypertarget(self, id, withdoc=True, anchor=True): if withdoc: @@ -605,9 +571,9 @@ class LaTeXTranslator(nodes.NodeVisitor): if len(codeblock) == 1: pass # FIXME else: - ret.append('\\SetupFloatingEnvironment{literal-block}{name=%s}\n' % - escape_abbr(text_type(codeblock[0]).translate(tex_escape_map))) - if table[1]: + definition = escape_abbr(text_type(codeblock[0]).translate(tex_escape_map)) + ret.append(self.babel_renewcommand('\\literalblockname', definition)) + if codeblock[1]: pass # FIXME return ''.join(ret) @@ -625,11 +591,11 @@ class LaTeXTranslator(nodes.NodeVisitor): for entry in entries: if not entry[3]: continue - ret.append('\\item {\\texttt{%s}}' % self.encode(entry[0])) + ret.append('\\item {\\sphinxstyleindexentry{%s}}' % self.encode(entry[0])) if entry[4]: # add "extra" info - ret.append(' \\emph{(%s)}' % self.encode(entry[4])) - ret.append(', \\pageref{%s:%s}\n' % + ret.append('\\sphinxstyleindexextra{%s}' % self.encode(entry[4])) + ret.append('\\sphinxstyleindexpageref{%s:%s}\n' % (entry[2], self.idescape(entry[3]))) ret.append('\\end{theindex}\n') @@ -743,11 +709,11 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_topic(self, node): self.in_minipage = 1 - self.body.append('\n\\begin{SphinxShadowBox}\n') + self.body.append('\n\\begin{sphinxShadowBox}\n') def depart_topic(self, node): self.in_minipage = 0 - self.body.append('\\end{SphinxShadowBox}\n') + self.body.append('\\end{sphinxShadowBox}\n') visit_sidebar = visit_topic depart_sidebar = depart_topic @@ -817,9 +783,12 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context[-1] += self.hypertarget(id, anchor=False) self.next_section_ids.clear() - elif isinstance(parent, (nodes.topic, nodes.sidebar)): - self.body.append(r'\textbf{') - self.context.append('}\n\n\medskip\n\n') + elif isinstance(parent, nodes.topic): + self.body.append(r'\sphinxstyletopictitle{') + self.context.append('}\n') + elif isinstance(parent, nodes.sidebar): + self.body.append(r'\sphinxstylesidebartitle{') + self.context.append('}\n') elif isinstance(parent, nodes.Admonition): self.body.append('{') self.context.append('}\n') @@ -831,7 +800,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 'encountered title node not in section, topic, table, ' 'admonition or sidebar', (self.curfilestack[-1], node.line or '')) - self.body.append('\\textbf{') + self.body.append('\\sphinxstyleothertitle{') self.context.append('}\n') self.in_title = 1 @@ -845,8 +814,8 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_subtitle(self, node): if isinstance(node.parent, nodes.sidebar): - self.body.append('~\\\\\n\\textbf{') - self.context.append('}\n\\smallskip\n') + self.body.append('\\sphinxstylesidebarsubtitle{') + self.context.append('}\n') else: self.context.append('') @@ -878,7 +847,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('}') def visit_desc_addname(self, node): - self.body.append(r'\code{') + self.body.append(r'\sphinxcode{') self.literal_whitespace += 1 def depart_desc_addname(self, node): @@ -898,7 +867,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'}') def visit_desc_name(self, node): - self.body.append(r'\bfcode{') + self.body.append(r'\sphinxbfcode{') self.no_contractions += 1 self.literal_whitespace += 1 @@ -929,13 +898,13 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('}') def visit_desc_optional(self, node): - self.body.append(r'\optional{') + self.body.append(r'\sphinxoptional{') def depart_desc_optional(self, node): self.body.append('}') def visit_desc_annotation(self, node): - self.body.append(r'\strong{') + self.body.append(r'\sphinxstrong{') def depart_desc_annotation(self, node): self.body.append('}') @@ -949,7 +918,7 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_seealso(self, node): - self.body.append(u'\n\n\\strong{%s:}\n\n' % admonitionlabels['seealso']) + self.body.append(u'\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso']) def depart_seealso(self, node): self.body.append("\n\n") @@ -972,9 +941,9 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_collected_footnote(self, node): self.in_footnote += 1 if 'footnotetext' in node: - self.body.append('\\footnotetext[%s]{\sphinxAtStartFootnote%%' % node['number']) + self.body.append('\\footnotetext[%s]{\sphinxAtStartFootnote\n' % node['number']) else: - self.body.append('\\footnote[%s]{\sphinxAtStartFootnote%%' % node['number']) + self.body.append('\\footnote[%s]{\sphinxAtStartFootnote\n' % node['number']) def depart_collected_footnote(self, node): self.body.append('}') @@ -1022,23 +991,23 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n\\begin{longtable}') endmacro = '\\end{longtable}\n\n' elif self.table.has_verbatim: - self.body.append('\n\\begin{tabular}') + self.body.append('\n\\noindent\\begin{tabular}') endmacro = '\\end{tabular}\n\n' elif self.table.has_problematic and not self.table.colspec: # if the user has given us tabularcolumns, accept them and use # tabulary nevertheless - self.body.append('\n\\begin{tabular}') + self.body.append('\n\\noindent\\begin{tabular}') endmacro = '\\end{tabular}\n\n' else: - self.body.append('\n\\begin{tabulary}{\\linewidth}') + self.body.append('\n\\noindent\\begin{tabulary}{\\linewidth}') endmacro = '\\end{tabulary}\n\n' if self.table.colspec: self.body.append(self.table.colspec) else: if self.table.has_problematic: - colwidth = 0.95 / self.table.colcount - colspec = ('p{%.3f\\linewidth}|' % colwidth) * \ - self.table.colcount + colspec = ('*{%d}{p{\\dimexpr(\\linewidth-\\arrayrulewidth)/%d' + '-2\\tabcolsep-\\arrayrulewidth\\relax}|}' % + (self.table.colcount, self.table.colcount)) self.body.append('{|' + colspec + '}\n') elif self.table.longtable: self.body.append('{|' + ('l|' * self.table.colcount) + '}\n') @@ -1143,28 +1112,28 @@ class LaTeXTranslator(nodes.NodeVisitor): self.remember_multirow[self.table.col] -= 1 if self.remember_multirowcol.get(self.table.col, 0): extracols = self.remember_multirowcol[self.table.col] - self.body.append(' \\multicolumn{') + self.body.append('\\multicolumn{') self.body.append(str(extracols + 1)) - self.body.append('}{|l|}{}') + self.body.append('}{|l|}{}\\relax ') self.table.col += extracols - self.body.append(' & ') + self.body.append('&') else: - self.body.append(' & ') + self.body.append('&') self.table.col += 1 context = '' if 'morecols' in node: - self.body.append(' \\multicolumn{') + self.body.append('\\multicolumn{') self.body.append(str(node.get('morecols') + 1)) if self.table.col == 1: - self.body.append('}{|l|}{') + self.body.append('}{|l|}{\\relax ') else: - self.body.append('}{l|}{') - context += '}' + self.body.append('}{l|}{\\relax ') + context += '\\unskip}\\relax ' if 'morerows' in node: - self.body.append(' \\multirow{') + self.body.append('\\multirow{') self.body.append(str(node.get('morerows') + 1)) - self.body.append('}{*}{') - context += '}' + self.body.append('}{*}{\\relax ') + context += '\\unskip}\\relax ' self.remember_multirow[self.table.col] = node.get('morerows') if 'morecols' in node: if 'morerows' in node: @@ -1181,17 +1150,17 @@ class LaTeXTranslator(nodes.NodeVisitor): if len(node) == 1 and isinstance(node[0], nodes.paragraph) and node.astext() == '': pass else: - self.body.append('\\textsf{\\relax ') - context += '}' + self.body.append('\\sphinxstylethead{\\relax ') + context += '\\unskip}\\relax ' while self.remember_multirow.get(self.table.col + 1, 0): self.table.col += 1 self.remember_multirow[self.table.col] -= 1 - context += ' & ' + context += '&' if self.remember_multirowcol.get(self.table.col, 0): extracols = self.remember_multirowcol[self.table.col] - context += ' \\multicolumn{' + context += '\\multicolumn{' context += str(extracols + 1) - context += '}{l|}{}' + context += '}{l|}{}\\relax ' self.table.col += extracols if len(node.traverse(nodes.paragraph)) >= 2: self.table.has_problematic = True @@ -1314,12 +1283,15 @@ class LaTeXTranslator(nodes.NodeVisitor): depart_field_body = depart_definition def visit_paragraph(self, node): - # insert blank line, if the paragraph follows a non-paragraph node in a compound index = node.parent.index(node) if (index > 0 and isinstance(node.parent, nodes.compound) and not isinstance(node.parent[index - 1], nodes.paragraph) and not isinstance(node.parent[index - 1], nodes.compound)): + # insert blank line, if the paragraph follows a non-paragraph node in a compound self.body.append('\\noindent\n') + elif index == 0 and isinstance(node.parent, nodes.footnote): + # don't insert blank line, if the paragraph is first child of a footnote + pass else: self.body.append('\n') @@ -1354,18 +1326,10 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def latex_image_length(self, width_str): - match = re.match('(\d*\.?\d*)\s*(\S*)', width_str) - if not match: - # fallback - return width_str - res = width_str - amount, unit = match.groups()[:2] - if not unit or unit == "px": - # pixels: let LaTeX alone - return None - elif unit == "%": - res = "%.3f\\linewidth" % (float(amount) / 100.0) - return res + try: + return rstdim_to_latexdim(width_str) + except ValueError: + self.builder.warn('dimension unit %s is invalid. Ignored.' % width_str) def is_inline(self, node): """Check whether a node represents an inline element.""" @@ -1409,7 +1373,7 @@ class LaTeXTranslator(nodes.NodeVisitor): except KeyError: pass if not is_inline: - pre.append('\n') + pre.append('\n\\noindent') post.append('\n') pre.reverse() if node['uri'] in self.builder.images: @@ -1427,7 +1391,14 @@ class LaTeXTranslator(nodes.NodeVisitor): if include_graphics_options: options = '[%s]' % ','.join(include_graphics_options) base, ext = path.splitext(uri) - self.body.append('\\includegraphics%s{{%s}%s}' % (options, base, ext)) + if self.in_title and base: + # Lowercase tokens forcely because some fncychap themes capitalize + # the options of \sphinxincludegraphics unexpectly (ex. WIDTH=...). + self.body.append('\\lowercase{\\sphinxincludegraphics%s}{{%s}%s}' % + (options, base, ext)) + else: + self.body.append('\\sphinxincludegraphics%s{{%s}%s}' % + (options, base, ext)) self.body.extend(post) def depart_image(self, node): @@ -1444,13 +1415,26 @@ class LaTeXTranslator(nodes.NodeVisitor): isinstance(node.children[0], nodes.image) and node.children[0]['ids']): ids += self.hypertarget(node.children[0]['ids'][0], anchor=False) - if node.get('align', '') in ('left', 'right'): + if self.table: + # TODO: support align option if 'width' in node: - length = width_to_latex_length(node['width']) + length = self.latex_image_length(node['width']) + if length: + self.body.append('\\begin{sphinxfigure-in-table}[%s]\n' + '\\centering\n' % length) else: - length = '0pt' + self.body.append('\\begin{sphinxfigure-in-table}\n\\centering\n') + if any(isinstance(child, nodes.caption) for child in node): + self.body.append('\\capstart') + self.context.append(ids + '\\end{sphinxfigure-in-table}\\relax\n') + elif node.get('align', '') in ('left', 'right'): + length = None + if 'width' in node: + length = self.latex_image_length(node['width']) + elif 'width' in node[0]: + length = self.latex_image_length(node[0]['width']) self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % - (node['align'] == 'right' and 'r' or 'l', length)) + (node['align'] == 'right' and 'r' or 'l', length or '0pt')) self.context.append(ids + '\\end{wrapfigure}\n') elif self.in_minipage: if ('align' not in node.attributes or @@ -1483,9 +1467,11 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_caption(self, node): self.in_caption += 1 if self.in_container_literal_block: - self.body.append('\\SphinxSetupCaptionForVerbatim{literal-block}{') + self.body.append('\\sphinxSetupCaptionForVerbatim{') elif self.in_minipage and isinstance(node.parent, nodes.figure): self.body.append('\\captionof{figure}{') + elif self.table and node.parent.tagname == 'figure': + self.body.append('\\sphinxfigcaption{') else: self.body.append('\\caption{') @@ -1500,19 +1486,19 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('}') def visit_admonition(self, node): - self.body.append('\n\\begin{notice}{note}') + self.body.append('\n\\begin{sphinxadmonition}{note}') def depart_admonition(self, node): - self.body.append('\\end{notice}\n') + self.body.append('\\end{sphinxadmonition}\n') def _make_visit_admonition(name): def visit_admonition(self, node): - self.body.append(u'\n\\begin{notice}{%s}{%s:}' % + self.body.append(u'\n\\begin{sphinxadmonition}{%s}{%s:}' % (name, admonitionlabels[name])) return visit_admonition def _depart_named_admonition(self, node): - self.body.append('\\end{notice}\n') + self.body.append('\\end{sphinxadmonition}\n') visit_attention = _make_visit_admonition('attention') depart_attention = _depart_named_admonition @@ -1683,9 +1669,9 @@ class LaTeXTranslator(nodes.NodeVisitor): # don't add a pageref for glossary terms self.context.append('}}}') # mark up as termreference - self.body.append(r'\termref{') + self.body.append(r'\sphinxtermref{') else: - self.body.append(r'\crossref{') + self.body.append(r'\sphinxcrossref{') if self.builder.config.latex_show_pagerefs and not \ self.in_production_list: self.context.append('}}} (%s)' % self.hyperpageref(id)) @@ -1726,36 +1712,36 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_emphasis(self, node): - self.body.append(r'\emph{') + self.body.append(r'\sphinxstyleemphasis{') def depart_emphasis(self, node): self.body.append('}') def visit_literal_emphasis(self, node): - self.body.append(r'\emph{\texttt{') + self.body.append(r'\sphinxstyleliteralemphasis{') self.no_contractions += 1 def depart_literal_emphasis(self, node): - self.body.append('}}') + self.body.append('}') self.no_contractions -= 1 def visit_strong(self, node): - self.body.append(r'\textbf{') + self.body.append(r'\sphinxstylestrong{') def depart_strong(self, node): self.body.append('}') def visit_literal_strong(self, node): - self.body.append(r'\textbf{\texttt{') + self.body.append(r'\sphinxstyleliteralstrong{') self.no_contractions += 1 def depart_literal_strong(self, node): - self.body.append('}}') + self.body.append('}') self.no_contractions -= 1 def visit_abbreviation(self, node): abbr = node.astext() - self.body.append(r'\textsc{') + self.body.append(r'\sphinxstyleabbreviation{') # spell out the explanation once if node.hasattr('explanation') and abbr not in self.handled_abbrs: self.context.append('} (%s)' % self.encode(node['explanation'])) @@ -1773,7 +1759,7 @@ class LaTeXTranslator(nodes.NodeVisitor): return self.depart_literal_emphasis(node) def visit_title_reference(self, node): - self.body.append(r'\titleref{') + self.body.append(r'\sphinxtitleref{') def depart_title_reference(self, node): self.body.append('}') @@ -1799,9 +1785,9 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_literal(self, node): self.no_contractions += 1 if self.in_title: - self.body.append(r'\texttt{') + self.body.append(r'\sphinxstyleliteralintitle{') else: - self.body.append(r'\code{') + self.body.append(r'\sphinxcode{') def depart_literal(self, node): self.no_contractions -= 1 @@ -1849,7 +1835,7 @@ class LaTeXTranslator(nodes.NodeVisitor): ids += self.hypertarget(node['ids'][0], anchor=False) # LaTeX code will insert \phantomsection prior to \label if ids: - self.body.append('\n\\def\\SphinxLiteralBlockLabel{' + ids + '}') + self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}') code = node.astext() lang = self.hlsettingstack[-1][0] linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1 @@ -1873,18 +1859,22 @@ class LaTeXTranslator(nodes.NodeVisitor): **highlight_args) # workaround for Unicode issue hlcode = hlcode.replace(u'€', u'@texteuro[]') - # must use original Verbatim environment and "tabular" environment + # if in table raise verbatim flag to avoid "tabulary" environment + # and opt for sphinxVerbatimintable to handle caption & long lines if self.table: - hlcode = hlcode.replace('\\begin{Verbatim}', - '\\begin{OriginalVerbatim}') self.table.has_problematic = True self.table.has_verbatim = True + hlcode = hlcode.replace('\\begin{Verbatim}', + '\\begin{sphinxVerbatimintable}') + else: + hlcode = hlcode.replace('\\begin{Verbatim}', + '\\begin{sphinxVerbatim}') # get consistent trailer hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim} - self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' % - (self.table and 'Original' or '')) + self.body.append('\n' + hlcode + '\\end{sphinxVerbatim%s}\n' % + (self.table and 'intable' or '')) if ids: - self.body.append('\\let\\SphinxLiteralBlockLabel\empty\n') + self.body.append('\\let\\sphinxLiteralBlockLabel\empty\n') raise nodes.SkipNode def depart_literal_block(self, node): @@ -2010,10 +2000,10 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_inline(self, node): classes = node.get('classes', []) if classes in [['menuselection'], ['guilabel']]: - self.body.append(r'\menuselection{') + self.body.append(r'\sphinxmenuselection{') self.context.append('}') elif classes in [['accelerator']]: - self.body.append(r'\accelerator{') + self.body.append(r'\sphinxaccelerator{') self.context.append('}') elif classes and not self.in_title: self.body.append(r'\DUrole{%s}{' % ','.join(classes)) @@ -2047,13 +2037,13 @@ class LaTeXTranslator(nodes.NodeVisitor): ids += self.hypertarget(node['ids'][0], anchor=False) # define label for use in caption. if ids: - self.body.append('\n\\def\\SphinxLiteralBlockLabel{' + ids + '}\n') + self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}\n') def depart_container(self, node): if node.get('literal_block'): self.in_container_literal_block -= 1 - self.body.append('\\let\\SphinxVerbatimTitle\\empty\n') - self.body.append('\\let\\SphinxLiteralBlockLabel\\empty\n') + self.body.append('\\let\\sphinxVerbatimTitle\\empty\n') + self.body.append('\\let\\sphinxLiteralBlockLabel\\empty\n') def visit_decoration(self, node): pass diff --git a/test-reqs.txt b/test-reqs.txt index 582afe69f..690174b7d 100644 --- a/test-reqs.txt +++ b/test-reqs.txt @@ -13,3 +13,4 @@ alabaster sphinx_rtd_theme imagesize requests +html5lib diff --git a/tests/coverage.py b/tests/coverage.py index f9341d8ba..cd36e218a 100755 --- a/tests/coverage.py +++ b/tests/coverage.py @@ -472,10 +472,9 @@ class coverage: def save(self): if self.usecache and self.cache: self.canonicalize_filenames() - cache = open(self.cache, 'wb') import marshal - marshal.dump(self.cexecuted, cache) - cache.close() + with open(self.cache, 'wb') as cache: + marshal.dump(self.cexecuted, cache) # restore(). Restore coverage data from the coverage cache (if it exists). @@ -488,10 +487,9 @@ class coverage: def restore_file(self, file_name): try: - cache = open(file_name, 'rb') import marshal - cexecuted = marshal.load(cache) - cache.close() + with open(file_name, 'rb') as cache: + cexecuted = marshal.load(cache) if isinstance(cexecuted, dict): return cexecuted else: @@ -614,8 +612,8 @@ class coverage: ) filename = filename[:-1] if not source: - sourcef = open(filename, 'rU') - source = sourcef.read() + with open(filename, 'rU') as sourcef: + source = sourcef.read() try: lines, excluded_lines, line_map = self.find_executable_statements( source, exclude=self.exclude_re @@ -625,8 +623,6 @@ class coverage: "Couldn't parse '%s' as Python source: '%s' at line %d" % (filename, synerr.msg, synerr.lineno) ) - if sourcef: - sourcef.close() result = filename, lines, excluded_lines, line_map self.analysis_cache[morf] = result return result diff --git a/tests/path.py b/tests/path.py index 901c9ce96..959b54875 100755 --- a/tests/path.py +++ b/tests/path.py @@ -206,10 +206,13 @@ class path(text_type): class _repr_text(text_type): def __repr__(self): return self._repr + + class _repr_bin(binary_type): def __repr__(self): return self._repr + def repr_as(string, repr_): wrapper = _repr_text if isinstance(string, text_type) else _repr_bin proxy = wrapper(string) diff --git a/tests/root/img.foo.png b/tests/root/img.foo.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/root/img.foo.png and b/tests/root/img.foo.png differ diff --git a/tests/root/img.png b/tests/root/img.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/root/img.png and b/tests/root/img.png differ diff --git a/tests/root/markup.txt b/tests/root/markup.txt index cc1fa2a63..77f245a6f 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -220,6 +220,13 @@ Tables with multirow and multicol: | | +----+ +.. list-table:: + :header-rows: 0 + + * - .. figure:: img.png + + figure in table + Figures ------- @@ -246,6 +253,12 @@ Figures figure with align & figwidth option +.. figure:: rimg.png + :align: right + :width: 3cm + + figure with align & width option + Version markup -------------- diff --git a/tests/root/rimg.png b/tests/root/rimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/root/rimg.png and b/tests/root/rimg.png differ diff --git a/tests/root/subdir/img.png b/tests/root/subdir/img.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/root/subdir/img.png and b/tests/root/subdir/img.png differ diff --git a/tests/root/subdir/simg.png b/tests/root/subdir/simg.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/root/subdir/simg.png and b/tests/root/subdir/simg.png differ diff --git a/tests/root/testtheme/static/staticimg.png b/tests/root/testtheme/static/staticimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/root/testtheme/static/staticimg.png and b/tests/root/testtheme/static/staticimg.png differ diff --git a/tests/roots/test-add_enumerable_node/rimg.png b/tests/roots/test-add_enumerable_node/rimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-add_enumerable_node/rimg.png and b/tests/roots/test-add_enumerable_node/rimg.png differ diff --git a/tests/roots/test-config/conf.py b/tests/roots/test-config/conf.py index b6075e5cb..1e583d1e0 100644 --- a/tests/roots/test-config/conf.py +++ b/tests/roots/test-config/conf.py @@ -1,4 +1,4 @@ -from sphinx.config import string_classes +from sphinx.config import string_classes, ENUM value1 = 123 # wrong type value2 = 123 # lambda with wrong type @@ -45,3 +45,4 @@ def setup(app): app.add_config_value('value14', None, False, string_classes) app.add_config_value('value15', u'unicode', False) app.add_config_value('value16', u'unicode', False) + app.add_config_value('value17', 'default', False, ENUM('default', 'one', 'two')) diff --git a/tests/roots/test-footnotes/rimg.png b/tests/roots/test-footnotes/rimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-footnotes/rimg.png and b/tests/roots/test-footnotes/rimg.png differ diff --git a/tests/roots/test-html_extra_path/conf.py b/tests/roots/test-html_assets/conf.py similarity index 63% rename from tests/roots/test-html_extra_path/conf.py rename to tests/roots/test-html_assets/conf.py index 53ee62197..a17e417a3 100644 --- a/tests/roots/test-html_extra_path/conf.py +++ b/tests/roots/test-html_assets/conf.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- master_doc = 'index' +project = 'Sphinx' +version = '1.4.4' +html_static_path = ['static', 'subdir'] html_extra_path = ['extra', 'subdir'] exclude_patterns = ['**/_build', '**/.htpasswd'] diff --git a/tests/roots/test-html_extra_path/extra/.htaccess b/tests/roots/test-html_assets/extra/.htaccess similarity index 100% rename from tests/roots/test-html_extra_path/extra/.htaccess rename to tests/roots/test-html_assets/extra/.htaccess diff --git a/tests/roots/test-html_extra_path/extra/.htpasswd b/tests/roots/test-html_assets/extra/.htpasswd similarity index 100% rename from tests/roots/test-html_extra_path/extra/.htpasswd rename to tests/roots/test-html_assets/extra/.htpasswd diff --git a/tests/roots/test-html_assets/extra/API.html_t b/tests/roots/test-html_assets/extra/API.html_t new file mode 100644 index 000000000..34ecd9df1 --- /dev/null +++ b/tests/roots/test-html_assets/extra/API.html_t @@ -0,0 +1 @@ +{{ project }}-{{ version }} diff --git a/tests/roots/test-html_extra_path/extra/css/style.css b/tests/roots/test-html_assets/extra/css/style.css similarity index 100% rename from tests/roots/test-html_extra_path/extra/css/style.css rename to tests/roots/test-html_assets/extra/css/style.css diff --git a/tests/roots/test-html_assets/extra/rimg.png b/tests/roots/test-html_assets/extra/rimg.png new file mode 100644 index 000000000..fda6cd29e Binary files /dev/null and b/tests/roots/test-html_assets/extra/rimg.png differ diff --git a/tests/roots/test-html_extra_path/extra/API.html_t b/tests/roots/test-html_assets/extra/subdir/.htaccess similarity index 100% rename from tests/roots/test-html_extra_path/extra/API.html_t rename to tests/roots/test-html_assets/extra/subdir/.htaccess diff --git a/tests/roots/test-html_extra_path/subdir/_build/index.html b/tests/roots/test-html_assets/extra/subdir/.htpasswd similarity index 100% rename from tests/roots/test-html_extra_path/subdir/_build/index.html rename to tests/roots/test-html_assets/extra/subdir/.htpasswd diff --git a/tests/roots/test-html_extra_path/index.rst b/tests/roots/test-html_assets/index.rst similarity index 100% rename from tests/roots/test-html_extra_path/index.rst rename to tests/roots/test-html_assets/index.rst diff --git a/tests/roots/test-html_assets/static/.htaccess b/tests/roots/test-html_assets/static/.htaccess new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-html_assets/static/.htpasswd b/tests/roots/test-html_assets/static/.htpasswd new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-html_assets/static/API.html_t b/tests/roots/test-html_assets/static/API.html_t new file mode 100644 index 000000000..34ecd9df1 --- /dev/null +++ b/tests/roots/test-html_assets/static/API.html_t @@ -0,0 +1 @@ +{{ project }}-{{ version }} diff --git a/tests/roots/test-html_assets/static/css/style.css b/tests/roots/test-html_assets/static/css/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-html_assets/static/rimg.png b/tests/roots/test-html_assets/static/rimg.png new file mode 100644 index 000000000..fda6cd29e Binary files /dev/null and b/tests/roots/test-html_assets/static/rimg.png differ diff --git a/tests/roots/test-html_assets/static/subdir/.htaccess b/tests/roots/test-html_assets/static/subdir/.htaccess new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-html_assets/static/subdir/.htpasswd b/tests/roots/test-html_assets/static/subdir/.htpasswd new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-html_assets/subdir/_build/index.html b/tests/roots/test-html_assets/subdir/_build/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-html_assets/subdir/background.png b/tests/roots/test-html_assets/subdir/background.png new file mode 100644 index 000000000..fda6cd29e Binary files /dev/null and b/tests/roots/test-html_assets/subdir/background.png differ diff --git a/tests/roots/test-html_extra_path/extra/rimg.png b/tests/roots/test-html_extra_path/extra/rimg.png deleted file mode 100644 index 1081dc143..000000000 Binary files a/tests/roots/test-html_extra_path/extra/rimg.png and /dev/null differ diff --git a/tests/roots/test-html_extra_path/subdir/background.png b/tests/roots/test-html_extra_path/subdir/background.png deleted file mode 100644 index 1081dc143..000000000 Binary files a/tests/roots/test-html_extra_path/subdir/background.png and /dev/null differ diff --git a/tests/roots/test-image-glob/img.ja.png b/tests/roots/test-image-glob/img.ja.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/roots/test-image-glob/img.ja.png and b/tests/roots/test-image-glob/img.ja.png differ diff --git a/tests/roots/test-image-glob/img.png b/tests/roots/test-image-glob/img.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/roots/test-image-glob/img.png and b/tests/roots/test-image-glob/img.png differ diff --git a/tests/roots/test-image-glob/img.zh.png b/tests/roots/test-image-glob/img.zh.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/roots/test-image-glob/img.zh.png and b/tests/roots/test-image-glob/img.zh.png differ diff --git a/tests/roots/test-image-glob/rimg.png b/tests/roots/test-image-glob/rimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-image-glob/rimg.png and b/tests/roots/test-image-glob/rimg.png differ diff --git a/tests/roots/test-image-glob/rimg.xx.png b/tests/roots/test-image-glob/rimg.xx.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-image-glob/rimg.xx.png and b/tests/roots/test-image-glob/rimg.xx.png differ diff --git a/tests/roots/test-image-glob/subdir/rimg.png b/tests/roots/test-image-glob/subdir/rimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-image-glob/subdir/rimg.png and b/tests/roots/test-image-glob/subdir/rimg.png differ diff --git a/tests/roots/test-image-glob/subdir/rimg.xx.png b/tests/roots/test-image-glob/subdir/rimg.xx.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-image-glob/subdir/rimg.xx.png and b/tests/roots/test-image-glob/subdir/rimg.xx.png differ diff --git a/tests/roots/test-image-glob/testimäge.png b/tests/roots/test-image-glob/testimäge.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/roots/test-image-glob/testimäge.png and b/tests/roots/test-image-glob/testimäge.png differ diff --git a/tests/roots/test-image-in-section/pic.png b/tests/roots/test-image-in-section/pic.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-image-in-section/pic.png and b/tests/roots/test-image-in-section/pic.png differ diff --git a/tests/roots/test-intl/i18n.png b/tests/roots/test-intl/i18n.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/roots/test-intl/i18n.png and b/tests/roots/test-intl/i18n.png differ diff --git a/tests/roots/test-intl/img.png b/tests/roots/test-intl/img.png index 4c8f89929..a97e86d66 100644 Binary files a/tests/roots/test-intl/img.png and b/tests/roots/test-intl/img.png differ diff --git a/tests/roots/test-numfig/rimg.png b/tests/roots/test-numfig/rimg.png index 1081dc143..fda6cd29e 100644 Binary files a/tests/roots/test-numfig/rimg.png and b/tests/roots/test-numfig/rimg.png differ diff --git a/tests/run.py b/tests/run.py index b4bbf9822..7b750ca92 100755 --- a/tests/run.py +++ b/tests/run.py @@ -16,6 +16,7 @@ import sys import traceback from path import path +import nose testroot = os.path.dirname(__file__) or '.' sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir))) @@ -23,7 +24,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir))) # check dependencies before testing print('Checking dependencies...') for modname in ('nose', 'mock', 'six', 'docutils', 'jinja2', 'pygments', - 'snowballstemmer', 'babel'): + 'snowballstemmer', 'babel', 'html5lib'): try: __import__(modname) except ImportError as err: @@ -47,5 +48,4 @@ tempdir.makedirs() print('Running Sphinx test suite (with Python %s)...' % sys.version.split()[0]) sys.stdout.flush() -import nose nose.main() diff --git a/tests/test_api_translator.py b/tests/test_api_translator.py index 8f0f88a1d..e8cfcb458 100644 --- a/tests/test_api_translator.py +++ b/tests/test_api_translator.py @@ -67,7 +67,7 @@ def test_html_with_set_translator_for_html_and_html_translator_class( assert translator_class.__name__ == 'ConfHTMLTranslator' -## this test break test_websupport.test_comments test. why? +# this test break test_websupport.test_comments test. why? # @with_app( # buildername='dirhtml', # srcdir=(test_roots / 'test-api-set-translator'), diff --git a/tests/test_application.py b/tests/test_application.py index 7bc970c9d..ad4f84870 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -14,7 +14,7 @@ from docutils import nodes from sphinx.application import ExtensionError from sphinx.domains import Domain -from util import with_app, raises_msg +from util import with_app, raises_msg, strip_escseq @with_app() @@ -60,14 +60,21 @@ def test_output(app, status, warning): old_count = app._warncount app.warn("Bad news!") - assert warning.getvalue() == "WARNING: Bad news!\n" + assert strip_escseq(warning.getvalue()) == "WARNING: Bad news!\n" assert app._warncount == old_count + 1 @with_app() def test_extensions(app, status, warning): app.setup_extension('shutil') - assert warning.getvalue().startswith("WARNING: extension 'shutil'") + assert strip_escseq(warning.getvalue()).startswith("WARNING: extension 'shutil'") + + +@with_app() +def test_extension_in_blacklist(app, status, warning): + app.setup_extension('sphinxjp.themecore') + msg = strip_escseq(warning.getvalue()) + assert msg.startswith("WARNING: the extension 'sphinxjp.themecore' was") @with_app() diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 183484846..747746478 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -11,7 +11,7 @@ """ # "raises" imported for usage by autodoc -from util import TestApp, Struct, raises, SkipTest +from util import TestApp, Struct, raises, SkipTest # NOQA from nose.tools import with_setup, eq_ from six import StringIO @@ -22,6 +22,7 @@ from sphinx.ext.autodoc import AutoDirective, add_documenter, \ app = None + def setup_module(): global app app = TestApp() @@ -38,6 +39,7 @@ def teardown_module(): directive = options = None + def setup_test(): global options, directive global processed_docstrings, processed_signatures, _warnings @@ -73,19 +75,19 @@ def setup_test(): _warnings = [] +processed_docstrings = [] +processed_signatures = [] + def warnfunc(msg): _warnings.append(msg) -processed_docstrings = [] - def process_docstring(app, what, name, obj, options, lines): processed_docstrings.append((what, name)) if name == 'bar': lines.extend(['42', '']) -processed_signatures = [] def process_signature(app, what, name, obj, options, args, retann): processed_signatures.append((what, name)) @@ -164,25 +166,28 @@ def test_format_signature(): # test for functions def f(a, b, c=1, **d): pass + def g(a='\n'): pass assert formatsig('function', 'f', f, None, None) == '(a, b, c=1, **d)' assert formatsig('function', 'f', f, 'a, b, c, d', None) == '(a, b, c, d)' - assert formatsig('function', 'f', f, None, 'None') == \ - '(a, b, c=1, **d) -> None' + assert formatsig('function', 'f', f, None, 'None') == '(a, b, c=1, **d) -> None' assert formatsig('function', 'g', g, None, None) == r"(a='\\n')" # test for classes class D: pass + class E(object): pass # no signature for classes without __init__ for C in (D, E): assert formatsig('class', 'D', C, None, None) == '' + class F: def __init__(self, a, b=None): pass + class G(F, object): pass for C in (F, G): @@ -191,6 +196,7 @@ def test_format_signature(): # __init__ have signature at first line of docstring directive.env.config.autoclass_content = 'both' + class F2: '''some docstring for F2.''' def __init__(self, *args, **kw): @@ -211,8 +217,10 @@ def test_format_signature(): class H: def foo1(self, b, *c): pass + def foo2(b, *c): pass + def foo3(self, d='\n'): pass assert formatsig('method', 'H.foo', H.foo1, None, None) == '(b, *c)' @@ -431,6 +439,7 @@ def test_docstring_processing(): lid = app.connect('autodoc-process-docstring', cut_lines(1, 1, ['function'])) + def f(): """ first line @@ -441,6 +450,7 @@ def test_docstring_processing(): app.disconnect(lid) lid = app.connect('autodoc-process-docstring', between('---', ['function'])) + def g(): """ first line @@ -452,8 +462,9 @@ def test_docstring_processing(): assert process('function', 'g', g) == ['second line', ''] app.disconnect(lid) - lid = app.connect('autodoc-process-docstring', between('---', ['function'], - exclude=True)) + lid = app.connect('autodoc-process-docstring', + between('---', ['function'], exclude=True)) + def h(): """ first line @@ -522,7 +533,7 @@ def test_new_documenter(): def assert_result_contains(item, objtype, name, **kw): inst = AutoDirective._registry[objtype](directive, name) inst.generate(**kw) - #print '\n'.join(directive.result) + # print '\n'.join(directive.result) assert len(_warnings) == 0, _warnings assert item in directive.result del directive.result[:] @@ -535,6 +546,7 @@ def test_new_documenter(): def test_attrgetter_using(): def assert_getter_works(objtype, name, obj, attrs=[], **kw): getattr_spy = [] + def special_getattr(obj, name, *defargs): if name in attrs: getattr_spy.append((obj, name)) @@ -556,12 +568,10 @@ def test_attrgetter_using(): options.members = ALL options.inherited_members = False - assert_getter_works('class', 'test_autodoc.Class', Class, - ['meth']) + assert_getter_works('class', 'test_autodoc.Class', Class, ['meth']) options.inherited_members = True - assert_getter_works('class', 'test_autodoc.Class', Class, - ['meth', 'inheritedmeth']) + assert_getter_works('class', 'test_autodoc.Class', Class, ['meth', 'inheritedmeth']) @with_setup(setup_test) @@ -578,7 +588,7 @@ def test_generate(): inst = AutoDirective._registry[objtype](directive, name) inst.generate(**kw) assert directive.result - #print '\n'.join(directive.result) + # print '\n'.join(directive.result) assert len(_warnings) == 0, _warnings del directive.result[:] @@ -586,13 +596,12 @@ def test_generate(): del processed_docstrings[:] del processed_signatures[:] assert_works(objtype, name, **kw) - assert set(processed_docstrings) | set(processed_signatures) == \ - set(items) + assert set(processed_docstrings) | set(processed_signatures) == set(items) def assert_result_contains(item, objtype, name, **kw): inst = AutoDirective._registry[objtype](directive, name) inst.generate(**kw) - #print '\n'.join(directive.result) + # print '\n'.join(directive.result) assert len(_warnings) == 0, _warnings assert item in directive.result del directive.result[:] @@ -604,17 +613,17 @@ def test_generate(): assert len(_warnings) == 0, _warnings items = list(reversed(items)) lineiter = iter(directive.result) - #for line in directive.result: - # if line.strip(): - # print repr(line) + # for line in directive.result: + # if line.strip(): + # print repr(line) while items: item = items.pop() for line in lineiter: if line == item: break else: # ran out of items! - assert False, 'item %r not found in result or not in the ' \ - ' correct order' % item + assert False, ('item %r not found in result or not in the ' + ' correct order' % item) del directive.result[:] options.members = [] @@ -824,19 +833,19 @@ def test_generate(): 'module', 'test_autodoc') # --- generate fodder ------------ -import six, sys - __all__ = ['Class'] #: documentation for the integer integer = 1 + class CustomEx(Exception): """My custom exception.""" def f(self): """Exception method.""" + class CustomDataDescriptor(object): """Descriptor class docstring.""" @@ -852,6 +861,7 @@ class CustomDataDescriptor(object): """Function.""" return "The Answer" + def _funky_classmethod(name, b, c, d, docstring=None): """Generates a classmethod for a class from a template by filling out some arguments.""" @@ -863,6 +873,7 @@ def _funky_classmethod(name, b, c, d, docstring=None): function.__doc__ = docstring return classmethod(function) + class Base(object): def inheritedmeth(self): """Inherited function.""" @@ -908,10 +919,10 @@ class Class(Base): roger = _funky_classmethod("roger", 2, 3, 4) moore = _funky_classmethod("moore", 9, 8, 7, - docstring="moore(a, e, f) -> happiness") + docstring="moore(a, e, f) -> happiness") def __init__(self, arg): - self.inst_attr_inline = None #: an inline documented instance attr + self.inst_attr_inline = None #: an inline documented instance attr #: a documented instance attribute self.inst_attr_comment = None self.inst_attr_string = None @@ -928,6 +939,7 @@ class Class(Base): class CustomDict(dict): """Docstring.""" + def function(foo, *args, **kwds): """ Return spam. @@ -977,14 +989,17 @@ First line of docstring """ return 456 + class StrRepr(str): def __repr__(self): return self + class AttCls(object): a1 = StrRepr('hello\nworld') a2 = None + class InstAttCls(object): """Class with documented class and instance attributes.""" diff --git a/tests/test_autodoc_py35.py b/tests/test_autodoc_py35.py index 9bacc3d65..43cda2260 100644 --- a/tests/test_autodoc_py35.py +++ b/tests/test_autodoc_py35.py @@ -11,6 +11,8 @@ """ # "raises" imported for usage by autodoc +import six +import sys from util import TestApp, Struct, raises, SkipTest from nose.tools import with_setup, eq_ @@ -22,6 +24,7 @@ from sphinx.ext.autodoc import AutoDirective, add_documenter, \ app = None + def setup_module(): global app app = TestApp() @@ -38,6 +41,7 @@ def teardown_module(): directive = options = None + def setup_test(): global options, directive global processed_docstrings, processed_signatures, _warnings @@ -73,19 +77,19 @@ def setup_test(): _warnings = [] +processed_docstrings = [] +processed_signatures = [] + def warnfunc(msg): _warnings.append(msg) -processed_docstrings = [] - def process_docstring(app, what, name, obj, options, lines): processed_docstrings.append((what, name)) if name == 'bar': lines.extend(['42', '']) -processed_signatures = [] def process_signature(app, what, name, obj, options, args, retann): processed_signatures.append((what, name)) @@ -116,7 +120,7 @@ def test_generate(): inst = AutoDirective._registry[objtype](directive, name) inst.generate(**kw) assert directive.result - #print '\n'.join(directive.result) + # print '\n'.join(directive.result) assert len(_warnings) == 0, _warnings del directive.result[:] @@ -124,13 +128,12 @@ def test_generate(): del processed_docstrings[:] del processed_signatures[:] assert_works(objtype, name, **kw) - assert set(processed_docstrings) | set(processed_signatures) == \ - set(items) + assert set(processed_docstrings) | set(processed_signatures) == set(items) def assert_result_contains(item, objtype, name, **kw): inst = AutoDirective._registry[objtype](directive, name) inst.generate(**kw) - #print '\n'.join(directive.result) + # print '\n'.join(directive.result) assert len(_warnings) == 0, _warnings assert item in directive.result del directive.result[:] @@ -142,17 +145,17 @@ def test_generate(): assert len(_warnings) == 0, _warnings items = list(reversed(items)) lineiter = iter(directive.result) - #for line in directive.result: - # if line.strip(): - # print repr(line) + # for line in directive.result: + # if line.strip(): + # print repr(line) while items: item = items.pop() for line in lineiter: if line == item: break else: # ran out of items! - assert False, 'item %r not found in result or not in the ' \ - ' correct order' % item + assert False, ('item %r not found in result or not in the ' + ' correct order' % item) del directive.result[:] options.members = [] @@ -233,19 +236,19 @@ def test_generate(): # --- generate fodder ------------ -import six, sys - __all__ = ['Class'] #: documentation for the integer integer = 1 + class CustomEx(Exception): """My custom exception.""" def f(self): """Exception method.""" + class CustomDataDescriptor(object): """Descriptor class docstring.""" @@ -261,6 +264,7 @@ class CustomDataDescriptor(object): """Function.""" return "The Answer" + def _funky_classmethod(name, b, c, d, docstring=None): """Generates a classmethod for a class from a template by filling out some arguments.""" @@ -272,6 +276,7 @@ def _funky_classmethod(name, b, c, d, docstring=None): function.__doc__ = docstring return classmethod(function) + class Base(object): def inheritedmeth(self): """Inherited function.""" @@ -322,10 +327,10 @@ class Class(Base): roger = _funky_classmethod("roger", 2, 3, 4) moore = _funky_classmethod("moore", 9, 8, 7, - docstring="moore(a, e, f) -> happiness") + docstring="moore(a, e, f) -> happiness") def __init__(self, arg): - self.inst_attr_inline = None #: an inline documented instance attr + self.inst_attr_inline = None #: an inline documented instance attr #: a documented instance attribute self.inst_attr_comment = None self.inst_attr_string = None diff --git a/tests/test_build.py b/tests/test_build.py index 1c0d55e1b..95910f60b 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -15,6 +15,7 @@ import pickle from docutils import nodes from textwrap import dedent from sphinx.errors import SphinxError +import sphinx.builders.linkcheck from util import with_app, rootdir, tempdir, SkipTest, TestApp @@ -31,7 +32,6 @@ class MockOpener(object): url = req.url return result() -import sphinx.builders.linkcheck sphinx.builders.linkcheck.opener = MockOpener() diff --git a/tests/test_build_applehelp.py b/tests/test_build_applehelp.py index 66b24e1bd..de2b5d032 100644 --- a/tests/test_build_applehelp.py +++ b/tests/test_build_applehelp.py @@ -11,7 +11,6 @@ :license: BSD, see LICENSE for details. """ -import os import plistlib from util import with_app diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 22fec975e..2dc62b838 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -114,7 +114,7 @@ def test_gettext_index_entries(app, status, warning): "Exception", "Statement", "Builtin", - ] + ] for expect in expected_msgids: assert expect in msgids msgids.remove(expect) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 87ea8c595..aac5757d9 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -13,34 +13,37 @@ import os import re from six import PY3, iteritems -from six.moves import html_entities from sphinx import __display_version__ -from util import remove_unicode_literals, gen_with_app, with_app -from etree13 import ElementTree as ET +from util import remove_unicode_literals, gen_with_app, with_app, strip_escseq +from etree13 import ElementTree +from html5lib import getTreeBuilder, HTMLParser +TREE_BUILDER = getTreeBuilder('etree', implementation=ElementTree) +HTML_PARSER = HTMLParser(TREE_BUILDER, namespaceHTMLElements=False) + ENV_WARNINGS = """\ (%(root)s/autodoc_fodder.py:docstring of autodoc_fodder\\.MarkupError:2: \ WARNING: Explicit markup ends without a blank line; unexpected \ unindent\\.\\n? -)?%(root)s/images.txt:9: WARNING: image file not readable: foo.png -%(root)s/images.txt:23: WARNING: nonlocal image URI found: \ +)?%(root)s/images.txt:\\d+: WARNING: image file not readable: foo.png +%(root)s/images.txt:\\d+3: WARNING: nonlocal image URI found: \ http://www.python.org/logo.png -%(root)s/includes.txt:\\d*: WARNING: Encoding 'utf-8-sig' used for \ +%(root)s/includes.txt:\\d+: WARNING: Encoding 'utf-8-sig' used for \ reading included file u'.*?wrongenc.inc' seems to be wrong, try giving an \ :encoding: option\\n? -%(root)s/includes.txt:4: WARNING: download file not readable: .*?nonexisting.png -(%(root)s/markup.txt:373: WARNING: invalid single index entry u'')? -(%(root)s/undecodable.txt:3: WARNING: undecodable source characters, replacing \ +%(root)s/includes.txt:\\d+: WARNING: download file not readable: .*?nonexisting.png +(%(root)s/markup.txt:\\d+: WARNING: invalid single index entry u'')? +(%(root)s/undecodable.txt:\\d+: WARNING: undecodable source characters, replacing \ with "\\?": b?'here: >>>(\\\\|/)xbb<<<' )?""" HTML_WARNINGS = ENV_WARNINGS + """\ -%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' -%(root)s/markup.txt:285: WARNING: Could not lex literal_block as "c". Highlighting skipped. -%(root)s/footnote.txt:60: WARNING: citation not found: missing -%(root)s/markup.txt:164: WARNING: unknown option: &option +%(root)s/images.txt:\\d+: WARNING: no matching candidate for image URI u'foo.\\*' +%(root)s/markup.txt:\\d+: WARNING: Could not lex literal_block as "c". Highlighting skipped. +%(root)s/footnote.txt:\\d+: WARNING: citation not found: missing +%(root)s/markup.txt:\\d+: WARNING: unknown option: &option """ if PY3: @@ -65,6 +68,7 @@ HTML_XPATH = { (".//img[@src='_images/img1.png']", ''), (".//img[@src='_images/simg.png']", ''), (".//img[@src='_images/svgimg.svg']", ''), + (".//a[@href='_sources/images.txt']", ''), ], 'subdir/images.html': [ (".//img[@src='../_images/img1.png']", ''), @@ -174,7 +178,7 @@ HTML_XPATH = { # ``seealso`` directive (".//div/p[@class='first admonition-title']", 'See also'), # a ``hlist`` directive - (".//table[@class='hlist']/tr/td/ul/li", '^This$'), + (".//table[@class='hlist']/tbody/tr/td/ul/li", '^This$'), # a ``centered`` directive (".//p[@class='centered']/strong", 'LICENSE'), # a glossary @@ -315,25 +319,11 @@ HTML_XPATH = { ], 'otherext.html': [ (".//h1", "Generated section"), + (".//a[@href='_sources/otherext.foo.txt']", ''), ] } -class NslessParser(ET.XMLParser): - """XMLParser that throws away namespaces in tag names.""" - - def _fixname(self, key): - try: - return self._names[key] - except KeyError: - name = key - br = name.find('}') - if br > 0: - name = name[br+1:] - self._names[key] = name = self._fixtext(name) - return name - - def check_xpath(etree, fname, path, check, be_found=True): nodes = list(etree.findall(path)) if check is None: @@ -396,7 +386,7 @@ def check_extra_entries(outdir): tags=['testtag']) def test_html_output(app, status, warning): app.builder.build_all() - html_warnings = warning.getvalue().replace(os.sep, '/') + html_warnings = strip_escseq(warning.getvalue().replace(os.sep, '/')) html_warnings_exp = HTML_WARNINGS % { 'root': re.escape(app.srcdir.replace(os.sep, '/'))} assert re.match(html_warnings_exp + '$', html_warnings), \ @@ -405,10 +395,8 @@ def test_html_output(app, status, warning): '--- Got:\n' + html_warnings for fname, paths in iteritems(HTML_XPATH): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for path, check in paths: yield check_xpath, etree, fname, path, check @@ -455,10 +443,8 @@ def test_tocdepth(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -497,10 +483,8 @@ def test_tocdepth_singlehtml(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -553,10 +537,8 @@ def test_numfig_disabled(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -596,7 +578,7 @@ def test_numfig_without_numbered_toctree(app, status, warning): (".//li/a/span", '^Table:6$', True), (".//li/a/span", '^Listing 9$', True), (".//li/a/span", '^Code-6$', True), - ], + ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1 $', True), @@ -622,7 +604,7 @@ def test_numfig_without_numbered_toctree(app, status, warning): "span[@class='caption-number']", '^Listing 3 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 4 $', True), - ], + ], 'bar.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 5 $', True), @@ -654,10 +636,8 @@ def test_numfig_without_numbered_toctree(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -693,7 +673,7 @@ def test_numfig_with_numbered_toctree(app, status, warning): (".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Code-2.2$', True), - ], + ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), @@ -719,7 +699,7 @@ def test_numfig_with_numbered_toctree(app, status, warning): "span[@class='caption-number']", '^Listing 1.3 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 1.4 $', True), - ], + ], 'bar.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1 $', True), @@ -751,10 +731,8 @@ def test_numfig_with_numbered_toctree(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -793,7 +771,7 @@ def test_numfig_with_prefix(app, status, warning): (".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Code-1$', True), (".//li/a/span", '^Code-2.2$', True), - ], + ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:1.1 $', True), @@ -819,7 +797,7 @@ def test_numfig_with_prefix(app, status, warning): "span[@class='caption-number']", '^Code-1.3 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Code-1.4 $', True), - ], + ], 'bar.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Figure:2.1 $', True), @@ -851,10 +829,8 @@ def test_numfig_with_prefix(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -890,7 +866,7 @@ def test_numfig_with_secnum_depth(app, status, warning): (".//li/a/span", '^Table:2.1.2$', True), (".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Code-2.1.2$', True), - ], + ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), @@ -916,7 +892,7 @@ def test_numfig_with_secnum_depth(app, status, warning): "span[@class='caption-number']", '^Listing 1.1.2 $', True), (".//div[@class='code-block-caption']/" "span[@class='caption-number']", '^Listing 1.2.1 $', True), - ], + ], 'bar.html': [ (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 2.1.1 $', True), @@ -948,10 +924,8 @@ def test_numfig_with_secnum_depth(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -980,10 +954,8 @@ def test_enumerable_node(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -996,18 +968,32 @@ def test_jsmath(app, status, warning): assert '
    \na^2 + b^2 = c^2
    ' in content assert '
    \n\\begin{split}a + 1 < b\\end{split}
    ' in content - assert ('(1)
    \n' - 'e^{i\\pi} = 1
    ' in content) + assert (u'(1)\xb6' + u'
    \ne^{i\\pi} = 1
    ' in content) assert ('(2)
    \n' 'e^{ix} = \\cos x + i\\sin x
    ' in content) assert '
    \nn \\in \\mathbb N
    ' in content assert '
    \na + 1 < b
    ' in content -@with_app(buildername='html', testroot='html_extra_path') -def test_html_extra_path(app, status, warning): +@with_app(buildername='html', testroot='html_assets') +def test_html_assets(app, status, warning): app.builder.build_all() + # html_static_path + assert not (app.outdir / '_static' / '.htaccess').exists() + assert not (app.outdir / '_static' / '.htpasswd').exists() + assert (app.outdir / '_static' / 'API.html').exists() + assert (app.outdir / '_static' / 'API.html').text() == 'Sphinx-1.4.4' + assert (app.outdir / '_static' / 'css/style.css').exists() + assert (app.outdir / '_static' / 'rimg.png').exists() + assert not (app.outdir / '_static' / '_build/index.html').exists() + assert (app.outdir / '_static' / 'background.png').exists() + assert not (app.outdir / '_static' / 'subdir' / '.htaccess').exists() + assert not (app.outdir / '_static' / 'subdir' / '.htpasswd').exists() + + # html_extra_path assert (app.outdir / '.htaccess').exists() assert not (app.outdir / '.htpasswd').exists() assert (app.outdir / 'API.html_t').exists() @@ -1015,3 +1001,17 @@ def test_html_extra_path(app, status, warning): assert (app.outdir / 'rimg.png').exists() assert not (app.outdir / '_build/index.html').exists() assert (app.outdir / 'background.png').exists() + assert (app.outdir / 'subdir' / '.htaccess').exists() + assert not (app.outdir / 'subdir' / '.htpasswd').exists() + + +@with_app(buildername='html', confoverrides={'html_sourcelink_suffix': ''}) +def test_html_sourcelink_suffix(app, status, warning): + app.builder.build_all() + content_otherext = (app.outdir / 'otherext.html').text() + content_images = (app.outdir / 'images.html').text() + + assert ' g)', 'f', '1f') + def test_member_definitions(): check('member', ' const std::string & name = 42', @@ -397,11 +402,11 @@ def test_templates(): "RK18c_string_view_baseIK4Char6TraitsE") -#def test_print(): -# # used for getting all the ids out for checking -# for a in ids: -# print(a) -# raise DefinitionError("") +# def test_print(): +# # used for getting all the ids out for checking +# for a in ids: +# print(a) +# raise DefinitionError("") @with_app(testroot='domain-cpp', confoverrides={'add_function_parentheses': True}) diff --git a/tests/test_domain_rst.py b/tests/test_domain_rst.py index c1f8c6caa..0d6dab5d8 100644 --- a/tests/test_domain_rst.py +++ b/tests/test_domain_rst.py @@ -11,8 +11,8 @@ from sphinx.domains.rst import parse_directive -def test_parse_directive(): +def test_parse_directive(): s = parse_directive(u' foö ') assert s == (u'foö', '') diff --git a/tests/test_ext_githubpages.py b/tests/test_ext_githubpages.py index 65276df7a..f74ed5315 100644 --- a/tests/test_ext_githubpages.py +++ b/tests/test_ext_githubpages.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for details. """ -import re - from util import with_app diff --git a/tests/test_ext_ifconfig.py b/tests/test_ext_ifconfig.py index 56e31acae..4ae043f7f 100644 --- a/tests/test_ext_ifconfig.py +++ b/tests/test_ext_ifconfig.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for details. """ -import re - from util import with_app diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py index 13b5d1aff..90987a3ef 100644 --- a/tests/test_ext_intersphinx.py +++ b/tests/test_ext_intersphinx.py @@ -43,6 +43,7 @@ module2 py:module 0 foo.html#module-$ - module1.func py:function 1 sub/foo.html#$ - CFunc c:function 2 cfunc.html#CFunc - a term std:term -1 glossary.html#term-a-term - +a term including:colon std:term -1 glossary.html#term-a-term-including-colon - '''.encode('utf-8')) @@ -78,6 +79,8 @@ def test_read_inventory_v2(): assert invdata1['c:function']['CFunc'][2] == '/util/cfunc.html#CFunc' assert invdata1['std:term']['a term'][2] == \ '/util/glossary.html#term-a-term' + assert invdata1['std:term']['a term including:colon'][2] == \ + '/util/glossary.html#term-a-term-including-colon' @with_app() diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 87219c294..4b168b40c 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -28,6 +28,7 @@ def test_imgmath_png(app, status, warning): '\s*alt="a\^2\+b\^2=c\^2"/>\s*

    \s*') assert re.search(html, content, re.S) + @with_app('html', testroot='ext-math', confoverrides={'extensions': ['sphinx.ext.imgmath'], 'imgmath_image_format': 'svg'}) @@ -43,6 +44,7 @@ def test_imgmath_svg(app, status, warning): '\s*alt="a\^2\+b\^2=c\^2"/>\s*

    \s*') assert re.search(html, content, re.S) + @with_app('html', testroot='ext-math', confoverrides={'extensions': ['sphinx.ext.mathjax']}) def test_mathjax_align(app, status, warning): @@ -54,6 +56,7 @@ def test_mathjax_align(app, status, warning): r'V \&= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]') assert re.search(html, content, re.S) + @with_app('html', testroot='ext-math', confoverrides={'math_number_all': True, 'extensions': ['sphinx.ext.mathjax']}) @@ -65,6 +68,7 @@ def test_math_number_all_mathjax(app, status, warning): r'\(1\)\\\[a\^2\+b\^2=c\^2\\\]') assert re.search(html, content, re.S) + @with_app('latex', testroot='ext-math', confoverrides={'extensions': ['sphinx.ext.mathjax']}) def test_math_number_all_latex(app, status, warning): diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index ece55ebe4..b6f16b26b 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -329,7 +329,9 @@ Returns: codecode """ expected = """ -:returns: foo:: +:returns: + + foo:: codecode codecode diff --git a/tests/test_highlighting.py b/tests/test_highlighting.py index 176565f62..328abdf31 100644 --- a/tests/test_highlighting.py +++ b/tests/test_highlighting.py @@ -65,7 +65,7 @@ def test_detect_interactive(): def test_lexer_options(): bridge = PygmentsBridge('html') - ret = bridge.highlight_block('//comment', 'php', opts={'startinline' : True}) + ret = bridge.highlight_block('//comment', 'php', opts={'startinline': True}) assert '//comment' in ret diff --git a/tests/test_intl.py b/tests/test_intl.py index 47a51589d..b31f1678b 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -680,7 +680,7 @@ def test_xml_builder(app, status, warning): def test_additional_targets_should_not_be_translated(app, status, warning): app.builder.build_all() - ## literalblock.txt + # [literalblock.txt] result = (app.outdir / 'literalblock.html').text(encoding='utf-8') # title should be translated @@ -707,7 +707,7 @@ def test_additional_targets_should_not_be_translated(app, status, warning): """# sys importing""") yield assert_count(expected_expr, result, 1) - ## raw.txt + # [raw.txt] result = (app.outdir / 'raw.html').text(encoding='utf-8') @@ -715,7 +715,7 @@ def test_additional_targets_should_not_be_translated(app, status, warning): expected_expr = """""" yield assert_count(expected_expr, result, 1) - ## figure.txt + # [figure.txt] result = (app.outdir / 'figure.html').text(encoding='utf-8') @@ -741,7 +741,7 @@ def test_additional_targets_should_not_be_translated(app, status, warning): def test_additional_targets_should_be_translated(app, status, warning): app.builder.build_all() - ## literalblock.txt + # [literalblock.txt] result = (app.outdir / 'literalblock.html').text(encoding='utf-8') # title should be translated @@ -768,7 +768,7 @@ def test_additional_targets_should_be_translated(app, status, warning): """# SYS IMPORTING""") yield assert_count(expected_expr, result, 1) - ## raw.txt + # [raw.txt] result = (app.outdir / 'raw.html').text(encoding='utf-8') @@ -776,7 +776,7 @@ def test_additional_targets_should_be_translated(app, status, warning): expected_expr = """""" yield assert_count(expected_expr, result, 1) - ## figure.txt + # [figure.txt] result = (app.outdir / 'figure.html').text(encoding='utf-8') diff --git a/tests/test_markup.py b/tests/test_markup.py index d12138692..102235e1d 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -41,6 +41,7 @@ def setup_module(): def teardown_module(): app.cleanup() + # since we're not resolving the markup afterwards, these nodes may remain class ForgivingTranslator: def visit_pending_xref(self, node): @@ -74,11 +75,12 @@ def verify_re(rst, html_expected, latex_expected): if latex_expected: latex_translator = ForgivingLaTeXTranslator(document, app.builder) - latex_translator.first_document = -1 # don't write \begin{document} + latex_translator.first_document = -1 # don't write \begin{document} document.walkabout(latex_translator) latex_translated = ''.join(latex_translator.body).strip() assert re.match(latex_expected, latex_translated), 'from ' + repr(rst) + def verify(rst, html_expected, latex_expected): if html_expected: html_expected = re.escape(html_expected) + '$' @@ -91,32 +93,32 @@ def test_inline(): # correct interpretation of code with whitespace _html = ('

    ' 'code   sample

    ') - yield verify_re, '``code sample``', _html, r'\\code{code sample}' - yield verify_re, ':samp:`code sample`', _html, r'\\code{code sample}' + yield verify_re, '``code sample``', _html, r'\\sphinxcode{code sample}' + yield verify_re, ':samp:`code sample`', _html, r'\\sphinxcode{code sample}' # interpolation of braces in samp and file roles (HTML only) yield (verify, ':samp:`a{b}c`', '

    a' 'b' 'c

    ', - '\\code{a\\emph{b}c}') + '\\sphinxcode{a\\sphinxstyleemphasis{b}c}') # interpolation of arrows in menuselection yield (verify, ':menuselection:`a --> b`', u'

    a \N{TRIANGULAR BULLET} b

    ', - '\\menuselection{a \\(\\rightarrow\\) b}') + '\\sphinxmenuselection{a \\(\\rightarrow\\) b}') # interpolation of ampersands in guilabel/menuselection yield (verify, ':guilabel:`&Foo -&&- &Bar`', u'

    Foo ' '-&- Bar

    ', - r'\menuselection{\accelerator{F}oo -\&- \accelerator{B}ar}') + r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}') # non-interpolation of dashes in option role yield (verify_re, ':option:`--with-option`', '

    ' '--with-option

    $', - r'\\code{-{-}with-option}$') + r'\\sphinxcode{-{-}with-option}$') # verify smarty-pants quotes yield verify, '"John"', '

    “John”

    ', "``John''" @@ -124,12 +126,13 @@ def test_inline(): yield (verify, '``"John"``', '

    ' '"John"

    ', - '\\code{"John"}') + '\\sphinxcode{"John"}') # verify classes for inline roles yield (verify, ':manpage:`mp(1)`', '

    mp(1)

    ', - '\\emph{\\texttt{mp(1)}}') + '\\sphinxstyleliteralemphasis{mp(1)}') + def test_latex_escaping(): # correct escaping in normal mode @@ -137,9 +140,9 @@ def test_latex_escaping(): r'\(\Gamma\)\textbackslash{}\(\infty\)\$') # in verbatim code fragments yield (verify, u'::\n\n @Γ\\∞${}', None, - u'\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n' u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n' - u'\\end{Verbatim}') + u'\\end{sphinxVerbatim}') # in URIs yield (verify_re, u'`test `_', None, r'\\href{http://example.com/~me/}{test}.*') diff --git a/tests/test_search.py b/tests/test_search.py index 3edcf06e8..63d78df46 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -21,6 +21,7 @@ from util import with_app settings = parser = None + def setup_module(): global settings, parser optparser = frontend.OptionParser(components=(rst.Parser,)) @@ -45,13 +46,14 @@ FILE_CONTENTS = '''\ test that non-comments are indexed: fermion ''' + def test_wordcollector(): doc = utils.new_document(b'test data', settings) doc['file'] = 'dummy' parser.parse(FILE_CONTENTS, doc) ix = IndexBuilder(None, 'en', {}, None) - ix.feed('filename', 'title', doc) + ix.feed('docname', 'filename', 'title', doc) assert 'boson' not in ix._mapping assert 'fermion' in ix._mapping diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index f6a389fea..e8c5c9364 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -41,7 +41,7 @@ def search_adapter_helper(adapter): # Make sure documents are properly updated by the search adapter. s.init_indexing(changed=['markup']) - s.add_document(u'markup', u'title', u'SomeLongRandomWord') + s.add_document(u'markup', u'filename', u'title', u'SomeLongRandomWord') s.finish_indexing() # Now a search for "Epigraph" should return zero results. results = s.query(u'Epigraph') diff --git a/tests/test_setup_command.py b/tests/test_setup_command.py index 77c9ade46..c92f6220f 100644 --- a/tests/test_setup_command.py +++ b/tests/test_setup_command.py @@ -108,3 +108,25 @@ def test_build_sphinx_return_nonzero_status(pkgroot, proc): print(out) print(err) assert proc.returncode != 0, 'expect non-zero status for setup.py' + + +@with_setup_command(root) +def test_build_sphinx_warning_return_zero_status(pkgroot, proc): + srcdir = (pkgroot / 'doc') + (srcdir / 'contents.txt').write_text( + 'See :ref:`unexisting-reference-label`') + out, err = proc.communicate() + print(out) + print(err) + assert proc.returncode == 0 + + +@with_setup_command(root, '--warning-is-error') +def test_build_sphinx_warning_is_error_return_nonzero_status(pkgroot, proc): + srcdir = (pkgroot / 'doc') + (srcdir / 'contents.txt').write_text( + 'See :ref:`unexisting-reference-label`') + out, err = proc.communicate() + print(out) + print(err) + assert proc.returncode != 0, 'expect non-zero status for setup.py' diff --git a/tests/test_util_fileutil.py b/tests/test_util_fileutil.py new file mode 100644 index 000000000..a56543614 --- /dev/null +++ b/tests/test_util_fileutil.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +""" + test_util_fileutil + ~~~~~~~~~~~~~~~~~~ + + Tests sphinx.util.fileutil functions. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from sphinx.util.fileutil import copy_asset, copy_asset_file +from sphinx.jinja2glue import BuiltinTemplateLoader + +from mock import Mock +from util import with_tempdir + + +class DummyTemplateLoader(BuiltinTemplateLoader): + def __init__(self): + BuiltinTemplateLoader.__init__(self) + builder = Mock() + builder.config.templates_path = [] + builder.app.translater = None + self.init(builder) + + +@with_tempdir +def test_copy_asset_file(tmpdir): + renderer = DummyTemplateLoader() + + # copy normal file + src = (tmpdir / 'asset.txt') + src.write_text('# test data') + dest = (tmpdir / 'output.txt') + + copy_asset_file(src, dest) + assert dest.exists() + assert src.text() == dest.text() + + # copy template file + src = (tmpdir / 'asset.txt_t') + src.write_text('# {{var1}} data') + dest = (tmpdir / 'output.txt_t') + + copy_asset_file(src, dest, {'var1': 'template'}, renderer) + assert not dest.exists() + assert (tmpdir / 'output.txt').exists() + assert (tmpdir / 'output.txt').text() == '# template data' + + # copy template file to subdir + src = (tmpdir / 'asset.txt_t') + src.write_text('# {{var1}} data') + subdir1 = (tmpdir / 'subdir') + subdir1.makedirs() + + copy_asset_file(src, subdir1, {'var1': 'template'}, renderer) + assert (subdir1 / 'asset.txt').exists() + assert (subdir1 / 'asset.txt').text() == '# template data' + + # copy template file without context + src = (tmpdir / 'asset.txt_t') + subdir2 = (tmpdir / 'subdir2') + subdir2.makedirs() + + copy_asset_file(src, subdir2) + assert not (subdir2 / 'asset.txt').exists() + assert (subdir2 / 'asset.txt_t').exists() + assert (subdir2 / 'asset.txt_t').text() == '# {{var1}} data' + + +@with_tempdir +def test_copy_asset(tmpdir): + renderer = DummyTemplateLoader() + + # prepare source files + source = (tmpdir / 'source') + source.makedirs() + (source / 'index.rst').write_text('index.rst') + (source / 'foo.rst_t').write_text('{{var1}}.rst') + (source / '_static').makedirs() + (source / '_static' / 'basic.css').write_text('basic.css') + (source / '_templates').makedirs() + (source / '_templates' / 'layout.html').write_text('layout.html') + (source / '_templates' / 'sidebar.html_t').write_text('sidebar: {{var2}}') + + # copy a single file + assert not (tmpdir / 'test1').exists() + copy_asset(source / 'index.rst', tmpdir / 'test1') + assert (tmpdir / 'test1').exists() + assert (tmpdir / 'test1/index.rst').exists() + + # copy directories + destdir = tmpdir / 'test2' + copy_asset(source, destdir, context=dict(var1='bar', var2='baz'), renderer=renderer) + assert (destdir / 'index.rst').exists() + assert (destdir / 'foo.rst').exists() + assert (destdir / 'foo.rst').text() == 'bar.rst' + assert (destdir / '_static' / 'basic.css').exists() + assert (destdir / '_templates' / 'layout.html').exists() + assert (destdir / '_templates' / 'sidebar.html').exists() + assert (destdir / '_templates' / 'sidebar.html').text() == 'sidebar: baz' + + # copy with exclusion + def excluded(path): + return ('sidebar.html' in path or 'basic.css' in path) + + destdir = tmpdir / 'test3' + copy_asset(source, destdir, excluded, + context=dict(var1='bar', var2='baz'), renderer=renderer) + assert (destdir / 'index.rst').exists() + assert (destdir / 'foo.rst').exists() + assert not (destdir / '_static' / 'basic.css').exists() + assert (destdir / '_templates' / 'layout.html').exists() + assert not (destdir / '_templates' / 'sidebar.html').exists() diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py index 6ff988c81..3e0cfd5f3 100644 --- a/tests/test_util_i18n.py +++ b/tests/test_util_i18n.py @@ -47,7 +47,7 @@ def test_catalog_outdated(dir): mo_file.write_text('#') assert not cat.is_outdated() # if mo is exist and newer than po - os.utime(mo_file, (os.stat(mo_file).st_mtime - 10,) * 2) # to be outdate + os.utime(mo_file, (os.stat(mo_file).st_mtime - 10,) * 2) # to be outdate assert cat.is_outdated() # if mo is exist and older than po @@ -213,6 +213,7 @@ def test_format_date(): assert i18n.format_date(format, date=datet) == 'Feb 7, 2016, 5:11:17 AM' assert i18n.format_date(format, date=date) == 'Feb 7, 2016' + def test_get_filename_for_language(): app = TestApp() diff --git a/tests/test_util_matching.py b/tests/test_util_matching.py new file mode 100644 index 000000000..9e99a5322 --- /dev/null +++ b/tests/test_util_matching.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +""" + test_util_matching + ~~~~~~~~~~~~~~~~~~ + + Tests sphinx.util.matching functions. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from sphinx.util.matching import compile_matchers, Matcher + + +def test_compile_matchers(): + # exact matching + pat = compile_matchers(['hello.py']).pop() + assert pat('hello.py') + assert not pat('hello-py') + assert not pat('subdir/hello.py') + + # wild card (*) + pat = compile_matchers(['hello.*']).pop() + assert pat('hello.py') + assert pat('hello.rst') + + pat = compile_matchers(['*.py']).pop() + assert pat('hello.py') + assert pat('world.py') + assert not pat('subdir/hello.py') + + # wild card (**) + pat = compile_matchers(['hello.**']).pop() + assert pat('hello.py') + assert pat('hello.rst') + assert pat('hello.py/world.py') + + pat = compile_matchers(['**.py']).pop() + assert pat('hello.py') + assert pat('world.py') + assert pat('subdir/hello.py') + + pat = compile_matchers(['**/hello.py']).pop() + assert not pat('hello.py') + assert pat('subdir/hello.py') + assert pat('subdir/subdir/hello.py') + + # wild card (?) + pat = compile_matchers(['hello.?']).pop() + assert pat('hello.c') + assert not pat('hello.py') + + # pattern ([...]) + pat = compile_matchers(['hello[12\\].py']).pop() + assert pat('hello1.py') + assert pat('hello2.py') + assert pat('hello\\.py') + assert not pat('hello3.py') + + pat = compile_matchers(['hello[^12].py']).pop() # "^" is not negative identifier + assert pat('hello1.py') + assert pat('hello2.py') + assert pat('hello^.py') + assert not pat('hello3.py') + + # negative pattern ([!...]) + pat = compile_matchers(['hello[!12].py']).pop() + assert not pat('hello1.py') + assert not pat('hello2.py') + assert not pat('hello/.py') # negative pattern does not match to "/" + assert pat('hello3.py') + + # non patterns + pat = compile_matchers(['hello[.py']).pop() + assert pat('hello[.py') + assert not pat('hello.py') + + pat = compile_matchers(['hello[].py']).pop() + assert pat('hello[].py') + assert not pat('hello.py') + + pat = compile_matchers(['hello[!].py']).pop() + assert pat('hello[!].py') + assert not pat('hello.py') + + +def test_Matcher(): + matcher = Matcher(['hello.py', '**/world.py']) + assert matcher('hello.py') + assert not matcher('subdir/hello.py') + assert matcher('world.py') + assert matcher('subdir/world.py') diff --git a/tests/test_util_nodes.py b/tests/test_util_nodes.py index a41af3cc8..e7a4b7e90 100644 --- a/tests/test_util_nodes.py +++ b/tests/test_util_nodes.py @@ -87,7 +87,6 @@ def test_extract_messages(): nodes.rubric, 1, ) - text = dedent( """ | spam @@ -100,7 +99,6 @@ def test_extract_messages(): nodes.line, 2, ) - text = dedent( """ section @@ -118,7 +116,6 @@ def test_extract_messages(): nodes.line, 2, ) - text = dedent( """ * | **Title 1** diff --git a/tests/test_writer_latex.py b/tests/test_writer_latex.py new file mode 100644 index 000000000..72eb7ed2a --- /dev/null +++ b/tests/test_writer_latex.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" + test_writer_latex + ~~~~~~~~~~~~~~~~ + + Test the LaTeX writer + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from __future__ import print_function +from sphinx.writers.latex import rstdim_to_latexdim + +from util import raises + + +def test_rstdim_to_latexdim(): + # Length units docutils supported + # http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units + assert rstdim_to_latexdim('160em') == '160em' + assert rstdim_to_latexdim('160px') == '160\\sphinxpxdimen' + assert rstdim_to_latexdim('160in') == '160in' + assert rstdim_to_latexdim('160cm') == '160cm' + assert rstdim_to_latexdim('160mm') == '160mm' + assert rstdim_to_latexdim('160pt') == '160bp' + assert rstdim_to_latexdim('160pc') == '160pc' + assert rstdim_to_latexdim('30%') == '0.300\\linewidth' + assert rstdim_to_latexdim('160') == '160\\sphinxpxdimen' + + # flaot values + assert rstdim_to_latexdim('160.0em') == '160.0em' + assert rstdim_to_latexdim('.5em') == '.5em' + + # unknown values (it might be generated by 3rd party extension) + raises(ValueError, rstdim_to_latexdim, 'unknown') + assert rstdim_to_latexdim('160.0unknown') == '160.0unknown' diff --git a/tests/util.py b/tests/util.py index b3f5e01d9..27b41bc15 100644 --- a/tests/util.py +++ b/tests/util.py @@ -26,7 +26,7 @@ from sphinx.theming import Theme from sphinx.ext.autodoc import AutoDirective from sphinx.pycode import ModuleAnalyzer -from path import path, repr_as +from path import path, repr_as # NOQA try: # Python >=3.3 @@ -110,6 +110,7 @@ except ImportError: def assert_in(x, thing, msg=''): if x not in thing: assert False, msg or '%r is not in %r' % (x, thing) + def assert_not_in(x, thing, msg=''): if x in thing: assert False, msg or '%r is in %r' % (x, thing) @@ -308,3 +309,7 @@ def find_files(root, suffix=None): for f in [f for f in files if not suffix or f.endswith(suffix)]: fpath = dirpath / f yield os.path.relpath(fpath, root) + + +def strip_escseq(text): + return re.sub('\x1b.*?m', '', text) diff --git a/tox.ini b/tox.ini index 8fcb7b177..b646fd285 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ deps= nose sqlalchemy whoosh + html5lib setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild commands= diff --git a/utils/check_sources.py b/utils/check_sources.py index 16bc918cb..18d444057 100755 --- a/utils/check_sources.py +++ b/utils/check_sources.py @@ -223,11 +223,8 @@ def main(argv): print("Checking %s..." % fn) try: - f = open(fn, 'rb') - try: + with open(fn, 'rb') as f: lines = list(f) - finally: - f.close() except (IOError, OSError) as err: print("%s: cannot open: %s" % (fn, err)) num += 1 diff --git a/utils/reindent.py b/utils/reindent.py index ef53fe8ca..ee13a634a 100755 --- a/utils/reindent.py +++ b/utils/reindent.py @@ -40,11 +40,13 @@ you'd prefer. You can always use the --nobackup option to prevent this. """ from __future__ import print_function -__version__ = "1" - -import tokenize -import os, shutil +import os import sys +import shutil +import tokenize +from six.ranges import range + +__version__ = "1" if sys.version_info >= (3, 0): def tokens(readline, tokeneater): @@ -58,11 +60,13 @@ recurse = 0 dryrun = 0 makebackup = True + def usage(msg=None): if msg is not None: print(msg, file=sys.stderr) print(__doc__, file=sys.stderr) + def errprint(*args): sep = "" for arg in args: @@ -70,12 +74,13 @@ def errprint(*args): sep = " " sys.stderr.write("\n") + def main(): import getopt global verbose, recurse, dryrun, makebackup try: opts, args = getopt.getopt(sys.argv[1:], "drnvh", - ["dryrun", "recurse", "nobackup", "verbose", "help"]) + ["dryrun", "recurse", "nobackup", "verbose", "help"]) except getopt.error as msg: usage(msg) return @@ -99,6 +104,7 @@ def main(): for arg in args: check(arg) + def check(file): if os.path.isdir(file) and not os.path.islink(file): if verbose: @@ -108,8 +114,8 @@ def check(file): fullname = os.path.join(file, name) if ((recurse and os.path.isdir(fullname) and not os.path.islink(fullname) and - not os.path.split(fullname)[1].startswith(".")) - or name.lower().endswith(".py")): + not os.path.split(fullname)[1].startswith(".")) or + name.lower().endswith(".py")): check(fullname) return @@ -121,8 +127,8 @@ def check(file): errprint("%s: I/O Error: %s" % (file, str(msg))) return - r = Reindenter(f) - f.close() + with f: + r = Reindenter(f) if r.run(): if verbose: print("changed.") @@ -134,9 +140,8 @@ def check(file): shutil.copyfile(file, bak) if verbose: print("backed up", file, "to", bak) - f = open(file, "w") - r.write(f) - f.close() + with open(file, "w") as f: + r.write(f) if verbose: print("wrote new", file) return True @@ -145,6 +150,7 @@ def check(file): print("unchanged.") return False + def _rstrip(line, JUNK='\n \t'): """Return line stripped of trailing spaces, tabs, newlines. @@ -158,8 +164,8 @@ def _rstrip(line, JUNK='\n \t'): i -= 1 return line[:i] -class Reindenter: +class Reindenter: def __init__(self, f): self.find_stmt = 1 # next token begins a fresh stmt? self.level = 0 # current indent level @@ -212,21 +218,21 @@ class Reindenter: want = have2want.get(have, -1) if want < 0: # Then it probably belongs to the next real stmt. - for j in xrange(i+1, len(stats)-1): + for j in range(i+1, len(stats)-1): jline, jlevel = stats[j] if jlevel >= 0: if have == getlspace(lines[jline]): want = jlevel * 4 break - if want < 0: # Maybe it's a hanging - # comment like this one, + if want < 0: # Maybe it's a hanging + # comment like this one, # in which case we should shift it like its base # line got shifted. - for j in xrange(i-1, -1, -1): + for j in range(i-1, -1, -1): jline, jlevel = stats[j] if jlevel >= 0: - want = have + getlspace(after[jline-1]) - \ - getlspace(lines[jline]) + want = (have + getlspace(after[jline-1]) - + getlspace(lines[jline])) break if want < 0: # Still no luck -- leave it alone. @@ -301,6 +307,7 @@ class Reindenter: if line: # not endmarker self.stats.append((position[0], self.level)) + # Count number of leading blanks. def getlspace(line): i, n = 0, len(line) diff --git a/utils/release-checklist b/utils/release-checklist index 8efe980db..3dd52158c 100644 --- a/utils/release-checklist +++ b/utils/release-checklist @@ -1,22 +1,26 @@ Release checklist ================= +* Open https://travis-ci.org/sphinx-doc/sphinx/branches and check stable branch is green * Check `git status` * Run `make style-check` * Run `tx pull -a -f` in sphinx/locale if final major release * Update version info in sphinx/__init__.py * Update release date in CHANGES -* `git commit` +* `git commit -am 'Bump to x.y.z final'` * `make clean` -* `python setup.py release bdist_wheel sdist upload --identify=[your key]` +* `python setup.py release bdist_wheel sdist upload --identity=[your key]` * Check PyPI release page for obvious errors * `git tag` with version number * Merge default into stable if final major release -* `git push stable --tags` +* `git push origin stable --tags` * Open https://readthedocs.org/dashboard/sphinx/versions/ and enable the released version * Add new version/milestone to tracker categories * Write announcement and send to sphinx-dev, sphinx-users and python-announce * Update version info, add new CHANGES entry for next version -* `git commit` -* `git push` +* `git commit -am 'Bump version'` +* `git push origin stable` +* `git checkout master` +* `git merge stable` +* `git push origin master` * Update `sphinx-doc-translations `_