├── .github └── workflows │ └── tests.yml ├── .gitignore ├── CHANGES.rst ├── CONTRIBUTING.rst ├── CONTRIBUTORS.txt ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── setup.py ├── sphinxcontrib ├── __init__.py ├── builders │ ├── __init__.py │ └── rst.py ├── restbuilder.py └── writers │ ├── __init__.py │ └── rst.py ├── tests ├── __init__.py ├── conftest.py ├── datasets │ ├── common │ │ ├── bold.rst │ │ ├── bullet-list-consecutive.rst │ │ ├── bullet-list.rst │ │ ├── definition-list.rst │ │ ├── external-hyperlinks.rst │ │ ├── grid-table.rst │ │ ├── headings.rst │ │ ├── hyperlink-targets.rst │ │ ├── indentation.rst │ │ ├── index.rst │ │ ├── internal-hyperlinks.rst │ │ ├── italic.rst │ │ ├── list-table.rst │ │ ├── literal-block.rst │ │ ├── literal.rst │ │ ├── multiline-list.rst │ │ ├── nested-list.rst │ │ ├── nonexistent-target.rst │ │ ├── ordered-list-properties.rst │ │ ├── ordered-list.rst │ │ ├── paragraph.rst │ │ ├── simple-table.rst │ │ ├── smart-quotes.rst │ │ ├── subscript.rst │ │ └── superscript.rst │ ├── directives │ │ ├── code-language.rst │ │ ├── code-number-lines.rst │ │ ├── code.rst │ │ ├── index.rst │ │ └── math.rst │ ├── index.rst │ ├── roles │ │ └── index.rst │ ├── sphinx-directives │ │ ├── code-block-language.rst │ │ ├── code-block-linenos.rst │ │ ├── code-block.rst │ │ ├── deprecated.rst │ │ ├── index.rst │ │ ├── toctree-nested │ │ │ ├── chapter1.rst │ │ │ ├── chapter1 │ │ │ │ ├── section1.rst │ │ │ │ └── section2.rst │ │ │ ├── chapter2.rst │ │ │ ├── chapter2 │ │ │ │ ├── section1.rst │ │ │ │ └── section2.rst │ │ │ └── index.rst │ │ ├── toctree │ │ │ ├── doc1.rst │ │ │ ├── doc2.rst │ │ │ └── index.rst │ │ ├── versionadded.rst │ │ └── versionchanged.rst │ └── sphinx-roles │ │ ├── doc │ │ ├── doc1.rst │ │ ├── doc2.rst │ │ └── index.rst │ │ ├── index.rst │ │ ├── ref.rst │ │ └── ref │ │ ├── doc1.rst │ │ └── index.rst ├── expected │ ├── common │ │ ├── bold.rst │ │ ├── bullet-list-consecutive.rst │ │ ├── bullet-list.rst │ │ ├── definition-list.rst │ │ ├── external-hyperlinks.rst │ │ ├── grid-table.rst │ │ ├── headings.rst │ │ ├── hyperlink-targets.rst │ │ ├── indentation.rst │ │ ├── index.rst │ │ ├── internal-hyperlinks.rst │ │ ├── italic.rst │ │ ├── list-table.rst │ │ ├── literal-block.rst │ │ ├── literal.rst │ │ ├── multiline-list.rst │ │ ├── nested-list.rst │ │ ├── nonexistent-target.rst │ │ ├── ordered-list-properties.rst │ │ ├── ordered-list.rst │ │ ├── paragraph.rst │ │ ├── simple-table.rst │ │ ├── smart-quotes.rst │ │ ├── subscript.rst │ │ └── superscript.rst │ ├── directives │ │ ├── code-language.rst │ │ ├── code-number-lines.rst │ │ ├── code.rst │ │ ├── index.rst │ │ └── math.rst │ ├── index.rst │ ├── roles │ │ └── index.rst │ ├── sphinx-directives │ │ ├── code-block-language.rst │ │ ├── code-block-linenos.rst │ │ ├── code-block.rst │ │ ├── deprecated.rst │ │ ├── index.rst │ │ ├── toctree-nested │ │ │ └── index.rst │ │ ├── toctree │ │ │ ├── doc1.rst │ │ │ ├── doc2.rst │ │ │ └── index.rst │ │ ├── versionadded.rst │ │ └── versionchanged.rst │ └── sphinx-roles │ │ ├── doc │ │ ├── doc1.rst │ │ ├── doc2.rst │ │ └── index.rst │ │ ├── index.rst │ │ ├── ref.rst │ │ └── ref │ │ ├── doc1.rst │ │ └── index.rst ├── test_rst_blocks.py ├── test_rst_code_blocks.py ├── test_rst_directives_math.py ├── test_rst_formatting.py ├── test_rst_headings.py ├── test_rst_hyperlinks.py ├── test_rst_list.py ├── test_rst_table.py ├── test_rst_toctree.py ├── test_sphinx_versionmodified.py └── utils.py └── tox.ini /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - dev-* 7 | - local-* 8 | - temp-* 9 | pull_request: 10 | 11 | jobs: 12 | build_and_test: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | # If you change the test matrix, also change it in tox.ini 16 | matrix: 17 | # Test the latest stable Sphinx version on all current Python versions 18 | sphinx: ['4.0'] 19 | python: [3.6, 3.7, 3.8, 3.9] 20 | include: 21 | # Also test oldest support Sphinx version for each Python version 22 | # and ensure that we test all major versions of Sphinx too. 23 | # Note: X.0 versions must be quoted as string; Github would otherwise 24 | # set them as "X" (without the ".0"), which is undefined in tox.ini. 25 | 26 | # Sphinx 1.4 added support for Python 3.5 27 | - sphinx: 1.4 28 | python: 2.7 29 | - sphinx: 1.4 30 | python: 3.5 31 | - sphinx: '2.0' 32 | python: 3.5 33 | # Sphinx 1.5 added support for Python 3.6 34 | - sphinx: 1.5 35 | python: 3.6 36 | # Sphinx 1.8 added support for Python 3.7 and 3.8 37 | - sphinx: 1.8 38 | python: 2.7 39 | - sphinx: 1.8 40 | python: 3.8 41 | # Sphinx 2.4 added support for Python 3.9 42 | - sphinx: 2.4 43 | python: 3.9 44 | - sphinx: '3.0' 45 | python: 3.9 46 | - sphinx: 3.4 47 | python: 3.9 48 | name: Python ${{ matrix.python }} - Sphinx ${{ matrix.sphinx }} 49 | steps: 50 | - name: Checkout latest commit 51 | uses: actions/checkout@v2 52 | 53 | - name: Set up Python ${{ matrix.python }} 54 | uses: actions/setup-python@v2 55 | with: 56 | python-version: ${{ matrix.python }} 57 | 58 | - name: Cache pip 59 | uses: actions/cache@v2 60 | id: cache-pip 61 | with: 62 | path: ~/.cache/pip 63 | key: ${{ matrix.python }}-pip- 64 | 65 | - name: Install dependencies 66 | run: python -m pip install --upgrade tox 67 | 68 | - name: Run tests 69 | env: 70 | TOXENV: python${{ matrix.python }}-sphinx${{ matrix.sphinx }} 71 | run: tox -e $TOXENV 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | output/ 4 | sphinxcontrib/__pycache__ 5 | sphinxcontrib_restbuilder.egg-info 6 | .tox/ 7 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | restbuilder 0.3 (28 February 2021) 5 | ---------------------------------- 6 | * Now requires Sphinx 1.4 or higher (drop support for 1.0-1.3), support Sphinx 4.x. 7 | * Now requires Python 2.7, or Python 3.5 or higher (drop support for 3.0-3.4) 8 | Note: Python 2.7 and 3.5 are end of life and will no longer be actively supported. 9 | * Fix indentation for multi-line lists (Ned Batchelder) 10 | * Fix versionadd directive for Sphinx >= 3.0 (Ned Batchelder) 11 | * Preliminary support for tables (Jack Burridge) 12 | * Add test suite (Jack Burridge) 13 | * Add tox script and Github workflow to easily run tests 14 | 15 | restbuilder 0.2 (7 June 2018) 16 | -------------------------------- 17 | * Code moved to https://gitub.com/sphinx-contrib/restbuilder 18 | * Python 3 compatible (Nicola Musatti) 19 | * Adhere to rst_indent-specified indentation (Matthew Planchard) 20 | * Fixed issue where links were converted to plaintext (Matthew Planchard) 21 | 22 | restbuilder 0.1 (25 August 2013) 23 | -------------------------------- 24 | * Code submitted to sphinx-contrib 25 | https://bitbucket.org/birkenfeld/sphinx-contrib 26 | * Released as sphinxcontrib-restbuilder 27 | * Added basic documentation 28 | * Unsupported/unknown tags are not printed, but send to log facility. 29 | 30 | restbuilder (no version) (28 April 2012) 31 | ----------------------------------------- 32 | * First release as port of a documentation generator in the NBT package 33 | https://github.com/twoolie/NBT/commit/eefbd26c422a0e5f3c89e84fabcfb951a11722b0 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Thank you for contributing! 2 | =========================== 3 | 4 | Thank you for considering a contribution to Sphinx reStructuredText 5 | builder/writer, and for reading this document. 6 | 7 | Use issues for bug reports, feature requests, or any sort of feedback 8 | that you may have. 9 | 10 | If you are able to spend the time to create a pull request, that is 11 | most appreciated. 12 | 13 | 14 | Testing your code 15 | ================= 16 | 17 | * Run `pytest`_ to run all tests using your default Python version and 18 | Sphinx version. 19 | 20 | * To test with multiple Python version and Sphinx versions, run `tox`_. 21 | 22 | * Run ``tox -e python3.9-sphinx3.4`` to run tox with a specific Python 23 | and Sphinx version. 24 | 25 | * Each commit to master, and each pull request to the master branch is 26 | tested with a few different tests. See the actions tab for the 27 | results of each run, or click on the checkmark (✔ or ✘) listed with 28 | each commit. 29 | 30 | .. _pytest: https://www.pytest.org/ 31 | .. _tox: https://tox.readthedocs.io/ 32 | 33 | 34 | Adding and debugging tests 35 | ========================== 36 | 37 | * For testing, Sphinx is run for each test in the ``tests/datasets`` 38 | directory, and the output is written to the ``output`` directory. 39 | The result is then compared to the expected output in 40 | ``tests/expected``. 41 | 42 | * Specific features should be accompanied by a test. 43 | 44 | * Add a test file to one of the subdirectories in ``tests/datasets``, 45 | add the expected outcome to ``tests/expected``, and create a 46 | test runner in one of the ``test_*.py`` files in `tests`. 47 | 48 | * Run ``pytest -k testName`` to only run the test called testName. 49 | 50 | * Optionally run ``pytest -v -r A -W d`` for a very verbose output, 51 | including stdout and stderr for all tests (note that some tests like 52 | `nonexistent-target.rst` are expected to give a warning), and 53 | including all Python warnings. 54 | 55 | * Rather than a byte-by-byte comparison, ``output`` and 56 | ``tests/expected`` are compared after reading the file with docutils 57 | (which contains a basic reStructuredText parser), to allow for slight 58 | variations in layout. 59 | 60 | * If a test fails, the XML version of the parsed documents is written 61 | to the ``output`` directory. ``test-file.expected.xml`` for 62 | ``test-file.rst`` in ``tests/expected``, and ``test-file.output.xml`` 63 | for ``test-file.rst`` in ``output``. 64 | 65 | * If you want to see what to see how Sphinx parses a rst file, run e.g. 66 | ``sphinx-build -b xml -C tests/datasets output``. You can also 67 | specify ``-b html`` for HTML output. Beware that ``-b rst`` will use 68 | the restbuilder installed using pip, not the git checkout. This is 69 | most likely not what you intended. Run ``pytest`` instead. 70 | 71 | * Sphinx will read all files in a directory. So multiple tests in the 72 | same directory are not standalone! This means that you should not use 73 | the same identifier for hyperlink targets in different tests, as they 74 | may inadvertedly point to a target in a different file. 75 | Either use unique target names, or use a separate subdirectory for 76 | your test. 77 | 78 | 79 | Supported versions 80 | ================== 81 | 82 | * See ``.github/workflows/tests.yml`` for a list of which version are 83 | tested for by GitHub after each pull request. 84 | 85 | * See ``tox.ini`` for a list of which version are tested for by ``tox``. 86 | These two files should be kept in sync. 87 | (Note: at this moment, ``tox`` will still run Python 2.7 tests; 88 | GitHub will not.) 89 | 90 | * As a rule of thumb, this project supports all Sphinx versions 91 | starting from the one that came with the previous stable Debian 92 | distribution (`oldstable/sphinx-doc`_) till the most recent version. 93 | It supports all Python versions starting with the one that came with 94 | the previous stable Debian distribution (`oldstable/python3`_). 95 | This is currently Python 3.5 with Sphinx 1.4. 96 | 97 | * Python 2.7 is partially supported: some basis tests will work, but 98 | other tests are known to fail are disabled. We consider pull requests 99 | to add Python 2.7 support for these features, but will not actively 100 | fix it ourselves. 101 | 102 | * The same applies for Sphinx 1.4 - 1.8. Sphinx 1.x is partially 103 | supported: some basis tests will work, but other tests are known to 104 | fail, and are disabled. We consider pull requests to add Sphinx 1.x 105 | support for these features, but will not actively fix it ourselves. 106 | Look for ``@pytest.mark.skipif`` decorators in the test functions to 107 | see which features are currently not supported. 108 | 109 | .. _`oldstable/sphinx-doc`: https://packages.debian.org/oldstable/sphinx-doc 110 | .. _`oldstable/python3`: https://packages.debian.org/oldstable/python3 111 | 112 | 113 | Publishing versions 114 | =================== 115 | 116 | The code is available on the Python package index (PyPI) at 117 | https://pypi.org/project/sphinxcontrib-restbuilder/. 118 | 119 | *This following section is only relevant for maintainers.* 120 | 121 | Creating a release 122 | ------------------ 123 | 124 | Change the following files:: 125 | 126 | CHANGES.rst 127 | CONTRIBUTORS.txt (Add new contributors, if any) 128 | sphinxcontrib/restbuilder.py (Change version constant) 129 | 130 | Commit the changes, add a tag, and upload the changes:: 131 | 132 | git add CHANGES.rst CONTRIBUTORS.txt sphinxcontrib/restbuilder.py 133 | git commit -m "Bump version number to 0.1.2" 134 | git tag -a v0.1.2 HEAD 135 | git push --tags 136 | 137 | Publish a release at GitHub 138 | --------------------------- 139 | 140 | * Go to https://github.com/sphinx-contrib/restbuilder/releases 141 | * Click the "Draft a new release" button 142 | * Select the tag, and add a Release title (e.g. Sphinx Restbuilder 0.1.2) 143 | and release notes. I usually only list the most important changes, 144 | with optional link to CHANGES.rst for more details. 145 | 146 | Publish a release at PyPI 147 | ------------------------- 148 | 149 | Follow the process described at https://packaging.python.org/tutorials/distributing-packages/. 150 | 151 | Install requirements:: 152 | 153 | pip install --upgrade pip 154 | pip install setuptools wheel twine 155 | 156 | Create both a source (.tar.gz) and wheel (.whl) distribution:: 157 | 158 | python setup.py sdist 159 | python setup.py bdist_wheel --universal 160 | 161 | Upload all that was created to PyPI:: 162 | 163 | ls dist/ 164 | twine upload dist/* 165 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | Freek Dijkstra (macfreek) 2 | G. Nicholas d'Andrea (gnidan) 3 | Jeffrey Lo (jeffrey_lo) 4 | Matthew Planchard (mplanchard) 5 | Nicola Musatti (nmusatti) 6 | Jack Burridge (jackburridge) 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2021 by Freek Dijkstra and contributors. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE.txt 3 | include CHANGES.rst 4 | include CONTRIBUTING.rst 5 | include CONTRIBUTORS.txt 6 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. -*- restructuredtext -*- 2 | 3 | ======================= 4 | README for reST Builder 5 | ======================= 6 | 7 | Sphinx_ extension to build reST (reStructuredText_) files. 8 | 9 | This extension is in particular useful to use in combination with the autodoc 10 | extension to automatically generate documentation for use by any rst parser 11 | (such as the GitHub wiki). 12 | 13 | In itself, the extension is fairly straightforward -- it takes the parsed reST 14 | file from Sphinx_ and outputs it as reST. 15 | 16 | Requirements 17 | ============ 18 | 19 | * Sphinx_ 2.0 20 | * Python 3.5 21 | 22 | Sphinx 1.4 - 1.8 and Python 2.7 are partly supported. It does work, but some 23 | markup may not parse correctly. 24 | 25 | Installing 26 | ========== 27 | 28 | Using pip 29 | --------- 30 | 31 | :: 32 | 33 | pip install sphinxcontrib-restbuilder 34 | 35 | Manual 36 | ------ 37 | 38 | :: 39 | 40 | git clone https://github.com/sphinx-contrib/restbuilder.git 41 | cd restbuilder 42 | python setup.py install 43 | 44 | If you want to try reST builder without using the setuptools installer, 45 | you can put the reST builder in an extension subdirectory, and adjust 46 | ``sys.path`` to tell Sphinx where to look for it: 47 | 48 | - Add the extensions directory to the path in ``conf.py``. E.g.:: 49 | 50 | sys.path.append(os.path.abspath('exts')) 51 | 52 | Usage 53 | ===== 54 | 55 | - Set the builder as a extension in ``conf.py``:: 56 | 57 | extensions = ['sphinxcontrib.restbuilder'] 58 | 59 | - Run sphinx-build with target ``rst``:: 60 | 61 | sphinx-build -b rst -c . build/rst 62 | 63 | Configuration 64 | ============= 65 | 66 | The following four configuration variables are defined by sphinxcontrib.restbuilder: 67 | 68 | :literal:`rst_file_suffix` 69 | This is the file name suffix for generated reST files. The default is 70 | ``".rst"``. 71 | 72 | :literal:`rst_link_suffix` 73 | Suffix for generated links to reST files. The default is whatever 74 | `rst_file_suffix` is set to. 75 | 76 | :literal:`rst_file_transform` 77 | Function to translate a docname to a filename. 78 | By default, returns `docname` + :literal:`rst_file_suffix`. 79 | 80 | :literal:`rst_link_transform`: 81 | Function to translate a docname to a (partial) URI. 82 | By default, returns `docname` + :literal:`rst_link_suffix`. 83 | 84 | 85 | Further Reading 86 | =============== 87 | 88 | .. _Sphinx: http://sphinx-doc.org/ 89 | .. _`sphinx-contrib`: http://bitbucket.org/birkenfeld/sphinx-contrib 90 | .. _reStructuredText: http://docutils.sourceforge.net/rst.html 91 | 92 | Feedback 93 | ======== 94 | 95 | The reST builder is in a preliminary state. It's not (yet) widely used, so 96 | any feedback is particularly welcome. 97 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup, find_packages 4 | 5 | def get_restbuilder_version(): 6 | # load sphinxcontrib.restbuilder from local path. 7 | # (Lots of work, just to get the version info) 8 | from os.path import join, dirname 9 | import sys 10 | restbuilder_path = join(dirname(__file__), 'sphinxcontrib', 'restbuilder.py') 11 | if sys.version_info >= (3, 5): 12 | # requires Python 3.5 or up. 13 | import importlib.util 14 | spec = importlib.util.spec_from_file_location('sphinxcontrib.restbuilder', restbuilder_path) 15 | restbuilder = importlib.util.module_from_spec(spec) 16 | spec.loader.exec_module(restbuilder) 17 | else: 18 | # Python 2.7 support 19 | import imp 20 | restbuilder = imp.load_source('sphinxcontrib.restbuilder', restbuilder_path) 21 | return restbuilder.__version__ 22 | 23 | long_desc = ''' 24 | Sphinx_ extension to build and write reStructuredText_ (reST / rst) files. 25 | 26 | This extension is in particular useful to use in combination with the autodoc 27 | extension to automatically generate documentation for use by any rst parser 28 | (such as the GitHub wiki, which does not support the advanced Sphinx directives). 29 | 30 | In itself, the extension is fairly straightforward -- it takes the parsed 31 | reStructuredText file from Sphinx_ and outputs it as reStructuredText. 32 | 33 | .. _Sphinx: http://sphinx-doc.org/ 34 | .. _reStructuredText: http://docutils.sourceforge.net/rst.html 35 | ''' 36 | 37 | requires = ['Sphinx>=1.4', 'docutils'] 38 | 39 | setup( 40 | name='sphinxcontrib-restbuilder', 41 | version=get_restbuilder_version(), 42 | url='https://github.com/sphinx-contrib/restbuilder', 43 | download_url='http://pypi.python.org/pypi/sphinxcontrib-restbuilder', 44 | license='BSD 2-Clause', 45 | author='Freek Dijkstra', 46 | author_email='freek@macfreek.nl', 47 | description='Sphinx extension to output reST files.', 48 | long_description=long_desc, 49 | zip_safe=False, 50 | classifiers=[ 51 | 'Development Status :: 4 - Beta', 52 | 'Environment :: Console', 53 | 'Framework :: Sphinx :: Extension', 54 | 'Intended Audience :: Developers', 55 | 'License :: OSI Approved :: BSD License', 56 | 'Operating System :: OS Independent', 57 | 'Programming Language :: Python :: 2', 58 | 'Programming Language :: Python :: 3', 59 | 'Topic :: Documentation :: Sphinx', 60 | 'Topic :: Software Development :: Documentation', 61 | 'Topic :: Text Processing :: Markup :: reStructuredText', 62 | ], 63 | platforms='any', 64 | python_requires='>=2.7, !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*', 65 | packages=find_packages(exclude=['tests']), 66 | include_package_data=True, 67 | install_requires=requires, 68 | namespace_packages=['sphinxcontrib'], 69 | ) 70 | -------------------------------------------------------------------------------- /sphinxcontrib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sphinxcontrib 4 | ~~~~~~~~~~~~~ 5 | 6 | This package is a namespace package that contains all extensions 7 | distributed in the ``sphinx-contrib`` distribution. 8 | 9 | :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. 10 | :license: BSD, see LICENSE for details. 11 | """ 12 | 13 | __import__('pkg_resources').declare_namespace(__name__) 14 | 15 | -------------------------------------------------------------------------------- /sphinxcontrib/builders/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sphinxcontrib.builders 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Custom docutils builders. 7 | """ 8 | -------------------------------------------------------------------------------- /sphinxcontrib/builders/rst.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sphinxcontrib.builders.rst 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | ReST Sphinx builder. 7 | 8 | :copyright: Copyright 2012-2021 by Freek Dijkstra and contributors. 9 | :license: BSD, see LICENSE.txt for details. 10 | """ 11 | 12 | from __future__ import (print_function, unicode_literals, absolute_import) 13 | 14 | import codecs 15 | from os import path 16 | 17 | from docutils.io import StringOutput 18 | 19 | from sphinx.builders import Builder 20 | from sphinx.util.osutil import ensuredir, SEP 21 | from ..writers.rst import RstWriter 22 | 23 | 24 | class RstBuilder(Builder): 25 | name = 'rst' 26 | format = 'rst' 27 | file_suffix = '.rst' 28 | link_suffix = None # defaults to file_suffix 29 | 30 | def init(self): 31 | """Load necessary templates and perform initialization.""" 32 | if self.config.rst_file_suffix is not None: 33 | self.file_suffix = self.config.rst_file_suffix 34 | if self.config.rst_link_suffix is not None: 35 | self.link_suffix = self.config.rst_link_suffix 36 | elif self.link_suffix is None: 37 | self.link_suffix = self.file_suffix 38 | 39 | # Function to convert the docname to a reST file name. 40 | def file_transform(docname): 41 | return docname + self.file_suffix 42 | 43 | # Function to convert the docname to a relative URI. 44 | def link_transform(docname): 45 | return docname + self.link_suffix 46 | 47 | if self.config.rst_file_transform is not None: 48 | self.file_transform = self.config.rst_file_transform 49 | else: 50 | self.file_transform = file_transform 51 | if self.config.rst_link_transform is not None: 52 | self.link_transform = self.config.rst_link_transform 53 | else: 54 | self.link_transform = link_transform 55 | 56 | def get_outdated_docs(self): 57 | """ 58 | Return an iterable of input files that are outdated. 59 | """ 60 | # This method is taken from TextBuilder.get_outdated_docs() 61 | # with minor changes to support :confval:`rst_file_transform`. 62 | for docname in self.env.found_docs: 63 | if docname not in self.env.all_docs: 64 | yield docname 65 | continue 66 | sourcename = path.join(self.env.srcdir, docname + 67 | self.file_suffix) 68 | targetname = path.join(self.outdir, self.file_transform(docname)) 69 | # print (sourcename, targetname) 70 | 71 | try: 72 | targetmtime = path.getmtime(targetname) 73 | except Exception: 74 | targetmtime = 0 75 | try: 76 | srcmtime = path.getmtime(sourcename) 77 | if srcmtime > targetmtime: 78 | yield docname 79 | except EnvironmentError: 80 | # source doesn't exist anymore 81 | pass 82 | 83 | def get_target_uri(self, docname, typ=None): 84 | return self.link_transform(docname) 85 | 86 | def prepare_writing(self, docnames): 87 | self.writer = RstWriter(self) 88 | 89 | def write_doc(self, docname, doctree): 90 | # This method is taken from TextBuilder.write_doc() 91 | # with minor changes to support :confval:`rst_file_transform`. 92 | destination = StringOutput(encoding='utf-8') 93 | # print "write(%s,%s)" % (type(doctree), type(destination)) 94 | 95 | self.writer.write(doctree, destination) 96 | outfilename = path.join(self.outdir, self.file_transform(docname)) 97 | # print "write(%s,%s) -> %s" % (type(doctree), type(destination), outfilename) 98 | ensuredir(path.dirname(outfilename)) 99 | try: 100 | f = codecs.open(outfilename, 'w', 'utf-8') 101 | try: 102 | f.write(self.writer.output) 103 | finally: 104 | f.close() 105 | except (IOError, OSError) as err: 106 | self.warn("error writing file %s: %s" % (outfilename, err)) 107 | 108 | def finish(self): 109 | pass 110 | -------------------------------------------------------------------------------- /sphinxcontrib/restbuilder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sphinxcontrib.restbuilder 4 | ========================= 5 | 6 | Sphinx extension to output reST files. 7 | 8 | .. moduleauthor:: Freek Dijkstra 9 | 10 | :copyright: Copyright 2012-2021 by Freek Dijkstra and contributors. 11 | :license: BSD, see LICENSE.txt for details. 12 | """ 13 | 14 | from __future__ import (print_function, unicode_literals, absolute_import) 15 | 16 | __version__ = '0.3.0' 17 | __author__ = 'Freek Dijkstra and contributors' 18 | 19 | def setup(app): 20 | # imports defined inside setup function, so that the __version__ can be loaded, 21 | # even if Sphinx is not yet installed. 22 | from sphinx.writers.text import STDINDENT 23 | from .builders.rst import RstBuilder # loads RstWriter as well. 24 | 25 | app.require_sphinx('1.4') 26 | app.add_builder(RstBuilder) 27 | app.add_config_value('rst_file_suffix', ".rst", False) 28 | """This is the file name suffix for reST files""" 29 | app.add_config_value('rst_link_suffix', None, False) 30 | """The is the suffix used in internal links. By default, takes the same value as rst_file_suffix""" 31 | app.add_config_value('rst_file_transform', None, False) 32 | """Function to translate a docname to a filename. By default, returns docname + rst_file_suffix.""" 33 | app.add_config_value('rst_link_transform', None, False) 34 | """Function to translate a docname to a (partial) URI. By default, returns docname + rst_link_suffix.""" 35 | app.add_config_value('rst_indent', STDINDENT, False) 36 | 37 | return { 38 | 'version': __version__, 39 | # 'env_version': 1, # not needed; restbuilder does not store data in the environment 40 | 'parallel_read_safe': True, 41 | 'parallel_write_safe': True, 42 | } 43 | -------------------------------------------------------------------------------- /sphinxcontrib/writers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sphinxcontrib.writers 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Custom docutils writers. 7 | """ 8 | -------------------------------------------------------------------------------- /sphinxcontrib/writers/rst.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sphinxcontrib.writers.rst 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Custom docutils writer for ReStructuredText. 7 | 8 | :copyright: Copyright 2012-2021 by Freek Dijkstra and contributors. 9 | :license: BSD, see LICENSE.txt for details. 10 | 11 | Based on sphinx.writers.text.TextWriter, copyright 2007-2014 by the Sphinx team. 12 | """ 13 | 14 | from __future__ import (print_function, unicode_literals, absolute_import) 15 | 16 | import os 17 | import sys 18 | import textwrap 19 | import logging 20 | 21 | from docutils import nodes, writers 22 | from docutils.nodes import fully_normalize_name 23 | 24 | from sphinx import addnodes 25 | from sphinx.locale import admonitionlabels, _ 26 | from sphinx.writers.text import MAXWIDTH, STDINDENT 27 | 28 | 29 | def escape_uri(uri): 30 | if uri.endswith('_'): 31 | uri = uri[:-1] + '\\_' 32 | return uri 33 | 34 | 35 | class RstWriter(writers.Writer): 36 | supported = ('text',) 37 | settings_spec = ('No options here.', '', ()) 38 | settings_defaults = {} 39 | 40 | output = None 41 | 42 | def __init__(self, builder): 43 | writers.Writer.__init__(self) 44 | self.builder = builder 45 | 46 | def translate(self): 47 | visitor = RstTranslator(self.document, self.builder) 48 | self.document.walkabout(visitor) 49 | self.output = visitor.body 50 | 51 | 52 | class RstTranslator(nodes.NodeVisitor): 53 | sectionchars = '*=-~"+`' 54 | 55 | def __init__(self, document, builder): 56 | nodes.NodeVisitor.__init__(self, document) 57 | 58 | self.document = document 59 | self.builder = builder 60 | 61 | newlines = builder.config.text_newlines 62 | if newlines == 'windows': 63 | self.nl = '\r\n' 64 | elif newlines == 'native': 65 | self.nl = os.linesep 66 | else: 67 | self.nl = '\n' 68 | self.sectionchars = builder.config.text_sectionchars 69 | self.states = [[]] 70 | self.stateindent = [0] 71 | self.list_counter = [] 72 | self.list_formatter = [] 73 | self.sectionlevel = 0 74 | self.table = None 75 | if self.builder.config.rst_indent: 76 | self.indent = self.builder.config.rst_indent 77 | else: 78 | self.indent = STDINDENT 79 | self.wrapper = textwrap.TextWrapper(width=MAXWIDTH, break_long_words=False, break_on_hyphens=False) 80 | 81 | def log_warning(self, message): 82 | logger = logging.getLogger("sphinxcontrib.writers.rst") 83 | if len(logger.handlers) == 0: 84 | # Logging is not yet configured. Configure it. 85 | logging.basicConfig(level=logging.INFO, stream=sys.stderr, format='%(levelname)-8s %(message)s') 86 | logger = logging.getLogger("sphinxcontrib.writers.rst") 87 | logger.warning(message) 88 | 89 | def log_unknown(self, type, node): 90 | self.log_warning("%s(%s) unsupported formatting" % (type, node)) 91 | 92 | def wrap(self, text, width=MAXWIDTH): 93 | self.wrapper.width = width 94 | return self.wrapper.wrap(text) 95 | 96 | def add_text(self, text): 97 | self.states[-1].append((-1, text)) 98 | def new_state(self, indent=STDINDENT): 99 | self.states.append([]) 100 | self.stateindent.append(indent) 101 | def end_state(self, wrap=True, end=[''], first=None): 102 | content = self.states.pop() 103 | width = max(MAXWIDTH//3, MAXWIDTH - sum(self.stateindent)) 104 | indent = self.stateindent.pop() 105 | result = [] 106 | toformat = [] 107 | def do_format(): 108 | if not toformat: 109 | return 110 | if wrap: 111 | res = self.wrap(''.join(toformat), width=width) 112 | else: 113 | res = ''.join(toformat).splitlines() 114 | if end: 115 | res += end 116 | result.append((indent, res)) 117 | for itemindent, item in content: 118 | if itemindent == -1: 119 | toformat.append(item) 120 | else: 121 | do_format() 122 | result.append((indent + itemindent, item)) 123 | toformat = [] 124 | do_format() 125 | if first is not None and result: 126 | itemindent, item = result[0] 127 | if item: 128 | result.insert(0, (itemindent - indent, [first + item[0]])) 129 | result[1] = (itemindent, item[1:]) 130 | self.states[-1].extend(result) 131 | 132 | def visit_document(self, node): 133 | self.new_state(0) 134 | def depart_document(self, node): 135 | self.end_state() 136 | self.body = self.nl.join(line and (' '*indent + line) 137 | for indent, lines in self.states[0] 138 | for line in lines) 139 | # TODO: add header/footer? 140 | 141 | def visit_highlightlang(self, node): 142 | raise nodes.SkipNode 143 | 144 | def visit_section(self, node): 145 | self._title_char = self.sectionchars[self.sectionlevel] 146 | self.sectionlevel += 1 147 | def depart_section(self, node): 148 | self.sectionlevel -= 1 149 | 150 | def visit_topic(self, node): 151 | self.new_state(0) 152 | def depart_topic(self, node): 153 | self.end_state() 154 | 155 | visit_sidebar = visit_topic 156 | depart_sidebar = depart_topic 157 | 158 | def visit_rubric(self, node): 159 | self.new_state(0) 160 | self.add_text('-[ ') 161 | def depart_rubric(self, node): 162 | self.add_text(' ]-') 163 | self.end_state() 164 | 165 | def visit_compound(self, node): 166 | # self.log_unknown("compount", node) 167 | pass 168 | def depart_compound(self, node): 169 | pass 170 | 171 | def visit_glossary(self, node): 172 | # self.log_unknown("glossary", node) 173 | pass 174 | def depart_glossary(self, node): 175 | pass 176 | 177 | def visit_title(self, node): 178 | if isinstance(node.parent, nodes.Admonition): 179 | self.add_text(node.astext()+': ') 180 | raise nodes.SkipNode 181 | self.new_state(0) 182 | def depart_title(self, node): 183 | if isinstance(node.parent, nodes.section): 184 | char = self._title_char 185 | else: 186 | char = '^' 187 | text = ''.join(x[1] for x in self.states.pop() if x[0] == -1) 188 | self.stateindent.pop() 189 | self.states[-1].append((0, ['', text, '%s' % (char * len(text)), ''])) 190 | 191 | def visit_subtitle(self, node): 192 | # self.log_unknown("subtitle", node) 193 | pass 194 | def depart_subtitle(self, node): 195 | pass 196 | 197 | def visit_attribution(self, node): 198 | self.add_text('-- ') 199 | def depart_attribution(self, node): 200 | pass 201 | 202 | def visit_desc(self, node): 203 | self.new_state(0) 204 | def depart_desc(self, node): 205 | self.end_state() 206 | 207 | def visit_desc_signature(self, node): 208 | if node.parent['objtype'] in ('class', 'exception', 'method', 'function'): 209 | self.add_text('**') 210 | else: 211 | self.add_text('``') 212 | def depart_desc_signature(self, node): 213 | if node.parent['objtype'] in ('class', 'exception', 'method', 'function'): 214 | self.add_text('**') 215 | else: 216 | self.add_text('``') 217 | 218 | def visit_desc_name(self, node): 219 | # self.log_unknown("desc_name", node) 220 | pass 221 | def depart_desc_name(self, node): 222 | pass 223 | 224 | def visit_desc_addname(self, node): 225 | # self.log_unknown("desc_addname", node) 226 | pass 227 | def depart_desc_addname(self, node): 228 | pass 229 | 230 | def visit_desc_type(self, node): 231 | # self.log_unknown("desc_type", node) 232 | pass 233 | def depart_desc_type(self, node): 234 | pass 235 | 236 | def visit_desc_returns(self, node): 237 | self.add_text(' -> ') 238 | def depart_desc_returns(self, node): 239 | pass 240 | 241 | def visit_desc_parameterlist(self, node): 242 | self.add_text('(') 243 | self.first_param = 1 244 | def depart_desc_parameterlist(self, node): 245 | self.add_text(')') 246 | 247 | def visit_desc_parameter(self, node): 248 | if not self.first_param: 249 | self.add_text(', ') 250 | else: 251 | self.first_param = 0 252 | self.add_text(node.astext()) 253 | raise nodes.SkipNode 254 | 255 | def visit_desc_optional(self, node): 256 | self.add_text('[') 257 | def depart_desc_optional(self, node): 258 | self.add_text(']') 259 | 260 | def visit_desc_annotation(self, node): 261 | content = node.astext() 262 | if len(content) > MAXWIDTH: 263 | h = int(MAXWIDTH/3) 264 | content = content[:h] + " ... " + content[-h:] 265 | self.add_text(content) 266 | raise nodes.SkipNode 267 | def depart_desc_annotation(self, node): 268 | pass 269 | 270 | def visit_refcount(self, node): 271 | pass 272 | def depart_refcount(self, node): 273 | pass 274 | 275 | def visit_desc_content(self, node): 276 | self.new_state(self.indent) 277 | def depart_desc_content(self, node): 278 | self.end_state() 279 | 280 | def visit_figure(self, node): 281 | self.new_state(self.indent) 282 | def depart_figure(self, node): 283 | self.end_state() 284 | 285 | def visit_caption(self, node): 286 | # self.log_unknown("caption", node) 287 | pass 288 | def depart_caption(self, node): 289 | pass 290 | 291 | def visit_productionlist(self, node): 292 | self.new_state(self.indent) 293 | names = [] 294 | for production in node: 295 | names.append(production['tokenname']) 296 | maxlen = max(len(name) for name in names) 297 | for production in node: 298 | if production['tokenname']: 299 | self.add_text(production['tokenname'].ljust(maxlen) + ' ::=') 300 | lastname = production['tokenname'] 301 | else: 302 | self.add_text('%s ' % (' '*len(lastname))) 303 | self.add_text(production.astext() + self.nl) 304 | self.end_state(wrap=False) 305 | raise nodes.SkipNode 306 | 307 | def visit_seealso(self, node): 308 | self.new_state(self.indent) 309 | def depart_seealso(self, node): 310 | self.end_state(first='') 311 | 312 | def visit_footnote(self, node): 313 | self._footnote = node.children[0].astext().strip() 314 | self.new_state(len(self._footnote) + self.indent) 315 | def depart_footnote(self, node): 316 | self.end_state(first='[%s] ' % self._footnote) 317 | 318 | def visit_citation(self, node): 319 | if len(node) and isinstance(node[0], nodes.label): 320 | self._citlabel = node[0].astext() 321 | else: 322 | self._citlabel = '' 323 | self.new_state(len(self._citlabel) + self.indent) 324 | def depart_citation(self, node): 325 | self.end_state(first='[%s] ' % self._citlabel) 326 | 327 | def visit_label(self, node): 328 | raise nodes.SkipNode 329 | 330 | # TODO: option list could use some better styling 331 | 332 | def visit_option_list(self, node): 333 | # self.log_unknown("option_list", node) 334 | pass 335 | def depart_option_list(self, node): 336 | pass 337 | 338 | def visit_option_list_item(self, node): 339 | self.new_state(0) 340 | def depart_option_list_item(self, node): 341 | self.end_state() 342 | 343 | def visit_option_group(self, node): 344 | self._firstoption = True 345 | def depart_option_group(self, node): 346 | self.add_text(' ') 347 | 348 | def visit_option(self, node): 349 | if self._firstoption: 350 | self._firstoption = False 351 | else: 352 | self.add_text(', ') 353 | def depart_option(self, node): 354 | pass 355 | 356 | def visit_option_string(self, node): 357 | # self.log_unknown("option_string", node) 358 | pass 359 | def depart_option_string(self, node): 360 | pass 361 | 362 | def visit_option_argument(self, node): 363 | self.add_text(node['delimiter']) 364 | def depart_option_argument(self, node): 365 | pass 366 | 367 | def visit_description(self, node): 368 | # self.log_unknown("description", node) 369 | pass 370 | def depart_description(self, node): 371 | pass 372 | 373 | def visit_tabular_col_spec(self, node): 374 | raise nodes.SkipNode 375 | 376 | def visit_colspec(self, node): 377 | self.table[0].append(round(node['colwidth'])) 378 | raise nodes.SkipNode 379 | 380 | def visit_tgroup(self, node): 381 | # self.log_unknown("tgroup", node) 382 | pass 383 | def depart_tgroup(self, node): 384 | pass 385 | 386 | def visit_thead(self, node): 387 | # self.log_unknown("thead", node) 388 | pass 389 | def depart_thead(self, node): 390 | pass 391 | 392 | def visit_tbody(self, node): 393 | self.table.append('sep') 394 | def depart_tbody(self, node): 395 | pass 396 | 397 | def visit_row(self, node): 398 | self.table.append([]) 399 | def depart_row(self, node): 400 | pass 401 | 402 | def visit_entry(self, node): 403 | if 'morerows' in node or 'morecols' in node: 404 | self.log_warning('Column or row spanning cells are not implemented.') 405 | self.new_state(0) 406 | def depart_entry(self, node): 407 | text = self.nl.join(self.nl.join(x[1]) for x in self.states.pop()) 408 | self.stateindent.pop() 409 | self.table[-1].append(text) 410 | 411 | def visit_table(self, node): 412 | if self.table: 413 | self.log_warning('Nested tables are not supported.') 414 | self.new_state(0) 415 | self.table = [[]] 416 | def depart_table(self, node): 417 | lines = self.table[1:] 418 | fmted_rows = [] 419 | colwidths = self.table[0] 420 | realwidths = colwidths[:] 421 | separator = 0 422 | # don't allow paragraphs in table cells for now 423 | for line in lines: 424 | if line == 'sep': 425 | separator = len(fmted_rows) 426 | else: 427 | cells = [] 428 | for i, cell in enumerate(line): 429 | par = self.wrap(cell, width=colwidths[i]) 430 | if par: 431 | maxwidth = max(list(map(len, par))) 432 | else: 433 | maxwidth = 0 434 | realwidths[i] = max(realwidths[i], maxwidth) 435 | cells.append(par) 436 | fmted_rows.append(cells) 437 | 438 | def writesep(char='-'): 439 | out = ['+'] 440 | for width in realwidths: 441 | out.append(char * (width+2)) 442 | out.append('+') 443 | self.add_text(''.join(out) + self.nl) 444 | 445 | def writerow(row): 446 | lines = list(zip(*row)) 447 | for line in lines: 448 | out = ['|'] 449 | for i, cell in enumerate(line): 450 | if cell: 451 | out.append(' ' + cell.ljust(realwidths[i]+1)) 452 | else: 453 | out.append(' ' * (realwidths[i] + 2)) 454 | out.append('|') 455 | self.add_text(''.join(out) + self.nl) 456 | 457 | for i, row in enumerate(fmted_rows): 458 | if separator and i == separator: 459 | writesep('=') 460 | else: 461 | writesep('-') 462 | writerow(row) 463 | writesep('-') 464 | self.table = None 465 | self.end_state(wrap=False) 466 | 467 | def visit_acks(self, node): 468 | self.new_state(0) 469 | self.add_text(', '.join(n.astext() for n in node.children[0].children) 470 | + '.') 471 | self.end_state() 472 | raise nodes.SkipNode 473 | 474 | def visit_image(self, node): 475 | self.new_state(0) 476 | if 'uri' in node: 477 | self.add_text(_('.. image:: %s') % escape_uri(node['uri'])) 478 | elif 'target' in node.attributes: 479 | self.add_text(_('.. image: %s') % node['target']) 480 | elif 'alt' in node.attributes: 481 | self.add_text(_('[image: %s]') % node['alt']) 482 | else: 483 | self.add_text(_('[image]')) 484 | self.end_state(wrap=False) 485 | raise nodes.SkipNode 486 | 487 | def visit_transition(self, node): 488 | indent = sum(self.stateindent) 489 | self.new_state(0) 490 | self.add_text('=' * (MAXWIDTH - indent)) 491 | self.end_state() 492 | raise nodes.SkipNode 493 | 494 | def visit_bullet_list(self, node): 495 | def bullet_list_format(counter): 496 | return '*' 497 | self.list_counter.append(-1) # TODO: just 0 is fine. 498 | self.list_formatter.append(bullet_list_format) 499 | def depart_bullet_list(self, node): 500 | self.list_counter.pop() 501 | self.list_formatter.pop() 502 | 503 | def visit_enumerated_list(self, node): 504 | def enumerated_list_format(counter): 505 | return str(counter) + '.' 506 | self.list_counter.append(0) 507 | self.list_formatter.append(enumerated_list_format) 508 | def depart_enumerated_list(self, node): 509 | self.list_counter.pop() 510 | self.list_formatter.pop() 511 | 512 | def visit_list_item(self, node): 513 | self.list_counter[-1] += 1 514 | bullet_formatter = self.list_formatter[-1] 515 | bullet = bullet_formatter(self.list_counter[-1]) 516 | indent = max(self.indent, len(bullet) + 1) 517 | self.new_state(indent) 518 | def depart_list_item(self, node): 519 | # formatting to make the string `self.stateindent[-1]` chars long. 520 | format = '%%-%ds' % (self.stateindent[-1]) 521 | bullet_formatter = self.list_formatter[-1] 522 | bullet = format % bullet_formatter(self.list_counter[-1]) 523 | self.end_state(first=bullet, end=None) 524 | 525 | def visit_definition_list(self, node): 526 | pass 527 | def depart_definition_list(self, node): 528 | pass 529 | 530 | def visit_definition_list_item(self, node): 531 | self._li_has_classifier = len(node) >= 2 and \ 532 | isinstance(node[1], nodes.classifier) 533 | def depart_definition_list_item(self, node): 534 | pass 535 | 536 | def visit_term(self, node): 537 | self.new_state(0) 538 | def depart_term(self, node): 539 | if not self._li_has_classifier: 540 | self.end_state(end=None) 541 | 542 | def visit_termsep(self, node): 543 | self.add_text(', ') 544 | raise nodes.SkipNode 545 | 546 | def visit_classifier(self, node): 547 | self.add_text(' : ') 548 | def depart_classifier(self, node): 549 | self.end_state(end=None) 550 | 551 | def visit_definition(self, node): 552 | self.new_state(self.indent) 553 | def depart_definition(self, node): 554 | self.end_state() 555 | 556 | def visit_field_list(self, node): 557 | # self.log_unknown("field_list", node) 558 | pass 559 | def depart_field_list(self, node): 560 | pass 561 | 562 | def visit_field(self, node): 563 | self.new_state(0) 564 | def depart_field(self, node): 565 | self.end_state(end=None) 566 | 567 | def visit_field_name(self, node): 568 | self.add_text(':') 569 | def depart_field_name(self, node): 570 | self.add_text(':') 571 | content = node.astext() 572 | self.add_text((16-len(content))*' ') 573 | 574 | def visit_field_body(self, node): 575 | self.new_state(self.indent) 576 | def depart_field_body(self, node): 577 | self.end_state() 578 | 579 | def visit_centered(self, node): 580 | pass 581 | def depart_centered(self, node): 582 | pass 583 | 584 | def visit_hlist(self, node): 585 | # self.log_unknown("hlist", node) 586 | pass 587 | def depart_hlist(self, node): 588 | pass 589 | 590 | def visit_hlistcol(self, node): 591 | # self.log_unknown("hlistcol", node) 592 | pass 593 | def depart_hlistcol(self, node): 594 | pass 595 | 596 | def visit_admonition(self, node): 597 | self.new_state(0) 598 | def depart_admonition(self, node): 599 | self.end_state() 600 | 601 | def _visit_admonition(self, node): 602 | self.new_state(self.indent) 603 | def _make_depart_admonition(name): 604 | def depart_admonition(self, node): 605 | self.end_state(first=admonitionlabels[name] + ': ') 606 | return depart_admonition 607 | 608 | visit_attention = _visit_admonition 609 | depart_attention = _make_depart_admonition('attention') 610 | visit_caution = _visit_admonition 611 | depart_caution = _make_depart_admonition('caution') 612 | visit_danger = _visit_admonition 613 | depart_danger = _make_depart_admonition('danger') 614 | visit_error = _visit_admonition 615 | depart_error = _make_depart_admonition('error') 616 | visit_hint = _visit_admonition 617 | depart_hint = _make_depart_admonition('hint') 618 | visit_important = _visit_admonition 619 | depart_important = _make_depart_admonition('important') 620 | visit_note = _visit_admonition 621 | depart_note = _make_depart_admonition('note') 622 | visit_tip = _visit_admonition 623 | depart_tip = _make_depart_admonition('tip') 624 | visit_warning = _visit_admonition 625 | depart_warning = _make_depart_admonition('warning') 626 | 627 | def visit_versionmodified(self, node): 628 | self.new_state(0) 629 | def depart_versionmodified(self, node): 630 | self.end_state() 631 | 632 | def visit_literal_block(self, node): 633 | is_code_block = False 634 | # Support for Sphinx < 2.0, which defines classes['code', 'language'] 635 | if 'code' in node.get('classes', []): 636 | is_code_block = True 637 | if node.get('language', 'default') == 'default' and len(node['classes']) >= 2: 638 | node['language'] = node['classes'][1] 639 | # highlight_args is the only way to distinguish between :: and .. code:: in Sphinx 2 or higher. 640 | if node.get('highlight_args') != None: 641 | is_code_block = True 642 | if is_code_block: 643 | if node.get('language', 'default') == 'default': 644 | directive = ".. code::" 645 | else: 646 | directive = ".. code:: %s" % node['language'] 647 | if node.get('linenos'): 648 | indent = self.indent * ' ' 649 | directive += "%s%s:number-lines:" % (self.nl, indent) 650 | else: 651 | directive = "::" 652 | self.new_state(0) 653 | self.add_text(directive) 654 | self.end_state(wrap=False) 655 | self.new_state(self.indent) 656 | 657 | def depart_literal_block(self, node): 658 | self.end_state(wrap=False) 659 | 660 | def visit_doctest_block(self, node): 661 | self.new_state(0) 662 | def depart_doctest_block(self, node): 663 | self.end_state(wrap=False) 664 | 665 | def visit_line_block(self, node): 666 | self.new_state(0) 667 | def depart_line_block(self, node): 668 | self.end_state(wrap=False) 669 | 670 | def visit_line(self, node): 671 | # self.log_unknown("line", node) 672 | pass 673 | def depart_line(self, node): 674 | pass 675 | 676 | def visit_block_quote(self, node): 677 | self.new_state(self.indent) 678 | def depart_block_quote(self, node): 679 | self.end_state() 680 | 681 | def visit_compact_paragraph(self, node): 682 | self.visit_paragraph(node) 683 | def depart_compact_paragraph(self, node): 684 | self.depart_paragraph(node) 685 | 686 | def visit_paragraph(self, node): 687 | if not isinstance(node.parent, nodes.Admonition) or \ 688 | isinstance(node.parent, addnodes.seealso): 689 | self.new_state(0) 690 | def depart_paragraph(self, node): 691 | if not isinstance(node.parent, nodes.Admonition) or \ 692 | isinstance(node.parent, addnodes.seealso): 693 | self.end_state() 694 | 695 | def visit_target(self, node): 696 | is_inline = node.parent.tagname in ('paragraph',) 697 | if is_inline or node.get('anonymous'): 698 | return 699 | refid = node.get('refid') 700 | refuri = node.get('refuri') 701 | if refid: 702 | self.new_state(0) 703 | if node.get('ids'): 704 | self.add_text(self.nl.join( 705 | '.. _%s: %s_' % (id, refid) for id in node['ids'] 706 | )) 707 | else: 708 | self.add_text('.. _'+node['refid']+':') 709 | self.end_state(wrap=False) 710 | raise nodes.SkipNode 711 | def depart_target(self, node): 712 | pass 713 | 714 | def visit_index(self, node): 715 | raise nodes.SkipNode 716 | 717 | def visit_substitution_definition(self, node): 718 | raise nodes.SkipNode 719 | 720 | def visit_pending_xref(self, node): 721 | pass 722 | def depart_pending_xref(self, node): 723 | pass 724 | 725 | def visit_reference(self, node): 726 | refname = node.get('name') 727 | refbody = node.astext() 728 | refuri = node.get('refuri') 729 | refid = node.get('refid') 730 | if node.get('anonymous'): 731 | underscore = '__' 732 | else: 733 | underscore = '_' 734 | if not refname: 735 | refname = refbody 736 | 737 | if refid: 738 | if refid == self.document.nameids.get(fully_normalize_name(refname)): 739 | self.add_text('`%s`%s' % (refname, underscore)) 740 | else: 741 | self.add_text('`%s <%s_>`%s' % (refname, refid, underscore)) 742 | raise nodes.SkipNode 743 | elif refuri: 744 | if refuri == refname: 745 | self.add_text(escape_uri(refuri)) 746 | else: 747 | self.add_text('`%s <%s>`%s' % (refname, escape_uri(refuri), underscore)) 748 | raise nodes.SkipNode 749 | 750 | def depart_reference(self, node): 751 | pass 752 | 753 | def visit_download_reference(self, node): 754 | self.log_unknown("download_reference", node) 755 | pass 756 | def depart_download_reference(self, node): 757 | pass 758 | 759 | def visit_emphasis(self, node): 760 | self.add_text('*') 761 | def depart_emphasis(self, node): 762 | self.add_text('*') 763 | 764 | def visit_literal_emphasis(self, node): 765 | self.add_text('*') 766 | def depart_literal_emphasis(self, node): 767 | self.add_text('*') 768 | 769 | def visit_strong(self, node): 770 | self.add_text('**') 771 | def depart_strong(self, node): 772 | self.add_text('**') 773 | 774 | def visit_abbreviation(self, node): 775 | self.add_text('') 776 | def depart_abbreviation(self, node): 777 | if node.hasattr('explanation'): 778 | self.add_text(' (%s)' % node['explanation']) 779 | 780 | def visit_title_reference(self, node): 781 | # self.log_unknown("title_reference", node) 782 | self.add_text('*') 783 | def depart_title_reference(self, node): 784 | self.add_text('*') 785 | 786 | def visit_literal(self, node): 787 | self.add_text('``') 788 | def depart_literal(self, node): 789 | self.add_text('``') 790 | 791 | def visit_subscript(self, node): 792 | self.add_text(':sub:`') 793 | 794 | def depart_subscript(self, node): 795 | self.add_text('`') 796 | 797 | def visit_superscript(self, node): 798 | self.add_text(':sup:`') 799 | 800 | def depart_superscript(self, node): 801 | self.add_text('`') 802 | 803 | def visit_footnote_reference(self, node): 804 | self.add_text('[%s]' % node.astext()) 805 | raise nodes.SkipNode 806 | 807 | def visit_citation_reference(self, node): 808 | self.add_text('[%s]' % node.astext()) 809 | raise nodes.SkipNode 810 | 811 | def visit_math_block(self, node): 812 | self.add_text(".. math::") 813 | self.new_state(self.indent) 814 | 815 | def depart_math_block(self, node): 816 | self.end_state(wrap=False) 817 | 818 | def visit_Text(self, node): 819 | self.add_text(node.astext()) 820 | def depart_Text(self, node): 821 | pass 822 | 823 | def visit_generated(self, node): 824 | # self.log_unknown("generated", node) 825 | pass 826 | def depart_generated(self, node): 827 | pass 828 | 829 | def visit_inline(self, node): 830 | pass 831 | def depart_inline(self, node): 832 | pass 833 | 834 | def visit_problematic(self, node): 835 | pass 836 | def depart_problematic(self, node): 837 | pass 838 | 839 | def visit_system_message(self, node): 840 | self.new_state(0) 841 | self.add_text('' % node.astext()) 842 | self.end_state() 843 | raise nodes.SkipNode 844 | 845 | def visit_comment(self, node): 846 | raise nodes.SkipNode 847 | 848 | def visit_meta(self, node): 849 | # only valid for HTML 850 | raise nodes.SkipNode 851 | 852 | def visit_raw(self, node): 853 | if 'text' in node.get('format', '').split(): 854 | self.body.append(node.astext()) 855 | raise nodes.SkipNode 856 | 857 | def visit_docinfo(self, node): 858 | raise nodes.SkipNode 859 | 860 | def unknown_visit(self, node): 861 | self.log_unknown(node.__class__.__name__, node) 862 | 863 | def unknown_departure(self, node): 864 | pass 865 | 866 | default_visit = unknown_visit 867 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sphinx-contrib/restbuilder/c8b9c0d5764480d0433215504311f14895aa2215/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, realpath, join 2 | import shutil 3 | 4 | import pytest 5 | 6 | 7 | @pytest.fixture 8 | def src_dir(): 9 | return join( 10 | dirname(realpath(__file__)), 'datasets' 11 | ) 12 | 13 | 14 | @pytest.fixture 15 | def expected_dir(): 16 | return join( 17 | dirname(realpath(__file__)), 'expected' 18 | ) 19 | 20 | 21 | @pytest.fixture(scope="session") 22 | def output_dir(): 23 | out_dir = realpath(join(dirname(realpath(__file__)), '..', 'output')) 24 | shutil.rmtree(out_dir, ignore_errors=True) 25 | return out_dir 26 | -------------------------------------------------------------------------------- /tests/datasets/common/bold.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This is **bold**. -------------------------------------------------------------------------------- /tests/datasets/common/bullet-list-consecutive.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | - one 4 | - two 5 | 6 | * three 7 | * four 8 | -------------------------------------------------------------------------------- /tests/datasets/common/bullet-list.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | First list: 4 | 5 | - one 6 | - two 7 | 8 | Another list: 9 | 10 | * three 11 | * four 12 | -------------------------------------------------------------------------------- /tests/datasets/common/definition-list.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Definition lists: 4 | 5 | what 6 | Definition lists associate a term with 7 | a definition. 8 | 9 | how 10 | The term is a one-line phrase, and the 11 | definition is one or more paragraphs or 12 | body elements, indented relative to the 13 | term. Blank lines are not allowed 14 | between term and definition. -------------------------------------------------------------------------------- /tests/datasets/common/external-hyperlinks.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Embedded external hyperlinks, like `Python 4 | `_. 5 | 6 | Indirect external hyperlinks, like Larch_ or Python_. 7 | 8 | `Write to me`_ with your questions. 9 | 10 | .. _Write to me: jdoe@example.com 11 | .. _Larch: https://en.wikipedia.org/wiki/Larch 12 | 13 | Anonymous hyperlinks, like `the web site of my favorite programming language`__. 14 | 15 | .. __: http://www.python.org 16 | 17 | Direct hyperlinks, like http://www.python.org. 18 | 19 | This link_ refers to a file called ``underscore_``. 20 | 21 | .. _link: underscore\_ 22 | -------------------------------------------------------------------------------- /tests/datasets/common/grid-table.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | +---+---+ 4 | | a | b | 5 | +===+===+ 6 | | c | d | 7 | +---+---+ 8 | -------------------------------------------------------------------------------- /tests/datasets/common/headings.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Headline 4 | ======== 5 | Text immediately following. 6 | 7 | Subheader 8 | --------- 9 | 10 | Blank line before paragraph. 11 | 12 | Another paragraph. 13 | 14 | Subsubheader 15 | ____________ 16 | 17 | Subheader 2 18 | ----------- 19 | 20 | Another Chapter 21 | =============== 22 | End of file. -------------------------------------------------------------------------------- /tests/datasets/common/hyperlink-targets.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. _target: 4 | 5 | The hyperlink target above points to this paragraph. 6 | Internal hyperlink targets may be chained. Multiple adjacent internal hyperlink targets all point to the same element: 7 | 8 | .. _target1: 9 | .. _target2: 10 | 11 | The targets target1_ and target2_ are synonyms; they both 12 | point to this paragraph. 13 | 14 | .. _one: two_ 15 | .. _two: three_ 16 | .. _three: 17 | 18 | Indirect hyperlink targets have a hyperlink reference in their link blocks. In the following example, target one_ indirectly references whatever target two_ references, and target two references target three_, an internal hyperlink target. In effect, all three reference the same thing. 19 | -------------------------------------------------------------------------------- /tests/datasets/common/indentation.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Not indented 4 | 5 | Four spaces 6 | 7 | Single tab 8 | 9 | Two indented lines after each other 10 | 11 | A long line that goes on and on and on and on and on and really is so lang that it will be wrapped at some point. Well, at least it should really be wrapped by now, because really, this is a long sentence, isn’t it? 12 | 13 | Four spaces 14 | 15 | Eight spaces 16 | 17 | Twelve spaces 18 | 19 | Eight spaces 20 | 21 | Four spaces 22 | 23 | Indented 24 | 25 | Not indented 26 | 27 | Three spaces 28 | -------------------------------------------------------------------------------- /tests/datasets/common/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This directory contains test of common reStructuredText markup. 4 | See https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -------------------------------------------------------------------------------- /tests/datasets/common/internal-hyperlinks.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Clicking on this internal hyperlink will take us to the target_ 4 | below. 5 | 6 | .. _target: 7 | 8 | The hyperlink target above points to this paragraph. 9 | 10 | Titles are targets, too 11 | ======================= 12 | Implict references, even with spaces, like `Titles are 13 | targets, too`_. 14 | 15 | Untitled references target_ and `target`_ should be the same. 16 | 17 | .. _named-section: 18 | 19 | Names Links 20 | =========== 21 | 22 | `Link to target `_ 23 | 24 | `Link to named sections `_ -------------------------------------------------------------------------------- /tests/datasets/common/italic.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This is *italic*. -------------------------------------------------------------------------------- /tests/datasets/common/list-table.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. list-table:: 4 | :header-rows: 1 5 | 6 | * - key 7 | - value 8 | * - 1 9 | - 2 10 | * - 3 11 | - 4 -------------------------------------------------------------------------------- /tests/datasets/common/literal-block.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Basic literal block: 4 | 5 | :: 6 | 7 | # Some code block 8 | for a in [5,4,3,2,1]: # this is program code, shown as-is 9 | print(a) 10 | print("it's...") 11 | 12 | Partial minimized form: :: 13 | 14 | Literal block 15 | 16 | Fully minimized form:: 17 | 18 | Literal block 19 | -------------------------------------------------------------------------------- /tests/datasets/common/literal.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This is ``literal``. -------------------------------------------------------------------------------- /tests/datasets/common/multiline-list.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | * List with long lines. 4 | * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent at ante id 5 | ante interdum porttitor. Cras mollis urna scelerisque, tincidunt diam sit 6 | amet, vulputate ante. Orci varius natoque penatibus et magnis dis parturient 7 | montes, nascetur ridiculus mus. Pellentesque habitant morbi tristique 8 | senectus et netus et malesuada fames ac turpis egestas. 9 | * ``Longline`` is a cute name for this sentence. Vestibulum cursus dui ut ex 10 | consequat, sed ultrices justo accumsan. Donec pretium iaculis neque. Sed 11 | iaculis tortor ac odio facilisis, mattis semper neque venenatis. Sed eu 12 | neque non tellus iaculis blandit eu ac tellus. Fusce eu erat justo. 13 | 14 | 1. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 15 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 16 | vitae, molestie odio. 17 | 2. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 18 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 19 | vitae, molestie odio. 20 | 3. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 21 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 22 | vitae, molestie odio. 23 | 4. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 24 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 25 | vitae, molestie odio. 26 | 5. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 27 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 28 | vitae, molestie odio. 29 | 6. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 30 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 31 | vitae, molestie odio. 32 | 7. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 33 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 34 | vitae, molestie odio. 35 | 8. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 36 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 37 | vitae, molestie odio. 38 | 9. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 39 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 40 | vitae, molestie odio. 41 | 10. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue metus, sed 42 | ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, volutpat lectus 43 | vitae, molestie odio. 44 | 45 | * Morbi a est quis diam auctor pretium eu a quam. Integer auctor, ex pulvinar 46 | tempor pretium, lacus erat bibendum arcu, nec aliquam dolor purus id massa. 47 | 48 | 1. Mauris risus enim, cursus quis euismod nec, elementum ut nibh. Sed 49 | sollicitudin, lorem ut tempor bibendum, justo massa facilisis mauris, id 50 | fringilla massa lectus eu urna. 51 | 52 | * Donec arcu diam, egestas nec eros a, porta mattis lectus. Vestibulum 53 | justo orci, rutrum eget metus vitae, molestie suscipit odio. 54 | 55 | 1. Morbi a est quis diam auctor pretium eu a quam. Integer auctor, ex pulvinar 56 | tempor pretium, lacus erat bibendum arcu, nec aliquam dolor purus id massa. 57 | 58 | * Mauris risus enim, cursus quis euismod nec, elementum ut nibh. Sed 59 | sollicitudin, lorem ut tempor bibendum, justo massa facilisis mauris, id 60 | fringilla massa lectus eu urna. 61 | 62 | 1. Donec arcu diam, egestas nec eros a, porta mattis lectus. Vestibulum 63 | justo orci, rutrum eget metus vitae, molestie suscipit odio. 64 | -------------------------------------------------------------------------------- /tests/datasets/common/nested-list.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | * first item 4 | * second item 5 | 6 | * first sub item 7 | * second sub item, 8 | over multiple line 9 | * third sub item 10 | 11 | * third item 12 | * fourth item 13 | 14 | 15 | 1. Item 1 initial text. 16 | 17 | * Item 1a. 18 | * Item 1b. 19 | 20 | 2. * Item 2a. 21 | * Item 2b. 22 | -------------------------------------------------------------------------------- /tests/datasets/common/nonexistent-target.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Pointer to a nonexisting_ target. 4 | -------------------------------------------------------------------------------- /tests/datasets/common/ordered-list-properties.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | 3. This is a numbered list. 4 | 4. It started with item 3. 5 | 5. Five 6 | 6. Six 7 | 7. Seven 8 | 8. Eight 9 | 9. Nine 10 | 10. Ten 11 | 11. Eleven 12 | 12. Twelve 13 | 13. Thirteen 14 | 15 | C) Third letter of the alphabet 16 | D) Fourth letter of the alphabet 17 | E) Fifth letter of the alphabet 18 | 19 | I. Roman numerals 20 | II. Roman numerals 21 | III. Roman numerals 22 | IV. Roman numerals 23 | 24 | (a) one 25 | (b) two 26 | (c) three 27 | -------------------------------------------------------------------------------- /tests/datasets/common/ordered-list.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | 1. This is a numbered list. 4 | 2. It has two items too. 5 | 6 | #. This is a continuation of the list. 7 | #. It is numbered too. 8 | -------------------------------------------------------------------------------- /tests/datasets/common/paragraph.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Single line 4 | 5 | Two short 6 | lines. 7 | 8 | Lines continued \ 9 | with backslash. 10 | 11 | Single line with multiple spaces. 12 | 13 | A long line that goes on and on and on and on and on and really is so lang that it will be wrapped at some point. Well, at least it should really be wrapped by now, because really, this is a long sentence, eh? 14 | 15 | A line with a very long word is abcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyz. Long or what? 16 | -------------------------------------------------------------------------------- /tests/datasets/common/simple-table.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | == == == 4 | a1 a2 a3 5 | == == == 6 | b1 b2 b3 7 | c1 c2 c3 8 | d1 d2 d3 9 | == == == -------------------------------------------------------------------------------- /tests/datasets/common/smart-quotes.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This is a line with a quote, isn't it? 4 | 5 | This is a line with a smart quote, isn’t it? 6 | 7 | This is a 'word' between single quotes. 8 | 9 | This is a "word" between double quotes. 10 | 11 | This is a ‘word’ between smart single quotes. 12 | 13 | This is a “word” between smart double quotes. 14 | 15 | A plain ellipsis … 16 | 17 | Three dots is an ellipsis ... 18 | 19 | Even three dots with spaces in between them . . . 20 | 21 | Two dashes -- that makes a en-dash: – 22 | 23 | Two dashes --- that makes a em-dash: — 24 | -------------------------------------------------------------------------------- /tests/datasets/common/subscript.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This is :sub:`subscript`. 4 | -------------------------------------------------------------------------------- /tests/datasets/common/superscript.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This is :sup:`superscript`. 4 | -------------------------------------------------------------------------------- /tests/datasets/directives/code-language.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. code:: python 4 | 5 | def foo(): 6 | bar() 7 | 8 | .. code:: c++ 9 | 10 | class Foo { 11 | int i; 12 | }; 13 | 14 | See also sphinx-directives/code-block.rst 15 | -------------------------------------------------------------------------------- /tests/datasets/directives/code-number-lines.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. code:: python 4 | :number-lines: 5 | 6 | def foo(): 7 | bar() 8 | 9 | See also sphinx-directives/code-block.rst 10 | -------------------------------------------------------------------------------- /tests/datasets/directives/code.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. code:: 4 | 5 | def foo(): 6 | bar() 7 | 8 | See also sphinx-directives/code-block.rst 9 | -------------------------------------------------------------------------------- /tests/datasets/directives/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This directory contains tests of general reStructuredText directives. 4 | See https://docutils.sourceforge.io/docs/ref/rst/directives.html -------------------------------------------------------------------------------- /tests/datasets/directives/math.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. math:: 4 | 5 | E = m c^2 -------------------------------------------------------------------------------- /tests/datasets/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This folder contains test sources. 4 | 5 | It is catagorized in directories: 6 | 7 | * `common` - tests of common reStructuredText markup. 8 | * `directives` - tests of general reStructuredText directives. 9 | * `roles` - tests of general reStructuredText roles. 10 | * `sphinx-directives` - tests of Sphinx-specific reStructuredText directives. 11 | * `sphinx-roles` - tests of Sphinx-specific reStructuredText roles. 12 | -------------------------------------------------------------------------------- /tests/datasets/roles/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This directory contains tests of general reStructuredText roles. 4 | See https://docutils.sourceforge.io/docs/ref/rst/roles.html -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/code-block-language.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Some Python Code: 4 | 5 | .. code-block:: python 6 | 7 | def foo(): 8 | bar() 9 | 10 | Some C++ code: 11 | 12 | .. code-block:: c++ 13 | 14 | class Foo { 15 | int i; 16 | }; 17 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/code-block-linenos.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. code-block:: python 4 | :linenos: 5 | 6 | def foo(): 7 | bar() 8 | 9 | See also directives/code.rst 10 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/code-block.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. code-block:: 4 | 5 | def foo(): 6 | bar() 7 | 8 | See also directives/code.rst 9 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/deprecated.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. deprecated:: 0.3.1 4 | 5 | .. deprecated:: 2.5.0 6 | Common sense is deprecated altogether. 7 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This directory contains tests of Sphinx-specific reStructuredText directives. 4 | See https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/chapter1.rst: -------------------------------------------------------------------------------- 1 | Chapter 1 2 | ========= 3 | 4 | .. toctree:: 5 | 6 | chapter1/section1 7 | chapter1/section2 -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/chapter1/section1.rst: -------------------------------------------------------------------------------- 1 | Section 1 of Chapter 1 2 | ====================== 3 | 4 | Once upon a time -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/chapter1/section2.rst: -------------------------------------------------------------------------------- 1 | Section 2 of Chapter 1 2 | ====================== 3 | 4 | There was a rich king -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/chapter2.rst: -------------------------------------------------------------------------------- 1 | Chapter 2 2 | ========= 3 | 4 | .. toctree:: 5 | 6 | chapter2/section1 7 | chapter2/section2 -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/chapter2/section1.rst: -------------------------------------------------------------------------------- 1 | Section 1 of Chapter 2 2 | ====================== 3 | 4 | He died -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/chapter2/section2.rst: -------------------------------------------------------------------------------- 1 | Section 2 of Chapter 2 2 | ====================== 3 | 4 | The end -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree-nested/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | root 4 | ==== 5 | 6 | .. toctree:: 7 | 8 | chapter1 9 | chapter2 -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree/doc1.rst: -------------------------------------------------------------------------------- 1 | Doc 1 2 | ===== 3 | 4 | This is document 1. 5 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree/doc2.rst: -------------------------------------------------------------------------------- 1 | Doc 2 2 | ===== 3 | 4 | This is document 2. -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/toctree/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | root 4 | ==== 5 | 6 | .. toctree:: 7 | 8 | doc1 9 | This is Doc 2 -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/versionadded.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. versionadded:: 0.3.1 4 | 5 | .. versionadded:: 1.7.0 6 | The spam methods is new. 7 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-directives/versionchanged.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. versionchanged:: 0.3.1 4 | 5 | .. versionchanged:: 2.5.0 6 | The spam parameter is turned on by default. 7 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/doc/doc1.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Doc 1 4 | ===== 5 | 6 | Jump to :doc:`doc2`. 7 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/doc/doc2.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Doc 2 4 | ===== 5 | 6 | 7 | Jump to :doc:`doc1`. 8 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/doc/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | root 4 | ==== 5 | 6 | * :doc:`doc1`. 7 | * :doc:`This is Doc 2 `. -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | This directory contains tests of Sphinx-specific reStructuredText roles. 4 | See https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/ref.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Read more in :ref:`section-w2zn7enu`. 4 | 5 | This should link to `Section Title `_. 6 | 7 | .. _section-w2zn7enu: 8 | 9 | Section Title 10 | ============= 11 | Text body. 12 | 13 | See the examples in :ref:`section-example-1` and :ref:`section-example-2`. 14 | 15 | .. _section-example-1: 16 | 17 | Example 18 | ------- 19 | First section with equal name. 20 | 21 | .. _section-example-2: 22 | 23 | Example 24 | ------- 25 | Second section with equal name. 26 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/ref/doc1.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | .. _section-znu5w4ex: 4 | 5 | Section Title 6 | ============= 7 | Text body. 8 | -------------------------------------------------------------------------------- /tests/datasets/sphinx-roles/ref/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | root 4 | ==== 5 | 6 | Read more in :ref:`section-znu5w4ex`. 7 | -------------------------------------------------------------------------------- /tests/expected/common/bold.rst: -------------------------------------------------------------------------------- 1 | This is **bold**. 2 | -------------------------------------------------------------------------------- /tests/expected/common/bullet-list-consecutive.rst: -------------------------------------------------------------------------------- 1 | - one 2 | - two 3 | 4 | * three 5 | * four 6 | -------------------------------------------------------------------------------- /tests/expected/common/bullet-list.rst: -------------------------------------------------------------------------------- 1 | First list: 2 | 3 | * one 4 | * two 5 | 6 | Another list: 7 | 8 | * three 9 | * four 10 | -------------------------------------------------------------------------------- /tests/expected/common/definition-list.rst: -------------------------------------------------------------------------------- 1 | Definition lists: 2 | 3 | what 4 | Definition lists associate a term with 5 | a definition. 6 | 7 | how 8 | The term is a one-line phrase, and the 9 | definition is one or more paragraphs or 10 | body elements, indented relative to the 11 | term. Blank lines are not allowed 12 | between term and definition. -------------------------------------------------------------------------------- /tests/expected/common/external-hyperlinks.rst: -------------------------------------------------------------------------------- 1 | Embedded external hyperlinks, like `Python 2 | `_. 3 | 4 | Indirect external hyperlinks, like `Larch 5 | `_ or `Python 6 | `_. 7 | 8 | `Write to me `_ with your questions. 9 | 10 | Anonymous hyperlinks, like `the web site of my favorite programming 11 | language `__. 12 | 13 | Direct hyperlinks, like http://www.python.org. 14 | 15 | This `link `_ refers to a file called ``underscore_``. 16 | -------------------------------------------------------------------------------- /tests/expected/common/grid-table.rst: -------------------------------------------------------------------------------- 1 | +-----+-----+ 2 | | a | b | 3 | +=====+=====+ 4 | | c | d | 5 | +-----+-----+ 6 | -------------------------------------------------------------------------------- /tests/expected/common/headings.rst: -------------------------------------------------------------------------------- 1 | Headline 2 | ======== 3 | Text immediately following. 4 | 5 | Subheader 6 | --------- 7 | 8 | Blank line before paragraph. 9 | 10 | Another paragraph. 11 | 12 | Subsubheader 13 | ____________ 14 | 15 | Subheader 2 16 | ----------- 17 | 18 | Another Chapter 19 | =============== 20 | End of file. -------------------------------------------------------------------------------- /tests/expected/common/hyperlink-targets.rst: -------------------------------------------------------------------------------- 1 | .. _target: 2 | 3 | The hyperlink target above points to this paragraph. Internal 4 | hyperlink targets may be chained. Multiple adjacent internal hyperlink 5 | targets all point to the same element: 6 | 7 | .. _target1: 8 | .. _target2: 9 | 10 | The targets `target1`_ and `target2`_ are synonyms; they both point to 11 | this paragraph. 12 | 13 | .. _one: three_ 14 | .. _two: three_ 15 | .. _three: 16 | 17 | Indirect hyperlink targets have a hyperlink reference in their link 18 | blocks. In the following example, target `one `_ indirectly 19 | references whatever target `two `_ references, and target two 20 | references target `three`_, an internal hyperlink target. In effect, 21 | all three reference the same thing. 22 | -------------------------------------------------------------------------------- /tests/expected/common/indentation.rst: -------------------------------------------------------------------------------- 1 | Not indented 2 | 3 | Four spaces 4 | 5 | Single tab 6 | 7 | Two indented lines after each other 8 | 9 | A long line that goes on and on and on and on and on and really is 10 | so lang that it will be wrapped at some point. Well, at least it 11 | should really be wrapped by now, because really, this is a long 12 | sentence, isn’t it? 13 | 14 | Four spaces 15 | 16 | Eight spaces 17 | 18 | Twelve spaces 19 | 20 | Eight spaces 21 | 22 | Four spaces 23 | 24 | Indented 25 | 26 | Not indented 27 | 28 | Three spaces 29 | 30 | -------------------------------------------------------------------------------- /tests/expected/common/index.rst: -------------------------------------------------------------------------------- 1 | This directory contains test of common reStructuredText markup. 2 | See https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html -------------------------------------------------------------------------------- /tests/expected/common/internal-hyperlinks.rst: -------------------------------------------------------------------------------- 1 | Clicking on this internal hyperlink will take us to the target_ 2 | below. 3 | 4 | .. _target: 5 | 6 | The hyperlink target above points to this paragraph. 7 | 8 | Titles are targets, too 9 | ======================= 10 | Implict references, even with spaces, like `Titles are 11 | targets, too`_. 12 | 13 | Untitled references target_ and `target`_ should be the same. 14 | 15 | .. _named-section: 16 | 17 | Names Links 18 | =========== 19 | 20 | `Link to target `_ 21 | 22 | `Link to named sections `_ -------------------------------------------------------------------------------- /tests/expected/common/italic.rst: -------------------------------------------------------------------------------- 1 | This is *italic*. 2 | -------------------------------------------------------------------------------- /tests/expected/common/list-table.rst: -------------------------------------------------------------------------------- 1 | +----------------------------------------------------+----------------------------------------------------+ 2 | | key | value | 3 | +====================================================+====================================================+ 4 | | 1 | 2 | 5 | +----------------------------------------------------+----------------------------------------------------+ 6 | | 3 | 4 | 7 | +----------------------------------------------------+----------------------------------------------------+ 8 | -------------------------------------------------------------------------------- /tests/expected/common/literal-block.rst: -------------------------------------------------------------------------------- 1 | Basic literal block: 2 | 3 | :: 4 | 5 | # Some code block 6 | for a in [5,4,3,2,1]: # this is program code, shown as-is 7 | print(a) 8 | print("it's...") 9 | 10 | Partial minimized form: :: 11 | 12 | Literal block 13 | 14 | Fully minimized form:: 15 | 16 | Literal block 17 | -------------------------------------------------------------------------------- /tests/expected/common/literal.rst: -------------------------------------------------------------------------------- 1 | This is ``literal``. -------------------------------------------------------------------------------- /tests/expected/common/multiline-list.rst: -------------------------------------------------------------------------------- 1 | * List with long lines. 2 | * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent 3 | at ante id ante interdum porttitor. Cras mollis urna scelerisque, 4 | tincidunt diam sit amet, vulputate ante. Orci varius natoque 5 | penatibus et magnis dis parturient montes, nascetur ridiculus mus. 6 | Pellentesque habitant morbi tristique senectus et netus et 7 | malesuada fames ac turpis egestas. 8 | * ``Longline`` is a cute name for this sentence. Vestibulum cursus 9 | dui ut ex consequat, sed ultrices justo accumsan. Donec pretium 10 | iaculis neque. Sed iaculis tortor ac odio facilisis, mattis semper 11 | neque venenatis. Sed eu neque non tellus iaculis blandit eu ac 12 | tellus. Fusce eu erat justo. 13 | 14 | 1. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 15 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 16 | volutpat lectus vitae, molestie odio. 17 | 2. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 18 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 19 | volutpat lectus vitae, molestie odio. 20 | 3. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 21 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 22 | volutpat lectus vitae, molestie odio. 23 | 4. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 24 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 25 | volutpat lectus vitae, molestie odio. 26 | 5. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 27 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 28 | volutpat lectus vitae, molestie odio. 29 | 6. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 30 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 31 | volutpat lectus vitae, molestie odio. 32 | 7. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 33 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 34 | volutpat lectus vitae, molestie odio. 35 | 8. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 36 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 37 | volutpat lectus vitae, molestie odio. 38 | 9. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 39 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 40 | volutpat lectus vitae, molestie odio. 41 | 10. Mauris vel ante tellus. Suspendisse potenti. Sed dictum augue 42 | metus, sed ullamcorper nisi suscipit eu. Ut non arcu ullamcorper, 43 | volutpat lectus vitae, molestie odio. 44 | 45 | * Morbi a est quis diam auctor pretium eu a quam. Integer auctor, ex 46 | pulvinar tempor pretium, lacus erat bibendum arcu, nec aliquam 47 | dolor purus id massa. 48 | 49 | 1. Mauris risus enim, cursus quis euismod nec, elementum ut nibh. 50 | Sed sollicitudin, lorem ut tempor bibendum, justo massa 51 | facilisis mauris, id fringilla massa lectus eu urna. 52 | 53 | * Donec arcu diam, egestas nec eros a, porta mattis lectus. 54 | Vestibulum justo orci, rutrum eget metus vitae, molestie 55 | suscipit odio. 56 | 57 | 1. Morbi a est quis diam auctor pretium eu a quam. Integer auctor, ex 58 | pulvinar tempor pretium, lacus erat bibendum arcu, nec aliquam 59 | dolor purus id massa. 60 | 61 | * Mauris risus enim, cursus quis euismod nec, elementum ut nibh. 62 | Sed sollicitudin, lorem ut tempor bibendum, justo massa 63 | facilisis mauris, id fringilla massa lectus eu urna. 64 | 65 | 1. Donec arcu diam, egestas nec eros a, porta mattis lectus. 66 | Vestibulum justo orci, rutrum eget metus vitae, molestie 67 | suscipit odio. 68 | -------------------------------------------------------------------------------- /tests/expected/common/nested-list.rst: -------------------------------------------------------------------------------- 1 | * first item 2 | * second item 3 | 4 | * first sub item 5 | * second sub item, 6 | over multiple line 7 | * third sub item 8 | 9 | * third item 10 | * fourth item 11 | 12 | 13 | 1. Item 1 initial text. 14 | 15 | * Item 1a. 16 | * Item 1b. 17 | 18 | 2. * Item 2a. 19 | * Item 2b. 20 | -------------------------------------------------------------------------------- /tests/expected/common/nonexistent-target.rst: -------------------------------------------------------------------------------- 1 | Pointer to a nonexisting_ target. 2 | -------------------------------------------------------------------------------- /tests/expected/common/ordered-list-properties.rst: -------------------------------------------------------------------------------- 1 | 3. This is a numbered list. 2 | 4. It started with item 3. 3 | 5. Five 4 | 6. Six 5 | 7. Seven 6 | 8. Eight 7 | 9. Nine 8 | 10. Ten 9 | 11. Eleven 10 | 12. Twelve 11 | 13. Thirteen 12 | 13 | C) Third letter of the alphabet 14 | D) Fourth letter of the alphabet 15 | E) Fifth letter of the alphabet 16 | 17 | I. Roman numerals 18 | II. Roman numerals 19 | III. Roman numerals 20 | IV. Roman numerals 21 | 22 | (a) one 23 | (b) two 24 | (c) three 25 | -------------------------------------------------------------------------------- /tests/expected/common/ordered-list.rst: -------------------------------------------------------------------------------- 1 | 1. This is a numbered list. 2 | 2. It has two items too. 3 | 3. This is a continuation of the list. 4 | 4. It is numbered too. 5 | -------------------------------------------------------------------------------- /tests/expected/common/paragraph.rst: -------------------------------------------------------------------------------- 1 | Single line 2 | 3 | Two short lines. 4 | 5 | Lines continued with backslash. 6 | 7 | Single line with multiple spaces. 8 | 9 | A long line that goes on and on and on and on and on and really is so 10 | lang that it will be wrapped at some point. Well, at least it should 11 | really be wrapped by now, because really, this is a long sentence, eh? 12 | 13 | A line with a very long word is 14 | abcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyzabcdefghijklmoqprstuvwxyz. 15 | Long or what? 16 | -------------------------------------------------------------------------------- /tests/expected/common/simple-table.rst: -------------------------------------------------------------------------------- 1 | +----+----+----+ 2 | | a1 | a2 | a3 | 3 | +====+====+====+ 4 | | b1 | b2 | b3 | 5 | +----+----+----+ 6 | | c1 | c2 | c3 | 7 | +----+----+----+ 8 | | d1 | d2 | d3 | 9 | +----+----+----+ 10 | -------------------------------------------------------------------------------- /tests/expected/common/smart-quotes.rst: -------------------------------------------------------------------------------- 1 | This is a line with a quote, isn’t it? 2 | 3 | This is a line with a smart quote, isn’t it? 4 | 5 | This is a ‘word’ between single quotes. 6 | 7 | This is a “word” between double quotes. 8 | 9 | This is a ‘word’ between smart single quotes. 10 | 11 | This is a “word” between smart double quotes. 12 | 13 | A plain ellipsis … 14 | 15 | Three dots is an ellipsis … 16 | 17 | Even three dots with spaces in between them … 18 | 19 | Two dashes – that makes a en-dash: – 20 | 21 | Two dashes — that makes a em-dash: — 22 | -------------------------------------------------------------------------------- /tests/expected/common/subscript.rst: -------------------------------------------------------------------------------- 1 | This is :sub:`subscript`. 2 | -------------------------------------------------------------------------------- /tests/expected/common/superscript.rst: -------------------------------------------------------------------------------- 1 | This is :sup:`superscript`. 2 | -------------------------------------------------------------------------------- /tests/expected/directives/code-language.rst: -------------------------------------------------------------------------------- 1 | .. code:: python 2 | 3 | def foo(): 4 | bar() 5 | 6 | .. code:: c++ 7 | 8 | class Foo { 9 | int i; 10 | }; 11 | 12 | See also sphinx-directives/code-block.rst 13 | -------------------------------------------------------------------------------- /tests/expected/directives/code-number-lines.rst: -------------------------------------------------------------------------------- 1 | .. code:: python 2 | :number-lines: 3 | 4 | def foo(): 5 | bar() 6 | 7 | See also sphinx-directives/code-block.rst 8 | -------------------------------------------------------------------------------- /tests/expected/directives/code.rst: -------------------------------------------------------------------------------- 1 | .. code:: 2 | 3 | def foo(): 4 | bar() 5 | 6 | See also sphinx-directives/code-block.rst 7 | -------------------------------------------------------------------------------- /tests/expected/directives/index.rst: -------------------------------------------------------------------------------- 1 | This directory contains tests of general reStructuredText directives. 2 | See https://docutils.sourceforge.io/docs/ref/rst/directives.html -------------------------------------------------------------------------------- /tests/expected/directives/math.rst: -------------------------------------------------------------------------------- 1 | .. math:: 2 | 3 | E = m c^2 -------------------------------------------------------------------------------- /tests/expected/index.rst: -------------------------------------------------------------------------------- 1 | This folder contains expected test results. 2 | 3 | It is catagorized in directories: 4 | 5 | * `common` - tests of common reStructuredText markup. 6 | * `directives` - tests of general reStructuredText directives. 7 | * `roles` - tests of general reStructuredText roles. 8 | * `sphinx-directives` - tests of Sphinx-specific reStructuredText directives. 9 | * `sphinx-roles` - tests of Sphinx-specific reStructuredText roles. 10 | -------------------------------------------------------------------------------- /tests/expected/roles/index.rst: -------------------------------------------------------------------------------- 1 | This directory contains tests of general reStructuredText roles. 2 | See https://docutils.sourceforge.io/docs/ref/rst/roles.html -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/code-block-language.rst: -------------------------------------------------------------------------------- 1 | Some Python Code: 2 | 3 | .. code:: python 4 | 5 | def foo(): 6 | bar() 7 | 8 | Some C++ code: 9 | 10 | .. code:: c++ 11 | 12 | class Foo { 13 | int i; 14 | }; 15 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/code-block-linenos.rst: -------------------------------------------------------------------------------- 1 | .. code:: python 2 | :number-lines: 3 | 4 | def foo(): 5 | bar() 6 | 7 | See also directives/code.rst 8 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/code-block.rst: -------------------------------------------------------------------------------- 1 | .. code:: 2 | 3 | def foo(): 4 | bar() 5 | 6 | See also directives/code.rst 7 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/deprecated.rst: -------------------------------------------------------------------------------- 1 | Deprecated since version 0.3.1. 2 | 3 | Deprecated since version 2.5.0: Common sense is deprecated altogether. 4 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/index.rst: -------------------------------------------------------------------------------- 1 | This directory contains tests of Sphinx-specific reStructuredText directives. 2 | See https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/toctree-nested/index.rst: -------------------------------------------------------------------------------- 1 | root 2 | **** 3 | 4 | * `Chapter 1 `_ 5 | 6 | * `Section 1 of Chapter 1 `_ 7 | * `Section 2 of Chapter 1 `_ 8 | 9 | * `Chapter 2 `_ 10 | 11 | * `Section 1 of Chapter 2 `_ 12 | * `Section 2 of Chapter 2 `_ 13 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/toctree/doc1.rst: -------------------------------------------------------------------------------- 1 | Doc 1 2 | ***** 3 | 4 | This is document 1. 5 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/toctree/doc2.rst: -------------------------------------------------------------------------------- 1 | Doc 2 2 | ***** 3 | 4 | This is document 2. 5 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/toctree/index.rst: -------------------------------------------------------------------------------- 1 | root 2 | **** 3 | 4 | * `Doc 1 `_ 5 | * `This is Doc 2 `_ 6 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/versionadded.rst: -------------------------------------------------------------------------------- 1 | New in version 0.3.1. 2 | 3 | New in version 1.7.0: The spam methods is new. 4 | -------------------------------------------------------------------------------- /tests/expected/sphinx-directives/versionchanged.rst: -------------------------------------------------------------------------------- 1 | Changed in version 0.3.1. 2 | 3 | Changed in version 2.5.0: The spam parameter is turned on by default. 4 | -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/doc/doc1.rst: -------------------------------------------------------------------------------- 1 | Doc 1 2 | ***** 3 | 4 | Jump to `Doc 2 `_. 5 | -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/doc/doc2.rst: -------------------------------------------------------------------------------- 1 | Doc 2 2 | ***** 3 | 4 | Jump to `Doc 1 `_. 5 | -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/doc/index.rst: -------------------------------------------------------------------------------- 1 | root 2 | **** 3 | 4 | * `Doc 1 `_. 5 | * `This is Doc 2 `_. 6 | -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/index.rst: -------------------------------------------------------------------------------- 1 | This directory contains tests of Sphinx-specific reStructuredText roles. 2 | See https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/ref.rst: -------------------------------------------------------------------------------- 1 | Read more in `Section Title `_. 2 | 3 | This should link to `Section Title `_. 4 | 5 | .. _section-w2zn7enu: 6 | 7 | Section Title 8 | ============= 9 | 10 | Text body. 11 | 12 | See the examples in `Example `_ and `Example 13 | `_. 14 | 15 | .. _section-example-1: 16 | 17 | Example 18 | ------- 19 | 20 | First section with equal name. 21 | 22 | .. _section-example-2: 23 | 24 | Example 25 | ------- 26 | 27 | Second section with equal name. 28 | -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/ref/doc1.rst: -------------------------------------------------------------------------------- 1 | .. _section-znu5w4ex: 2 | 3 | Section Title 4 | ============= 5 | Text body. 6 | -------------------------------------------------------------------------------- /tests/expected/sphinx-roles/ref/index.rst: -------------------------------------------------------------------------------- 1 | root 2 | **** 3 | 4 | Read more in `Section Title `_. 5 | -------------------------------------------------------------------------------- /tests/test_rst_blocks.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_paragraph(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['paragraph']) 6 | 7 | 8 | def test_indentation(src_dir, expected_dir, output_dir): 9 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['indentation']) 10 | 11 | 12 | def test_literal_block(src_dir, expected_dir, output_dir): 13 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['literal-block']) 14 | -------------------------------------------------------------------------------- /tests/test_rst_code_blocks.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | import pytest 4 | import sphinx 5 | 6 | @pytest.mark.skipif(sphinx.version_info < (2, 0), reason="Sphinx 1.x does not support code blocks without language") 7 | def test_code_block(src_dir, expected_dir, output_dir): 8 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives', ['code-block']) 9 | 10 | 11 | def test_code_block_language(src_dir, expected_dir, output_dir): 12 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives', ['code-block-language']) 13 | 14 | 15 | def test_code_block_linenos(src_dir, expected_dir, output_dir): 16 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives', ['code-block-linenos']) 17 | 18 | 19 | def test_code(src_dir, expected_dir, output_dir): 20 | run_parse_test(src_dir, expected_dir, output_dir, 'directives', ['code']) 21 | 22 | 23 | def test_code_language(src_dir, expected_dir, output_dir): 24 | run_parse_test(src_dir, expected_dir, output_dir, 'directives', ['code-language']) 25 | 26 | 27 | @pytest.mark.skipif(sphinx.version_info < (2, 0), reason="Sphinx 1.x renders line numbers inline.") 28 | def test_code_number_lines(src_dir, expected_dir, output_dir): 29 | run_parse_test(src_dir, expected_dir, output_dir, 'directives', ['code-number-lines']) 30 | -------------------------------------------------------------------------------- /tests/test_rst_directives_math.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_directive_math(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'directives', ['math']) 6 | -------------------------------------------------------------------------------- /tests/test_rst_formatting.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | import pytest 4 | import sphinx 5 | 6 | 7 | def test_bold(src_dir, expected_dir, output_dir): 8 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['bold']) 9 | 10 | 11 | def test_italic(src_dir, expected_dir, output_dir): 12 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['italic']) 13 | 14 | 15 | def test_literal(src_dir, expected_dir, output_dir): 16 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['literal']) 17 | 18 | 19 | def test_subscript(src_dir, expected_dir, output_dir): 20 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['subscript']) 21 | 22 | 23 | def test_superscript(src_dir, expected_dir, output_dir): 24 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['superscript']) 25 | 26 | 27 | @pytest.mark.skipif(sphinx.version_info < (1, 6), reason="Smart quotes were introduces in Sphinx 1.6") 28 | def test_smart_quotes(src_dir, expected_dir, output_dir): 29 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['smart-quotes']) 30 | 31 | -------------------------------------------------------------------------------- /tests/test_rst_headings.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_headings(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['headings']) 6 | -------------------------------------------------------------------------------- /tests/test_rst_hyperlinks.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_hyperlink_targets(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['hyperlink-targets']) 6 | 7 | 8 | def test_external_hyperlinks(src_dir, expected_dir, output_dir): 9 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['external-hyperlinks']) 10 | 11 | 12 | def test_internal_hyperlinks(src_dir, expected_dir, output_dir): 13 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['internal-hyperlinks']) 14 | 15 | 16 | def test_nonexisting_target(src_dir, expected_dir, output_dir): 17 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['nonexistent-target']) 18 | 19 | 20 | def test_ref(src_dir, expected_dir, output_dir): 21 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-roles', ['ref']) 22 | 23 | 24 | def test_cross_ref(src_dir, expected_dir, output_dir): 25 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-roles/ref', ['index', 'doc1']) 26 | 27 | 28 | def test_doc_role(src_dir, expected_dir, output_dir): 29 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-roles/doc', ['index', 'doc1', 'doc2']) 30 | -------------------------------------------------------------------------------- /tests/test_rst_list.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | import pytest 4 | 5 | 6 | def test_bullet_list(src_dir, expected_dir, output_dir): 7 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['bullet-list']) 8 | 9 | 10 | def test_ordered_list(src_dir, expected_dir, output_dir): 11 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['ordered-list']) 12 | 13 | 14 | def test_nested_list(src_dir, expected_dir, output_dir): 15 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['nested-list']) 16 | 17 | 18 | def test_multiline_list(src_dir, expected_dir, output_dir): 19 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['multiline-list']) 20 | 21 | 22 | @pytest.mark.skip(reason="work in progress") 23 | def test_ordered_list_properties(src_dir, expected_dir, output_dir): 24 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['ordered-list-properties']) 25 | 26 | 27 | @pytest.mark.skip(reason="work in progress") 28 | def test_bullet_list_consecutive(src_dir, expected_dir, output_dir): 29 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['bullet-list-consecutive']) 30 | 31 | 32 | def test_definition_list(src_dir, expected_dir, output_dir): 33 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['definition-list']) 34 | -------------------------------------------------------------------------------- /tests/test_rst_table.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_simple_table(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['simple-table']) 6 | 7 | 8 | def test_grid_table(src_dir, expected_dir, output_dir): 9 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['grid-table']) 10 | 11 | 12 | def test_list_table(src_dir, expected_dir, output_dir): 13 | run_parse_test(src_dir, expected_dir, output_dir, 'common', ['list-table']) 14 | -------------------------------------------------------------------------------- /tests/test_rst_toctree.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_toctree(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives/toctree', ['index', 'doc1', 'doc2']) 6 | 7 | 8 | def test_toctree_nested(src_dir, expected_dir, output_dir): 9 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives/toctree-nested', ['index']) 10 | -------------------------------------------------------------------------------- /tests/test_sphinx_versionmodified.py: -------------------------------------------------------------------------------- 1 | from tests.utils import run_parse_test 2 | 3 | 4 | def test_versionadded(src_dir, expected_dir, output_dir): 5 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives', ['versionadded']) 6 | 7 | 8 | def test_versionchanged(src_dir, expected_dir, output_dir): 9 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives', ['versionchanged']) 10 | 11 | 12 | def test_deprecated(src_dir, expected_dir, output_dir): 13 | run_parse_test(src_dir, expected_dir, output_dir, 'sphinx-directives', ['deprecated']) 14 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | from os.path import join 2 | import os 3 | try: 4 | from itertools import zip_longest 5 | except ImportError: 6 | # Python 2.7 support. 7 | from itertools import izip_longest as zip_longest 8 | import io 9 | 10 | import docutils 11 | from docutils.frontend import OptionParser 12 | from docutils.nodes import Text, Element, system_message 13 | from docutils.parsers.rst import Parser 14 | from docutils.utils import new_document 15 | from docutils.core import publish_from_doctree 16 | from sphinx.application import Sphinx 17 | 18 | # sphinx.util.docutils requires Sphinx 1.5 and up. 19 | try: 20 | from sphinx.util.docutils import docutils_namespace 21 | except ImportError: 22 | # Attempt to support Sphinx 1.4 and thus the old Debian Stretch (oldstable) 23 | from copy import copy 24 | from contextlib import contextmanager 25 | from docutils.parsers.rst import directives, roles 26 | @contextmanager 27 | def docutils_namespace(): 28 | """Create namespace for reST parsers.""" 29 | try: 30 | _directives = copy(directives._directives) 31 | _roles = copy(roles._roles) 32 | yield 33 | finally: 34 | directives._directives = _directives 35 | roles._roles = _roles 36 | 37 | 38 | def build_sphinx(src_dir, output_dir, files=None, config={}): 39 | doctrees_dir = join(output_dir, '.doctrees') 40 | 41 | filenames = [] 42 | force_all = True 43 | 44 | default_config = { 45 | 'extensions': ['sphinxcontrib.restbuilder'], 46 | 'master_doc': 'index', 47 | } 48 | default_config.update(config) 49 | config = default_config 50 | 51 | if files: 52 | force_all = False 53 | filenames = [join(src_dir, file + '.rst') for file in files] 54 | config['master_doc'] = files[0] 55 | 56 | with docutils_namespace(): 57 | app = Sphinx( 58 | src_dir, 59 | None, 60 | output_dir, 61 | doctrees_dir, 62 | 'rst', 63 | confoverrides=config, 64 | verbosity=0, 65 | ) 66 | 67 | app.build(force_all=force_all, filenames=filenames) 68 | 69 | 70 | def assert_node_equal(output, expected): 71 | assert type(output) == type(expected) 72 | if isinstance(output, Text): 73 | output_text = output.replace('\r\n', ' ') 74 | output_text = output_text.replace('\n', ' ') 75 | expected_text = expected.replace('\r\n', ' ') 76 | expected_text = expected_text.replace('\n', ' ') 77 | assert output_text == expected_text 78 | elif isinstance(output, system_message): 79 | assert len(output.children) == len(expected.children) 80 | # Don't check specifics of system_messages (warnings) 81 | # E.g. the line number may be off 82 | elif isinstance(output, Element): 83 | assert len(output.children) == len(expected.children) 84 | assert output.attributes == expected.attributes 85 | else: 86 | raise AssertionError 87 | 88 | 89 | def assert_doc_equal(output_doc, expected_doc): 90 | """ 91 | Can be used to compare two documents, ignoring any whitespace changes 92 | """ 93 | for output, expected in zip_longest( 94 | output_doc.traverse(include_self=False), expected_doc.traverse(include_self=False) 95 | ): 96 | assert_node_equal(output, expected) 97 | 98 | 99 | def parse_doc(dir, file): 100 | parser = Parser() 101 | wd = os.getcwd() 102 | os.chdir(dir) 103 | with io.open(join(dir, file + '.rst'), encoding='utf-8') as fh: 104 | doc = new_document( 105 | file, 106 | OptionParser( 107 | components=(docutils.parsers.rst.Parser,) 108 | ).get_default_values(), 109 | ) 110 | parser.parse( 111 | fh.read(), 112 | doc, 113 | ) 114 | os.chdir(wd) 115 | return doc 116 | 117 | 118 | def run_parse_test(src_dir, expected_dir, output_dir, subdir, files): 119 | src_dir = join(src_dir, subdir) 120 | expected_dir = join(expected_dir, subdir) 121 | output_dir = join(output_dir, subdir) 122 | build_sphinx(src_dir, output_dir, files) 123 | 124 | for file in files: 125 | output_doc = parse_doc(output_dir, file) 126 | expected_doc = parse_doc(expected_dir, file) 127 | try: 128 | assert_doc_equal(output_doc, expected_doc) 129 | except AssertionError: 130 | # output XML version of doctree for easier debugging 131 | with open(join(output_dir, file + '.output.xml'), 'wb') as fw: 132 | fw.write(publish_from_doctree(output_doc, writer_name='xml')) 133 | with open(join(output_dir, file + '.expected.xml'), 'wb') as fw: 134 | fw.write(publish_from_doctree(expected_doc, writer_name='xml')) 135 | raise 136 | 137 | 138 | 139 | if __name__ == '__main__': 140 | pass 141 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | ; If you change the test matrix, also change it in .github/workflows/tests.yml 3 | envlist = 4 | python2.7-sphinx{1.4,1.8} # Supported by Sphinx 1.0-1.8 5 | python3.5-sphinx{1.4,2.0,3.4} # Supported by Sphinx 1.4-3.4 6 | python3.6-sphinx{1.5,4.0} # Supported by Sphinx 1.5-4.x 7 | python3.7-sphinx{4.0} # Supported by Sphinx 1.8-4.x 8 | python3.8-sphinx{1.8,4.0} # Supported by Sphinx 1.8-4.x 9 | python3.9-sphinx{2.4,3.0,3.4,4.0} # Supported by Sphinx 2.4-4.x 10 | 11 | [testenv] 12 | basepython = 13 | python2.7: python2.7 14 | python3.5: python3.5 15 | python3.6: python3.6 16 | python3.7: python3.7 17 | python3.8: python3.8 18 | python3.9: python3.9 19 | python3.10: python3.10 20 | 21 | deps = 22 | pytest 23 | sphinx1.4: Sphinx >= 1.4, < 1.5 # Supports Python 2.6, 2.7, 3.3, 3.4, 3.5 24 | sphinx1.5: Sphinx >= 1.5, < 1.6 # Supports Python 2.7, 3.4, 3.5 25 | sphinx1.6: Sphinx >= 1.6, < 1.7 # Supports Python 2.7, 3.4, 3.5, 3.6 26 | sphinx1.7: Sphinx >= 1.7, < 1.8 # Supports Python 2.7, 3.4, 3.5, 3.6 27 | sphinx1.8: Sphinx >= 1.8, < 2.0 # Supports Python 2.7, 3.4, 3.5, 3.6, 3.7, 3.8 28 | sphinx2.0: Sphinx >= 2.0, < 2.1 # Supports Python 3.4, 3.5, 3.6, 3.7, 3.8 29 | sphinx2.1: Sphinx >= 2.1, < 2.2 # Supports Python 3.5, 3.6, 3.7, 3.8 30 | sphinx2.2: Sphinx >= 2.2, < 2.3 # Supports Python 3.5, 3.6, 3.7, 3.8 31 | sphinx2.3: Sphinx >= 2.3, < 2.4 # Supports Python 3.5, 3.6, 3.7, 3.8 32 | sphinx2.4: Sphinx >= 2.4, < 3.0 # Supports Python 3.5, 3.6, 3.7, 3.8, 3.9 33 | sphinx3.0: Sphinx >= 3.0, < 3.1 # Supports Python 3.5, 3.6, 3.7, 3.8, 3.9 34 | sphinx3.4: Sphinx >= 3.4, < 3.5 # Supports Python 3.5, 3.6, 3.7, 3.8, 3.9 35 | sphinx3.5: Sphinx >= 3.5, < 3.6 # Supports Python 3.5, 3.6, 3.7, 3.8, 3.9 36 | sphinx4.0: Sphinx >= 4.0, < 4.1 # Supports Python 3.6, 3.7, 3.8, 3.9 37 | commands = 38 | pytest -v 39 | 40 | [pytest] 41 | filterwarnings = 42 | ; suppress FutureWarning in Sphinx 1.0...2.2 about Node.traverse() 43 | ignore::FutureWarning:sphinx.util.nodes 44 | --------------------------------------------------------------------------------