pax_global_header00006660000000000000000000000064146350406760014525gustar00rootroot0000000000000052 comment=2172f92dbb38de1b9e801865196b7b7266d3c467 sphinx-autoapi-3.1.2/000077500000000000000000000000001463504067600145015ustar00rootroot00000000000000sphinx-autoapi-3.1.2/.devcontainer/000077500000000000000000000000001463504067600172405ustar00rootroot00000000000000sphinx-autoapi-3.1.2/.devcontainer/devcontainer.json000066400000000000000000000010531463504067600226130ustar00rootroot00000000000000// For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/python { "name": "Sphinx AutoAPI", "image": "mcr.microsoft.com/devcontainers/python:0-3.11", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Configure tool-specific properties. "customizations": { }, // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "pip3 install ." } sphinx-autoapi-3.1.2/.github/000077500000000000000000000000001463504067600160415ustar00rootroot00000000000000sphinx-autoapi-3.1.2/.github/workflows/000077500000000000000000000000001463504067600200765ustar00rootroot00000000000000sphinx-autoapi-3.1.2/.github/workflows/main.yml000066400000000000000000000014211463504067600215430ustar00rootroot00000000000000name: tests on: - push - pull_request jobs: test: strategy: matrix: # Keep this in sync with tox.ini python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel python -m pip install tox tox-gh-actions - name: Run tests run: tox sphinx-autoapi-3.1.2/.github/workflows/release.yml000066400000000000000000000044631463504067600222500ustar00rootroot00000000000000name: Publish Python 🐍 distribution 📦 to PyPI on: push jobs: build: name: Build distribution 📦 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.x" - name: Install pypa/build run: >- python3 -m pip install build --user - name: Build a binary wheel and a source tarball run: python3 -m build - name: Store the distribution packages uses: actions/upload-artifact@v3 with: name: python-package-distributions path: dist/ publish-to-pypi: name: >- Publish Python 🐍 distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes needs: - build runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/sphinx-autoapi permissions: id-token: write # IMPORTANT: mandatory for trusted publishing steps: - name: Download all the dists uses: actions/download-artifact@v3 with: name: python-package-distributions path: dist/ - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 github-release: name: >- Sign the Python 🐍 distribution 📦 with Sigstore and upload them to GitHub Release needs: - publish-to-pypi runs-on: ubuntu-latest permissions: contents: write # IMPORTANT: mandatory for making GitHub Releases id-token: write # IMPORTANT: mandatory for sigstore steps: - name: Download all the dists uses: actions/download-artifact@v3 with: name: python-package-distributions path: dist/ - name: Sign the dists with Sigstore uses: sigstore/gh-action-sigstore-python@v2.1.1 with: inputs: >- ./dist/*.tar.gz ./dist/*.whl - name: Upload artifact signatures to GitHub Release env: GITHUB_TOKEN: ${{ github.token }} # Upload to GitHub Release using the `gh` CLI. # `dist/` contains the built packages, and the # sigstore-produced signatures and certificates. run: >- gh release upload '${{ github.ref_name }}' dist/** --repo '${{ github.repository }}'sphinx-autoapi-3.1.2/.gitignore000066400000000000000000000004601463504067600164710ustar00rootroot00000000000000*.egg-info *.pyc *.swp .doctrees _build_rtd _build docs/autoapi readthedocs_build tests/*/autoapi tests/dotnetexample/example/Identity/ _api_ .tox .eggs .ropeproject/ .cache .pytest_cache/ build/ dist/ tests/python/pyexample/autoapi/example/index.rst tests/python/pymovedconfpy/autoapi/example/index.rst sphinx-autoapi-3.1.2/.readthedocs.yml000066400000000000000000000011241463504067600175650ustar00rootroot00000000000000version: 2 # Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF and ePub # formats: # - pdf # - epub # Optional but recommended, declare the Python requirements required # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - method: pip path: . extra_requirements: - docssphinx-autoapi-3.1.2/CHANGELOG.rst000066400000000000000000000520471463504067600165320ustar00rootroot00000000000000Changelog ========= Versions follow `Semantic Versioning `_ (``..``). .. towncrier release notes start v3.1.2 (2024-06-20) ------------------- Bugfixes ^^^^^^^^ - Fix imported members being rendered in modules (#452) v3.1.1 (2024-05-22) ------------------- Bugfixes ^^^^^^^^ - Fix private subpackages causing orphan pages (#446) v3.1.0 (2024-05-20) ------------------- Features ^^^^^^^^ - Objects can render to their own page (#226) - Render PEP-695 type aliases as TypeAlias assignments. (#414) Bugfixes ^^^^^^^^ - Values are always rendered for TypeAlises and PEP-695 type aliases. (#224) - Fix submodule with `__init__.pyi` documented as `__init__` instead of submodule name (#398) - Fix IndexError when a module docstring contains only a heading (#412) - Preserve strings inside Literal type annotations (#423) - Stopped using xrefs in page titles (#427) - Fix unpickable configuration value warning when using autoapi_prepare_jinja_env (#445) - Fix emitting ignore event twice for methods. Misc ^^^^ - #388 v3.0.0 (2023-09-26) ------------------- Bugfixes ^^^^^^^^ - Ensure `tooltip` is always a `str`. (str-tooltip) - Replaced usage of deprecated sphinx.util.status_iterator (#391) Deprecations and Removals ^^^^^^^^^^^^^^^^^^^^^^^^^ - Removed support for documenting languages other than Python (#248) - Removed support for Python 3.7 Misc ^^^^ - #389, #390, #392, #407 v2.1.1 (2023-06-10) ------------------- Bugfixes ^^^^^^^^ - Fix "document isn't included" warning when using autoapi_add_toctree_entry (#319) - Types used in PEP-604 union syntax can be linked with intersphinx (#366) - Fix class overrides not rendering correctly. - Fix separated type comments for arguments not merging correctly in Python 3.7 - Fixed viewcode being unable to find the source code for imported objects Improved Documentation ^^^^^^^^^^^^^^^^^^^^^^ - Made it clearer how to customise what objects AutoAPI will document. (#339) Misc ^^^^ - #375, #382 v2.1.0 (2023-03-28) ------------------- Deprecations and Removals ^^^^^^^^^^^^^^^^^^^^^^^^^ - Support for documenting languages other than Python is deprecated. (#248) - Removed the option to have autoapi generate toctree entries for domain objects. Domain objects are now added to the toctree by Sphinx. Dropped support for sphinx < 5.2.0. (#369) Misc ^^^^ - Added basic type checking. - Integrated towncrier into the release workflow. v2.0.1 (2023-01-16) ------------------- Features ^^^^^^^^ - Can turn off the addition of documented objects to the TOC tree. - Added support for Python 3.11. Bug Fixes ^^^^^^^^^ - `#330 `: (Python) Render tuple values as tuples, not lists. - `#341 `: (Python) Fix module level assignments to class attributes being documented as module level attributes. - (Python) Fix "bysource" sort order showing items in alphabetical order. - (Python) Use the correct directives for a variable type and value. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Removed some autogenerated test data from the repository. v2.0.0 (2022-09-27) ------------------- Breaking Changes ^^^^^^^^^^^^^^^^ - Dropped support for Sphinx <4. - `#352 `: (Python) Properties are rendered with the ``property`` directive, fixing support for Sphinx 5.2. A new ``PythonPythonMapper`` object (``PythonProperty``) has been created to support this change. This object can be passed to templates, filters, and hooks. A new ``property.rst`` template has also been created to support this change. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Use https links where possible in documentation. - Pass correct argument types to ``status_iterator``. V1.9.0 (2022-07-25) ------------------- Breaking Changes ^^^^^^^^^^^^^^^^ - Dropped support for Python 3.6. Features ^^^^^^^^ - Added support for Python 3.10. - `#222 `: Marked extension as parallel read safe. Bug Fixes ^^^^^^^^^ - `#324 `: (Python) Fail elegantly when no source files are found. - (Python) Stop calling ``autodoc-process-docstring`` when docstring is empty. Works around https://github.com/sphinx-doc/sphinx/issues/10701. - `#318 `: (Python) Fixed misaligned argument types on methods/classmethods when using type comments. - `#278 `: (Python) Limit signatures to 60 characters in summaries. - Fix keyerror when using markdown sources. - `#328 `: (Python) Fix kw-only marker getting ignored if first in the signature. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed tests in Sphinx 5. - Fixed many typos throughout the documentation. v1.8.4 (2021-08-16) ------------------- Bug Fixes ^^^^^^^^^ - `#301 `: (Python) Fixed compatibility with astroid 2.7+. v1.8.3 (2021-07-31) ------------------- Bug Fixes ^^^^^^^^^ - `#299 `: (Python) Fixed incorrect indentation in generated documentation when a class with no constructor has a summary line spanning multiple lines. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed broken link to Jinja objects.inv. v1.8.2 (2021-07-26) ------------------- Bug Fixes ^^^^^^^^^ - Fixed error when parsing a class with no constructor. - `#293 `: Fixed failure to build out of source conf.py files. Configuration values using relative values are now relative to the source directory instead of relative to the conf.py file. - `#289 `: (Python) Fixed AttributeError using inheritance diagrams on a module with plain imports. - `#292 `: Explicitly use the domain for generated directives. v1.8.1 (2021-04-24) ------------------- Bug Fixes ^^^^^^^^^ - `#273 `: Fixed type annotations being shown for only a single module. v1.8.0 (2021-04-12) ------------------- Features ^^^^^^^^ - Expandable value for multi-line string attributes. - `#265 `: Can resolve the qualified paths of parameters to generics. - `#275 `: Warnings have been categorised and can be suppressed through ``suppress_warnings``. - `#280 `: Data attributes are documented in module summaries. Bug Fixes ^^^^^^^^^ - `#273 `: Fixed setting ``autodoc_typehints`` to ``none`` or ``description`` not turning off signature type hints. ``autodoc_typehints`` integration is considered experimental until the extension properly supports overload functions. - `#261 `: Fixed data annotations causing pickle or deepcopy errors. - Documentation can be generated when multiple source directories share a single ``conf.py`` file. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed ``DeprecationWarning`` for invalid escape sequence ``\s`` in tests. - Fixed ``FutureWarning`` for ``Node.traverse()`` becoming an iterator instead of list. - New example implementation of ``autoapi-skip-member`` Sphinx event. - Can run tests with tox 4. - Updated packaging to use PEP-517. - All unittest style tests have been converted to pytest style tests. - An exception raised by docfx is raised directly instead of wrapping it. - Started using Github Actions for continuous integration. V1.7.0 (2021-01-31) ------------------- Features ^^^^^^^^ - The fully qualified path of objects are included type annotations so that Sphinx can link to them. - Added support for Sphinx 3.3. and 3.4. - `#240 `: The docstrings of ``object.__init__``, ``object.__new__``, ``type.__init__``, and ``type.__new__`` are not inherited. Bug Fixes ^^^^^^^^^ - `#260 `: The overload signatures of ``__init__`` methods are documented. V1.6.0 (2021-01-20) ------------------- Breaking Changes ^^^^^^^^^^^^^^^^ - Dropped support for Python 2 and Sphinx 1.x/2.x. Python 2 source code can still be parsed. Features ^^^^^^^^ - (Python) Added support for using type hints as parameter types and return types via the ``sphinx.ext.autodoc.typehints`` extension. - `#191 `: Basic incremental build support is enabled ``autoapi_keep_files`` is enabled. Providing none of the source files have changed, AutoAPI will skip parsing the source code and regenerating the API documentation. - `#200 `: Can pass a callback that edits the Jinja Environment object before template rendering begins. This allows custom filters, tests, and globals to be added to the environment. - Added support for Python 3.9. Bug Fixes ^^^^^^^^^ - `#246 `: (Python) Fixed TypeError when parsing a class that inherits from ``type``. - `#244 `: Fixed an unnecessary deprecation warning being raised when running sphinx-build from the same directory as conf.py. - (Python) Fixed properties documented by Autodoc directives getting documented as methods. V1.5.1 (2020-10-01) ------------------- Bug Fixes ^^^^^^^^^ - Fixed AttributeError when generating an inheritance diagram for a module. V1.5.0 (2020-08-31) ------------------- This will be the last minor version to support Python 2 and Sphinx 1.x/2.x. Features ^^^^^^^^ - `#222 `: Declare the extension as parallel unsafe. - `#217 `: (Python) All overload signatures are documented. - `#243 `: Files are found in order of preference according to ``autoapi_file_patterns``. - Added support for Sphinx 3.2. Bug Fixes ^^^^^^^^^ - `#219 `: (Python) Fixed return types not showing for methods. - (Python) Fixed incorrect formatting of properties on generated method directives. - Fixed every toctree entry getting added as a new list. - `#234 `: Fixed only some entries getting added to the toctree. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - autoapisummary directive inherits from autosummary for future stability. v1.4.0 (2020-06-07) ------------------- Features ^^^^^^^^ - `#197 `: Added ``autoapi.__version__`` and ``autoapi.__version_info__`` attributes for accessing version information. - `#201 `: (Python) Added the ``autoapi_member_order`` option to allow the order that members are documented to be configurable. - `#203 `: (Python) A class without a docstring inherits one from its parent. A methods without a docstring inherits one from the method that it overrides. - `#204 `: (Python) Added the ``imported-members`` AutoAPI option to be able to enable or disable documenting objects imported from the same top-level package or module without needing to override templates. Bug Fixes ^^^^^^^^^ - `#198 `: Documentation describes the required layout for template override directories. - `#195 `: (Python) Fixed incorrect formatting when ``show-inheritance-diagram`` and ``private-members`` are turned on. - `#193 ` and `#208 `: (Python) Inheritance diagrams can follow imports to find classes to document. - `#213 `: (Python) Fixed module summary never showing. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - black shows diffs by default - `#207 `: Fixed a typo in the code of the golang tutorial. v1.3.0 (2020-04-05) ------------------- Breaking Changes ^^^^^^^^^^^^^^^^ - Dropped support for Python 3.4 and 3.5. Features ^^^^^^^^ - `#151 `: (Python) Added the ``autoapi_python_use_implicit_namespaces`` option to allow AutoAPI to search for implicit namespace packages. - Added support for Sphinx 2.2 and 2.3. - Added support for Python 3.8. - `#140 `: (Python) Added the ``autoapi-inheritance-diagram`` directive to create inheritance diagrams without importing modules. Enable the ``show-inheritance-diagram`` AutoAPI option to turn the diagrams on in generated documentation. - `#183 `: (Python) Added the ``show-inheritance`` AutoAPI option to be able to enable or disable the display of a list of base classes in generated documentation about a class. Added the ``inherited-members`` AutoAPI option to be able to enable or disable the display of members inherited from a base class in generated documentation about a class. - The ``autoapi_include_summaries`` option has been replaced with the ``show-module-summary`` AutoAPI option. ``autoapi_include_summaries`` will stop working in the next major version. - Added support for Sphinx 2.4 and 3.0 Bug Fixes ^^^^^^^^^ - `#186 `: (Python) Fixed an exception when there are too many argument type annotations in a type comment. - (Python) args and kwargs type annotations can be read from the function type comment. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Tests are now included in the sdist. v1.2.1 (2019-10-09) ------------------- Bug Fixes ^^^^^^^^^ - (Python) "Invalid desc node" warning no longer raised for autodoc-style directives. v1.2.0 (2019-10-05) ------------------- Features ^^^^^^^^ - (Python) Can read per argument type comments with astroid > 2.2.5. - (Python) Added autoapidecorator directive with Sphinx >= 2.0. - (Python) Can use autodoc_docstring_signature with Autodoc-style directives. - (Python) Added autoapi-skip-member event. - Made it more clear which file causes an error, when an error occurs. - Sphinx language domains are now optional dependencies. Bug Fixes ^^^^^^^^^ - (Python) Forward reference annotations are no longer rendered as strings. - (Python) autoapifunction directive no longer documents async functions as a normal function. - (Python) Fixed unicode decode errors in some Python 3 situations. - Documentation more accurately describes what configuration accepts relative paths and where they are relative to. v1.1.0 (2019-06-23) ------------------- Features ^^^^^^^^ - (Python) Can override ignoring local imports in modules by using __all__. Bug Fixes ^^^^^^^^^ - (Python) Fixed incorrect formatting of functions and methods. - Added support for Sphinx 2.1. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed some dead links in the README. - Fixed lint virtualenv. v1.0.0 (2019-04-24) ------------------- Features ^^^^^^^^ - `#100 `: (Python) Added support for documenting C extensions via ``.pyi`` stub files. - Added support for Sphinx 2.0. - Toned down the API reference index page. - (Go) Patterns configured in ``autoapi_ignore`` are passed to godocjson. - New and improved documentation. - No longer need to set ``autoapi_add_toctree_entry`` to False when ``autoapi_generate_api_docs`` is False. - `#139 ` Added support for basic type annotations in documentation generation and autodoc-style directives. Bug Fixes ^^^^^^^^^ - `#159 `: (Python) Fixed ``UnicodeDecodeError`` on Python 2 when a documenting an attribute that contains binary data. - (Python) Fixed private submodules displaying when ``private-members`` is turned off. - Templates no longer produce excessive whitespace. - (Python) Fixed an error when giving an invalid object to an autodoc-style directive. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - No longer pin the version of black. - Added missing test environments to travis. v0.7.1 (2019-02-04) ------------------- Bug Fixes ^^^^^^^^^ - (Python) Fixed a false warning when importing a local module. v0.7.0 (2019-01-30) ------------------- Breaking Changes ^^^^^^^^^^^^^^^^ - Dropped support for Sphinx<1.6. Features ^^^^^^^^ - Added debug messages about what AutoAPI is doing. Bug Fixes ^^^^^^^^^ - `#156 `: (Python) Made import resolution more stable. Also capable of giving more detailed warnings. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Code is now formatted using black. - Removed references to old css and js files. - Replaced usage of deprecated Sphinx features. - Reorganised tests to be more pytest-like. v0.6.2 (2018-11-15) ------------------- Bug Fixes ^^^^^^^^^ - (Python) Fixed some import chains failing to resolve depending on resolution order. v0.6.1 (2018-11-14) ------------------- Bug Fixes ^^^^^^^^^ - (Python) Fixed unicode decoding on Python 3.7. - (Python) Fixed autodoc directives not documenting anything in submodules or subpackages. - (Python) Fixed error parsing files with unicode docstrings. - (Python) Fixed error when documenting something that's imported in more than one place. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - (Python) Added Python 3.7 testing. - Started testing against stable version of Sphinx 1.8. - Fixed all "no title" warnings during tests. v0.6.0 (2018-08-20) ------------------- Breaking Changes ^^^^^^^^^^^^^^^^ - `#152 `: Removed the ``autoapi_add_api_root_toctree`` option. This has been replaced with the ``autoapi_add_toctree_entry`` option. - `#25 `: Removed distutils support. - Removed redundant ``package_dir`` and ``package_data`` options. Features ^^^^^^^^ - (Python) Added viewcode support for imported members. - `#146 `: (Python) No longer documents ``__init__()`` attributes without a docstring. - `#153 `: (Python) Can document a public python API. - `#111 `: (Python) Can opt to write manual documentation through new autodoc-style directives. - `#152 `: Made it easier to remove default index page. Also removed autoapi_add_api_root_toctree config option - `#150 `: (Python) ``private-members`` also controls private subpackages and submodules. - (Python) Added support for static and class methods. - (Python) Methods include ``self`` in their arguments. This more closely matches autodoc behaviour. - `#145 `: (Python) Added support for detecting Python exceptions. - (Python) Can control how __init__ docstring is displayed. - (Python) Added support for viewcode. - (Python) Source files no longer need to be in ``sys.path``. Bug Fixes ^^^^^^^^^ - (Python) Fixed linking to builtin bases. - (Python) Fixed properties being documented more than once when set in ``__init__()``. - (Python) Fixed nested classes not getting displayed. - `#148 `: (Python) Fixed astroid 2.0 compatibility. - (Python) Fixed filtered classes and attributes getting displayed. - (Python) Fixed incorrect display of long lists. - `#125 `: (Javascript) Fixed running incorrect jsdoc command on Windows. - `#125 `: (Python) Support specifying package directories in ``autoapi_dirs``. Trivial/Internal Changes ^^^^^^^^^^^^^^^^^^^^^^^^ - Added Sphinx 1.7 and 1.8.0b1 testing. - `#120 `: Updated documentation to remove outdated references. - Removed old testing dependencies. - `#143 `: Removed unnecessary wheel dependency. sphinx-autoapi-3.1.2/LICENSE.rst000066400000000000000000000021241463504067600163140ustar00rootroot00000000000000The MIT License (MIT) ===================== Copyright (c) 2015 Read the Docs, Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sphinx-autoapi-3.1.2/MANIFEST.in000066400000000000000000000001341463504067600162350ustar00rootroot00000000000000recursive-include autoapi *.rst include *.rst include LICENSE.mit recursive-include tests * sphinx-autoapi-3.1.2/README.rst000066400000000000000000000067301463504067600161760ustar00rootroot00000000000000Sphinx AutoAPI ============== .. image:: https://readthedocs.org/projects/sphinx-autoapi/badge/?version=latest :target: https://sphinx-autoapi.readthedocs.org :alt: Documentation .. image:: https://github.com/readthedocs/sphinx-autoapi/actions/workflows/main.yml/badge.svg?branch=main :target: https://github.com/readthedocs/sphinx-autoapi/actions/workflows/main.yml?query=branch%3Amain :alt: Github Build Status .. image:: https://img.shields.io/pypi/v/sphinx-autoapi.svg :target: https://pypi.org/project/sphinx-autoapi/ :alt: PyPI Version .. image:: https://img.shields.io/pypi/pyversions/sphinx-autoapi.svg :target: https://pypi.org/project/sphinx-autoapi/ :alt: Supported Python Versions .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/python/black :alt: Formatted with Black Sphinx AutoAPI is a Sphinx extension for generating complete API documentation without needing to load, run, or import the project being documented. In contrast to the traditional `Sphinx autodoc `_, which requires manual authoring and uses code imports, AutoAPI finds and generates documentation by parsing source code. For more information, see `the full documentation `_. Getting Started --------------- The following steps will walk through how to add AutoAPI to an existing Sphinx project. For instructions on how to set up a Sphinx project, see `Sphinx's documentation `_. Installation ~~~~~~~~~~~~ AutoAPI can be installed through pip: .. code-block:: bash pip install sphinx-autoapi Next, add and configure AutoAPI in your Sphinx project's `conf.py`. .. code-block:: python extensions.append('autoapi.extension') autoapi_dirs = ['path/to/source/files', 'src'] When the documentation is built, AutoAPI will now generate API documentation into an `autoapi/` directory and add an entry to the documentation in your top level table of contents! To configure AutoAPI behaviour further, see the `Configuration documentation `_. Contributing ------------ Running the tests ~~~~~~~~~~~~~~~~~ Tests are executed through `tox `_. .. code-block:: bash tox Code Style ~~~~~~~~~~ Code is formatted using `black `_. You can check your formatting using black's check mode: .. code-block:: bash tox -e formatting You can also get black to format your changes for you: .. code-block:: bash black autoapi/ tests/ You can even get black to format changes automatically when you commit using `pre-commit `_: .. code-block:: bash pip install pre-commit pre-commit install Release Notes ~~~~~~~~~~~~~ Release notes are managed through `towncrier `_. When making a pull request you will need to create a news fragment to document your change: .. code-block:: bash tox -e release_notes -- create --help Versioning ---------- We use `SemVer `_ for versioning. For the versions available, see the `tags on this repository `_. License ------- This project is licensed under the MIT License. See the `LICENSE.rst `_ file for details. sphinx-autoapi-3.1.2/autoapi/000077500000000000000000000000001463504067600161435ustar00rootroot00000000000000sphinx-autoapi-3.1.2/autoapi/__init__.py000066400000000000000000000001741463504067600202560ustar00rootroot00000000000000"""Sphinx AutoAPI""" from .extension import setup __all__ = ("setup",) __version__ = "3.1.2" __version_info__ = (3, 1, 2) sphinx-autoapi-3.1.2/autoapi/_astroid_utils.py000066400000000000000000000472351463504067600215540ustar00rootroot00000000000000import builtins import itertools import re import astroid import astroid.nodes import astroid.nodes.node_classes def resolve_import_alias(name, import_names): """Resolve a name from an aliased import to its original name. :param name: The potentially aliased name to resolve. :type name: str :param import_names: The pairs of original names and aliases from the import. :type import_names: iterable(tuple(str, str or None)) :returns: The original name. :rtype: str """ resolved_name = name for import_name, imported_as in import_names: if import_name == name: break if imported_as == name: resolved_name = import_name break return resolved_name def get_full_import_name(import_from, name): """Get the full path of a name from a ``from x import y`` statement. Args: import_from (astroid.nodes.ImportFrom): The astroid node to resolve the name of. name (str) Returns: str: The full import path of the name. """ partial_basename = resolve_import_alias(name, import_from.names) module_name = import_from.modname if import_from.level: module = import_from.root() assert isinstance(module, astroid.nodes.Module) module_name = module.relative_to_absolute_name( import_from.modname, level=import_from.level ) return f"{module_name}.{partial_basename}" def resolve_qualname(node, basename): """Resolve where a node is defined to get its fully qualified name. Args: node (astroid.NodeNG): The node representing the base name. basename (str): The partial base name to resolve. Returns: str: The fully resolved base name. """ full_basename = basename top_level_name = re.sub(r"\(.*\)", "", basename).split(".", 1)[0] if isinstance(node, astroid.nodes.LocalsDictNodeNG): lookup_node = node else: lookup_node = node.scope() assigns = lookup_node.lookup(top_level_name)[1] for assignment in assigns: if isinstance(assignment, astroid.nodes.ImportFrom): import_name = get_full_import_name(assignment, top_level_name) full_basename = basename.replace(top_level_name, import_name, 1) break if isinstance(assignment, astroid.nodes.Import): import_name = resolve_import_alias(top_level_name, assignment.names) full_basename = basename.replace(top_level_name, import_name, 1) break if isinstance(assignment, astroid.nodes.ClassDef): full_basename = assignment.qname() break if isinstance(assignment, astroid.nodes.AssignName): full_basename = f"{assignment.scope().qname()}.{assignment.name}" if isinstance(node, astroid.nodes.Call): full_basename = re.sub(r"\(.*\)", "()", full_basename) if full_basename.startswith("builtins."): return full_basename[len("builtins.") :] if full_basename.startswith("__builtin__."): return full_basename[len("__builtin__.") :] return full_basename def get_full_basenames(node): """Resolve the partial names of a class' bases to fully qualified names. Args: node: The class definition node to resolve the bases of. :type: astroid.ClassDef Returns: iterable(str): The full names. """ for base in node.bases: yield _resolve_annotation(base) def _get_const_value(node): if isinstance(node, astroid.nodes.Const): if isinstance(node.value, str) and "\n" in node.value: return '"""{0}"""'.format(node.value) class NotConstException(Exception): pass def _inner(node): if isinstance(node, (astroid.nodes.List, astroid.nodes.Tuple)): new_value = [] for element in node.elts: new_value.append(_inner(element)) if isinstance(node, astroid.nodes.Tuple): return tuple(new_value) return new_value elif isinstance(node, astroid.nodes.Const): # Don't allow multi-line strings inside a data structure. if isinstance(node.value, str) and "\n" in node.value: raise NotConstException() return node.value raise NotConstException() try: result = _inner(node) except NotConstException: return None return repr(result) def get_assign_value(node): """Get the name and value of the assignment of the given node. Assignments to multiple names are ignored, as per PEP 257. Args: node (astroid.nodes.Assign or astroid.nodes.AnnAssign): The node to get the assignment value from. Returns: tuple(str, str or None) or None: The name that is assigned to, and the string representation of the value assigned to the name (if it can be converted). """ try: targets = node.targets except AttributeError: targets = [node.target] if len(targets) == 1: target = targets[0] if isinstance(target, astroid.nodes.AssignName): name = target.name elif isinstance(target, astroid.nodes.AssignAttr): name = target.attrname else: return None return (name, _get_const_value(node.value)) return None def get_assign_annotation(node): """Get the type annotation of the assignment of the given node. Args: node (astroid.nodes.Assign or astroid.nodes.AnnAssign): The node to get the annotation for. Returns: str or None: The type annotation as a string, or None if one does not exist. """ annotation_node = None try: annotation_node = node.annotation except AttributeError: annotation_node = node.type_annotation return format_annotation(annotation_node) def is_decorated_with_property(node): """Check if the function is decorated as a property. Args: node (astroid.nodes.FunctionDef): The node to check. Returns: bool: True if the function is a property, False otherwise. """ if not node.decorators: return False for decorator in node.decorators.nodes: if not isinstance(decorator, astroid.Name): continue try: if _is_property_decorator(decorator): return True except astroid.InferenceError: pass return False def _is_property_decorator(decorator): def _is_property_class(class_node): return ( class_node.name == "property" and class_node.root().name == builtins.__name__ ) for inferred in decorator.infer(): if not isinstance(inferred, astroid.nodes.ClassDef): continue if _is_property_class(inferred): return True if any(_is_property_class(ancestor) for ancestor in inferred.ancestors()): return True return False def is_decorated_with_property_setter(node): """Check if the function is decorated as a property setter. Args: node (astroid.nodes.FunctionDef): The node to check. Returns: bool: True if the function is a property setter, False otherwise. """ if not node.decorators: return False for decorator in node.decorators.nodes: if ( isinstance(decorator, astroid.nodes.Attribute) and decorator.attrname == "setter" ): return True return False def is_decorated_with_overload(node): """Check if the function is decorated as an overload definition. Args: node (astroid.nodes.FunctionDef): The node to check. Returns: bool: True if the function is an overload definition, False otherwise. """ if not node.decorators: return False for decorator in node.decorators.nodes: if not isinstance(decorator, (astroid.Name, astroid.Attribute)): continue try: if _is_overload_decorator(decorator): return True except astroid.InferenceError: pass return False def _is_overload_decorator(decorator): for inferred in decorator.infer(): if not isinstance(inferred, astroid.nodes.FunctionDef): continue if inferred.name == "overload" and inferred.root().name == "typing": return True return False def is_constructor(node): """Check if the function is a constructor. Args: node (astroid.nodes.FunctionDef): The node to check. Returns: bool: True if the function is a constructor, False otherwise. """ return ( node.parent and isinstance(node.parent.scope(), astroid.nodes.ClassDef) and node.name == "__init__" ) def is_exception(node): """Check if a class is an exception. Args: node (astroid.nodes.ClassDef): The node to check. Returns: bool: True if the class is an exception, False otherwise. """ if node.name in ("Exception", "BaseException") and node.root().name == "builtins": return True if not hasattr(node, "ancestors"): return False return any(is_exception(parent) for parent in node.ancestors(recurs=True)) def is_local_import_from(node, package_name): """Check if a node is an import from the local package. Args: node (astroid.node.NodeNG): The node to check. package_name (str): The name of the local package. Returns: bool: True if the node is an import from the local package, False otherwise. """ if not isinstance(node, astroid.ImportFrom): return False return ( node.level or node.modname == package_name or node.modname.startswith(package_name + ".") ) def get_module_all(node): """Get the contents of the ``__all__`` variable from a module. Args: node (astroid.nodes.Module): The module to get ``__all__`` from. Returns: list(str) or None: The contents of ``__all__`` if defined. Otherwise None. """ all_ = None if "__all__" in node.locals: assigned = next(node.igetattr("__all__")) if assigned is not astroid.Uninferable: all_ = [] for elt in getattr(assigned, "elts", ()): try: elt_name = next(elt.infer()) except astroid.InferenceError: continue if elt_name is astroid.Uninferable: continue if isinstance(elt_name, astroid.Const) and isinstance( elt_name.value, str ): all_.append(elt_name.value) return all_ def _is_ellipsis(node): return isinstance(node, astroid.Const) and node.value == Ellipsis def merge_annotations(annotations, comment_annotations): for ann, comment_ann in itertools.zip_longest(annotations, comment_annotations): if ann and not _is_ellipsis(ann): yield ann elif comment_ann and not _is_ellipsis(comment_ann): yield comment_ann else: yield None def _resolve_annotation(annotation): resolved = None if isinstance(annotation, astroid.Const): resolved = resolve_qualname(annotation, str(annotation.value)) elif isinstance(annotation, astroid.Name): resolved = resolve_qualname(annotation, annotation.name) elif isinstance(annotation, astroid.Attribute): resolved = resolve_qualname(annotation, annotation.as_string()) elif isinstance(annotation, astroid.Subscript): value = _resolve_annotation(annotation.value) slice_node = annotation.slice # astroid.Index was removed in astroid v3 if hasattr(astroid, "Index") and isinstance(slice_node, astroid.Index): slice_node = slice_node.value if value == "Literal": if isinstance(slice_node, astroid.Tuple): elts = slice_node.elts else: elts = [slice_node] slice_ = ", ".join( ( elt.as_string() if isinstance(elt, astroid.Const) else _resolve_annotation(elt) ) for elt in elts ) elif isinstance(slice_node, astroid.Tuple): slice_ = ", ".join(_resolve_annotation(elt) for elt in slice_node.elts) else: slice_ = _resolve_annotation(slice_node) resolved = f"{value}[{slice_}]" elif isinstance(annotation, astroid.Tuple): resolved = ( "(" + ", ".join(_resolve_annotation(elt) for elt in annotation.elts) + ")" ) elif isinstance(annotation, astroid.List): resolved = ( "[" + ", ".join(_resolve_annotation(elt) for elt in annotation.elts) + "]" ) elif isinstance(annotation, astroid.BinOp) and annotation.op == "|": left = _resolve_annotation(annotation.left) right = _resolve_annotation(annotation.right) resolved = f"{left} | {right}" else: resolved = annotation.as_string() if resolved.startswith("typing."): return resolved[len("typing.") :] # Sphinx is capable of linking anything in the same module # without needing a fully qualified path. module_prefix = annotation.root().name + "." if resolved.startswith(module_prefix): return resolved[len(module_prefix) :] return resolved def format_annotation(annotation): if annotation: return _resolve_annotation(annotation) return annotation def _iter_args(args, annotations, defaults): default_offset = len(args) - len(defaults) packed = itertools.zip_longest(args, annotations) for i, (arg, annotation) in enumerate(packed): default = None if defaults is not None and i >= default_offset: if defaults[i - default_offset] is not None: default = defaults[i - default_offset].as_string() name = arg.name if isinstance(arg, astroid.Tuple): argument_names = ", ".join(x.name for x in arg.elts) name = f"({argument_names})" yield (name, format_annotation(annotation), default) def get_args_info(args_node): result = [] positional_only_defaults = [] positional_or_keyword_defaults = args_node.defaults if args_node.defaults: args = args_node.args or [] positional_or_keyword_defaults = args_node.defaults[-len(args) :] positional_only_defaults = args_node.defaults[ : len(args_node.defaults) - len(args) ] plain_annotations = args_node.annotations or () func_comment_annotations = args_node.parent.type_comment_args or [] if args_node.parent.type in ("method", "classmethod"): func_comment_annotations = [None] + func_comment_annotations comment_annotations = args_node.type_comment_posonlyargs comment_annotations += args_node.type_comment_args or [] comment_annotations += args_node.type_comment_kwonlyargs annotations = list( merge_annotations( plain_annotations, merge_annotations(func_comment_annotations, comment_annotations), ) ) annotation_offset = 0 if args_node.posonlyargs: posonlyargs_annotations = args_node.posonlyargs_annotations if not any(args_node.posonlyargs_annotations): num_args = len(args_node.posonlyargs) posonlyargs_annotations = annotations[ annotation_offset : annotation_offset + num_args ] for arg, annotation, default in _iter_args( args_node.posonlyargs, posonlyargs_annotations, positional_only_defaults ): result.append((None, arg, annotation, default)) result.append(("/", None, None, None)) if not any(args_node.posonlyargs_annotations): annotation_offset += num_args if args_node.args: num_args = len(args_node.args) for arg, annotation, default in _iter_args( args_node.args, annotations[annotation_offset : annotation_offset + num_args], positional_or_keyword_defaults, ): result.append((None, arg, annotation, default)) annotation_offset += num_args if args_node.vararg: annotation = None if args_node.varargannotation: annotation = format_annotation(args_node.varargannotation) elif len(annotations) > annotation_offset and annotations[annotation_offset]: annotation = format_annotation(annotations[annotation_offset]) annotation_offset += 1 result.append(("*", args_node.vararg, annotation, None)) if args_node.kwonlyargs: if not args_node.vararg: result.append(("*", None, None, None)) kwonlyargs_annotations = args_node.kwonlyargs_annotations if not any(args_node.kwonlyargs_annotations): num_args = len(args_node.kwonlyargs) kwonlyargs_annotations = annotations[ annotation_offset : annotation_offset + num_args ] for arg, annotation, default in _iter_args( args_node.kwonlyargs, kwonlyargs_annotations, args_node.kw_defaults, ): result.append((None, arg, annotation, default)) if not any(args_node.kwonlyargs_annotations): annotation_offset += num_args if args_node.kwarg: annotation = None if args_node.kwargannotation: annotation = format_annotation(args_node.kwargannotation) elif len(annotations) > annotation_offset and annotations[annotation_offset]: annotation = format_annotation(annotations[annotation_offset]) annotation_offset += 1 result.append(("**", args_node.kwarg, annotation, None)) if args_node.parent.type in ("method", "classmethod") and result: result.pop(0) return result def get_return_annotation(node): """Get the return annotation of a node. Args: node (astroid.nodes.FunctionDef) """ return_annotation = None if node.returns: return_annotation = format_annotation(node.returns) elif node.type_comment_returns: return_annotation = format_annotation(node.type_comment_returns) return return_annotation def get_func_docstring(node): """Get the docstring of a node, using a parent docstring if needed. Args: node (astroid.nodes.FunctionDef): The node to get a docstring for. """ doc = node.doc_node.value if node.doc_node else "" if not doc and isinstance(node.parent, astroid.nodes.ClassDef): for base in node.parent.ancestors(): if node.name in ("__init__", "__new__") and base.qname() in ( "__builtins__.object", "builtins.object", "builtins.type", ): continue for child in base.get_children(): if ( isinstance(child, node.__class__) and child.name == node.name and child.doc_node is not None ): return child.doc_node.value return doc def get_class_docstring(node): """Get the docstring of a node, using a parent docstring if needed. Args: node (astroid.nodes.ClassDef): The node to get a docstring for. """ doc = node.doc_node.value if node.doc_node else "" if not doc: for base in node.ancestors(): if base.qname() in ( "__builtins__.object", "builtins.object", "builtins.type", ): continue if base.doc_node is not None: return base.doc_node.value return doc sphinx-autoapi-3.1.2/autoapi/_mapper.py000066400000000000000000000533031463504067600201440ustar00rootroot00000000000000import collections import copy import fnmatch import itertools import operator import os import re from jinja2 import Environment, FileSystemLoader import sphinx import sphinx.environment from sphinx.errors import ExtensionError import sphinx.util import sphinx.util.logging from sphinx.util.console import colorize from sphinx.util.display import status_iterator import sphinx.util.docstrings from sphinx.util.osutil import ensuredir from ._parser import Parser from ._objects import ( PythonClass, PythonFunction, PythonModule, PythonMethod, PythonPackage, PythonProperty, PythonAttribute, PythonData, PythonException, ) from .settings import OWN_PAGE_LEVELS, TEMPLATE_DIR LOGGER = sphinx.util.logging.getLogger(__name__) def _expand_wildcard_placeholder(original_module, originals_map, placeholder): """Expand a wildcard placeholder to a sequence of named placeholders. :param original_module: The data dictionary of the module that the placeholder is imported from. :type original_module: dict :param originals_map: A map of the names of children under the module to their data dictionaries. :type originals_map: dict(str, dict) :param placeholder: The wildcard placeholder to expand. :type placeholder: dict :returns: The placeholders that the wildcard placeholder represents. :rtype: list(dict) """ originals = originals_map.values() if original_module["all"] is not None: originals = [] for name in original_module["all"]: if name == "__all__": continue if name not in originals_map: msg = f"Invalid __all__ entry {name} in {original_module['name']}" LOGGER.warning(msg, type="autoapi", subtype="python_import_resolution") continue originals.append(originals_map[name]) placeholders = [] for original in originals: new_full_name = placeholder["full_name"].replace("*", original["name"]) new_qual_name = placeholder["qual_name"].replace("*", original["name"]) new_original_path = placeholder["original_path"].replace("*", original["name"]) if "original_path" in original: new_original_path = original["original_path"] new_placeholder = dict( placeholder, name=original["name"], qual_name=new_qual_name, full_name=new_full_name, original_path=new_original_path, ) placeholders.append(new_placeholder) return placeholders def _resolve_module_placeholders(modules, module_name, visit_path, resolved): """Resolve all placeholder children under a module. Args: modules (dict(str, dict)): A mapping of module names to their data dictionary. Placeholders are resolved in place. module_name (str): The name of the module to resolve. visit_path: An ordered set of visited module names. visited (collections.OrderedDict) resolved (set(str)): A set of already resolved module names. """ if module_name in resolved: return visit_path[module_name] = True module, children = modules[module_name] for child in list(children.values()): if child["type"] != "placeholder": continue if child["original_path"] in modules: module["children"].remove(child) children.pop(child["name"]) continue imported_from, original_name = child["original_path"].rsplit(".", 1) if imported_from in visit_path: visit_str = ", ".join(visit_path) msg = f"Cannot resolve cyclic import: {visit_str}, {imported_from}" LOGGER.warning(msg, type="autoapi", subtype="python_import_resolution") module["children"].remove(child) children.pop(child["name"]) continue if imported_from not in modules: msg = ( f"Cannot resolve import of unknown module {imported_from}" f" in {module_name}" ) LOGGER.warning(msg, type="autoapi", subtype="python_import_resolution") module["children"].remove(child) children.pop(child["name"]) continue _resolve_module_placeholders(modules, imported_from, visit_path, resolved) if original_name == "*": original_module, originals_map = modules[imported_from] # Replace the wildcard placeholder # with a list of named placeholders. new_placeholders = _expand_wildcard_placeholder( original_module, originals_map, child ) child_index = module["children"].index(child) module["children"][child_index : child_index + 1] = new_placeholders children.pop(child["name"]) for new_placeholder in new_placeholders: if new_placeholder["name"] not in children: children[new_placeholder["name"]] = new_placeholder original = originals_map[new_placeholder["name"]] _resolve_placeholder(new_placeholder, original) elif original_name not in modules[imported_from][1]: msg = f"Cannot resolve import of {child['original_path']} in {module_name}" LOGGER.warning(msg, type="autoapi", subtype="python_import_resolution") module["children"].remove(child) children.pop(child["name"]) continue else: original = modules[imported_from][1][original_name] _resolve_placeholder(child, original) del visit_path[module_name] resolved.add(module_name) def _resolve_placeholder(placeholder, original): """Resolve a placeholder to the given original object. Args: placeholder (dict): The placeholder to resolve, in place. original (dict): The object that the placeholder represents. """ new = copy.deepcopy(original) # We are supposed to be resolving the placeholder, # not replacing it with another. assert original["type"] != "placeholder" # The name remains the same. new["name"] = placeholder["name"] new["qual_name"] = placeholder["qual_name"] new["full_name"] = placeholder["full_name"] # Record where the placeholder originally came from. new["original_path"] = original["full_name"] # The source lines for this placeholder do not exist in this file. # The keys might not exist if original is a resolved placeholder. new.pop("from_line_no", None) new.pop("to_line_no", None) # Resolve the children stack = list(new.get("children", ())) while stack: child = stack.pop() # Relocate the child to the new location assert child["full_name"].startswith(original["full_name"]) suffix = child["full_name"][len(original["full_name"]) :] child["full_name"] = new["full_name"] + suffix # The source lines for this placeholder do not exist in this file. # The keys might not exist if original is a resolved placeholder. child.pop("from_line_no", None) child.pop("to_line_no", None) # Resolve the remaining children stack.extend(child.get("children", ())) placeholder.clear() placeholder.update(new) def _link_objs(value): result = "" delims = r"(\s*[\[\]\(\),]\s*)" delims_re = re.compile(delims) sub_targets = re.split(delims, value.strip()) for sub_target in sub_targets: sub_target = sub_target.strip() if delims_re.match(sub_target): result += f"{sub_target}" if sub_target.endswith(","): result += " " else: result += "\\ " elif sub_target: result += f":py:obj:`{sub_target}`\\ " # Strip off the extra "\ " return result[:-2] class Mapper: """Base class for mapping `PythonMapperBase` objects to Sphinx. Args: app: Sphinx application instance """ _OBJ_MAP = { cls.type: cls for cls in ( PythonClass, PythonFunction, PythonModule, PythonMethod, PythonPackage, PythonProperty, PythonAttribute, PythonData, PythonException, ) } def __init__(self, app, template_dir=None, dir_root=None, url_root=None): self.app = app template_paths = [TEMPLATE_DIR] if template_dir: # Put at the front so it's loaded first template_paths.insert(0, template_dir) self.jinja_env = Environment( loader=FileSystemLoader(template_paths), trim_blocks=True, lstrip_blocks=True, ) def _wrapped_prepare(value): return value self.jinja_env.filters["prepare_docstring"] = _wrapped_prepare if self.app.config.autoapi_prepare_jinja_env: self.app.config.autoapi_prepare_jinja_env(self.jinja_env) own_page_level = self.app.config.autoapi_own_page_level desired_page_level = OWN_PAGE_LEVELS.index(own_page_level) self.own_page_types = set(OWN_PAGE_LEVELS[: desired_page_level + 1]) self.dir_root = dir_root self.url_root = url_root # Mapping of {filepath -> raw data} self.paths = collections.OrderedDict() # Mapping of {object id -> Python Object} self.objects_to_render = collections.OrderedDict() # Mapping of {object id -> Python Object} self.all_objects = collections.OrderedDict() # Mapping of {namespace id -> Python Object} self.namespaces = collections.OrderedDict() self.jinja_env.filters["link_objs"] = _link_objs self._use_implicit_namespace = ( self.app.config.autoapi_python_use_implicit_namespaces ) @staticmethod def find_files(patterns, dirs, ignore): if not ignore: ignore = [] pattern_regexes = [] for pattern in patterns: regex = re.compile(fnmatch.translate(pattern).replace(".*", "(.*)")) pattern_regexes.append((pattern, regex)) for _dir in dirs: for root, _, filenames in os.walk(_dir): seen = set() for pattern, pattern_re in pattern_regexes: for filename in fnmatch.filter(filenames, pattern): skip = False match = re.match(pattern_re, filename) norm_name = match.groups() if norm_name in seen: continue # Skip ignored files for ignore_pattern in ignore: if fnmatch.fnmatch( os.path.join(root, filename), ignore_pattern ): LOGGER.info( colorize("bold", "[AutoAPI] ") + colorize( "darkgreen", f"Ignoring {root}/{filename}" ) ) skip = True if skip: continue # Make sure the path is full if not os.path.isabs(filename): filename = os.path.join(root, filename) yield filename seen.add(norm_name) def output_rst(self, source_suffix): for _, obj in status_iterator( self.objects_to_render.items(), colorize("bold", "[AutoAPI] ") + "Rendering Data... ", length=len(self.objects_to_render), verbosity=1, stringify_func=(lambda x: x[0]), ): rst = obj.render(is_own_page=True) if not rst: continue output_dir = obj.output_dir(self.dir_root) ensuredir(output_dir) output_path = output_dir / obj.output_filename() path = f"{output_path}{source_suffix}" with open(path, "wb+") as detail_file: detail_file.write(rst.encode("utf-8")) if self.app.config.autoapi_add_toctree_entry: self._output_top_rst() def _output_top_rst(self): # Render Top Index top_level_index = os.path.join(self.dir_root, "index.rst") pages = [obj for obj in self.objects_to_render.values() if obj.display] with open(top_level_index, "wb") as top_level_file: content = self.jinja_env.get_template("index.rst") top_level_file.write(content.render(pages=pages).encode("utf-8")) def _need_to_load(self, files): last_files = getattr(self.app.env, "autoapi_source_files", []) self.app.env.autoapi_source_files = files last_mtime = getattr(self.app.env, "autoapi_max_mtime", 0) this_mtime = max(os.path.getmtime(file) for _, file in files) self.app.env.autoapi_max_mtime = this_mtime if not self.app.config.autoapi_keep_files: return True if self.app.env.config_status != sphinx.environment.CONFIG_OK: return True return last_files != files or not last_mtime or last_mtime < this_mtime def _find_files(self, patterns, dirs, ignore): for dir_ in dirs: dir_root = dir_ if ( os.path.exists(os.path.join(dir_, "__init__.py")) or os.path.exists(os.path.join(dir_, "__init__.pyi")) or self._use_implicit_namespace ): dir_root = os.path.abspath(os.path.join(dir_, os.pardir)) for path in self.find_files(patterns=patterns, dirs=[dir_], ignore=ignore): yield dir_root, path def load(self, patterns, dirs, ignore=None): """Load objects from the filesystem into the ``paths`` dictionary Also include an attribute on the object, ``relative_path`` which is the shortened, relative path the package/module """ dir_root_files = list(self._find_files(patterns, dirs, ignore)) if not dir_root_files: raise ExtensionError(f"No source files found in: {','.join(dirs)}") if not self._need_to_load(dir_root_files): LOGGER.debug( "[AutoAPI] Skipping read stage because source files have not changed." ) return False for dir_root, path in status_iterator( dir_root_files, colorize("bold", "[AutoAPI] Reading files... "), length=len(dir_root_files), stringify_func=(lambda x: x[1]), ): data = self.read_file(path=path, dir_root=dir_root) if data: data["relative_path"] = os.path.relpath(path, dir_root) self.paths[path] = data return True def read_file(self, path, **kwargs): """Read file input into memory, returning deserialized objects Args: path: Path of file to read """ dir_root = kwargs.get("dir_root") try: if self._use_implicit_namespace: parsed_data = Parser().parse_file_in_namespace(path, dir_root) else: parsed_data = Parser().parse_file(path) return parsed_data except (IOError, TypeError, ImportError): LOGGER.debug("Reason:", exc_info=True) LOGGER.warning( f"Unable to read file: {path}", type="autoapi", subtype="not_readable", ) return None def _resolve_placeholders(self): """Resolve objects that have been imported from elsewhere.""" modules = {} for module in self.paths.values(): children = {child["name"]: child for child in module["children"]} modules[module["name"]] = (module, children) resolved = set() for module_name in modules: visit_path = collections.OrderedDict() _resolve_module_placeholders(modules, module_name, visit_path, resolved) def _hide_yo_kids(self): """For all direct children of a module/package, hide them if needed.""" for module in self.paths.values(): if module["all"] is not None: all_names = set(module["all"]) for child in module["children"]: if child["qual_name"] not in all_names: child["hide"] = True elif module["type"] == "module": for child in module["children"]: if "original_path" in child: child["hide"] = True def map(self, options=None): self._resolve_placeholders() self._hide_yo_kids() self.app.env.autoapi_annotations = {} for _, data in status_iterator( self.paths.items(), colorize("bold", "[AutoAPI] ") + "Mapping Data... ", length=len(self.paths), stringify_func=(lambda x: x[0]), ): for obj in self.create_class(data, options=options): self.all_objects[obj.id] = obj self._create_module_hierarchy() self._render_selection() self.app.env.autoapi_objects = self.objects_to_render self.app.env.autoapi_all_objects = self.all_objects def _create_module_hierarchy(self) -> None: """Populate the sub{module,package}s attributes of all top level objects.""" for obj in self.all_objects.values(): parent_name = obj.name.rsplit(".", 1)[0] if parent_name in self.all_objects and parent_name != obj.name: parent = self.all_objects[parent_name] attr = f"sub{obj.type}s" getattr(parent, attr).append(obj) for obj in self.all_objects.values(): obj.submodules.sort() obj.subpackages.sort() def _render_selection(self): """Propagate display values to children.""" for obj in sorted(self.all_objects.values(), key=lambda obj: len(obj.id)): if obj.display: assert obj.type in self.own_page_types self.objects_to_render[obj.id] = obj else: for module in itertools.chain(obj.subpackages, obj.submodules): module.obj["hide"] = True def _inner(parent): for child in parent.children: self.all_objects[child.id] = child if not parent.display: child.obj["hide"] = True if child.display and child.type in self.own_page_types: self.objects_to_render[child.id] = child _inner(child) for obj in list(self.all_objects.values()): _inner(obj) def create_class(self, data, options=None): """Create a class from the passed in data Args: data: dictionary data of parser output """ try: cls = self._OBJ_MAP[data["type"]] except KeyError: # this warning intentionally has no (sub-)type LOGGER.warning(f"Unknown type: {data['type']}") else: obj = cls( data, class_content=self.app.config.autoapi_python_class_content, options=self.app.config.autoapi_options, jinja_env=self.jinja_env, app=self.app, url_root=self.url_root, ) for child_data in data.get("children", []): for child_obj in self.create_class(child_data, options=options): obj.children.append(child_obj) # Some objects require children to establish their docstring # or type annotations (eg classes with inheritance), # so do this after all children have been created. lines = obj.docstring.splitlines() if lines: # Add back the trailing newline that .splitlines removes lines.append("") if "autodoc-process-docstring" in self.app.events.events: self.app.emit( "autodoc-process-docstring", cls.type, obj.name, None, None, lines, ) obj.docstring = "\n".join(lines) self._record_typehints(obj) # Parser gives children in source order already if self.app.config.autoapi_member_order == "alphabetical": obj.children.sort(key=operator.attrgetter("name")) elif self.app.config.autoapi_member_order == "groupwise": obj.children.sort(key=lambda x: (x.member_order, x.name)) yield obj def _record_typehints(self, obj): if ( isinstance(obj, (PythonClass, PythonFunction, PythonMethod)) and not obj.overloads ) or isinstance(obj, PythonProperty): obj_annotations = {} include_return_annotation = True obj_data = obj.obj if isinstance(obj, PythonClass): constructor = obj.constructor if constructor: include_return_annotation = False obj_data = constructor.obj else: return for _, name, annotation, _ in obj_data["args"]: if name and annotation: obj_annotations[name] = annotation return_annotation = obj_data["return_annotation"] if include_return_annotation and return_annotation: obj_annotations["return"] = return_annotation self.app.env.autoapi_annotations[obj.id] = obj_annotations sphinx-autoapi-3.1.2/autoapi/_objects.py000066400000000000000000000375311463504067600203160ustar00rootroot00000000000000from __future__ import annotations import functools import pathlib from typing import List, Optional, Tuple import sphinx import sphinx.util import sphinx.util.logging from .settings import OWN_PAGE_LEVELS LOGGER = sphinx.util.logging.getLogger(__name__) def _format_args(args_info, include_annotations=True, ignore_self=None): result = [] for i, (prefix, name, annotation, default) in enumerate(args_info): if i == 0 and ignore_self is not None and name == ignore_self: continue formatted = ( (prefix or "") + (name or "") + (f": {annotation}" if annotation and include_annotations else "") + ((" = {}" if annotation else "={}").format(default) if default else "") ) result.append(formatted) return ", ".join(result) class PythonObject: """A class representing an entity from the parsed source code. This class turns the dictionaries output by the parser into an object. Args: obj: JSON object representing this object jinja_env: A template environment for rendering this object """ member_order = 0 """The ordering of objects when doing "groupwise" sorting.""" type: str def __init__( self, obj, jinja_env, app, url_root, options=None, class_content="class" ): self.app = app self.obj = obj self.options = options self.jinja_env = jinja_env self.url_root = url_root self.name: str = obj["name"] """The name of the object, as named in the parsed source code. This name will have no periods in it. """ self.qual_name: str = obj["qual_name"] """The qualified name for this object.""" self.id: str = obj.get("full_name", self.name) """A globally unique identifier for this object. This is the same as the fully qualified name of the object. """ self.children: List[PythonObject] = [] """The members of this object. For example, the classes and functions defined in the parent module. """ self._docstring: str = obj["doc"] self.imported: bool = "original_path" in obj """Whether this object was imported from another module.""" self.inherited: bool = obj.get("inherited", False) """Whether this was inherited from an ancestor of the parent class.""" # For later self._class_content = class_content self._display_cache: Optional[bool] = None def __getstate__(self): """Obtains serialisable data for pickling.""" __dict__ = self.__dict__.copy() __dict__.update(app=None, jinja_env=None) # clear unpickable attributes return __dict__ def render(self, **kwargs): LOGGER.log("VERBOSE", "Rendering %s", self.id) template = self.jinja_env.get_template(f"python/{self.type}.rst") ctx = {} ctx.update(**self.get_context_data()) ctx.update(**kwargs) return template.render(**ctx) @property def rendered(self): """Shortcut to render an object in templates.""" return self.render() def get_context_data(self): own_page_level = self.app.config.autoapi_own_page_level desired_page_level = OWN_PAGE_LEVELS.index(own_page_level) own_page_types = set(OWN_PAGE_LEVELS[: desired_page_level + 1]) return { "autoapi_options": self.app.config.autoapi_options, "include_summaries": self.app.config.autoapi_include_summaries, "obj": self, "own_page_types": own_page_types, "sphinx_version": sphinx.version_info, } def __lt__(self, other): """Object sorting comparison""" if not isinstance(other, PythonObject): return NotImplemented return self.id < other.id def __str__(self) -> str: return f"<{self.__class__.__name__} {self.id}>" @property def short_name(self) -> str: """Shorten name property""" return self.name.split(".")[-1] def output_dir(self, root): """The directory to render this object.""" module = self.id[: -(len("." + self.qual_name))] parts = [root] + module.split(".") return pathlib.PurePosixPath(*parts) def output_filename(self) -> str: """The name of the file to render into, without a file suffix.""" filename = self.qual_name if filename == "index": filename = ".index" return filename @property def include_path(self) -> str: """Return 'absolute' path without regarding OS path separator This is used in ``toctree`` directives, as Sphinx always expects Unix path separators """ return str(self.output_dir(self.url_root) / self.output_filename()) @property def docstring(self) -> str: """The docstring for this object. If a docstring did not exist on the object, this will be the empty string. For classes, this will also depend on the :confval:`autoapi_python_class_content` option. """ return self._docstring @docstring.setter def docstring(self, value: str) -> None: self._docstring = value self._docstring_resolved = True @property def is_top_level_object(self) -> bool: """Whether this object is at the very top level (True) or not (False). This will be False for subpackages and submodules. """ return "." not in self.id @property def is_undoc_member(self) -> bool: """Whether this object has a docstring (False) or not (True).""" return not bool(self.docstring) @property def is_private_member(self) -> bool: """Whether this object is private (True) or not (False).""" return self.short_name.startswith("_") and not self.short_name.endswith("__") @property def is_special_member(self) -> bool: """Whether this object is a special member (True) or not (False).""" return self.short_name.startswith("__") and self.short_name.endswith("__") @property def display(self) -> bool: """Whether this object should be displayed in documentation. This attribute depends on the configuration options given in :confval:`autoapi_options` and the result of :event:`autoapi-skip-member`. """ if self._display_cache is None: self._display_cache = not self._ask_ignore(self._should_skip()) return self._display_cache @property def summary(self) -> str: """The summary line of the docstring. The summary line is the first non-empty line, as-per :pep:`257`. This will be the empty string if the object does not have a docstring. """ for line in self.docstring.splitlines(): line = line.strip() if line: return line return "" def _should_skip(self) -> bool: skip_undoc_member = self.is_undoc_member and "undoc-members" not in self.options skip_private_member = ( self.is_private_member and "private-members" not in self.options ) skip_special_member = ( self.is_special_member and "special-members" not in self.options ) skip_imported_member = self.imported and "imported-members" not in self.options skip_inherited_member = ( self.inherited and "inherited-members" not in self.options ) return ( self.obj.get("hide", False) or skip_undoc_member or skip_private_member or skip_special_member or skip_imported_member or skip_inherited_member ) def _ask_ignore(self, skip: bool) -> bool: ask_result = self.app.emit_firstresult( "autoapi-skip-member", self.type, self.id, self, skip, self.options ) return ask_result if ask_result is not None else skip def _children_of_type(self, type_: str) -> List[PythonObject]: return list(child for child in self.children if child.type == type_) class PythonFunction(PythonObject): """The representation of a function.""" type = "function" member_order = 30 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) autodoc_typehints = getattr(self.app.config, "autodoc_typehints", "signature") show_annotations = autodoc_typehints != "none" and not ( autodoc_typehints == "description" and not self.obj["overloads"] ) self.args: str = _format_args(self.obj["args"], show_annotations) """The arguments to this object, formatted as a string.""" self.return_annotation: Optional[str] = ( self.obj["return_annotation"] if show_annotations else None ) """The type annotation for the return type of this function. This will be ``None`` if an annotation or annotation comment was not given. """ self.properties: List[str] = self.obj["properties"] """The properties that describe what type of function this is. Can be only be: async. """ self.overloads: List[Tuple[str, str]] = [ (_format_args(args), return_annotation) for args, return_annotation in self.obj["overloads"] ] """The overloaded signatures of this function. Each tuple is a tuple of ``(args, return_annotation)`` """ class PythonMethod(PythonFunction): """The representation of a method.""" type = "method" member_order = 50 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.properties: List[str] = self.obj["properties"] """The properties that describe what type of method this is. Can be any of: abstractmethod, async, classmethod, property, staticmethod. """ def _should_skip(self) -> bool: return super()._should_skip() or self.name in ( "__new__", "__init__", ) class PythonProperty(PythonObject): """The representation of a property on a class.""" type = "property" member_order = 60 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.annotation: Optional[str] = self.obj["return_annotation"] """The type annotation of this property.""" self.properties: List[str] = self.obj["properties"] """The properties that describe what type of property this is. Can be any of: abstractmethod, classmethod. """ class PythonData(PythonObject): """Global, module level data.""" type = "data" member_order = 40 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.value: Optional[str] = self.obj.get("value") """The value of this attribute. This will be ``None`` if the value is not constant. """ self.annotation: Optional[str] = self.obj.get("annotation") """The type annotation of this attribute. This will be ``None`` if an annotation or annotation comment was not given. """ class PythonAttribute(PythonData): """An object/class level attribute.""" type = "attribute" member_order = 60 class TopLevelPythonPythonMapper(PythonObject): """A common base class for modules and packages.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.subpackages = [] self.submodules = [] self.all = self.obj["all"] """The contents of ``__all__`` if assigned to. Only constants are included. This will be ``None`` if no ``__all__`` was set. :type: list(str) or None """ @property def functions(self): """All of the member functions. :type: list(PythonFunction) """ return self._children_of_type("function") @property def classes(self): """All of the member classes. :type: list(PythonClass) """ return self._children_of_type("class") def output_dir(self, root): """The path to the file to render into, without a file suffix.""" parts = [root] + self.name.split(".") return pathlib.PurePosixPath(*parts) def output_filename(self): """The path to the file to render into, without a file suffix.""" return "index" class PythonModule(TopLevelPythonPythonMapper): """The representation of a module.""" type = "module" class PythonPackage(TopLevelPythonPythonMapper): """The representation of a package.""" type = "package" class PythonClass(PythonObject): """The representation of a class.""" type = "class" member_order = 20 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.bases: List[str] = self.obj["bases"] """The fully qualified names of all base classes.""" self._docstring_resolved: bool = False @property def args(self) -> str: """The arguments to this object, formatted as a string.""" args = "" if self.constructor: autodoc_typehints = getattr( self.app.config, "autodoc_typehints", "signature" ) show_annotations = autodoc_typehints != "none" and not ( autodoc_typehints == "description" and not self.constructor.overloads ) args_data = self.constructor.obj["args"] args = _format_args(args_data, show_annotations, ignore_self="self") return args @property def overloads(self) -> List[Tuple[str, str]]: overloads = [] if self.constructor: overload_data = self.constructor.obj["overloads"] autodoc_typehints = getattr( self.app.config, "autodoc_typehints", "signature" ) show_annotations = autodoc_typehints not in ("none", "description") overloads = [ ( _format_args(args, show_annotations, ignore_self="self"), return_annotation, ) for args, return_annotation in overload_data ] return overloads @property def docstring(self) -> str: docstring = self._docstring if not self._docstring_resolved and self._class_content in ("both", "init"): constructor_docstring = self.constructor_docstring if constructor_docstring: if self._class_content == "both": docstring = f"{docstring}\n{constructor_docstring}" else: docstring = constructor_docstring return docstring @docstring.setter def docstring(self, value: str) -> None: self._docstring = value self._docstring_resolved = True @property def methods(self): return self._children_of_type("method") @property def properties(self): return self._children_of_type("property") @property def attributes(self): return self._children_of_type("attribute") @property def classes(self): return self._children_of_type("class") @property @functools.lru_cache() def constructor(self): for child in self.children: if child.short_name == "__init__": return child return None @property def constructor_docstring(self) -> str: docstring = "" constructor = self.constructor if constructor and constructor.docstring: docstring = constructor.docstring else: for child in self.children: if child.short_name == "__new__": docstring = child.docstring break return docstring class PythonException(PythonClass): """The representation of an exception class.""" type = "exception" member_order = 10 sphinx-autoapi-3.1.2/autoapi/_parser.py000066400000000000000000000264241463504067600201600ustar00rootroot00000000000000import collections import itertools import os import astroid import astroid.builder import sphinx.util.docstrings from . import _astroid_utils def _prepare_docstring(doc): return "\n".join(sphinx.util.docstrings.prepare_docstring(doc)) class Parser: def __init__(self): self._qual_name_stack = [] self._full_name_stack = [] self._encoding = None def _get_qual_name(self, name): return ".".join(self._qual_name_stack + [name]) def _get_full_name(self, name): return ".".join(self._full_name_stack + [name]) def _parse_file(self, file_path, condition): directory, filename = os.path.split(file_path) module_parts = [] if filename != "__init__.py" and filename != "__init__.pyi": module_part = os.path.splitext(filename)[0] module_parts = [module_part] module_parts = collections.deque(module_parts) while directory and condition(directory): directory, module_part = os.path.split(directory) if module_part: module_parts.appendleft(module_part) module_name = ".".join(module_parts) node = astroid.builder.AstroidBuilder().file_build(file_path, module_name) return self.parse(node) def parse_file(self, file_path): return self._parse_file( file_path, lambda directory: ( os.path.isfile(os.path.join(directory, "__init__.py")) or os.path.isfile(os.path.join(directory, "__init__.pyi")) ), ) def parse_file_in_namespace(self, file_path, dir_root): return self._parse_file( file_path, lambda directory: os.path.abspath(directory) != dir_root ) def parse_annassign(self, node): # Don't document module level assignments to class attributes if isinstance(node.target, astroid.nodes.AssignAttr): return [] return self._parse_assign(node) def parse_assign(self, node): # Don't document module level assignments to class attributes if any(isinstance(target, astroid.nodes.AssignAttr) for target in node.targets): return [] return self._parse_assign(node) def _parse_assign(self, node): doc = "" doc_node = node.next_sibling() if isinstance(doc_node, astroid.nodes.Expr) and isinstance( doc_node.value, astroid.nodes.Const ): doc = doc_node.value.value type_ = "data" if isinstance( node.scope(), astroid.nodes.ClassDef ) or _astroid_utils.is_constructor(node.scope()): type_ = "attribute" assign_value = _astroid_utils.get_assign_value(node) if not assign_value: return [] target = assign_value[0] value = assign_value[1] annotation = _astroid_utils.get_assign_annotation(node) if annotation in ("TypeAlias", "typing.TypeAlias"): value = node.value.as_string() data = { "type": type_, "name": target, "qual_name": self._get_qual_name(target), "full_name": self._get_full_name(target), "doc": _prepare_docstring(doc), "value": value, "from_line_no": node.fromlineno, "to_line_no": node.tolineno, "annotation": annotation, } return [data] def parse_classdef(self, node, data=None): type_ = "class" if _astroid_utils.is_exception(node): type_ = "exception" basenames = list(_astroid_utils.get_full_basenames(node)) data = { "type": type_, "name": node.name, "qual_name": self._get_qual_name(node.name), "full_name": self._get_full_name(node.name), "bases": basenames, "doc": _prepare_docstring(_astroid_utils.get_class_docstring(node)), "from_line_no": node.fromlineno, "to_line_no": node.tolineno, "children": [], } self._qual_name_stack.append(node.name) self._full_name_stack.append(node.name) overridden = set() overloads = {} for base in itertools.chain(iter((node,)), node.ancestors()): seen = set() if base.qname() in ( "__builtins__.object", "builtins.object", "builtins.type", ): continue for child in base.get_children(): name = getattr(child, "name", None) if isinstance(child, (astroid.Assign, astroid.AnnAssign)): assign_value = _astroid_utils.get_assign_value(child) if not assign_value: continue name = assign_value[0] if not name or name in overridden: continue seen.add(name) child_data = self.parse(child) data["children"].extend( _parse_child(node, child_data, overloads, base, name) ) overridden.update(seen) self._qual_name_stack.pop() self._full_name_stack.pop() return [data] def parse_asyncfunctiondef(self, node): return self.parse_functiondef(node) def parse_functiondef(self, node): if _astroid_utils.is_decorated_with_property_setter(node): return [] type_ = "method" properties = [] if node.type == "function": type_ = "function" if isinstance(node, astroid.AsyncFunctionDef): properties.append("async") elif _astroid_utils.is_decorated_with_property(node): type_ = "property" if node.type == "classmethod": properties.append(node.type) if node.is_abstract(pass_is_abstract=False): properties.append("abstractmethod") else: # "__new__" method is implicit classmethod if node.type in ("staticmethod", "classmethod") and node.name != "__new__": properties.append(node.type) if node.is_abstract(pass_is_abstract=False): properties.append("abstractmethod") if isinstance(node, astroid.AsyncFunctionDef): properties.append("async") data = { "type": type_, "name": node.name, "qual_name": self._get_qual_name(node.name), "full_name": self._get_full_name(node.name), "args": _astroid_utils.get_args_info(node.args), "doc": _prepare_docstring(_astroid_utils.get_func_docstring(node)), "from_line_no": node.fromlineno, "to_line_no": node.tolineno, "return_annotation": _astroid_utils.get_return_annotation(node), "properties": properties, "is_overload": _astroid_utils.is_decorated_with_overload(node), "overloads": [], } result = [data] if node.name == "__init__": for child in node.get_children(): if isinstance(child, (astroid.nodes.Assign, astroid.nodes.AnnAssign)): child_data = self._parse_assign(child) result.extend(data for data in child_data if data["doc"]) return result def _parse_local_import_from(self, node): result = [] for import_name, alias in node.names: is_wildcard = (alias or import_name) == "*" original_path = _astroid_utils.get_full_import_name( node, alias or import_name ) name = original_path if is_wildcard else (alias or import_name) qual_name = self._get_qual_name(alias or import_name) full_name = self._get_full_name(alias or import_name) data = { "type": "placeholder", "name": name, "qual_name": qual_name, "full_name": full_name, "original_path": original_path, } result.append(data) return result def parse_module(self, node): path = node.path if isinstance(node.path, list): path = node.path[0] if node.path else None type_ = "module" if node.package: type_ = "package" self._full_name_stack = [node.name] self._encoding = node.file_encoding data = { "type": type_, "name": node.name, "qual_name": node.name, "full_name": node.name, "doc": _prepare_docstring(node.doc_node.value if node.doc_node else ""), "children": [], "file_path": path, "encoding": node.file_encoding, "all": _astroid_utils.get_module_all(node), } overloads = {} top_name = node.name.split(".", 1)[0] for child in node.get_children(): if _astroid_utils.is_local_import_from(child, top_name): child_data = self._parse_local_import_from(child) else: child_data = self.parse(child) data["children"].extend(_parse_child(node, child_data, overloads)) return data def parse_typealias(self, node): doc = "" doc_node = node.next_sibling() if isinstance(doc_node, astroid.nodes.Expr) and isinstance( doc_node.value, astroid.nodes.Const ): doc = doc_node.value.value if isinstance(node.name, astroid.nodes.AssignName): name = node.name.name elif isinstance(node.name, astroid.nodes.AssignAttr): name = node.name.attrname else: return [] data = { "type": "data", "name": name, "qual_name": self._get_qual_name(name), "full_name": self._get_full_name(name), "doc": _prepare_docstring(doc), "value": node.value.as_string(), "from_line_no": node.fromlineno, "to_line_no": node.tolineno, "annotation": "TypeAlias", } return [data] def parse(self, node): data = {} node_type = node.__class__.__name__.lower() parse_func = getattr(self, "parse_" + node_type, None) if parse_func: data = parse_func(node) else: for child in node.get_children(): data = self.parse(child) if data: break return data def _parse_child(node, child_data, overloads, base=None, name=None): result = [] for single_data in child_data: if single_data["type"] in ("function", "method", "property"): if name is None: name = single_data["name"] if name in overloads: grouped = overloads[name] grouped["doc"] = single_data["doc"] if single_data["is_overload"]: grouped["overloads"].append( (single_data["args"], single_data["return_annotation"]) ) continue if single_data["is_overload"] and name not in overloads: overloads[name] = single_data if base: single_data["inherited"] = base is not node result.append(single_data) return result sphinx-autoapi-3.1.2/autoapi/directives.py000066400000000000000000000040251463504067600206570ustar00rootroot00000000000000"""AutoAPI directives""" from docutils.parsers.rst import Directive from docutils import nodes from sphinx.ext.autosummary import Autosummary, mangle_signature from sphinx.util.nodes import nested_parse_with_titles from ._objects import PythonFunction class AutoapiSummary(Autosummary): """A version of autosummary that uses static analysis.""" def get_items(self, names): items = [] env = self.state.document.settings.env all_objects = env.autoapi_all_objects max_item_chars = 60 for name in names: obj = all_objects[name] if isinstance(obj, PythonFunction): if obj.overloads: sig = "(\u2026)" else: sig = f"({obj.args})" if obj.return_annotation is not None: sig += f" \u2192 {obj.return_annotation}" else: sig = "" if sig: max_sig_chars = max(10, max_item_chars - len(obj.short_name)) sig = mangle_signature(sig, max_chars=max_sig_chars) item = (obj.short_name, sig, obj.summary, obj.id) items.append(item) return items class NestedParse(Directive): """Nested parsing to remove the first heading of included rST This is used to handle the case where we like to remove user supplied headings from module docstrings. This is required to reduce the number of duplicate headings on sections. """ has_content = True required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False def run(self): node = nodes.container() node.document = self.state.document nested_parse_with_titles(self.state, self.content, node) try: if isinstance(node[0], nodes.section) and isinstance( node[0][0], nodes.title ): node.children = node[0][1:] + node.children[1:] except IndexError: pass return node.children sphinx-autoapi-3.1.2/autoapi/documenters.py000066400000000000000000000234601463504067600210520ustar00rootroot00000000000000import re from sphinx.ext import autodoc from ._objects import ( PythonFunction, PythonClass, PythonMethod, PythonProperty, PythonData, PythonAttribute, PythonException, ) class AutoapiDocumenter(autodoc.Documenter): def get_attr(self, obj, name, *defargs): attrgetters = self.env.app.registry.autodoc_attrgettrs for type_, func in attrgetters.items(): if isinstance(obj, type_): return func(obj, name, *defargs) if name == "__doc__": return obj.docstring for child in obj.children: if child.name == name: return child if defargs: return defargs[0] raise AttributeError(name) def import_object(self): max_splits = self.fullname.count(".") for num_splits in range(max_splits, -1, -1): path_stack = list(reversed(self.fullname.rsplit(".", num_splits))) objects = self.env.autoapi_objects parent = None current = objects.get(path_stack.pop()) while current and path_stack: parent = current current = self.get_attr(current, path_stack.pop(), None) if current: self.object = current self.object_name = current.name self._method_parent = parent return True return False def get_real_modname(self): # Return a fake modname so that nothing can be imported return None def get_doc(self, encoding=None, ignore=1): return [self.object.docstring.splitlines()] def process_doc(self, docstrings): for docstring in docstrings: for line in docstring: yield line yield "" def get_object_members(self, want_all): children = ((child.name, child) for child in self.object.children) if not want_all: if not self.options.members: return False, [] children = (child for child in children if child[0] in self.options.members) elif not self.options.inherited_members: children = (child for child in children if not child[1].inherited) return False, children class _AutoapiDocstringSignatureMixin: def format_signature(self, **kwargs): # Set "manual" attributes at the last possible moment. # This is to let a manual entry or docstring searching happen first, # and falling back to the discovered signature only when necessary. if self.args is None: self.args = self.object.args if self.retann is None: self.retann = self.object.return_annotation return super().format_signature(**kwargs) class AutoapiFunctionDocumenter( AutoapiDocumenter, autodoc.FunctionDocumenter, _AutoapiDocstringSignatureMixin ): objtype = "apifunction" directivetype = "function" # Always prefer AutoapiDocumenters priority = autodoc.FunctionDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonFunction) def format_args(self, **kwargs): return "(" + self.object.args + ")" def add_directive_header(self, sig): autodoc.Documenter.add_directive_header(self, sig) if "async" in self.object.properties: sourcename = self.get_sourcename() self.add_line(" :async:", sourcename) class AutoapiDecoratorDocumenter( AutoapiFunctionDocumenter, AutoapiDocumenter, autodoc.DecoratorDocumenter ): objtype = "apidecorator" directivetype = "decorator" priority = autodoc.DecoratorDocumenter.priority * 100 + 100 def format_signature(self, **kwargs): if self.args is None: self.args = self.format_args(**kwargs) return super().format_signature(**kwargs) def format_args(self, **kwargs): to_format = self.object.args if re.match(r"func\W", to_format) or to_format == "func": if "," not in to_format: return None # We need to do better stripping here. # An annotation with a comma will mess this up. to_format = self.object.args.split(",", 1)[1] return "(" + to_format + ")" class AutoapiClassDocumenter( AutoapiDocumenter, autodoc.ClassDocumenter, _AutoapiDocstringSignatureMixin ): objtype = "apiclass" directivetype = "class" doc_as_attr = False priority = autodoc.ClassDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonClass) def format_args(self, **kwargs): return "(" + self.object.args + ")" def add_directive_header(self, sig): autodoc.Documenter.add_directive_header(self, sig) if self.options.show_inheritance: sourcename = self.get_sourcename() self.add_line("", sourcename) # TODO: Change sphinx to allow overriding of getting base names if self.object.bases: bases = ", ".join(f":class:`{base}`" for base in self.object.bases) self.add_line(f" Bases: {bases}", sourcename) class AutoapiMethodDocumenter( AutoapiDocumenter, autodoc.MethodDocumenter, _AutoapiDocstringSignatureMixin ): objtype = "apimethod" directivetype = "method" priority = autodoc.MethodDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonMethod) def format_args(self, **kwargs): return "(" + self.object.args + ")" def import_object(self): result = super().import_object() if result: self.parent = self._method_parent if "staticmethod" in self.object.properties: # document static members before ordinary ones self.member_order = self.member_order - 2 elif "classmethod" in self.object.properties: # document class members before ordinary ones but after static ones self.member_order = self.member_order - 1 return result def add_directive_header(self, sig): autodoc.Documenter.add_directive_header(self, sig) sourcename = self.get_sourcename() for property_type in ( "abstractmethod", "async", "classmethod", "staticmethod", ): if property_type in self.object.properties: self.add_line(f" :{property_type}:", sourcename) class AutoapiPropertyDocumenter(AutoapiDocumenter, autodoc.PropertyDocumenter): objtype = "apiproperty" directivetype = "property" priority = autodoc.PropertyDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonProperty) def add_directive_header(self, sig): autodoc.ClassLevelDocumenter.add_directive_header(self, sig) sourcename = self.get_sourcename() if self.options.annotation and self.options.annotation is not autodoc.SUPPRESS: self.add_line(f" :type: {self.options.annotation}", sourcename) for property_type in ( "abstractmethod", "classmethod", ): if property_type in self.object.properties: self.add_line(f" :{property_type}:", sourcename) class AutoapiDataDocumenter(AutoapiDocumenter, autodoc.DataDocumenter): objtype = "apidata" directivetype = "data" priority = autodoc.DataDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonData) def add_directive_header(self, sig): autodoc.ModuleLevelDocumenter.add_directive_header(self, sig) sourcename = self.get_sourcename() if not self.options.annotation: # TODO: Change sphinx to allow overriding of object description if self.object.value is not None: self.add_line(f" :annotation: = {self.object.value}", sourcename) elif self.options.annotation is autodoc.SUPPRESS: pass else: self.add_line(f" :annotation: {self.options.annotation}", sourcename) class AutoapiAttributeDocumenter(AutoapiDocumenter, autodoc.AttributeDocumenter): objtype = "apiattribute" directivetype = "attribute" _datadescriptor = True priority = autodoc.AttributeDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonAttribute) def add_directive_header(self, sig): autodoc.ClassLevelDocumenter.add_directive_header(self, sig) sourcename = self.get_sourcename() if not self.options.annotation: # TODO: Change sphinx to allow overriding of object description if self.object.value is not None: self.add_line(f" :annotation: = {self.object.value}", sourcename) elif self.options.annotation is autodoc.SUPPRESS: pass else: self.add_line(f" :annotation: {self.options.annotation}", sourcename) class AutoapiModuleDocumenter(AutoapiDocumenter, autodoc.ModuleDocumenter): objtype = "apimodule" directivetype = "module" priority = autodoc.ModuleDocumenter.priority * 100 + 100 class AutoapiExceptionDocumenter( AutoapiClassDocumenter, AutoapiDocumenter, autodoc.ExceptionDocumenter ): objtype = "apiexception" directivetype = "exception" priority = autodoc.ExceptionDocumenter.priority * 100 + 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonException) sphinx-autoapi-3.1.2/autoapi/extension.py000066400000000000000000000260371463504067600205410ustar00rootroot00000000000000"""Sphinx Auto-API Top-level Extension. This extension allows you to automagically generate API documentation from your project. """ import io import os import shutil from typing import Dict, Tuple import warnings import sphinx from sphinx.util.console import colorize from sphinx.addnodes import toctree from sphinx.errors import ExtensionError import sphinx.util.logging from docutils.parsers.rst import directives from . import documenters from .directives import AutoapiSummary, NestedParse from .inheritance_diagrams import AutoapiInheritanceDiagram from ._mapper import Mapper from .settings import API_ROOT LOGGER = sphinx.util.logging.getLogger(__name__) _DEFAULT_FILE_PATTERNS = ["*.py", "*.pyi"] _DEFAULT_IGNORE_PATTERNS = ["*migrations*"] _DEFAULT_OPTIONS = [ "members", "undoc-members", "private-members", "show-inheritance", "show-module-summary", "special-members", "imported-members", ] _VALID_PAGE_LEVELS = [ "module", "class", "function", "method", "attribute", ] _VIEWCODE_CACHE: Dict[str, Tuple[str, Dict]] = {} """Caches a module's parse results for use in viewcode.""" class RemovedInAutoAPI3Warning(DeprecationWarning): """Indicates something that will be removed in sphinx-autoapi v3.""" if "PYTHONWARNINGS" not in os.environ: warnings.filterwarnings("default", category=RemovedInAutoAPI3Warning) def _normalise_autoapi_dirs(autoapi_dirs, srcdir): normalised_dirs = [] if isinstance(autoapi_dirs, str): autoapi_dirs = [autoapi_dirs] for path in autoapi_dirs: if os.path.isabs(path): normalised_dirs.append(path) else: normalised_dirs.append(os.path.normpath(os.path.join(srcdir, path))) return normalised_dirs def run_autoapi(app): """Load AutoAPI data from the filesystem.""" if not app.config.autoapi_dirs: raise ExtensionError("You must configure an autoapi_dirs setting") if app.config.autoapi_include_summaries is not None: warnings.warn( "autoapi_include_summaries has been replaced by " "the show-module-summary AutoAPI option\n", RemovedInAutoAPI3Warning, ) if app.config.autoapi_include_summaries: app.config.autoapi_options.append("show-module-summary") own_page_level = app.config.autoapi_own_page_level if own_page_level not in _VALID_PAGE_LEVELS: raise ValueError(f"Invalid autoapi_own_page_level '{own_page_level}") # Make sure the paths are full normalised_dirs = _normalise_autoapi_dirs(app.config.autoapi_dirs, app.srcdir) for _dir in normalised_dirs: if not os.path.exists(_dir): raise ExtensionError( f"AutoAPI Directory `{_dir}` not found. " "Please check your `autoapi_dirs` setting." ) normalized_root = os.path.normpath( os.path.join(app.srcdir, app.config.autoapi_root) ) url_root = os.path.join("/", app.config.autoapi_root) template_dir = app.config.autoapi_template_dir if template_dir and not os.path.isabs(template_dir): if not os.path.isdir(template_dir): template_dir = os.path.join(app.srcdir, app.config.autoapi_template_dir) elif app.srcdir != os.getcwd(): warnings.warn( "autoapi_template_dir will be expected to be " "relative to the Sphinx source directory instead of " "relative to where sphinx-build is run\n", RemovedInAutoAPI3Warning, ) sphinx_mapper_obj = Mapper( app, template_dir=template_dir, dir_root=normalized_root, url_root=url_root ) if app.config.autoapi_file_patterns: file_patterns = app.config.autoapi_file_patterns else: file_patterns = _DEFAULT_FILE_PATTERNS if app.config.autoapi_ignore: ignore_patterns = app.config.autoapi_ignore else: ignore_patterns = _DEFAULT_IGNORE_PATTERNS if ".rst" in app.config.source_suffix: out_suffix = ".rst" elif ".txt" in app.config.source_suffix: out_suffix = ".txt" else: # Fallback to first suffix listed out_suffix = next(iter(app.config.source_suffix)) if sphinx_mapper_obj.load( patterns=file_patterns, dirs=normalised_dirs, ignore=ignore_patterns ): sphinx_mapper_obj.map(options=app.config.autoapi_options) if app.config.autoapi_generate_api_docs: sphinx_mapper_obj.output_rst(source_suffix=out_suffix) # This function cannot be pickled into the Sphinx cache, so clear it. # We won't need access to it again until a full rebuild is done anyway. app.config.autoapi_prepare_jinja_env = None def build_finished(app, exception): if not app.config.autoapi_keep_files and app.config.autoapi_generate_api_docs: normalized_root = os.path.normpath( os.path.join(app.srcdir, app.config.autoapi_root) ) if app.verbosity > 1: LOGGER.info( colorize("bold", "[AutoAPI] ") + colorize("darkgreen", "Cleaning generated .rst files") ) shutil.rmtree(normalized_root) def source_read(app, docname, source): # temp_data is cleared after each source file has been processed, # so populate the annotations at the beginning of every file read. app.env.temp_data["annotations"] = getattr(app.env, "autoapi_annotations", {}) def doctree_read(app, doctree): """Inject AutoAPI into the TOC Tree dynamically.""" if app.env.docname == "index": all_docs = set() insert = True nodes = list(doctree.findall(toctree)) toc_entry = f"{app.config.autoapi_root}/index" add_entry = ( nodes and app.config.autoapi_generate_api_docs and app.config.autoapi_add_toctree_entry ) if not add_entry: return # Capture all existing toctree entries for node in nodes: for entry in node["entries"]: all_docs.add(entry[1]) # Don't insert autoapi it's already present for doc in all_docs: if doc.find(app.config.autoapi_root) != -1: insert = False if insert and app.config.autoapi_add_toctree_entry: # Insert AutoAPI index nodes[-1]["entries"].append((None, toc_entry)) nodes[-1]["includefiles"].append(toc_entry) message_prefix = colorize("bold", "[AutoAPI] ") message = colorize( "darkgreen", f"Adding AutoAPI TOCTree [{toc_entry}] to index.rst" ) LOGGER.info(message_prefix + message) def viewcode_find(app, modname): objects = app.env.autoapi_objects if modname not in objects: return None if modname in _VIEWCODE_CACHE: return _VIEWCODE_CACHE[modname] locations = {} module = objects[modname] for child in module.children: stack = [("", child)] while stack: prefix, obj = stack.pop() type_ = "other" if obj.type == "class": type_ = "class" elif obj.type in ("function", "method"): type_ = "def" full_name = prefix + obj.name if "from_line_no" in obj.obj: locations[full_name] = ( type_, obj.obj["from_line_no"], obj.obj["to_line_no"], ) children = getattr(obj, "children", ()) stack.extend((full_name + ".", gchild) for gchild in children) if module.obj["encoding"]: stream = io.open(module.obj["file_path"], encoding=module.obj["encoding"]) else: stream = open(module.obj["file_path"], encoding="utf-8") with stream as in_f: source = in_f.read() result = (source, locations) _VIEWCODE_CACHE[modname] = result return result def viewcode_follow_imported(app, modname, attribute): fullname = f"{modname}.{attribute}" all_objects = app.env.autoapi_all_objects if fullname not in all_objects: return None if all_objects[fullname].obj.get("type") == "method": fullname = fullname[: fullname.rfind(".")] attribute = attribute[: attribute.rfind(".")] while all_objects[fullname].obj.get("original_path", "") != "": fullname = all_objects[fullname].obj.get("original_path") orig_path = fullname if orig_path.endswith(attribute): return orig_path[: -len(attribute) - 1] return modname def setup(app): app.connect("builder-inited", run_autoapi) app.connect("source-read", source_read) # Use a lower priority than the default to ensure that we can # inject into the toctree before Sphinx tries to use it # in another doctree-read transformer. app.connect("doctree-read", doctree_read, priority=400) app.connect("build-finished", build_finished) if "viewcode-find-source" in app.events.events: app.connect("viewcode-find-source", viewcode_find) if "viewcode-follow-imported" in app.events.events: app.connect("viewcode-follow-imported", viewcode_follow_imported) app.add_config_value("autoapi_root", API_ROOT, "html") app.add_config_value("autoapi_ignore", [], "html") app.add_config_value("autoapi_options", _DEFAULT_OPTIONS, "html") app.add_config_value("autoapi_member_order", "bysource", "html") app.add_config_value("autoapi_file_patterns", None, "html") app.add_config_value("autoapi_dirs", [], "html") app.add_config_value("autoapi_keep_files", False, "html") app.add_config_value("autoapi_add_toctree_entry", True, "html") app.add_config_value("autoapi_template_dir", None, "html") app.add_config_value("autoapi_include_summaries", None, "html") app.add_config_value("autoapi_python_use_implicit_namespaces", False, "html") app.add_config_value("autoapi_python_class_content", "class", "html") app.add_config_value("autoapi_generate_api_docs", True, "html") app.add_config_value("autoapi_prepare_jinja_env", None, "html") app.add_config_value("autoapi_own_page_level", "module", "html") app.add_autodocumenter(documenters.AutoapiFunctionDocumenter) app.add_autodocumenter(documenters.AutoapiPropertyDocumenter) app.add_autodocumenter(documenters.AutoapiDecoratorDocumenter) app.add_autodocumenter(documenters.AutoapiClassDocumenter) app.add_autodocumenter(documenters.AutoapiMethodDocumenter) app.add_autodocumenter(documenters.AutoapiDataDocumenter) app.add_autodocumenter(documenters.AutoapiAttributeDocumenter) app.add_autodocumenter(documenters.AutoapiModuleDocumenter) app.add_autodocumenter(documenters.AutoapiExceptionDocumenter) directives.register_directive("autoapi-nested-parse", NestedParse) directives.register_directive("autoapisummary", AutoapiSummary) app.setup_extension("sphinx.ext.autosummary") app.add_event("autoapi-skip-member") app.setup_extension("sphinx.ext.inheritance_diagram") app.add_directive("autoapi-inheritance-diagram", AutoapiInheritanceDiagram) return { "parallel_read_safe": True, "parallel_write_safe": True, } sphinx-autoapi-3.1.2/autoapi/inheritance_diagrams.py000066400000000000000000000103131463504067600226530ustar00rootroot00000000000000import astroid import sphinx.ext.inheritance_diagram def _do_import_class(name, currmodule=None): path_stack = list(reversed(name.split("."))) if not currmodule: currmodule = path_stack.pop() try: target = astroid.MANAGER.ast_from_module_name(currmodule) while target and path_stack: path_part = path_stack.pop() target = (target.getattr(path_part) or (None,))[0] while isinstance(target, (astroid.ImportFrom, astroid.Import)): try: target = target.do_import_module(path_part) except astroid.AstroidImportError: target = target.do_import_module() target = (target.getattr(path_part) or (None,))[0] break except astroid.AstroidError: target = None return target def _import_class(name, currmodule): target = None if currmodule: target = _do_import_class(name, currmodule) if target is None: target = _do_import_class(name) if not target: raise sphinx.ext.inheritance_diagram.InheritanceException( f"Could not import class or module {name} specified for inheritance diagram" ) if isinstance(target, astroid.ClassDef): return [target] if isinstance(target, astroid.Module): classes = [] for child in target.get_children(): if isinstance(child, astroid.ClassDef): classes.append(child) return classes raise sphinx.ext.inheritance_diagram.InheritanceException( f"{name} specified for inheritance diagram is not a class or module" ) class _AutoapiInheritanceGraph(sphinx.ext.inheritance_diagram.InheritanceGraph): @staticmethod def _import_classes(class_names, currmodule): classes = [] for name in class_names: classes.extend(_import_class(name, currmodule)) return classes def _class_info( self, classes, show_builtins, private_bases, parts, aliases, top_classes ): all_classes = {} def recurse(cls): if cls in all_classes: return if not show_builtins and cls.root().name == "builtins": return if not private_bases and cls.name.startswith("_"): return nodename = self.class_name(cls, parts, aliases) fullname = self.class_name(cls, 0, aliases) tooltip = None if cls.doc_node: doc = cls.doc_node.value.strip().split("\n")[0] if doc: tooltip = '"%s"' % doc.replace('"', '\\"') baselist = [] all_classes[cls] = (nodename, fullname, baselist, tooltip or "") if fullname in top_classes: return for base in cls.ancestors(recurs=False): if not show_builtins and base.root().name == "builtins": continue if not private_bases and base.name.startswith("_"): continue baselist.append(self.class_name(base, parts, aliases)) if base not in all_classes: recurse(base) for cls in classes: recurse(cls) return list(all_classes.values()) @staticmethod def class_name(node, parts=0, aliases=None): fullname = node.qname() if fullname.startswith(("__builtin__.", "builtins")): fullname = fullname.split(".", 1)[-1] if parts == 0: result = fullname else: name_parts = fullname.split(".") result = ".".join(name_parts[-parts:]) if aliases is not None and result in aliases: return aliases[result] return result class AutoapiInheritanceDiagram(sphinx.ext.inheritance_diagram.InheritanceDiagram): def run(self): # Yucky! Monkeypatch InheritanceGraph to use our own old_graph = sphinx.ext.inheritance_diagram.InheritanceGraph sphinx.ext.inheritance_diagram.InheritanceGraph = _AutoapiInheritanceGraph try: return super().run() finally: sphinx.ext.inheritance_diagram.InheritanceGraph = old_graph sphinx-autoapi-3.1.2/autoapi/settings.py000066400000000000000000000005721463504067600203610ustar00rootroot00000000000000"""Basic settings for AutoAPI projects. You shouldn't need to touch this. """ import os SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) TEMPLATE_DIR = os.path.join(SITE_ROOT, "templates") API_ROOT = "autoapi" OWN_PAGE_LEVELS = [ "package", "module", "exception", "class", "function", "method", "property", "data", "attribute", ] sphinx-autoapi-3.1.2/autoapi/templates/000077500000000000000000000000001463504067600201415ustar00rootroot00000000000000sphinx-autoapi-3.1.2/autoapi/templates/index.rst000066400000000000000000000005031463504067600220000ustar00rootroot00000000000000API Reference ============= This page contains auto-generated API reference documentation [#f1]_. .. toctree:: :titlesonly: {% for page in pages|selectattr("is_top_level_object") %} {{ page.include_path }} {% endfor %} .. [#f1] Created with `sphinx-autoapi `_ sphinx-autoapi-3.1.2/autoapi/templates/python/000077500000000000000000000000001463504067600214625ustar00rootroot00000000000000sphinx-autoapi-3.1.2/autoapi/templates/python/attribute.rst000066400000000000000000000000401463504067600242110ustar00rootroot00000000000000{% extends "python/data.rst" %} sphinx-autoapi-3.1.2/autoapi/templates/python/class.rst000066400000000000000000000052311463504067600233220ustar00rootroot00000000000000{% if obj.display %} {% if is_own_page %} {{ obj.id }} {{ "=" * obj.id | length }} {% endif %} {% set visible_children = obj.children|selectattr("display")|list %} {% set own_page_children = visible_children|selectattr("type", "in", own_page_types)|list %} {% if is_own_page and own_page_children %} .. toctree:: :hidden: {% for child in own_page_children %} {{ child.include_path }} {% endfor %} {% endif %} .. py:{{ obj.type }}:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}{% if obj.args %}({{ obj.args }}){% endif %} {% for (args, return_annotation) in obj.overloads %} {{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %} {% endfor %} {% if obj.bases %} {% if "show-inheritance" in autoapi_options %} Bases: {% for base in obj.bases %}{{ base|link_objs }}{% if not loop.last %}, {% endif %}{% endfor %} {% endif %} {% if "show-inheritance-diagram" in autoapi_options and obj.bases != ["object"] %} .. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }} :parts: 1 {% if "private-members" in autoapi_options %} :private-bases: {% endif %} {% endif %} {% endif %} {% if obj.docstring %} {{ obj.docstring|indent(3) }} {% endif %} {% for obj_item in visible_children %} {% if obj_item.type not in own_page_types %} {{ obj_item.render()|indent(3) }} {% endif %} {% endfor %} {% if is_own_page and own_page_children %} {% set visible_attributes = own_page_children|selectattr("type", "equalto", "attribute")|list %} {% if visible_attributes %} Attributes ---------- .. autoapisummary:: {% for attribute in visible_attributes %} {{ attribute.id }} {% endfor %} {% endif %} {% set visible_exceptions = own_page_children|selectattr("type", "equalto", "exception")|list %} {% if visible_exceptions %} Exceptions ---------- .. autoapisummary:: {% for exception in visible_exceptions %} {{ exception.id }} {% endfor %} {% endif %} {% set visible_classes = own_page_children|selectattr("type", "equalto", "class")|list %} {% if visible_classes %} Classes ------- .. autoapisummary:: {% for klass in visible_classes %} {{ klass.id }} {% endfor %} {% endif %} {% set visible_methods = own_page_children|selectattr("type", "equalto", "method")|list %} {% if visible_methods %} Methods ------- .. autoapisummary:: {% for method in visible_methods %} {{ method.id }} {% endfor %} {% endif %} {% endif %} {% endif %} sphinx-autoapi-3.1.2/autoapi/templates/python/data.rst000066400000000000000000000014161463504067600231270ustar00rootroot00000000000000{% if obj.display %} {% if is_own_page %} {{ obj.id }} {{ "=" * obj.id | length }} {% endif %} .. py:{{ obj.type }}:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.name }}{% endif %} {% if obj.annotation is not none %} :type: {% if obj.annotation %} {{ obj.annotation }}{% endif %} {% endif %} {% if obj.value is not none %} {% if obj.value.splitlines()|count > 1 %} :value: Multiline-String .. raw:: html
Show Value .. code-block:: python {{ obj.value|indent(width=6,blank=true) }} .. raw:: html
{% else %} :value: {{ obj.value|truncate(100) }} {% endif %} {% endif %} {% if obj.docstring %} {{ obj.docstring|indent(3) }} {% endif %} {% endif %} sphinx-autoapi-3.1.2/autoapi/templates/python/exception.rst000066400000000000000000000000411463504067600242050ustar00rootroot00000000000000{% extends "python/class.rst" %} sphinx-autoapi-3.1.2/autoapi/templates/python/function.rst000066400000000000000000000013071463504067600240420ustar00rootroot00000000000000{% if obj.display %} {% if is_own_page %} {{ obj.id }} {{ "=" * obj.id | length }} {% endif %} .. py:function:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %} {% for (args, return_annotation) in obj.overloads %} {%+ if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %} {% endfor %} {% for property in obj.properties %} :{{ property }}: {% endfor %} {% if obj.docstring %} {{ obj.docstring|indent(3) }} {% endif %} {% endif %} sphinx-autoapi-3.1.2/autoapi/templates/python/method.rst000066400000000000000000000013031463504067600234710ustar00rootroot00000000000000{% if obj.display %} {% if is_own_page %} {{ obj.id }} {{ "=" * obj.id | length }} {% endif %} .. py:method:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %} {% for (args, return_annotation) in obj.overloads %} {%+ if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %} {% endfor %} {% for property in obj.properties %} :{{ property }}: {% endfor %} {% if obj.docstring %} {{ obj.docstring|indent(3) }} {% endif %} {% endif %} sphinx-autoapi-3.1.2/autoapi/templates/python/module.rst000066400000000000000000000106261463504067600235060ustar00rootroot00000000000000{% if obj.display %} {% if is_own_page %} {{ obj.id }} {{ "=" * obj.id|length }} .. py:module:: {{ obj.name }} {% if obj.docstring %} .. autoapi-nested-parse:: {{ obj.docstring|indent(3) }} {% endif %} {% block subpackages %} {% set visible_subpackages = obj.subpackages|selectattr("display")|list %} {% if visible_subpackages %} Subpackages ----------- .. toctree:: :maxdepth: 1 {% for subpackage in visible_subpackages %} {{ subpackage.include_path }} {% endfor %} {% endif %} {% endblock %} {% block submodules %} {% set visible_submodules = obj.submodules|selectattr("display")|list %} {% if visible_submodules %} Submodules ---------- .. toctree:: :maxdepth: 1 {% for submodule in visible_submodules %} {{ submodule.include_path }} {% endfor %} {% endif %} {% endblock %} {% block content %} {% set visible_children = obj.children|selectattr("display")|list %} {% if visible_children %} {% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %} {% if visible_attributes %} {% if "attribute" in own_page_types or "show-module-summary" in autoapi_options %} Attributes ---------- {% if "attribute" in own_page_types %} .. toctree:: :hidden: {% for attribute in visible_attributes %} {{ attribute.include_path }} {% endfor %} {% endif %} .. autoapisummary:: {% for attribute in visible_attributes %} {{ attribute.id }} {% endfor %} {% endif %} {% endif %} {% set visible_exceptions = visible_children|selectattr("type", "equalto", "exception")|list %} {% if visible_exceptions %} {% if "exception" in own_page_types or "show-module-summary" in autoapi_options %} Exceptions ---------- {% if "exception" in own_page_types %} .. toctree:: :hidden: {% for exception in visible_exceptions %} {{ exception.include_path }} {% endfor %} {% endif %} .. autoapisummary:: {% for exception in visible_exceptions %} {{ exception.id }} {% endfor %} {% endif %} {% endif %} {% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %} {% if visible_classes %} {% if "class" in own_page_types or "show-module-summary" in autoapi_options %} Classes ------- {% if "class" in own_page_types %} .. toctree:: :hidden: {% for klass in visible_classes %} {{ klass.include_path }} {% endfor %} {% endif %} .. autoapisummary:: {% for klass in visible_classes %} {{ klass.id }} {% endfor %} {% endif %} {% endif %} {% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %} {% if visible_functions %} {% if "function" in own_page_types or "show-module-summary" in autoapi_options %} Functions --------- {% if "function" in own_page_types %} .. toctree:: :hidden: {% for function in visible_functions %} {{ function.include_path }} {% endfor %} {% endif %} .. autoapisummary:: {% for function in visible_functions %} {{ function.id }} {% endfor %} {% endif %} {% endif %} {% set this_page_children = visible_children|rejectattr("type", "in", own_page_types)|list %} {% if this_page_children %} {{ obj.type|title }} Contents {{ "-" * obj.type|length }}--------- {% for obj_item in this_page_children %} {{ obj_item.render()|indent(0) }} {% endfor %} {% endif %} {% endif %} {% endblock %} {% else %} .. py:module:: {{ obj.name }} {% if obj.docstring %} .. autoapi-nested-parse:: {{ obj.docstring|indent(6) }} {% endif %} {% for obj_item in visible_children %} {{ obj_item.render()|indent(3) }} {% endfor %} {% endif %} {% endif %} sphinx-autoapi-3.1.2/autoapi/templates/python/package.rst000066400000000000000000000000421463504067600236030ustar00rootroot00000000000000{% extends "python/module.rst" %} sphinx-autoapi-3.1.2/autoapi/templates/python/property.rst000066400000000000000000000006561463504067600241070ustar00rootroot00000000000000{% if obj.display %} {% if is_own_page %} {{ obj.id }} {{ "=" * obj.id | length }} {% endif %} .. py:property:: {% if is_own_page %}{{ obj.id}}{% else %}{{ obj.short_name }}{% endif %} {% if obj.annotation %} :type: {{ obj.annotation }} {% endif %} {% for property in obj.properties %} :{{ property }}: {% endfor %} {% if obj.docstring %} {{ obj.docstring|indent(3) }} {% endif %} {% endif %} sphinx-autoapi-3.1.2/docs/000077500000000000000000000000001463504067600154315ustar00rootroot00000000000000sphinx-autoapi-3.1.2/docs/_static/000077500000000000000000000000001463504067600170575ustar00rootroot00000000000000sphinx-autoapi-3.1.2/docs/_static/overrides.css000066400000000000000000000001021463504067600215640ustar00rootroot00000000000000.sd-card-title { margin-bottom: 1em; text-align: center; }sphinx-autoapi-3.1.2/docs/changes/000077500000000000000000000000001463504067600170415ustar00rootroot00000000000000sphinx-autoapi-3.1.2/docs/changes/.gitkeep000066400000000000000000000000001463504067600204600ustar00rootroot00000000000000sphinx-autoapi-3.1.2/docs/conf.py000066400000000000000000000052531463504067600167350ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html import re from sphinx import addnodes from sphinx.util.docfields import TypedField import autoapi # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'Sphinx AutoAPI' copyright = '2023, Read the Docs' author = 'Read the Docs' version = ".".join(str(x) for x in autoapi.__version_info__[:2]) release = autoapi.__version__ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ 'autoapi.extension', 'sphinx.ext.intersphinx', 'sphinx.ext.napoleon', 'sphinx_design', ] templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'changes/*.rst'] # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = 'furo' html_static_path = ['_static'] html_css_files = ['overrides.css'] # -- Options for AutoAPI extension ------------------------------------------- autoapi_dirs = ['../autoapi'] autoapi_generate_api_docs = False # -- Options for intersphinx extension --------------------------------------- intersphinx_mapping = { 'jinja': ('https://jinja.palletsprojects.com/en/3.0.x/', None), 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), 'python': ('https://docs.python.org/3/', None), } # -- Enable confval and event roles ------------------------------------------ event_sig_re = re.compile(r'([a-zA-Z-]+)\s*\((.*)\)') def parse_event(env, sig, signode): m = event_sig_re.match(sig) if not m: signode += addnodes.desc_name(sig, sig) return sig name, args = m.groups() signode += addnodes.desc_name(name, name) plist = addnodes.desc_parameterlist() for arg in args.split(','): arg = arg.strip() plist += addnodes.desc_parameter(arg, arg) signode += plist return name def setup(app): app.add_object_type('confval', 'confval', objname='configuration value', indextemplate='pair: %s; configuration value') fdesc = TypedField('parameter', label='Parameters', names=('param',), typenames=('type',), can_collapse=True) app.add_object_type('event', 'event', 'pair: %s; event', parse_event, doc_field_types=[fdesc]) sphinx-autoapi-3.1.2/docs/how_to.rst000066400000000000000000000201171463504067600174630ustar00rootroot00000000000000How-to Guides ============= These guides will take you through the steps to perform common actions or solve common problems in AutoAPI. They will assume that you already have a Sphinx project with AutoAPI set up already. If you don't know how to do this then read the :doc:`tutorials`. .. _customise-documented-api: How to Customise What Gets Documented ------------------------------------- With the default settings, AutoAPI will document everything that is publicly accessible through the actual package when loaded in Python. For example if a function is imported from a submodule into a package then that function is documented in both the submodule and the package. .. note:: The one exception to this rule is that any object imported into a module is not documented by default. However there are multiple options available for controlling what AutoAPI will document. Set ``__all__`` ^^^^^^^^^^^^^^^ AutoAPI treats the definition of `__all__ `_ as the specification of what objects are public in a module or package, and which aren't. In the following example, only ``func_a()`` and ``A`` would be documented. .. code-block:: python # mypackage/__init__.py from . import B __all__ = ("A", "func_a") class A: ... def func_a(): ... def func_b(): ... Configure :confval:`autoapi_options` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The :confval:`autoapi_options` configuration value gives some high level control over what is documented. For example you could remove ``private-members`` from :confval:`autoapi_options` and hide your object definitions in private modules. .. code-block:: python # package/__init__.py from ._submodule import public_function # package/_submodule.py def public_function(): """This public function will be documented only in ``package``.""" ... def private_function() """This private function won't be documented.""" ... As another example, you could remove ``undoc-members`` from :confval:`autoapi_options` and only add docstrings for the modules and other entities that you want to be documented. See :confval:`autoapi_options` for more information on how to use this option. Connect to the :event:`autoapi-skip-member` event ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The :event:`autoapi-skip-member` event is emitted whenever a template has to decide whether a member should be included in the documentation. For example, to document only packages -- in other words, to not document submodules -- you could implement an event handler in your conf.py like the following. .. code-block:: python def skip_submodules(app, what, name, obj, skip, options): if what == "module": skip = True return skip def setup(sphinx): sphinx.connect("autoapi-skip-member", skip_submodules) Customise the API Documentation Templates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Finally, you can configure what gets rendered by customising the templates. This is a rather heavy handed approach, so it should only be necessary when the other options do not give you the control the you need. You can learn how to customise the templates in the next section; :ref:`customise-templates`. .. _customise-templates: How to Customise Layout Through Templates ----------------------------------------- .. warning:: Templates control a lot of behaviour, so customising templates can mean that you lose out on new functionality until you update your customised templates after a new release of AutoAPI. You can customise the look of the documentation that AutoAPI generates by changing the Jinja2 templates that it uses. The default templates live in the ``autoapi/templates`` directory of the AutoAPI package. Simply copy whichever templates you want to customise to a local directory and edit them. To get AutoAPI to use these templates, point the :confval:`autoapi_template_dir` configuration option to your directory. It can be absolute, or relative to the root of the documentation source directory (ie the directory passed to ``sphinx-build``). .. code-block:: python autoapi_template_dir = '_autoapi_templates' Your template directory must to follow the same layout as the default templates. For example, to override the Python class and module templates: .. code-block:: none _autoapi_templates └── python ├── class.rst └── module.rst .. _customise-index-page: How to Customise the Index Page ------------------------------- The index page that AutoAPI creates is generated using a template. So customising the index page follows the same steps as customising a template. Simply edit the ``autoapi/templates/index.rst`` template with the same steps as :ref:`customising a template `. How to Remove the Index Page ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To remove the index page altogether, turn off the :confval:`autoapi_add_toctree_entry` configuration option:: autoapi_add_toctree_entry = False You will then need to include the generated documentation in the toctree yourself. For example if you were generating documentation for a package called "example", you would add the following toctree entry:: .. toctree:: autoapi/example/index Note that ``autoapi/`` is the default location of documentation, as configured by :confval:`autoapi_root`. If you change :confval:`autoapi_root`, then the entry that you need to add would change also. How to Configure Where Documentation Appears in the TOC Tree ------------------------------------------------------------ The :confval:`autoapi_root` configuration option defines where generated documentation is output. To change where documentation is output, simply change this option to another directory relative to the documentation source directory: .. code-block:: python autoapi_root = 'technical/api' How to Transition to Autodoc-Style Documentation ---------------------------------------------------- Once you have written some documentation with the :ref:`autodoc-directives`, turning the automatic documentation generation off is as easy as disabling the :confval:`autoapi_generate_api_docs` configuration option:: autoapi_generate_api_docs = False How to Transition to Manual Documentation ----------------------------------------- To start writing API documentation yourself, you can get AutoAPI to keep its generated files around as a base to start from using the :confval:`autoapi_keep_files` option:: autoapi_keep_files = True Once you have built your documentation with this option turned on, you can disable AutoAPI altogether from your project. How to Include Type Annotations as Types in Rendered Docstrings --------------------------------------------------------------- .. warning:: This feature is experimental and may change or be removed in future versions. Since v3.0, :mod:`sphinx` has included an :mod:`sphinx.ext.autodoc.typehints` extension that is capable of rendering type annotations as parameter types and return types. For example the following function: .. code-block:: def _func(a: int, b: Optional[str]) -> bool """My function. :param a: The first arg. :param b: The second arg. :returns: Something. """ would be rendered as: .. py:function:: _func(a, b) :noindex: :param int a: The first arg. :param b: The second arg. :type b: Optional[str] :returns: Something. :rtype: bool AutoAPI is capable of the same thing. To enable this behaviour, load the :mod:`sphinx.ext.autodoc.typehints` (or :mod:`sphinx.ext.autodoc`) extension in Sphinx's ``conf.py`` file and set :confval:`autodoc_typehints` to ``description`` as normal:: extensions = ['sphinx.ext.autodoc', 'autoapi.extension'] autodoc_typehints = 'description' .. note:: Unless :confval:`autodoc_typehints` is set to ``none``, the type annotations of overloads will always be output in the signature and never merged into the description because it is impossible to represent all overloads as a list of parameters. sphinx-autoapi-3.1.2/docs/index.rst000066400000000000000000000034541463504067600173000ustar00rootroot00000000000000Sphinx AutoAPI ============== Sphinx AutoAPI is a Sphinx extension for generating complete API documentation without needing to load, run, or import the project being documented. In contrast to the traditional `Sphinx autodoc `_, which requires some manual authoring and uses code imports, AutoAPI finds and generates documentation by parsing source code. .. grid:: 2 :gutter: 3 .. grid-item-card:: Beginner Guides New to AutoAPI? Check out the Tutorials. They are a hands-on introduction for beginners. +++ .. button-ref:: tutorials :expand: :color: secondary :click-parent: To the beginner guides .. grid-item-card:: User Guides The user guides are recipes for key tasks and common problems. +++ .. button-ref:: how_to :expand: :color: secondary :click-parent: To the user guides .. grid-item-card:: Reference Guide The reference guide contains a detailed description of the configuration options, directives, and templates included in AutoAPI. +++ .. button-ref:: reference/index :expand: :color: secondary :click-parent: To the reference guide .. grid-item-card:: Contributor Guides Want to add to the codebase? The contributing guidelines will guide you through the process of improving AutoAPI. +++ .. button-ref:: maintenance/index :expand: :color: secondary :click-parent: To the contributor guides .. toctree:: :hidden: tutorials how_to reference/index maintenance/index sphinx-autoapi-3.1.2/docs/maintenance/000077500000000000000000000000001463504067600177135ustar00rootroot00000000000000sphinx-autoapi-3.1.2/docs/maintenance/design.rst000066400000000000000000000102371463504067600217210ustar00rootroot00000000000000Design Reference ================ Python ------ When choosing what to document, AutoAPI aims to document anything that is publicly accessible through the actual package when loaded in Python. For example if a function is imported from a submodule into a package, that function is documented in both the submodule and the package. There are some exceptions to this rule: * Anything that is imported into a module is not documented. Usually a module is where implementations exist. Therefore an import of something is usually for the usage of the implementation, and not as something to be accessed publicly. * When the module or package defines an ``__all__``, only the members named in ``__all__`` are documented. * When a configuration option indicates that private or special members should also be documented. Furthermore, AutoAPI follows the same docstring inheritance rules as :func:`inspect.getdoc`, with some exceptions: * The docstrings of the following methods are not inherited because they are usually redundant: * :meth:`object.__init__` * :meth:`object.__new__` * :meth:`type.__init__` * :meth:`type.__new__` Introduction ------------ We are working with Sphinx, which has an existing way of doing this. Generally, you define a `Domain` which describes the various language structure, a *Class* or *Method*, for example. Then the user will write RST that uses these definitions, and Sphinx will create output from that markup. .. code-block:: rst .. py:function:: spam(eggs) Spam the foo. The author of the documentation will have now told Sphinx that the *spam* function exists in the Python project that is being documented. Autogenerated Output ~~~~~~~~~~~~~~~~~~~~ Sphinx then built a series of tools to make the generation of this markup easier and more automatic: * `Autodoc `_ * `Autosummary `_ Autodoc is a Python-only solution that imports the author's code into memory, and then allows the author to more automatically document full objects. For example, you can document a whole class on a page. .. code-block:: py .. autoclass:: Noodle This will generate output that looks like: .. class:: Noodle :noindex: Noodle's docstring. There are also options for it to include a full listing of the classes attributes, methods, and other things, automatically. .. warning:: Remember, this depends on ``Noodle`` being importable by the Python interpreter running Sphinx. Proposed Architecture --------------------- The proposed architecture for this project is as follows: * A parser will read the source files into an internal representation of the objects that can be documented. * Take the internal representation and generate in-memory rst that corresponds to the Sphinx domain objects. * Sphinx will output HTML based on the doctree generated from the in-memory rst. In diagram form:: Code -> Internal Objects -> RST -> Sphinx -> HTML File Structure vs. Hierarchy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Specific ID's should have one specific detail representation. This means that every internal object should only have one place that it is rendered with a ``.. :::`` canonical identifier. All other places it is referenced should be in either: * A reference * A toctree (listing) Sphinx Implementation ~~~~~~~~~~~~~~~~~~~~~ The user will run a normal `make html` as part of the experience. The generation and loading will be done as an extension that can be configured. There will be Sphinx configuration for how things get built: .. code-block:: rst autoapi_root = 'api' # Where HTML is generated autoapi_dirs = ['yaml'] # Directory of YAML sources We will then loop over all source files in the ``autoapi_dir`` and parse them. They will then be output into ``autoapi_root`` inside the documentation. Examples -------- A nice example of Sphinx Python output similar to what we want: * http://dta.googlecode.com/git/doc/_build/html/index.html * Src: https://raw.githubusercontent.com/sfcta/dta/master/doc/index.rst An example domain for Spec: * https://subversion.xray.aps.anl.gov/bcdaext/specdomain/trunk/src/specdomain/sphinxcontrib/specdomain.pysphinx-autoapi-3.1.2/docs/maintenance/index.rst000066400000000000000000000001541463504067600215540ustar00rootroot00000000000000#################### Contributor's Guides #################### .. toctree:: design release-processsphinx-autoapi-3.1.2/docs/maintenance/release-process.rst000066400000000000000000000014161463504067600235430ustar00rootroot00000000000000Release Process =============== This page documents the steps to be taken to release a new version of Sphinx AutoAPI. Pre-Checks ---------- 1. Check that the dependencies of the package are correct. 2. Clean the ``.tox`` directory and run the tests. 3. Commit and push any changes needed to make the tests pass. 4. Check that the tests passed on github. Preparation ----------- 1. Update the version numbers in ``autoapi/__init__.py``. 2. Run ``tox -e release_notes -- build`` 3. Commit and push the changes. 4. Check that the tests passed on github. Release ------- Create a new release in github that tags the commit and uses the built release notes as the description. The tag created by the release will trigger the github actions to build and upload the package to PyPI.sphinx-autoapi-3.1.2/docs/reference/000077500000000000000000000000001463504067600173675ustar00rootroot00000000000000sphinx-autoapi-3.1.2/docs/reference/config.rst000066400000000000000000000223771463504067600214010ustar00rootroot00000000000000Configuration Options ===================== .. confval:: autoapi_dirs **Required** Paths (relative or absolute) to the source code that you wish to generate your API documentation from. The paths are searched recursively for files matching :confval:`autoapi_file_patterns`. Relative paths should be relative to the source directory of your documentation. For Python, if a package directory is specified, the package directory itself will be included in the relative path of the children. If an ordinary directory is specified, that directory will not be included in the relative path. .. confval:: autoapi_template_dir Default: ``''`` A directory that has user-defined templates to override our default templates. The path can either be absolute, or relative to the source directory of your documentation files. An path relative to where `sphinx-build` is run is allowed for backwards compatibility only and will be removed in a future version. You can view the default templates in the `autoapi/templates `_ directory of the package. .. confval:: autoapi_file_patterns Default: ``['*.py', '*.pyi']`` A list containing the file patterns to look for when generating documentation. Patterns should be listed in order of preference. For example, if ``autoapi_file_patterns`` is set to the default value and a `.py` file and a `.pyi` file are found, then the `.py` will be read. .. confval:: autoapi_generate_api_docs Default: ``True`` Whether to generate API documentation. If this is ``False``, documentation should be generated though the :doc:`directives`. Customisation Options --------------------- .. confval:: autoapi_options Default: [ ``'members'``, ``'undoc-members'``, ``'private-members'``, ``'show-inheritance'``, ``'show-module-summary'``, ``'special-members'``, ``'imported-members'``, ] Options for display of the generated documentation. * ``members``: Display children of an object * ``inherited-members``: Display children of an object that have been inherited from a base class. * ``undoc-members``: Display objects that have no docstring * ``private-members``: Display private objects (eg. ``_foo`` in Python) * ``special-members``: Display special objects (eg. ``__foo__`` in Python) * ``show-inheritance``: Display a list of base classes below the class signature. * ``show-inheritance-diagram``: Display an inheritance diagram in generated class documentation. It makes use of the :mod:`sphinx.ext.inheritance_diagram` extension, and requires `Graphviz `_ to be installed. * ``show-module-summary``: Whether to include autosummary directives in generated module documentation. * ``imported-members``: For objects imported into a package, display objects imported from the same top level package or module. This option does not effect objects imported into a module. .. confval:: autoapi_ignore Default: ``['*migrations*']`` A list of patterns to ignore when finding files. .. confval:: autoapi_root Default: ``autoapi`` Path to output the generated AutoAPI files into, including the generated index page. This path must be relative to the source directory of your documentation files. This can be used to place the generated documentation anywhere in your documentation hierarchy. .. confval:: autoapi_add_toctree_entry Default: ``True`` Whether to insert the generated documentation into the TOC tree. If this is ``False``, the default AutoAPI index page is not generated and you will need to include the generated documentation in a TOC tree entry yourself. .. confval:: autoapi_python_class_content Default: ``class`` Which docstring to insert into the content of a class. * ``class``: Use only the class docstring. * ``both``: Use the concatenation of the class docstring and the ``__init__`` docstring. * ``init``: Use only the ``__init__`` docstring. If the class does not have an ``__init__`` or the ``__init__`` docstring is empty and the class defines a ``__new__`` with a docstring, the ``__new__`` docstring is used instead of the ``__init__`` docstring. .. confval:: autoapi_member_order Default: ``bysource`` The order to document members. This option can have the following values: * ``alphabetical``: Order members by their name, case sensitively. * ``bysource``: Order members by the order that they were defined in the source code. * ``groupwise``: Order members by their type then alphabetically, ordering the types as follows: * Submodules and subpackages * Attributes * Exceptions * Classes * Functions * Methods .. confval:: autoapi_python_use_implicit_namespaces Default: ``False`` This changes the package detection behaviour to be compatible with :pep:`420`, but directories in :confval:`autoapi_dirs` are no longer searched recursively for packages. Instead, when this is ``True``, :confval:`autoapi_dirs` should point directly to the directories of implicit namespaces and the directories of packages. If searching is still required, this should be done manually in the ``conf.py``. .. confval:: autoapi_prepare_jinja_env Default: ``None`` A callback that is called shortly after the Jinja environment is created. It passed the Jinja environment for editing before template rendering begins. The callback should have the following signature: .. py:function:: prepare_jinja_env(jinja_env: jinja2.Environment) -> None :noindex: .. confval:: autoapi_own_page_level Default: ``'module'`` This configuration value specifies the level at which objects are rendered on a single page. Valid levels, in descending order of hierarchy, are as follows: * ``module``: Packages, modules, subpackages, and submodules. * ``class``: Classes, exceptions, and all object types mentioned above. * ``function``: Functions, and all object types mentioned above. * ``method``: Methods, and all object types mentioned above. * ``attribute``: Class and module level attributes, properties, and all object types mentioned above. Events ~~~~~~ The following events allow you to control the behaviour of AutoAPI. .. event:: autoapi-skip-member (app, what, name, obj, skip, options) Emitted when a template has to decide whether a member should be included in the documentation. Usually the member is skipped if a handler returns ``True``, and included otherwise. Handlers should return ``None`` to fall back to the default skipping behaviour of AutoAPI or another attached handler. .. code-block:: python :caption: Example conf.py def skip_util_classes(app, what, name, obj, skip, options): if what == "class" and "util" in name: skip = True return skip def setup(sphinx): sphinx.connect("autoapi-skip-member", skip_util_classes) :param app: The Sphinx application object. :param what: The type of the object which the docstring belongs to. This can be one of: ``"attribute"``, ``"class"``, ``"data"``, ``"exception"``, ``"function"``, ``"method"``, ``"module"``, ``"package"``. :type what: str :param name: The fully qualified name of the object. :type name: str :param obj: The object itself. :type obj: PythonPythonMapper :param skip: Whether AutoAPI will skip this member if the handler does not override the decision. :type skip: bool :param options: The options given to the directive. Advanced Options ----------------- .. confval:: autoapi_keep_files Default: ``False`` Keep the AutoAPI generated files on the filesystem after the run. Useful for debugging or transitioning to manual documentation. Keeping files will also allow AutoAPI to use incremental builds. Providing none of the source files have changed, AutoAPI will skip parsing the source code and regenerating the API documentation. Suppressing Warnings --------------------- .. confval:: suppress_warnings This is a sphinx builtin option that enables the granular filtering of AutoAPI generated warnings. Items in the ``suppress_warnings`` list are of the format ``"type.subtype"`` where ``".subtype"`` can be left out to cover all subtypes. To suppress all AutoAPI warnings add the type ``"autoapi"`` to the list: .. code-block:: python suppress_warnings = ["autoapi"] If narrower suppression is wanted, the available subtypes for AutoAPI are: * python_import_resolution Used if resolving references to objects in an imported module failed. Potential reasons include cyclical imports and missing (parent) modules. * not_readable Emitted if processing (opening, parsing, ...) an input file failed. * toc_reference Used if a reference to an entry in a table of content cannot be resolved. So if all AutoAPI warnings concerning unreadable sources and failing Python imports should be filtered, but all other warnings should not, the option would be .. code-block:: python suppress_warnings = ["autoapi.python_import_resolution", "autoapi.not_readable"] sphinx-autoapi-3.1.2/docs/reference/directives.rst000066400000000000000000000037471463504067600222750ustar00rootroot00000000000000Directives ========== .. _autodoc-directives: Autodoc-Style Directives ------------------------ You can opt to write API documentation yourself using autodoc style directives. These directives work similarly to autodoc, but docstrings are retrieved through static analysis instead of through imports. .. seealso:: When transitioning to autodoc-style documentation, you may want to turn the :confval:`autoapi_generate_api_docs` option off so that automatic API documentation is no longer generated. To use these directives you will need to enable the autodoc extension in your Sphinx project's ``conf.py``: .. code:: python extensions = ['sphinx.ext.autodoc', 'autoapi.extension'] For Python, all directives have an autodoc equivalent and accept the same options. The following directives are available: .. rst:directive:: autoapimodule autoapiclass autoapiexception Equivalent to :rst:dir:`automodule`, :rst:dir:`autoclass`, and :rst:dir:`autoexception` respectively. :confval:`autodoc_inherit_docstrings` does not currently work. .. rst:directive:: autoapifunction autoapidata autoapimethod autoapiattribute Equivalent to :rst:dir:`autofunction`, :rst:dir:`autodata`, :rst:dir:`automethod`, and :rst:dir:`autoattribute` respectively. Inheritance Diagrams -------------------- .. rst:directive:: autoapi-inheritance-diagram This directive uses the :mod:`sphinx.ext.inheritance_diagram` extension to create inheritance diagrams for classes. For example: .. autoapi-inheritance-diagram:: autoapi._objects.PythonModule autoapi._objects.PythonPackage :parts: 1 :mod:`sphinx.ext.inheritance_diagram` makes use of the :mod:`sphinx.ext.graphviz` extension, and therefore it requires `Graphviz `_ to be installed. The directive can be configured using the same options as :mod:`sphinx.ext.inheritance_diagram`. sphinx-autoapi-3.1.2/docs/reference/index.rst000066400000000000000000000004401463504067600212260ustar00rootroot00000000000000############### Reference Guide ############### The reference guide contains a detailed description of the configuration options, directives, and templates included in AutoAPI. To learn how to use AutoAPI, see the :doc:`/tutorials`. .. toctree:: config directives templatessphinx-autoapi-3.1.2/docs/reference/templates.rst000066400000000000000000000053201463504067600221170ustar00rootroot00000000000000Templates ========= A lot of the power from AutoAPI comes from templates. We are basically building a mapping from code to docs, and templates let you highly customise the display of said docs. Structure --------- Every type of data structure has its own template. It uses the form :samp:`python/{type}.rst` to find the template to render. The full search path is: * :samp:`python/{type}.rst` So for a Python Class, this would resolve to: * :samp:`python/class.rst` We provide :samp:`base/base.rst` as an incredibly basic output of every object:: .. py:{type}:: {name} Custom Filters, Tests, and Globals ---------------------------------- The :confval:`autoapi_prepare_jinja_env` configuration option allows you to pass a callback that can edit the :class:`jinja2.Environment` object before rendering begins. This callback, among other things, can be used to add custom filters, tests, and/or globals to the Jinja environment. For example: .. code-block:: python def autoapi_prepare_jinja_env(jinja_env): jinja_env.filters["my_custom_filter"] = lambda value: value.upper() Context ------- Every template is given a set context that can be accessed in the templates. This contains: * ``autoapi_options``: The value of the :confval:`autoapi_options` configuration option. * ``include_summaries``: The value of the :confval:`autoapi_include_summaries` configuration option. * ``obj``: A Python object derived from :class:`PythonPythonMapper`. * ``own_page_types``: A set of strings that contains the object types that render on their own page. * ``sphinx_version``: The contents of :attr:`sphinx.version_info`. The object in ``obj`` has a number of standard attributes that you can reliably access. .. warning:: These classes should not be constructed manually. They can be reliably accessed through templates and :event:`autoapi-skip-member` only. .. autoapiclass:: autoapi._objects.PythonPythonMapper :members: .. autoapiclass:: autoapi._objects.PythonFunction :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonMethod :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonProperty :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonData :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonAttribute :members: :show-inheritance: .. autoapiclass:: autoapi._objects.TopLevelPythonPythonMapper :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonModule :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonPackage :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonClass :members: :show-inheritance: .. autoapiclass:: autoapi._objects.PythonException :members: :show-inheritance:sphinx-autoapi-3.1.2/docs/tutorials.rst000066400000000000000000000073541463504067600202220ustar00rootroot00000000000000Tutorials ========= These tutorials will guide you through how to start using AutoAPI. They will assume that you already have a basic Sphinx project set up. If you are not sure how to do this, you can follow the :doc:`sphinx:usage/quickstart` guide in the Sphinx documentation. Setting up Automatic API Documentation Generation ------------------------------------------------- The recommended way of installing AutoAPI is through a `virtualenv `_. Once you have a virtualenv set up, you can install AutoAPI with the command: .. code-block:: bash pip install sphinx-autoapi .. Validate this section with the following commands: $ mkdir mypackage $ cd mypackage $ mkdir mypackage $ echo -e 'from ._client import Client\nfrom ._server import Server' > mypackage/__init__.py $ echo -e 'class Client:\n pass' > mypackage/_client.py $ echo -e 'class Server:\n pass' > mypackage/_server.py $ touch README.md $ python -m venv .venv $ .venv/bin/pip install /path/to/sphinx-autoapi $ .venv/bin/sphinx-quickstart --no-sep --project mypackage --author me -v 0.1.0 --release 0.1.0 --language en --extensions autoapi.extension docs $ echo 'autoapi_dirs = ["../mypackage"]' >> docs/conf.py $ .venv/bin/sphinx-build -b html docs/ docs/_build To enable the extension, we need to add it to the list of extensions in Sphinx's ``conf.py`` file:: extensions = ['autoapi.extension'] There is only one required configuration option that we need to set. :confval:`autoapi_dirs` tells AutoAPI which directories contain the source code to document. These can either be absolute, or relative to the source directory of your documentation files. For example, say we have a package and we have used ``sphinx-quickstart`` to create a Sphinx project in a ``docs/`` folder. The directory structure might look like this: .. code-block:: none mypackage/ ├── docs │   ├── _build │   ├── conf.py │   ├── index.rst │   ├── make.bat │   ├── Makefile │   ├── _static │   └── _templates ├── mypackage │   ├── _client.py │   ├── __init__.py │   └── _server.py └── README.md ``sphinx-quickstart`` sets up the ``sphinx-build`` to run from inside the ``docs/`` directory, and the source code is one level up. So the value of our :confval:`autoapi_dirs` option would be:: autoapi_dirs = ['../mypackage'] If you are documenting many packages, you can point AutoAPI to the directory that contains those packages. For example if our source code was inside a ``src/`` directory: .. code-block:: none mypackage/ ├── docs │   ├── _build │   ├── conf.py │   ├── index.rst │   ├── make.bat │   ├── Makefile │   ├── _static │   └── _templates ├── README.md └── src └── mypackage ├── _client.py ├── __init__.py └── _server.py We can configure :confval:`autoapi_dirs` to be:: autoapi_dirs = ['../src'] Now that everything is configured, AutoAPI will generate documentation when you run Sphinx! .. code-block:: bash cd docs/ sphinx-build -b html . _build With the documentation successfully built you should now be able to open the ``_build/index.html`` file in a web browser. The page will have a table of contents with a link to API reference documentation that has been generated by AutoAPI. Next, you might want to :ref:`customise what gets documented ` or :ref:`customise or remove the API reference index page `.sphinx-autoapi-3.1.2/pyproject.toml000066400000000000000000000007141463504067600174170ustar00rootroot00000000000000[build-system] requires = ["setuptools>=46.4.0"] build-backend = "setuptools.build_meta" [[tool.mypy.overrides]] module = "astroid.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "autoapi.documenters" ignore_errors = true [tool.ruff.lint.pydocstyle] convention = "google" [tool.towncrier] directory = "docs/changes" filename = "CHANGELOG.rst" package = "autoapi" title_format = "v{version} ({project_date})" underlines = ["-", "^", "\""] sphinx-autoapi-3.1.2/setup.cfg000066400000000000000000000025171463504067600163270ustar00rootroot00000000000000[metadata] name = sphinx-autoapi version = attr: autoapi.__version__ author = Eric Holscher author_email = eric@ericholscher.com maintainer = Ashley Whetter maintainer_email = ashley@awhetter.co.uk description = Sphinx API documentation generator long_description = file: README.rst long_description_content_type = text/x-rst url = http://github.com/readthedocs/sphinx-autoapi license = MIT license_files = LICENSE.rst classifiers = Development Status :: 4 - Beta Environment :: Plugins Framework :: Sphinx :: Extension Intended Audience :: Developers License :: OSI Approved :: MIT License Natural Language :: English Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 project_urls = Documentation = https://sphinx-autoapi.readthedocs.io/en/latest/ [options] packages = find: include_package_data = True python_requires = >=3.8 install_requires = astroid>=2.7;python_version<"3.12" astroid>=3.0.0a1;python_version>="3.12" Jinja2 PyYAML sphinx>=6.1.0 [options.extras_require] docs = furo sphinx sphinx_design [options.packages.find] include = autoapi autoapi.* [bdist_wheel] universal = 1 sphinx-autoapi-3.1.2/setup.py000066400000000000000000000000451463504067600162120ustar00rootroot00000000000000from setuptools import setup setup() sphinx-autoapi-3.1.2/tests/000077500000000000000000000000001463504067600156435ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/000077500000000000000000000000001463504067600171645ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/conftest.py000066400000000000000000000032411463504067600213630ustar00rootroot00000000000000import io import os import pathlib import shutil from unittest.mock import call from bs4 import BeautifulSoup import pytest from sphinx.application import Sphinx @pytest.fixture(scope="session") def rebuild(): def _rebuild(confdir=".", **kwargs): app = Sphinx( srcdir=".", confdir=confdir, outdir="_build/html", doctreedir="_build/.doctrees", buildername="html", pdb=True, **kwargs, ) app.build() return _rebuild @pytest.fixture(scope="class") def builder(rebuild): cwd = os.getcwd() def build(test_dir, **kwargs): if kwargs.get("warningiserror"): # Add any warnings raised when using `Sphinx` more than once # in a Python session. confoverrides = kwargs.setdefault("confoverrides", {}) confoverrides.setdefault("suppress_warnings", []) suppress = confoverrides["suppress_warnings"] suppress.append("app.add_node") suppress.append("app.add_directive") suppress.append("app.add_role") os.chdir("tests/python/{0}".format(test_dir)) rebuild(**kwargs) yield build try: shutil.rmtree("_build") if (pathlib.Path("autoapi") / "index.rst").exists(): shutil.rmtree("autoapi") finally: os.chdir(cwd) @pytest.fixture(scope="class") def parse(): cache = {} def parser(path): if path not in cache: with io.open(path, encoding="utf8") as file_handle: cache[path] = BeautifulSoup(file_handle, features="html.parser") return cache[path] yield parser sphinx-autoapi-3.1.2/tests/python/pep695/000077500000000000000000000000001463504067600202145ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pep695/conf.py000066400000000000000000000011061463504067600215110ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.intersphinx", "sphinx.ext.autodoc", "autoapi.extension"] intersphinx_mapping = {"python": ("https://docs.python.org/3.10", None)} autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" sphinx-autoapi-3.1.2/tests/python/pep695/example/000077500000000000000000000000001463504067600216475ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pep695/example/example.py000066400000000000000000000001541463504067600236540ustar00rootroot00000000000000from typing import TypeAlias MyTypeAliasA: TypeAlias = tuple[str, int] type MyTypeAliasB = tuple[str, int] sphinx-autoapi-3.1.2/tests/python/pep695/index.rst000066400000000000000000000007201463504067600220540ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/py310unionpipe/000077500000000000000000000000001463504067600217675ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py310unionpipe/conf.py000066400000000000000000000011061463504067600232640ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.intersphinx", "sphinx.ext.autodoc", "autoapi.extension"] intersphinx_mapping = {"python": ("https://docs.python.org/3.10", None)} autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" sphinx-autoapi-3.1.2/tests/python/py310unionpipe/example/000077500000000000000000000000001463504067600234225ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py310unionpipe/example/example.py000066400000000000000000000004111463504067600254230ustar00rootroot00000000000000from pathlib import Path from typing import Optional, Union def simple(p: Path): """This is OK""" def optional(p: Optional[Path]): """This is OK""" def union(p: Union[Path, None]): """This is OK""" def pipe(p: Path | None): """This is OK""" sphinx-autoapi-3.1.2/tests/python/py310unionpipe/index.rst000066400000000000000000000007201463504067600236270ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/py38positionalparams/000077500000000000000000000000001463504067600232755ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py38positionalparams/conf.py000066400000000000000000000007431463504067600246000ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" sphinx-autoapi-3.1.2/tests/python/py38positionalparams/example/000077500000000000000000000000001463504067600247305ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py38positionalparams/example/example.py000066400000000000000000000013741463504067600267420ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ from typing import Optional def f_simple(a, b, /, c, d, *, e, f): print(a, b, c, d, e, f) def f_comment(a, b, /, c, d, *, e, f): # type: (int, int, Optional[int], Optional[int], float, float) -> None print(a, b, c, d, e, f) def f_annotation( a: int, b: int, /, c: Optional[int], d: Optional[int], *, e: float, f: float ) -> None: print(a, b, c, d, e, f) def f_arg_comment( a, # type: int b, # type: int /, c, # type: Optional[int] d, # type: Optional[int] *, e, # type: float f, # type: float ): # type: (...) -> None print(a, b, c, d, e, f) def f_no_cd(a: int, b: int, /, *, e: float, f: float): print(a, b, e, f) sphinx-autoapi-3.1.2/tests/python/py38positionalparams/index.rst000066400000000000000000000007201463504067600251350ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/py3example/000077500000000000000000000000001463504067600212535ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py3example/conf.py000066400000000000000000000013731463504067600225560ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_python_class_content = "both" autoapi_keep_files = True autoapi_options = [ "members", "undoc-members", # this is temporary until we add docstrings across the codebase "show-inheritance", "show-module-summary", "special-members", "imported-members", "inherited-members", ] sphinx-autoapi-3.1.2/tests/python/py3example/example/000077500000000000000000000000001463504067600227065ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py3example/example/example.py000066400000000000000000000061501463504067600247150ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ import asyncio import typing from typing import ClassVar, Dict, Iterable, Generic, List, TypeVar, Union, overload from example2 import B T = TypeVar("T") U = TypeVar("U") software = "sphin'x" more_software = 'sphinx"autoapi' interesting_string = "interesting\"fun'\\'string" code_snippet = """The following is some code: # -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals # from future.builtins.disabled import * # from builtins import * print("chunky o'block") """ max_rating: int = 10 is_valid: bool if max_rating > 100: is_valid = False else: is_valid = True ratings: List[int] = [0, 1, 2, 3, 4, 5] rating_names: Dict[int, str] = {0: "zero", 1: "one"} def f(start: int, end: int) -> Iterable[int]: "This is f" i = start while i < end: yield i i += 1 mixed_list: List[Union[str, int]] = [1, "two", 3] "This is mixed" def f2(not_yet_a: "A") -> int: ... def f3(imported: B) -> B: ... class MyGeneric(Generic[T, U]): ... @overload def overloaded_func(a: float) -> float: ... @typing.overload def overloaded_func(a: str) -> str: ... def overloaded_func(a: Union[float, str]) -> Union[float, str]: """Overloaded function""" return a * 2 @overload def undoc_overloaded_func(a: str) -> str: ... def undoc_overloaded_func(a: str) -> str: return a * 2 class A: """class A""" is_an_a: ClassVar[bool] = True not_assigned_to: ClassVar[str] def __init__(self): self.instance_var: bool = True """This is an instance_var.""" async def async_method(self, wait: bool) -> int: if wait: await asyncio.sleep(1) return 5 @property def my_prop(self) -> str: """My property.""" return "prop" def my_method(self) -> str: """My method.""" return "method" @overload def overloaded_method(self, a: float) -> float: ... @typing.overload def overloaded_method(self, a: str) -> str: ... def overloaded_method(self, a: Union[float, str]) -> Union[float, str]: """Overloaded method""" return a * 2 @overload def undoc_overloaded_method(self, a: float) -> float: ... def undoc_overloaded_method(self, a: float) -> float: return a * 2 @typing.overload @classmethod def overloaded_class_method(cls, a: float) -> float: ... @overload @classmethod def overloaded_class_method(cls, a: str) -> str: ... @classmethod def overloaded_class_method(cls, a: Union[float, str]) -> Union[float, str]: """Overloaded class method""" return a * 2 class C: @overload def __init__(self, a: int) -> None: ... @typing.overload def __init__(self, a: float) -> None: ... def __init__(self, a: str): ... class D(C): class Da: ... class DB(Da): ... ... async def async_function(wait: bool) -> int: """Blah. Args: wait: Blah """ if wait: await asyncio.sleep(1) return 5 global_a: A = A() class SomeMetaclass(type): ... sphinx-autoapi-3.1.2/tests/python/py3example/example/example2.py000066400000000000000000000000221463504067600247670ustar00rootroot00000000000000class B: pass sphinx-autoapi-3.1.2/tests/python/py3example/index.rst000066400000000000000000000007171463504067600231210ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/000077500000000000000000000000001463504067600233075ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/conf.py000066400000000000000000000010361463504067600246060ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "py3implicitnamespacedoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["namespace"] autoapi_file_pattern = "*.py" autoapi_python_use_implicit_namespaces = True sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/index.rst000066400000000000000000000007611463504067600251540ustar00rootroot00000000000000.. py3implicitnamespace documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to py3implicitnamespace's documentation! ================================================ .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/namespace/000077500000000000000000000000001463504067600252435ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/namespace/example/000077500000000000000000000000001463504067600266765ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/namespace/example/__init__.py000066400000000000000000000001711463504067600310060ustar00rootroot00000000000000from ..sibling import * from ..sibling.sub_sibling import * def example_method(foo): """Example method""" pass sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/namespace/sibling/000077500000000000000000000000001463504067600266725ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/namespace/sibling/__init__.py000066400000000000000000000002261463504067600310030ustar00rootroot00000000000000def first_method(): """First sibling package method.""" return 1 def second_method(): """Second sibling package method.""" return 2 sphinx-autoapi-3.1.2/tests/python/py3implicitnamespace/namespace/sibling/sub_sibling.py000066400000000000000000000002411463504067600315410ustar00rootroot00000000000000def first_sub_method(): """First sub-sibling package method.""" return 1 def second_sub_method(): """Second sub-subpackage method.""" return 2 sphinx-autoapi-3.1.2/tests/python/pyannotationcommentsexample/000077500000000000000000000000001463504067600250315ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyannotationcommentsexample/conf.py000066400000000000000000000007751463504067600263410ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" autoapi_keep_files = True sphinx-autoapi-3.1.2/tests/python/pyannotationcommentsexample/example/000077500000000000000000000000001463504067600264645ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyannotationcommentsexample/example/example.py000066400000000000000000000023411463504067600304710ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ from typing import ClassVar, Dict, Iterable, List, Union max_rating = 10 # type: int ratings = [0, 1, 2, 3, 4, 5] # type: List[int] rating_names = {0: "zero", 1: "one"} # type: Dict[int, str] def f( start, # type: int end, # type: int ): # type: (...) -> Iterable[int] i = start while i < end: yield i i += 1 mixed_list = [1, "two", 3] # type: List[Union[str, int]] def f2(not_yet_a): # type: (A) -> int pass class A: is_an_a = True # type: ClassVar[bool] def __init__(self): self.instance_var = True # type: bool """This is an instance_var.""" global_a = A() # type: A def f3(first_arg, **kwargs): # type: (first_arg, Any) -> None """Annotation incorrectly leaves out `**`.""" class B: """Annotation keeps self/cls and shift all arg types""" def __init__(self, a): # type: (str) -> None pass def method(self, b): # type: (list) -> None pass @classmethod def class_method(cls, c): # type: (int) -> None pass @staticmethod def static_method(d): # type: (float) -> Union[str,None] pass sphinx-autoapi-3.1.2/tests/python/pyannotationcommentsexample/index.rst000066400000000000000000000007201463504067600266710ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pyautodoc_typehints/000077500000000000000000000000001463504067600233025ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyautodoc_typehints/conf.py000066400000000000000000000014331463504067600246020ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension", "sphinx.ext.napoleon"] autoapi_dirs = ["example"] autoapi_python_class_content = "both" autoapi_options = [ "members", "undoc-members", # this is temporary until we add docstrings across the codebase "show-inheritance", "show-module-summary", "special-members", "imported-members", "inherited-members", ] autodoc_typehints = "description" sphinx-autoapi-3.1.2/tests/python/pyautodoc_typehints/example/000077500000000000000000000000001463504067600247355ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyautodoc_typehints/example/example.py000066400000000000000000000002061463504067600267400ustar00rootroot00000000000000class A: def test(self, a: int) -> bool: """Test. Args: a: Argument """ return False sphinx-autoapi-3.1.2/tests/python/pyautodoc_typehints/example/example2.py000066400000000000000000000002061463504067600270220ustar00rootroot00000000000000class B: def test(self, a: int) -> bool: """Test. Args: a: Argument """ return False sphinx-autoapi-3.1.2/tests/python/pyautodoc_typehints/index.rst000066400000000000000000000007171463504067600251500ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pyemptyexample/000077500000000000000000000000001463504067600222475ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyemptyexample/conf.py000066400000000000000000000013411463504067600235450ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_python_class_content = "both" autoapi_options = [ "members", "undoc-members", # this is temporary until we add docstrings across the codebase "show-inheritance", "show-module-summary", "special-members", "imported-members", "inherited-members", ] sphinx-autoapi-3.1.2/tests/python/pyemptyexample/index.rst000066400000000000000000000007171463504067600241150ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pyexample/000077500000000000000000000000001463504067600211705ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyexample/conf.py000066400000000000000000000010431463504067600224650ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" autoapi_python_class_content = "both" autoapi_keep_files = True sphinx-autoapi-3.1.2/tests/python/pyexample/example/000077500000000000000000000000001463504067600226235ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyexample/example/example.py000066400000000000000000000063521463504067600246360ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ A_TUPLE = ("a", "b") """A tuple to be rendered as a tuple.""" A_LIST = ["c", "d"] """A list to be rendered as a list.""" class Foo(object): """Can we parse arguments from the class docstring? :param attr: Set an attribute. :type attr: str """ class_var = 42 #: Class var docstring another_class_var = 42 """Another class var docstring""" class Meta(object): """A nested class just to test things out""" @classmethod def foo(): """The foo class method""" return True def __init__(self, attr): """Constructor docstring""" self.attr = attr self.attr2 = attr """This is the docstring of an instance attribute. :type: str """ @property def property_simple(self) -> int: """This property should parse okay.""" return 42 def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True def method_multiline(self, foo=None, bar=None, baz=None): """This is on multiple lines, but should parse okay too pydocstyle gives us lines of source. Test if this means that multiline definitions are covered in the way we're anticipating here """ return True def method_tricky(self, foo=None, bar=dict(foo=1, bar=2)): """This will likely fail our argument testing We parse naively on commas, so the nested dictionary will throw this off """ return True def method_sphinx_docs(self, foo, bar=0): """This method is documented with sphinx style docstrings. :param foo: The first argument. :type foo: int :param int bar: The second argument. :returns: The sum of foo and bar. :rtype: int """ return foo + bar def method_google_docs(self, foo, bar=0): """This method is documented with google style docstrings. Args: foo (int): The first argument. bar (int): The second argument. Returns: int: The sum of foo and bar. """ return foo + bar def method_sphinx_unicode(self): """This docstring uses unicodé. :returns: A string. :rtype: str """ return "sphinx" def method_google_unicode(self): """This docstring uses unicodé. Returns: str: A string. """ return "google" Foo.bar = "dinglebop" def decorator_okay(func): """This decorator should parse okay.""" def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper class Bar(Foo): def method_okay(self, foo=None, bar=None): pass class ClassWithNoInit: pass class One: """One.""" def __init__(self): """One __init__.""" super().__init__() class MultilineOne(One): """This is a naughty summary line that exists on two lines.""" class Two(One): """Two.""" def fn_with_long_sig( this, *, function=None, has=True, quite=True, a, long, signature, many, keyword, arguments ): """A function with a long signature.""" sphinx-autoapi-3.1.2/tests/python/pyexample/index.rst000066400000000000000000000006451463504067600230360ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: :glob: * Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pyexample/manualapi.rst000066400000000000000000000002151463504067600236670ustar00rootroot00000000000000Autodoc Directives ================== .. autoapimodule:: example :members: .. autoapidecorator:: example.decorator_okay :noindex: sphinx-autoapi-3.1.2/tests/python/pyexample/null.rst000066400000000000000000000000121463504067600226650ustar00rootroot00000000000000Null ==== sphinx-autoapi-3.1.2/tests/python/pyiexample/000077500000000000000000000000001463504067600213415ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyiexample/conf.py000066400000000000000000000007571463504067600226510ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_patterns = ["*.pyi", "*.py"] sphinx-autoapi-3.1.2/tests/python/pyiexample/example/000077500000000000000000000000001463504067600227745ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyiexample/example/example.py000066400000000000000000000001061463504067600247760ustar00rootroot00000000000000class DoNotFindThis(object): """pyi files should be preferred.""" sphinx-autoapi-3.1.2/tests/python/pyiexample/example/example.pyi000066400000000000000000000021051463504067600251500ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ class Foo(object): """Can we parse arguments from the class docstring? :param attr: Set an attribute. :type attr: str """ class_var = 42 #: Class var docstring another_class_var = 42 """Another class var docstring""" class_var_without_value = ... """A class var without a value.""" class Meta(object): """A nested class just to test things out""" @classmethod def foo(): """The foo class method""" ... def __init__(self, attr): """Constructor docstring""" ... def method_okay(self, foo=None, bar=None): """This method should parse okay""" ... def method_multiline(self, foo=None, bar=None, baz=None): """This is on multiple lines, but should parse okay too pydocstyle gives us lines of source. Test if this means that multiline definitions are covered in the way we're anticipating here """ ... def method_without_docstring(self): ... sphinx-autoapi-3.1.2/tests/python/pyiexample/index.rst000066400000000000000000000003141463504067600232000ustar00rootroot00000000000000Welcome to pyiexample's documentation! ====================================== .. toctree:: autoapi/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pyiexample2/000077500000000000000000000000001463504067600214235ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyiexample2/conf.py000066400000000000000000000007051463504067600227240ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] sphinx-autoapi-3.1.2/tests/python/pyiexample2/example/000077500000000000000000000000001463504067600230565ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyiexample2/example/example.py000066400000000000000000000001431463504067600250610ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ class Foo(object): pass sphinx-autoapi-3.1.2/tests/python/pyiexample2/example/example.pyi000066400000000000000000000001051463504067600252300ustar00rootroot00000000000000class DoNotFindThis(object): """py files should be preferred.""" sphinx-autoapi-3.1.2/tests/python/pyiexample2/index.rst000066400000000000000000000003161463504067600232640ustar00rootroot00000000000000Welcome to pyiexample2's documentation! ======================================= .. toctree:: autoapi/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/000077500000000000000000000000001463504067600225715ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/conf.py000066400000000000000000000007231463504067600240720ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyisubmoduleinit" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyisubmoduleinitdoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/example/000077500000000000000000000000001463504067600242245ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/example/example.py000066400000000000000000000000001463504067600262170ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/example/submodule_foo/000077500000000000000000000000001463504067600270665ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/example/submodule_foo/__init__.pyi000066400000000000000000000005261463504067600313530ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example __init__ in submodule foo Documentation generated for this file should be titled submodule_foo instead of __init__ This is a description """ class Foo(object): """ This is a description """ def bar(self, a: int) -> None: """ This is a description """ ... sphinx-autoapi-3.1.2/tests/python/pyisubmoduleinit/index.rst000066400000000000000000000003301463504067600244260ustar00rootroot00000000000000Welcome to pyisubmoduleinit's documentation! ============================================ .. toctree:: autoapi/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pymovedconfpy/000077500000000000000000000000001463504067600220665ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pymovedconfpy/confpy/000077500000000000000000000000001463504067600233645ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pymovedconfpy/confpy/conf.py000077700000000000000000000000001463504067600306072../../pyexample/conf.pyustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pymovedconfpy/example000077700000000000000000000000001463504067600272342../pyexample/exampleustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pymovedconfpy/index.rst000077700000000000000000000000001463504067600300022../pyexample/index.rstustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pymovedconfpy/manualapi.rst000077700000000000000000000000001463504067600315022../pyexample/manualapi.rstustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/000077500000000000000000000000001463504067600225205ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/000077500000000000000000000000001463504067600241675ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/__init__.py000066400000000000000000000003621463504067600263010ustar00rootroot00000000000000""" This heading will be removed ============================ """ from .subpackage import public_chain from .subpackage.submodule import public_multiple_imports def module_level_function(foo, bar): """A module level method""" pass sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/_private_module.py000066400000000000000000000003541463504067600277210ustar00rootroot00000000000000class PrivateClass(object): """A private class with public facing methods.""" def public_method(): """This is public.""" return 5 def imported_function(): """A function that gets imported.""" return 1 sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/binary_data.py000066400000000000000000000000571463504067600270200ustar00rootroot00000000000000# -*- coding: utf-8 -*- data = b"\xf0\xf0\xf0" sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/foo.py000066400000000000000000000033661463504067600253340ustar00rootroot00000000000000"""Example module This is a description """ from ._private_module import PrivateClass as PublicClass from ._subpackage import module_level_function __all__ = ["PublicClass", "Foo"] class Foo(object): class_var = 42 #: Class var docstring another_class_var = 42 """Another class var docstring""" class Meta(object): """A nested class just to test things out""" @classmethod def foo(): """The foo class method""" return True def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True def method_multiline(self, foo=None, bar=None, baz=None): """This is on multiple lines, but should parse okay too pydocstyle gives us lines of source. Test if this means that multiline definitions are covered in the way we're anticipating here """ return True def method_tricky(self, foo=None, bar=dict(foo=1, bar=2)): """This will likely fail our argument testing We parse naively on commas, so the nested dictionary will throw this off """ return True def method_sphinx_docs(self, foo, bar=0): """This method is documented with sphinx style docstrings. :param foo: The first argument. :type foo: int :param int bar: The second argument. :returns: The sum of foo and bar. :rtype: int """ return foo + bar def method_google_docs(self, foo, bar=0): """This method is documented with google style docstrings. Args: foo (int): The first argument. bar (int): The second argument. Returns: int: The sum of foo and bar. """ return foo + bar sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/submodule.py000066400000000000000000000002051463504067600265350ustar00rootroot00000000000000from ._private_module import imported_function def defined_function(): """A function defined in the submodule.""" return 1 sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/subpackage/000077500000000000000000000000001463504067600262745ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/subpackage/__init__.py000066400000000000000000000003461463504067600304100ustar00rootroot00000000000000from .submodule import public_chain from .submodule import _private_made_public as now_public_function from .submodule import public_multiple_imports def module_level_function(foo, bar): """A module level method""" pass sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/subpackage/submodule.py000066400000000000000000000005751463504067600306540ustar00rootroot00000000000000""" This heading will be removed ============================ This docstring will not be removed. """ def public_chain(): """Part of a public resolution chain.""" return 5 def _private_made_public(): """A private function made public by import.""" return 5 def public_multiple_imports(): """A public function imported in multiple places.""" return 5 sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/unicode_data.py000066400000000000000000000001311463504067600271530ustar00rootroot00000000000000# -*- coding: utf-8 -*- unicode_str = "αβγδεζηθικλμνξοπρςστυφχψ" sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildall/000077500000000000000000000000001463504067600256175ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildall/__init__.py000066400000000000000000000000261463504067600277260ustar00rootroot00000000000000from .simple import * sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildall/simple/000077500000000000000000000000001463504067600271105ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildall/simple/__init__.py000066400000000000000000000006001463504067600312150ustar00rootroot00000000000000from ...subpackage import * __all__ = [ "SimpleClass", "simple_function", "public_chain", "module_level_function", "does_not_exist", ] class SimpleClass(object): def simple_method(self): return 5 class NotAllClass(object): def not_all_method(self): return 5 def simple_function(): return 5 def not_all_function(): return 5 sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildcard/000077500000000000000000000000001463504067600257605ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildcard/__init__.py000066400000000000000000000000331463504067600300650ustar00rootroot00000000000000from ..subpackage import * sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildchain/000077500000000000000000000000001463504067600261315ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildchain/__init__.py000066400000000000000000000001211463504067600302340ustar00rootroot00000000000000from ..wildcard import module_level_function from ..wildcard import public_chain sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildwildchain/000077500000000000000000000000001463504067600270115ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/complex/wildwildchain/__init__.py000066400000000000000000000000321463504067600311150ustar00rootroot00000000000000from ..wildchain import * sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/conf.py000066400000000000000000000007331463504067600240220ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pypackagecomplex" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pypackagecomplexdoc" extensions = ["autoapi.extension"] autoapi_dirs = ["complex"] autoapi_file_pattern = "*.py" sphinx-autoapi-3.1.2/tests/python/pypackagecomplex/index.rst000066400000000000000000000007441463504067600243660ustar00rootroot00000000000000.. pypackagecomplex documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pypackagecomplex's documentation! ============================================ .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pypackageexample/000077500000000000000000000000001463504067600225045ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackageexample/conf.py000066400000000000000000000007651463504067600240130ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pypackageexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pypackageexampledoc" extensions = ["autoapi.extension"] autoapi_dirs = ["package"] autoapi_file_pattern = "*.py" autoapi_keep_files = True sphinx-autoapi-3.1.2/tests/python/pypackageexample/index.rst000066400000000000000000000007441463504067600243520ustar00rootroot00000000000000.. pypackageexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pypackageexample's documentation! ============================================ .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/000077500000000000000000000000001463504067600240775ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/__init__.py000066400000000000000000000014501463504067600262100ustar00rootroot00000000000000"""This is a docstring.""" from . import submodule from .subpackage.submodule import function as aliased_function from .subpackage.submodule import not_in_all_function __all__ = ( "aliased_function", "Class", "DATA", "function", "MyException", ) DATA = 42 def function(foo, bar): """A module level function""" class Class(object): """This is a class.""" class_var = 42 """Class var docstring""" class NestedClass(object): """A nested class just to test things out""" @classmethod def a_classmethod(): """A class method""" return True def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True class MyException(Exception): """This is an exception.""" sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_submodule.py000066400000000000000000000002331463504067600303370ustar00rootroot00000000000000class PrivateClass(object): """A private class with public facing methods.""" def public_method(): """This is public.""" return 5 sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_subpackage/000077500000000000000000000000001463504067600300755ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_subpackage/__init__.py000066400000000000000000000003561463504067600322120ustar00rootroot00000000000000"""This is a docstring.""" from .submodule import function as aliased_function from .submodule import not_in_all_function __all__ = ( "aliased_function", "function", ) def function(foo, bar): """A module level function""" sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_subpackage/submodule.py000066400000000000000000000013651463504067600324530ustar00rootroot00000000000000"""Example module This is a description """ DATA = 42 def function(foo, bar): """A module level function""" def _private_function(): """A function that shouldn't get rendered.""" def not_in_all_function(): """A function that doesn't exist in __all__ when imported.""" class Class(object): """This is a class.""" class_var = 42 """Class var docstring""" class NestedClass(object): """A nested class just to test things out""" @classmethod def a_classmethod(): """A class method""" return True def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True class MyException(Exception): """This is an exception.""" sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_subpackage/subpackage/000077500000000000000000000000001463504067600322025ustar00rootroot00000000000000__init__.py000066400000000000000000000003561463504067600342400ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_subpackage/subpackage"""This is a docstring.""" from .submodule import function as aliased_function from .submodule import not_in_all_function __all__ = ( "aliased_function", "function", ) def function(foo, bar): """A module level function""" submodule.py000066400000000000000000000013651463504067600345010ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/_private_subpackage/subpackage"""Example module This is a description """ DATA = 42 def function(foo, bar): """A module level function""" def _private_function(): """A function that shouldn't get rendered.""" def not_in_all_function(): """A function that doesn't exist in __all__ when imported.""" class Class(object): """This is a class.""" class_var = 42 """Class var docstring""" class NestedClass(object): """A nested class just to test things out""" @classmethod def a_classmethod(): """A class method""" return True def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True class MyException(Exception): """This is an exception.""" sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/submodule.py000066400000000000000000000036701463504067600264560ustar00rootroot00000000000000"""Example module This is a description """ from .subpackage.submodule import function as aliased_function from .subpackage.submodule import not_in_all_function __all__ = ( "aliased_function", "Class", "DATA", "function", "MyException", ) DATA = 42 def function(foo, bar): """A module level function""" class Class(object): """This is a class.""" class_var = 42 """Class var docstring""" class NestedClass(object): """A nested class just to test things out""" @classmethod def a_classmethod(): """A class method""" return True def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True def method_multiline(self, foo=None, bar=None, baz=None): """This is on multiple lines, but should parse okay too pydocstyle gives us lines of source. Test if this means that multiline definitions are covered in the way we're anticipating here """ return True def method_tricky(self, foo=None, bar=dict(foo=1, bar=2)): """This will likely fail our argument testing We parse naively on commas, so the nested dictionary will throw this off """ return True def method_sphinx_docs(self, foo, bar=0): """This method is documented with sphinx style docstrings. :param foo: The first argument. :type foo: int :param int bar: The second argument. :returns: The sum of foo and bar. :rtype: int """ return foo + bar def method_google_docs(self, foo, bar=0): """This method is documented with google style docstrings. Args: foo (int): The first argument. bar (int): The second argument. Returns: int: The sum of foo and bar. """ return foo + bar class MyException(Exception): """This is an exception.""" sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/subpackage/000077500000000000000000000000001463504067600262045ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/subpackage/__init__.py000066400000000000000000000003561463504067600303210ustar00rootroot00000000000000"""This is a docstring.""" from .submodule import function as aliased_function from .submodule import not_in_all_function __all__ = ( "aliased_function", "function", ) def function(foo, bar): """A module level function""" sphinx-autoapi-3.1.2/tests/python/pypackageexample/package/subpackage/submodule.py000066400000000000000000000013651463504067600305620ustar00rootroot00000000000000"""Example module This is a description """ DATA = 42 def function(foo, bar): """A module level function""" def _private_function(): """A function that shouldn't get rendered.""" def not_in_all_function(): """A function that doesn't exist in __all__ when imported.""" class Class(object): """This is a class.""" class_var = 42 """Class var docstring""" class NestedClass(object): """A nested class just to test things out""" @classmethod def a_classmethod(): """A class method""" return True def method_okay(self, foo=None, bar=None): """This method should parse okay""" return True class MyException(Exception): """This is an exception.""" sphinx-autoapi-3.1.2/tests/python/pyskipexample/000077500000000000000000000000001463504067600220575ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyskipexample/conf.py000066400000000000000000000014361463504067600233620ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" htmlhelp_basename = "pyexampledoc" extensions = ["sphinx.ext.autodoc", "autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" autoapi_options = ["members", "undoc-members", "special-members"] SKIP = {"example.foo", "example.Bar", "example.Bar.m", "example.Baf.m", "example.baz"} def maybe_skip_member(app, what, name, obj, skip, options): return name in SKIP def setup(app): app.connect("autoapi-skip-member", maybe_skip_member) sphinx-autoapi-3.1.2/tests/python/pyskipexample/example/000077500000000000000000000000001463504067600235125ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/python/pyskipexample/example/example.py000066400000000000000000000005561463504067600255250ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Example module This is a description """ def foo(): """foo doc""" pass class Bar: """bar doc""" def m(self): """bar m doc""" pass class Baf: """baf doc""" def m(self): """baf m doc""" baz = 100 """baz doc""" anchor = "value" """must be in result document because not ignored""" sphinx-autoapi-3.1.2/tests/python/pyskipexample/index.rst000066400000000000000000000007171463504067600237250ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/python/test_own_page_option.py000066400000000000000000001203301463504067600237630ustar00rootroot00000000000000import os import pytest class TestModule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pypackageexample", warningiserror=True, confoverrides={ "autoapi_own_page_level": "module", "autoapi_options": [ "members", "undoc-members", "show-inheritance", "imported-members", ], }, ) def test_package(self, parse): package_path = "_build/html/autoapi/package/index.html" package_file = parse(package_path) docstring = package_file.find("p") assert docstring.text == "This is a docstring." # There should be links to the children with their own page subpackages = package_file.find(id="subpackages") assert subpackages assert subpackages.find("a", string="package.subpackage") submodules = package_file.find(id="submodules") assert submodules assert submodules.find("a", string="package.submodule") # There should not be links to the children without their own page assert not package_file.find(id="attributes") assert not package_file.find(id="exceptions") assert not package_file.find(id="classes") assert not package_file.find(id="functions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = package_file.find(id="package-contents") assert contents.find(id="package.DATA") assert contents.find(id="package.MyException") assert contents.find(id="package.Class") assert contents.find(id="package.Class.class_var") assert contents.find(id="package.Class.NestedClass") assert contents.find(id="package.Class.method_okay") assert contents.find(id="package.Class.NestedClass") assert contents.find(id="package.Class.NestedClass.a_classmethod") assert contents.find(id="package.function") assert contents.find(id="package.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.not_in_all_function") def test_subpackage(self, parse): subpackage_path = "_build/html/autoapi/package/subpackage/index.html" subpackage_file = parse(subpackage_path) docstring = subpackage_file.find("p") assert docstring.text == "This is a docstring." # There should be links to the children with their own page assert not subpackage_file.find(id="subpackages") submodules = subpackage_file.find(id="submodules") assert submodules assert submodules.find("a", string="package.subpackage.submodule") # There should not be links to the children without their own page assert not subpackage_file.find(id="attributes") assert not subpackage_file.find(id="exceptions") assert not subpackage_file.find(id="classes") assert not subpackage_file.find(id="functions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = subpackage_file.find(id="package-contents") assert contents.find(id="package.subpackage.function") assert contents.find(id="package.subpackage.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.subpackage.not_in_all_function") def test_module(self, parse): submodule_path = "_build/html/autoapi/package/submodule/index.html" submodule_file = parse(submodule_path) docstring = submodule_file.find("p") assert docstring.text == "Example module" # There should be links to the children with their own page pass # there are no children with their own page # There should not be links to the children without their own page assert not submodule_file.find(id="submodules") assert not submodule_file.find(id="subpackages") assert not submodule_file.find(id="attributes") assert not submodule_file.find(id="exceptions") assert not submodule_file.find(id="classes") assert not submodule_file.find(id="functions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = submodule_file.find(id="module-contents") assert contents.find(id="package.submodule.DATA") assert contents.find(id="package.submodule.MyException") assert contents.find(id="package.submodule.Class") assert contents.find(id="package.submodule.Class.class_var") assert contents.find(id="package.submodule.Class.NestedClass") assert contents.find(id="package.submodule.Class.method_okay") assert contents.find(id="package.submodule.Class.NestedClass") assert contents.find(id="package.submodule.Class.NestedClass.a_classmethod") assert contents.find(id="package.submodule.function") assert contents.find(id="package.submodule.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.submodule.not_in_all_function") def test_rendered_only_expected_pages(self): _, dirs, files = next(os.walk("_build/html/autoapi/package")) assert sorted(dirs) == ["submodule", "subpackage"] assert files == ["index.html"] _, dirs, files = next(os.walk("_build/html/autoapi/package/submodule")) assert not dirs assert files == ["index.html"] _, dirs, files = next(os.walk("_build/html/autoapi/package/subpackage")) assert dirs == ["submodule"] assert files == ["index.html"] _, dirs, files = next( os.walk("_build/html/autoapi/package/subpackage/submodule") ) assert not dirs assert files == ["index.html"] def test_index(self, parse): index_path = "_build/html/autoapi/index.html" index_file = parse(index_path) top_links = index_file.find_all(class_="toctree-l1") top_hrefs = sorted(link.a["href"] for link in top_links) assert top_hrefs == [ "#", "package/index.html", ] class TestClass: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pypackageexample", warningiserror=True, confoverrides={ "autoapi_own_page_level": "class", "autoapi_options": [ "members", "undoc-members", "show-inheritance", "imported-members", ], }, ) def test_package(self, parse): package_path = "_build/html/autoapi/package/index.html" package_file = parse(package_path) docstring = package_file.find("p") assert docstring.text == "This is a docstring." # There should be links to the children with their own page subpackages = package_file.find(id="subpackages") assert subpackages assert subpackages.find("a", string="package.subpackage") submodules = package_file.find(id="submodules") assert submodules assert submodules.find("a", string="package.submodule") exceptions = package_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.MyException") classes = package_file.find(id="classes") assert classes assert classes.find("a", title="package.Class") assert not classes.find("a", title="package.Class.NestedClass") # There should not be links to the children without their own page assert not package_file.find(id="attributes") assert not package_file.find(id="functions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = package_file.find(id="package-contents") assert contents.find(id="package.DATA") assert not contents.find(id="package.MyException") assert not contents.find(id="package.Class") assert not contents.find(id="package.Class.class_var") assert not contents.find(id="package.Class.NestedClass") assert not contents.find(id="package.Class.method_okay") assert not contents.find(id="package.Class.NestedClass") assert not contents.find(id="package.Class.NestedClass.a_classmethod") assert contents.find(id="package.function") assert contents.find(id="package.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.not_in_all_function") def test_module(self, parse): submodule_path = "_build/html/autoapi/package/submodule/index.html" submodule_file = parse(submodule_path) docstring = submodule_file.find("p") assert docstring.text == "Example module" # There should be links to the children with their own page exceptions = submodule_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.submodule.MyException") classes = submodule_file.find(id="classes") assert classes assert classes.find("a", title="package.submodule.Class") assert not classes.find("a", title="package.submodule.Class.NestedClass") # There should not be links to the children without their own page assert not submodule_file.find(id="submodules") assert not submodule_file.find(id="subpackages") assert not submodule_file.find(id="attributes") assert not submodule_file.find(id="functions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = submodule_file.find(id="module-contents") assert contents.find(id="package.submodule.DATA") assert not contents.find(id="package.submodule.MyException") assert not contents.find(id="package.submodule.Class") assert not contents.find(id="package.submodule.Class.class_var") assert not contents.find(id="package.submodule.Class.NestedClass") assert not contents.find(id="package.submodule.Class.method_okay") assert not contents.find(id="package.submodule.Class.NestedClass") assert not contents.find(id="package.submodule.Class.NestedClass.a_classmethod") assert contents.find(id="package.submodule.function") assert contents.find(id="package.submodule.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.submodule.not_in_all_function") def test_class(self, parse): class_path = "_build/html/autoapi/package/Class.html" class_file = parse(class_path) class_sig = class_file.find(id="package.Class") assert class_sig class_ = class_sig.parent docstring = class_.find_all("p")[1] assert docstring.text == "This is a class." # There should be links to the children with their own page classes = class_file.find(id="classes") assert classes assert classes.find("a", title="package.Class.NestedClass") # There should not be links to the children without their own page assert not class_file.find(id="attributes") assert not class_file.find(id="exceptions") assert not class_file.find(id="methods") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. assert class_.find(id="package.Class.class_var") assert class_.find(id="package.Class.method_okay") nested_class_path = "_build/html/autoapi/package/Class.NestedClass.html" nested_class_file = parse(nested_class_path) nested_class_sig = nested_class_file.find(id="package.Class.NestedClass") assert nested_class_sig nested_class = nested_class_sig.parent # There should be links to the children with their own page pass # there are no children with their own page # There should not be links to the children without their own page assert not class_file.find(id="attributes") assert not class_file.find(id="exceptions") assert not class_file.find(id="methods") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. assert nested_class.find(id="package.Class.NestedClass.a_classmethod") def test_exception(self, parse): exception_path = "_build/html/autoapi/package/MyException.html" exception_file = parse(exception_path) exception_sig = exception_file.find(id="package.MyException") assert exception_sig exception = exception_sig.parent docstring = exception.find_all("p")[1] assert docstring.text == "This is an exception." # There should be links to the children with their own page pass # there are no children with their own page # There should not be links to the children without their own page assert not exception_file.find(id="attributes") assert not exception_file.find(id="exceptions") assert not exception_file.find(id="methods") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. pass # there are no children without their own page def test_rendered_only_expected_pages(self): _, dirs, files = next(os.walk("_build/html/autoapi/package")) assert sorted(dirs) == ["submodule", "subpackage"] assert sorted(files) == [ "Class.NestedClass.html", "Class.html", "MyException.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/submodule")) assert not dirs assert sorted(files) == [ "Class.NestedClass.html", "Class.html", "MyException.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/subpackage")) assert dirs == ["submodule"] assert files == ["index.html"] _, dirs, files = next( os.walk("_build/html/autoapi/package/subpackage/submodule") ) assert not dirs assert sorted(files) == [ "Class.NestedClass.html", "Class.html", "MyException.html", "index.html", ] def test_index(self, parse): index_path = "_build/html/autoapi/index.html" index_file = parse(index_path) top_links = index_file.find_all(class_="toctree-l1") top_hrefs = sorted(link.a["href"] for link in top_links) assert top_hrefs == [ "#", "package/index.html", ] class TestFunction: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pypackageexample", warningiserror=True, confoverrides={ "autoapi_own_page_level": "function", "autoapi_options": [ "members", "undoc-members", "show-inheritance", "imported-members", ], }, ) def test_package(self, parse): package_path = "_build/html/autoapi/package/index.html" package_file = parse(package_path) docstring = package_file.find("p") assert docstring.text == "This is a docstring." # There should be links to the children with their own page subpackages = package_file.find(id="subpackages") assert subpackages assert subpackages.find("a", string="package.subpackage") submodules = package_file.find(id="submodules") assert submodules assert submodules.find("a", string="package.submodule") classes = package_file.find(id="classes") assert classes assert classes.find("a", title="package.Class") exceptions = package_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.MyException") functions = package_file.find(id="functions") assert functions assert functions.find("a", title="package.function") # There should not be links to the children without their own page assert not package_file.find(id="attributes") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = package_file.find(id="package-contents") assert contents.find(id="package.DATA") assert not contents.find(id="package.MyException") assert not contents.find(id="package.Class") assert not contents.find(id="package.Class.class_var") assert not contents.find(id="package.Class.NestedClass") assert not contents.find(id="package.Class.method_okay") assert not contents.find(id="package.Class.NestedClass") assert not contents.find(id="package.Class.NestedClass.a_classmethod") assert not contents.find(id="package.function") assert not contents.find(id="package.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.not_in_all_function") def test_module(self, parse): submodule_path = "_build/html/autoapi/package/submodule/index.html" submodule_file = parse(submodule_path) docstring = submodule_file.find("p") assert docstring.text == "Example module" # There should be links to the children with their own page exceptions = submodule_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.submodule.MyException") classes = submodule_file.find(id="classes") assert classes assert classes.find("a", title="package.submodule.Class") assert not classes.find("a", title="package.submodule.Class.NestedClass") functions = submodule_file.find(id="functions") assert functions assert functions.find("a", title="package.submodule.function") # There should not be links to the children without their own page assert not submodule_file.find(id="submodules") assert not submodule_file.find(id="subpackages") assert not submodule_file.find(id="attributes") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = submodule_file.find(id="module-contents") assert contents.find(id="package.submodule.DATA") assert not contents.find(id="package.submodule.MyException") assert not contents.find(id="package.submodule.Class") assert not contents.find(id="package.submodule.Class.class_var") assert not contents.find(id="package.submodule.Class.NestedClass") assert not contents.find(id="package.submodule.Class.method_okay") assert not contents.find(id="package.submodule.Class.NestedClass") assert not contents.find(id="package.submodule.Class.NestedClass.a_classmethod") assert not contents.find(id="package.submodule.function") assert not contents.find(id="package.submodule.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.submodule.not_in_all_function") def test_class(self, parse): class_path = "_build/html/autoapi/package/Class.html" class_file = parse(class_path) class_sig = class_file.find(id="package.Class") assert class_sig class_ = class_sig.parent docstring = class_.find_all("p")[1] assert docstring.text == "This is a class." # There should be links to the children with their own page classes = class_file.find(id="classes") assert classes assert classes.find("a", title="package.Class.NestedClass") # There should not be links to the children without their own page assert not class_file.find(id="attributes") assert not class_file.find(id="exceptions") assert not class_file.find(id="methods") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. assert class_.find(id="package.Class.class_var") assert class_.find(id="package.Class.method_okay") def test_function(self, parse): function_path = "_build/html/autoapi/package/function.html" function_file = parse(function_path) function_sig = function_file.find(id="package.function") assert function_sig function_path = "_build/html/autoapi/package/submodule/function.html" function_file = parse(function_path) assert function_file.find(id="package.submodule.function") def test_rendered_only_expected_pages(self): _, dirs, files = next(os.walk("_build/html/autoapi/package")) assert sorted(dirs) == ["submodule", "subpackage"] assert sorted(files) == [ "Class.NestedClass.html", "Class.html", "MyException.html", "aliased_function.html", "function.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/submodule")) assert not dirs assert sorted(files) == [ "Class.NestedClass.html", "Class.html", "MyException.html", "aliased_function.html", "function.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/subpackage")) assert dirs == ["submodule"] assert sorted(files) == ["aliased_function.html", "function.html", "index.html"] _, dirs, files = next( os.walk("_build/html/autoapi/package/subpackage/submodule") ) assert not dirs assert sorted(files) == [ "Class.NestedClass.html", "Class.html", "MyException.html", "function.html", "index.html", "not_in_all_function.html", ] def test_index(self, parse): index_path = "_build/html/autoapi/index.html" index_file = parse(index_path) top_links = index_file.find_all(class_="toctree-l1") top_hrefs = sorted(link.a["href"] for link in top_links) assert top_hrefs == [ "#", "package/index.html", ] class TestMethod: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pypackageexample", warningiserror=True, confoverrides={ "autoapi_own_page_level": "method", "autoapi_options": [ "members", "undoc-members", "show-inheritance", "imported-members", ], }, ) def test_package(self, parse): package_path = "_build/html/autoapi/package/index.html" package_file = parse(package_path) docstring = package_file.find("p") assert docstring.text == "This is a docstring." # There should be links to the children with their own page subpackages = package_file.find(id="subpackages") assert subpackages assert subpackages.find("a", string="package.subpackage") submodules = package_file.find(id="submodules") assert submodules assert submodules.find("a", string="package.submodule") classes = package_file.find(id="classes") assert classes assert classes.find("a", title="package.Class") exceptions = package_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.MyException") functions = package_file.find(id="functions") assert functions assert functions.find("a", title="package.function") # There should not be links to the children without their own page assert not package_file.find(id="attributes") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = package_file.find(id="package-contents") assert contents.find(id="package.DATA") assert not contents.find(id="package.MyException") assert not contents.find(id="package.Class") assert not contents.find(id="package.Class.class_var") assert not contents.find(id="package.Class.NestedClass") assert not contents.find(id="package.Class.method_okay") assert not contents.find(id="package.Class.NestedClass") assert not contents.find(id="package.Class.NestedClass.a_classmethod") assert not contents.find(id="package.function") assert not contents.find(id="package.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.not_in_all_function") def test_module(self, parse): submodule_path = "_build/html/autoapi/package/submodule/index.html" submodule_file = parse(submodule_path) docstring = submodule_file.find("p") assert docstring.text == "Example module" # There should be links to the children with their own page exceptions = submodule_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.submodule.MyException") classes = submodule_file.find(id="classes") assert classes assert classes.find("a", title="package.submodule.Class") assert not classes.find("a", title="package.submodule.Class.NestedClass") functions = submodule_file.find(id="functions") assert functions assert functions.find("a", title="package.submodule.function") # There should not be links to the children without their own page assert not submodule_file.find(id="submodules") assert not submodule_file.find(id="subpackages") assert not submodule_file.find(id="attributes") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. contents = submodule_file.find(id="module-contents") assert contents.find(id="package.submodule.DATA") assert not contents.find(id="package.submodule.MyException") assert not contents.find(id="package.submodule.Class") assert not contents.find(id="package.submodule.Class.class_var") assert not contents.find(id="package.submodule.Class.NestedClass") assert not contents.find(id="package.submodule.Class.method_okay") assert not contents.find(id="package.submodule.Class.NestedClass") assert not contents.find(id="package.submodule.Class.NestedClass.a_classmethod") assert not contents.find(id="package.submodule.function") assert not contents.find(id="package.submodule.aliased_function") # Hidden children are never rendered. assert not contents.find(id="package.submodule.not_in_all_function") def test_class(self, parse): class_path = "_build/html/autoapi/package/Class.html" class_file = parse(class_path) class_sig = class_file.find(id="package.Class") assert class_sig class_ = class_sig.parent docstring = class_.find_all("p")[1] assert docstring.text == "This is a class." # There should be links to the children with their own page classes = class_file.find(id="classes") assert classes assert classes.find("a", title="package.Class.NestedClass") methods = class_file.find(id="methods") assert methods assert methods.find("a", title="package.Class.method_okay") # There should not be links to the children without their own page assert not class_file.find(id="attributes") assert not class_file.find(id="exceptions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. assert class_.find(id="package.Class.class_var") assert not class_.find(id="package.Class.method_okay") def test_function(self, parse): function_path = "_build/html/autoapi/package/function.html" function_file = parse(function_path) function_sig = function_file.find(id="package.function") assert function_sig function_path = "_build/html/autoapi/package/submodule/function.html" function_file = parse(function_path) assert function_file.find(id="package.submodule.function") def test_method(self, parse): method_path = "_build/html/autoapi/package/Class.method_okay.html" method_file = parse(method_path) method_sig = method_file.find(id="package.Class.method_okay") assert method_sig method_path = "_build/html/autoapi/package/submodule/Class.method_okay.html" method_file = parse(method_path) assert method_file.find(id="package.submodule.Class.method_okay") def test_rendered_only_expected_pages(self): _, dirs, files = next(os.walk("_build/html/autoapi/package")) assert sorted(dirs) == ["submodule", "subpackage"] assert sorted(files) == [ "Class.NestedClass.a_classmethod.html", "Class.NestedClass.html", "Class.html", "Class.method_okay.html", "MyException.html", "aliased_function.html", "function.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/submodule")) assert not dirs assert sorted(files) == [ "Class.NestedClass.a_classmethod.html", "Class.NestedClass.html", "Class.html", "Class.method_google_docs.html", "Class.method_multiline.html", "Class.method_okay.html", "Class.method_sphinx_docs.html", "Class.method_tricky.html", "MyException.html", "aliased_function.html", "function.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/subpackage")) assert dirs == ["submodule"] assert sorted(files) == ["aliased_function.html", "function.html", "index.html"] _, dirs, files = next( os.walk("_build/html/autoapi/package/subpackage/submodule") ) assert not dirs assert sorted(files) == [ "Class.NestedClass.a_classmethod.html", "Class.NestedClass.html", "Class.html", "Class.method_okay.html", "MyException.html", "function.html", "index.html", "not_in_all_function.html", ] def test_index(self, parse): index_path = "_build/html/autoapi/index.html" index_file = parse(index_path) top_links = index_file.find_all(class_="toctree-l1") top_hrefs = sorted(link.a["href"] for link in top_links) assert top_hrefs == [ "#", "package/index.html", ] class TestAttribute: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pypackageexample", warningiserror=True, confoverrides={ "autoapi_own_page_level": "attribute", "autoapi_options": [ "members", "undoc-members", "show-inheritance", "imported-members", ], }, ) # TODO: Include a test for a property def test_package(self, parse): package_path = "_build/html/autoapi/package/index.html" package_file = parse(package_path) docstring = package_file.find("p") assert docstring.text == "This is a docstring." # There should be links to the children with their own page subpackages = package_file.find(id="subpackages") assert subpackages assert subpackages.find("a", string="package.subpackage") submodules = package_file.find(id="submodules") assert submodules assert submodules.find("a", string="package.submodule") classes = package_file.find(id="classes") assert classes assert classes.find("a", title="package.Class") exceptions = package_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.MyException") functions = package_file.find(id="functions") assert functions assert functions.find("a", title="package.function") attributes = package_file.find(id="attributes") assert attributes assert attributes.find("a", title="package.DATA") # There should not be links to the children without their own page pass # there are no children without their own page # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. # Hidden children are never rendered. assert not package_file.find(id="package-contents") def test_module(self, parse): submodule_path = "_build/html/autoapi/package/submodule/index.html" submodule_file = parse(submodule_path) docstring = submodule_file.find("p") assert docstring.text == "Example module" # There should be links to the children with their own page exceptions = submodule_file.find(id="exceptions") assert exceptions assert exceptions.find("a", title="package.submodule.MyException") classes = submodule_file.find(id="classes") assert classes assert classes.find("a", title="package.submodule.Class") assert not classes.find("a", title="package.submodule.Class.NestedClass") functions = submodule_file.find(id="functions") assert functions assert functions.find("a", title="package.submodule.function") attributes = submodule_file.find(id="attributes") assert attributes assert attributes.find("a", title="package.submodule.DATA") # There should not be links to the children without their own page assert not submodule_file.find(id="submodules") assert not submodule_file.find(id="subpackages") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. # Hidden children are never rendered. assert not submodule_file.find(id="module-contents") def test_class(self, parse): class_path = "_build/html/autoapi/package/Class.html" class_file = parse(class_path) class_sig = class_file.find(id="package.Class") assert class_sig class_ = class_sig.parent docstring = class_.find_all("p")[1] assert docstring.text == "This is a class." # There should be links to the children with their own page classes = class_file.find(id="classes") assert classes assert classes.find("a", title="package.Class.NestedClass") methods = class_file.find(id="methods") assert methods assert methods.find("a", title="package.Class.method_okay") attributes = class_file.find(id="attributes") assert attributes assert attributes.find("a", title="package.Class.class_var") # There should not be links to the children without their own page assert not class_file.find(id="exceptions") # Children without their own page should be rendered on this page, # and children with their own page should not be rendered on this page. assert not class_.find(id="package.Class.class_var") assert not class_.find(id="package.Class.method_okay") def test_function(self, parse): function_path = "_build/html/autoapi/package/function.html" function_file = parse(function_path) function_sig = function_file.find(id="package.function") assert function_sig function_path = "_build/html/autoapi/package/submodule/function.html" function_file = parse(function_path) assert function_file.find(id="package.submodule.function") def test_method(self, parse): method_path = "_build/html/autoapi/package/Class.method_okay.html" method_file = parse(method_path) method_sig = method_file.find(id="package.Class.method_okay") assert method_sig method_path = "_build/html/autoapi/package/submodule/Class.method_okay.html" method_file = parse(method_path) assert method_file.find(id="package.submodule.Class.method_okay") def test_data(self, parse): data_path = "_build/html/autoapi/package/DATA.html" data_file = parse(data_path) data_sig = data_file.find(id="package.DATA") assert data_sig def test_attribute(self, parse): attribute_path = "_build/html/autoapi/package/Class.class_var.html" attribute_file = parse(attribute_path) attribute_sig = attribute_file.find(id="package.Class.class_var") assert attribute_sig def test_rendered_only_expected_pages(self): _, dirs, files = next(os.walk("_build/html/autoapi/package")) assert sorted(dirs) == ["submodule", "subpackage"] assert sorted(files) == [ "Class.NestedClass.a_classmethod.html", "Class.NestedClass.html", "Class.class_var.html", "Class.html", "Class.method_okay.html", "DATA.html", "MyException.html", "aliased_function.html", "function.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/submodule")) assert not dirs assert sorted(files) == [ "Class.NestedClass.a_classmethod.html", "Class.NestedClass.html", "Class.class_var.html", "Class.html", "Class.method_google_docs.html", "Class.method_multiline.html", "Class.method_okay.html", "Class.method_sphinx_docs.html", "Class.method_tricky.html", "DATA.html", "MyException.html", "aliased_function.html", "function.html", "index.html", ] _, dirs, files = next(os.walk("_build/html/autoapi/package/subpackage")) assert dirs == ["submodule"] assert sorted(files) == ["aliased_function.html", "function.html", "index.html"] _, dirs, files = next( os.walk("_build/html/autoapi/package/subpackage/submodule") ) assert not dirs assert sorted(files) == [ "Class.NestedClass.a_classmethod.html", "Class.NestedClass.html", "Class.class_var.html", "Class.html", "Class.method_okay.html", "DATA.html", "MyException.html", "function.html", "index.html", "not_in_all_function.html", ] def test_index(self, parse): index_path = "_build/html/autoapi/index.html" index_file = parse(index_path) top_links = index_file.find_all(class_="toctree-l1") top_hrefs = sorted(link.a["href"] for link in top_links) assert top_hrefs == [ "#", "package/index.html", ] @pytest.mark.parametrize( "value", ["package", "exception", "property", "data", "not_a_value"] ) def test_invalid_values(builder, value): """Test failure when autoapi_own_page_level is invalid.""" with pytest.raises(ValueError): builder( "pypackageexample", confoverrides={ "autoapi_own_page_level": value, }, ) sphinx-autoapi-3.1.2/tests/python/test_parser.py000066400000000000000000000072771463504067600221060ustar00rootroot00000000000000"""Test Python parser""" import astroid from autoapi._parser import Parser class TestPythonParser: def parse(self, source): node = astroid.extract_node(source) return Parser().parse(node) def test_parses_basic_file(self): source = """ def foo(bar): pass """ data = self.parse(source)[0] assert data["name"] == "foo" assert data["type"] == "function" def test_parses_all(self): source = """ __all__ = ['Foo', 5.0] """ data = self.parse(source)[0] assert data["name"] == "__all__" assert data["value"] == "['Foo', 5.0]" def test_parses_all_multiline(self): source = """ __all__ = [ 'foo', 'bar', ] """ data = self.parse(source)[0] assert data["value"] == "['foo', 'bar']" def test_parses_name(self): source = "foo.bar" assert self.parse(source) == {} def test_parses_list(self): name = "__all__" value = "[1, 2, 3, 4]" source = "{} = {}".format(name, value) data = self.parse(source)[0] assert data["name"] == name assert data["value"] == value def test_parses_nested_list(self): name = "__all__" value = "[[1, 2], [3, 4]]" source = "{} = {}".format(name, value) data = self.parse(source)[0] assert data["name"] == name assert data["value"] == value def test_arguments(self): """Argument parsing of source""" source = ( "def foobar(self, bar, baz=42, foo=True,\n" " *args, **kwargs):\n" ' "This is a docstring"\n' " return True\n" ) data = self.parse(source)[0] expected = [ (None, "self", None, None), (None, "bar", None, None), (None, "baz", None, "42"), (None, "foo", None, "True"), ("*", "args", None, None), ("**", "kwargs", None, None), ] assert data["args"] == expected def test_advanced_arguments(self): """Advanced argument parsing""" source = ( 'def foobar(self, a, b, c=42, d="string", e=(1,2),\n' ' f={"a": True}, g=None, h=[1,2,3,4],\n' " i=dict(a=True), j=False, *args, **kwargs):\n" ' "This is a docstring"\n' " return True\n" ) data = self.parse(source)[0] expected = [ (None, "self", None, None), (None, "a", None, None), (None, "b", None, None), (None, "c", None, "42"), (None, "d", None, "'string'"), (None, "e", None, "(1, 2)"), (None, "f", None, "{'a': True}"), (None, "g", None, "None"), (None, "h", None, "[1, 2, 3, 4]"), (None, "i", None, "dict(a=True)"), (None, "j", None, "False"), ("*", "args", None, None), ("**", "kwargs", None, None), ] assert data["args"] == expected def test_dict_key_assignment(self): """Ignore assignment to dictionary entries.""" source = """ MY_DICT = {} #@ if condition: MY_DICT['key'] = 'value' MY_DICT['key2'] = 'value2' """ data = self.parse(source)[0] assert data["name"] == "MY_DICT" def test_list_index_assignment(self): """Ignore assignment to indexes.""" source = """ COLOUR = [255, 128, 0] #@ if condition: COLOUR[1] = 255 COLOUR[2] = 255 """ data = self.parse(source)[0] assert data["name"] == "COLOUR" sphinx-autoapi-3.1.2/tests/python/test_pyintegration.py000066400000000000000000001243311463504067600234750ustar00rootroot00000000000000import io import os import pathlib import sys from unittest.mock import Mock, call import autoapi.settings from autoapi._objects import ( PythonClass, PythonData, PythonFunction, PythonMethod, PythonModule, ) from packaging import version import pytest import sphinx from sphinx.application import Sphinx from sphinx.errors import ExtensionError import sphinx.util.logging sphinx_version = version.parse(sphinx.__version__).release class TestSimpleModule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pyexample", warningiserror=True, confoverrides={"exclude_patterns": ["manualapi.rst"]}, ) def test_integration(self, parse): self.check_integration(parse, "_build/html/autoapi/example/index.html") index_file = parse("_build/html/index.html") toctree = index_file.select("li > a") assert any(item.text == "API Reference" for item in toctree) def check_integration(self, parse, example_path): example_file = parse(example_path) foo_sig = example_file.find(id="example.Foo") assert foo_sig assert foo_sig.find(class_="sig-name").text == "Foo" assert foo_sig.find(class_="sig-param").text == "attr" # Check that nested classes are documented foo = foo_sig.parent assert foo.find(id="example.Foo.Meta") # Check that class attributes are documented attr2 = foo.find(id="example.Foo.attr2") assert "attr2" in attr2.text # Check that attribute docstrings are used assert attr2.parent.find("dd").text.startswith( "This is the docstring of an instance attribute." ) method_okay = foo.find(id="example.Foo.method_okay") assert method_okay args = method_okay.find_all(class_="sig-param") assert len(args) == 2 assert args[0].text == "foo=None" assert args[1].text == "bar=None" method_multiline = foo.find(id="example.Foo.method_multiline") assert method_multiline args = method_multiline.find_all(class_="sig-param") assert len(args) == 3 assert args[0].text == "foo=None" assert args[1].text == "bar=None" assert args[2].text == "baz=None" method_tricky = foo.find(id="example.Foo.method_tricky") assert method_tricky args = method_tricky.find_all(class_="sig-param") assert len(args) == 2 assert args[0].text == "foo=None" assert args[1].text == "bar=dict(foo=1, bar=2)" # Are constructor arguments from the class docstring parsed? init_args = foo.parent.find_next(class_="field-list") assert "Set an attribute" in init_args.text # "self" should not be included in constructor arguments assert len(foo_sig.find_all(class_="sig-param")) == 1 property_simple = foo.find(id="example.Foo.property_simple") assert property_simple assert ( property_simple.parent.find("dd").text.strip() == "This property should parse okay." ) # Overridden methods without their own docstring # should inherit the parent's docstring bar_method_okay = example_file.find(id="example.Bar.method_okay") assert ( bar_method_okay.parent.find("dd").text.strip() == "This method should parse okay" ) assert not os.path.exists("_build/html/autoapi/method_multiline") # Inherited constructor docstrings should be included in a merged # (autoapi_python_class_content="both") class docstring only once. two = example_file.find(id="example.Two") assert two.parent.find("dd").text.count("One __init__") == 1 # Tuples should be rendered as tuples, not lists a_tuple = example_file.find(id="example.A_TUPLE") assert a_tuple.find(class_="property").text.endswith("('a', 'b')") # Lists should be rendered as lists, not tuples a_list = example_file.find(id="example.A_LIST") assert a_list.find(class_="property").text.endswith("['c', 'd']") # Assigning a class level attribute at the module level # should not get documented as a module level attribute. assert "dinglebop" not in example_file.text index_file = parse("_build/html/index.html") toctree = index_file.select("li > a") assert any(item.text == "Foo" for item in toctree) assert any(item.text == "Foo.Meta" for item in toctree) def test_napoleon_integration_not_loaded(self, parse): example_file = parse("_build/html/autoapi/example/index.html") # Check that docstrings are not transformed without napoleon loaded method_google = example_file.find(id="example.Foo.method_google_docs") assert "Args" in method_google.parent.find("dd").text assert "Returns" in method_google.parent.find("dd").text def test_show_inheritance(self, parse): example_file = parse("_build/html/autoapi/example/index.html") foo = example_file.find(id="example.Foo") foo_docstring = foo.parent.find("dd").contents[0] assert foo_docstring.text.startswith("Bases:") def test_long_signature(self, parse): example_file = parse("_build/html/autoapi/example/index.html") summary_row = example_file.find_all(class_="autosummary")[-1].find_all("tr")[-1] assert summary_row cells = summary_row.find_all("td") assert ( cells[0].text.replace("\xa0", " ") == "fn_with_long_sig(this, *[, function, has, quite])" ) assert cells[1].text.strip() == "A function with a long signature." class TestSimpleModuleManual: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pyexample", warningiserror=True, confoverrides={ "autoapi_generate_api_docs": False, "autoapi_add_toctree_entry": False, }, ) def test_manual_directives(self, parse): example_file = parse("_build/html/manualapi.html") assert example_file.find(id="example.decorator_okay") class TestMovedConfPy(TestSimpleModule): @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pymovedconfpy", confdir="confpy", warningiserror=True, confoverrides={"exclude_patterns": ["manualapi.rst"]}, ) class TestSimpleModuleDifferentPrimaryDomain(TestSimpleModule): @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pyexample", warningiserror=True, confoverrides={ "exclude_patterns": ["manualapi.rst"], "primary_domain": "cpp", }, ) class TestSimpleStubModule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pyiexample", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") # Are pyi files preferred assert "DoNotFindThis" not in example_file foo_sig = example_file.find(id="example.Foo") assert foo_sig foo = foo_sig.parent assert foo.find(id="example.Foo.Meta") class_var = foo.find(id="example.Foo.another_class_var") class_var_docstring = class_var.parent.find("dd").contents[0].text assert class_var_docstring.strip() == "Another class var docstring" class_var = foo.find(id="example.Foo.class_var_without_value") class_var_docstring = class_var.parent.find("dd").contents[0].text assert class_var_docstring.strip() == "A class var without a value." method_okay = foo.find(id="example.Foo.method_okay") assert method_okay method_multiline = foo.find(id="example.Foo.method_multiline") assert method_multiline method_without_docstring = foo.find(id="example.Foo.method_without_docstring") assert method_without_docstring # Are constructor arguments from the class docstring parsed? init_args = foo.parent.find_next(class_="field-list") assert "Set an attribute" in init_args.text class TestSimpleStubModuleNotPreferred: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pyiexample2", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") # Are py files preferred assert "DoNotFindThis" not in example_file foo_sig = example_file.find(id="example.Foo") assert foo_sig class TestStubInitModuleInSubmodule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pyisubmoduleinit", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") # Documentation should list # submodule_foo instead of __init__ assert example_file.find(title="submodule_foo") assert not example_file.find(title="__init__") class TestPy3Module: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("py3example") def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") assert "Initialize self" not in example_file assert "a new type" not in example_file def test_annotations(self, parse): example_file = parse("_build/html/autoapi/example/index.html") software = example_file.find(id="example.software") assert software software_value = software.find(class_="property").contents[-1] assert software_value.text.endswith('''"sphin'x"''') more_software = example_file.find(id="example.more_software") assert more_software more_software_value = more_software.find(class_="property").contents[-1] assert more_software_value.text.endswith("""'sphinx"autoapi'""") interesting = example_file.find(id="example.interesting_string") assert interesting interesting_value = interesting.find(class_="property").contents[-1] assert interesting_value.text.endswith("'interesting\"fun\\'\\\\\\'string'") code_snippet = example_file.find(id="example.code_snippet") assert code_snippet code_snippet_value = code_snippet.find(class_="property").contents[-1] assert code_snippet_value.text == "Multiline-String" max_rating = example_file.find(id="example.max_rating") assert max_rating max_rating_value = max_rating.find_all(class_="property") assert max_rating_value[0].text == ": int" assert max_rating_value[1].text == " = 10" # TODO: This should either not have a value # or should display the value as part of the type declaration. # This prevents setting warningiserror. assert example_file.find(id="example.is_valid") ratings = example_file.find(id="example.ratings") assert ratings ratings_value = ratings.find_all(class_="property") assert "List[int]" in ratings_value[0].text rating_names = example_file.find(id="example.rating_names") assert rating_names rating_names_value = rating_names.find_all(class_="property") assert "Dict[int, str]" in rating_names_value[0].text f = example_file.find(id="example.f") assert f assert f.find(class_="sig-param").text == "start: int" assert f.find(class_="sig-return-typehint").text == "Iterable[int]" mixed_list = example_file.find(id="example.mixed_list") assert mixed_list mixed_list_value = mixed_list.find_all(class_="property") if sphinx_version >= (6,): assert "List[str | int]" in mixed_list_value[0].text else: assert "List[Union[str, int]]" in mixed_list_value[0].text f2 = example_file.find(id="example.f2") assert f2 arg = f2.find(class_="sig-param") assert arg.contents[0].text == "not_yet_a" assert arg.find("a").text == "A" f3 = example_file.find(id="example.f3") assert f3 arg = f3.find(class_="sig-param") assert arg.contents[0].text == "imported" assert arg.find("a").text == "example2.B" returns = f3.find(class_="sig-return-typehint") assert returns.find("a").text == "example2.B" is_an_a = example_file.find(id="example.A.is_an_a") assert is_an_a is_an_a_value = is_an_a.find_all(class_="property") assert "ClassVar" in is_an_a_value[0].text assert example_file.find(id="example.A.instance_var") global_a = example_file.find(id="example.global_a") assert global_a global_a_value = global_a.find_all(class_="property") assert global_a_value[0].text == ": A" assert example_file.find(id="example.SomeMetaclass") def test_overload(self, parse): example_file = parse("_build/html/autoapi/example/index.html") overloaded_func = example_file.find(id="example.overloaded_func") assert overloaded_func arg = overloaded_func.find(class_="sig-param") assert arg.text == "a: float" overloaded_func = overloaded_func.next_sibling.next_sibling arg = overloaded_func.find(class_="sig-param") assert arg.text == "a: str" docstring = overloaded_func.next_sibling.next_sibling assert docstring.tag != "dt" assert docstring.text.strip() == "Overloaded function" overloaded_method = example_file.find(id="example.A.overloaded_method") assert overloaded_method arg = overloaded_method.find(class_="sig-param") assert arg.text == "a: float" overloaded_method = overloaded_method.next_sibling.next_sibling arg = overloaded_method.find(class_="sig-param") assert arg.text == "a: str" docstring = overloaded_method.next_sibling.next_sibling assert docstring.tag != "dt" assert docstring.text.strip() == "Overloaded method" overloaded_class_method = example_file.find( id="example.A.overloaded_class_method" ) assert overloaded_class_method arg = overloaded_class_method.find(class_="sig-param") assert arg.text == "a: float" overloaded_class_method = overloaded_class_method.next_sibling.next_sibling arg = overloaded_class_method.find(class_="sig-param") assert arg.text == "a: str" docstring = overloaded_class_method.next_sibling.next_sibling assert docstring.tag != "dt" assert docstring.text.strip() == "Overloaded class method" assert example_file.find(id="example.undoc_overloaded_func") assert example_file.find(id="example.A.undoc_overloaded_method") c = example_file.find(id="example.C") assert c arg = c.find(class_="sig-param") assert arg.text == "a: int" c = c.next_sibling.next_sibling arg = c.find(class_="sig-param") assert arg.text == "a: float" docstring = c.next_sibling.next_sibling assert docstring.tag != "dt" # D inherits overloaded constructor d = example_file.find(id="example.D") assert d arg = d.find(class_="sig-param") assert arg.text == "a: int" d = d.next_sibling.next_sibling arg = d.find(class_="sig-param") assert arg.text == "a: float" docstring = d.next_sibling.next_sibling assert docstring.tag != "dt" def test_async(self, parse): example_file = parse("_build/html/autoapi/example/index.html") async_method = example_file.find(id="example.A.async_method") assert async_method.find(class_="property").text.strip() == "async" async_function = example_file.find(id="example.async_function") assert async_function.find(class_="property").text.strip() == "async" def test_py3_hiding_undoc_overloaded_members(builder, parse): confoverrides = {"autoapi_options": ["members", "special-members"]} builder("py3example", confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") overloaded_func = example_file.find(id="example.overloaded_func") assert overloaded_func overloaded_method = example_file.find(id="example.A.overloaded_method") assert overloaded_method assert not example_file.find(id="example.undoc_overloaded_func") assert not example_file.find(id="example.A.undoc_overloaded_method") class TestAnnotationCommentsModule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pyannotationcommentsexample", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") max_rating = example_file.find(id="example.max_rating") assert max_rating max_rating_value = max_rating.find_all(class_="property") assert max_rating_value[0].text == ": int" assert max_rating_value[1].text == " = 10" ratings = example_file.find(id="example.ratings") assert ratings ratings_value = ratings.find_all(class_="property") assert "List[int]" in ratings_value[0].text rating_names = example_file.find(id="example.rating_names") assert rating_names rating_names_value = rating_names.find_all(class_="property") assert "Dict[int, str]" in rating_names_value[0].text f = example_file.find(id="example.f") assert f assert f.find(class_="sig-param").text == "start: int" assert f.find(class_="sig-return-typehint").text == "Iterable[int]" mixed_list = example_file.find(id="example.mixed_list") assert mixed_list mixed_list_value = mixed_list.find_all(class_="property") if sphinx_version >= (6,): assert "List[str | int]" in mixed_list_value[0].text else: assert "List[Union[str, int]]" in mixed_list_value[0].text f2 = example_file.find(id="example.f2") assert f2 arg = f2.find(class_="sig-param") assert arg.contents[0].text == "not_yet_a" assert arg.find("a").text == "A" is_an_a = example_file.find(id="example.A.is_an_a") assert is_an_a is_an_a_value = is_an_a.find_all(class_="property") assert "ClassVar" in is_an_a_value[0].text assert example_file.find(id="example.A.instance_var") global_a = example_file.find(id="example.global_a") assert global_a global_a_value = global_a.find_all(class_="property") assert global_a_value[0].text == ": A" @pytest.mark.skipif( sys.version_info < (3, 8), reason="Positional only arguments need Python >=3.8" ) class TestPositionalOnlyArgumentsModule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("py38positionalparams", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") f_simple = example_file.find(id="example.f_simple") assert "f_simple(a, b, /, c, d, *, e, f)" in f_simple.text if sphinx_version >= (6,): f_comment = example_file.find(id="example.f_comment") assert ( "f_comment(a: int, b: int, /, c: int | None, d: int | None, *, e: float, f: float)" in f_comment.text ) f_annotation = example_file.find(id="example.f_annotation") assert ( "f_annotation(a: int, b: int, /, c: int | None, d: int | None, *, e: float, f: float)" in f_annotation.text ) f_arg_comment = example_file.find(id="example.f_arg_comment") assert ( "f_arg_comment(a: int, b: int, /, c: int | None, d: int | None, *, e: float, f: float)" in f_arg_comment.text ) else: f_comment = example_file.find(id="example.f_comment") assert ( "f_comment(a: int, b: int, /, c: Optional[int], d: Optional[int], *, e: float, f: float)" in f_comment.text ) f_annotation = example_file.find(id="example.f_annotation") assert ( "f_annotation(a: int, b: int, /, c: Optional[int], d: Optional[int], *, e: float, f: float)" in f_annotation.text ) f_arg_comment = example_file.find(id="example.f_arg_comment") assert ( "f_arg_comment(a: int, b: int, /, c: Optional[int], d: Optional[int], *, e: float, f: float)" in f_arg_comment.text ) f_no_cd = example_file.find(id="example.f_no_cd") assert "f_no_cd(a: int, b: int, /, *, e: float, f: float)" in f_no_cd.text @pytest.mark.skipif( sys.version_info < (3, 10), reason="Union pipe syntax requires Python >=3.10" ) class TestPipeUnionModule: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("py310unionpipe", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") simple = example_file.find(id="example.simple") args = simple.find_all(class_="sig-param") assert len(args) == 1 links = args[0].select("span > a") assert len(links) == 1 assert links[0].text == "pathlib.Path" optional = example_file.find(id="example.optional") args = optional.find_all(class_="sig-param") assert len(args) == 1 links = args[0].select("span > a") assert len(links) == 2 assert links[0].text == "pathlib.Path" assert links[1].text == "None" union = example_file.find(id="example.union") args = union.find_all(class_="sig-param") assert len(args) == 1 links = args[0].select("span > a") assert len(links) == 2 assert links[0].text == "pathlib.Path" assert links[1].text == "None" pipe = example_file.find(id="example.pipe") args = pipe.find_all(class_="sig-param") assert len(args) == 1 links = args[0].select("span > a") assert len(links) == 2 assert links[0].text == "pathlib.Path" assert links[1].text == "None" @pytest.mark.skipif( sys.version_info < (3, 12), reason="PEP-695 support requires Python >=3.12" ) class TestPEP695: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pep695", warningiserror=True) def test_integration(self, parse): example_file = parse("_build/html/autoapi/example/index.html") alias = example_file.find(id="example.MyTypeAliasA") properties = alias.find_all(class_="property") assert len(properties) == 2 annotation = properties[0].text assert annotation == ": TypeAlias" value = properties[1].text assert value == " = tuple[str, int]" alias = example_file.find(id="example.MyTypeAliasB") properties = alias.find_all(class_="property") assert len(properties) == 2 annotation = properties[0].text assert annotation == ": TypeAlias" value = properties[1].text assert value == " = tuple[str, int]" def test_napoleon_integration_loaded(builder, parse): confoverrides = { "exclude_patterns": ["manualapi.rst"], "extensions": [ "autoapi.extension", "sphinx.ext.autodoc", "sphinx.ext.napoleon", ], } builder("pyexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") method_google = example_file.find(id="example.Foo.method_google_docs") params, returns, return_type = method_google.parent.select(".field-list > dt") assert params.text == "Parameters:" assert returns.text == "Returns:" assert return_type.text == "Return type:" class TestSimplePackage: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pypackageexample", warningiserror=True) def test_integration_with_package(self, parse): example_file = parse("_build/html/autoapi/package/index.html") entries = example_file.find_all(class_="toctree-l1") assert any(entry.text == "package.submodule" for entry in entries) assert example_file.find(id="package.function") example_foo_file = parse("_build/html/autoapi/package/submodule/index.html") submodule = example_foo_file.find(id="package.submodule.Class") assert submodule method_okay = submodule.parent.find(id="package.submodule.Class.method_okay") assert method_okay index_file = parse("_build/html/index.html") toctree = index_file.select("li > a") assert any(item.text == "API Reference" for item in toctree) assert any(item.text == "package.submodule" for item in toctree) assert any(item.text == "Class" for item in toctree) assert any(item.text == "function()" for item in toctree) def test_simple_no_false_warnings(builder, caplog): logger = sphinx.util.logging.getLogger("autoapi") logger.logger.addHandler(caplog.handler) builder("pypackageexample", warningiserror=True) assert "Cannot resolve" not in caplog.text def _test_class_content(builder, parse, class_content): confoverrides = { "autoapi_python_class_content": class_content, "exclude_patterns": ["manualapi.rst"], } builder("pyexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") foo = example_file.find(id="example.Foo").parent.find("dd") if class_content == "init": assert "Can we parse arguments" not in foo.text else: assert "Can we parse arguments" in foo.text if class_content not in ("both", "init"): assert "Constructor docstring" not in foo.text else: assert "Constructor docstring" in foo.text def test_class_class_content(builder, parse): _test_class_content(builder, parse, "class") def test_both_class_content(builder, parse): _test_class_content(builder, parse, "both") def test_init_class_content(builder, parse): _test_class_content(builder, parse, "init") def test_hiding_private_members(builder, parse): confoverrides = {"autoapi_options": ["members", "undoc-members", "special-members"]} builder("pypackageexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/package/index.html") entries = example_file.find_all(class_="toctree-l1") assert all("private" not in entry.text for entry in entries) assert not pathlib.Path( "_build/html/autoapi/package/_private_module/index.html" ).exists() def test_hiding_inheritance(builder, parse): confoverrides = { "autoapi_options": ["members", "undoc-members", "special-members"], "exclude_patterns": ["manualapi.rst"], } builder("pyexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") assert "Bases:" not in example_file.find(id="example.Foo").parent.find("dd").text def test_hiding_imported_members(builder, parse): confoverrides = {"autoapi_options": ["members", "undoc-members"]} builder("pypackagecomplex", confoverrides=confoverrides) subpackage_file = parse("_build/html/autoapi/complex/subpackage/index.html") assert not subpackage_file.find(id="complex.subpackage.public_chain") package_file = parse("_build/html/autoapi/complex/index.html") assert not package_file.find(id="complex.public_chain") submodule_file = parse("_build/html/autoapi/complex/subpackage/index.html") assert not submodule_file.find(id="complex.subpackage.now_public_function") def test_imports_into_modules_always_hidden(builder, parse): confoverrides = { "autoapi_options": ["members", "undoc-members", "imported-members"] } builder("pypackagecomplex", confoverrides=confoverrides) submodule_file = parse("_build/html/autoapi/complex/submodule/index.html") assert not submodule_file.find(id="complex.submodule.imported_function") def test_inherited_members(builder, parse): confoverrides = { "autoapi_options": ["members", "inherited-members", "undoc-members"], "exclude_patterns": ["manualapi.rst"], } builder("pyexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") bar = example_file.find(id="example.Bar") assert bar assert bar.parent.find(id="example.Bar.method_okay") def test_skipping_members(builder, parse): builder("pyskipexample", warningiserror=True) example_file = parse("_build/html/autoapi/example/index.html") assert not example_file.find(id="example.foo") assert not example_file.find(id="example.Bar") assert not example_file.find(id="example.Bar.m") assert example_file.find(id="example.Baf") assert not example_file.find(id="example.Baf.m") assert not example_file.find(id="example.baz") assert example_file.find(id="example.anchor") @pytest.mark.parametrize( "value,order", [ ("bysource", ["Foo", "decorator_okay", "Bar"]), ("alphabetical", ["Bar", "Foo", "decorator_okay"]), ("groupwise", ["Bar", "Foo", "decorator_okay"]), ], ) def test_order_members(builder, parse, value, order): confoverrides = { "autoapi_member_order": value, "exclude_patterns": ["manualapi.rst"], } builder("pyexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") indexes = [example_file.find(id=f"example.{name}").sourceline for name in order] assert indexes == sorted(indexes) class _CompareInstanceType: def __init__(self, type_): self.type = type_ def __eq__(self, other): return self.type is type(other) def __repr__(self): return "".format(self.type.__name__) def test_skip_members_hook(builder): os.chdir("tests/python/pyskipexample") emit_firstresult_patch = None class MockSphinx(Sphinx): def __init__(self, *args, **kwargs): nonlocal emit_firstresult_patch emit_firstresult_patch = Mock(wraps=self.emit_firstresult) self.emit_firstresult = emit_firstresult_patch super().__init__(*args, **kwargs) app = MockSphinx( srcdir=".", confdir=".", outdir="_build/html", doctreedir="_build/.doctrees", buildername="html", warningiserror=True, confoverrides={ "suppress_warnings": [ "app.add_node", "app.add_directive", "app.add_role", ] }, ) app.build() options = ["members", "undoc-members", "special-members"] mock_calls = [ call( "autoapi-skip-member", "module", "example", _CompareInstanceType(PythonModule), False, options, ), call( "autoapi-skip-member", "function", "example.foo", _CompareInstanceType(PythonFunction), False, options, ), call( "autoapi-skip-member", "class", "example.Bar", _CompareInstanceType(PythonClass), False, options, ), call( "autoapi-skip-member", "class", "example.Baf", _CompareInstanceType(PythonClass), False, options, ), call( "autoapi-skip-member", "data", "example.baz", _CompareInstanceType(PythonData), False, options, ), call( "autoapi-skip-member", "data", "example.anchor", _CompareInstanceType(PythonData), False, options, ), call( "autoapi-skip-member", "method", "example.Baf.m", _CompareInstanceType(PythonMethod), False, options, ), ] for mock_call in mock_calls: assert mock_call in emit_firstresult_patch.mock_calls class TestComplexPackage: @pytest.fixture(autouse=True, scope="class") def built(self, builder): # We don't set warningiserror=True because we test that invalid imports # do not fail the build builder("pypackagecomplex") def test_public_chain_resolves(self, parse): submodule_file = parse( "_build/html/autoapi/complex/subpackage/submodule/index.html" ) assert submodule_file.find(id="complex.subpackage.submodule.public_chain") subpackage_file = parse("_build/html/autoapi/complex/subpackage/index.html") assert subpackage_file.find(id="complex.subpackage.public_chain") package_file = parse("_build/html/autoapi/complex/index.html") assert package_file.find(id="complex.public_chain") def test_private_made_public(self, parse): submodule_file = parse("_build/html/autoapi/complex/subpackage/index.html") assert submodule_file.find(id="complex.subpackage.now_public_function") def test_multiple_import_locations(self, parse): submodule_file = parse( "_build/html/autoapi/complex/subpackage/submodule/index.html" ) assert submodule_file.find( id="complex.subpackage.submodule.public_multiple_imports" ) subpackage_file = parse("_build/html/autoapi/complex/subpackage/index.html") assert subpackage_file.find(id="complex.subpackage.public_multiple_imports") package_file = parse("_build/html/autoapi/complex/index.html") assert package_file.find(id="complex.public_multiple_imports") def test_simple_wildcard_imports(self, parse): wildcard_file = parse("_build/html/autoapi/complex/wildcard/index.html") assert wildcard_file.find(id="complex.wildcard.public_chain") assert wildcard_file.find(id="complex.wildcard.now_public_function") assert wildcard_file.find(id="complex.wildcard.public_multiple_imports") assert wildcard_file.find(id="complex.wildcard.module_level_function") def test_wildcard_all_imports(self, parse): wildcard_file = parse("_build/html/autoapi/complex/wildall/index.html") assert not wildcard_file.find(id="complex.wildall.not_all") assert not wildcard_file.find(id="complex.wildall.NotAllClass") assert not wildcard_file.find(id="complex.wildall.does_not_exist") assert wildcard_file.find(id="complex.wildall.SimpleClass") assert wildcard_file.find(id="complex.wildall.simple_function") assert wildcard_file.find(id="complex.wildall.public_chain") assert wildcard_file.find(id="complex.wildall.module_level_function") def test_no_imports_in_module_with_all(self, parse): foo_file = parse("_build/html/autoapi/complex/foo/index.html") assert not foo_file.find(id="complex.foo.module_level_function") def test_all_overrides_import_in_module_with_all(self, parse): foo_file = parse("_build/html/autoapi/complex/foo/index.html") assert foo_file.find(id="complex.foo.PublicClass") def test_parses_unicode_file(self, parse): foo_file = parse("_build/html/autoapi/complex/unicode_data/index.html") assert foo_file.find(id="complex.unicode_data.unicode_str") def test_nested_parse_directive(self, parse): package_file = parse("_build/html/autoapi/complex/index.html") complex = package_file.find(id="complex") assert "This heading will be removed" not in complex.parent.text assert complex.parent.find("section")["id"] != "this-heading-will-be-removed" class TestComplexPackageParallel(TestComplexPackage): @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pypackagecomplex", parallel=2) def test_caching(builder, rebuild): mtimes = (0, 0) def record_mtime(): nonlocal mtimes mtime = 0 for root, _, files in os.walk("_build/html/autoapi"): for name in files: this_mtime = os.path.getmtime(os.path.join(root, name)) mtime = max(mtime, this_mtime) mtimes = (*mtimes[1:], mtime) builder("pypackagecomplex", confoverrides={"autoapi_keep_files": True}) record_mtime() rebuild(confoverrides={"autoapi_keep_files": True}) record_mtime() assert mtimes[1] == mtimes[0] # Check that adding a file rebuilds the docs extra_file = "complex/new.py" with open(extra_file, "w") as out_f: out_f.write("\n") try: rebuild(confoverrides={"autoapi_keep_files": True}) finally: os.remove(extra_file) record_mtime() assert mtimes[1] != mtimes[0] # Removing a file also rebuilds the docs rebuild(confoverrides={"autoapi_keep_files": True}) record_mtime() assert mtimes[1] != mtimes[0] # Changing not keeping files always builds rebuild() record_mtime() assert mtimes[1] != mtimes[0] class TestImplicitNamespacePackage: @pytest.fixture(autouse=True, scope="class") def built(self, builder): # TODO: Cannot set warningiserror=True because namespaces are not added # to the toctree automatically. builder("py3implicitnamespace") def test_sibling_import_from_namespace(self, parse): example_file = parse("_build/html/autoapi/namespace/example/index.html") assert example_file.find(id="namespace.example.first_method") def test_sub_sibling_import_from_namespace(self, parse): example_file = parse("_build/html/autoapi/namespace/example/index.html") assert example_file.find(id="namespace.example.second_sub_method") def test_custom_jinja_filters(builder, parse, tmp_path): py_templates = tmp_path / "python" py_templates.mkdir() orig_py_templates = pathlib.Path(autoapi.settings.TEMPLATE_DIR) / "python" orig_template = (orig_py_templates / "class.rst").read_text() (py_templates / "class.rst").write_text( orig_template.replace("obj.docstring", "obj.docstring|prepare_docstring") ) confoverrides = { "autoapi_prepare_jinja_env": ( lambda jinja_env: jinja_env.filters.update( { "prepare_docstring": ( lambda docstring: "This is using custom filters.\n" ) } ) ), "autoapi_template_dir": str(tmp_path), "exclude_patterns": ["manualapi.rst"], } builder("pyexample", warningiserror=True, confoverrides=confoverrides) example_file = parse("_build/html/autoapi/example/index.html") foo = example_file.find(id="example.Foo").parent.find("dd") assert "This is using custom filters." in foo.text def test_string_module_attributes(builder): """Test toggle for multi-line string attribute values (GitHub #267).""" keep_rst = { "autoapi_keep_files": True, } builder("py3example", confoverrides=keep_rst) example_path = os.path.join("autoapi", "example", "index.rst") with io.open(example_path, encoding="utf8") as example_handle: example_file = example_handle.read() code_snippet_contents = [ ".. py:data:: code_snippet", " :value: Multiline-String", "", " .. raw:: html", "", "
Show Value", "", " .. code-block:: python", "", ' """The following is some code:', " ", # <--- Line array monstrosity to preserve these leading spaces " # -*- coding: utf-8 -*-", " from __future__ import absolute_import, division, print_function, unicode_literals", " # from future.builtins.disabled import *", " # from builtins import *", " ", """ print("chunky o'block")""", ' """', "", " .. raw:: html", "", "
", ] assert "\n".join(code_snippet_contents) in example_file class TestAutodocTypehintsPackage: """Test integrations with the autodoc.typehints extension.""" @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder("pyautodoc_typehints", warningiserror=True) def test_renders_typehint(self, parse): example_file = parse("_build/html/autoapi/example/index.html") test = example_file.find(id="example.A.test") args = test.parent.select(".field-list > dd") assert args[0].text.startswith("a (int)") def test_renders_typehint_in_second_module(self, parse): example2_file = parse("_build/html/autoapi/example2/index.html") test = example2_file.find(id="example2.B.test") args = test.parent.select(".field-list > dd") assert args[0].text.startswith("a (int)") def test_no_files_found(builder): """Test that building does not fail when no sources files are found.""" with pytest.raises(ExtensionError) as exc_info: builder("pyemptyexample") assert os.path.dirname(__file__) in str(exc_info.value) class TestMdSource: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pyexample", warningiserror=True, confoverrides={"source_suffix": ["md"]}, ) class TestMemberOrder: @pytest.fixture(autouse=True, scope="class") def built(self, builder): builder( "pyexample", warningiserror=True, confoverrides={ "autodoc_member_order": "bysource", "autoapi_generate_api_docs": False, "autoapi_add_toctree_entry": False, }, ) def test_line_number_order(self, parse): example_file = parse("_build/html/manualapi.html") method_tricky = example_file.find(id="example.Foo.method_tricky") method_sphinx_docs = example_file.find(id="example.Foo.method_sphinx_docs") assert method_tricky.sourceline < method_sphinx_docs.sourceline sphinx-autoapi-3.1.2/tests/templateexample/000077500000000000000000000000001463504067600210325ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/templateexample/conf.py000066400000000000000000000011041463504067600223250ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" html_static_path = ["_static"] htmlhelp_basename = "pyexampledoc" extensions = ["autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" autoapi_template_dir = "template_overrides" exclude_patterns += [autoapi_template_dir] sphinx-autoapi-3.1.2/tests/templateexample/example/000077500000000000000000000000001463504067600224655ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/templateexample/example/example.py000066400000000000000000000002151463504067600244700ustar00rootroot00000000000000__author__ = "swenson" import math def example_function(x): """Compute the square root of x and return it.""" return math.sqrt(x) sphinx-autoapi-3.1.2/tests/templateexample/index.rst000066400000000000000000000007201463504067600226720ustar00rootroot00000000000000.. pyexample documentation master file, created by sphinx-quickstart on Fri May 29 13:34:37 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyexample's documentation! ===================================== .. toctree:: autoapi/index Contents: .. toctree:: :maxdepth: 2 Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` sphinx-autoapi-3.1.2/tests/templateexample/template_overrides/000077500000000000000000000000001463504067600247275ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/templateexample/template_overrides/python/000077500000000000000000000000001463504067600262505ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/templateexample/template_overrides/python/function.rst000066400000000000000000000000451463504067600306260ustar00rootroot00000000000000This is a function template override!sphinx-autoapi-3.1.2/tests/test_astroid_utils.py000066400000000000000000000173111463504067600221440ustar00rootroot00000000000000import sys import astroid from autoapi import _astroid_utils from autoapi import _objects import pytest def generate_module_names(): for i in range(1, 5): yield ".".join("module{}".format(j) for j in range(i)) yield "package.repeat.repeat" def imported_basename_cases(): for module_name in generate_module_names(): import_ = "import {}".format(module_name) basename = "{}.ImportedClass".format(module_name) expected = basename yield (import_, basename, expected) import_ = "import {} as aliased".format(module_name) basename = "aliased.ImportedClass" yield (import_, basename, expected) if "." in module_name: from_name, attribute = module_name.rsplit(".", 1) import_ = "from {} import {}".format(from_name, attribute) basename = "{}.ImportedClass".format(attribute) yield (import_, basename, expected) import_ += " as aliased" basename = "aliased.ImportedClass" yield (import_, basename, expected) import_ = "from {} import ImportedClass".format(module_name) basename = "ImportedClass" yield (import_, basename, expected) import_ = "from {} import ImportedClass as AliasedClass".format(module_name) basename = "AliasedClass" yield (import_, basename, expected) def generate_args(): for i in range(5): yield ", ".join("arg{}".format(j) for j in range(i)) def imported_call_cases(): for args in generate_args(): for import_, basename, expected in imported_basename_cases(): basename += "({})".format(args) expected += "()" yield import_, basename, expected class TestAstroidUtils: @pytest.mark.parametrize( ("import_", "basename", "expected"), list(imported_basename_cases()) ) def test_can_get_full_imported_basename(self, import_, basename, expected): source = """ {} class ThisClass({}): #@ pass """.format( import_, basename ) node = astroid.extract_node(source) basenames = _astroid_utils.resolve_qualname(node.bases[0], node.basenames[0]) assert basenames == expected @pytest.mark.parametrize( ("import_", "basename", "expected"), list(imported_call_cases()) ) def test_can_get_full_function_basename(self, import_, basename, expected): source = """ {} class ThisClass({}): #@ pass """.format( import_, basename ) node = astroid.extract_node(source) basenames = _astroid_utils.resolve_qualname(node.bases[0], node.basenames[0]) assert basenames == expected @pytest.mark.parametrize( ("source", "expected"), [ ('a = "a"', ("a", "'a'")), ("a = 1", ("a", "1")), ("a, b, c = (1, 2, 3)", None), ("a = b = 1", None), ("a = [1, 2, [3, 4]]", ("a", "[1, 2, [3, 4]]")), ("a = [1, 2, variable[subscript]]", ("a", None)), ('a = """multiline\nstring"""', ("a", '"""multiline\nstring"""')), ('a = ["""multiline\nstring"""]', ("a", None)), ("a = (1, 2, 3)", ("a", "(1, 2, 3)")), ("a = (1, 'two', 3)", ("a", "(1, 'two', 3)")), ("a = None", ("a", "None")), ], ) def test_can_get_assign_values(self, source, expected): node = astroid.extract_node(source) value = _astroid_utils.get_assign_value(node) assert value == expected @pytest.mark.parametrize( "signature,expected", [ ( "a: bool, b: int = 5", [(None, "a", "bool", None), (None, "b", "int", "5")], ), pytest.param( "a: bool, /, b: int, *, c: str", [ (None, "a", "bool", None), ("/", None, None, None), (None, "b", "int", None), ("*", None, None, None), (None, "c", "str", None), ], marks=pytest.mark.skipif( sys.version_info[:2] < (3, 8), reason="Uses Python 3.8+ syntax" ), ), pytest.param( "a: bool, /, b: int, *args, c: str, **kwargs", [ (None, "a", "bool", None), ("/", None, None, None), (None, "b", "int", None), ("*", "args", None, None), (None, "c", "str", None), ("**", "kwargs", None, None), ], marks=pytest.mark.skipif( sys.version_info[:2] < (3, 8), reason="Uses Python 3.8+ syntax" ), ), pytest.param( "a: int, *args, b: str, **kwargs", [ (None, "a", "int", None), ("*", "args", None, None), (None, "b", "str", None), ("**", "kwargs", None, None), ], marks=pytest.mark.skipif( sys.version_info[:2] < (3, 8), reason="Uses Python 3.8+ syntax" ), ), ], ) def test_parse_annotations(self, signature, expected): node = astroid.extract_node( """ def func({}) -> str: #@ pass """.format( signature ) ) annotations = _astroid_utils.get_args_info(node.args) assert annotations == expected def test_parse_split_type_comments(self): node = astroid.extract_node( """ def func( a, # type: int b, # type: int ): # type: (...) -> str pass """ ) annotations = _astroid_utils.get_args_info(node.args) expected = [ (None, "a", "int", None), (None, "b", "int", None), ] assert annotations == expected @pytest.mark.parametrize( "signature,expected", [ ("a: bool, b: int = 5, c='hi'", "a: bool, b: int = 5, c='hi'"), pytest.param( "a: bool, /, b: int, *, c: str", "a: bool, /, b: int, *, c: str", marks=pytest.mark.skipif( sys.version_info[:2] < (3, 8), reason="Uses Python 3.8+ syntax" ), ), pytest.param( "a: bool, /, b: int, *args, c: str, **kwargs", "a: bool, /, b: int, *args, c: str, **kwargs", marks=pytest.mark.skipif( sys.version_info[:2] < (3, 8), reason="Uses Python 3.8+ syntax" ), ), pytest.param( "*, a: int, b: int", "*, a: int, b: int", marks=pytest.mark.skipif( sys.version_info[:2] < (3, 8), reason="Uses Python 3.8+ syntax" ), ), ("a: int, *args, b: str, **kwargs", "a: int, *args, b: str, **kwargs"), ("a: 'A'", "a: A"), ("a: Literal[1]", "a: Literal[1]"), ("a: Literal['x']", "a: Literal['x']"), ("a: Literal['x', 'y', 'z']", "a: Literal['x', 'y', 'z']"), ], ) def test_format_args(self, signature, expected): node = astroid.extract_node( """ def func({}) -> str: #@ pass """.format( signature ) ) args_info = _astroid_utils.get_args_info(node.args) formatted = _objects._format_args(args_info) assert formatted == expected sphinx-autoapi-3.1.2/tests/test_integration.py000066400000000000000000000030461463504067600216020ustar00rootroot00000000000000import io import os import shutil from contextlib import contextmanager from sphinx.application import Sphinx @contextmanager def sphinx_build(test_dir, confoverrides=None): os.chdir("tests/{0}".format(test_dir)) try: app = Sphinx( srcdir=".", confdir=".", outdir="_build/text", doctreedir="_build/.doctrees", buildername="text", confoverrides=confoverrides, ) app.build(force_all=True) yield finally: if os.path.exists("_build"): shutil.rmtree("_build") os.chdir("../..") class LanguageIntegrationTests: def _run_test(self, test_dir, test_file, test_string): with sphinx_build(test_dir): with io.open(test_file, encoding="utf8") as fin: text = fin.read().strip() assert test_string in text class TestIntegration(LanguageIntegrationTests): def test_template_overrides(self): self._run_test( "templateexample", "_build/text/autoapi/example/index.txt", "This is a function template override", ) class TestTOCTree(LanguageIntegrationTests): def test_toctree_overrides(self): self._run_test("toctreeexample", "_build/text/index.txt", "API Reference") def test_toctree_domain_insertion(self): """ Test that the example_function gets added to the TOC Tree """ self._run_test( "toctreeexample", "_build/text/index.txt", '* "example_function()"' ) sphinx-autoapi-3.1.2/tests/toctreeexample/000077500000000000000000000000001463504067600206645ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/toctreeexample/conf.py000066400000000000000000000007541463504067600221710ustar00rootroot00000000000000# -*- coding: utf-8 -*- templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyexample" copyright = "2015, readthedocs" author = "readthedocs" version = "0.1" release = "0.1" language = "en" exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False html_theme = "alabaster" html_static_path = ["_static"] htmlhelp_basename = "pyexampledoc" extensions = ["autoapi.extension"] autoapi_dirs = ["example"] autoapi_file_pattern = "*.py" sphinx-autoapi-3.1.2/tests/toctreeexample/example/000077500000000000000000000000001463504067600223175ustar00rootroot00000000000000sphinx-autoapi-3.1.2/tests/toctreeexample/example/example.py000066400000000000000000000002151463504067600243220ustar00rootroot00000000000000__author__ = "swenson" import math def example_function(x): """Compute the square root of x and return it.""" return math.sqrt(x) sphinx-autoapi-3.1.2/tests/toctreeexample/index.rst000066400000000000000000000001401463504067600225200ustar00rootroot00000000000000Welcome to pyexample's documentation! ===================================== .. toctree:: sphinx-autoapi-3.1.2/tox.ini000066400000000000000000000020641463504067600160160ustar00rootroot00000000000000[tox] isolated_build = true envlist = # Keep this in sync with .github/workflows/main.yml py{38,39,310,311,312,313} formatting typecheck lint docs release_notes [gh-actions] python = 3.8: py38 3.9: py39 3.10: py310 3.11: py311 3.12: py312, formatting, typecheck, lint, docs, release_notes 3.13: py313 [testenv] deps = beautifulsoup4 pytest commands = pytest {posargs} [testenv:formatting] skip_install = true deps = black commands = black --check --diff autoapi tests [testenv:lint] skip_install = true deps = ruff commands = ruff check {posargs:autoapi} [testenv:typecheck] deps = mypy types-docutils types-PyYAML commands = mypy {posargs:autoapi} [testenv:docs] extras = docs deps = changedir = {toxinidir}/docs commands = sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html [testenv:release_notes] deps = towncrier importlib-resources<6 # pinned due to https://github.com/twisted/towncrier/issues/528 commands = towncrier {posargs:check}