mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-09-03 20:52:55 -05:00
Merge branch 'master' into dummy-builder
This commit is contained in:
+5
-1
@@ -5,6 +5,7 @@ python:
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "pypy"
|
||||
env:
|
||||
- DOCUTILS=0.11
|
||||
@@ -13,4 +14,7 @@ install:
|
||||
- pip install docutils==$DOCUTILS
|
||||
- pip install -r test-reqs.txt
|
||||
before_script: flake8
|
||||
script: make test
|
||||
script:
|
||||
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then make test-async; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != '3.5' ]]; then make test; fi
|
||||
|
||||
@@ -29,6 +29,7 @@ Other contributors, listed alphabetically, are:
|
||||
* Horst Gutmann -- internationalization support
|
||||
* Martin Hans -- autodoc improvements
|
||||
* Doug Hellmann -- graphviz improvements
|
||||
* Timotheus Kampik - stop words language fix
|
||||
* Takeshi Komiya -- numref feature
|
||||
* Dave Kuhlman -- original LaTeX writer
|
||||
* Blaise Laflamme -- pyramid theme
|
||||
@@ -51,12 +52,14 @@ Other contributors, listed alphabetically, are:
|
||||
* Stefan Seefeld -- toctree improvements
|
||||
* Shibukawa Yoshiki -- pluggable search API and Japanese search
|
||||
* Antonio Valentino -- qthelp builder
|
||||
* Filip Vavera -- napoleon todo directive
|
||||
* Pauli Virtanen -- autodoc improvements, autosummary extension
|
||||
* Stefan van der Walt -- autosummary extension
|
||||
* Thomas Waldmann -- apidoc module fixes
|
||||
* John Waltman -- Texinfo builder
|
||||
* Barry Warsaw -- setup command improvements
|
||||
* Sebastian Wiesner -- image handling, distutils support
|
||||
* Michael Wilson -- Intersphinx HTTP basic auth support
|
||||
* Joel Wurtz -- cellspanning support in LaTeX
|
||||
|
||||
Many thanks for all contributions!
|
||||
|
||||
@@ -8,17 +8,136 @@ Features added
|
||||
--------------
|
||||
* Added the ``dummy`` builder: syntax check without output.
|
||||
|
||||
* #2092: add todo directive support in napoleon package
|
||||
* #1962: when adding directives, roles or nodes from an extension, warn if such
|
||||
an element is already present (built-in or added by another extension).
|
||||
* #1909: Add "doc" references to Intersphinx inventories.
|
||||
* C++ type alias support (e.g., ``.. type:: T = int``)
|
||||
* C++ template support for classes, functions, type aliases, and variables (#1729, #1314).
|
||||
* C++, added new scope management directives ``namespace-push`` and ``namespace-pop``.
|
||||
* Intersphinx: Added support for fetching Intersphinx inventories with URLs
|
||||
using HTTP basic auth
|
||||
* C++, added support for template parameter in function info field lists.
|
||||
* C++, added support for pointers to member (function).
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #1789: ``:pyobject:`` option of ``literalinclude`` directive includes following
|
||||
lines after class definitions
|
||||
* #1790: ``literalinclude`` strips empty lines at the head and tail
|
||||
* #1913: C++, fix assert bug for enumerators in next-to-global and global scope.
|
||||
* C++, fix parsing of 'signed char' and 'unsigned char' as types.
|
||||
* C++, add missing support for 'friend' functions.
|
||||
* C++, add missing support for virtual base classes (thanks to Rapptz).
|
||||
* C++, add support for final classes.
|
||||
* C++, fix parsing of types prefixed with 'enum'.
|
||||
* #2023: Dutch search support uses Danish stemming info
|
||||
* C++, add support for user-defined literals.
|
||||
* #1804: Now html output wraps overflowed long-line-text in the sidebar. Thanks to
|
||||
Hassen ben tanfous.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* #1757: Fix for usage of :confval:`html_last_updated_fmt`. Thanks to Ralf Hemmecke.
|
||||
|
||||
|
||||
Release 1.3.4 (in development)
|
||||
==============================
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #2134: Fix figure caption with reference causes latex build error
|
||||
* #2094: Fix rubric with reference not working in Latex
|
||||
|
||||
|
||||
Release 1.3.3 (released Dec 2, 2015)
|
||||
====================================
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #2012: Fix exception occurred if ``numfig_format`` is invalid
|
||||
* #2142: Provide non-minified JS code in ``sphinx/search/non-minified-js/*.js`` for
|
||||
source distribution on PyPI.
|
||||
* #2148: Error while building devhelp target with non-ASCII document.
|
||||
|
||||
|
||||
Release 1.3.2 (released Nov 29, 2015)
|
||||
=====================================
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
* #1935: Make "numfig_format" overridable in latex_elements.
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #1976: Avoid "2.0" version of Babel because it doesn't work with Windows environment.
|
||||
* Add a "default.css" stylesheet (which imports "classic.css") for compatibility.
|
||||
* #1788: graphviz extension raises exception when caption option is present.
|
||||
* #1789: ``:pyobject:`` option of ``literalinclude`` directive includes following
|
||||
lines after class definitions
|
||||
* #1790: ``literalinclude`` strips empty lines at the head and tail
|
||||
* #1802: load plugin themes automatically when theme.conf use it as 'inherit'. Thanks to
|
||||
Takayuki Hirai.
|
||||
* #1794: custom theme extended from alabaster or sphinx_rtd_theme can't find base theme.
|
||||
* #1834: compatibility for docutils-0.13: handle_io_errors keyword argument for
|
||||
docutils.io.FileInput cause TypeError.
|
||||
* #1823: '.' as <module_path> for sphinx-apidoc cause an unfriendly error. Now '.'
|
||||
is converted to absolute path automatically.
|
||||
* Fix a crash when setting up extensions which do not support metadata.
|
||||
* #1784: Provide non-minified JS code in ``sphinx/search/non-minified-js/*.js``
|
||||
* #1822, #1892: Fix regression for #1061. autosummary can't generate doc for imported
|
||||
members since sphinx-1.3b3. Thanks to Eric Larson.
|
||||
* #1793, #1819: "see also" misses a linebreak in text output. Thanks to Takayuki Hirai.
|
||||
* #1780, #1866: "make text" shows "class" keyword twice. Thanks to Takayuki
|
||||
Hirai.
|
||||
* #1871: Fix for LaTeX output of tables with one column and multirows.
|
||||
* Work around the lack of the HTMLParserError exception in Python 3.5.
|
||||
* #1949: Use ``safe_getattr`` in the coverage builder to avoid aborting with
|
||||
descriptors that have custom behavior.
|
||||
* #1915: Do not generate smart quotes in doc field type annotations.
|
||||
* #1796: On py3, automated .mo building caused UnicodeDecodeError.
|
||||
* #1923: Use babel features only if the babel latex element is nonempty.
|
||||
* #1942: Fix a KeyError in websupport.
|
||||
* #1903: Fix strange id generation for glossary terms.
|
||||
* #1796, On py3, automated .mo building cause UnicodeDecodeError
|
||||
* Fix: ``make text`` will crush if a definition list item has more than 1 classifiers as:
|
||||
* #1796: On py3, automated .mo building cause UnicodeDecodeError
|
||||
* ``make text`` will crush if a definition list item has more than 1 classifiers as:
|
||||
* Fixed #1855: make gettext generates broken po file for definition lists with classifier.
|
||||
``term : classifier1 : classifier2``.
|
||||
* #1855: make gettext generates broken po file for definition lists with classifier.
|
||||
* #1869: Fix problems when dealing with files containing non-ASCII characters. Thanks to
|
||||
Marvin Schmidt.
|
||||
* #1798: Fix building LaTeX with references in titles.
|
||||
* #1725: On py2 environment, doctest with using non-ASCII characters causes
|
||||
``'ascii' codec can't decode byte`` exception.
|
||||
* #1540: Fix RuntimeError with circular referenced toctree
|
||||
* #1983: i18n translation feature breaks references which uses section name.
|
||||
* #1990: Use caption of toctree to title of \tableofcontents in LaTeX
|
||||
* #1987: Fix ampersand is ignored in ``:menuselection:`` and ``:guilabel:`` on LaTeX builder
|
||||
* #1994: More supporting non-standard parser (like recommonmark parser) for Translation and
|
||||
WebSupport feature. Now node.rawsource is fall backed to node.astext() during docutils
|
||||
transforming.
|
||||
* On Py2 environment, conf.py that is generated by sphinx-quickstart should have u prefixed
|
||||
config value for 'version' and 'release'.
|
||||
* #2102: On Windows + Py3, using ``|today|`` and non-ASCII date format will raise
|
||||
UnicodeEncodeError.
|
||||
* #1974: UnboundLocalError: local variable 'domain' referenced before assignment when
|
||||
using `any` role and `sphinx.ext.intersphinx` in same time.
|
||||
* #2121: multiple words search doesn't find pages when words across on the page title and
|
||||
the page content.
|
||||
* #1884, #1885: plug-in html themes cannot inherit another plug-in theme. Thanks to
|
||||
Suzumizaki.
|
||||
* #1818: `sphinx.ext.todo` directive generates broken html class attribute as
|
||||
'admonition-' when :confval:`language` is specified with non-ASCII linguistic area like
|
||||
'ru' or 'ja'. To fix this, now ``todo`` directive can use ```:class:`` option.
|
||||
* #2140: Fix footnotes in table has broken in LaTeX
|
||||
* #2127: MecabBinder for html searching feature doesn't work with Python 3.
|
||||
Thanks to Tomoko Uchida.
|
||||
|
||||
|
||||
Release 1.3.1 (released Mar 17, 2015)
|
||||
=====================================
|
||||
|
||||
@@ -16,6 +16,7 @@ include sphinx-apidoc.py
|
||||
recursive-include sphinx/texinputs *
|
||||
recursive-include sphinx/themes *
|
||||
recursive-include sphinx/locale *
|
||||
recursive-include sphinx/search/non-minified-js *.js
|
||||
recursive-include sphinx/ext/autosummary/templates *
|
||||
recursive-include tests *
|
||||
recursive-include utils *
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
PYTHON ?= python
|
||||
|
||||
.PHONY: all check clean clean-pyc clean-patchfiles clean-backupfiles \
|
||||
clean-generated pylint reindent test covertest build convert-utils
|
||||
.PHONY: all style-check clean clean-pyc clean-patchfiles clean-backupfiles \
|
||||
clean-generated pylint reindent test covertest build
|
||||
|
||||
DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
|
||||
-i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
|
||||
-i .ropeproject -i doc/_build -i tests/path.py \
|
||||
-i tests/coverage.py -i env -i utils/convert.py \
|
||||
-i tests/typing_test_data.py \
|
||||
-i tests/test_autodoc_py35.py \
|
||||
-i tests/build \
|
||||
-i sphinx/search/da.py \
|
||||
-i sphinx/search/de.py \
|
||||
-i sphinx/search/en.py \
|
||||
@@ -23,17 +26,12 @@ DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
|
||||
-i sphinx/search/ru.py \
|
||||
-i sphinx/search/sv.py \
|
||||
-i sphinx/search/tr.py \
|
||||
-i utils/reindent3.py -i utils/check_sources3.py -i .tox
|
||||
-i .tox
|
||||
|
||||
all: clean-pyc clean-backupfiles check test
|
||||
all: clean-pyc clean-backupfiles style-check test
|
||||
|
||||
ifeq ($(PYTHON), python3)
|
||||
check: convert-utils
|
||||
@$(PYTHON) utils/check_sources3.py $(DONT_CHECK) .
|
||||
else
|
||||
check:
|
||||
style-check:
|
||||
@$(PYTHON) utils/check_sources.py $(DONT_CHECK) .
|
||||
endif
|
||||
|
||||
clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated
|
||||
|
||||
@@ -55,15 +53,13 @@ clean-generated:
|
||||
pylint:
|
||||
@pylint --rcfile utils/pylintrc sphinx
|
||||
|
||||
ifeq ($(PYTHON), python3)
|
||||
reindent: convert-utils
|
||||
@$(PYTHON) utils/reindent3.py -r -n .
|
||||
else
|
||||
reindent:
|
||||
@$(PYTHON) utils/reindent.py -r -n .
|
||||
endif
|
||||
|
||||
test:
|
||||
@cd tests; $(PYTHON) run.py -I py35 -d -m '^[tT]est' $(TEST)
|
||||
|
||||
test-async:
|
||||
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
|
||||
|
||||
covertest:
|
||||
@@ -72,8 +68,3 @@ covertest:
|
||||
|
||||
build:
|
||||
@$(PYTHON) setup.py build
|
||||
|
||||
ifeq ($(PYTHON), python3)
|
||||
convert-utils:
|
||||
@python3 utils/convert.py -i utils/convert.py utils/
|
||||
endif
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ are also available.{%endtrans%}</p>
|
||||
|
||||
<h3>{%trans%}Questions? Suggestions?{%endtrans%}</h3>
|
||||
|
||||
<p>{%trans%}Join the <a href="http://groups.google.com/group/sphinx-users">Google group</a>:{%endtrans%}</p>
|
||||
<p>{%trans%}Join the <a href="http://groups.google.com/group/sphinx-users">sphinx-users</a> mailing list on Google Groups:{%endtrans%}</p>
|
||||
<form action="http://groups.google.com/group/sphinx-users/boxsubscribe"
|
||||
style="padding-left: 0.5em">
|
||||
<input type="text" name="email" value="your@email" style="font-size: 90%; width: 120px"
|
||||
|
||||
@@ -23,6 +23,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. class:: DirectoryHTMLBuilder
|
||||
@@ -36,6 +38,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 0.6
|
||||
@@ -48,6 +52,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.0
|
||||
@@ -61,6 +67,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.qthelp
|
||||
@@ -72,6 +80,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. _Qt help: http://qt-project.org/doc/qt-4.8/qthelp-framework.html
|
||||
@@ -96,6 +106,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.3
|
||||
@@ -109,6 +121,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.epub
|
||||
@@ -122,6 +136,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.latex
|
||||
@@ -145,6 +161,8 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
Note that a direct PDF builder using ReportLab is available in `rst2pdf
|
||||
@@ -162,6 +180,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 0.4
|
||||
@@ -175,6 +195,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.0
|
||||
@@ -196,6 +218,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.1
|
||||
@@ -261,6 +285,8 @@ for details.
|
||||
|
||||
The old name ``web`` still works as well.
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
The file suffix is ``.fpickle``. The global context is called
|
||||
@@ -276,6 +302,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
The file suffix is ``.fjson``. The global context is called
|
||||
@@ -293,6 +321,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.1
|
||||
@@ -307,6 +337,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.dummy
|
||||
@@ -330,6 +362,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.xml
|
||||
@@ -341,6 +375,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.2
|
||||
@@ -355,6 +391,8 @@ for details.
|
||||
|
||||
.. autoattribute:: name
|
||||
|
||||
.. autoattribute:: format
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
+9
-6
@@ -250,7 +250,7 @@ General configuration
|
||||
.. confval:: numfig
|
||||
|
||||
If true, figures, tables and code-blocks are automatically numbered if they
|
||||
has caption. For now, it works only with the HTML builder. Default is ``False``.
|
||||
have a caption. For now, it works only with the HTML builder. Default is ``False``.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
@@ -637,9 +637,10 @@ that use Sphinx's HTMLWriter class.
|
||||
|
||||
.. confval:: html_last_updated_fmt
|
||||
|
||||
If this is not the empty string, a 'Last updated on:' timestamp is inserted
|
||||
at every page bottom, using the given :func:`strftime` format. Default is
|
||||
``'%b %d, %Y'`` (or a locale-dependent equivalent).
|
||||
If this is not None, a 'Last updated on:' timestamp is inserted
|
||||
at every page bottom, using the given :func:`strftime` format.
|
||||
The empty string is equivalent to ``'%b %d, %Y'`` (or a
|
||||
locale-dependent equivalent).
|
||||
|
||||
.. confval:: html_use_smartypants
|
||||
|
||||
@@ -867,7 +868,7 @@ that use Sphinx's HTMLWriter class.
|
||||
* ``it`` -- Italian
|
||||
* ``ja`` -- Japanese
|
||||
* ``no`` -- Norwegian
|
||||
* ``pr`` -- Portuguese
|
||||
* ``pt`` -- Portuguese
|
||||
* ``ro`` -- Romanian
|
||||
* ``ru`` -- Russian
|
||||
* ``es`` -- Spanish
|
||||
@@ -1448,6 +1449,9 @@ These options influence LaTeX output.
|
||||
``'maketitle'``
|
||||
"maketitle" call, default ``'\\maketitle'``. Override if you want to
|
||||
generate a differently-styled title page.
|
||||
``'releasename'``
|
||||
value that prefixes ``'release'`` element on title page, default
|
||||
``'Release'``.
|
||||
``'tableofcontents'``
|
||||
"tableofcontents" call, default ``'\\tableofcontents'``. Override if
|
||||
you want to generate a different table of contents or put content
|
||||
@@ -1473,7 +1477,6 @@ These options influence LaTeX output.
|
||||
``'release'``
|
||||
``'author'``
|
||||
``'logo'``
|
||||
``'releasename'``
|
||||
``'makeindex'``
|
||||
``'shorthandoff'``
|
||||
|
||||
|
||||
+252
-44
@@ -528,60 +528,111 @@ a visibility statement (``public``, ``private`` or ``protected``).
|
||||
|
||||
Describe a class/struct, possibly with specification of inheritance, e.g.,::
|
||||
|
||||
.. cpp:class:: SomeName::SomeClass : public MyBase, MyOtherBase
|
||||
.. cpp:class:: MyClass : public MyBase, MyOtherBase
|
||||
|
||||
.. rst:directive:: .. cpp:function:: (member-)function prototype
|
||||
The class can be directly declared inside a nested scope, e.g.,::
|
||||
|
||||
.. cpp:class:: OuterScope::MyClass : public MyBase, MyOtherBase
|
||||
|
||||
A template class can be declared::
|
||||
|
||||
.. cpp:class:: template<typename T, std::size_t N> std::array
|
||||
|
||||
or with a line break::
|
||||
|
||||
.. cpp:class:: template<typename T, std::size_t N> \
|
||||
std::array
|
||||
|
||||
Full and partial template specialisations can be declared::
|
||||
|
||||
.. cpp::class:: template<> \
|
||||
std::array<bool, 256>
|
||||
|
||||
.. cpp::class:: template<typename T> \
|
||||
std::array<T, 42>
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:function:: (member) function prototype
|
||||
|
||||
Describe a function or member function, e.g.,::
|
||||
|
||||
.. cpp:function:: bool namespaced::theclass::method(int arg1, std::string arg2)
|
||||
.. cpp:function:: bool myMethod(int arg1, std::string arg2)
|
||||
|
||||
Describes a method with parameters and types.
|
||||
A function with parameters and types.
|
||||
|
||||
.. cpp:function:: bool namespaced::theclass::method(T1, T2)
|
||||
.. cpp:function:: bool myMethod(int, double)
|
||||
|
||||
Describes a method with unnamed parameters.
|
||||
A function with unnamed parameters.
|
||||
|
||||
.. cpp:function:: const T &array<T>::operator[]() const
|
||||
.. cpp:function:: const T &MyClass::operator[](std::size_t i) const
|
||||
|
||||
Describes the constant indexing operator of a templated array.
|
||||
An overload for the indexing operator.
|
||||
|
||||
.. cpp:function:: operator bool() const
|
||||
|
||||
Describe a casting operator here.
|
||||
A casting operator.
|
||||
|
||||
.. cpp:function:: constexpr void foo(std::string &bar[2]) noexcept
|
||||
|
||||
Describe a constexpr function here.
|
||||
A constexpr function.
|
||||
|
||||
.. cpp:function:: MyClass::MyClass(const MyClass&) = default
|
||||
|
||||
Describe a copy constructor with default implementation.
|
||||
A copy constructor with default implementation.
|
||||
|
||||
.. rst:directive:: .. cpp:member:: (member-)variable declaration
|
||||
.. cpp:var:: (member-)variable declaration
|
||||
Function templates can also be described::
|
||||
|
||||
.. cpp:function:: template<typename U> \
|
||||
void print(U &&u)
|
||||
|
||||
and function template specialisations::
|
||||
|
||||
.. cpp:function:: template<> \
|
||||
void print(int i)
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:member:: (member) variable declaration
|
||||
.. cpp:var:: (member) variable declaration
|
||||
|
||||
Describe a varible or member variable, e.g.,::
|
||||
|
||||
.. cpp:member:: std::string theclass::name
|
||||
.. cpp:member:: std::string MyClass::myMember
|
||||
|
||||
.. cpp:member:: std::string theclass::name[N][M]
|
||||
.. cpp:var:: std::string MyClass::myOtherMember[N][M]
|
||||
|
||||
.. cpp:member:: int a = 42
|
||||
|
||||
.. rst:directive:: .. cpp:type:: typedef-like declaration
|
||||
.. cpp:type:: name
|
||||
Variable templates can also be described::
|
||||
|
||||
Describe a type as in a typedef declaration, or the name of a type with unspecified type, e.g.,::
|
||||
.. cpp:member:: template<class T> \
|
||||
constexpr T pi = T(3.1415926535897932385)
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:type:: typedef declaration
|
||||
.. cpp:type:: name
|
||||
.. cpp:type:: type alias declaration
|
||||
|
||||
Describe a type as in a typedef declaration, a type alias declaration,
|
||||
or simply the name of a type with unspecified type, e.g.,::
|
||||
|
||||
.. cpp:type:: std::vector<int> MyList
|
||||
|
||||
A typedef-like declaration of a type.
|
||||
|
||||
.. cpp:type:: theclass::const_iterator
|
||||
.. cpp:type:: MyContainer::const_iterator
|
||||
|
||||
Declaration of a type alias with unspecified type.
|
||||
|
||||
.. cpp:type:: MyType = std::unordered_map<int, std::string>
|
||||
|
||||
Declaration of a type alias.
|
||||
|
||||
A type alias can also be templated::
|
||||
|
||||
.. cpp:type:: template<typename T>
|
||||
MyContainer = std::vector<T>
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:enum:: unscoped enum declaration
|
||||
.. cpp:enum-struct:: scoped enum declaration
|
||||
.. cpp:enum-class:: scoped enum declaration
|
||||
@@ -610,25 +661,111 @@ a visibility statement (``public``, ``private`` or ``protected``).
|
||||
.. rst:directive:: .. cpp:enumerator:: name
|
||||
.. cpp:enumerator:: name = constant
|
||||
|
||||
Describe an enumerator, optionally with its value defined.
|
||||
Describe an enumerator, optionally with its value defined, e.g.,::
|
||||
|
||||
.. rst:directive:: .. cpp:namespace:: namespace
|
||||
.. cpp::enumerator:: MyEnum::myEnumerator
|
||||
|
||||
Select the current namespace for the subsequent objects. Note that the namespace
|
||||
does not need to correspond to C++ namespaces, but can end in names of classes, e.g.,::
|
||||
.. cpp::enumerator:: MyEnum::myOtherEnumerator = 42
|
||||
|
||||
|
||||
Namespacing
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Declarations in the C++ doamin are as default placed in global scope.
|
||||
The current scope can be changed using three namespace directives.
|
||||
They manage a stack declarations where ``cpp:namespace`` resets the stack and
|
||||
changes a given scope.
|
||||
The ``cpp:namespace-push`` directive changes the scope to a given inner scope
|
||||
of the current one.
|
||||
The ``cpp:namespace-pop`` directive undos the most recent ``cpp:namespace-push``
|
||||
directive.
|
||||
|
||||
.. rst:directive:: .. cpp:namespace:: scope specification
|
||||
|
||||
Changes the current scope for the subsequent objects to the given scope,
|
||||
and resets the namespace directive stack.
|
||||
Note that the namespace does not need to correspond to C++ namespaces,
|
||||
but can end in names of classes, e.g.,::
|
||||
|
||||
.. cpp:namespace:: Namespace1::Namespace2::SomeClass::AnInnerClass
|
||||
|
||||
All subsequent objects will be defined as if their name were declared with the namespace
|
||||
prepended. The subsequent cross-references will be searched for by both their specified name
|
||||
and with the namespace prepended.
|
||||
All subsequent objects will be defined as if their name were declared with the scope
|
||||
prepended. The subsequent cross-references will be searched for starting in the current scope.
|
||||
|
||||
Using ``NULL``, ``0``, or ``nullptr`` as the namespace will reset it to the global namespace.
|
||||
Using ``NULL``, ``0``, or ``nullptr`` as the scope will change to global scope.
|
||||
|
||||
A namespace declaration can also be templated, e.g.,::
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
std::vector
|
||||
|
||||
.. cpp:namespace:: template<typename T> std::vector
|
||||
|
||||
.. cpp:function:: std::size_t size() const
|
||||
|
||||
declares ``size`` as a member function of the template class ``std::vector``.
|
||||
Equivalently this could have been declared using::
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
std::vector
|
||||
|
||||
.. cpp:function:: std::size_t size() const
|
||||
|
||||
or:::
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
std::vector
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:namespace-push:: scope specification
|
||||
|
||||
Change the scope relatively to the current scope. For example, after::
|
||||
|
||||
.. cpp:namespace:: A::B
|
||||
|
||||
.. cpp:namespace-push:: C::D
|
||||
|
||||
the current scope will be ``A::B::C::D``.
|
||||
|
||||
.. rst:directive:: .. cpp:namespace-pop::
|
||||
|
||||
Undo the previous ``cpp:namespace-push`` directive (*not* just pop a scope).
|
||||
For example, after::
|
||||
|
||||
.. cpp:namespace:: A::B
|
||||
|
||||
.. cpp:namespace-push:: C::D
|
||||
|
||||
.. cpp:namespace-pop::
|
||||
|
||||
the current scope will be ``A::B`` (*not* ``A::B::C``).
|
||||
|
||||
If no previous ``cpp:namespace-push`` directive has been used, but only a ``cpp:namespace``
|
||||
directive, then the current scope will be reset to global scope.
|
||||
That is, ``.. cpp:namespace:: A::B`` is equivalent to::
|
||||
|
||||
.. cpp:namespace:: nullptr
|
||||
|
||||
.. cpp:namespace-push:: A::B
|
||||
|
||||
|
||||
Info field lists
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The C++ directives support the following info fields (see also :ref:`info-field-lists`):
|
||||
|
||||
* `param`, `parameter`, `arg`, `argument`: Description of a parameter.
|
||||
* `tparam`: Description of a template parameter.
|
||||
* `returns`, `return`: Description of a return value.
|
||||
* `throws`, `throw`, `exception`: Description of a possibly thrown exception.
|
||||
|
||||
|
||||
.. _cpp-roles:
|
||||
|
||||
These roles link to the given object types:
|
||||
Cross-referencing
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
These roles link to the given declaration types:
|
||||
|
||||
.. rst:role:: cpp:any
|
||||
cpp:class
|
||||
@@ -639,19 +776,19 @@ These roles link to the given object types:
|
||||
cpp:enum
|
||||
cpp:enumerator
|
||||
|
||||
Reference a C++ object by name. The name must be properly qualified relative to the
|
||||
position of the link.
|
||||
Reference a C++ declaration by name (see below for details).
|
||||
The name must be properly qualified relative to the position of the link.
|
||||
|
||||
.. note::
|
||||
.. admonition:: Note on References with Templates Parameters/Arguments
|
||||
|
||||
Sphinx's syntax to give references a custom title can interfere with
|
||||
linking to template classes, if nothing follows the closing angle
|
||||
bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass<T>```.
|
||||
This is interpreted as a link to ``T`` with a title of ``MyClass``.
|
||||
In this case, please escape the opening angle bracket with a backslash,
|
||||
like this: ``:cpp:class:`MyClass\<T>```.
|
||||
Sphinx's syntax to give references a custom title can interfere with
|
||||
linking to template classes, if nothing follows the closing angle
|
||||
bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass<int>```.
|
||||
This is interpreted as a link to ``int`` with a title of ``MyClass``.
|
||||
In this case, please escape the opening angle bracket with a backslash,
|
||||
like this: ``:cpp:class:`MyClass\<int>```.
|
||||
|
||||
.. admonition:: Note on References
|
||||
.. admonition:: Note on References to Overloaded Functions
|
||||
|
||||
It is currently impossible to link to a specific version of an
|
||||
overloaded method. Currently the C++ domain is the first domain
|
||||
@@ -660,11 +797,79 @@ These roles link to the given object types:
|
||||
specific overload. Currently Sphinx will link to the first overloaded
|
||||
version of the method / function.
|
||||
|
||||
.. admonition:: Note on Template Delcarations
|
||||
Declarations without template parameters and template arguments
|
||||
.................................................................
|
||||
|
||||
For linking to non-templated declarations the name must be a nested name,
|
||||
e.g., ``f`` or ``MyClass::f``.
|
||||
|
||||
Templated declarations
|
||||
......................
|
||||
|
||||
Assume the following declarations.
|
||||
|
||||
.. cpp:class:: Wrapper
|
||||
|
||||
.. cpp:class:: template<typename TOuter> \
|
||||
Outer
|
||||
|
||||
.. cpp:class:: template<typename TInner> \
|
||||
Inner
|
||||
|
||||
In general the reference must include the template paraemter declarations, e.g.,
|
||||
``template\<typename TOuter> Wrapper::Outer`` (:cpp:class:`template\<typename TOuter> Wrapper::Outer`).
|
||||
Currently the lookup only succeed if the template parameter identifiers are equal strings. That is,
|
||||
``template\<typename UOuter> Wrapper::Outer`` will not work.
|
||||
|
||||
The inner template class can not be directly referenced, unless the current namespace
|
||||
is changed or the following shorthand is used.
|
||||
If a template parameter list is omitted, then the lookup will assume either a template or a non-template,
|
||||
but not a partial template specialisation.
|
||||
This means the following references work.
|
||||
|
||||
- ``Wrapper::Outer`` (:cpp:class:`Wrapper::Outer`)
|
||||
- ``Wrapper::Outer::Inner`` (:cpp:class:`Wrapper::Outer::Inner`)
|
||||
- ``template\<typename TInner> Wrapper::Outer::Inner`` (:cpp:class:`template\<typename TInner> Wrapper::Outer::Inner`)
|
||||
|
||||
(Full) Template Specialisations
|
||||
................................
|
||||
|
||||
Assume the following declarations.
|
||||
|
||||
.. cpp:class:: template<typename TOuter> \
|
||||
Outer
|
||||
|
||||
.. cpp:class:: template<typename TInner> \
|
||||
Inner
|
||||
|
||||
.. cpp:class:: template<> \
|
||||
Outer<int>
|
||||
|
||||
.. cpp:class:: template<typename TInner> \
|
||||
Inner
|
||||
|
||||
.. cpp:class:: template<> \
|
||||
Inner<bool>
|
||||
|
||||
In general the reference must include a template parameter list for each template argument list.
|
||||
The full specialisation above can therefore be referenced with ``template\<> Outer\<int>`` (:cpp:class:`template\<> Outer\<int>`)
|
||||
and ``template\<> template\<> Outer\<int>::Inner\<bool>`` (:cpp:class:`template\<> template\<> Outer\<int>::Inner\<bool>`).
|
||||
As a shorthand the empty template parameter list can be omitted, e.g., ``Outer\<int>`` (:cpp:class:`Outer\<int>`)
|
||||
and ``Outer\<int>::Inner\<bool>`` (:cpp:class:`Outer\<int>::Inner\<bool>`).
|
||||
|
||||
|
||||
Partial Template Specialisations
|
||||
.................................
|
||||
|
||||
Assume the following declaration.
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
Outer<T*>
|
||||
|
||||
References to partial specialisations must always include the template parameter lists, e.g.,
|
||||
``template\<typename T> Outer\<T*>`` (:cpp:class:`template\<typename T> Outer\<T*>`).
|
||||
Currently the lookup only succeed if the template parameter identifiers are equal strings.
|
||||
|
||||
The C++ domain currently does not support template classes/functions/aliases/variables
|
||||
(e.g., ``template<typename T> MyClass``), only template instantiations
|
||||
(e.g., ``MyClass<T>``).
|
||||
|
||||
|
||||
The Standard Domain
|
||||
@@ -696,6 +901,8 @@ There is a set of directives allowing documenting command-line programs:
|
||||
referencable by :rst:role:`option` (in the example case, you'd use something
|
||||
like ``:option:`dest_dir```, ``:option:`-m```, or ``:option:`--module```).
|
||||
|
||||
``cmdoption`` directive is a deprecated alias for the ``option`` directive.
|
||||
|
||||
.. rst:directive:: .. envvar:: name
|
||||
|
||||
Describes an environment variable that the documented code or program uses or
|
||||
@@ -875,13 +1082,14 @@ More domains
|
||||
|
||||
The sphinx-contrib_ repository contains more domains available as extensions;
|
||||
currently Ada_, CoffeeScript_, Erlang_, HTTP_, Lasso_, MATLAB_, PHP_, and Ruby_
|
||||
domains. Also available are domains for `Common Lisp`_, dqn_, Go_, Jinja_,
|
||||
Operation_, and Scala_.
|
||||
domains. Also available are domains for `Chapel`_, `Common Lisp`_, dqn_, Go_,
|
||||
Jinja_, Operation_, and Scala_.
|
||||
|
||||
|
||||
.. _sphinx-contrib: https://bitbucket.org/birkenfeld/sphinx-contrib/
|
||||
|
||||
.. _Ada: https://pypi.python.org/pypi/sphinxcontrib-adadomain
|
||||
.. _Chapel: https://pypi.python.org/pypi/sphinxcontrib-chapeldomain
|
||||
.. _CoffeeScript: https://pypi.python.org/pypi/sphinxcontrib-coffee
|
||||
.. _Common Lisp: https://pypi.python.org/pypi/sphinxcontrib-cldomain
|
||||
.. _dqn: https://pypi.python.org/pypi/sphinxcontrib-dqndomain
|
||||
|
||||
@@ -24,6 +24,10 @@ Attributes:
|
||||
one convention to document module level variables and be consistent
|
||||
with it.
|
||||
|
||||
Todo:
|
||||
* For module TODOs
|
||||
* You have to also use ``sphinx.ext.todo`` extension
|
||||
|
||||
.. _Google Python Style Guide:
|
||||
http://google.github.io/styleguide/pyguide.html
|
||||
|
||||
@@ -237,16 +241,17 @@ class ExampleClass(object):
|
||||
return True
|
||||
|
||||
def __special__(self):
|
||||
"""By default special members with docstrings are included.
|
||||
"""By default special members with docstrings are not included.
|
||||
|
||||
Special members are any methods or attributes that start with and
|
||||
end with a double underscore. Any special member with a docstring
|
||||
will be included in the output.
|
||||
will be included in the output, if
|
||||
``napoleon_include_special_with_doc`` is set to True.
|
||||
|
||||
This behavior can be disabled by changing the following setting in
|
||||
This behavior can be enabled by changing the following setting in
|
||||
Sphinx's conf.py::
|
||||
|
||||
napoleon_include_special_with_doc = False
|
||||
napoleon_include_special_with_doc = True
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -286,16 +286,17 @@ class ExampleClass(object):
|
||||
return True
|
||||
|
||||
def __special__(self):
|
||||
"""By default special members with docstrings are included.
|
||||
"""By default special members with docstrings are not included.
|
||||
|
||||
Special members are any methods or attributes that start with and
|
||||
end with a double underscore. Any special member with a docstring
|
||||
will be included in the output.
|
||||
will be included in the output, if
|
||||
``napoleon_include_special_with_doc`` is set to True.
|
||||
|
||||
This behavior can be disabled by changing the following setting in
|
||||
This behavior can be enabled by changing the following setting in
|
||||
Sphinx's conf.py::
|
||||
|
||||
napoleon_include_special_with_doc = False
|
||||
napoleon_include_special_with_doc = True
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -53,6 +53,10 @@ It adds these directives:
|
||||
|
||||
"bar" -- "baz";
|
||||
|
||||
.. note:: The graph name is passed unchanged to Graphviz. If it contains
|
||||
non-alphanumeric characters (e.g. a dash), you will have to double-quote
|
||||
it.
|
||||
|
||||
|
||||
.. rst:directive:: digraph
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ This extension is quite simple, and features only one directive:
|
||||
:file:`conf.py`, e.g.::
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('releaselevel', '', True)
|
||||
app.add_config_value('releaselevel', '', 'env')
|
||||
|
||||
The second argument is the default value, the third should always be ``True``
|
||||
The second argument is the default value, the third should always be ``'env'``
|
||||
for such values (it selects if Sphinx re-reads the documents if the value
|
||||
changes).
|
||||
|
||||
@@ -110,6 +110,7 @@ All of the following section headers are supported:
|
||||
* ``Raises``
|
||||
* ``References``
|
||||
* ``See Also``
|
||||
* ``Todo``
|
||||
* ``Warning``
|
||||
* ``Warnings`` *(alias of Warning)*
|
||||
* ``Warns``
|
||||
|
||||
@@ -16,6 +16,10 @@ There are two additional directives when using this extension:
|
||||
It will only show up in the output if :confval:`todo_include_todos` is
|
||||
``True``.
|
||||
|
||||
.. versionadded:: 1.3.2
|
||||
This directive supports an ``class`` option that determines the class attribute
|
||||
for HTML output. If not given, the class defaults to ``admonition-todo``.
|
||||
|
||||
|
||||
.. rst:directive:: todolist
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ The new elements are added in the extension's setup function. Let us create a
|
||||
new Python module called :file:`todo.py` and add the setup function::
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('todo_include_todos', False, False)
|
||||
app.add_config_value('todo_include_todos', False, 'html')
|
||||
|
||||
app.add_node(todolist)
|
||||
app.add_node(todo,
|
||||
@@ -171,7 +171,7 @@ the individual calls do is the following:
|
||||
new *config value* ``todo_include_todos``, whose default value should be
|
||||
``False`` (this also tells Sphinx that it is a boolean value).
|
||||
|
||||
If the third argument was ``True``, all documents would be re-read if the
|
||||
If the third argument was ``'html'``, HTML documents would be full rebuild if the
|
||||
config value changed its value. This is needed for config values that
|
||||
influence reading (build phase 1).
|
||||
|
||||
|
||||
+2
-2
@@ -114,7 +114,7 @@ as ``sphinx-build`` easily from the Command Prompt.
|
||||
|
||||
|
||||
Install the pip command
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Python has a very useful :command:`pip` command which can download and install
|
||||
3rd-party libraries with a single command. This is provided by the
|
||||
@@ -139,7 +139,7 @@ install.
|
||||
|
||||
|
||||
Installing Sphinx with pip
|
||||
---------------------------
|
||||
--------------------------
|
||||
|
||||
If you finished the installation of pip, type this line in the command prompt:
|
||||
|
||||
|
||||
@@ -81,8 +81,6 @@ that has to return the directory with themes in it::
|
||||
Builtin themes
|
||||
--------------
|
||||
|
||||
.. cssclass:: right
|
||||
|
||||
+--------------------+--------------------+
|
||||
| **Theme overview** | |
|
||||
+--------------------+--------------------+
|
||||
|
||||
@@ -51,9 +51,9 @@ requires = [
|
||||
'Pygments>=2.0',
|
||||
'docutils>=0.11',
|
||||
'snowballstemmer>=1.1',
|
||||
'babel>=1.3',
|
||||
'babel>=1.3,!=2.0',
|
||||
'alabaster>=0.7,<0.8',
|
||||
'sphinx_rtd_theme>=0.1,<0.2',
|
||||
'sphinx_rtd_theme>=0.1,<2.0',
|
||||
]
|
||||
extras_require = {
|
||||
# Environment Marker works for wheel 0.24 or later
|
||||
@@ -162,7 +162,7 @@ else:
|
||||
messages=jscatalog,
|
||||
plural_expr=catalog.plural_expr,
|
||||
locale=str(catalog.locale)
|
||||
), outfile)
|
||||
), outfile, sort_keys=True)
|
||||
outfile.write(');')
|
||||
finally:
|
||||
outfile.close()
|
||||
|
||||
@@ -373,3 +373,7 @@ Note: By default this script will not overwrite already created files.""")
|
||||
qs.generate(d, silent=True, overwrite=opts.force)
|
||||
elif not opts.notoc:
|
||||
create_modules_toc_file(modules, opts)
|
||||
|
||||
# So program can be started with "python -m sphinx.apidoc ..."
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+27
-4
@@ -78,6 +78,7 @@ class Sphinx(object):
|
||||
self._extensions = {}
|
||||
self._extension_metadata = {}
|
||||
self._listeners = {}
|
||||
self._setting_up_extension = ['?']
|
||||
self.domains = BUILTIN_DOMAINS.copy()
|
||||
self.buildername = buildername
|
||||
self.builderclasses = BUILTIN_BUILDERS.copy()
|
||||
@@ -144,6 +145,7 @@ class Sphinx(object):
|
||||
self.setup_extension(extension)
|
||||
# the config file itself can be an extension
|
||||
if self.config.setup:
|
||||
self._setting_up_extension = ['conf.py']
|
||||
# py31 doesn't have 'callable' function for below check
|
||||
if hasattr(self.config.setup, '__call__'):
|
||||
self.config.setup(self)
|
||||
@@ -200,7 +202,8 @@ class Sphinx(object):
|
||||
else:
|
||||
locale_dirs = []
|
||||
self.translator, has_translation = locale.init(locale_dirs,
|
||||
self.config.language)
|
||||
self.config.language,
|
||||
charset=self.config.source_encoding)
|
||||
if self.config.language is not None:
|
||||
if has_translation or self.config.language == 'en':
|
||||
# "en" never needs to be translated
|
||||
@@ -426,6 +429,7 @@ class Sphinx(object):
|
||||
self.debug('[app] setting up extension: %r', extension)
|
||||
if extension in self._extensions:
|
||||
return
|
||||
self._setting_up_extension.append(extension)
|
||||
try:
|
||||
mod = __import__(extension, None, None, ['setup'])
|
||||
except ImportError as err:
|
||||
@@ -460,6 +464,7 @@ class Sphinx(object):
|
||||
ext_meta = {'version': 'unknown version'}
|
||||
self._extensions[extension] = mod
|
||||
self._extension_metadata[extension] = ext_meta
|
||||
self._setting_up_extension.pop()
|
||||
|
||||
def require_sphinx(self, version):
|
||||
# check the Sphinx version if requested
|
||||
@@ -530,13 +535,14 @@ class Sphinx(object):
|
||||
builder.name, self.builderclasses[builder.name].__module__))
|
||||
self.builderclasses[builder.name] = builder
|
||||
|
||||
def add_config_value(self, name, default, rebuild):
|
||||
self.debug('[app] adding config value: %r', (name, default, rebuild))
|
||||
def add_config_value(self, name, default, rebuild, types=()):
|
||||
self.debug('[app] adding config value: %r',
|
||||
(name, default, rebuild) + ((types,) if types else ()))
|
||||
if name in self.config.values:
|
||||
raise ExtensionError('Config value %r already present' % name)
|
||||
if rebuild in (False, True):
|
||||
rebuild = rebuild and 'env' or ''
|
||||
self.config.values[name] = (default, rebuild)
|
||||
self.config.values[name] = (default, rebuild, types)
|
||||
|
||||
def add_event(self, name):
|
||||
self.debug('[app] adding event: %r', name)
|
||||
@@ -550,6 +556,11 @@ class Sphinx(object):
|
||||
|
||||
def add_node(self, node, **kwds):
|
||||
self.debug('[app] adding node: %r', (node, kwds))
|
||||
if not kwds.pop('override', False) and \
|
||||
hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__):
|
||||
self.warn('while setting up extension %s: node class %r is '
|
||||
'already registered, its visitors will be overridden' %
|
||||
(self._setting_up_extension, node.__name__))
|
||||
nodes._add_node_class_names([node.__name__])
|
||||
for key, val in iteritems(kwds):
|
||||
try:
|
||||
@@ -594,17 +605,29 @@ class Sphinx(object):
|
||||
def add_directive(self, name, obj, content=None, arguments=None, **options):
|
||||
self.debug('[app] adding directive: %r',
|
||||
(name, obj, content, arguments, options))
|
||||
if name in directives._directives:
|
||||
self.warn('while setting up extension %s: directive %r is '
|
||||
'already registered, it will be overridden' %
|
||||
(self._setting_up_extension[-1], name))
|
||||
directives.register_directive(
|
||||
name, self._directive_helper(obj, content, arguments, **options))
|
||||
|
||||
def add_role(self, name, role):
|
||||
self.debug('[app] adding role: %r', (name, role))
|
||||
if name in roles._roles:
|
||||
self.warn('while setting up extension %s: role %r is '
|
||||
'already registered, it will be overridden' %
|
||||
(self._setting_up_extension[-1], name))
|
||||
roles.register_local_role(name, role)
|
||||
|
||||
def add_generic_role(self, name, nodeclass):
|
||||
# don't use roles.register_generic_role because it uses
|
||||
# register_canonical_role
|
||||
self.debug('[app] adding generic role: %r', (name, nodeclass))
|
||||
if name in roles._roles:
|
||||
self.warn('while setting up extension %s: role %r is '
|
||||
'already registered, it will be overridden' %
|
||||
(self._setting_up_extension[-1], name))
|
||||
role = roles.GenericRole(name, nodeclass)
|
||||
roles.register_local_role(name, role)
|
||||
|
||||
|
||||
@@ -160,16 +160,21 @@ class Builder(object):
|
||||
def compile_catalogs(self, catalogs, message):
|
||||
if not self.config.gettext_auto_build:
|
||||
return
|
||||
|
||||
def cat2relpath(cat):
|
||||
return path.relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP)
|
||||
|
||||
self.info(bold('building [mo]: ') + message)
|
||||
for catalog in self.app.status_iterator(
|
||||
catalogs, 'writing output... ', darkgreen, len(catalogs),
|
||||
lambda c: c.mo_path):
|
||||
cat2relpath):
|
||||
catalog.write_mo(self.config.language)
|
||||
|
||||
def compile_all_catalogs(self):
|
||||
catalogs = i18n.find_catalog_source_files(
|
||||
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
|
||||
self.config.language,
|
||||
charset=self.config.source_encoding,
|
||||
gettext_compact=self.config.gettext_compact,
|
||||
force_all=True)
|
||||
message = 'all of %d po files' % len(catalogs)
|
||||
@@ -186,6 +191,7 @@ class Builder(object):
|
||||
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
|
||||
self.config.language,
|
||||
domains=list(specified_domains),
|
||||
charset=self.config.source_encoding,
|
||||
gettext_compact=self.config.gettext_compact)
|
||||
message = 'targets for %d po files that are specified' % len(catalogs)
|
||||
self.compile_catalogs(catalogs, message)
|
||||
@@ -194,6 +200,7 @@ class Builder(object):
|
||||
catalogs = i18n.find_catalog_source_files(
|
||||
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
|
||||
self.config.language,
|
||||
charset=self.config.source_encoding,
|
||||
gettext_compact=self.config.gettext_compact)
|
||||
message = 'targets for %d po files that are out of date' % len(catalogs)
|
||||
self.compile_catalogs(catalogs, message)
|
||||
|
||||
@@ -91,7 +91,7 @@ class DevhelpBuilder(StandaloneHTMLBuilder):
|
||||
write_toc(subnode, item)
|
||||
elif isinstance(node, nodes.reference):
|
||||
parent.attrib['link'] = node['refuri']
|
||||
parent.attrib['name'] = node.astext().encode('utf-8')
|
||||
parent.attrib['name'] = node.astext()
|
||||
|
||||
def istoctree(node):
|
||||
return isinstance(node, addnodes.compact_paragraph) and \
|
||||
@@ -129,6 +129,6 @@ class DevhelpBuilder(StandaloneHTMLBuilder):
|
||||
# Dump the XML file
|
||||
f = comp_open(path.join(outdir, outname + '.devhelp'), 'w')
|
||||
try:
|
||||
tree.write(f)
|
||||
tree.write(f, 'utf-8')
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import codecs
|
||||
import zipfile
|
||||
from os import path
|
||||
@@ -29,7 +28,7 @@ from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.util.osutil import ensuredir, copyfile, EEXIST
|
||||
from sphinx.util.osutil import ensuredir, copyfile, ustrftime, EEXIST
|
||||
from sphinx.util.smartypants import sphinx_smarty_pants as ssp
|
||||
from sphinx.util.console import brown
|
||||
|
||||
@@ -511,7 +510,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
metadata['copyright'] = self.esc(self.config.epub_copyright)
|
||||
metadata['scheme'] = self.esc(self.config.epub_scheme)
|
||||
metadata['id'] = self.esc(self.config.epub_identifier)
|
||||
metadata['date'] = self.esc(time.strftime('%Y-%m-%d'))
|
||||
metadata['date'] = self.esc(ustrftime('%Y-%m-%d'))
|
||||
metadata['files'] = files
|
||||
metadata['spine'] = spine
|
||||
metadata['guide'] = guide
|
||||
|
||||
+10
-5
@@ -22,7 +22,7 @@ from six.moves import cPickle as pickle
|
||||
from docutils import nodes
|
||||
from docutils.io import DocTreeInput, StringOutput
|
||||
from docutils.core import Publisher
|
||||
from docutils.utils import new_document
|
||||
from docutils.utils import new_document, relative_path
|
||||
from docutils.frontend import OptionParser
|
||||
from docutils.readers.doctree import Reader as DoctreeReader
|
||||
|
||||
@@ -559,12 +559,15 @@ class StandaloneHTMLBuilder(Builder):
|
||||
(path.join(self.srcdir, src), err))
|
||||
|
||||
def copy_download_files(self):
|
||||
def to_relpath(f):
|
||||
return relative_path(self.srcdir, f)
|
||||
# copy downloadable files
|
||||
if self.env.dlfiles:
|
||||
ensuredir(path.join(self.outdir, '_downloads'))
|
||||
for src in self.app.status_iterator(self.env.dlfiles,
|
||||
'copying downloadable files... ',
|
||||
brown, len(self.env.dlfiles)):
|
||||
brown, len(self.env.dlfiles),
|
||||
stringify_func=to_relpath):
|
||||
dest = self.env.dlfiles[src][1]
|
||||
try:
|
||||
copyfile(path.join(self.srcdir, src),
|
||||
@@ -834,13 +837,15 @@ class StandaloneHTMLBuilder(Builder):
|
||||
u'# The remainder of this file is compressed using zlib.\n'
|
||||
% (self.config.project, self.config.version)).encode('utf-8'))
|
||||
compressor = zlib.compressobj(9)
|
||||
for domainname, domain in iteritems(self.env.domains):
|
||||
for domainname, domain in sorted(self.env.domains.items()):
|
||||
for name, dispname, type, docname, anchor, prio in \
|
||||
sorted(domain.get_objects()):
|
||||
if anchor.endswith(name):
|
||||
# this can shorten the inventory by as much as 25%
|
||||
anchor = anchor[:-len(name)] + '$'
|
||||
uri = self.get_target_uri(docname) + '#' + anchor
|
||||
uri = self.get_target_uri(docname)
|
||||
if anchor:
|
||||
uri += '#' + anchor
|
||||
if dispname == name:
|
||||
dispname = u'-'
|
||||
f.write(compressor.compress(
|
||||
@@ -942,7 +947,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
||||
def assemble_doctree(self):
|
||||
master = self.config.master_doc
|
||||
tree = self.env.get_doctree(master)
|
||||
tree = inline_all_toctrees(self, set(), master, tree, darkgreen)
|
||||
tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
|
||||
tree['docname'] = master
|
||||
self.env.resolve_references(tree, master, self)
|
||||
self.fix_refuris(tree)
|
||||
|
||||
@@ -102,11 +102,22 @@ class LaTeXBuilder(Builder):
|
||||
doctree.settings = docsettings
|
||||
doctree.settings.author = author
|
||||
doctree.settings.title = title
|
||||
doctree.settings.contentsname = self.get_contentsname(docname)
|
||||
doctree.settings.docname = docname
|
||||
doctree.settings.docclass = docclass
|
||||
docwriter.write(doctree, destination)
|
||||
self.info("done")
|
||||
|
||||
def get_contentsname(self, indexfile):
|
||||
tree = self.env.get_doctree(indexfile)
|
||||
contentsname = None
|
||||
for toctree in tree.traverse(addnodes.toctree):
|
||||
if toctree['caption']:
|
||||
contentsname = toctree['caption']
|
||||
break
|
||||
|
||||
return contentsname
|
||||
|
||||
def assemble_doctree(self, indexfile, toctree_only, appendices):
|
||||
self.docnames = set([indexfile] + appendices)
|
||||
self.info(darkgreen(indexfile) + " ", nonl=1)
|
||||
@@ -124,7 +135,7 @@ class LaTeXBuilder(Builder):
|
||||
new_sect += node
|
||||
tree = new_tree
|
||||
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
|
||||
darkgreen)
|
||||
darkgreen, [indexfile])
|
||||
largetree['docname'] = indexfile
|
||||
for docname in appendices:
|
||||
appendix = self.env.get_doctree(docname)
|
||||
|
||||
@@ -19,9 +19,19 @@ from six.moves import queue
|
||||
from six.moves.urllib.request import build_opener, Request, HTTPRedirectHandler
|
||||
from six.moves.urllib.parse import unquote, urlsplit, quote
|
||||
from six.moves.urllib.error import HTTPError
|
||||
from six.moves.html_parser import HTMLParser, HTMLParseError
|
||||
from six.moves.html_parser import HTMLParser
|
||||
from docutils import nodes
|
||||
|
||||
# 2015-06-25 barry@python.org. This exception was deprecated in Python 3.3 and
|
||||
# removed in Python 3.5, however for backward compatibility reasons, we're not
|
||||
# going to just remove it. If it doesn't exist, define an exception that will
|
||||
# never be caught but leaves the code in check_anchor() intact.
|
||||
try:
|
||||
from six.moves.html_parser import HTMLParseError
|
||||
except ImportError:
|
||||
class HTMLParseError(Exception):
|
||||
pass
|
||||
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.util.console import purple, red, darkgreen, darkgray, \
|
||||
darkred, turquoise
|
||||
|
||||
@@ -70,7 +70,7 @@ class ManualPageBuilder(Builder):
|
||||
tree = self.env.get_doctree(docname)
|
||||
docnames = set()
|
||||
largetree = inline_all_toctrees(self, docnames, docname, tree,
|
||||
darkgreen)
|
||||
darkgreen, [docname])
|
||||
self.info('} ', nonl=True)
|
||||
self.env.resolve_references(largetree, docname, self)
|
||||
# remove pending_xref nodes
|
||||
|
||||
@@ -180,7 +180,7 @@ class TexinfoBuilder(Builder):
|
||||
new_sect += node
|
||||
tree = new_tree
|
||||
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
|
||||
darkgreen)
|
||||
darkgreen, [indexfile])
|
||||
largetree['docname'] = indexfile
|
||||
for docname in appendices:
|
||||
appendix = self.env.get_doctree(docname)
|
||||
|
||||
@@ -114,6 +114,8 @@ class WebSupportBuilder(PickleHTMLBuilder):
|
||||
doc_ctx = {
|
||||
'body': ctx.get('body', ''),
|
||||
'title': ctx.get('title', ''),
|
||||
'css': ctx.get('css', ''),
|
||||
'script': ctx.get('script', ''),
|
||||
}
|
||||
# partially render the html template to get at interesting macros
|
||||
template = self.templates.environment.get_template(templatename)
|
||||
|
||||
+1
-6
@@ -27,11 +27,6 @@ from sphinx.util.osutil import abspath, fs_encoding
|
||||
from sphinx.util.pycompat import terminal_safe
|
||||
|
||||
|
||||
def usage(argv, msg=None):
|
||||
if msg:
|
||||
print(msg, file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
|
||||
USAGE = """\
|
||||
Sphinx v%s
|
||||
Usage: %%prog [options] sourcedir outdir [filenames...]
|
||||
@@ -150,7 +145,7 @@ def main(argv):
|
||||
return 1
|
||||
outdir = abspath(args[1])
|
||||
except IndexError:
|
||||
usage(argv, 'Error: Insufficient arguments.')
|
||||
parser.print_help()
|
||||
return 1
|
||||
except UnicodeError:
|
||||
print(
|
||||
|
||||
+45
-41
@@ -18,7 +18,7 @@ from six import PY3, iteritems, string_types, binary_type, integer_types
|
||||
from sphinx.errors import ConfigError
|
||||
from sphinx.locale import l_
|
||||
from sphinx.util.osutil import make_filename, cd
|
||||
from sphinx.util.pycompat import execfile_
|
||||
from sphinx.util.pycompat import execfile_, NoneType
|
||||
|
||||
nonascii_re = re.compile(br'[\x80-\xff]')
|
||||
|
||||
@@ -27,10 +27,8 @@ 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()"
|
||||
|
||||
IGNORE_CONFIG_TYPE_CHECKS = (
|
||||
'html_domain_indices', 'latex_domain_indices', 'texinfo_domain_indices'
|
||||
)
|
||||
CONFIG_TYPE_WARNING = "The config value `{name}' has type `{current.__name__}', " \
|
||||
"defaults to `{default.__name__}.'"
|
||||
|
||||
|
||||
class Config(object):
|
||||
@@ -50,9 +48,10 @@ class Config(object):
|
||||
version = ('', 'env'),
|
||||
release = ('', 'env'),
|
||||
today = ('', 'env'),
|
||||
today_fmt = (None, 'env'), # the real default is locale-dependent
|
||||
# the real default is locale-dependent
|
||||
today_fmt = (None, 'env', [str]),
|
||||
|
||||
language = (None, 'env'),
|
||||
language = (None, 'env', [str]),
|
||||
locale_dirs = ([], 'env'),
|
||||
|
||||
master_doc = ('contents', 'env'),
|
||||
@@ -60,23 +59,23 @@ class Config(object):
|
||||
source_encoding = ('utf-8-sig', 'env'),
|
||||
source_parsers = ({}, 'env'),
|
||||
exclude_patterns = ([], 'env'),
|
||||
default_role = (None, 'env'),
|
||||
default_role = (None, 'env', [str]),
|
||||
add_function_parentheses = (True, 'env'),
|
||||
add_module_names = (True, 'env'),
|
||||
trim_footnote_reference_space = (False, 'env'),
|
||||
show_authors = (False, 'env'),
|
||||
pygments_style = (None, 'html'),
|
||||
pygments_style = (None, 'html', [str]),
|
||||
highlight_language = ('python', 'env'),
|
||||
highlight_options = ({}, 'env'),
|
||||
templates_path = ([], 'html'),
|
||||
template_bridge = (None, 'html'),
|
||||
template_bridge = (None, 'html', [str]),
|
||||
keep_warnings = (False, 'env'),
|
||||
modindex_common_prefix = ([], 'html'),
|
||||
rst_epilog = (None, 'env'),
|
||||
rst_prolog = (None, 'env'),
|
||||
rst_epilog = (None, 'env', [str]),
|
||||
rst_prolog = (None, 'env', [str]),
|
||||
trim_doctest_flags = (True, 'env'),
|
||||
primary_domain = ('py', 'env'),
|
||||
needs_sphinx = (None, None),
|
||||
primary_domain = ('py', 'env', [NoneType]),
|
||||
needs_sphinx = (None, None, [str]),
|
||||
needs_extensions = ({}, None),
|
||||
nitpicky = (False, 'env'),
|
||||
nitpick_ignore = ([], 'html'),
|
||||
@@ -95,34 +94,34 @@ class Config(object):
|
||||
(self.project, self.release),
|
||||
'html'),
|
||||
html_short_title = (lambda self: self.html_title, 'html'),
|
||||
html_style = (None, 'html'),
|
||||
html_logo = (None, 'html'),
|
||||
html_favicon = (None, 'html'),
|
||||
html_style = (None, 'html', [str]),
|
||||
html_logo = (None, 'html', [str]),
|
||||
html_favicon = (None, 'html', [str]),
|
||||
html_static_path = ([], 'html'),
|
||||
html_extra_path = ([], 'html'),
|
||||
# the real default is locale-dependent
|
||||
html_last_updated_fmt = (None, 'html'),
|
||||
html_last_updated_fmt = (None, 'html', [str]),
|
||||
html_use_smartypants = (True, 'html'),
|
||||
html_translator_class = (None, 'html'),
|
||||
html_translator_class = (None, 'html', [str]),
|
||||
html_sidebars = ({}, 'html'),
|
||||
html_additional_pages = ({}, 'html'),
|
||||
html_use_modindex = (True, 'html'), # deprecated
|
||||
html_domain_indices = (True, 'html'),
|
||||
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'),
|
||||
html_link_suffix = (None, 'html'),
|
||||
html_file_suffix = (None, 'html', [str]),
|
||||
html_link_suffix = (None, 'html', [str]),
|
||||
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'),
|
||||
html_search_language = (None, 'html', [str]),
|
||||
html_search_options = ({}, 'html'),
|
||||
html_search_scorer = ('', None),
|
||||
html_scaled_image_link = (True, 'html'),
|
||||
@@ -139,17 +138,17 @@ class Config(object):
|
||||
# Apple help options
|
||||
applehelp_bundle_name = (lambda self: make_filename(self.project),
|
||||
'applehelp'),
|
||||
applehelp_bundle_id = (None, 'applehelp'),
|
||||
applehelp_bundle_id = (None, 'applehelp', [str]),
|
||||
applehelp_dev_region = ('en-us', 'applehelp'),
|
||||
applehelp_bundle_version = ('1', 'applehelp'),
|
||||
applehelp_icon = (None, 'applehelp'),
|
||||
applehelp_icon = (None, 'applehelp', [str]),
|
||||
applehelp_kb_product = (lambda self: '%s-%s' %
|
||||
(make_filename(self.project), self.release),
|
||||
'applehelp'),
|
||||
applehelp_kb_url = (None, 'applehelp'),
|
||||
applehelp_remote_url = (None, 'applehelp'),
|
||||
applehelp_index_anchors = (False, 'applehelp'),
|
||||
applehelp_min_term_length = (None, 'applehelp'),
|
||||
applehelp_kb_url = (None, 'applehelp', [str]),
|
||||
applehelp_remote_url = (None, 'applehelp', [str]),
|
||||
applehelp_index_anchors = (False, 'applehelp', [str]),
|
||||
applehelp_min_term_length = (None, 'applehelp', [str]),
|
||||
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'),
|
||||
@@ -196,11 +195,11 @@ class Config(object):
|
||||
self.project,
|
||||
'', 'manual')],
|
||||
None),
|
||||
latex_logo = (None, None),
|
||||
latex_logo = (None, None, [str]),
|
||||
latex_appendices = ([], None),
|
||||
latex_use_parts = (False, None),
|
||||
latex_use_modindex = (True, None), # deprecated
|
||||
latex_domain_indices = (True, None),
|
||||
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
|
||||
@@ -236,13 +235,13 @@ class Config(object):
|
||||
None),
|
||||
texinfo_appendices = ([], None),
|
||||
texinfo_elements = ({}, None),
|
||||
texinfo_domain_indices = (True, None),
|
||||
texinfo_domain_indices = (True, None, [list]),
|
||||
texinfo_show_urls = ('footnote', None),
|
||||
texinfo_no_detailmenu = (False, None),
|
||||
|
||||
# linkcheck options
|
||||
linkcheck_ignore = ([], None),
|
||||
linkcheck_timeout = (None, None),
|
||||
linkcheck_timeout = (None, None, [int]),
|
||||
linkcheck_workers = (5, None),
|
||||
linkcheck_anchors = (True, None),
|
||||
|
||||
@@ -292,25 +291,30 @@ class Config(object):
|
||||
# NB. since config values might use l_() we have to wait with calling
|
||||
# this method until i18n is initialized
|
||||
for name in self._raw_config:
|
||||
if name in IGNORE_CONFIG_TYPE_CHECKS:
|
||||
continue # for a while, ignore multiple types config value. see #1781
|
||||
if name not in Config.config_values:
|
||||
if name not in self.values:
|
||||
continue # we don't know a default value
|
||||
default, dummy_rebuild = Config.config_values[name]
|
||||
settings = self.values[name]
|
||||
default, dummy_rebuild = settings[:2]
|
||||
permitted = settings[2] if len(settings) == 3 else ()
|
||||
|
||||
if hasattr(default, '__call__'):
|
||||
default = default(self) # could invoke l_()
|
||||
if default is None:
|
||||
continue
|
||||
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
|
||||
|
||||
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("the config value %r has type `%s', defaults to `%s.'" %
|
||||
(name, type(current).__name__, type(default).__name__))
|
||||
|
||||
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,
|
||||
|
||||
@@ -142,7 +142,7 @@ class Author(Directive):
|
||||
env = self.state.document.settings.env
|
||||
if not env.config.show_authors:
|
||||
return []
|
||||
para = nodes.paragraph()
|
||||
para = nodes.paragraph(translatable=False)
|
||||
emph = nodes.emphasis()
|
||||
para += emph
|
||||
if self.name == 'sectionauthor':
|
||||
@@ -205,7 +205,7 @@ class VersionChange(Directive):
|
||||
if len(self.arguments) == 2:
|
||||
inodes, messages = self.state.inline_text(self.arguments[1],
|
||||
self.lineno+1)
|
||||
para = nodes.paragraph(self.arguments[1], '', *inodes)
|
||||
para = nodes.paragraph(self.arguments[1], '', *inodes, translatable=False)
|
||||
set_source_info(self, para)
|
||||
node.append(para)
|
||||
else:
|
||||
@@ -218,13 +218,14 @@ class VersionChange(Directive):
|
||||
content.source = node[0].source
|
||||
content.line = node[0].line
|
||||
content += node[0].children
|
||||
node[0].replace_self(nodes.paragraph('', '', content))
|
||||
node[0].replace_self(nodes.paragraph('', '', content, translatable=False))
|
||||
node[0].insert(0, nodes.inline('', '%s: ' % text,
|
||||
classes=['versionmodified']))
|
||||
else:
|
||||
para = nodes.paragraph('', '',
|
||||
nodes.inline('', '%s.' % text,
|
||||
classes=['versionmodified']))
|
||||
classes=['versionmodified']),
|
||||
translatable=False)
|
||||
node.append(para)
|
||||
env = self.state.document.settings.env
|
||||
# XXX should record node.source as well
|
||||
|
||||
+1808
-541
File diff suppressed because it is too large
Load Diff
+17
-7
@@ -224,7 +224,7 @@ def make_termnodes_from_paragraph_node(env, node, new_id=None):
|
||||
|
||||
termtext = node.astext()
|
||||
if new_id is None:
|
||||
new_id = 'term-' + nodes.make_id(termtext)
|
||||
new_id = nodes.make_id('term-' + termtext)
|
||||
if new_id in gloss_entries:
|
||||
new_id = 'term-' + str(len(gloss_entries))
|
||||
gloss_entries.add(new_id)
|
||||
@@ -585,12 +585,14 @@ class StandardDomain(Domain):
|
||||
continue
|
||||
labels[name] = docname, labelid, sectname
|
||||
|
||||
def build_reference_node(self, fromdocname, builder,
|
||||
docname, labelid, sectname,
|
||||
**options):
|
||||
def build_reference_node(self, fromdocname, builder, docname, labelid,
|
||||
sectname, rolename, **options):
|
||||
nodeclass = options.pop('nodeclass', nodes.reference)
|
||||
newnode = nodeclass('', '', internal=True, **options)
|
||||
innernode = nodes.inline(sectname, sectname)
|
||||
if innernode.get('classes') is not None:
|
||||
innernode['classes'].append('std')
|
||||
innernode['classes'].append('std-' + rolename)
|
||||
if docname == fromdocname:
|
||||
newnode['refid'] = labelid
|
||||
else:
|
||||
@@ -624,7 +626,7 @@ class StandardDomain(Domain):
|
||||
return None
|
||||
|
||||
return self.build_reference_node(fromdocname, builder,
|
||||
docname, labelid, sectname)
|
||||
docname, labelid, sectname, 'ref')
|
||||
elif typ == 'numref':
|
||||
docname, labelid = self.data['anonlabels'].get(target, ('', ''))
|
||||
if not docname:
|
||||
@@ -646,9 +648,14 @@ class StandardDomain(Domain):
|
||||
if target == fully_normalize_name(title):
|
||||
title = env.config.numfig_format.get(figtype, '')
|
||||
|
||||
newtitle = title % '.'.join(map(str, fignumber))
|
||||
try:
|
||||
newtitle = title % '.'.join(map(str, fignumber))
|
||||
except TypeError:
|
||||
env.warn(fromdocname, 'invalid numfig_format: %s' % title)
|
||||
return None
|
||||
|
||||
return self.build_reference_node(fromdocname, builder,
|
||||
docname, labelid, newtitle,
|
||||
docname, labelid, newtitle, 'numref',
|
||||
nodeclass=addnodes.number_reference,
|
||||
title=title)
|
||||
elif typ == 'keyword':
|
||||
@@ -713,6 +720,9 @@ class StandardDomain(Domain):
|
||||
return results
|
||||
|
||||
def get_objects(self):
|
||||
# handle the special 'doc' reference here
|
||||
for doc in self.env.all_docs:
|
||||
yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
|
||||
for (prog, option), info in iteritems(self.data['progoptions']):
|
||||
yield (option, option, 'option', info[0], info[1], 1)
|
||||
for (type, name), info in iteritems(self.data['objects']):
|
||||
|
||||
@@ -49,9 +49,11 @@ from sphinx.util.websupport import is_commentable
|
||||
from sphinx.errors import SphinxError, ExtensionError
|
||||
from sphinx.locale import _
|
||||
from sphinx.versioning import add_uids, merge_doctrees
|
||||
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
|
||||
HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale, \
|
||||
RemoveTranslatableInline, SphinxContentsFilter, ExtraTranslatableNodes
|
||||
from sphinx.transforms import (
|
||||
DefaultSubstitutions, MoveModuleTargets, ApplySourceWorkaround,
|
||||
HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale,
|
||||
RemoveTranslatableInline, SphinxContentsFilter, ExtraTranslatableNodes,
|
||||
)
|
||||
|
||||
|
||||
orig_role_function = roles.role
|
||||
@@ -99,7 +101,7 @@ class SphinxStandaloneReader(standalone.Reader):
|
||||
"""
|
||||
Add our own transforms.
|
||||
"""
|
||||
transforms = [ExtraTranslatableNodes, Locale, CitationReferences,
|
||||
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, Locale, CitationReferences,
|
||||
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
|
||||
AutoNumbering, SortIds, RemoveTranslatableInline]
|
||||
|
||||
@@ -1604,7 +1606,8 @@ class BuildEnvironment:
|
||||
caption = node.astext()
|
||||
else:
|
||||
caption = clean_astext(self.titles[docname])
|
||||
innernode = nodes.emphasis(caption, caption)
|
||||
innernode = nodes.inline(caption, caption)
|
||||
innernode['classes'].append('doc')
|
||||
newnode = nodes.reference('', '', internal=True)
|
||||
newnode['refuri'] = builder.get_relative_uri(node['refdoc'], docname)
|
||||
newnode.append(innernode)
|
||||
|
||||
+131
-9
@@ -17,7 +17,8 @@ import inspect
|
||||
import traceback
|
||||
from types import FunctionType, BuiltinFunctionType, MethodType
|
||||
|
||||
from six import iteritems, itervalues, text_type, class_types, string_types
|
||||
from six import iterkeys, iteritems, itervalues, text_type, class_types, \
|
||||
string_types, StringIO
|
||||
from docutils import nodes
|
||||
from docutils.utils import assemble_option_dict
|
||||
from docutils.statemachine import ViewList
|
||||
@@ -33,6 +34,10 @@ from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
|
||||
safe_getattr, object_description, is_builtin_class_method
|
||||
from sphinx.util.docstrings import prepare_docstring
|
||||
|
||||
try:
|
||||
import typing
|
||||
except ImportError:
|
||||
typing = None
|
||||
|
||||
#: extended signature RE: with explicit module name separated by ::
|
||||
py_ext_sig_re = re.compile(
|
||||
@@ -245,9 +250,126 @@ def between(marker, what=None, keepempty=False, exclude=False):
|
||||
return process
|
||||
|
||||
|
||||
def formatargspec(*argspec):
|
||||
return inspect.formatargspec(*argspec,
|
||||
formatvalue=lambda x: '=' + object_description(x))
|
||||
def format_annotation(annotation):
|
||||
"""Return formatted representation of a type annotation.
|
||||
|
||||
Show qualified names for types and additional details for types from
|
||||
the ``typing`` module.
|
||||
|
||||
Displaying complex types from ``typing`` relies on its private API.
|
||||
"""
|
||||
qualified_name = (annotation.__module__ + '.' + annotation.__qualname__
|
||||
if annotation else repr(annotation))
|
||||
|
||||
if not isinstance(annotation, type):
|
||||
return repr(annotation)
|
||||
elif annotation.__module__ == 'builtins':
|
||||
return annotation.__qualname__
|
||||
elif typing:
|
||||
if isinstance(annotation, typing.TypeVar):
|
||||
return annotation.__name__
|
||||
elif hasattr(typing, 'GenericMeta') and \
|
||||
isinstance(annotation, typing.GenericMeta) and \
|
||||
hasattr(annotation, '__parameters__'):
|
||||
params = annotation.__parameters__
|
||||
if params is not None:
|
||||
param_str = ', '.join(format_annotation(p) for p in params)
|
||||
return '%s[%s]' % (qualified_name, param_str)
|
||||
elif hasattr(typing, 'UnionMeta') and \
|
||||
isinstance(annotation, typing.UnionMeta) and \
|
||||
hasattr(annotation, '__union_params__'):
|
||||
params = annotation.__union_params__
|
||||
if params is not None:
|
||||
param_str = ', '.join(format_annotation(p) for p in params)
|
||||
return '%s[%s]' % (qualified_name, param_str)
|
||||
elif hasattr(typing, 'CallableMeta') and \
|
||||
isinstance(annotation, typing.CallableMeta) and \
|
||||
hasattr(annotation, '__args__') and \
|
||||
hasattr(annotation, '__result__'):
|
||||
args = annotation.__args__
|
||||
if args is Ellipsis:
|
||||
args_str = '...'
|
||||
else:
|
||||
formatted_args = (format_annotation(a) for a in args)
|
||||
args_str = '[%s]' % ', '.join(formatted_args)
|
||||
return '%s[%s, %s]' % (qualified_name,
|
||||
args_str,
|
||||
format_annotation(annotation.__result__))
|
||||
elif hasattr(typing, 'TupleMeta') and \
|
||||
isinstance(annotation, typing.TupleMeta) and \
|
||||
hasattr(annotation, '__tuple_params__') and \
|
||||
hasattr(annotation, '__tuple_use_ellipsis__'):
|
||||
params = annotation.__tuple_params__
|
||||
if params is not None:
|
||||
param_strings = [format_annotation(p) for p in params]
|
||||
if annotation.__tuple_use_ellipsis__:
|
||||
param_strings.append('...')
|
||||
return '%s[%s]' % (qualified_name,
|
||||
', '.join(param_strings))
|
||||
return qualified_name
|
||||
|
||||
|
||||
def formatargspec(function, args, varargs=None, varkw=None, defaults=None,
|
||||
kwonlyargs=(), kwonlydefaults={}, annotations={}):
|
||||
"""Return a string representation of an ``inspect.FullArgSpec`` tuple.
|
||||
|
||||
An enhanced version of ``inspect.formatargspec()`` that handles typing
|
||||
annotations better.
|
||||
"""
|
||||
|
||||
def format_arg_with_annotation(name):
|
||||
if name in annotations:
|
||||
return '%s: %s' % (name, format_annotation(get_annotation(name)))
|
||||
return name
|
||||
|
||||
def get_annotation(name):
|
||||
value = annotations[name]
|
||||
if isinstance(value, string_types):
|
||||
return introspected_hints.get(name, value)
|
||||
else:
|
||||
return value
|
||||
|
||||
introspected_hints = (typing.get_type_hints(function)
|
||||
if typing and hasattr(function, '__code__') else {})
|
||||
|
||||
fd = StringIO()
|
||||
fd.write('(')
|
||||
|
||||
formatted = []
|
||||
defaults_start = len(args) - len(defaults) if defaults else len(args)
|
||||
|
||||
for i, arg in enumerate(args):
|
||||
arg_fd = StringIO()
|
||||
arg_fd.write(format_arg_with_annotation(arg))
|
||||
if defaults and i >= defaults_start:
|
||||
arg_fd.write(' = ' if arg in annotations else '=')
|
||||
arg_fd.write(object_description(defaults[i - defaults_start]))
|
||||
formatted.append(arg_fd.getvalue())
|
||||
|
||||
if varargs:
|
||||
formatted.append('*' + format_arg_with_annotation(varargs))
|
||||
|
||||
if kwonlyargs:
|
||||
formatted.append('*')
|
||||
for kwarg in kwonlyargs:
|
||||
arg_fd = StringIO()
|
||||
arg_fd.write(format_arg_with_annotation(kwarg))
|
||||
if kwonlydefaults and kwarg in kwonlydefaults:
|
||||
arg_fd.write(' = ' if kwarg in annotations else '=')
|
||||
arg_fd.write(object_description(kwonlydefaults[kwarg]))
|
||||
formatted.append(arg_fd.getvalue())
|
||||
|
||||
if varkw:
|
||||
formatted.append('**' + format_arg_with_annotation(varkw))
|
||||
|
||||
fd.write(', '.join(formatted))
|
||||
fd.write(')')
|
||||
|
||||
if 'return' in annotations:
|
||||
fd.write(' -> ')
|
||||
fd.write(format_annotation(get_annotation('return')))
|
||||
|
||||
return fd.getvalue()
|
||||
|
||||
|
||||
class Documenter(object):
|
||||
@@ -598,7 +720,7 @@ class Documenter(object):
|
||||
# __dict__ contains only the members directly defined in
|
||||
# the class (but get them via getattr anyway, to e.g. get
|
||||
# unbound method objects instead of function objects);
|
||||
# using keys() because apparently there are objects for which
|
||||
# using list(iterkeys()) because apparently there are objects for which
|
||||
# __dict__ changes while getting attributes
|
||||
try:
|
||||
obj_dict = self.get_attr(self.object, '__dict__')
|
||||
@@ -606,7 +728,7 @@ class Documenter(object):
|
||||
members = []
|
||||
else:
|
||||
members = [(mname, self.get_attr(self.object, mname, None))
|
||||
for mname in obj_dict.keys()]
|
||||
for mname in list(iterkeys(obj_dict))]
|
||||
membernames = set(m[0] for m in members)
|
||||
# add instance attributes from the analyzer
|
||||
for aname in analyzed_member_names:
|
||||
@@ -1061,7 +1183,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):
|
||||
argspec = getargspec(self.object.__init__)
|
||||
if argspec[0]:
|
||||
del argspec[0][0]
|
||||
args = formatargspec(*argspec)
|
||||
args = formatargspec(self.object, *argspec)
|
||||
# escape backslashes for reST
|
||||
args = args.replace('\\', '\\\\')
|
||||
return args
|
||||
@@ -1116,7 +1238,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):
|
||||
return None
|
||||
if argspec[0] and argspec[0][0] in ('cls', 'self'):
|
||||
del argspec[0][0]
|
||||
return formatargspec(*argspec)
|
||||
return formatargspec(initmeth, *argspec)
|
||||
|
||||
def format_signature(self):
|
||||
if self.doc_as_attr:
|
||||
@@ -1283,7 +1405,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):
|
||||
argspec = getargspec(self.object)
|
||||
if argspec[0] and argspec[0][0] in ('cls', 'self'):
|
||||
del argspec[0][0]
|
||||
args = formatargspec(*argspec)
|
||||
args = formatargspec(self.object, *argspec)
|
||||
# escape backslashes for reST
|
||||
args = args.replace('\\', '\\\\')
|
||||
return args
|
||||
|
||||
@@ -276,7 +276,7 @@ class Autosummary(Directive):
|
||||
self.warn('failed to import object %s' % real_name)
|
||||
items.append((display_name, '', '', real_name))
|
||||
continue
|
||||
if not documenter.check_module():
|
||||
if documenter.options.members and not documenter.check_module():
|
||||
continue
|
||||
|
||||
# try to also get a source code analyzer for attribute docs
|
||||
@@ -583,5 +583,5 @@ def setup(app):
|
||||
app.add_role('autolink', autolink_role)
|
||||
app.connect('doctree-read', process_autosummary_toc)
|
||||
app.connect('builder-inited', process_generate_options)
|
||||
app.add_config_value('autosummary_generate', [], True)
|
||||
app.add_config_value('autosummary_generate', [], True, [bool])
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
||||
@@ -20,6 +20,7 @@ from six.moves import cPickle as pickle
|
||||
|
||||
import sphinx
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.util.inspect import safe_getattr
|
||||
|
||||
|
||||
# utility
|
||||
@@ -187,7 +188,10 @@ class CoverageBuilder(Builder):
|
||||
for attr_name in dir(obj):
|
||||
if attr_name not in obj.__dict__:
|
||||
continue
|
||||
attr = getattr(obj, attr_name)
|
||||
try:
|
||||
attr = safe_getattr(obj, attr_name)
|
||||
except AttributeError:
|
||||
continue
|
||||
if not (inspect.ismethod(attr) or
|
||||
inspect.isfunction(attr)):
|
||||
continue
|
||||
|
||||
+46
-14
@@ -9,14 +9,16 @@
|
||||
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import codecs
|
||||
from os import path
|
||||
import doctest
|
||||
|
||||
from six import itervalues, StringIO, binary_type
|
||||
from six import itervalues, StringIO, binary_type, text_type, PY2
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
@@ -26,13 +28,30 @@ from sphinx.util import force_decode
|
||||
from sphinx.util.nodes import set_source_info
|
||||
from sphinx.util.compat import Directive
|
||||
from sphinx.util.console import bold
|
||||
|
||||
# circumvent relative import
|
||||
doctest = __import__('doctest')
|
||||
from sphinx.util.osutil import fs_encoding
|
||||
|
||||
blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
|
||||
doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
|
||||
|
||||
if PY2:
|
||||
def doctest_encode(text, encoding):
|
||||
if isinstance(text, text_type):
|
||||
text = text.encode(encoding)
|
||||
if text.startswith(codecs.BOM_UTF8):
|
||||
text = text[len(codecs.BOM_UTF8):]
|
||||
return text
|
||||
else:
|
||||
def doctest_encode(text, encoding):
|
||||
return text
|
||||
|
||||
|
||||
class _SpoofOutSphinx(doctest._SpoofOut):
|
||||
# override: convert console encoding to unicode
|
||||
if PY2:
|
||||
def getvalue(self):
|
||||
result = doctest._SpoofOut.getvalue(self)
|
||||
return result.decode('string_escape')
|
||||
|
||||
|
||||
# set up the necessary directives
|
||||
|
||||
@@ -165,6 +184,11 @@ class TestCode(object):
|
||||
|
||||
|
||||
class SphinxDocTestRunner(doctest.DocTestRunner):
|
||||
def __init__(self, *args, **kw):
|
||||
doctest.DocTestRunner.__init__(self, *args, **kw)
|
||||
# Override a fake output target for capturing doctest output.
|
||||
self._fakeout = _SpoofOutSphinx()
|
||||
|
||||
def summarize(self, out, verbose=None):
|
||||
string_io = StringIO()
|
||||
old_stdout = sys.stdout
|
||||
@@ -358,19 +382,25 @@ Doctest summary
|
||||
return compile(code, name, self.type, flags, dont_inherit)
|
||||
|
||||
def test_group(self, group, filename):
|
||||
if PY2:
|
||||
filename_str = filename.encode(fs_encoding)
|
||||
else:
|
||||
filename_str = filename
|
||||
|
||||
ns = {}
|
||||
|
||||
def run_setup_cleanup(runner, testcodes, what):
|
||||
examples = []
|
||||
for testcode in testcodes:
|
||||
examples.append(doctest.Example(testcode.code, '',
|
||||
lineno=testcode.lineno))
|
||||
examples.append(doctest.Example(
|
||||
doctest_encode(testcode.code, self.env.config.source_encoding), '',
|
||||
lineno=testcode.lineno))
|
||||
if not examples:
|
||||
return True
|
||||
# simulate a doctest with the code
|
||||
sim_doctest = doctest.DocTest(examples, {},
|
||||
'%s (%s code)' % (group.name, what),
|
||||
filename, 0, None)
|
||||
filename_str, 0, None)
|
||||
sim_doctest.globs = ns
|
||||
old_f = runner.failures
|
||||
self.type = 'exec' # the snippet may contain multiple statements
|
||||
@@ -389,8 +419,9 @@ Doctest summary
|
||||
if len(code) == 1:
|
||||
# ordinary doctests (code/output interleaved)
|
||||
try:
|
||||
test = parser.get_doctest(code[0].code, {}, group.name,
|
||||
filename, code[0].lineno)
|
||||
test = parser.get_doctest(
|
||||
doctest_encode(code[0].code, self.env.config.source_encoding), {},
|
||||
group.name, filename_str, code[0].lineno)
|
||||
except Exception:
|
||||
self.warn('ignoring invalid doctest code: %r' %
|
||||
code[0].code,
|
||||
@@ -416,12 +447,13 @@ Doctest summary
|
||||
exc_msg = m.group('msg')
|
||||
else:
|
||||
exc_msg = None
|
||||
example = doctest.Example(code[0].code, output,
|
||||
exc_msg=exc_msg,
|
||||
lineno=code[0].lineno,
|
||||
options=options)
|
||||
example = doctest.Example(
|
||||
doctest_encode(code[0].code, self.env.config.source_encoding), output,
|
||||
exc_msg=exc_msg,
|
||||
lineno=code[0].lineno,
|
||||
options=options)
|
||||
test = doctest.DocTest([example], {}, group.name,
|
||||
filename, code[0].lineno, None)
|
||||
filename_str, code[0].lineno, None)
|
||||
self.type = 'exec' # multiple statements again
|
||||
# DocTest.__init__ copies the globs namespace, which we don't want
|
||||
test.globs = ns
|
||||
|
||||
+131
-9
@@ -24,6 +24,8 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import time
|
||||
import zlib
|
||||
import codecs
|
||||
@@ -32,7 +34,7 @@ from os import path
|
||||
import re
|
||||
|
||||
from six import iteritems
|
||||
from six.moves.urllib import request
|
||||
from six.moves.urllib import parse, request
|
||||
from docutils import nodes
|
||||
from docutils.utils import relative_path
|
||||
|
||||
@@ -41,14 +43,14 @@ from sphinx.locale import _
|
||||
from sphinx.builders.html import INVENTORY_FILENAME
|
||||
|
||||
|
||||
handlers = [request.ProxyHandler(), request.HTTPRedirectHandler(),
|
||||
request.HTTPHandler()]
|
||||
default_handlers = [request.ProxyHandler(), request.HTTPRedirectHandler(),
|
||||
request.HTTPHandler()]
|
||||
try:
|
||||
handlers.append(request.HTTPSHandler)
|
||||
default_handlers.append(request.HTTPSHandler)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
request.install_opener(request.build_opener(*handlers))
|
||||
default_opener = request.build_opener(*default_handlers)
|
||||
|
||||
UTF8StreamReader = codecs.lookup('utf-8')[2]
|
||||
|
||||
@@ -122,15 +124,109 @@ def read_inventory_v2(f, uri, join, bufsize=16*1024):
|
||||
return invdata
|
||||
|
||||
|
||||
def _strip_basic_auth(url):
|
||||
"""Returns *url* with basic auth credentials removed. Also returns the
|
||||
basic auth username and password if they're present in *url*.
|
||||
|
||||
E.g.: https://user:pass@example.com => https://example.com
|
||||
|
||||
*url* need not include basic auth credentials.
|
||||
|
||||
:param url: url which may or may not contain basic auth credentials
|
||||
:type url: ``str``
|
||||
|
||||
:return: 3-``tuple`` of:
|
||||
|
||||
* (``str``) -- *url* with any basic auth creds removed
|
||||
* (``str`` or ``NoneType``) -- basic auth username or ``None`` if basic
|
||||
auth username not given
|
||||
* (``str`` or ``NoneType``) -- basic auth password or ``None`` if basic
|
||||
auth password not given
|
||||
|
||||
:rtype: ``tuple``
|
||||
"""
|
||||
url_parts = parse.urlsplit(url)
|
||||
username = url_parts.username
|
||||
password = url_parts.password
|
||||
frags = list(url_parts)
|
||||
# swap out "user[:pass]@hostname" for "hostname"
|
||||
frags[1] = url_parts.hostname
|
||||
url = parse.urlunsplit(frags)
|
||||
return (url, username, password)
|
||||
|
||||
|
||||
def _read_from_url(url):
|
||||
"""Reads data from *url* with an HTTP *GET*.
|
||||
|
||||
This function supports fetching from resources which use basic HTTP auth as
|
||||
laid out by RFC1738 § 3.1. See § 5 for grammar definitions for URLs.
|
||||
|
||||
.. seealso:
|
||||
|
||||
https://www.ietf.org/rfc/rfc1738.txt
|
||||
|
||||
:param url: URL of an HTTP resource
|
||||
:type url: ``str``
|
||||
|
||||
:return: data read from resource described by *url*
|
||||
:rtype: ``file``-like object
|
||||
"""
|
||||
url, username, password = _strip_basic_auth(url)
|
||||
if username is not None and password is not None:
|
||||
# case: url contains basic auth creds
|
||||
password_mgr = request.HTTPPasswordMgrWithDefaultRealm()
|
||||
password_mgr.add_password(None, url, username, password)
|
||||
handler = request.HTTPBasicAuthHandler(password_mgr)
|
||||
opener = request.build_opener(default_handlers + [handler])
|
||||
else:
|
||||
opener = default_opener
|
||||
|
||||
return opener.open(url)
|
||||
|
||||
|
||||
def _get_safe_url(url):
|
||||
"""Gets version of *url* with basic auth passwords obscured. This function
|
||||
returns results suitable for printing and logging.
|
||||
|
||||
E.g.: https://user:12345@example.com => https://user:********@example.com
|
||||
|
||||
.. note::
|
||||
|
||||
The number of astrisks is invariant in the length of the basic auth
|
||||
password, so minimal information is leaked.
|
||||
|
||||
:param url: a url
|
||||
:type url: ``str``
|
||||
|
||||
:return: *url* with password obscured
|
||||
:rtype: ``str``
|
||||
"""
|
||||
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)
|
||||
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)
|
||||
|
||||
return safe_url
|
||||
|
||||
|
||||
def fetch_inventory(app, uri, inv):
|
||||
"""Fetch, parse and return an intersphinx inventory file."""
|
||||
# both *uri* (base URI of the links to generate) and *inv* (actual
|
||||
# location of the inventory file) can be local or remote URIs
|
||||
localuri = uri.find('://') == -1
|
||||
localuri = '://' not in uri
|
||||
if not localuri:
|
||||
# case: inv URI points to remote resource; strip any existing auth
|
||||
uri, _, _ = _strip_basic_auth(uri)
|
||||
join = localuri and path.join or posixpath.join
|
||||
try:
|
||||
if inv.find('://') != -1:
|
||||
f = request.urlopen(inv)
|
||||
if '://' in inv:
|
||||
f = _read_from_url(inv)
|
||||
else:
|
||||
f = open(path.join(app.srcdir, inv), 'rb')
|
||||
except Exception as err:
|
||||
@@ -192,7 +288,9 @@ def load_mappings(app):
|
||||
# files; remote ones only if the cache time is expired
|
||||
if '://' not in inv or uri not in cache \
|
||||
or cache[uri][1] < cache_time:
|
||||
app.info('loading intersphinx inventory from %s...' % inv)
|
||||
safe_inv_url = _get_safe_url(inv)
|
||||
app.info(
|
||||
'loading intersphinx inventory from %s...' % safe_inv_url)
|
||||
invdata = fetch_inventory(app, uri, inv)
|
||||
if invdata:
|
||||
cache[uri] = (name, now, invdata)
|
||||
@@ -228,6 +326,10 @@ def missing_reference(app, env, node, contnode):
|
||||
objtypes = ['%s:%s' % (domain.name, objtype)
|
||||
for domain in env.domains.values()
|
||||
for objtype in domain.object_types]
|
||||
domain = None
|
||||
elif node['reftype'] == 'doc':
|
||||
domain = 'std' # special case
|
||||
objtypes = ['std:doc']
|
||||
else:
|
||||
domain = node.get('refdomain')
|
||||
if not domain:
|
||||
@@ -283,3 +385,23 @@ def setup(app):
|
||||
app.connect('missing-reference', missing_reference)
|
||||
app.connect('builder-inited', load_mappings)
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# debug functionality to print out an inventory
|
||||
import sys
|
||||
|
||||
class MockApp(object):
|
||||
srcdir = ''
|
||||
|
||||
def warn(self, msg):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
filename = sys.argv[1]
|
||||
invdata = fetch_inventory(MockApp(), '', filename)
|
||||
for key in sorted(invdata or {}):
|
||||
print(key)
|
||||
for entry, einfo in sorted(invdata[key].items()):
|
||||
print('\t%-40s %s%s' % (entry,
|
||||
einfo[3] != '-' and '%-40s: ' % einfo[3] or '',
|
||||
einfo[2]))
|
||||
|
||||
@@ -192,7 +192,7 @@ def number_equations(app, doctree, docname):
|
||||
|
||||
|
||||
def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
|
||||
app.add_node(math,
|
||||
app.add_node(math, override=True,
|
||||
latex=(latex_visit_math, None),
|
||||
text=(text_visit_math, None),
|
||||
man=(man_visit_math, None),
|
||||
|
||||
@@ -34,7 +34,7 @@ class Config(object):
|
||||
napoleon_google_docstring = True
|
||||
napoleon_numpy_docstring = True
|
||||
napoleon_include_private_with_doc = False
|
||||
napoleon_include_special_with_doc = True
|
||||
napoleon_include_special_with_doc = False
|
||||
napoleon_use_admonition_for_examples = False
|
||||
napoleon_use_admonition_for_notes = False
|
||||
napoleon_use_admonition_for_references = False
|
||||
@@ -43,7 +43,7 @@ class Config(object):
|
||||
napoleon_use_rtype = True
|
||||
|
||||
.. _Google style:
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
http://google.github.io/styleguide/pyguide.html
|
||||
.. _NumPy style:
|
||||
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
|
||||
@@ -71,7 +71,7 @@ class Config(object):
|
||||
# This will NOT be included in the docs
|
||||
pass
|
||||
|
||||
napoleon_include_special_with_doc : bool, defaults to True
|
||||
napoleon_include_special_with_doc : 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.
|
||||
@@ -209,7 +209,7 @@ class Config(object):
|
||||
'napoleon_google_docstring': (True, 'env'),
|
||||
'napoleon_numpy_docstring': (True, 'env'),
|
||||
'napoleon_include_private_with_doc': (False, 'env'),
|
||||
'napoleon_include_special_with_doc': (True, 'env'),
|
||||
'napoleon_include_special_with_doc': (False, 'env'),
|
||||
'napoleon_use_admonition_for_examples': (False, 'env'),
|
||||
'napoleon_use_admonition_for_notes': (False, 'env'),
|
||||
'napoleon_use_admonition_for_references': (False, 'env'),
|
||||
@@ -239,12 +239,12 @@ def setup(app):
|
||||
|
||||
See Also
|
||||
--------
|
||||
The Sphinx documentation on `Extensions`_, the `Extension Tutorial`_, and
|
||||
the `Extension API`_.
|
||||
`The Sphinx documentation on Extensions
|
||||
<http://sphinx-doc.org/extensions.html>`_
|
||||
|
||||
.. _Extensions: http://sphinx-doc.org/extensions.html
|
||||
.. _Extension Tutorial: http://sphinx-doc.org/ext/tutorial.html
|
||||
.. _Extension API: http://sphinx-doc.org/ext/appapi.html
|
||||
`The Extension Tutorial <http://sphinx-doc.org/extdev/tutorial.html>`_
|
||||
|
||||
`The Extension API <http://sphinx-doc.org/extdev/appapi.html>`_
|
||||
|
||||
"""
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
@@ -15,7 +15,7 @@ import collections
|
||||
import inspect
|
||||
import re
|
||||
|
||||
from six import string_types
|
||||
from six import string_types, u
|
||||
from six.moves import range
|
||||
|
||||
from sphinx.ext.napoleon.iterators import modify_iter
|
||||
@@ -23,7 +23,9 @@ from sphinx.util.pycompat import UnicodeMixin
|
||||
|
||||
|
||||
_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*$')
|
||||
_xref_regex = re.compile(r'(:\w+:\S+:`.+?`|:\S+:`.+?`|`.+?`)')
|
||||
|
||||
|
||||
@@ -142,6 +144,7 @@ class GoogleDocstring(UnicodeMixin):
|
||||
'raises': self._parse_raises_section,
|
||||
'references': self._parse_references_section,
|
||||
'see also': self._parse_see_also_section,
|
||||
'todo': self._parse_todo_section,
|
||||
'warning': self._parse_warning_section,
|
||||
'warnings': self._parse_warning_section,
|
||||
'warns': self._parse_warns_section,
|
||||
@@ -159,7 +162,7 @@ class GoogleDocstring(UnicodeMixin):
|
||||
Unicode version of the docstring.
|
||||
|
||||
"""
|
||||
return u'\n'.join(self.lines())
|
||||
return u('\n').join(self.lines())
|
||||
|
||||
def lines(self):
|
||||
"""Return the parsed lines of the docstring in reStructuredText format.
|
||||
@@ -209,10 +212,7 @@ class GoogleDocstring(UnicodeMixin):
|
||||
_name = match.group(1)
|
||||
_type = match.group(2)
|
||||
|
||||
if _name[:2] == '**':
|
||||
_name = r'\*\*'+_name[2:]
|
||||
elif _name[:1] == '*':
|
||||
_name = r'\*'+_name[1:]
|
||||
_name = self._escape_args_and_kwargs(_name)
|
||||
|
||||
if prefer_type and not _type:
|
||||
_type, _name = _name, _type
|
||||
@@ -294,6 +294,14 @@ class GoogleDocstring(UnicodeMixin):
|
||||
min_indent = self._get_min_indent(lines)
|
||||
return [line[min_indent:] for line in lines]
|
||||
|
||||
def _escape_args_and_kwargs(self, name):
|
||||
if name[:2] == '**':
|
||||
return r'\*\*' + name[2:]
|
||||
elif name[:1] == '*':
|
||||
return r'\*' + name[1:]
|
||||
else:
|
||||
return name
|
||||
|
||||
def _format_admonition(self, admonition, lines):
|
||||
lines = self._strip_empty(lines)
|
||||
if len(lines) == 1:
|
||||
@@ -402,7 +410,8 @@ class GoogleDocstring(UnicodeMixin):
|
||||
|
||||
def _is_section_header(self):
|
||||
section = self._line_iter.peek().lower()
|
||||
if section.strip(':') in self._sections:
|
||||
match = _google_section_regex.match(section)
|
||||
if match and section.strip(':') in self._sections:
|
||||
header_indent = self._get_indent(section)
|
||||
section_indent = self._get_current_indent(peek_ahead=1)
|
||||
return section_indent > header_indent
|
||||
@@ -603,6 +612,10 @@ class GoogleDocstring(UnicodeMixin):
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition('seealso', lines)
|
||||
|
||||
def _parse_todo_section(self, section):
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition('todo', lines)
|
||||
|
||||
def _parse_warning_section(self, section):
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition('warning', lines)
|
||||
@@ -763,6 +776,7 @@ class NumpyDocstring(GoogleDocstring):
|
||||
else:
|
||||
_name, _type = line, ''
|
||||
_name, _type = _name.strip(), _type.strip()
|
||||
_name = self._escape_args_and_kwargs(_name)
|
||||
if prefer_type and not _type:
|
||||
_type, _name = _name, _type
|
||||
indent = self._get_indent(line)
|
||||
@@ -793,8 +807,7 @@ class NumpyDocstring(GoogleDocstring):
|
||||
section, underline = self._line_iter.peek(2)
|
||||
section = section.lower()
|
||||
if section in self._sections and isinstance(underline, string_types):
|
||||
pattern = r'[=\-`:\'"~^_*+#<>]{' + str(len(section)) + r'}$'
|
||||
return bool(re.match(pattern, underline))
|
||||
return bool(_numpy_section_regex.match(underline))
|
||||
elif self._directive_sections:
|
||||
if _directive_regex.match(section):
|
||||
for directive_section in self._directive_sections:
|
||||
|
||||
+8
-1
@@ -13,6 +13,7 @@
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
import sphinx
|
||||
from sphinx.locale import _
|
||||
@@ -38,13 +39,18 @@ class Todo(Directive):
|
||||
required_arguments = 0
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
option_spec = {}
|
||||
option_spec = {
|
||||
'class': directives.class_option,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
targetid = 'index-%s' % env.new_serialno('index')
|
||||
targetnode = nodes.target('', '', ids=[targetid])
|
||||
|
||||
if not self.options.get('class'):
|
||||
self.options['class'] = ['admonition-todo']
|
||||
|
||||
ad = make_admonition(todo_node, self.name, [_('Todo')], self.options,
|
||||
self.content, self.lineno, self.content_offset,
|
||||
self.block_text, self.state, self.state_machine)
|
||||
@@ -165,6 +171,7 @@ def merge_info(app, env, docnames, other):
|
||||
|
||||
def visit_todo_node(self, node):
|
||||
self.visit_admonition(node)
|
||||
# self.visit_admonition(node, 'todo')
|
||||
|
||||
|
||||
def depart_todo_node(self, node):
|
||||
|
||||
@@ -195,7 +195,7 @@ else:
|
||||
return translators['sphinx'].ugettext(message)
|
||||
|
||||
|
||||
def init(locale_dirs, language, catalog='sphinx'):
|
||||
def init(locale_dirs, language, catalog='sphinx', charset='utf-8'):
|
||||
"""Look for message catalogs in `locale_dirs` and *ensure* that there is at
|
||||
least a NullTranslations catalog set in `translators`. If called multiple
|
||||
times or if several ``.mo`` files are found, their contents are merged
|
||||
@@ -212,7 +212,8 @@ def init(locale_dirs, language, catalog='sphinx'):
|
||||
# compile mo files if po file is updated
|
||||
# TODO: remove circular importing
|
||||
from sphinx.util.i18n import find_catalog_source_files
|
||||
for catinfo in find_catalog_source_files(locale_dirs, language, domains=[catalog]):
|
||||
for catinfo in find_catalog_source_files(locale_dirs, language, domains=[catalog],
|
||||
charset=charset):
|
||||
catinfo.write_mo(language)
|
||||
|
||||
# loading
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Grammar for Python 3.x (with at least x <= 4)
|
||||
# Grammar for Python 3.x (with at least x <= 5)
|
||||
|
||||
|
||||
# IMPORTANT: when copying over a new Grammar file, make sure file_input
|
||||
# is the first nonterminal in the file!
|
||||
@@ -14,8 +15,11 @@ eval_input: testlist NEWLINE* ENDMARKER
|
||||
|
||||
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
|
||||
decorators: decorator+
|
||||
decorated: decorators (classdef | funcdef)
|
||||
decorated: decorators (classdef | funcdef | async_funcdef)
|
||||
|
||||
async_funcdef: ASYNC funcdef
|
||||
funcdef: 'def' NAME parameters ['->' test] ':' suite
|
||||
|
||||
parameters: '(' [typedargslist] ')'
|
||||
typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [','
|
||||
['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]]
|
||||
@@ -33,7 +37,7 @@ small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
|
||||
expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
|
||||
('=' (yield_expr|testlist_star_expr))*)
|
||||
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
|
||||
augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
|
||||
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
|
||||
'<<=' | '>>=' | '**=' | '//=')
|
||||
# For normal assignments, additional restrictions enforced by the interpreter
|
||||
del_stmt: 'del' exprlist
|
||||
@@ -58,7 +62,8 @@ global_stmt: 'global' NAME (',' NAME)*
|
||||
nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
|
||||
assert_stmt: 'assert' test [',' test]
|
||||
|
||||
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
|
||||
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
|
||||
async_stmt: ASYNC (funcdef | with_stmt | for_stmt)
|
||||
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
|
||||
while_stmt: 'while' test ':' suite ['else' ':' suite]
|
||||
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
||||
@@ -82,7 +87,7 @@ and_test: not_test ('and' not_test)*
|
||||
not_test: 'not' not_test | comparison
|
||||
comparison: expr (comp_op expr)*
|
||||
# <> isn't actually a valid comparison operator in Python. It's here for the
|
||||
# sake of a __future__ import described in PEP 401
|
||||
# sake of a __future__ import described in PEP 401 (which really works :-)
|
||||
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
|
||||
star_expr: '*' expr
|
||||
expr: xor_expr ('|' xor_expr)*
|
||||
@@ -90,9 +95,9 @@ xor_expr: and_expr ('^' and_expr)*
|
||||
and_expr: shift_expr ('&' shift_expr)*
|
||||
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
|
||||
arith_expr: term (('+'|'-') term)*
|
||||
term: factor (('*'|'/'|'%'|'//') factor)*
|
||||
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
|
||||
factor: ('+'|'-'|'~') factor | power
|
||||
power: atom trailer* ['**' factor]
|
||||
power: [AWAIT] atom trailer* ['**' factor]
|
||||
atom: ('(' [yield_expr|testlist_comp] ')' |
|
||||
'[' [testlist_comp] ']' |
|
||||
'{' [dictorsetmaker] '}' |
|
||||
@@ -104,17 +109,29 @@ subscript: test | [test] ':' [test] [sliceop]
|
||||
sliceop: ':' [test]
|
||||
exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
|
||||
testlist: test (',' test)* [',']
|
||||
dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
|
||||
(test (comp_for | (',' test)* [','])) )
|
||||
dictorsetmaker: ( ((test ':' test | '**' expr)
|
||||
(comp_for | (',' (test ':' test | '**' expr))* [','])) |
|
||||
((test | star_expr)
|
||||
(comp_for | (',' (test | star_expr))* [','])) )
|
||||
|
||||
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
||||
|
||||
arglist: (argument ',')* (argument [',']
|
||||
|'*' test (',' argument)* [',' '**' test]
|
||||
|'**' test)
|
||||
arglist: argument (',' argument)* [',']
|
||||
|
||||
# The reason that keywords are test nodes instead of NAME is that using NAME
|
||||
# results in an ambiguity. ast.c makes sure it's a NAME.
|
||||
argument: test [comp_for] | test '=' test # Really [keyword '='] test
|
||||
# "test '=' test" is really "keyword '=' test", but we have no such token.
|
||||
# These need to be in a single rule to avoid grammar that is ambiguous
|
||||
# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
|
||||
# we explicitly match '*' here, too, to give it proper precedence.
|
||||
# Illegal combinations and orderings are blocked in ast.c:
|
||||
# multiple (test comp_for) arguements are blocked; keyword unpackings
|
||||
# that precede iterable unpackings are blocked; etc.
|
||||
argument: ( test [comp_for] |
|
||||
test '=' test |
|
||||
'**' test |
|
||||
'*' test )
|
||||
|
||||
comp_iter: comp_for | comp_if
|
||||
comp_for: 'for' exprlist 'in' or_test [comp_iter]
|
||||
comp_if: 'if' test_nocond [comp_iter]
|
||||
|
||||
@@ -139,6 +139,7 @@ opmap_raw = """
|
||||
{ LBRACE
|
||||
} RBRACE
|
||||
@ AT
|
||||
@= ATEQUAL
|
||||
== EQEQUAL
|
||||
!= NOTEQUAL
|
||||
<> NOTEQUAL
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
from __future__ import print_function
|
||||
|
||||
from six import iteritems
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError: # Fallback for Python 2.6
|
||||
OrderedDict = dict
|
||||
|
||||
# Pgen imports
|
||||
|
||||
@@ -57,7 +61,7 @@ class ParserGenerator(object):
|
||||
def make_first(self, c, name):
|
||||
rawfirst = self.first[name]
|
||||
first = {}
|
||||
for label in rawfirst:
|
||||
for label in sorted(rawfirst):
|
||||
ilabel = self.make_label(c, label)
|
||||
##assert ilabel not in first # X X X failed on <> ... !=
|
||||
first[ilabel] = 1
|
||||
@@ -138,8 +142,8 @@ class ParserGenerator(object):
|
||||
totalset[label] = 1
|
||||
overlapcheck[label] = {label: 1}
|
||||
inverse = {}
|
||||
for label, itsfirst in iteritems(overlapcheck):
|
||||
for symbol in itsfirst:
|
||||
for label, itsfirst in sorted(overlapcheck.items()):
|
||||
for symbol in sorted(itsfirst):
|
||||
if symbol in inverse:
|
||||
raise ValueError("rule %s is ambiguous; %s is in the"
|
||||
" first sets of %s as well as %s" %
|
||||
@@ -349,6 +353,9 @@ class NFAState(object):
|
||||
assert isinstance(next, NFAState)
|
||||
self.arcs.append((label, next))
|
||||
|
||||
def __hash__(self):
|
||||
return hash(tuple(x[0] for x in self.arcs))
|
||||
|
||||
class DFAState(object):
|
||||
|
||||
def __init__(self, nfaset, final):
|
||||
@@ -357,7 +364,10 @@ class DFAState(object):
|
||||
assert isinstance(final, NFAState)
|
||||
self.nfaset = nfaset
|
||||
self.isfinal = final in nfaset
|
||||
self.arcs = {} # map from label to DFAState
|
||||
self.arcs = OrderedDict() # map from label to DFAState
|
||||
|
||||
def __hash__(self):
|
||||
return hash(tuple(self.arcs))
|
||||
|
||||
def addarc(self, next, label):
|
||||
assert isinstance(label, str)
|
||||
|
||||
@@ -57,13 +57,16 @@ DOUBLESTAREQUAL = 47
|
||||
DOUBLESLASH = 48
|
||||
DOUBLESLASHEQUAL = 49
|
||||
AT = 50
|
||||
OP = 51
|
||||
COMMENT = 52
|
||||
NL = 53
|
||||
RARROW = 54
|
||||
ERRORTOKEN = 55
|
||||
ELLIPSIS = 56
|
||||
N_TOKENS = 57
|
||||
ATEQUAL = 51
|
||||
RARROW = 52
|
||||
ELLIPSIS = 53
|
||||
OP = 54
|
||||
AWAIT = 55
|
||||
ASYNC = 56
|
||||
COMMENT = 57
|
||||
NL = 58
|
||||
ERRORTOKEN = 59
|
||||
N_TOKENS = 60
|
||||
NT_OFFSET = 256
|
||||
#--end constants--
|
||||
|
||||
|
||||
@@ -360,6 +360,16 @@ def generate_tokens(readline):
|
||||
spos, epos, pos = (lnum, start), (lnum, end), end
|
||||
token, initial = line[start:end], line[start]
|
||||
|
||||
if end < max:
|
||||
next_pseudomatch = pseudoprog.match(line, end)
|
||||
if next_pseudomatch:
|
||||
n_start, n_end = next_pseudomatch.span(1)
|
||||
n_token = line[n_start:n_end]
|
||||
else:
|
||||
n_token = None
|
||||
else:
|
||||
n_token = None
|
||||
|
||||
if initial in numchars or (
|
||||
initial == '.' and token not in ('.', '...')
|
||||
): # ordinary number
|
||||
@@ -396,6 +406,10 @@ def generate_tokens(readline):
|
||||
break
|
||||
else: # ordinary string
|
||||
yield (STRING, token, spos, epos, line)
|
||||
elif token == 'await' and n_token:
|
||||
yield (AWAIT, token, spos, epos, line)
|
||||
elif token == 'async' and n_token in ('def', 'for', 'with'):
|
||||
yield (ASYNC, token, spos, epos, line)
|
||||
elif initial in namechars: # ordinary name
|
||||
yield (NAME, token, spos, epos, line)
|
||||
elif token in ('...',): # ordinary name
|
||||
|
||||
+10
-10
@@ -87,7 +87,6 @@ QUICKSTART_CONF += u'''\
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
@@ -128,9 +127,9 @@ author = u'%(author_str)s'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '%(version_str)s'
|
||||
version = u'%(version_str)s'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '%(release_str)s'
|
||||
release = u'%(release_str)s'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -217,9 +216,10 @@ html_static_path = ['%(dot)sstatic']
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%%b %%d, %%Y'
|
||||
# If not None, a 'Last updated on:' timestamp is inserted at every page
|
||||
# bottom, using the given strftime format.
|
||||
# The empty string is equivalent to '%%b %%d, %%Y'.
|
||||
#html_last_updated_fmt = None
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
@@ -472,12 +472,12 @@ BUILDDIR = %(rbuilddir)s
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error \
|
||||
\t$(error \
|
||||
The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx \
|
||||
installed, then set the SPHINXBUILD environment variable to point \
|
||||
to the full path of the '$(SPHINXBUILD)' executable. Alternatively you \
|
||||
can add the directory with the executable to your PATH. \
|
||||
If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
If you don\\'t have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
@@ -716,7 +716,7 @@ if "%%1" == "clean" (
|
||||
|
||||
|
||||
REM Check if sphinx-build is available and fallback to Python version if any
|
||||
%%SPHINXBUILD%% 2> nul
|
||||
%%SPHINXBUILD%% 1>NUL 2>NUL
|
||||
if errorlevel 9009 goto sphinx_python
|
||||
goto sphinx_ok
|
||||
|
||||
@@ -979,7 +979,7 @@ set SPHINXPROJ=%(project_fn)s
|
||||
|
||||
if "%%1" == "" goto help
|
||||
|
||||
%%SPHINXBUILD%% 2> nul
|
||||
%%SPHINXBUILD%% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
\techo.
|
||||
\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
|
||||
+1
-1
@@ -329,7 +329,7 @@ specific_docroles = {
|
||||
# links to download references
|
||||
'download': XRefRole(nodeclass=addnodes.download_reference),
|
||||
# links to documents
|
||||
'doc': XRefRole(warn_dangling=True),
|
||||
'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
|
||||
# links to anything
|
||||
'any': AnyXRefRole(warn_dangling=True),
|
||||
|
||||
|
||||
@@ -275,9 +275,9 @@ class IndexBuilder(object):
|
||||
rv = {}
|
||||
otypes = self._objtypes
|
||||
onames = self._objnames
|
||||
for domainname, domain in iteritems(self.env.domains):
|
||||
for domainname, domain in sorted(iteritems(self.env.domains)):
|
||||
for fullname, dispname, type, docname, anchor, prio in \
|
||||
domain.get_objects():
|
||||
sorted(domain.get_objects()):
|
||||
# XXX use dispname?
|
||||
if docname not in fn2index:
|
||||
continue
|
||||
|
||||
+6
-3
@@ -21,7 +21,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from six import iteritems
|
||||
from six import iteritems, PY3
|
||||
|
||||
try:
|
||||
import MeCab
|
||||
@@ -43,13 +43,16 @@ class MecabBinder(object):
|
||||
self.dict_encode = options.get('dic_enc', 'utf-8')
|
||||
|
||||
def split(self, input):
|
||||
input2 = input.encode(self.dict_encode)
|
||||
input2 = input if PY3 else input.encode(self.dict_encode)
|
||||
if native_module:
|
||||
result = self.native.parse(input2)
|
||||
else:
|
||||
result = self.ctypes_libmecab.mecab_sparse_tostr(
|
||||
self.ctypes_mecab, input.encode(self.dict_encode))
|
||||
return result.decode(self.dict_encode).split(' ')
|
||||
if PY3:
|
||||
return result.split(' ')
|
||||
else:
|
||||
return result.decode(self.dict_encode).split(' ')
|
||||
|
||||
def init_native(self, options):
|
||||
param = '-Owakati'
|
||||
|
||||
+105
-98
@@ -3,7 +3,7 @@
|
||||
sphinx.search.nl
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Danish search language: includes the JS porter stemmer.
|
||||
Dutch search language: includes the JS porter stemmer.
|
||||
|
||||
:copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
@@ -13,102 +13,109 @@ from sphinx.search import SearchLanguage, parse_stop_word
|
||||
|
||||
import snowballstemmer
|
||||
|
||||
danish_stopwords = parse_stop_word(u'''
|
||||
| source: http://snowball.tartarus.org/algorithms/danish/stop.txt
|
||||
og | and
|
||||
i | in
|
||||
jeg | I
|
||||
det | that (dem. pronoun)/it (pers. pronoun)
|
||||
at | that (in front of a sentence)/to (with infinitive)
|
||||
en | a/an
|
||||
den | it (pers. pronoun)/that (dem. pronoun)
|
||||
til | to/at/for/until/against/by/of/into, more
|
||||
er | present tense of "to be"
|
||||
som | who, as
|
||||
på | on/upon/in/on/at/to/after/of/with/for, on
|
||||
de | they
|
||||
med | with/by/in, along
|
||||
han | he
|
||||
af | of/by/from/off/for/in/with/on, off
|
||||
for | at/for/to/from/by/of/ago, in front/before, because
|
||||
ikke | not
|
||||
der | who/which, there/those
|
||||
var | past tense of "to be"
|
||||
mig | me/myself
|
||||
sig | oneself/himself/herself/itself/themselves
|
||||
men | but
|
||||
et | a/an/one, one (number), someone/somebody/one
|
||||
har | present tense of "to have"
|
||||
om | round/about/for/in/a, about/around/down, if
|
||||
vi | we
|
||||
min | my
|
||||
havde | past tense of "to have"
|
||||
ham | him
|
||||
hun | she
|
||||
nu | now
|
||||
over | over/above/across/by/beyond/past/on/about, over/past
|
||||
da | then, when/as/since
|
||||
fra | from/off/since, off, since
|
||||
du | you
|
||||
ud | out
|
||||
sin | his/her/its/one's
|
||||
dem | them
|
||||
os | us/ourselves
|
||||
op | up
|
||||
man | you/one
|
||||
hans | his
|
||||
hvor | where
|
||||
eller | or
|
||||
hvad | what
|
||||
skal | must/shall etc.
|
||||
selv | myself/youself/herself/ourselves etc., even
|
||||
her | here
|
||||
alle | all/everyone/everybody etc.
|
||||
vil | will (verb)
|
||||
blev | past tense of "to stay/to remain/to get/to become"
|
||||
kunne | could
|
||||
ind | in
|
||||
når | when
|
||||
være | present tense of "to be"
|
||||
dog | however/yet/after all
|
||||
noget | something
|
||||
ville | would
|
||||
jo | you know/you see (adv), yes
|
||||
deres | their/theirs
|
||||
efter | after/behind/according to/for/by/from, later/afterwards
|
||||
ned | down
|
||||
skulle | should
|
||||
denne | this
|
||||
end | than
|
||||
dette | this
|
||||
mit | my/mine
|
||||
også | also
|
||||
under | under/beneath/below/during, below/underneath
|
||||
have | have
|
||||
dig | you
|
||||
anden | other
|
||||
hende | her
|
||||
mine | my
|
||||
alt | everything
|
||||
meget | much/very, plenty of
|
||||
sit | his, her, its, one's
|
||||
sine | his, her, its, one's
|
||||
vor | our
|
||||
mod | against
|
||||
disse | these
|
||||
hvis | if
|
||||
din | your/yours
|
||||
nogle | some
|
||||
hos | by/at
|
||||
blive | be/become
|
||||
mange | many
|
||||
ad | by/through
|
||||
bliver | present tense of "to be/to become"
|
||||
hendes | her/hers
|
||||
været | be
|
||||
thi | for (conj)
|
||||
jer | you
|
||||
sådan | such, like this/like that
|
||||
dutch_stopwords = parse_stop_word(u'''
|
||||
| source: http://snowball.tartarus.org/algorithms/dutch/stop.txt
|
||||
de | the
|
||||
en | and
|
||||
van | of, from
|
||||
ik | I, the ego
|
||||
te | (1) chez, at etc, (2) to, (3) too
|
||||
dat | that, which
|
||||
die | that, those, who, which
|
||||
in | in, inside
|
||||
een | a, an, one
|
||||
hij | he
|
||||
het | the, it
|
||||
niet | not, nothing, naught
|
||||
zijn | (1) to be, being, (2) his, one's, its
|
||||
is | is
|
||||
was | (1) was, past tense of all persons sing. of 'zijn' (to be) (2) wax, (3) the washing, (4) rise of river
|
||||
op | on, upon, at, in, up, used up
|
||||
aan | on, upon, to (as dative)
|
||||
met | with, by
|
||||
als | like, such as, when
|
||||
voor | (1) before, in front of, (2) furrow
|
||||
had | had, past tense all persons sing. of 'hebben' (have)
|
||||
er | there
|
||||
maar | but, only
|
||||
om | round, about, for etc
|
||||
hem | him
|
||||
dan | then
|
||||
zou | should/would, past tense all persons sing. of 'zullen'
|
||||
of | or, whether, if
|
||||
wat | what, something, anything
|
||||
mijn | possessive and noun 'mine'
|
||||
men | people, 'one'
|
||||
dit | this
|
||||
zo | so, thus, in this way
|
||||
door | through by
|
||||
over | over, across
|
||||
ze | she, her, they, them
|
||||
zich | oneself
|
||||
bij | (1) a bee, (2) by, near, at
|
||||
ook | also, too
|
||||
tot | till, until
|
||||
je | you
|
||||
mij | me
|
||||
uit | out of, from
|
||||
der | Old Dutch form of 'van der' still found in surnames
|
||||
daar | (1) there, (2) because
|
||||
haar | (1) her, their, them, (2) hair
|
||||
naar | (1) unpleasant, unwell etc, (2) towards, (3) as
|
||||
heb | present first person sing. of 'to have'
|
||||
hoe | how, why
|
||||
heeft | present third person sing. of 'to have'
|
||||
hebben | 'to have' and various parts thereof
|
||||
deze | this
|
||||
u | you
|
||||
want | (1) for, (2) mitten, (3) rigging
|
||||
nog | yet, still
|
||||
zal | 'shall', first and third person sing. of verb 'zullen' (will)
|
||||
me | me
|
||||
zij | she, they
|
||||
nu | now
|
||||
ge | 'thou', still used in Belgium and south Netherlands
|
||||
geen | none
|
||||
omdat | because
|
||||
iets | something, somewhat
|
||||
worden | to become, grow, get
|
||||
toch | yet, still
|
||||
al | all, every, each
|
||||
waren | (1) 'were' (2) to wander, (3) wares, (3)
|
||||
veel | much, many
|
||||
meer | (1) more, (2) lake
|
||||
doen | to do, to make
|
||||
toen | then, when
|
||||
moet | noun 'spot/mote' and present form of 'to must'
|
||||
ben | (1) am, (2) 'are' in interrogative second person singular of 'to be'
|
||||
zonder | without
|
||||
kan | noun 'can' and present form of 'to be able'
|
||||
hun | their, them
|
||||
dus | so, consequently
|
||||
alles | all, everything, anything
|
||||
onder | under, beneath
|
||||
ja | yes, of course
|
||||
eens | once, one day
|
||||
hier | here
|
||||
wie | who
|
||||
werd | imperfect third person sing. of 'become'
|
||||
altijd | always
|
||||
doch | yet, but etc
|
||||
wordt | present third person sing. of 'become'
|
||||
wezen | (1) to be, (2) 'been' as in 'been fishing', (3) orphans
|
||||
kunnen | to be able
|
||||
ons | us/our
|
||||
zelf | self
|
||||
tegen | against, towards, at
|
||||
na | after, near
|
||||
reeds | already
|
||||
wil | (1) present tense of 'want', (2) 'will', noun, (3) fender
|
||||
kon | could; past tense of 'to be able'
|
||||
niets | nothing
|
||||
uw | your
|
||||
iemand | somebody
|
||||
geweest | been; past participle of 'be'
|
||||
andere | other
|
||||
''')
|
||||
|
||||
js_stemmer = u"""
|
||||
@@ -122,7 +129,7 @@ class SearchDutch(SearchLanguage):
|
||||
language_name = 'Dutch'
|
||||
js_stemmer_rawcode = 'dutch-stemmer.js'
|
||||
js_stemmer_code = js_stemmer
|
||||
stopwords = danish_stopwords
|
||||
stopwords = dutch_stopwords
|
||||
|
||||
def init(self, options):
|
||||
self.stemmer = snowballstemmer.stemmer('dutch')
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
\RequirePackage{titlesec}
|
||||
\RequirePackage{tabulary}
|
||||
\RequirePackage{amsmath} % for \text
|
||||
\RequirePackage{amssymb} % for some symbols
|
||||
\RequirePackage{makeidx}
|
||||
\RequirePackage{framed}
|
||||
\RequirePackage{ifthen}
|
||||
|
||||
@@ -52,6 +52,8 @@ div.sphinxsidebar {
|
||||
width: {{ theme_sidebarwidth|toint }}px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap : break-word;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* searchtools.js_t
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilties for the full-text search.
|
||||
* Sphinx JavaScript utilities for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
@@ -195,8 +195,7 @@ var Search = {
|
||||
}
|
||||
|
||||
// lookup as search terms in fulltext
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
|
||||
.concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
|
||||
|
||||
// let the scorer override scores with a custom scoring function
|
||||
if (Scorer.score) {
|
||||
@@ -360,23 +359,47 @@ var Search = {
|
||||
/**
|
||||
* search for full-text terms in the index
|
||||
*/
|
||||
performTermsSearch : function(searchterms, excluded, terms, score) {
|
||||
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var i, j, file, files;
|
||||
var i, j, file;
|
||||
var fileMap = {};
|
||||
var scoreMap = {};
|
||||
var results = [];
|
||||
|
||||
// perform the search on the required terms
|
||||
for (i = 0; i < searchterms.length; i++) {
|
||||
var word = searchterms[i];
|
||||
var files = [];
|
||||
var _o = [
|
||||
{files: terms[word], score: Scorer.term},
|
||||
{files: titleterms[word], score: Scorer.title}
|
||||
];
|
||||
|
||||
// no match but word was a required one
|
||||
if ((files = terms[word]) === undefined)
|
||||
if ($u.every(_o, function(o){return o.files === undefined;})) {
|
||||
break;
|
||||
if (files.length === undefined) {
|
||||
files = [files];
|
||||
}
|
||||
// found search word in contents
|
||||
$u.each(_o, function(o) {
|
||||
var _files = o.files;
|
||||
if (_files === undefined)
|
||||
return
|
||||
|
||||
if (_files.length === undefined)
|
||||
_files = [_files];
|
||||
files = files.concat(_files);
|
||||
|
||||
// set score for the word in each file to Scorer.term
|
||||
for (j = 0; j < _files.length; j++) {
|
||||
file = _files[j];
|
||||
if (!(file in scoreMap))
|
||||
scoreMap[file] = {}
|
||||
scoreMap[file][word] = o.score;
|
||||
}
|
||||
});
|
||||
|
||||
// create the mapping
|
||||
for (j = 0; j < files.length; j++) {
|
||||
file = files[j];
|
||||
@@ -398,7 +421,9 @@ var Search = {
|
||||
// ensure that none of the excluded terms is in the search result
|
||||
for (i = 0; i < excluded.length; i++) {
|
||||
if (terms[excluded[i]] == file ||
|
||||
$u.contains(terms[excluded[i]] || [], file)) {
|
||||
titleterms[excluded[i]] == file ||
|
||||
$u.contains(terms[excluded[i]] || [], file) ||
|
||||
$u.contains(titleterms[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
@@ -406,6 +431,9 @@ var Search = {
|
||||
|
||||
// if we have still a valid result we can add it to the result list
|
||||
if (valid) {
|
||||
// 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]);
|
||||
}
|
||||
}
|
||||
@@ -416,7 +444,7 @@ var Search = {
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurance, the
|
||||
* words. the first one is used to find the occurrence, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
makeSearchSummary : function(text, keywords, hlwords) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* websupport.js
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* sphinx.websupport utilties for all documentation.
|
||||
* sphinx.websupport utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
|
||||
+2
-3
@@ -136,9 +136,8 @@ class Theme(object):
|
||||
except configparser.NoOptionError:
|
||||
raise ThemeError('theme %r doesn\'t have "inherit" setting' % name)
|
||||
|
||||
if inherit in ['alabaster', 'sphinx_rtd_theme']:
|
||||
# include 'alabaster' or 'sphinx_themes' automatically #1794
|
||||
self.load_extra_theme(inherit)
|
||||
# load inherited theme automatically #1794, #1884, #1885
|
||||
self.load_extra_theme(inherit)
|
||||
|
||||
if inherit == 'none':
|
||||
self.base = None
|
||||
|
||||
+19
-3
@@ -22,6 +22,7 @@ from sphinx.locale import _, init as init_locale
|
||||
from sphinx.util import split_index_msg
|
||||
from sphinx.util.nodes import (
|
||||
traverse_translatable_index, extract_messages, LITERAL_TYPE_NODES, IMAGE_TYPE_NODES,
|
||||
apply_source_workaround,
|
||||
)
|
||||
from sphinx.util.osutil import ustrftime
|
||||
from sphinx.util.i18n import find_catalog
|
||||
@@ -169,6 +170,18 @@ TRANSLATABLE_NODES = {
|
||||
}
|
||||
|
||||
|
||||
class ApplySourceWorkaround(Transform):
|
||||
"""
|
||||
update source and rawsource attributes
|
||||
"""
|
||||
default_priority = 10
|
||||
|
||||
def apply(self):
|
||||
for n in self.document.traverse():
|
||||
if isinstance(n, nodes.TextElement):
|
||||
apply_source_workaround(n)
|
||||
|
||||
|
||||
class ExtraTranslatableNodes(Transform):
|
||||
"""
|
||||
make nodes translatable
|
||||
@@ -225,7 +238,8 @@ class Locale(Transform):
|
||||
dirs = [path.join(env.srcdir, directory)
|
||||
for directory in env.config.locale_dirs]
|
||||
catalog, has_catalog = init_locale(dirs, env.config.language,
|
||||
textdomain)
|
||||
textdomain,
|
||||
charset=env.config.source_encoding)
|
||||
if not has_catalog:
|
||||
return
|
||||
|
||||
@@ -276,8 +290,10 @@ class Locale(Transform):
|
||||
# document nameids mapping with new name.
|
||||
names = section_node.setdefault('names', [])
|
||||
names.append(new_name)
|
||||
if old_name in names:
|
||||
names.remove(old_name)
|
||||
# Original section name (reference target name) should be kept to refer
|
||||
# from other nodes which is still not translated or uses explicit target
|
||||
# name like "`text to display <explicit target name_>`_"..
|
||||
# So, `old_name` is still exist in `names`.
|
||||
|
||||
_id = self.document.nameids.get(old_name, None)
|
||||
explicit = self.document.nametypes.get(old_name, None)
|
||||
|
||||
@@ -53,8 +53,8 @@ class Field(object):
|
||||
self.rolename = rolename
|
||||
self.bodyrolename = bodyrolename
|
||||
|
||||
def make_xref(self, rolename, domain, target, innernode=nodes.emphasis,
|
||||
contnode=None):
|
||||
def make_xref(self, rolename, domain, target,
|
||||
innernode=addnodes.literal_emphasis, contnode=None):
|
||||
if not rolename:
|
||||
return contnode or innernode(target, target)
|
||||
refnode = addnodes.pending_xref('', refdomain=domain, refexplicit=False,
|
||||
@@ -159,7 +159,8 @@ class TypedField(GroupedField):
|
||||
fieldtype = types.pop(fieldarg)
|
||||
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
|
||||
typename = u''.join(n.astext() for n in fieldtype)
|
||||
par += self.make_xref(self.typerolename, domain, typename)
|
||||
par += self.make_xref(self.typerolename, domain, typename,
|
||||
addnodes.literal_emphasis)
|
||||
else:
|
||||
par += fieldtype
|
||||
par += nodes.Text(')')
|
||||
|
||||
+6
-5
@@ -9,6 +9,7 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
import gettext
|
||||
import io
|
||||
from os import path
|
||||
from collections import namedtuple
|
||||
|
||||
@@ -19,7 +20,7 @@ from sphinx.util.osutil import walk
|
||||
from sphinx.util import SEP
|
||||
|
||||
|
||||
LocaleFileInfoBase = namedtuple('CatalogInfo', 'base_dir,domain')
|
||||
LocaleFileInfoBase = namedtuple('CatalogInfo', 'base_dir,domain,charset')
|
||||
|
||||
|
||||
class CatalogInfo(LocaleFileInfoBase):
|
||||
@@ -46,8 +47,8 @@ class CatalogInfo(LocaleFileInfoBase):
|
||||
path.getmtime(self.mo_path) < path.getmtime(self.po_path))
|
||||
|
||||
def write_mo(self, locale):
|
||||
with open(self.po_path, 'rt') as po:
|
||||
with open(self.mo_path, 'wb') as mo:
|
||||
with io.open(self.po_path, 'rt', encoding=self.charset) as po:
|
||||
with io.open(self.mo_path, 'wb') as mo:
|
||||
write_mo(mo, read_po(po, locale))
|
||||
|
||||
|
||||
@@ -72,7 +73,7 @@ def find_catalog_files(docname, srcdir, locale_dirs, lang, compaction):
|
||||
|
||||
|
||||
def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact=False,
|
||||
force_all=False):
|
||||
charset='utf-8', force_all=False):
|
||||
"""
|
||||
:param list locale_dirs:
|
||||
list of path as `['locale_dir1', 'locale_dir2', ...]` to find
|
||||
@@ -112,7 +113,7 @@ def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact
|
||||
domain = domain.replace(path.sep, SEP)
|
||||
if domains and domain not in domains:
|
||||
continue
|
||||
cat = CatalogInfo(base_dir, domain)
|
||||
cat = CatalogInfo(base_dir, domain, charset)
|
||||
if force_all or cat.is_outdated():
|
||||
catalogs.add(cat)
|
||||
|
||||
|
||||
+42
-31
@@ -36,20 +36,27 @@ caption_ref_re = explicit_title_re # b/w compat alias
|
||||
|
||||
|
||||
def apply_source_workaround(node):
|
||||
# workaround: nodes.term have wrong rawsource if classifier is specified.
|
||||
# The behavior of docutils-0.11, 0.12 is:
|
||||
# * when ``term text : classifier1 : classifier2`` is specified,
|
||||
# * rawsource of term node will have: ``term text : classifier1 : classifier2``
|
||||
# * rawsource of classifier node will be None
|
||||
if isinstance(node, nodes.classifier) and not node.rawsource:
|
||||
definition_list_item = node.parent
|
||||
node.source = definition_list_item.source
|
||||
node.line = definition_list_item.line - 1
|
||||
node.rawsource = node.astext() # set 'classifier1' (or 'classifier2')
|
||||
if isinstance(node, nodes.term):
|
||||
# overwrite: ``term : classifier1 : classifier2`` -> ``term text``
|
||||
node.rawsource = node.astext()
|
||||
|
||||
# workaround: recommonmark-0.2.0 doesn't set rawsource attribute
|
||||
if not node.rawsource:
|
||||
node.rawsource = node.astext()
|
||||
|
||||
if node.source and node.rawsource:
|
||||
return
|
||||
|
||||
# workaround: nodes.term doesn't have source, line and rawsource
|
||||
# (fixed in Docutils r7495)
|
||||
if isinstance(node, nodes.term):
|
||||
definition_list_item = node.parent
|
||||
if definition_list_item.line is not None:
|
||||
node.source = definition_list_item.source
|
||||
node.line = definition_list_item.line - 1
|
||||
node.rawsource = definition_list_item. \
|
||||
rawsource.split("\n", 2)[0]
|
||||
return
|
||||
|
||||
# workaround: docutils-0.10.0 or older's nodes.caption for nodes.figure
|
||||
# and nodes.title for nodes.admonition doesn't have source, line.
|
||||
# this issue was filed to Docutils tracker:
|
||||
@@ -71,18 +78,20 @@ IGNORED_NODES = (
|
||||
nodes.Inline,
|
||||
nodes.literal_block,
|
||||
nodes.doctest_block,
|
||||
addnodes.versionmodified,
|
||||
# XXX there are probably more
|
||||
)
|
||||
|
||||
|
||||
def is_translatable(node):
|
||||
if isinstance(node, nodes.TextElement):
|
||||
apply_source_workaround(node)
|
||||
|
||||
if not node.source:
|
||||
return False # built-in message
|
||||
if isinstance(node, IGNORED_NODES) and 'translatable' not in node:
|
||||
return False
|
||||
if not node.get('translatable', True):
|
||||
# not(node['translatable'] == True or node['translatable'] is None)
|
||||
return False
|
||||
# <field_name>orphan</field_name>
|
||||
# XXX ignore all metadata (== docinfo)
|
||||
if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
|
||||
@@ -225,7 +234,7 @@ def process_index_entry(entry, targetid):
|
||||
return indexentries
|
||||
|
||||
|
||||
def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc):
|
||||
def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc, traversed):
|
||||
"""Inline all toctrees in the *tree*.
|
||||
|
||||
Record all docnames in *docnameset*, and output docnames with *colorfunc*.
|
||||
@@ -235,23 +244,25 @@ def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc):
|
||||
newnodes = []
|
||||
includefiles = map(text_type, toctreenode['includefiles'])
|
||||
for includefile in includefiles:
|
||||
try:
|
||||
builder.info(colorfunc(includefile) + " ", nonl=1)
|
||||
subtree = inline_all_toctrees(builder, docnameset, includefile,
|
||||
builder.env.get_doctree(includefile),
|
||||
colorfunc)
|
||||
docnameset.add(includefile)
|
||||
except Exception:
|
||||
builder.warn('toctree contains ref to nonexisting '
|
||||
'file %r' % includefile,
|
||||
builder.env.doc2path(docname))
|
||||
else:
|
||||
sof = addnodes.start_of_file(docname=includefile)
|
||||
sof.children = subtree.children
|
||||
for sectionnode in sof.traverse(nodes.section):
|
||||
if 'docname' not in sectionnode:
|
||||
sectionnode['docname'] = includefile
|
||||
newnodes.append(sof)
|
||||
if includefile not in traversed:
|
||||
try:
|
||||
traversed.append(includefile)
|
||||
builder.info(colorfunc(includefile) + " ", nonl=1)
|
||||
subtree = inline_all_toctrees(builder, docnameset, includefile,
|
||||
builder.env.get_doctree(includefile),
|
||||
colorfunc, traversed)
|
||||
docnameset.add(includefile)
|
||||
except Exception:
|
||||
builder.warn('toctree contains ref to nonexisting '
|
||||
'file %r' % includefile,
|
||||
builder.env.doc2path(docname))
|
||||
else:
|
||||
sof = addnodes.start_of_file(docname=includefile)
|
||||
sof.children = subtree.children
|
||||
for sectionnode in sof.traverse(nodes.section):
|
||||
if 'docname' not in sectionnode:
|
||||
sectionnode['docname'] = includefile
|
||||
newnodes.append(sof)
|
||||
toctreenode.parent.replace(toctreenode, newnodes)
|
||||
return tree
|
||||
|
||||
|
||||
+18
-4
@@ -151,15 +151,29 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
|
||||
def make_filename(string):
|
||||
return no_fn_re.sub('', string) or 'sphinx'
|
||||
|
||||
if PY2:
|
||||
|
||||
def ustrftime(format, *args):
|
||||
# strftime for unicode strings
|
||||
def ustrftime(format, *args):
|
||||
if not args:
|
||||
# If time is not specified, try to use $SOURCE_DATE_EPOCH variable
|
||||
# See https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal
|
||||
source_date_epoch = os.getenv('SOURCE_DATE_EPOCH')
|
||||
if source_date_epoch is not None:
|
||||
time_struct = time.gmtime(float(source_date_epoch))
|
||||
args = [time_struct]
|
||||
if PY2:
|
||||
# if a locale is set, the time strings are encoded in the encoding
|
||||
# given by LC_TIME; if that is available, use it
|
||||
enc = locale.getlocale(locale.LC_TIME)[1] or 'utf-8'
|
||||
return time.strftime(text_type(format).encode(enc), *args).decode(enc)
|
||||
else:
|
||||
ustrftime = time.strftime
|
||||
else: # Py3
|
||||
# On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError.
|
||||
# http://bugs.python.org/issue8304
|
||||
try:
|
||||
return time.strftime(format, *args)
|
||||
except UnicodeEncodeError:
|
||||
r = time.strftime(format.encode('unicode-escape').decode(), *args)
|
||||
return r.encode().decode('unicode-escape')
|
||||
|
||||
|
||||
def safe_relpath(path, start=None):
|
||||
|
||||
@@ -20,6 +20,8 @@ from itertools import product
|
||||
|
||||
from six import PY3, text_type, exec_
|
||||
|
||||
NoneType = type(None)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Python 2/3 compatibility
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ tex_replacements = [
|
||||
('±', r'\(\pm\)'),
|
||||
('→', r'\(\rightarrow\)'),
|
||||
('‣', r'\(\rightarrow\)'),
|
||||
('✓', r'\(\checkmark\)'),
|
||||
# used to separate -- in options
|
||||
('', r'{}'),
|
||||
# map some special Unicode characters to similar ASCII ones
|
||||
|
||||
+79
-30
@@ -43,6 +43,7 @@ HEADER = r'''%% Generated by Sphinx.
|
||||
\usepackage{sphinx}
|
||||
\usepackage{multirow}
|
||||
%(usepackages)s
|
||||
%(contentsname)s
|
||||
%(numfig_format)s
|
||||
%(preamble)s
|
||||
|
||||
@@ -136,6 +137,7 @@ class Table(object):
|
||||
self.has_verbatim = False
|
||||
self.caption = None
|
||||
self.longtable = False
|
||||
self.footnotes = []
|
||||
|
||||
|
||||
class LaTeXTranslator(nodes.NodeVisitor):
|
||||
@@ -159,6 +161,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
'longtable': '\\usepackage{longtable}',
|
||||
'usepackages': '',
|
||||
'numfig_format': '',
|
||||
'contentsname': '',
|
||||
'preamble': '',
|
||||
'title': '',
|
||||
'date': '',
|
||||
@@ -248,12 +251,16 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
return '\\usepackage{%s}' % (packagename,)
|
||||
usepackages = (declare_package(*p) for p in builder.usepackages)
|
||||
self.elements['usepackages'] += "\n".join(usepackages)
|
||||
if getattr(document.settings, 'contentsname', None):
|
||||
self.elements['contentsname'] = \
|
||||
self.babel_renewcommand(builder, '\\contentsname',
|
||||
document.settings.contentsname)
|
||||
self.elements['numfig_format'] = self.generate_numfig_format(builder)
|
||||
# allow the user to override them all
|
||||
self.elements.update(builder.config.latex_elements)
|
||||
if self.elements['extraclassoptions']:
|
||||
self.elements['classoptions'] += ',' + \
|
||||
self.elements['extraclassoptions']
|
||||
self.elements['numfig_format'] = self.generate_numfig_format(builder)
|
||||
|
||||
self.highlighter = highlighting.PygmentsBridge(
|
||||
'latex',
|
||||
@@ -269,6 +276,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
# by .. highlight:: directive in the master file
|
||||
self.hlsettingstack = 2 * [[builder.config.highlight_language,
|
||||
sys.maxsize]]
|
||||
self.bodystack = []
|
||||
self.footnotestack = []
|
||||
self.curfilestack = []
|
||||
self.handled_abbrs = set()
|
||||
@@ -297,6 +305,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.remember_multirow = {}
|
||||
self.remember_multirowcol = {}
|
||||
|
||||
def pushbody(self, newbody):
|
||||
self.bodystack.append(self.body)
|
||||
self.body = newbody
|
||||
|
||||
def popbody(self):
|
||||
body = self.body
|
||||
self.body = self.bodystack.pop()
|
||||
return body
|
||||
|
||||
def format_docclass(self, docclass):
|
||||
""" prepends prefix to sphinx document classes
|
||||
"""
|
||||
@@ -329,8 +346,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
encode('ascii', 'backslashreplace').decode('ascii').\
|
||||
replace('\\', '_')
|
||||
|
||||
def generate_numfig_format(self, builder):
|
||||
ret = []
|
||||
def babel_renewcommand(self, builder, command, definition):
|
||||
if builder.config.language == 'ja':
|
||||
babel_prefix = ''
|
||||
babel_suffix = ''
|
||||
@@ -342,18 +358,25 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
else:
|
||||
language = 'english'
|
||||
|
||||
babel_prefix = '\\addto\\captions%s{' % language
|
||||
babel_suffix = '}'
|
||||
if self.elements['babel']:
|
||||
babel_prefix = '\\addto\\captions%s{' % language
|
||||
babel_suffix = '}'
|
||||
else:
|
||||
babel_prefix = ''
|
||||
babel_suffix = ''
|
||||
|
||||
return ('%s\\renewcommand{%s}{%s}%s\n' %
|
||||
(babel_prefix, command, definition, babel_suffix))
|
||||
|
||||
def generate_numfig_format(self, builder):
|
||||
ret = []
|
||||
figure = self.builder.config.numfig_format['figure'].split('%s', 1)
|
||||
if len(figure) == 1:
|
||||
ret.append('\\def\\fnum@figure{%s}\n' %
|
||||
text_type(figure[0]).translate(tex_escape_map))
|
||||
else:
|
||||
ret.append('%s\\renewcommand{\\figurename}{%s}%s\n' %
|
||||
(babel_prefix,
|
||||
text_type(figure[0]).translate(tex_escape_map),
|
||||
babel_suffix))
|
||||
definition = text_type(figure[0]).translate(tex_escape_map)
|
||||
ret.append(self.babel_renewcommand(builder, '\\figurename', definition))
|
||||
if figure[1]:
|
||||
ret.append('\\makeatletter\n')
|
||||
ret.append('\\def\\fnum@figure{\\figurename\\thefigure%s}\n' %
|
||||
@@ -365,10 +388,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
ret.append('\\def\\fnum@table{%s}\n' %
|
||||
text_type(table[0]).translate(tex_escape_map))
|
||||
else:
|
||||
ret.append('%s\\renewcommand{\\tablename}{%s}%s\n' %
|
||||
(babel_prefix,
|
||||
text_type(table[0]).translate(tex_escape_map),
|
||||
babel_suffix))
|
||||
definition = text_type(table[0]).translate(tex_escape_map)
|
||||
ret.append(self.babel_renewcommand(builder, '\\tablename', definition))
|
||||
if table[1]:
|
||||
ret.append('\\makeatletter\n')
|
||||
ret.append('\\def\\fnum@table{\\tablename\\thetable%s}\n' %
|
||||
@@ -485,7 +506,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
fnotes = {}
|
||||
for fn in footnotes_under(node):
|
||||
num = fn.children[0].astext().strip()
|
||||
fnotes[num] = [collected_footnote(*fn.children), False]
|
||||
newnode = collected_footnote(*fn.children, number=num)
|
||||
fnotes[num] = [newnode, False]
|
||||
return fnotes
|
||||
|
||||
def depart_start_of_file(self, node):
|
||||
@@ -592,8 +614,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.body.append('{')
|
||||
self.context.append('}\n')
|
||||
elif isinstance(parent, nodes.table):
|
||||
self.table.caption = self.encode(node.astext())
|
||||
raise nodes.SkipNode
|
||||
# Redirect body output until title is finished.
|
||||
self.pushbody([])
|
||||
else:
|
||||
self.builder.warn(
|
||||
'encountered title node not in section, topic, table, '
|
||||
@@ -605,7 +627,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
|
||||
def depart_title(self, node):
|
||||
self.in_title = 0
|
||||
self.body.append(self.context.pop())
|
||||
if isinstance(node.parent, nodes.table):
|
||||
self.table.caption = self.popbody()
|
||||
else:
|
||||
self.body.append(self.context.pop())
|
||||
|
||||
def visit_subtitle(self, node):
|
||||
if isinstance(node.parent, nodes.sidebar):
|
||||
@@ -733,7 +758,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
|
||||
def visit_collected_footnote(self, node):
|
||||
self.in_footnote += 1
|
||||
self.body.append('\\footnote{')
|
||||
if 'in_table' in node:
|
||||
self.body.append('\\footnotetext[%s]{' % node['number'])
|
||||
else:
|
||||
self.body.append('\\footnote[%s]{' % node['number'])
|
||||
|
||||
def depart_collected_footnote(self, node):
|
||||
self.body.append('}')
|
||||
@@ -760,16 +788,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.tablebody = []
|
||||
self.tableheaders = []
|
||||
# Redirect body output until table is finished.
|
||||
self._body = self.body
|
||||
self.body = self.tablebody
|
||||
self.pushbody(self.tablebody)
|
||||
|
||||
def depart_table(self, node):
|
||||
if self.table.rowcount > 30:
|
||||
self.table.longtable = True
|
||||
self.body = self._body
|
||||
self.popbody()
|
||||
if not self.table.longtable and self.table.caption is not None:
|
||||
self.body.append(u'\n\n\\begin{threeparttable}\n'
|
||||
u'\\capstart\\caption{%s}\n' % self.table.caption)
|
||||
self.body.append('\n\n\\begin{threeparttable}\n'
|
||||
'\\capstart\\caption{')
|
||||
for caption in self.table.caption:
|
||||
self.body.append(caption)
|
||||
self.body.append('}')
|
||||
for id in self.next_table_ids:
|
||||
self.body.append(self.hypertarget(id, anchor=False))
|
||||
if node['ids']:
|
||||
@@ -829,6 +859,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.body.append(endmacro)
|
||||
if not self.table.longtable and self.table.caption is not None:
|
||||
self.body.append('\\end{threeparttable}\n\n')
|
||||
if self.table.footnotes:
|
||||
for footnode in self.table.footnotes:
|
||||
footnode['in_table'] = True
|
||||
footnode.walkabout(self)
|
||||
self.table = None
|
||||
self.tablebody = None
|
||||
|
||||
@@ -874,6 +908,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.body.append('\\\\\n')
|
||||
if any(self.remember_multirow.values()):
|
||||
linestart = 1
|
||||
col = self.table.colcount
|
||||
for col in range(1, self.table.col + 1):
|
||||
if self.remember_multirow.get(col):
|
||||
if linestart != col:
|
||||
@@ -896,7 +931,7 @@ 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.table.col += extracols
|
||||
@@ -906,7 +941,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
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|}{')
|
||||
@@ -914,7 +949,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
self.body.append('}{l|}{')
|
||||
context += '}'
|
||||
if 'morerows' in node:
|
||||
self.body.append(' \multirow{')
|
||||
self.body.append(' \\multirow{')
|
||||
self.body.append(str(node.get('morerows') + 1))
|
||||
self.body.append('}{*}{')
|
||||
context += '}'
|
||||
@@ -932,7 +967,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
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|}{}'
|
||||
self.table.col += extracols
|
||||
@@ -1520,7 +1555,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
# if a footnote has been inserted once, it shouldn't be repeated
|
||||
# by the next reference
|
||||
if used:
|
||||
self.body.append('\\footnotemark[%s]' % num)
|
||||
if self.table:
|
||||
self.body.append('\\protect\\footnotemark[%s]' % num)
|
||||
else:
|
||||
self.body.append('\\footnotemark[%s]' % num)
|
||||
elif self.table:
|
||||
self.footnotestack[-1][num][1] = True
|
||||
self.body.append('\\protect\\footnotemark[%s]' % num)
|
||||
self.table.footnotes.append(footnode)
|
||||
else:
|
||||
if self.in_caption:
|
||||
raise UnsupportedError('%s:%s: footnotes in float captions '
|
||||
@@ -1701,11 +1743,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
classes = node.get('classes', [])
|
||||
if classes in [['menuselection'], ['guilabel']]:
|
||||
self.body.append(r'\emph{')
|
||||
else:
|
||||
self.context.append('}')
|
||||
elif classes in [['accelerator']]:
|
||||
self.body.append(r'\underline{')
|
||||
self.context.append('}')
|
||||
elif classes and not self.in_title:
|
||||
self.body.append(r'\DUspan{%s}{' % ','.join(classes))
|
||||
self.context.append('}')
|
||||
else:
|
||||
self.context.append('')
|
||||
|
||||
def depart_inline(self, node):
|
||||
self.body.append('}')
|
||||
self.body.append(self.context.pop())
|
||||
|
||||
def visit_generated(self, node):
|
||||
pass
|
||||
|
||||
@@ -303,8 +303,6 @@ class TextTranslator(nodes.NodeVisitor):
|
||||
|
||||
def visit_desc_signature(self, node):
|
||||
self.new_state(0)
|
||||
if node.parent['objtype'] in ('class', 'exception'):
|
||||
self.add_text('%s ' % node.parent['objtype'])
|
||||
|
||||
def depart_desc_signature(self, node):
|
||||
# XXX: wrap signatures in a way that makes sense
|
||||
@@ -630,8 +628,7 @@ class TextTranslator(nodes.NodeVisitor):
|
||||
self.end_state(first='%s. ' % self.list_counter[-1])
|
||||
|
||||
def visit_definition_list_item(self, node):
|
||||
self._li_has_classifier = len(node) >= 2 and \
|
||||
isinstance(node[1], nodes.classifier)
|
||||
self._classifier_count_in_li = len(node.traverse(nodes.classifier))
|
||||
|
||||
def depart_definition_list_item(self, node):
|
||||
pass
|
||||
@@ -640,7 +637,7 @@ class TextTranslator(nodes.NodeVisitor):
|
||||
self.new_state(0)
|
||||
|
||||
def depart_term(self, node):
|
||||
if not self._li_has_classifier:
|
||||
if not self._classifier_count_in_li:
|
||||
self.end_state(end=None)
|
||||
|
||||
def visit_termsep(self, node):
|
||||
@@ -651,7 +648,9 @@ class TextTranslator(nodes.NodeVisitor):
|
||||
self.add_text(' : ')
|
||||
|
||||
def depart_classifier(self, node):
|
||||
self.end_state(end=None)
|
||||
self._classifier_count_in_li -= 1
|
||||
if not self._classifier_count_in_li:
|
||||
self.end_state(end=None)
|
||||
|
||||
def visit_definition(self, node):
|
||||
self.new_state()
|
||||
@@ -711,6 +710,9 @@ class TextTranslator(nodes.NodeVisitor):
|
||||
def _visit_admonition(self, node):
|
||||
self.new_state(2)
|
||||
|
||||
if isinstance(node.children[0], nodes.Sequential):
|
||||
self.add_text(self.nl)
|
||||
|
||||
def _make_depart_admonition(name):
|
||||
def depart_admonition(self, node):
|
||||
self.end_state(first=admonitionlabels[name] + ': ')
|
||||
|
||||
@@ -24,6 +24,17 @@ citation
|
||||
|
||||
[bar]_
|
||||
|
||||
footnotes in table
|
||||
--------------------
|
||||
|
||||
.. list-table:: Table caption [#]_
|
||||
:header-rows: 1
|
||||
|
||||
* - name [#]_
|
||||
- desription
|
||||
* - VIDIOC_CROPCAP
|
||||
- Information about VIDIOC_CROPCAP
|
||||
|
||||
footenotes
|
||||
--------------------
|
||||
|
||||
@@ -39,6 +50,10 @@ footenotes
|
||||
|
||||
.. [bar] cite
|
||||
|
||||
.. [#] footnotes in table caption
|
||||
|
||||
.. [#] footnotes in table
|
||||
|
||||
|
||||
missing target
|
||||
--------------------
|
||||
|
||||
@@ -187,7 +187,7 @@ Tables
|
||||
| 2 | Empty cells: | |
|
||||
+----+----------------+----+
|
||||
|
||||
Table with multirow and multicol:
|
||||
Tables with multirow and multicol:
|
||||
|
||||
.. only:: latex
|
||||
|
||||
@@ -199,6 +199,12 @@ Table with multirow and multicol:
|
||||
| x | multi-column cell | x |
|
||||
+----+---------------------+----+
|
||||
|
||||
+----+
|
||||
| 1 |
|
||||
+ +
|
||||
| |
|
||||
+----+
|
||||
|
||||
|
||||
Figures
|
||||
-------
|
||||
|
||||
@@ -191,7 +191,7 @@ Referencing :userdescrole:`myobj`.
|
||||
CPP domain
|
||||
==========
|
||||
|
||||
.. cpp:class:: n::Array<T,d>
|
||||
.. cpp:class:: n::Array
|
||||
|
||||
.. cpp:function:: T& operator[]( unsigned j )
|
||||
const T& operator[]( unsigned j ) const
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.. seealso::
|
||||
|
||||
* item 1
|
||||
* item 2
|
||||
@@ -0,0 +1,32 @@
|
||||
value1 = 123 # wrong type
|
||||
value2 = 123 # lambda with wrong type
|
||||
value3 = [] # lambda with correct type
|
||||
value4 = True # child type
|
||||
value5 = 3 # parent type
|
||||
value6 = () # other sequence type, also raises
|
||||
value7 = ['foo'] # explicitly permitted
|
||||
|
||||
class A(object):
|
||||
pass
|
||||
class B(A):
|
||||
pass
|
||||
class C(A):
|
||||
pass
|
||||
|
||||
value8 = C() # sibling type
|
||||
|
||||
# both have no default or permissible types
|
||||
value9 = 'foo'
|
||||
value10 = 123
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('value1', 'string', False)
|
||||
app.add_config_value('value2', lambda conf: [], False)
|
||||
app.add_config_value('value3', [], False)
|
||||
app.add_config_value('value4', 100, False)
|
||||
app.add_config_value('value5', False, False)
|
||||
app.add_config_value('value6', [], False)
|
||||
app.add_config_value('value7', 'string', False, [list])
|
||||
app.add_config_value('value8', B(), False)
|
||||
app.add_config_value('value9', None, False)
|
||||
app.add_config_value('value10', None, False)
|
||||
@@ -0,0 +1,4 @@
|
||||
===
|
||||
Bar
|
||||
===
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
master_doc = 'index'
|
||||
html_theme = 'classic'
|
||||
@@ -0,0 +1,4 @@
|
||||
===
|
||||
Foo
|
||||
===
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
test-tocdepth
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of content
|
||||
|
||||
foo
|
||||
bar
|
||||
@@ -127,3 +127,12 @@ Special directives
|
||||
|
||||
import test_ext_doctest
|
||||
test_ext_doctest.cleanup_call()
|
||||
|
||||
non-ASCII result
|
||||
----------------
|
||||
|
||||
>>> print('umlauts: äöü.')
|
||||
umlauts: äöü.
|
||||
>>> print('Japanese: 日本語')
|
||||
Japanese: 日本語
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
@@ -0,0 +1,2 @@
|
||||
[theme]
|
||||
inherit = base_theme1
|
||||
@@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys, os
|
||||
|
||||
templates_path = ['_templates']
|
||||
master_doc = 'index'
|
||||
html_theme = 'base_theme2'
|
||||
@@ -0,0 +1,3 @@
|
||||
============================
|
||||
Test double inheriting theme
|
||||
============================
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
extensions = ['sphinx.ext.graphviz']
|
||||
master_doc = 'index'
|
||||
@@ -0,0 +1,7 @@
|
||||
graphviz
|
||||
========
|
||||
|
||||
.. digraph:: foo
|
||||
:caption: caption of graph
|
||||
|
||||
bar -> baz
|
||||
@@ -22,3 +22,4 @@ CONTENTS
|
||||
versionchange
|
||||
docfields
|
||||
raw
|
||||
refs
|
||||
|
||||
@@ -30,3 +30,13 @@ msgstr "SOME OTHER TERM"
|
||||
|
||||
msgid "The corresponding definition #2"
|
||||
msgstr "THE CORRESPONDING DEFINITION #2"
|
||||
|
||||
msgid "Some term with"
|
||||
msgstr "SOME TERM WITH"
|
||||
|
||||
msgid "classifier1"
|
||||
msgstr "CLASSIFIER1"
|
||||
|
||||
msgid "classifier2"
|
||||
msgstr "CLASSIFIER2"
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ Some term
|
||||
Some other term
|
||||
The corresponding definition #2
|
||||
|
||||
Some term with : classifier1 : classifier2
|
||||
The corresponding definition
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1191 1.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-08-08 15:31+0900\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Translation Tips"
|
||||
msgstr "X TIPS"
|
||||
|
||||
msgid "A-1. Here's how you can `download Sphinx`_."
|
||||
msgstr "A-1. HERE'S HOW YOU CAN `download Sphinx`_."
|
||||
|
||||
msgid "A-2. Here's how you can `download Sphinx`_."
|
||||
msgstr "A-2. HERE'S HOW YOU CAN `A1 DOWNLOAD SPHINX`_."
|
||||
|
||||
msgid "A-3. Here's how you can `download Sphinx`_."
|
||||
msgstr ""
|
||||
"A-3. HERE'S HOW YOU CAN `A3 DOWNLOAD SPHINX <download Sphinx_>`_ AND `A3 DOWNLOAD "
|
||||
"SPHINX <download Sphinx_>`_."
|
||||
|
||||
msgid "B-1. `Docutils site`_ and `Sphinx site`_."
|
||||
msgstr "B-1. `Docutils site`_ and `Sphinx site`_."
|
||||
|
||||
msgid "B-2. `Docutils site`_ and `Sphinx site`_."
|
||||
msgstr "B-2. `B1 DOCUTILS SITE`_ AND `B1 SPHINX SITE`_."
|
||||
|
||||
msgid "B-3. `Docutils site`_ and `Sphinx site`_."
|
||||
msgstr "B-3. `B2 SPHINX SITE`_ AND `B2 DOCUTILS SITE`_."
|
||||
|
||||
msgid "B-4. `Docutils site`_ and `Sphinx site`_."
|
||||
msgstr ""
|
||||
"B-4. `B4 SPHINX SITE <Sphinx site_>`_ AND `B4 DOCUTILS SITE <Docutils "
|
||||
"site_>`_."
|
||||
|
||||
msgid "B-5. `Docutils site`_ and `Sphinx site`_."
|
||||
msgstr ""
|
||||
"B-5. `B5 SPHINX SITE <Sphinx site_>`_ AND `B5 DOCUTILS SITE <Docutils "
|
||||
"site_>`_\" AND `B5 SPHINX SITE <Sphinx site_>`_."
|
||||
|
||||
msgid "C-1. Link to `Translation Tips`_ section."
|
||||
msgstr "C-1. LINK TO `Translation Tips`_ SECTION."
|
||||
|
||||
msgid "C-2. Link to `Translation Tips`_ section."
|
||||
msgstr "C-2. LINK TO `X TIPS`_ SECTION."
|
||||
|
||||
msgid "C-3. Link to `Translation Tips`_ section."
|
||||
msgstr "C-3. LINK TO `X TIPS <Translation Tips_>`_ SECTION."
|
||||
|
||||
msgid "C-4. Link to `Translation Tips`_ section."
|
||||
msgstr ""
|
||||
"C-4. LINK TO `X TIPS <Translation Tips_>`_ x `X TIPS <Translation Tips_>`_ "
|
||||
"SECTION."
|
||||
|
||||
msgid "C-5. Link to `Translation Tips`_ section."
|
||||
msgstr ""
|
||||
"C-5. LINK TO `TRANS <X TIPS_>`_ x `LATION <X TIPS_>`_ "
|
||||
|
||||
msgid "D-1. Link to `Translation Tips`_ and `Next Section`_ section."
|
||||
msgstr "D-1. LINK TO `Translation Tips`_ and `Next Section`_ SECTION."
|
||||
|
||||
msgid "D-2. Link to `Translation Tips`_ and `Next Section`_ section."
|
||||
msgstr "D-2. LINK TO `X TIPS`_ AND `N SECTION`_ SECTION."
|
||||
|
||||
msgid "D-3. Link to `Translation Tips`_ and `Next Section`_ section."
|
||||
msgstr "D-3. LINK TO `N SECTION`_ AND `X TIPS`_ SECTION."
|
||||
|
||||
msgid "D-4. Link to `Translation Tips`_ and `Next Section`_ section."
|
||||
msgstr ""
|
||||
"D-4. LINK TO `N SECTION <Next Section_>`_ AND `X TIPS <Translation Tips_>`_ "
|
||||
"SECTION."
|
||||
|
||||
msgid "D-5. Link to `Translation Tips`_ and `Next Section`_ section."
|
||||
msgstr ""
|
||||
"D-5. LINK TO `Next <N SECTION_>`_ AND `Tips <X TIPS_>`_ "
|
||||
|
||||
msgid "Next Section"
|
||||
msgstr "N SECTION"
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
References
|
||||
===========
|
||||
|
||||
Translation Tips
|
||||
-----------------
|
||||
|
||||
.. _download Sphinx: https://pypi.python.org/pypi/sphinx
|
||||
.. _Docutils site: http://docutils.sourceforge.net/
|
||||
.. _Sphinx site: http://sphinx-doc.org/
|
||||
|
||||
|
||||
A-1. Here's how you can `download Sphinx`_.
|
||||
|
||||
A-2. Here's how you can `download Sphinx`_.
|
||||
|
||||
A-3. Here's how you can `download Sphinx`_.
|
||||
|
||||
B-1. `Docutils site`_ and `Sphinx site`_.
|
||||
|
||||
B-2. `Docutils site`_ and `Sphinx site`_.
|
||||
|
||||
B-3. `Docutils site`_ and `Sphinx site`_.
|
||||
|
||||
B-4. `Docutils site`_ and `Sphinx site`_.
|
||||
|
||||
C-1. Link to `Translation Tips`_ section.
|
||||
|
||||
C-2. Link to `Translation Tips`_ section.
|
||||
|
||||
C-3. Link to `Translation Tips`_ section.
|
||||
|
||||
C-4. Link to `Translation Tips`_ section.
|
||||
|
||||
C-5. Link to `Translation Tips`_ section.
|
||||
|
||||
D-1. Link to `Translation Tips`_ and `Next Section`_ section.
|
||||
|
||||
D-2. Link to `Translation Tips`_ and `Next Section`_ section.
|
||||
|
||||
D-3. Link to `Translation Tips`_ and `Next Section`_ section.
|
||||
|
||||
D-4. Link to `Translation Tips`_ and `Next Section`_ section.
|
||||
|
||||
D-5. Link to `Translation Tips`_ and `Next Section`_ section.
|
||||
|
||||
Next Section
|
||||
-------------
|
||||
@@ -48,3 +48,5 @@ test-tocdepth
|
||||
* Table.2.2 is :numref:`Table:%s <table22>`
|
||||
* List.1 is :numref:`CODE_1`
|
||||
* List.2.2 is :numref:`Code-%s <CODE22>`
|
||||
* Invalid numfig_format 01: :numref:`invalid <fig1>`
|
||||
* Invalid numfig_format 02: :numref:`Fig %s %s <fig1>`
|
||||
|
||||
+47
-2
@@ -11,8 +11,8 @@
|
||||
"""
|
||||
|
||||
# "raises" imported for usage by autodoc
|
||||
from util import TestApp, Struct, raises
|
||||
from nose.tools import with_setup
|
||||
from util import TestApp, Struct, raises, SkipTest
|
||||
from nose.tools import with_setup, eq_
|
||||
|
||||
from six import StringIO
|
||||
from docutils.statemachine import ViewList
|
||||
@@ -791,6 +791,7 @@ def test_generate():
|
||||
'module', 'test_autodoc')
|
||||
|
||||
# --- generate fodder ------------
|
||||
import six, sys
|
||||
|
||||
__all__ = ['Class']
|
||||
|
||||
@@ -833,6 +834,7 @@ class Base(object):
|
||||
def inheritedmeth(self):
|
||||
"""Inherited function."""
|
||||
|
||||
|
||||
class Class(Base):
|
||||
"""Class to document."""
|
||||
|
||||
@@ -968,3 +970,46 @@ class InstAttCls(object):
|
||||
|
||||
self.ia2 = 'e'
|
||||
"""Docstring for instance attribute InstAttCls.ia2."""
|
||||
|
||||
|
||||
def test_type_hints():
|
||||
from sphinx.ext.autodoc import formatargspec
|
||||
from sphinx.util.inspect import getargspec
|
||||
|
||||
try:
|
||||
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8
|
||||
except (ImportError, SyntaxError):
|
||||
raise SkipTest('Cannot import Python code with function annotations')
|
||||
|
||||
def verify_arg_spec(f, expected):
|
||||
eq_(formatargspec(f, *getargspec(f)), expected)
|
||||
|
||||
# Class annotations
|
||||
verify_arg_spec(f0, '(x: int, y: numbers.Integral) -> None')
|
||||
|
||||
# Generic types with concrete parameters
|
||||
verify_arg_spec(f1, '(x: typing.List[int]) -> typing.List[int]')
|
||||
|
||||
# TypeVars and generic types with TypeVars
|
||||
verify_arg_spec(f2, '(x: typing.List[T],'
|
||||
' y: typing.List[T_co],'
|
||||
' z: T) -> typing.List[T_contra]')
|
||||
|
||||
# Union types
|
||||
verify_arg_spec(f3, '(x: typing.Union[str, numbers.Integral]) -> None')
|
||||
|
||||
# Quoted annotations
|
||||
verify_arg_spec(f4, '(x: str, y: str) -> None')
|
||||
|
||||
# Keyword-only arguments
|
||||
verify_arg_spec(f5, '(x: int, *, y: str, z: str) -> None')
|
||||
|
||||
# Space around '=' for defaults
|
||||
verify_arg_spec(f6, '(x: int = None, y: dict = {}) -> None')
|
||||
|
||||
# Callable types
|
||||
verify_arg_spec(f7, '(x: typing.Callable[[int, str], int]) -> None')
|
||||
|
||||
# Tuple types
|
||||
verify_arg_spec(f8, '(x: typing.Tuple[int, str],'
|
||||
' y: typing.Tuple[int, ...]) -> None')
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_autodoc
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Test the autodoc extension. This tests mainly the Documenters; the auto
|
||||
directives are tested in a test source file translated by test_build.
|
||||
|
||||
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
# "raises" imported for usage by autodoc
|
||||
from util import TestApp, Struct, raises, SkipTest
|
||||
from nose.tools import with_setup, eq_
|
||||
|
||||
from six import StringIO
|
||||
from docutils.statemachine import ViewList
|
||||
|
||||
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
|
||||
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
|
||||
|
||||
app = None
|
||||
|
||||
def setup_module():
|
||||
global app
|
||||
app = TestApp()
|
||||
app.builder.env.app = app
|
||||
app.builder.env.temp_data['docname'] = 'dummy'
|
||||
app.connect('autodoc-process-docstring', process_docstring)
|
||||
app.connect('autodoc-process-signature', process_signature)
|
||||
app.connect('autodoc-skip-member', skip_member)
|
||||
|
||||
|
||||
def teardown_module():
|
||||
app.cleanup()
|
||||
|
||||
|
||||
directive = options = None
|
||||
|
||||
def setup_test():
|
||||
global options, directive
|
||||
global processed_docstrings, processed_signatures, _warnings
|
||||
|
||||
options = Struct(
|
||||
inherited_members = False,
|
||||
undoc_members = False,
|
||||
private_members = False,
|
||||
special_members = False,
|
||||
imported_members = False,
|
||||
show_inheritance = False,
|
||||
noindex = False,
|
||||
annotation = None,
|
||||
synopsis = '',
|
||||
platform = '',
|
||||
deprecated = False,
|
||||
members = [],
|
||||
member_order = 'alphabetic',
|
||||
exclude_members = set(),
|
||||
)
|
||||
|
||||
directive = Struct(
|
||||
env = app.builder.env,
|
||||
genopt = options,
|
||||
result = ViewList(),
|
||||
warn = warnfunc,
|
||||
filename_set = set(),
|
||||
)
|
||||
|
||||
processed_docstrings = []
|
||||
processed_signatures = []
|
||||
_warnings = []
|
||||
|
||||
|
||||
_warnings = []
|
||||
|
||||
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))
|
||||
if name == 'bar':
|
||||
return '42', None
|
||||
|
||||
|
||||
def skip_member(app, what, name, obj, skip, options):
|
||||
if name in ('__special1__', '__special2__'):
|
||||
return skip
|
||||
if name.startswith('_'):
|
||||
return True
|
||||
if name == 'skipmeth':
|
||||
return True
|
||||
|
||||
|
||||
@with_setup(setup_test)
|
||||
def test_generate():
|
||||
def assert_warns(warn_str, objtype, name, **kw):
|
||||
inst = AutoDirective._registry[objtype](directive, name)
|
||||
inst.generate(**kw)
|
||||
assert len(directive.result) == 0, directive.result
|
||||
assert len(_warnings) == 1, _warnings
|
||||
assert warn_str in _warnings[0], _warnings
|
||||
del _warnings[:]
|
||||
|
||||
def assert_works(objtype, name, **kw):
|
||||
inst = AutoDirective._registry[objtype](directive, name)
|
||||
inst.generate(**kw)
|
||||
assert directive.result
|
||||
#print '\n'.join(directive.result)
|
||||
assert len(_warnings) == 0, _warnings
|
||||
del directive.result[:]
|
||||
|
||||
def assert_processes(items, objtype, name, **kw):
|
||||
del processed_docstrings[:]
|
||||
del processed_signatures[:]
|
||||
assert_works(objtype, name, **kw)
|
||||
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)
|
||||
assert len(_warnings) == 0, _warnings
|
||||
assert item in directive.result
|
||||
del directive.result[:]
|
||||
|
||||
def assert_order(items, objtype, name, member_order, **kw):
|
||||
inst = AutoDirective._registry[objtype](directive, name)
|
||||
inst.options.member_order = member_order
|
||||
inst.generate(**kw)
|
||||
assert len(_warnings) == 0, _warnings
|
||||
items = list(reversed(items))
|
||||
lineiter = iter(directive.result)
|
||||
#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
|
||||
del directive.result[:]
|
||||
|
||||
options.members = []
|
||||
|
||||
# no module found?
|
||||
assert_warns("import for autodocumenting 'foobar'",
|
||||
'function', 'foobar', more_content=None)
|
||||
# importing
|
||||
assert_warns("failed to import module 'test_foobar'",
|
||||
'module', 'test_foobar', more_content=None)
|
||||
# attributes missing
|
||||
assert_warns("failed to import function 'foobar' from module 'util'",
|
||||
'function', 'util.foobar', more_content=None)
|
||||
# method missing
|
||||
assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc_py35';",
|
||||
'method', 'test_autodoc_py35.Class.foobar', more_content=None)
|
||||
|
||||
# test auto and given content mixing
|
||||
directive.env.ref_context['py:module'] = 'test_autodoc_py35'
|
||||
assert_result_contains(' Function.', 'method', 'Class.meth')
|
||||
add_content = ViewList()
|
||||
add_content.append('Content.', '', 0)
|
||||
assert_result_contains(' Function.', 'method',
|
||||
'Class.meth', more_content=add_content)
|
||||
assert_result_contains(' Content.', 'method',
|
||||
'Class.meth', more_content=add_content)
|
||||
|
||||
# test check_module
|
||||
inst = FunctionDocumenter(directive, 'raises')
|
||||
inst.generate(check_module=True)
|
||||
assert len(directive.result) == 0
|
||||
|
||||
# assert that exceptions can be documented
|
||||
assert_works('exception', 'test_autodoc_py35.CustomEx', all_members=True)
|
||||
assert_works('exception', 'test_autodoc_py35.CustomEx')
|
||||
|
||||
# test diverse inclusion settings for members
|
||||
should = [('class', 'test_autodoc_py35.Class')]
|
||||
assert_processes(should, 'class', 'Class')
|
||||
should.extend([('method', 'test_autodoc_py35.Class.meth')])
|
||||
options.members = ['meth']
|
||||
options.exclude_members = set(['excludemeth'])
|
||||
assert_processes(should, 'class', 'Class')
|
||||
should.extend([('attribute', 'test_autodoc_py35.Class.prop'),
|
||||
('attribute', 'test_autodoc_py35.Class.descr'),
|
||||
('attribute', 'test_autodoc_py35.Class.attr'),
|
||||
('attribute', 'test_autodoc_py35.Class.docattr'),
|
||||
('attribute', 'test_autodoc_py35.Class.udocattr'),
|
||||
('attribute', 'test_autodoc_py35.Class.mdocattr'),
|
||||
('attribute', 'test_autodoc_py35.Class.inst_attr_comment'),
|
||||
('attribute', 'test_autodoc_py35.Class.inst_attr_inline'),
|
||||
('attribute', 'test_autodoc_py35.Class.inst_attr_string'),
|
||||
('method', 'test_autodoc_py35.Class.moore'),
|
||||
])
|
||||
if six.PY3 and sys.version_info[:2] >= (3, 5):
|
||||
should.extend([
|
||||
('method', 'test_autodoc_py35.Class.do_coroutine'),
|
||||
])
|
||||
options.members = ALL
|
||||
assert_processes(should, 'class', 'Class')
|
||||
options.undoc_members = True
|
||||
should.extend((('attribute', 'test_autodoc_py35.Class.skipattr'),
|
||||
('method', 'test_autodoc_py35.Class.undocmeth'),
|
||||
('method', 'test_autodoc_py35.Class.roger')))
|
||||
assert_processes(should, 'class', 'Class')
|
||||
options.inherited_members = True
|
||||
should.append(('method', 'test_autodoc_py35.Class.inheritedmeth'))
|
||||
assert_processes(should, 'class', 'Class')
|
||||
|
||||
# test special members
|
||||
options.special_members = ['__special1__']
|
||||
should.append(('method', 'test_autodoc_py35.Class.__special1__'))
|
||||
assert_processes(should, 'class', 'Class')
|
||||
options.special_members = ALL
|
||||
should.append(('method', 'test_autodoc_py35.Class.__special2__'))
|
||||
assert_processes(should, 'class', 'Class')
|
||||
options.special_members = False
|
||||
|
||||
|
||||
# --- 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."""
|
||||
|
||||
def __init__(self, doc):
|
||||
self.__doc__ = doc
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
return self
|
||||
return 42
|
||||
|
||||
def meth(self):
|
||||
"""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."""
|
||||
def template(cls, a, b, c, d=4, e=5, f=6):
|
||||
return a, b, c, d, e, f
|
||||
from functools import partial
|
||||
function = partial(template, b=b, c=c, d=d)
|
||||
function.__name__ = name
|
||||
function.__doc__ = docstring
|
||||
return classmethod(function)
|
||||
|
||||
class Base(object):
|
||||
def inheritedmeth(self):
|
||||
"""Inherited function."""
|
||||
|
||||
if six.PY3 and sys.version_info[:2] >= (3, 5):
|
||||
|
||||
async def _other_coro_func():
|
||||
return "run"
|
||||
|
||||
|
||||
class Class(Base):
|
||||
"""Class to document."""
|
||||
|
||||
descr = CustomDataDescriptor("Descriptor instance docstring.")
|
||||
|
||||
def meth(self):
|
||||
"""Function."""
|
||||
|
||||
def undocmeth(self):
|
||||
pass
|
||||
|
||||
def skipmeth(self):
|
||||
"""Method that should be skipped."""
|
||||
|
||||
def excludemeth(self):
|
||||
"""Method that should be excluded."""
|
||||
|
||||
# should not be documented
|
||||
skipattr = 'foo'
|
||||
|
||||
#: should be documented -- süß
|
||||
attr = 'bar'
|
||||
|
||||
@property
|
||||
def prop(self):
|
||||
"""Property."""
|
||||
|
||||
docattr = 'baz'
|
||||
"""should likewise be documented -- süß"""
|
||||
|
||||
udocattr = 'quux'
|
||||
u"""should be documented as well - süß"""
|
||||
|
||||
# initialized to any class imported from another module
|
||||
mdocattr = StringIO()
|
||||
"""should be documented as well - süß"""
|
||||
|
||||
roger = _funky_classmethod("roger", 2, 3, 4)
|
||||
|
||||
moore = _funky_classmethod("moore", 9, 8, 7,
|
||||
docstring="moore(a, e, f) -> happiness")
|
||||
|
||||
def __init__(self, arg):
|
||||
self.inst_attr_inline = None #: an inline documented instance attr
|
||||
#: a documented instance attribute
|
||||
self.inst_attr_comment = None
|
||||
self.inst_attr_string = None
|
||||
"""a documented instance attribute"""
|
||||
|
||||
def __special1__(self):
|
||||
"""documented special method"""
|
||||
|
||||
def __special2__(self):
|
||||
# undocumented special method
|
||||
pass
|
||||
|
||||
if six.PY3 and sys.version_info[:2] >= (3, 5):
|
||||
|
||||
async def do_coroutine(self):
|
||||
"""A documented coroutine function"""
|
||||
|
||||
attr_coro_result = await _other_coro_func()
|
||||
@@ -154,7 +154,7 @@ HTML_XPATH = {
|
||||
(".//a[@href='#grammar-token-try_stmt']"
|
||||
"[@class='reference internal']/code/span", '^statement$'),
|
||||
(".//a[@href='subdir/includes.html']"
|
||||
"[@class='reference internal']/em", 'Including in subdir'),
|
||||
"[@class='reference internal']/span", 'Including in subdir'),
|
||||
(".//a[@href='objects.html#cmdoption-python-c']"
|
||||
"[@class='reference internal']/code/span[@class='pre']", '-c'),
|
||||
# abbreviations
|
||||
@@ -280,14 +280,18 @@ HTML_XPATH = {
|
||||
(".//dt/a", "double"),
|
||||
],
|
||||
'footnote.html': [
|
||||
(".//a[@class='footnote-reference'][@href='#id5'][@id='id1']", r"\[1\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id6'][@id='id2']", r"\[2\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id7'][@id='id1']", r"\[1\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id8'][@id='id2']", r"\[2\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"),
|
||||
(".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id9'][@id='id5']", r"\[4\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id10'][@id='id6']", r"\[5\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id5']", r"\[4\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id6']", r"\[5\]"),
|
||||
],
|
||||
'otherext.html': [
|
||||
(".//h1", "Generated section"),
|
||||
@@ -484,6 +488,9 @@ def test_tocdepth_singlehtml(app, status, warning):
|
||||
def test_numfig_disabled(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
assert 'WARNING: invalid numfig_format: invalid' not in warning.getvalue()
|
||||
assert 'WARNING: invalid numfig_format: Fig %s %s' not in warning.getvalue()
|
||||
|
||||
expects = {
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
@@ -543,6 +550,9 @@ def test_numfig_without_numbered_toctree(app, status, warning):
|
||||
(app.srcdir / 'index.rst').write_text(index, encoding='utf-8')
|
||||
app.builder.build_all()
|
||||
|
||||
assert 'WARNING: invalid numfig_format: invalid' in warning.getvalue()
|
||||
assert 'WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
|
||||
|
||||
expects = {
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
@@ -638,6 +648,9 @@ def test_numfig_without_numbered_toctree(app, status, warning):
|
||||
def test_numfig_with_numbered_toctree(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
assert 'WARNING: invalid numfig_format: invalid' in warning.getvalue()
|
||||
assert 'WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
|
||||
|
||||
expects = {
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
@@ -736,6 +749,9 @@ def test_numfig_with_numbered_toctree(app, status, warning):
|
||||
def test_numfig_with_prefix(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
assert 'WARNING: invalid numfig_format: invalid' in warning.getvalue()
|
||||
assert 'WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
|
||||
|
||||
expects = {
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
@@ -831,6 +847,9 @@ def test_numfig_with_prefix(app, status, warning):
|
||||
def test_numfig_with_secnum_depth(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
assert 'WARNING: invalid numfig_format: invalid' in warning.getvalue()
|
||||
assert 'WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue()
|
||||
|
||||
expects = {
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
|
||||
@@ -275,3 +275,46 @@ def test_latex_add_latex_package(app, status, warning):
|
||||
result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8')
|
||||
assert '\\usepackage{foo}' in result
|
||||
assert '\\usepackage[baz]{bar}' in result
|
||||
|
||||
|
||||
@with_app(buildername='latex', testroot='contentsname')
|
||||
def test_contentsname(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'Python.tex').text(encoding='utf8')
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('\\addto\\captionsenglish{\\renewcommand{\\contentsname}{Table of content}}'
|
||||
in result)
|
||||
|
||||
|
||||
@with_app(buildername='latex', testroot='contentsname',
|
||||
confoverrides={'language': 'ja'})
|
||||
def test_contentsname_with_language_ja(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'Python.tex').text(encoding='utf8')
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert '\\renewcommand{\\contentsname}{Table of content}' in result
|
||||
|
||||
|
||||
@with_app(buildername='latex')
|
||||
def test_footnote(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8')
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert '\\footnote[1]{\nnumbered\n}' in result
|
||||
assert '\\footnote[2]{\nauto numbered\n}' in result
|
||||
assert '\\footnote[3]{\nnamed\n}' in result
|
||||
assert '{\\hyperref[footnote:bar]{\\emph{{[}bar{]}}}}' in result
|
||||
assert '\\bibitem[bar]{bar}{\\phantomsection\\label{footnote:bar} ' in result
|
||||
assert '\\bibitem[bar]{bar}{\\phantomsection\\label{footnote:bar} \ncite' in result
|
||||
assert '\\bibitem[bar]{bar}{\\phantomsection\\label{footnote:bar} \ncite\n}' in result
|
||||
assert '\\capstart\\caption{Table caption \\protect\\footnotemark[4]}' in result
|
||||
assert 'name \\protect\\footnotemark[5]' in result
|
||||
assert ('\\end{threeparttable}\n\n'
|
||||
'\\footnotetext[4]{\nfootnotes in table caption\n}'
|
||||
'\\footnotetext[5]{\nfootnotes in table\n}' in result)
|
||||
|
||||
@@ -99,3 +99,15 @@ def test_table_with_empty_cell(app, status, warning):
|
||||
assert lines[4] == "+-------+-------+"
|
||||
assert lines[5] == "| XXX | |"
|
||||
assert lines[6] == "+-------+-------+"
|
||||
|
||||
|
||||
@with_text_app()
|
||||
def test_list_items_in_admonition(app, status, warning):
|
||||
app.builder.build_update()
|
||||
result = (app.outdir / 'listitems.txt').text(encoding='utf-8')
|
||||
lines = [line.rstrip() for line in result.splitlines()]
|
||||
assert lines[0] == "See also:"
|
||||
assert lines[1] == ""
|
||||
assert lines[2] == " * item 1"
|
||||
assert lines[3] == ""
|
||||
assert lines[4] == " * item 2"
|
||||
|
||||
+34
-37
@@ -9,9 +9,10 @@
|
||||
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
from six import PY2, PY3, StringIO
|
||||
from six import PY2, PY3, StringIO, iteritems
|
||||
|
||||
from util import TestApp, with_app, with_tempdir, raises, raises_msg
|
||||
from util import TestApp, with_app, gen_with_app, with_tempdir, \
|
||||
raises, raises_msg, assert_in, assert_not_in
|
||||
|
||||
from sphinx.config import Config
|
||||
from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError
|
||||
@@ -135,40 +136,36 @@ def test_config_eol(tmpdir):
|
||||
assert cfg.project == u'spam'
|
||||
|
||||
|
||||
TYPECHECK_OVERRIDES = [
|
||||
# configuration key, override value, should warn, default type
|
||||
('master_doc', 123, True, str),
|
||||
('man_pages', 123, True, list), # lambda
|
||||
('man_pages', [], False, list),
|
||||
('epub_tocdepth', True, True, int), # child type
|
||||
('nitpicky', 3, False, bool), # parent type
|
||||
('templates_path', (), True, list), # other sequence, also raises
|
||||
]
|
||||
if PY2:
|
||||
# Run a check for proper sibling detection in Python 2. Under py3k, the
|
||||
# default types do not have any siblings.
|
||||
TYPECHECK_OVERRIDES.append(
|
||||
('html_add_permalinks', 'bar', False, unicode))
|
||||
@with_app(confoverrides={
|
||||
'master_doc': 123,
|
||||
'language': 'foo',
|
||||
'primary_domain': None})
|
||||
def test_builtin_conf(app, status, warning):
|
||||
warnings = warning.getvalue()
|
||||
assert_in('master_doc', warnings,
|
||||
'override on builtin "master_doc" should raise a type warning')
|
||||
assert_not_in('language', warnings, 'explicitly permitted '
|
||||
'override on builtin "language" should NOT raise a type warning')
|
||||
assert_not_in('primary_domain', warnings, 'override to None on builtin '
|
||||
'"primary_domain" should NOT raise a type warning')
|
||||
|
||||
def test_gen_check_types():
|
||||
for key, value, should, deftype in TYPECHECK_OVERRIDES:
|
||||
warning = StringIO()
|
||||
try:
|
||||
app = TestApp(confoverrides={key: value}, warning=warning)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
app.cleanup()
|
||||
|
||||
real = type(value).__name__
|
||||
msg = ("WARNING: the config value %r has type `%s',"
|
||||
" defaults to `%s.'\n" % (key, real, deftype.__name__))
|
||||
def test():
|
||||
warning_list = warning.getvalue()
|
||||
assert (msg in warning_list) == should, \
|
||||
"Setting %s to %r should%s raise: %s" % \
|
||||
(key, value, " not" if should else "", msg)
|
||||
test.description = "test_check_type_%s_on_%s" % \
|
||||
(real, type(Config.config_values[key][0]).__name__)
|
||||
|
||||
yield test
|
||||
# See roots/test-config/conf.py.
|
||||
TYPECHECK_WARNINGS = {
|
||||
'value1': True,
|
||||
'value2': True,
|
||||
'value3': False,
|
||||
'value4': True,
|
||||
'value5': False,
|
||||
'value6': True,
|
||||
'value7': False,
|
||||
'value8': False,
|
||||
'value9': False,
|
||||
'value10': False,
|
||||
}
|
||||
@gen_with_app(testroot='config')
|
||||
def test_gen_check_types(app, status, warning):
|
||||
for key, should in iteritems(TYPECHECK_WARNINGS):
|
||||
yield assert_in if should else assert_not_in, key, warning.getvalue(), \
|
||||
'override on "%s" should%s raise a type warning' % \
|
||||
(key, '' if should else ' NOT')
|
||||
|
||||
@@ -63,7 +63,7 @@ def test_code_block_caption_html(app, status, warning):
|
||||
@with_app('latex', testroot='directive-code')
|
||||
def test_code_block_caption_latex(app, status, warning):
|
||||
app.builder.build_all()
|
||||
latex = (app.outdir / 'Python.tex').text()
|
||||
latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
|
||||
caption = '\\caption{caption \\emph{test} rb}'
|
||||
assert caption in latex
|
||||
|
||||
@@ -101,7 +101,7 @@ def test_literal_include_dedent(app, status, warning):
|
||||
@with_app('html', testroot='directive-code')
|
||||
def test_literal_include_linenos(app, status, warning):
|
||||
app.builder.build(['linenos'])
|
||||
html = (app.outdir / 'linenos.html').text()
|
||||
html = (app.outdir / 'linenos.html').text(encoding='utf-8')
|
||||
linenos = (
|
||||
'<td class="linenos"><div class="linenodiv"><pre>'
|
||||
' 1\n'
|
||||
@@ -124,7 +124,7 @@ def test_literal_include_linenos(app, status, warning):
|
||||
@with_app('html', testroot='directive-code')
|
||||
def test_literal_include_lineno_start(app, status, warning):
|
||||
app.builder.build(['lineno_start'])
|
||||
html = (app.outdir / 'lineno_start.html').text()
|
||||
html = (app.outdir / 'lineno_start.html').text(encoding='utf-8')
|
||||
linenos = (
|
||||
'<td class="linenos"><div class="linenodiv"><pre>'
|
||||
'200\n'
|
||||
@@ -147,7 +147,7 @@ def test_literal_include_lineno_start(app, status, warning):
|
||||
@with_app('html', testroot='directive-code')
|
||||
def test_literal_include_lineno_match(app, status, warning):
|
||||
app.builder.build(['lineno_match'])
|
||||
html = (app.outdir / 'lineno_match.html').text()
|
||||
html = (app.outdir / 'lineno_match.html').text(encoding='utf-8')
|
||||
pyobject = (
|
||||
'<td class="linenos"><div class="linenodiv"><pre>'
|
||||
' 9\n'
|
||||
@@ -180,7 +180,7 @@ def test_literal_include_lineno_match(app, status, warning):
|
||||
@with_app('latex', testroot='directive-code')
|
||||
def test_literalinclude_file_whole_of_emptyline(app, status, warning):
|
||||
app.builder.build_all()
|
||||
latex = (app.outdir / 'Python.tex').text()
|
||||
latex = (app.outdir / 'Python.tex').text(encoding='utf-8').replace('\r\n', '\n')
|
||||
includes = (
|
||||
'\\begin{Verbatim}[commandchars=\\\\\\{\\},numbers=left,firstnumber=1,stepnumber=1]\n'
|
||||
'\n'
|
||||
@@ -204,6 +204,6 @@ def test_literalinclude_caption_html(app, status, warning):
|
||||
@with_app('latex', testroot='directive-code')
|
||||
def test_literalinclude_caption_latex(app, status, warning):
|
||||
app.builder.build('index')
|
||||
latex = (app.outdir / 'Python.tex').text()
|
||||
latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
|
||||
caption = '\\caption{caption \\textbf{test} py}'
|
||||
assert caption in latex
|
||||
|
||||
+159
-28
@@ -13,19 +13,27 @@ from six import text_type
|
||||
|
||||
from util import raises
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError
|
||||
from sphinx.domains.cpp import Symbol
|
||||
import sphinx.domains.cpp as cppDomain
|
||||
|
||||
ids = []
|
||||
|
||||
|
||||
def parse(name, string):
|
||||
parser = DefinitionParser(string)
|
||||
res = getattr(parser, "parse_" + name + "_object")()
|
||||
parser = DefinitionParser(string, None)
|
||||
ast = parser.parse_declaration(name)
|
||||
if not parser.eof:
|
||||
print("Parsing stopped at", parser.pos)
|
||||
print(string)
|
||||
print('-'*parser.pos + '^')
|
||||
raise DefinitionError("")
|
||||
return res
|
||||
# The scopedness would usually have been set by CPPEnumObject
|
||||
if name == "enum":
|
||||
ast.scoped = None # simulate unscoped enum
|
||||
return ast
|
||||
|
||||
|
||||
def check(name, input, idv1output=None, idv2output=None, output=None):
|
||||
# first a simple check of the AST
|
||||
@@ -34,25 +42,28 @@ def check(name, input, idv1output=None, idv2output=None, output=None):
|
||||
ast = parse(name, input)
|
||||
res = text_type(ast)
|
||||
if res != output:
|
||||
print("")
|
||||
print("Input: ", text_type(input))
|
||||
print("Result: ", res)
|
||||
print("Expected: ", output)
|
||||
raise DefinitionError("")
|
||||
ast.describe_signature([], 'lastIsName', None, parentScope=ast.name)
|
||||
# Artificially set the prefixedName, otherwise the get_id fails.
|
||||
# It would usually have been set in handle_signarue.
|
||||
ast.prefixedName = ast.name
|
||||
rootSymbol = Symbol(None, None, None, None, None, None)
|
||||
symbol = rootSymbol.add_declaration(ast, docname="Test")
|
||||
parentNode = addnodes.desc()
|
||||
signode = addnodes.desc_signature(input, '')
|
||||
parentNode += signode
|
||||
ast.describe_signature(signode, 'lastIsName', symbol)
|
||||
|
||||
if idv2output:
|
||||
idv2output = "_CPPv2" + idv2output
|
||||
try:
|
||||
idv1 = ast.get_id_v1()
|
||||
assert idv1 != None
|
||||
assert idv1 is not None
|
||||
except NoOldIdError:
|
||||
idv1 = None
|
||||
try:
|
||||
idv2 = ast.get_id_v2()
|
||||
assert idv2 != None
|
||||
assert idv2 is not None
|
||||
except NoOldIdError:
|
||||
idv2 = None
|
||||
if idv1 != idv1output or idv2 != idv2output:
|
||||
@@ -61,10 +72,33 @@ def check(name, input, idv1output=None, idv2output=None, output=None):
|
||||
print("result: %s %s" % (str(idv1).rjust(20), str(idv2).rjust(20)))
|
||||
print("expected: %s %s" % (str(idv1output).rjust(20),
|
||||
str(idv2output).rjust(20)))
|
||||
print(rootSymbol.dump(0))
|
||||
raise DefinitionError("")
|
||||
ids.append(ast.get_id_v2())
|
||||
#print ".. %s:: %s" % (name, input)
|
||||
|
||||
|
||||
def test_fundamental_types():
|
||||
# see http://en.cppreference.com/w/cpp/language/types
|
||||
for t, id_v2 in cppDomain._id_fundamental_v2.items():
|
||||
if t == "decltype(auto)":
|
||||
continue
|
||||
|
||||
def makeIdV1():
|
||||
id = t.replace(" ", "-").replace("long", "l").replace("int", "i")
|
||||
id = id.replace("bool", "b").replace("char", "c")
|
||||
id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t")
|
||||
id = id.replace("c32_t", "char32_t")
|
||||
return "f__%s" % id
|
||||
|
||||
def makeIdV2():
|
||||
id = id_v2
|
||||
if t == "std::nullptr_t":
|
||||
id = "NSt9nullptr_tE"
|
||||
return "1f%s" % id
|
||||
check("function", "void f(%s arg)" % t, makeIdV1(), makeIdV2())
|
||||
|
||||
|
||||
def test_type_definitions():
|
||||
check("type", "public bool b", "b", "1b", "bool b")
|
||||
check("type", "bool A::b", "A::b", "N1A1bE")
|
||||
@@ -84,14 +118,20 @@ def test_type_definitions():
|
||||
check("type", "std::function<R(A1, A2, A3)> F", "F", "1F")
|
||||
check("type", "std::function<R(A1, A2, A3, As...)> F", "F", "1F")
|
||||
check("type", "MyContainer::const_iterator",
|
||||
"MyContainer::const_iterator","N11MyContainer14const_iteratorE")
|
||||
"MyContainer::const_iterator", "N11MyContainer14const_iteratorE")
|
||||
check("type",
|
||||
"public MyContainer::const_iterator",
|
||||
"MyContainer::const_iterator", "N11MyContainer14const_iteratorE",
|
||||
output="MyContainer::const_iterator")
|
||||
# test decl specs on right
|
||||
check("type", "bool const b", "b", "1b")
|
||||
# test name in global scope
|
||||
check("type", "bool ::B::b", "B::b", "N1B1bE")
|
||||
|
||||
check('type', 'A = B', None, '1A')
|
||||
|
||||
|
||||
def test_member_definitions():
|
||||
check('member', ' const std::string & name = 42',
|
||||
"name__ssCR", "4name", output='const std::string &name = 42')
|
||||
check('member', ' const std::string & name', "name__ssCR", "4name",
|
||||
@@ -102,7 +142,10 @@ def test_type_definitions():
|
||||
"name__std::vector:unsigned-i.l:CR",
|
||||
"4name", output='const std::vector<unsigned int, long> &name')
|
||||
check('member', 'module::myclass foo[n]', "foo__module::myclassA", "3foo")
|
||||
check('member', 'int *const p', 'p__iPC', '1p')
|
||||
|
||||
|
||||
def test_function_definitions():
|
||||
check('function', 'operator bool() const', "castto-b-operatorC", "NKcvbEv")
|
||||
check('function', 'A::operator bool() const',
|
||||
"A::castto-b-operatorC", "NK1AcvbEv")
|
||||
@@ -208,31 +251,39 @@ def test_type_definitions():
|
||||
"A::f__doubleC", "NK1A1fEd")
|
||||
check("function", "void f(std::shared_ptr<int(double)> ptr)",
|
||||
None, "1fNSt10shared_ptrIFidEEE")
|
||||
check("function", "void f(int *const p)", "f__iPC", "1fPCi")
|
||||
check("function", "void f(int *volatile const p)", "f__iPVC", "1fPVCi")
|
||||
|
||||
# TODO: make tests for functions in a template, e.g., Test<int&&()>
|
||||
# such that the id generation for function type types is correct.
|
||||
|
||||
check('class', 'public A', "A", "1A", output='A')
|
||||
check('class', 'private A', "A", "1A")
|
||||
check('function', 'friend std::ostream &f(std::ostream&, int)',
|
||||
'f__osR.i', '1fRNSt7ostreamEi')
|
||||
|
||||
check('enum', 'A', None, "1A")
|
||||
check('enum', 'A : std::underlying_type<B>::type', None, "1A")
|
||||
check('enum', 'A : unsigned int', None, "1A")
|
||||
check('enum', 'public A', None, "1A", output='A')
|
||||
check('enum', 'private A', None, "1A")
|
||||
# from breathe#223
|
||||
check('function', 'void f(struct E e)', 'f__E', '1f1E')
|
||||
check('function', 'void f(class E e)', 'f__E', '1f1E')
|
||||
check('function', 'void f(typename E e)', 'f__E', '1f1E')
|
||||
check('function', 'void f(enum E e)', 'f__E', '1f1E')
|
||||
check('function', 'void f(union E e)', 'f__E', '1f1E')
|
||||
|
||||
check('enumerator', 'A', None, "1A")
|
||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()',
|
||||
None, "1A")
|
||||
# pointer to member (function)
|
||||
check('function', 'void f(int C::*)', None, '1fM1Ci')
|
||||
check('function', 'void f(int C::* p)', None, '1fM1Ci')
|
||||
check('function', 'void f(int ::C::* p)', None, '1fM1Ci')
|
||||
check('function', 'void f(int C::* const)', None, '1fKM1Ci')
|
||||
check('function', 'void f(int C::* const&)', None, '1fRKM1Ci')
|
||||
check('function', 'void f(int C::* volatile)', None, '1fVM1Ci')
|
||||
check('function', 'void f(int C::* const volatile)', None, '1fVKM1Ci',
|
||||
output='void f(int C::* volatile const)')
|
||||
check('function', 'void f(int C::* volatile const)', None, '1fVKM1Ci')
|
||||
check('function', 'void f(int (C::*)(float, double))', None, '1fM1CFifdE')
|
||||
check('function', 'void f(int (C::* p)(float, double))', None, '1fM1CFifdE')
|
||||
check('function', 'void f(int (::C::* p)(float, double))', None, '1fM1CFifdE')
|
||||
check('function', 'void f(void (C::*)() const &)', None, '1fM1CKRFvvE')
|
||||
check('function', 'int C::* f(int, double)', None, '1fid')
|
||||
check('function', 'void f(int C::* *)', None, '1fPM1Ci')
|
||||
|
||||
def test_bases():
|
||||
check('class', 'A', "A", "1A")
|
||||
check('class', 'A::B::C', "A::B::C", "N1A1B1CE")
|
||||
check('class', 'A : B', "A", "1A")
|
||||
check('class', 'A : private B', "A", "1A", output='A : B')
|
||||
check('class', 'A : public B', "A", "1A")
|
||||
check('class', 'A : B, C', "A", "1A")
|
||||
check('class', 'A : B, protected C, D', "A", "1A")
|
||||
|
||||
def test_operators():
|
||||
check('function', 'void operator new [ ] ()',
|
||||
@@ -257,6 +308,86 @@ def test_operators():
|
||||
check('function', 'void operator ! ()',
|
||||
"not-operator", "ntv", output='void operator!()')
|
||||
|
||||
check('function', 'void operator "" _udl()',
|
||||
None, 'li4_udlv', output='void operator""_udl()')
|
||||
|
||||
|
||||
def test_class_definitions():
|
||||
check('class', 'public A', "A", "1A", output='A')
|
||||
check('class', 'private A', "A", "1A")
|
||||
check('class', 'A final', 'A', '1A')
|
||||
|
||||
# test bases
|
||||
check('class', 'A', "A", "1A")
|
||||
check('class', 'A::B::C', "A::B::C", "N1A1B1CE")
|
||||
check('class', 'A : B', "A", "1A")
|
||||
check('class', 'A : private B', "A", "1A", output='A : B')
|
||||
check('class', 'A : public B', "A", "1A")
|
||||
check('class', 'A : B, C', "A", "1A")
|
||||
check('class', 'A : B, protected C, D', "A", "1A")
|
||||
check('class', 'A : virtual private B', 'A', '1A', output='A : virtual B')
|
||||
check('class', 'A : B, virtual C', 'A', '1A')
|
||||
check('class', 'A : public virtual B', 'A', '1A')
|
||||
check('class', 'A : B, C...', 'A', '1A')
|
||||
check('class', 'A : B..., C', 'A', '1A')
|
||||
|
||||
|
||||
def test_enum_definitions():
|
||||
check('enum', 'A', None, "1A")
|
||||
check('enum', 'A : std::underlying_type<B>::type', None, "1A")
|
||||
check('enum', 'A : unsigned int', None, "1A")
|
||||
check('enum', 'public A', None, "1A", output='A')
|
||||
check('enum', 'private A', None, "1A")
|
||||
|
||||
check('enumerator', 'A', None, "1A")
|
||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()',
|
||||
None, "1A")
|
||||
|
||||
|
||||
def test_templates():
|
||||
check('class', "A<T>", None, "IE1AI1TE", output="template<> A<T>")
|
||||
# first just check which objects support templating
|
||||
check('class', "template<> A", None, "IE1A")
|
||||
check('function', "template<> void A()", None, "IE1Av")
|
||||
check('member', "template<> A a", None, "IE1a")
|
||||
check('type', "template<> a = A", None, "IE1a")
|
||||
raises(DefinitionError, parse, 'enum', "template<> A")
|
||||
raises(DefinitionError, parse, 'enumerator', "template<> A")
|
||||
# then all the real tests
|
||||
check('class', "template<typename T1, typename T2> A", None, "I00E1A")
|
||||
check('type', "template<> a", None, "IE1a")
|
||||
|
||||
check('class', "template<typename T> A", None, "I0E1A")
|
||||
check('class', "template<class T> A", None, "I0E1A")
|
||||
check('class', "template<typename ...T> A", None, "IDpE1A")
|
||||
check('class', "template<typename...> A", None, "IDpE1A")
|
||||
check('class', "template<typename = Test> A", None, "I0E1A")
|
||||
check('class', "template<typename T = Test> A", None, "I0E1A")
|
||||
|
||||
check('class', "template<template<typename> typename T> A",
|
||||
None, "II0E0E1A")
|
||||
check('class', "template<int> A", None, "I_iE1A")
|
||||
check('class', "template<int T> A", None, "I_iE1A")
|
||||
check('class', "template<int... T> A", None, "I_DpiE1A")
|
||||
check('class', "template<int T = 42> A", None, "I_iE1A")
|
||||
check('class', "template<int = 42> A", None, "I_iE1A")
|
||||
|
||||
# from breathe#218
|
||||
check('function',
|
||||
"template<typename F> "
|
||||
"void allow(F *f, typename func<F, B, G!=1>::type tt)",
|
||||
None, "I0E5allowP1FN4funcI1F1BXG!=1EE4typeE")
|
||||
|
||||
# from #2058
|
||||
check('function',
|
||||
"template<typename Char, typename Traits> "
|
||||
"inline std::basic_ostream<Char, Traits> &operator<<("
|
||||
"std::basic_ostream<Char, Traits> &os, "
|
||||
"const c_string_view_base<const Char, Traits> &str)",
|
||||
None, "I00ElsRNSt13basic_ostreamI4Char6TraitsEE"
|
||||
"RK18c_string_view_baseIK4Char6TraitsE")
|
||||
|
||||
|
||||
#def test_print():
|
||||
# # used for getting all the ids out for checking
|
||||
# for a in ids:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user