mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0' into 6785_attr_can_refer_props
This commit is contained in:
commit
ecf7307023
20
CHANGES
20
CHANGES
@ -23,6 +23,17 @@ Deprecated
|
|||||||
* ``sphinx.util.get_module_source()``
|
* ``sphinx.util.get_module_source()``
|
||||||
* ``sphinx.util.inspect.Signature``
|
* ``sphinx.util.inspect.Signature``
|
||||||
* ``sphinx.util.inspect.safe_getmembers()``
|
* ``sphinx.util.inspect.safe_getmembers()``
|
||||||
|
* ``sphinx.writers.latex.LaTeXTranslator.settings.author``
|
||||||
|
* ``sphinx.writers.latex.LaTeXTranslator.settings.contentsname``
|
||||||
|
* ``sphinx.writers.latex.LaTeXTranslator.settings.docclass``
|
||||||
|
* ``sphinx.writers.latex.LaTeXTranslator.settings.docname``
|
||||||
|
* ``sphinx.writers.latex.LaTeXTranslator.settings.title``
|
||||||
|
* ``sphinx.writers.latex.ADDITIONAL_SETTINGS``
|
||||||
|
* ``sphinx.writers.latex.DEFAULT_SETTINGS``
|
||||||
|
* ``sphinx.writers.latex.LUALATEX_DEFAULT_FONTPKG``
|
||||||
|
* ``sphinx.writers.latex.PDFLATEX_DEFAULT_FONTPKG``
|
||||||
|
* ``sphinx.writers.latex.XELATEX_DEFAULT_FONTPKG``
|
||||||
|
* ``sphinx.writers.latex.XELATEX_GREEK_DEFAULT_FONTPKG``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
@ -36,6 +47,7 @@ Features added
|
|||||||
images (imagesize-1.2.0 or above is required)
|
images (imagesize-1.2.0 or above is required)
|
||||||
* #6994: imgconverter: Support illustrator file (.ai) to .png conversion
|
* #6994: imgconverter: Support illustrator file (.ai) to .png conversion
|
||||||
* autodoc: Support Positional-Only Argument separator (PEP-570 compliant)
|
* autodoc: Support Positional-Only Argument separator (PEP-570 compliant)
|
||||||
|
* autodoc: Support type annotations for variables
|
||||||
* #2755: autodoc: Add new event: :event:`autodoc-before-process-signature`
|
* #2755: autodoc: Add new event: :event:`autodoc-before-process-signature`
|
||||||
* #2755: autodoc: Support type_comment style (ex. ``# type: (str) -> str``)
|
* #2755: autodoc: Support type_comment style (ex. ``# type: (str) -> str``)
|
||||||
annotation (python3.8+ or `typed_ast <https://github.com/python/typed_ast>`_
|
annotation (python3.8+ or `typed_ast <https://github.com/python/typed_ast>`_
|
||||||
@ -48,7 +60,10 @@ Features added
|
|||||||
* SphinxTranslator now calls visitor/departure method for super node class if
|
* SphinxTranslator now calls visitor/departure method for super node class if
|
||||||
visitor/departure method for original node class not found
|
visitor/departure method for original node class not found
|
||||||
* #6418: Add new event: :event:`object-description-transform`
|
* #6418: Add new event: :event:`object-description-transform`
|
||||||
|
* py domain: :rst:dir:`py:data` and :rst:dir:`py:attribute` take new options
|
||||||
|
named ``:type:`` and ``:value:`` to describe its type and initial value
|
||||||
* #6785: py domain: ``:py:attr:`` is able to refer properties again
|
* #6785: py domain: ``:py:attr:`` is able to refer properties again
|
||||||
|
* #6772: apidoc: Add ``-q`` option for quiet mode
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
@ -56,13 +71,18 @@ Bugs fixed
|
|||||||
* #6925: html: Remove redundant type="text/javascript" from <script> elements
|
* #6925: html: Remove redundant type="text/javascript" from <script> elements
|
||||||
* #6906, #6907: autodoc: failed to read the source codes encoeded in cp1251
|
* #6906, #6907: autodoc: failed to read the source codes encoeded in cp1251
|
||||||
* #6961: latex: warning for babel shown twice
|
* #6961: latex: warning for babel shown twice
|
||||||
|
* #7059: latex: LaTeX compilation falls into infinite loop (wrapfig issue)
|
||||||
* #6559: Wrong node-ids are generated in glossary directive
|
* #6559: Wrong node-ids are generated in glossary directive
|
||||||
* #6986: apidoc: misdetects module name for .so file inside module
|
* #6986: apidoc: misdetects module name for .so file inside module
|
||||||
|
* #6899: apidoc: private members are not shown even if ``--private`` given
|
||||||
* #6999: napoleon: fails to parse tilde in :exc: role
|
* #6999: napoleon: fails to parse tilde in :exc: role
|
||||||
* #7019: gettext: Absolute path used in message catalogs
|
* #7019: gettext: Absolute path used in message catalogs
|
||||||
* #7023: autodoc: nested partial functions are not listed
|
* #7023: autodoc: nested partial functions are not listed
|
||||||
* #7023: autodoc: partial functions imported from other modules are listed as
|
* #7023: autodoc: partial functions imported from other modules are listed as
|
||||||
module members without :impoprted-members: option
|
module members without :impoprted-members: option
|
||||||
|
* #6889: autodoc: Trailing comma in ``:members::`` option causes cryptic warning
|
||||||
|
* #7055: linkcheck: redirect is treated as an error
|
||||||
|
* #7090: std domain: Can't assign numfig-numbers for custom container nodes
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -92,6 +92,61 @@ The following is a list of deprecated interfaces.
|
|||||||
- 4.0
|
- 4.0
|
||||||
- ``inspect.getmembers()``
|
- ``inspect.getmembers()``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LaTeXTranslator.settings.author``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LaTeXTranslator.settings.contentsname``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``document['contentsname']``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LaTeXTranslator.settings.docclass``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``document['docclass']``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LaTeXTranslator.settings.docname``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LaTeXTranslator.settings.title``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.ADDITIONAL_SETTINGS``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.builders.latex.constants.ADDITIONAL_SETTINGS``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.DEFAULT_SETTINGS``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.builders.latex.constants.DEFAULT_SETTINGS``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.LUALATEX_DEFAULT_FONTPKG``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.builders.latex.constants.LUALATEX_DEFAULT_FONTPKG``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.PDFLATEX_DEFAULT_FONTPKG``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.builders.latex.constants.PDFLATEX_DEFAULT_FONTPKG``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.XELATEX_DEFAULT_FONTPKG``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.builders.latex.constants.XELATEX_DEFAULT_FONTPKG``
|
||||||
|
|
||||||
|
* - ``sphinx.writers.latex.XELATEX_GREEK_DEFAULT_FONTPKG``
|
||||||
|
- 2.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.builders.latex.constants.XELATEX_GREEK_DEFAULT_FONTPKG``
|
||||||
|
|
||||||
* - ``sphinx.builders.gettext.POHEADER``
|
* - ``sphinx.builders.gettext.POHEADER``
|
||||||
- 2.3
|
- 2.3
|
||||||
- 4.0
|
- 4.0
|
||||||
|
@ -39,6 +39,11 @@ Options
|
|||||||
|
|
||||||
Directory to place the output files. If it does not exist, it is created.
|
Directory to place the output files. If it does not exist, it is created.
|
||||||
|
|
||||||
|
.. option:: -q
|
||||||
|
|
||||||
|
Do not output anything on standard output, only write warnings and errors to
|
||||||
|
standard error.
|
||||||
|
|
||||||
.. option:: -f, --force
|
.. option:: -f, --force
|
||||||
|
|
||||||
Force overwriting of any existing generated files.
|
Force overwriting of any existing generated files.
|
||||||
|
@ -195,6 +195,18 @@ The following directives are provided for module and class contents:
|
|||||||
as "defined constants." Class and object attributes are not documented
|
as "defined constants." Class and object attributes are not documented
|
||||||
using this environment.
|
using this environment.
|
||||||
|
|
||||||
|
.. rubric:: options
|
||||||
|
|
||||||
|
.. rst:directive:option:: type: type of the variable
|
||||||
|
:type: text
|
||||||
|
|
||||||
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
|
.. rst:directive:option:: value: initial value of the variable
|
||||||
|
:type: text
|
||||||
|
|
||||||
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
.. rst:directive:: .. py:exception:: name
|
.. rst:directive:: .. py:exception:: name
|
||||||
|
|
||||||
Describes an exception class. The signature can, but need not include
|
Describes an exception class. The signature can, but need not include
|
||||||
@ -229,6 +241,18 @@ The following directives are provided for module and class contents:
|
|||||||
information about the type of the data to be expected and whether it may be
|
information about the type of the data to be expected and whether it may be
|
||||||
changed directly.
|
changed directly.
|
||||||
|
|
||||||
|
.. rubric:: options
|
||||||
|
|
||||||
|
.. rst:directive:option:: type: type of the attribute
|
||||||
|
:type: text
|
||||||
|
|
||||||
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
|
.. rst:directive:option:: value: initial value of the attribute
|
||||||
|
:type: text
|
||||||
|
|
||||||
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
.. rst:directive:: .. py:method:: name(parameters)
|
.. rst:directive:: .. py:method:: name(parameters)
|
||||||
|
|
||||||
Describes an object method. The parameters should not include the ``self``
|
Describes an object method. The parameters should not include the ``self``
|
||||||
|
@ -20,6 +20,7 @@ import sphinx.builders.latex.nodes # NOQA # Workaround: import this before wri
|
|||||||
from sphinx import package_dir, addnodes, highlighting
|
from sphinx import package_dir, addnodes, highlighting
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
|
from sphinx.builders.latex.constants import ADDITIONAL_SETTINGS, DEFAULT_SETTINGS
|
||||||
from sphinx.builders.latex.util import ExtBabel
|
from sphinx.builders.latex.util import ExtBabel
|
||||||
from sphinx.config import Config, ENUM
|
from sphinx.config import Config, ENUM
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
@ -34,9 +35,7 @@ from sphinx.util.i18n import format_date
|
|||||||
from sphinx.util.nodes import inline_all_toctrees
|
from sphinx.util.nodes import inline_all_toctrees
|
||||||
from sphinx.util.osutil import SEP, make_filename_from_project
|
from sphinx.util.osutil import SEP, make_filename_from_project
|
||||||
from sphinx.util.template import LaTeXRenderer
|
from sphinx.util.template import LaTeXRenderer
|
||||||
from sphinx.writers.latex import (
|
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
|
||||||
ADDITIONAL_SETTINGS, DEFAULT_SETTINGS, LaTeXWriter, LaTeXTranslator
|
|
||||||
)
|
|
||||||
|
|
||||||
# load docutils.nodes after loading sphinx.builders.latex.nodes
|
# load docutils.nodes after loading sphinx.builders.latex.nodes
|
||||||
from docutils import nodes # NOQA
|
from docutils import nodes # NOQA
|
||||||
@ -222,6 +221,7 @@ class LaTeXBuilder(Builder):
|
|||||||
defaults=self.env.settings,
|
defaults=self.env.settings,
|
||||||
components=(docwriter,),
|
components=(docwriter,),
|
||||||
read_config_files=True).get_default_values() # type: Any
|
read_config_files=True).get_default_values() # type: Any
|
||||||
|
patch_settings(docsettings)
|
||||||
|
|
||||||
self.init_document_data()
|
self.init_document_data()
|
||||||
self.write_stylesheet()
|
self.write_stylesheet()
|
||||||
@ -244,16 +244,18 @@ class LaTeXBuilder(Builder):
|
|||||||
doctree = self.assemble_doctree(
|
doctree = self.assemble_doctree(
|
||||||
docname, toctree_only,
|
docname, toctree_only,
|
||||||
appendices=(self.config.latex_appendices if docclass != 'howto' else []))
|
appendices=(self.config.latex_appendices if docclass != 'howto' else []))
|
||||||
|
doctree['docclass'] = docclass
|
||||||
|
doctree['contentsname'] = self.get_contentsname(docname)
|
||||||
doctree['tocdepth'] = tocdepth
|
doctree['tocdepth'] = tocdepth
|
||||||
self.post_process_images(doctree)
|
self.post_process_images(doctree)
|
||||||
self.update_doc_context(title, author)
|
self.update_doc_context(title, author)
|
||||||
|
|
||||||
with progress_message(__("writing")):
|
with progress_message(__("writing")):
|
||||||
docsettings.author = author
|
docsettings._author = author
|
||||||
docsettings.title = title
|
docsettings._title = title
|
||||||
docsettings.contentsname = self.get_contentsname(docname)
|
docsettings._contentsname = doctree['contentsname']
|
||||||
docsettings.docname = docname
|
docsettings._docname = docname
|
||||||
docsettings.docclass = docclass
|
docsettings._docclass = docclass
|
||||||
|
|
||||||
doctree.settings = docsettings
|
doctree.settings = docsettings
|
||||||
docwriter.write(doctree, destination)
|
docwriter.write(doctree, destination)
|
||||||
@ -401,6 +403,44 @@ class LaTeXBuilder(Builder):
|
|||||||
copy_asset_file(filename, self.outdir, context=context, renderer=LaTeXRenderer())
|
copy_asset_file(filename, self.outdir, context=context, renderer=LaTeXRenderer())
|
||||||
|
|
||||||
|
|
||||||
|
def patch_settings(settings: Any) -> Any:
|
||||||
|
"""Make settings object to show deprecation messages."""
|
||||||
|
|
||||||
|
class Values(type(settings)): # type: ignore
|
||||||
|
@property
|
||||||
|
def author(self):
|
||||||
|
warnings.warn('settings.author is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
return self._author
|
||||||
|
|
||||||
|
@property
|
||||||
|
def title(self):
|
||||||
|
warnings.warn('settings.title is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
return self._title
|
||||||
|
|
||||||
|
@property
|
||||||
|
def contentsname(self):
|
||||||
|
warnings.warn('settings.contentsname is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
return self._contentsname
|
||||||
|
|
||||||
|
@property
|
||||||
|
def docname(self):
|
||||||
|
warnings.warn('settings.docname is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
return self._docname
|
||||||
|
|
||||||
|
@property
|
||||||
|
def docclass(self):
|
||||||
|
warnings.warn('settings.docclass is deprecated',
|
||||||
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
return self._docclass
|
||||||
|
|
||||||
|
# dynamic subclassing
|
||||||
|
settings.__class__ = Values
|
||||||
|
|
||||||
|
|
||||||
def validate_config_values(app: Sphinx, config: Config) -> None:
|
def validate_config_values(app: Sphinx, config: Config) -> None:
|
||||||
for key in list(config.latex_elements):
|
for key in list(config.latex_elements):
|
||||||
if key not in DEFAULT_SETTINGS:
|
if key not in DEFAULT_SETTINGS:
|
||||||
|
192
sphinx/builders/latex/constants.py
Normal file
192
sphinx/builders/latex/constants.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
"""
|
||||||
|
sphinx.builders.latex.constants
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
consntants for LaTeX builder.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
|
PDFLATEX_DEFAULT_FONTPKG = r'''
|
||||||
|
\usepackage{times}
|
||||||
|
\expandafter\ifx\csname T@LGR\endcsname\relax
|
||||||
|
\else
|
||||||
|
% LGR was declared as font encoding
|
||||||
|
\substitutefont{LGR}{\rmdefault}{cmr}
|
||||||
|
\substitutefont{LGR}{\sfdefault}{cmss}
|
||||||
|
\substitutefont{LGR}{\ttdefault}{cmtt}
|
||||||
|
\fi
|
||||||
|
\expandafter\ifx\csname T@X2\endcsname\relax
|
||||||
|
\expandafter\ifx\csname T@T2A\endcsname\relax
|
||||||
|
\else
|
||||||
|
% T2A was declared as font encoding
|
||||||
|
\substitutefont{T2A}{\rmdefault}{cmr}
|
||||||
|
\substitutefont{T2A}{\sfdefault}{cmss}
|
||||||
|
\substitutefont{T2A}{\ttdefault}{cmtt}
|
||||||
|
\fi
|
||||||
|
\else
|
||||||
|
% X2 was declared as font encoding
|
||||||
|
\substitutefont{X2}{\rmdefault}{cmr}
|
||||||
|
\substitutefont{X2}{\sfdefault}{cmss}
|
||||||
|
\substitutefont{X2}{\ttdefault}{cmtt}
|
||||||
|
\fi
|
||||||
|
'''
|
||||||
|
|
||||||
|
XELATEX_DEFAULT_FONTPKG = r'''
|
||||||
|
\setmainfont{FreeSerif}[
|
||||||
|
Extension = .otf,
|
||||||
|
UprightFont = *,
|
||||||
|
ItalicFont = *Italic,
|
||||||
|
BoldFont = *Bold,
|
||||||
|
BoldItalicFont = *BoldItalic
|
||||||
|
]
|
||||||
|
\setsansfont{FreeSans}[
|
||||||
|
Extension = .otf,
|
||||||
|
UprightFont = *,
|
||||||
|
ItalicFont = *Oblique,
|
||||||
|
BoldFont = *Bold,
|
||||||
|
BoldItalicFont = *BoldOblique,
|
||||||
|
]
|
||||||
|
\setmonofont{FreeMono}[
|
||||||
|
Extension = .otf,
|
||||||
|
UprightFont = *,
|
||||||
|
ItalicFont = *Oblique,
|
||||||
|
BoldFont = *Bold,
|
||||||
|
BoldItalicFont = *BoldOblique,
|
||||||
|
]
|
||||||
|
'''
|
||||||
|
|
||||||
|
XELATEX_GREEK_DEFAULT_FONTPKG = (XELATEX_DEFAULT_FONTPKG +
|
||||||
|
'\n\\newfontfamily\\greekfont{FreeSerif}' +
|
||||||
|
'\n\\newfontfamily\\greekfontsf{FreeSans}' +
|
||||||
|
'\n\\newfontfamily\\greekfonttt{FreeMono}')
|
||||||
|
|
||||||
|
LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG
|
||||||
|
|
||||||
|
DEFAULT_SETTINGS = {
|
||||||
|
'latex_engine': 'pdflatex',
|
||||||
|
'papersize': 'letterpaper',
|
||||||
|
'pointsize': '10pt',
|
||||||
|
'pxunit': '.75bp',
|
||||||
|
'classoptions': '',
|
||||||
|
'extraclassoptions': '',
|
||||||
|
'maxlistdepth': '',
|
||||||
|
'sphinxpkgoptions': '',
|
||||||
|
'sphinxsetup': '',
|
||||||
|
'fvset': '\\fvset{fontsize=\\small}',
|
||||||
|
'passoptionstopackages': '',
|
||||||
|
'geometry': '\\usepackage{geometry}',
|
||||||
|
'inputenc': '',
|
||||||
|
'utf8extra': '',
|
||||||
|
'cmappkg': '\\usepackage{cmap}',
|
||||||
|
'fontenc': '\\usepackage[T1]{fontenc}',
|
||||||
|
'amsmath': '\\usepackage{amsmath,amssymb,amstext}',
|
||||||
|
'multilingual': '',
|
||||||
|
'babel': '\\usepackage{babel}',
|
||||||
|
'polyglossia': '',
|
||||||
|
'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
|
||||||
|
'substitutefont': '',
|
||||||
|
'textcyrillic': '',
|
||||||
|
'textgreek': '\\usepackage{textalpha}',
|
||||||
|
'fncychap': '\\usepackage[Bjarne]{fncychap}',
|
||||||
|
'hyperref': ('% Include hyperref last.\n'
|
||||||
|
'\\usepackage{hyperref}\n'
|
||||||
|
'% Fix anchor placement for figures with captions.\n'
|
||||||
|
'\\usepackage{hypcap}% it must be loaded after hyperref.\n'
|
||||||
|
'% Set up styles of URL: it should be placed after hyperref.\n'
|
||||||
|
'\\urlstyle{same}'),
|
||||||
|
'contentsname': '',
|
||||||
|
'extrapackages': '',
|
||||||
|
'preamble': '',
|
||||||
|
'title': '',
|
||||||
|
'release': '',
|
||||||
|
'author': '',
|
||||||
|
'releasename': '',
|
||||||
|
'makeindex': '\\makeindex',
|
||||||
|
'shorthandoff': '',
|
||||||
|
'maketitle': '\\sphinxmaketitle',
|
||||||
|
'tableofcontents': '\\sphinxtableofcontents',
|
||||||
|
'atendofbody': '',
|
||||||
|
'printindex': '\\printindex',
|
||||||
|
'transition': '\n\n\\bigskip\\hrule\\bigskip\n\n',
|
||||||
|
'figure_align': 'htbp',
|
||||||
|
'tocdepth': '',
|
||||||
|
'secnumdepth': '',
|
||||||
|
} # type: Dict[str, Any]
|
||||||
|
|
||||||
|
ADDITIONAL_SETTINGS = {
|
||||||
|
'pdflatex': {
|
||||||
|
'inputenc': '\\usepackage[utf8]{inputenc}',
|
||||||
|
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
|
||||||
|
'% support both utf8 and utf8x syntaxes\n'
|
||||||
|
' \\ifdefined\\DeclareUnicodeCharacterAsOptional\n'
|
||||||
|
' \\def\\sphinxDUC#1{\\DeclareUnicodeCharacter{"#1}}\n'
|
||||||
|
' \\else\n'
|
||||||
|
' \\let\\sphinxDUC\\DeclareUnicodeCharacter\n'
|
||||||
|
' \\fi\n'
|
||||||
|
' \\sphinxDUC{00A0}{\\nobreakspace}\n'
|
||||||
|
' \\sphinxDUC{2500}{\\sphinxunichar{2500}}\n'
|
||||||
|
' \\sphinxDUC{2502}{\\sphinxunichar{2502}}\n'
|
||||||
|
' \\sphinxDUC{2514}{\\sphinxunichar{2514}}\n'
|
||||||
|
' \\sphinxDUC{251C}{\\sphinxunichar{251C}}\n'
|
||||||
|
' \\sphinxDUC{2572}{\\textbackslash}\n'
|
||||||
|
'\\fi'),
|
||||||
|
},
|
||||||
|
'xelatex': {
|
||||||
|
'latex_engine': 'xelatex',
|
||||||
|
'polyglossia': '\\usepackage{polyglossia}',
|
||||||
|
'babel': '',
|
||||||
|
'fontenc': ('\\usepackage{fontspec}\n'
|
||||||
|
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
|
||||||
|
'fontpkg': XELATEX_DEFAULT_FONTPKG,
|
||||||
|
'textgreek': '',
|
||||||
|
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
|
||||||
|
'{\\leavevmode\\nobreak\\ }'),
|
||||||
|
},
|
||||||
|
'lualatex': {
|
||||||
|
'latex_engine': 'lualatex',
|
||||||
|
'polyglossia': '\\usepackage{polyglossia}',
|
||||||
|
'babel': '',
|
||||||
|
'fontenc': ('\\usepackage{fontspec}\n'
|
||||||
|
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
|
||||||
|
'fontpkg': LUALATEX_DEFAULT_FONTPKG,
|
||||||
|
'textgreek': '',
|
||||||
|
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
|
||||||
|
'{\\leavevmode\\nobreak\\ }'),
|
||||||
|
},
|
||||||
|
'platex': {
|
||||||
|
'latex_engine': 'platex',
|
||||||
|
'babel': '',
|
||||||
|
'classoptions': ',dvipdfmx',
|
||||||
|
'fontpkg': '\\usepackage{times}',
|
||||||
|
'textgreek': '',
|
||||||
|
'fncychap': '',
|
||||||
|
'geometry': '\\usepackage[dvipdfm]{geometry}',
|
||||||
|
},
|
||||||
|
'uplatex': {
|
||||||
|
'latex_engine': 'uplatex',
|
||||||
|
'babel': '',
|
||||||
|
'classoptions': ',dvipdfmx',
|
||||||
|
'fontpkg': '\\usepackage{times}',
|
||||||
|
'textgreek': '',
|
||||||
|
'fncychap': '',
|
||||||
|
'geometry': '\\usepackage[dvipdfm]{geometry}',
|
||||||
|
},
|
||||||
|
|
||||||
|
# special settings for latex_engine + language_code
|
||||||
|
('xelatex', 'fr'): {
|
||||||
|
# use babel instead of polyglossia by default
|
||||||
|
'polyglossia': '',
|
||||||
|
'babel': '\\usepackage{babel}',
|
||||||
|
},
|
||||||
|
('xelatex', 'zh'): {
|
||||||
|
'fontenc': '\\usepackage{xeCJK}',
|
||||||
|
},
|
||||||
|
('xelatex', 'el'): {
|
||||||
|
'fontpkg': XELATEX_GREEK_DEFAULT_FONTPKG,
|
||||||
|
},
|
||||||
|
} # type: Dict[Any, Dict[str, Any]]
|
@ -26,7 +26,7 @@ from sphinx.builders import Builder
|
|||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.util import encode_uri, requests, logging
|
from sphinx.util import encode_uri, requests, logging
|
||||||
from sphinx.util.console import ( # type: ignore
|
from sphinx.util.console import ( # type: ignore
|
||||||
purple, red, darkgreen, darkgray, darkred, turquoise
|
purple, red, darkgreen, darkgray, turquoise
|
||||||
)
|
)
|
||||||
from sphinx.util.nodes import get_node_line
|
from sphinx.util.nodes import get_node_line
|
||||||
from sphinx.util.requests import is_ssl_error
|
from sphinx.util.requests import is_ssl_error
|
||||||
@ -251,11 +251,11 @@ class CheckExternalLinksBuilder(Builder):
|
|||||||
elif status == 'redirected':
|
elif status == 'redirected':
|
||||||
try:
|
try:
|
||||||
text, color = {
|
text, color = {
|
||||||
301: ('permanently', darkred),
|
301: ('permanently', purple),
|
||||||
302: ('with Found', purple),
|
302: ('with Found', purple),
|
||||||
303: ('with See Other', purple),
|
303: ('with See Other', purple),
|
||||||
307: ('temporarily', turquoise),
|
307: ('temporarily', turquoise),
|
||||||
308: ('permanently', darkred),
|
308: ('permanently', purple),
|
||||||
}[code]
|
}[code]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
text, color = ('with unknown code', purple)
|
text, color = ('with unknown code', purple)
|
||||||
|
@ -446,6 +446,25 @@ class PyFunction(PyObject):
|
|||||||
class PyVariable(PyObject):
|
class PyVariable(PyObject):
|
||||||
"""Description of a variable."""
|
"""Description of a variable."""
|
||||||
|
|
||||||
|
option_spec = PyObject.option_spec.copy()
|
||||||
|
option_spec.update({
|
||||||
|
'type': directives.unchanged,
|
||||||
|
'value': directives.unchanged,
|
||||||
|
})
|
||||||
|
|
||||||
|
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
|
||||||
|
fullname, prefix = super().handle_signature(sig, signode)
|
||||||
|
|
||||||
|
typ = self.options.get('type')
|
||||||
|
if typ:
|
||||||
|
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
||||||
|
|
||||||
|
value = self.options.get('value')
|
||||||
|
if value:
|
||||||
|
signode += addnodes.desc_annotation(value, ' = ' + value)
|
||||||
|
|
||||||
|
return fullname, prefix
|
||||||
|
|
||||||
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
||||||
name, cls = name_cls
|
name, cls = name_cls
|
||||||
if modname:
|
if modname:
|
||||||
@ -638,6 +657,25 @@ class PyStaticMethod(PyMethod):
|
|||||||
class PyAttribute(PyObject):
|
class PyAttribute(PyObject):
|
||||||
"""Description of an attribute."""
|
"""Description of an attribute."""
|
||||||
|
|
||||||
|
option_spec = PyObject.option_spec.copy()
|
||||||
|
option_spec.update({
|
||||||
|
'type': directives.unchanged,
|
||||||
|
'value': directives.unchanged,
|
||||||
|
})
|
||||||
|
|
||||||
|
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
|
||||||
|
fullname, prefix = super().handle_signature(sig, signode)
|
||||||
|
|
||||||
|
typ = self.options.get('type')
|
||||||
|
if typ:
|
||||||
|
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
||||||
|
|
||||||
|
value = self.options.get('value')
|
||||||
|
if value:
|
||||||
|
signode += addnodes.desc_annotation(value, ' = ' + value)
|
||||||
|
|
||||||
|
return fullname, prefix
|
||||||
|
|
||||||
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
||||||
name, cls = name_cls
|
name, cls = name_cls
|
||||||
try:
|
try:
|
||||||
|
@ -925,11 +925,11 @@ class StandardDomain(Domain):
|
|||||||
|
|
||||||
if isinstance(node, nodes.section):
|
if isinstance(node, nodes.section):
|
||||||
return 'section'
|
return 'section'
|
||||||
elif isinstance(node, nodes.container):
|
elif (isinstance(node, nodes.container) and
|
||||||
if node.get('literal_block') and has_child(node, nodes.literal_block):
|
'literal_block' in node and
|
||||||
return 'code-block'
|
has_child(node, nodes.literal_block)):
|
||||||
else:
|
# given node is a code-block having caption
|
||||||
return None
|
return 'code-block'
|
||||||
else:
|
else:
|
||||||
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
|
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
|
||||||
return figtype
|
return figtype
|
||||||
|
@ -20,6 +20,7 @@ import locale
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
from copy import copy
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from importlib.machinery import EXTENSION_SUFFIXES
|
from importlib.machinery import EXTENSION_SUFFIXES
|
||||||
from os import path
|
from os import path
|
||||||
@ -72,14 +73,19 @@ def module_join(*modnames: str) -> str:
|
|||||||
|
|
||||||
def write_file(name: str, text: str, opts: Any) -> None:
|
def write_file(name: str, text: str, opts: Any) -> None:
|
||||||
"""Write the output file for module/package <name>."""
|
"""Write the output file for module/package <name>."""
|
||||||
|
quiet = getattr(opts, 'quiet', None)
|
||||||
|
|
||||||
fname = path.join(opts.destdir, '%s.%s' % (name, opts.suffix))
|
fname = path.join(opts.destdir, '%s.%s' % (name, opts.suffix))
|
||||||
if opts.dryrun:
|
if opts.dryrun:
|
||||||
print(__('Would create file %s.') % fname)
|
if not quiet:
|
||||||
|
print(__('Would create file %s.') % fname)
|
||||||
return
|
return
|
||||||
if not opts.force and path.isfile(fname):
|
if not opts.force and path.isfile(fname):
|
||||||
print(__('File %s already exists, skipping.') % fname)
|
if not quiet:
|
||||||
|
print(__('File %s already exists, skipping.') % fname)
|
||||||
else:
|
else:
|
||||||
print(__('Creating file %s.') % fname)
|
if not quiet:
|
||||||
|
print(__('Creating file %s.') % fname)
|
||||||
with FileAvoidWrite(fname) as f:
|
with FileAvoidWrite(fname) as f:
|
||||||
f.write(text)
|
f.write(text)
|
||||||
|
|
||||||
@ -107,12 +113,16 @@ def format_directive(module: str, package: str = None) -> str:
|
|||||||
def create_module_file(package: str, basename: str, opts: Any,
|
def create_module_file(package: str, basename: str, opts: Any,
|
||||||
user_template_dir: str = None) -> None:
|
user_template_dir: str = None) -> None:
|
||||||
"""Build the text of the file and write the file."""
|
"""Build the text of the file and write the file."""
|
||||||
|
options = copy(OPTIONS)
|
||||||
|
if opts.includeprivate and 'private-members' not in options:
|
||||||
|
options.append('private-members')
|
||||||
|
|
||||||
qualname = module_join(package, basename)
|
qualname = module_join(package, basename)
|
||||||
context = {
|
context = {
|
||||||
'show_headings': not opts.noheadings,
|
'show_headings': not opts.noheadings,
|
||||||
'basename': basename,
|
'basename': basename,
|
||||||
'qualname': qualname,
|
'qualname': qualname,
|
||||||
'automodule_options': OPTIONS,
|
'automodule_options': options,
|
||||||
}
|
}
|
||||||
text = ReSTRenderer([user_template_dir, template_dir]).render('module.rst_t', context)
|
text = ReSTRenderer([user_template_dir, template_dir]).render('module.rst_t', context)
|
||||||
write_file(qualname, text, opts)
|
write_file(qualname, text, opts)
|
||||||
@ -133,6 +143,9 @@ def create_package_file(root: str, master_package: str, subroot: str, py_files:
|
|||||||
sub != INITPY]
|
sub != INITPY]
|
||||||
submodules = [module_join(master_package, subroot, modname)
|
submodules = [module_join(master_package, subroot, modname)
|
||||||
for modname in submodules]
|
for modname in submodules]
|
||||||
|
options = copy(OPTIONS)
|
||||||
|
if opts.includeprivate and 'private-members' not in options:
|
||||||
|
options.append('private-members')
|
||||||
|
|
||||||
pkgname = module_join(master_package, subroot)
|
pkgname = module_join(master_package, subroot)
|
||||||
context = {
|
context = {
|
||||||
@ -142,7 +155,7 @@ def create_package_file(root: str, master_package: str, subroot: str, py_files:
|
|||||||
'is_namespace': is_namespace,
|
'is_namespace': is_namespace,
|
||||||
'modulefirst': opts.modulefirst,
|
'modulefirst': opts.modulefirst,
|
||||||
'separatemodules': opts.separatemodules,
|
'separatemodules': opts.separatemodules,
|
||||||
'automodule_options': OPTIONS,
|
'automodule_options': options,
|
||||||
'show_headings': not opts.noheadings,
|
'show_headings': not opts.noheadings,
|
||||||
}
|
}
|
||||||
text = ReSTRenderer([user_template_dir, template_dir]).render('package.rst_t', context)
|
text = ReSTRenderer([user_template_dir, template_dir]).render('package.rst_t', context)
|
||||||
@ -316,6 +329,8 @@ Note: By default this script will not overwrite already created files."""))
|
|||||||
parser.add_argument('-o', '--output-dir', action='store', dest='destdir',
|
parser.add_argument('-o', '--output-dir', action='store', dest='destdir',
|
||||||
required=True,
|
required=True,
|
||||||
help=__('directory to place all output'))
|
help=__('directory to place all output'))
|
||||||
|
parser.add_argument('-q', action='store_true', dest='quiet',
|
||||||
|
help=__('no output on stdout, just warnings on stderr'))
|
||||||
parser.add_argument('-d', '--maxdepth', action='store', dest='maxdepth',
|
parser.add_argument('-d', '--maxdepth', action='store', dest='maxdepth',
|
||||||
type=int, default=4,
|
type=int, default=4,
|
||||||
help=__('maximum depth of submodules to show in the TOC '
|
help=__('maximum depth of submodules to show in the TOC '
|
||||||
@ -443,6 +458,8 @@ def main(argv: List[str] = sys.argv[1:]) -> int:
|
|||||||
}
|
}
|
||||||
if args.extensions:
|
if args.extensions:
|
||||||
d['extensions'].extend(args.extensions)
|
d['extensions'].extend(args.extensions)
|
||||||
|
if args.quiet:
|
||||||
|
d['quiet'] = True
|
||||||
|
|
||||||
for ext in d['extensions'][:]:
|
for ext in d['extensions'][:]:
|
||||||
if ',' in ext:
|
if ',' in ext:
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import importlib
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
@ -33,6 +34,7 @@ from sphinx.util import logging
|
|||||||
from sphinx.util import rpartition
|
from sphinx.util import rpartition
|
||||||
from sphinx.util.docstrings import prepare_docstring
|
from sphinx.util.docstrings import prepare_docstring
|
||||||
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
|
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
|
||||||
|
from sphinx.util.typing import stringify as stringify_typehint
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -72,14 +74,14 @@ def members_option(arg: Any) -> Union[object, List[str]]:
|
|||||||
"""Used to convert the :members: option to auto directives."""
|
"""Used to convert the :members: option to auto directives."""
|
||||||
if arg is None or arg is True:
|
if arg is None or arg is True:
|
||||||
return ALL
|
return ALL
|
||||||
return [x.strip() for x in arg.split(',')]
|
return [x.strip() for x in arg.split(',') if x.strip()]
|
||||||
|
|
||||||
|
|
||||||
def members_set_option(arg: Any) -> Union[object, Set[str]]:
|
def members_set_option(arg: Any) -> Union[object, Set[str]]:
|
||||||
"""Used to convert the :members: option to auto directives."""
|
"""Used to convert the :members: option to auto directives."""
|
||||||
if arg is None:
|
if arg is None:
|
||||||
return ALL
|
return ALL
|
||||||
return {x.strip() for x in arg.split(',')}
|
return {x.strip() for x in arg.split(',') if x.strip()}
|
||||||
|
|
||||||
|
|
||||||
SUPPRESS = object()
|
SUPPRESS = object()
|
||||||
@ -1232,12 +1234,22 @@ class DataDocumenter(ModuleLevelDocumenter):
|
|||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
if not self.options.annotation:
|
if not self.options.annotation:
|
||||||
|
# obtain annotation for this data
|
||||||
|
annotations = getattr(self.parent, '__annotations__', {})
|
||||||
|
if self.objpath[-1] in annotations:
|
||||||
|
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
||||||
|
self.add_line(' :type: ' + objrepr, sourcename)
|
||||||
|
else:
|
||||||
|
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
||||||
|
if self.analyzer and key in self.analyzer.annotations:
|
||||||
|
self.add_line(' :type: ' + self.analyzer.annotations[key],
|
||||||
|
sourcename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
objrepr = object_description(self.object)
|
objrepr = object_description(self.object)
|
||||||
|
self.add_line(' :value: ' + objrepr, sourcename)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
|
||||||
self.add_line(' :annotation: = ' + objrepr, sourcename)
|
|
||||||
elif self.options.annotation is SUPPRESS:
|
elif self.options.annotation is SUPPRESS:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@ -1276,6 +1288,12 @@ class DataDeclarationDocumenter(DataDocumenter):
|
|||||||
"""Never import anything."""
|
"""Never import anything."""
|
||||||
# disguise as a data
|
# disguise as a data
|
||||||
self.objtype = 'data'
|
self.objtype = 'data'
|
||||||
|
try:
|
||||||
|
# import module to obtain type annotation
|
||||||
|
self.parent = importlib.import_module(self.modname)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||||
@ -1404,12 +1422,22 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
if not self.options.annotation:
|
if not self.options.annotation:
|
||||||
if not self._datadescriptor:
|
if not self._datadescriptor:
|
||||||
|
# obtain annotation for this attribute
|
||||||
|
annotations = getattr(self.parent, '__annotations__', {})
|
||||||
|
if self.objpath[-1] in annotations:
|
||||||
|
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
||||||
|
self.add_line(' :type: ' + objrepr, sourcename)
|
||||||
|
else:
|
||||||
|
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
||||||
|
if self.analyzer and key in self.analyzer.annotations:
|
||||||
|
self.add_line(' :type: ' + self.analyzer.annotations[key],
|
||||||
|
sourcename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
objrepr = object_description(self.object)
|
objrepr = object_description(self.object)
|
||||||
|
self.add_line(' :value: ' + objrepr, sourcename)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
|
||||||
self.add_line(' :annotation: = ' + objrepr, sourcename)
|
|
||||||
elif self.options.annotation is SUPPRESS:
|
elif self.options.annotation is SUPPRESS:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
from typing import Any, Dict, Iterable
|
from typing import Any, Dict, Iterable
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
@ -37,13 +38,14 @@ def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any,
|
|||||||
"""Record type hints to env object."""
|
"""Record type hints to env object."""
|
||||||
try:
|
try:
|
||||||
if callable(obj):
|
if callable(obj):
|
||||||
annotations = app.env.temp_data.setdefault('annotations', {}).setdefault(name, {})
|
annotations = app.env.temp_data.setdefault('annotations', {})
|
||||||
|
annotation = annotations.setdefault(name, OrderedDict())
|
||||||
sig = inspect.signature(obj)
|
sig = inspect.signature(obj)
|
||||||
for param in sig.parameters.values():
|
for param in sig.parameters.values():
|
||||||
if param.annotation is not param.empty:
|
if param.annotation is not param.empty:
|
||||||
annotations[param.name] = typing.stringify(param.annotation)
|
annotation[param.name] = typing.stringify(param.annotation)
|
||||||
if sig.return_annotation is not sig.empty:
|
if sig.return_annotation is not sig.empty:
|
||||||
annotations['return'] = typing.stringify(sig.return_annotation)
|
annotation['return'] = typing.stringify(sig.return_annotation)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -55,7 +57,10 @@ def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element
|
|||||||
return
|
return
|
||||||
|
|
||||||
signature = cast(addnodes.desc_signature, contentnode.parent[0])
|
signature = cast(addnodes.desc_signature, contentnode.parent[0])
|
||||||
fullname = '.'.join([signature['module'], signature['fullname']])
|
if signature['module']:
|
||||||
|
fullname = '.'.join([signature['module'], signature['fullname']])
|
||||||
|
else:
|
||||||
|
fullname = signature['fullname']
|
||||||
annotations = app.env.temp_data.get('annotations', {})
|
annotations = app.env.temp_data.get('annotations', {})
|
||||||
if annotations.get(fullname, {}):
|
if annotations.get(fullname, {}):
|
||||||
field_lists = [n for n in contentnode if isinstance(n, nodes.field_list)]
|
field_lists = [n for n in contentnode if isinstance(n, nodes.field_list)]
|
||||||
|
@ -142,9 +142,10 @@ class ModuleAnalyzer:
|
|||||||
self.code = source.read()
|
self.code = source.read()
|
||||||
|
|
||||||
# will be filled by parse()
|
# will be filled by parse()
|
||||||
self.attr_docs = None # type: Dict[Tuple[str, str], List[str]]
|
self.annotations = None # type: Dict[Tuple[str, str], str]
|
||||||
self.tagorder = None # type: Dict[str, int]
|
self.attr_docs = None # type: Dict[Tuple[str, str], List[str]]
|
||||||
self.tags = None # type: Dict[str, Tuple[str, int, int]]
|
self.tagorder = None # type: Dict[str, int]
|
||||||
|
self.tags = None # type: Dict[str, Tuple[str, int, int]]
|
||||||
|
|
||||||
def parse(self) -> None:
|
def parse(self) -> None:
|
||||||
"""Parse the source code."""
|
"""Parse the source code."""
|
||||||
@ -159,6 +160,7 @@ class ModuleAnalyzer:
|
|||||||
else:
|
else:
|
||||||
self.attr_docs[scope] = ['']
|
self.attr_docs[scope] = ['']
|
||||||
|
|
||||||
|
self.annotations = parser.annotations
|
||||||
self.tags = parser.definitions
|
self.tags = parser.definitions
|
||||||
self.tagorder = parser.deforders
|
self.tagorder = parser.deforders
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
@ -38,6 +38,8 @@ def unparse(node: ast.AST) -> str:
|
|||||||
"""Unparse an AST to string."""
|
"""Unparse an AST to string."""
|
||||||
if node is None:
|
if node is None:
|
||||||
return None
|
return None
|
||||||
|
elif isinstance(node, str):
|
||||||
|
return node
|
||||||
elif isinstance(node, ast.Attribute):
|
elif isinstance(node, ast.Attribute):
|
||||||
return "%s.%s" % (unparse(node.value), node.attr)
|
return "%s.%s" % (unparse(node.value), node.attr)
|
||||||
elif isinstance(node, ast.Bytes):
|
elif isinstance(node, ast.Bytes):
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
import ast
|
|
||||||
import inspect
|
import inspect
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
@ -17,6 +16,9 @@ from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
|
|||||||
from tokenize import COMMENT, NL
|
from tokenize import COMMENT, NL
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
|
from sphinx.pycode.ast import ast # for py37 or older
|
||||||
|
from sphinx.pycode.ast import parse, unparse
|
||||||
|
|
||||||
|
|
||||||
comment_re = re.compile('^\\s*#: ?(.*)\r?\n?$')
|
comment_re = re.compile('^\\s*#: ?(.*)\r?\n?$')
|
||||||
indent_re = re.compile('^\\s*$')
|
indent_re = re.compile('^\\s*$')
|
||||||
@ -226,6 +228,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
self.current_classes = [] # type: List[str]
|
self.current_classes = [] # type: List[str]
|
||||||
self.current_function = None # type: ast.FunctionDef
|
self.current_function = None # type: ast.FunctionDef
|
||||||
self.comments = {} # type: Dict[Tuple[str, str], str]
|
self.comments = {} # type: Dict[Tuple[str, str], str]
|
||||||
|
self.annotations = {} # type: Dict[Tuple[str, str], str]
|
||||||
self.previous = None # type: ast.AST
|
self.previous = None # type: ast.AST
|
||||||
self.deforders = {} # type: Dict[str, int]
|
self.deforders = {} # type: Dict[str, int]
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -254,6 +257,18 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
|
|
||||||
self.comments[(context, name)] = comment
|
self.comments[(context, name)] = comment
|
||||||
|
|
||||||
|
def add_variable_annotation(self, name: str, annotation: ast.AST) -> None:
|
||||||
|
if self.current_function:
|
||||||
|
if self.current_classes and self.context[-1] == "__init__":
|
||||||
|
# store variable comments inside __init__ method of classes
|
||||||
|
context = ".".join(self.context[:-1])
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
context = ".".join(self.context)
|
||||||
|
|
||||||
|
self.annotations[(context, name)] = unparse(annotation)
|
||||||
|
|
||||||
def get_self(self) -> ast.arg:
|
def get_self(self) -> ast.arg:
|
||||||
"""Returns the name of first argument if in function."""
|
"""Returns the name of first argument if in function."""
|
||||||
if self.current_function and self.current_function.args.args:
|
if self.current_function and self.current_function.args.args:
|
||||||
@ -295,6 +310,14 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
return # this assignment is not new definition!
|
return # this assignment is not new definition!
|
||||||
|
|
||||||
|
# record annotation
|
||||||
|
if hasattr(node, 'annotation') and node.annotation: # type: ignore
|
||||||
|
for varname in varnames:
|
||||||
|
self.add_variable_annotation(varname, node.annotation) # type: ignore
|
||||||
|
elif hasattr(node, 'type_comment') and node.type_comment:
|
||||||
|
for varname in varnames:
|
||||||
|
self.add_variable_annotation(varname, node.type_comment) # type: ignore
|
||||||
|
|
||||||
# check comments after assignment
|
# check comments after assignment
|
||||||
parser = AfterCommentParser([current_line[node.col_offset:]] +
|
parser = AfterCommentParser([current_line[node.col_offset:]] +
|
||||||
self.buffers[node.lineno:])
|
self.buffers[node.lineno:])
|
||||||
@ -468,6 +491,7 @@ class Parser:
|
|||||||
def __init__(self, code: str, encoding: str = 'utf-8') -> None:
|
def __init__(self, code: str, encoding: str = 'utf-8') -> None:
|
||||||
self.code = filter_whitespace(code)
|
self.code = filter_whitespace(code)
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
self.annotations = {} # type: Dict[Tuple[str, str], str]
|
||||||
self.comments = {} # type: Dict[Tuple[str, str], str]
|
self.comments = {} # type: Dict[Tuple[str, str], str]
|
||||||
self.deforders = {} # type: Dict[str, int]
|
self.deforders = {} # type: Dict[str, int]
|
||||||
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
|
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
|
||||||
@ -479,9 +503,10 @@ class Parser:
|
|||||||
|
|
||||||
def parse_comments(self) -> None:
|
def parse_comments(self) -> None:
|
||||||
"""Parse the code and pick up comments."""
|
"""Parse the code and pick up comments."""
|
||||||
tree = ast.parse(self.code)
|
tree = parse(self.code)
|
||||||
picker = VariableCommentPicker(self.code.splitlines(True), self.encoding)
|
picker = VariableCommentPicker(self.code.splitlines(True), self.encoding)
|
||||||
picker.visit(tree)
|
picker.visit(tree)
|
||||||
|
self.annotations = picker.annotations
|
||||||
self.comments = picker.comments
|
self.comments = picker.comments
|
||||||
self.deforders = picker.deforders
|
self.deforders = picker.deforders
|
||||||
|
|
||||||
|
@ -68,182 +68,6 @@ ENUMERATE_LIST_STYLE = defaultdict(lambda: r'\arabic',
|
|||||||
'lowerroman': r'\roman',
|
'lowerroman': r'\roman',
|
||||||
'upperroman': r'\Roman',
|
'upperroman': r'\Roman',
|
||||||
})
|
})
|
||||||
PDFLATEX_DEFAULT_FONTPKG = r'''
|
|
||||||
\usepackage{times}
|
|
||||||
\expandafter\ifx\csname T@LGR\endcsname\relax
|
|
||||||
\else
|
|
||||||
% LGR was declared as font encoding
|
|
||||||
\substitutefont{LGR}{\rmdefault}{cmr}
|
|
||||||
\substitutefont{LGR}{\sfdefault}{cmss}
|
|
||||||
\substitutefont{LGR}{\ttdefault}{cmtt}
|
|
||||||
\fi
|
|
||||||
\expandafter\ifx\csname T@X2\endcsname\relax
|
|
||||||
\expandafter\ifx\csname T@T2A\endcsname\relax
|
|
||||||
\else
|
|
||||||
% T2A was declared as font encoding
|
|
||||||
\substitutefont{T2A}{\rmdefault}{cmr}
|
|
||||||
\substitutefont{T2A}{\sfdefault}{cmss}
|
|
||||||
\substitutefont{T2A}{\ttdefault}{cmtt}
|
|
||||||
\fi
|
|
||||||
\else
|
|
||||||
% X2 was declared as font encoding
|
|
||||||
\substitutefont{X2}{\rmdefault}{cmr}
|
|
||||||
\substitutefont{X2}{\sfdefault}{cmss}
|
|
||||||
\substitutefont{X2}{\ttdefault}{cmtt}
|
|
||||||
\fi
|
|
||||||
'''
|
|
||||||
XELATEX_DEFAULT_FONTPKG = r'''
|
|
||||||
\setmainfont{FreeSerif}[
|
|
||||||
Extension = .otf,
|
|
||||||
UprightFont = *,
|
|
||||||
ItalicFont = *Italic,
|
|
||||||
BoldFont = *Bold,
|
|
||||||
BoldItalicFont = *BoldItalic
|
|
||||||
]
|
|
||||||
\setsansfont{FreeSans}[
|
|
||||||
Extension = .otf,
|
|
||||||
UprightFont = *,
|
|
||||||
ItalicFont = *Oblique,
|
|
||||||
BoldFont = *Bold,
|
|
||||||
BoldItalicFont = *BoldOblique,
|
|
||||||
]
|
|
||||||
\setmonofont{FreeMono}[
|
|
||||||
Extension = .otf,
|
|
||||||
UprightFont = *,
|
|
||||||
ItalicFont = *Oblique,
|
|
||||||
BoldFont = *Bold,
|
|
||||||
BoldItalicFont = *BoldOblique,
|
|
||||||
]
|
|
||||||
'''
|
|
||||||
XELATEX_GREEK_DEFAULT_FONTPKG = (XELATEX_DEFAULT_FONTPKG +
|
|
||||||
'\n\\newfontfamily\\greekfont{FreeSerif}' +
|
|
||||||
'\n\\newfontfamily\\greekfontsf{FreeSans}' +
|
|
||||||
'\n\\newfontfamily\\greekfonttt{FreeMono}')
|
|
||||||
LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG
|
|
||||||
|
|
||||||
DEFAULT_SETTINGS = {
|
|
||||||
'latex_engine': 'pdflatex',
|
|
||||||
'papersize': 'letterpaper',
|
|
||||||
'pointsize': '10pt',
|
|
||||||
'pxunit': '.75bp',
|
|
||||||
'classoptions': '',
|
|
||||||
'extraclassoptions': '',
|
|
||||||
'maxlistdepth': '',
|
|
||||||
'sphinxpkgoptions': '',
|
|
||||||
'sphinxsetup': '',
|
|
||||||
'fvset': '\\fvset{fontsize=\\small}',
|
|
||||||
'passoptionstopackages': '',
|
|
||||||
'geometry': '\\usepackage{geometry}',
|
|
||||||
'inputenc': '',
|
|
||||||
'utf8extra': '',
|
|
||||||
'cmappkg': '\\usepackage{cmap}',
|
|
||||||
'fontenc': '\\usepackage[T1]{fontenc}',
|
|
||||||
'amsmath': '\\usepackage{amsmath,amssymb,amstext}',
|
|
||||||
'multilingual': '',
|
|
||||||
'babel': '\\usepackage{babel}',
|
|
||||||
'polyglossia': '',
|
|
||||||
'fontpkg': PDFLATEX_DEFAULT_FONTPKG,
|
|
||||||
'substitutefont': '',
|
|
||||||
'textcyrillic': '',
|
|
||||||
'textgreek': '\\usepackage{textalpha}',
|
|
||||||
'fncychap': '\\usepackage[Bjarne]{fncychap}',
|
|
||||||
'hyperref': ('% Include hyperref last.\n'
|
|
||||||
'\\usepackage{hyperref}\n'
|
|
||||||
'% Fix anchor placement for figures with captions.\n'
|
|
||||||
'\\usepackage{hypcap}% it must be loaded after hyperref.\n'
|
|
||||||
'% Set up styles of URL: it should be placed after hyperref.\n'
|
|
||||||
'\\urlstyle{same}'),
|
|
||||||
'contentsname': '',
|
|
||||||
'extrapackages': '',
|
|
||||||
'preamble': '',
|
|
||||||
'title': '',
|
|
||||||
'release': '',
|
|
||||||
'author': '',
|
|
||||||
'releasename': '',
|
|
||||||
'makeindex': '\\makeindex',
|
|
||||||
'shorthandoff': '',
|
|
||||||
'maketitle': '\\sphinxmaketitle',
|
|
||||||
'tableofcontents': '\\sphinxtableofcontents',
|
|
||||||
'atendofbody': '',
|
|
||||||
'printindex': '\\printindex',
|
|
||||||
'transition': '\n\n\\bigskip\\hrule\\bigskip\n\n',
|
|
||||||
'figure_align': 'htbp',
|
|
||||||
'tocdepth': '',
|
|
||||||
'secnumdepth': '',
|
|
||||||
} # type: Dict[str, Any]
|
|
||||||
|
|
||||||
ADDITIONAL_SETTINGS = {
|
|
||||||
'pdflatex': {
|
|
||||||
'inputenc': '\\usepackage[utf8]{inputenc}',
|
|
||||||
'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n'
|
|
||||||
'% support both utf8 and utf8x syntaxes\n'
|
|
||||||
' \\ifdefined\\DeclareUnicodeCharacterAsOptional\n'
|
|
||||||
' \\def\\sphinxDUC#1{\\DeclareUnicodeCharacter{"#1}}\n'
|
|
||||||
' \\else\n'
|
|
||||||
' \\let\\sphinxDUC\\DeclareUnicodeCharacter\n'
|
|
||||||
' \\fi\n'
|
|
||||||
' \\sphinxDUC{00A0}{\\nobreakspace}\n'
|
|
||||||
' \\sphinxDUC{2500}{\\sphinxunichar{2500}}\n'
|
|
||||||
' \\sphinxDUC{2502}{\\sphinxunichar{2502}}\n'
|
|
||||||
' \\sphinxDUC{2514}{\\sphinxunichar{2514}}\n'
|
|
||||||
' \\sphinxDUC{251C}{\\sphinxunichar{251C}}\n'
|
|
||||||
' \\sphinxDUC{2572}{\\textbackslash}\n'
|
|
||||||
'\\fi'),
|
|
||||||
},
|
|
||||||
'xelatex': {
|
|
||||||
'latex_engine': 'xelatex',
|
|
||||||
'polyglossia': '\\usepackage{polyglossia}',
|
|
||||||
'babel': '',
|
|
||||||
'fontenc': ('\\usepackage{fontspec}\n'
|
|
||||||
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
|
|
||||||
'fontpkg': XELATEX_DEFAULT_FONTPKG,
|
|
||||||
'textgreek': '',
|
|
||||||
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
|
|
||||||
'{\\leavevmode\\nobreak\\ }'),
|
|
||||||
},
|
|
||||||
'lualatex': {
|
|
||||||
'latex_engine': 'lualatex',
|
|
||||||
'polyglossia': '\\usepackage{polyglossia}',
|
|
||||||
'babel': '',
|
|
||||||
'fontenc': ('\\usepackage{fontspec}\n'
|
|
||||||
'\\defaultfontfeatures[\\rmfamily,\\sffamily,\\ttfamily]{}'),
|
|
||||||
'fontpkg': LUALATEX_DEFAULT_FONTPKG,
|
|
||||||
'textgreek': '',
|
|
||||||
'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0'
|
|
||||||
'{\\leavevmode\\nobreak\\ }'),
|
|
||||||
},
|
|
||||||
'platex': {
|
|
||||||
'latex_engine': 'platex',
|
|
||||||
'babel': '',
|
|
||||||
'classoptions': ',dvipdfmx',
|
|
||||||
'fontpkg': '\\usepackage{times}',
|
|
||||||
'textgreek': '',
|
|
||||||
'fncychap': '',
|
|
||||||
'geometry': '\\usepackage[dvipdfm]{geometry}',
|
|
||||||
},
|
|
||||||
'uplatex': {
|
|
||||||
'latex_engine': 'uplatex',
|
|
||||||
'babel': '',
|
|
||||||
'classoptions': ',dvipdfmx',
|
|
||||||
'fontpkg': '\\usepackage{times}',
|
|
||||||
'textgreek': '',
|
|
||||||
'fncychap': '',
|
|
||||||
'geometry': '\\usepackage[dvipdfm]{geometry}',
|
|
||||||
},
|
|
||||||
|
|
||||||
# special settings for latex_engine + language_code
|
|
||||||
('xelatex', 'fr'): {
|
|
||||||
# use babel instead of polyglossia by default
|
|
||||||
'polyglossia': '',
|
|
||||||
'babel': '\\usepackage{babel}',
|
|
||||||
},
|
|
||||||
('xelatex', 'zh'): {
|
|
||||||
'fontenc': '\\usepackage{xeCJK}',
|
|
||||||
},
|
|
||||||
('xelatex', 'el'): {
|
|
||||||
'fontpkg': XELATEX_GREEK_DEFAULT_FONTPKG,
|
|
||||||
},
|
|
||||||
} # type: Dict[Any, Dict[str, Any]]
|
|
||||||
|
|
||||||
EXTRA_RE = re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$')
|
EXTRA_RE = re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$')
|
||||||
|
|
||||||
@ -492,16 +316,18 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
self.compact_list = 0
|
self.compact_list = 0
|
||||||
self.first_param = 0
|
self.first_param = 0
|
||||||
|
|
||||||
|
sphinxpkgoptions = []
|
||||||
|
|
||||||
# sort out some elements
|
# sort out some elements
|
||||||
self.elements = self.builder.context.copy()
|
self.elements = self.builder.context.copy()
|
||||||
|
|
||||||
# but some have other interface in config file
|
# but some have other interface in config file
|
||||||
self.elements['wrapperclass'] = self.format_docclass(self.settings.docclass)
|
self.elements['wrapperclass'] = self.format_docclass(document.get('docclass'))
|
||||||
|
|
||||||
# we assume LaTeX class provides \chapter command except in case
|
# we assume LaTeX class provides \chapter command except in case
|
||||||
# of non-Japanese 'howto' case
|
# of non-Japanese 'howto' case
|
||||||
self.sectionnames = LATEXSECTIONNAMES[:]
|
self.sectionnames = LATEXSECTIONNAMES[:]
|
||||||
if self.settings.docclass == 'howto':
|
if document.get('docclass') == 'howto':
|
||||||
docclass = self.config.latex_docclass.get('howto', 'article')
|
docclass = self.config.latex_docclass.get('howto', 'article')
|
||||||
if docclass[0] == 'j': # Japanese class...
|
if docclass[0] == 'j': # Japanese class...
|
||||||
pass
|
pass
|
||||||
@ -535,15 +361,12 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
self.numfig_secnum_depth = min(self.numfig_secnum_depth,
|
self.numfig_secnum_depth = min(self.numfig_secnum_depth,
|
||||||
len(LATEXSECTIONNAMES) - 1)
|
len(LATEXSECTIONNAMES) - 1)
|
||||||
# if passed key value is < 1 LaTeX will act as if 0; see sphinx.sty
|
# if passed key value is < 1 LaTeX will act as if 0; see sphinx.sty
|
||||||
self.elements['sphinxpkgoptions'] += \
|
sphinxpkgoptions.append('numfigreset=%s' % self.numfig_secnum_depth)
|
||||||
(',numfigreset=%s' % self.numfig_secnum_depth)
|
|
||||||
else:
|
else:
|
||||||
self.elements['sphinxpkgoptions'] += ',nonumfigreset'
|
sphinxpkgoptions.append('nonumfigreset')
|
||||||
try:
|
|
||||||
if self.config.math_numfig:
|
if self.config.numfig and self.config.math_numfig:
|
||||||
self.elements['sphinxpkgoptions'] += ',mathnumfig'
|
sphinxpkgoptions.append('mathnumfig')
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if (self.config.language not in {None, 'en', 'ja'} and
|
if (self.config.language not in {None, 'en', 'ja'} and
|
||||||
'fncychap' not in self.config.latex_elements):
|
'fncychap' not in self.config.latex_elements):
|
||||||
@ -606,7 +429,7 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
# tocdepth = 1: show parts, chapters and sections
|
# tocdepth = 1: show parts, chapters and sections
|
||||||
# tocdepth = 2: show parts, chapters, sections and subsections
|
# tocdepth = 2: show parts, chapters, sections and subsections
|
||||||
# ...
|
# ...
|
||||||
tocdepth = self.document['tocdepth'] + self.top_sectionlevel - 2
|
tocdepth = self.document.get('tocdepth', 999) + self.top_sectionlevel - 2
|
||||||
if len(self.sectionnames) < len(LATEXSECTIONNAMES) and \
|
if len(self.sectionnames) < len(LATEXSECTIONNAMES) and \
|
||||||
self.top_sectionlevel > 0:
|
self.top_sectionlevel > 0:
|
||||||
tocdepth += 1 # because top_sectionlevel is shifted by -1
|
tocdepth += 1 # because top_sectionlevel is shifted by -1
|
||||||
@ -624,17 +447,15 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' %\
|
self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' %\
|
||||||
minsecnumdepth
|
minsecnumdepth
|
||||||
|
|
||||||
contentsname = self.settings.contentsname
|
contentsname = document.get('contentsname')
|
||||||
if contentsname:
|
if contentsname:
|
||||||
self.elements['contentsname'] = self.babel_renewcommand('\\contentsname',
|
self.elements['contentsname'] = self.babel_renewcommand('\\contentsname',
|
||||||
contentsname)
|
contentsname)
|
||||||
|
|
||||||
if self.elements['maxlistdepth']:
|
if self.elements['maxlistdepth']:
|
||||||
self.elements['sphinxpkgoptions'] += (',maxlistdepth=%s' %
|
sphinxpkgoptions.append('maxlistdepth=%s' % self.elements['maxlistdepth'])
|
||||||
self.elements['maxlistdepth'])
|
if sphinxpkgoptions:
|
||||||
if self.elements['sphinxpkgoptions']:
|
self.elements['sphinxpkgoptions'] = '[,%s]' % ','.join(sphinxpkgoptions)
|
||||||
self.elements['sphinxpkgoptions'] = ('[%s]' %
|
|
||||||
self.elements['sphinxpkgoptions'])
|
|
||||||
if self.elements['sphinxsetup']:
|
if self.elements['sphinxsetup']:
|
||||||
self.elements['sphinxsetup'] = ('\\sphinxsetup{%s}' %
|
self.elements['sphinxsetup'] = ('\\sphinxsetup{%s}' %
|
||||||
self.elements['sphinxsetup'])
|
self.elements['sphinxsetup'])
|
||||||
@ -1542,6 +1363,8 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
length = self.latex_image_length(node['width'])
|
length = self.latex_image_length(node['width'])
|
||||||
elif isinstance(node[0], nodes.image) and 'width' in node[0]:
|
elif isinstance(node[0], nodes.image) and 'width' in node[0]:
|
||||||
length = self.latex_image_length(node[0]['width'])
|
length = self.latex_image_length(node[0]['width'])
|
||||||
|
self.body.append('\n\n') # Insert a blank line to prevent infinite loop
|
||||||
|
# https://github.com/sphinx-doc/sphinx/issues/7059
|
||||||
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
|
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
|
||||||
('r' if node['align'] == 'right' else 'l', length or '0pt'))
|
('r' if node['align'] == 'right' else 'l', length or '0pt'))
|
||||||
self.context.append('\\end{wrapfigure}\n')
|
self.context.append('\\end{wrapfigure}\n')
|
||||||
@ -2415,6 +2238,7 @@ class LaTeXTranslator(SphinxTranslator):
|
|||||||
|
|
||||||
|
|
||||||
# Import old modules here for compatibility
|
# Import old modules here for compatibility
|
||||||
|
from sphinx.builders.latex import constants # NOQA
|
||||||
from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA
|
from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA
|
||||||
from sphinx.builders.latex.util import ExtBabel # NOQA
|
from sphinx.builders.latex.util import ExtBabel # NOQA
|
||||||
|
|
||||||
@ -2427,6 +2251,12 @@ deprecated_alias('sphinx.writers.latex',
|
|||||||
RemovedInSphinx30Warning)
|
RemovedInSphinx30Warning)
|
||||||
deprecated_alias('sphinx.writers.latex',
|
deprecated_alias('sphinx.writers.latex',
|
||||||
{
|
{
|
||||||
|
'ADDITIONAL_SETTINGS': constants.ADDITIONAL_SETTINGS,
|
||||||
|
'DEFAULT_SETTINGS': constants.DEFAULT_SETTINGS,
|
||||||
|
'LUALATEX_DEFAULT_FONTPKG': constants.LUALATEX_DEFAULT_FONTPKG,
|
||||||
|
'PDFLATEX_DEFAULT_FONTPKG': constants.PDFLATEX_DEFAULT_FONTPKG,
|
||||||
|
'XELATEX_DEFAULT_FONTPKG': constants.XELATEX_DEFAULT_FONTPKG,
|
||||||
|
'XELATEX_GREEK_DEFAULT_FONTPKG': constants.XELATEX_GREEK_DEFAULT_FONTPKG,
|
||||||
'ExtBabel': ExtBabel,
|
'ExtBabel': ExtBabel,
|
||||||
},
|
},
|
||||||
RemovedInSphinx40Warning)
|
RemovedInSphinx40Warning)
|
||||||
|
@ -2,12 +2,17 @@
|
|||||||
attr1: str = ''
|
attr1: str = ''
|
||||||
#: attr2
|
#: attr2
|
||||||
attr2: str
|
attr2: str
|
||||||
|
#: attr3
|
||||||
|
attr3 = '' # type: str
|
||||||
|
|
||||||
|
|
||||||
class Class:
|
class Class:
|
||||||
attr1: int = 0
|
attr1: int = 0
|
||||||
attr2: int
|
attr2: int
|
||||||
|
attr3 = 0 # type: int
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.attr3: int = 0 #: attr3
|
self.attr4: int = 0 #: attr4
|
||||||
self.attr4: int #: attr4
|
self.attr5: int #: attr5
|
||||||
|
self.attr6 = 0 # type: int
|
||||||
|
"""attr6"""
|
||||||
|
@ -906,7 +906,7 @@ def test_autodoc_module_scope(app):
|
|||||||
'',
|
'',
|
||||||
'.. py:attribute:: Class.mdocattr',
|
'.. py:attribute:: Class.mdocattr',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = <_io.StringIO object>',
|
' :value: <_io.StringIO object>',
|
||||||
'',
|
'',
|
||||||
' should be documented as well - süß',
|
' should be documented as well - süß',
|
||||||
' '
|
' '
|
||||||
@ -922,7 +922,7 @@ def test_autodoc_class_scope(app):
|
|||||||
'',
|
'',
|
||||||
'.. py:attribute:: Class.mdocattr',
|
'.. py:attribute:: Class.mdocattr',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = <_io.StringIO object>',
|
' :value: <_io.StringIO object>',
|
||||||
'',
|
'',
|
||||||
' should be documented as well - süß',
|
' should be documented as well - süß',
|
||||||
' '
|
' '
|
||||||
@ -942,12 +942,12 @@ def test_class_attributes(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: AttCls.a1',
|
' .. py:attribute:: AttCls.a1',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = hello world',
|
' :value: hello world',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: AttCls.a2',
|
' .. py:attribute:: AttCls.a2',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = None',
|
' :value: None',
|
||||||
' '
|
' '
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -966,7 +966,7 @@ def test_instance_attributes(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ca1',
|
' .. py:attribute:: InstAttCls.ca1',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
" :annotation: = 'a'",
|
" :value: 'a'",
|
||||||
' ',
|
' ',
|
||||||
' Doc comment for class attribute InstAttCls.ca1.',
|
' Doc comment for class attribute InstAttCls.ca1.',
|
||||||
' It can have multiple lines.',
|
' It can have multiple lines.',
|
||||||
@ -974,28 +974,28 @@ def test_instance_attributes(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ca2',
|
' .. py:attribute:: InstAttCls.ca2',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
" :annotation: = 'b'",
|
" :value: 'b'",
|
||||||
' ',
|
' ',
|
||||||
' Doc comment for InstAttCls.ca2. One line only.',
|
' Doc comment for InstAttCls.ca2. One line only.',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ca3',
|
' .. py:attribute:: InstAttCls.ca3',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
" :annotation: = 'c'",
|
" :value: 'c'",
|
||||||
' ',
|
' ',
|
||||||
' Docstring for class attribute InstAttCls.ca3.',
|
' Docstring for class attribute InstAttCls.ca3.',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ia1',
|
' .. py:attribute:: InstAttCls.ia1',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = None',
|
' :value: None',
|
||||||
' ',
|
' ',
|
||||||
' Doc comment for instance attribute InstAttCls.ia1',
|
' Doc comment for instance attribute InstAttCls.ia1',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ia2',
|
' .. py:attribute:: InstAttCls.ia2',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = None',
|
' :value: None',
|
||||||
' ',
|
' ',
|
||||||
' Docstring for instance attribute InstAttCls.ia2.',
|
' Docstring for instance attribute InstAttCls.ia2.',
|
||||||
' '
|
' '
|
||||||
@ -1014,7 +1014,7 @@ def test_instance_attributes(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ca1',
|
' .. py:attribute:: InstAttCls.ca1',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
" :annotation: = 'a'",
|
" :value: 'a'",
|
||||||
' ',
|
' ',
|
||||||
' Doc comment for class attribute InstAttCls.ca1.',
|
' Doc comment for class attribute InstAttCls.ca1.',
|
||||||
' It can have multiple lines.',
|
' It can have multiple lines.',
|
||||||
@ -1022,7 +1022,7 @@ def test_instance_attributes(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: InstAttCls.ia1',
|
' .. py:attribute:: InstAttCls.ia1',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
' :annotation: = None',
|
' :value: None',
|
||||||
' ',
|
' ',
|
||||||
' Doc comment for instance attribute InstAttCls.ia1',
|
' Doc comment for instance attribute InstAttCls.ia1',
|
||||||
' '
|
' '
|
||||||
@ -1090,28 +1090,28 @@ def test_enum_class(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: EnumCls.val1',
|
' .. py:attribute:: EnumCls.val1',
|
||||||
' :module: target.enum',
|
' :module: target.enum',
|
||||||
' :annotation: = 12',
|
' :value: 12',
|
||||||
' ',
|
' ',
|
||||||
' doc for val1',
|
' doc for val1',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: EnumCls.val2',
|
' .. py:attribute:: EnumCls.val2',
|
||||||
' :module: target.enum',
|
' :module: target.enum',
|
||||||
' :annotation: = 23',
|
' :value: 23',
|
||||||
' ',
|
' ',
|
||||||
' doc for val2',
|
' doc for val2',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: EnumCls.val3',
|
' .. py:attribute:: EnumCls.val3',
|
||||||
' :module: target.enum',
|
' :module: target.enum',
|
||||||
' :annotation: = 34',
|
' :value: 34',
|
||||||
' ',
|
' ',
|
||||||
' doc for val3',
|
' doc for val3',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: EnumCls.val4',
|
' .. py:attribute:: EnumCls.val4',
|
||||||
' :module: target.enum',
|
' :module: target.enum',
|
||||||
' :annotation: = 34',
|
' :value: 34',
|
||||||
' '
|
' '
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1121,7 +1121,7 @@ def test_enum_class(app):
|
|||||||
'',
|
'',
|
||||||
'.. py:attribute:: EnumCls.val1',
|
'.. py:attribute:: EnumCls.val1',
|
||||||
' :module: target.enum',
|
' :module: target.enum',
|
||||||
' :annotation: = 12',
|
' :value: 12',
|
||||||
'',
|
'',
|
||||||
' doc for val1',
|
' doc for val1',
|
||||||
' '
|
' '
|
||||||
@ -1405,40 +1405,68 @@ def test_autodoc_typed_instance_variables(app):
|
|||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: Class.attr1',
|
' .. py:attribute:: Class.attr1',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
' :annotation: = 0',
|
' :type: int',
|
||||||
|
' :value: 0',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: Class.attr2',
|
' .. py:attribute:: Class.attr2',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
' :annotation: = None',
|
' :type: int',
|
||||||
|
' :value: None',
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: Class.attr3',
|
' .. py:attribute:: Class.attr3',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
' :annotation: = None',
|
' :type: int',
|
||||||
|
' :value: 0',
|
||||||
' ',
|
' ',
|
||||||
' attr3',
|
|
||||||
' ',
|
|
||||||
' ',
|
' ',
|
||||||
' .. py:attribute:: Class.attr4',
|
' .. py:attribute:: Class.attr4',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
' :annotation: = None',
|
' :type: int',
|
||||||
|
' :value: None',
|
||||||
' ',
|
' ',
|
||||||
' attr4',
|
' attr4',
|
||||||
' ',
|
' ',
|
||||||
|
' ',
|
||||||
|
' .. py:attribute:: Class.attr5',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
' :value: None',
|
||||||
|
' ',
|
||||||
|
' attr5',
|
||||||
|
' ',
|
||||||
|
' ',
|
||||||
|
' .. py:attribute:: Class.attr6',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
' :value: None',
|
||||||
|
' ',
|
||||||
|
' attr6',
|
||||||
|
' ',
|
||||||
'',
|
'',
|
||||||
'.. py:data:: attr1',
|
'.. py:data:: attr1',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
" :annotation: = ''",
|
' :type: str',
|
||||||
|
" :value: ''",
|
||||||
'',
|
'',
|
||||||
' attr1',
|
' attr1',
|
||||||
' ',
|
' ',
|
||||||
'',
|
'',
|
||||||
'.. py:data:: attr2',
|
'.. py:data:: attr2',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
" :annotation: = None",
|
' :type: str',
|
||||||
|
' :value: None',
|
||||||
'',
|
'',
|
||||||
' attr2',
|
' attr2',
|
||||||
|
' ',
|
||||||
|
'',
|
||||||
|
'.. py:data:: attr3',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: str',
|
||||||
|
" :value: ''",
|
||||||
|
'',
|
||||||
|
' attr3',
|
||||||
' '
|
' '
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1455,7 +1483,7 @@ def test_autodoc_for_egged_code(app):
|
|||||||
'',
|
'',
|
||||||
'.. py:data:: CONSTANT',
|
'.. py:data:: CONSTANT',
|
||||||
' :module: sample',
|
' :module: sample',
|
||||||
' :annotation: = 1',
|
' :value: 1',
|
||||||
'',
|
'',
|
||||||
' constant on sample.py',
|
' constant on sample.py',
|
||||||
' ',
|
' ',
|
||||||
|
@ -285,6 +285,20 @@ def test_exceptions_module_is_ignored(app):
|
|||||||
|
|
||||||
|
|
||||||
def test_pydata_signature(app):
|
def test_pydata_signature(app):
|
||||||
|
text = (".. py:data:: version\n"
|
||||||
|
" :type: int\n"
|
||||||
|
" :value: 1\n")
|
||||||
|
doctree = restructuredtext.parse(app, text)
|
||||||
|
assert_node(doctree, (addnodes.index,
|
||||||
|
[desc, ([desc_signature, ([desc_name, "version"],
|
||||||
|
[desc_annotation, ": int"],
|
||||||
|
[desc_annotation, " = 1"])],
|
||||||
|
desc_content)]))
|
||||||
|
assert_node(doctree[1], addnodes.desc, desctype="data",
|
||||||
|
domain="py", objtype="data", noindex=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pydata_signature_old(app):
|
||||||
text = (".. py:data:: version\n"
|
text = (".. py:data:: version\n"
|
||||||
" :annotation: = 1\n")
|
" :annotation: = 1\n")
|
||||||
doctree = restructuredtext.parse(app, text)
|
doctree = restructuredtext.parse(app, text)
|
||||||
@ -481,7 +495,9 @@ def test_pystaticmethod(app):
|
|||||||
def test_pyattribute(app):
|
def test_pyattribute(app):
|
||||||
text = (".. py:class:: Class\n"
|
text = (".. py:class:: Class\n"
|
||||||
"\n"
|
"\n"
|
||||||
" .. py:attribute:: attr\n")
|
" .. py:attribute:: attr\n"
|
||||||
|
" :type: str\n"
|
||||||
|
" :value: ''\n")
|
||||||
domain = app.env.get_domain('py')
|
domain = app.env.get_domain('py')
|
||||||
doctree = restructuredtext.parse(app, text)
|
doctree = restructuredtext.parse(app, text)
|
||||||
assert_node(doctree, (addnodes.index,
|
assert_node(doctree, (addnodes.index,
|
||||||
@ -491,7 +507,9 @@ def test_pyattribute(app):
|
|||||||
desc)])]))
|
desc)])]))
|
||||||
assert_node(doctree[1][1][0], addnodes.index,
|
assert_node(doctree[1][1][0], addnodes.index,
|
||||||
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
||||||
assert_node(doctree[1][1][1], ([desc_signature, desc_name, "attr"],
|
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
||||||
|
[desc_annotation, ": str"],
|
||||||
|
[desc_annotation, " = ''"])],
|
||||||
[desc_content, ()]))
|
[desc_content, ()]))
|
||||||
assert 'Class.attr' in domain.objects
|
assert 'Class.attr' in domain.objects
|
||||||
assert domain.objects['Class.attr'] == ('index', 'attribute')
|
assert domain.objects['Class.attr'] == ('index', 'attribute')
|
||||||
|
@ -408,11 +408,13 @@ def test_private(tempdir):
|
|||||||
# without --private option
|
# without --private option
|
||||||
apidoc_main(['-o', tempdir, tempdir])
|
apidoc_main(['-o', tempdir, tempdir])
|
||||||
assert (tempdir / 'hello.rst').exists()
|
assert (tempdir / 'hello.rst').exists()
|
||||||
|
assert ':private-members:' not in (tempdir / 'hello.rst').text()
|
||||||
assert not (tempdir / '_world.rst').exists()
|
assert not (tempdir / '_world.rst').exists()
|
||||||
|
|
||||||
# with --private option
|
# with --private option
|
||||||
apidoc_main(['--private', '-o', tempdir, tempdir])
|
apidoc_main(['--private', '-f', '-o', tempdir, tempdir])
|
||||||
assert (tempdir / 'hello.rst').exists()
|
assert (tempdir / 'hello.rst').exists()
|
||||||
|
assert ':private-members:' in (tempdir / 'hello.rst').text()
|
||||||
assert (tempdir / '_world.rst').exists()
|
assert (tempdir / '_world.rst').exists()
|
||||||
|
|
||||||
|
|
||||||
|
@ -99,12 +99,19 @@ def test_annotated_assignment_py36():
|
|||||||
source = ('a: str = "Sphinx" #: comment\n'
|
source = ('a: str = "Sphinx" #: comment\n'
|
||||||
'b: int = 1\n'
|
'b: int = 1\n'
|
||||||
'"""string on next line"""\n'
|
'"""string on next line"""\n'
|
||||||
'c: int #: comment')
|
'c: int #: comment\n'
|
||||||
|
'd = 1 # type: int\n'
|
||||||
|
'"""string on next line"""\n')
|
||||||
parser = Parser(source)
|
parser = Parser(source)
|
||||||
parser.parse()
|
parser.parse()
|
||||||
assert parser.comments == {('', 'a'): 'comment',
|
assert parser.comments == {('', 'a'): 'comment',
|
||||||
('', 'b'): 'string on next line',
|
('', 'b'): 'string on next line',
|
||||||
('', 'c'): 'comment'}
|
('', 'c'): 'comment',
|
||||||
|
('', 'd'): 'string on next line'}
|
||||||
|
assert parser.annotations == {('', 'a'): 'str',
|
||||||
|
('', 'b'): 'int',
|
||||||
|
('', 'c'): 'int',
|
||||||
|
('', 'd'): 'int'}
|
||||||
assert parser.definitions == {}
|
assert parser.definitions == {}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user