Merge branch 'master' into dummy-builder

This commit is contained in:
Buck Evan
2015-12-09 17:07:08 -08:00
112 changed files with 4605 additions and 1172 deletions
+5 -1
View File
@@ -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
+3
View File
@@ -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!
+122 -3
View File
@@ -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)
=====================================
+1
View File
@@ -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 *
+11 -20
View File
@@ -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
+1 -1
View File
@@ -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"
+38
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9 -4
View File
@@ -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
+5 -4
View File
@@ -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
+4
View File
@@ -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
+2 -2
View File
@@ -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).
+1
View File
@@ -110,6 +110,7 @@ All of the following section headers are supported:
* ``Raises``
* ``References``
* ``See Also``
* ``Todo``
* ``Warning``
* ``Warnings`` *(alias of Warning)*
* ``Warns``
+4
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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:
-2
View File
@@ -81,8 +81,6 @@ that has to return the directory with themes in it::
Builtin themes
--------------
.. cssclass:: right
+--------------------+--------------------+
| **Theme overview** | |
+--------------------+--------------------+
+3 -3
View File
@@ -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()
+4
View File
@@ -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
View File
@@ -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)
+8 -1
View File
@@ -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)
+2 -2
View File
@@ -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()
+2 -3
View File
@@ -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
View File
@@ -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)
+12 -1
View File
@@ -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)
+11 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+5 -4
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+17 -7
View File
@@ -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']):
+8 -5
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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}
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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]))
+1 -1
View File
@@ -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),
+9 -9
View File
@@ -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
+22 -9
View File
@@ -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
View File
@@ -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):
+3 -2
View File
@@ -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
+30 -13
View File
@@ -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]
+1
View File
@@ -139,6 +139,7 @@ opmap_raw = """
{ LBRACE
} RBRACE
@ AT
@= ATEQUAL
== EQEQUAL
!= NOTEQUAL
<> NOTEQUAL
+14 -4
View File
@@ -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)
+10 -7
View File
@@ -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--
+14
View File
@@ -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
View File
@@ -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
View File
@@ -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),
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
| 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')
+1
View File
@@ -15,6 +15,7 @@
\RequirePackage{titlesec}
\RequirePackage{tabulary}
\RequirePackage{amsmath} % for \text
\RequirePackage{amssymb} % for some symbols
\RequirePackage{makeidx}
\RequirePackage{framed}
\RequirePackage{ifthen}
+2
View File
@@ -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 {
+38 -10
View File
@@ -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) {
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+4 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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):
+2
View File
@@ -20,6 +20,8 @@ from itertools import product
from six import PY3, text_type, exec_
NoneType = type(None)
# ------------------------------------------------------------------------------
# Python 2/3 compatibility
+1
View File
@@ -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
View File
@@ -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
+8 -6
View File
@@ -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] + ': ')
+15
View File
@@ -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
--------------------
+7 -1
View File
@@ -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
-------
+1 -1
View File
@@ -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
+32
View File
@@ -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)
+4
View File
@@ -0,0 +1,4 @@
===
Bar
===
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
master_doc = 'index'
html_theme = 'classic'
+4
View File
@@ -0,0 +1,4 @@
===
Foo
===
+8
View File
@@ -0,0 +1,8 @@
test-tocdepth
=============
.. toctree::
:caption: Table of content
foo
bar
+9
View File
@@ -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
============================
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
extensions = ['sphinx.ext.graphviz']
master_doc = 'index'
+7
View File
@@ -0,0 +1,7 @@
graphviz
========
.. digraph:: foo
:caption: caption of graph
bar -> baz
+1
View File
@@ -22,3 +22,4 @@ CONTENTS
versionchange
docfields
raw
refs
+10
View File
@@ -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
+85
View File
@@ -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"
+47
View File
@@ -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
-------------
+2
View File
@@ -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
View File
@@ -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')
+346
View File
@@ -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()
+22 -3
View File
@@ -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']/"
+43
View File
@@ -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)
+12
View File
@@ -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
View File
@@ -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')
+6 -6
View File
@@ -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
View File
@@ -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