Giter Club home page Giter Club logo

apidoc's Introduction

sphinxcontrib-apidoc

Build Status

A Sphinx extension for running sphinx-apidoc on each build.

Overview

sphinx-apidoc is a tool for automatic generation of Sphinx sources that, using the autodoc extension, documents a whole package in the style of other automatic API documentation tools. sphinx-apidoc does not actually build documentation - rather it simply generates it. As a result, it must be run before sphinx-build. This generally results in tox.ini files like the following:

[testenv:docs]
commands =
  sphinx-apidoc -o doc/api my_code my_code/tests
  sphinx-build -W -b html doc doc/_build/html

This extension eliminates the need to keep that configuration outside Sphinx. Instead, this functionality can be enabled and configured from your documentation's conf.py file, like so:

extensions = [
    'sphinxcontrib.apidoc',
    # ...
]
apidoc_module_dir = '../my_code'
apidoc_output_dir = 'reference'
apidoc_excluded_paths = ['tests']
apidoc_separate_modules = True

Configuration

The apidoc extension uses the following configuration values:

apidoc_module_dir

The path to the module to document. This must be a path to a Python package. This path can be a path relative to the documentation source directory or an absolute path.

Required

apidoc_output_dir

The output directory. If it does not exist, it is created. This path is relative to the documentation source directory.

Optional, defaults to api.

apidoc_template_dir

A directory containing user-defined templates. Template files in this directory that match the default apidoc templates (module.rst_t, package.rst_t, toc.rst_t) will overwrite them. The default templates can be found in site-packages/sphinx/templates/apidoc/. This path is relative to the documentation source directory.

Optional, defaults to 'templates'.

apidoc_excluded_paths

An optional list of modules to exclude. These should be paths relative to apidoc_module_dir. fnmatch-style wildcarding is supported.

Optional, defaults to [].

apidoc_separate_modules

Put documentation for each module on its own page. Otherwise there will be one page per (sub)package.

Optional, defaults to False.

apidoc_toc_file

Filename for a table of contents file. Defaults to modules. If set to False, apidoc will not create a table of contents file.

Optional, defaults to None.

apidoc_module_first

When set to True, put module documentation before submodule documentation.

Optional, defaults to False.

apidoc_extra_args

Extra arguments which will be passed to sphinx-apidoc. These are placed after flags and before the module name.

Optional, defaults to [].

Migration from pbr

pbr has historically included a custom variant of the build_sphinx distutils command. This provides, among other things, the ability to generate API documentation as part of build process. Clearly this is not necessary with this extension.

There are two implementations of the API documentation feature in pbr: autodoc_tree and autodoc. To describe the difference, let's explore how one would migrate real-world projects using both features. Your project might use one or both: autodoc_tree is enabled using the autodoc_tree_index_modules setting while autodoc is enabled using the autodoc_index_modules setting, both found in the [pbr] section of setup.cfg.

autodoc_tree

As autodoc_tree is based on sphinx-apidoc, migration is easy. Lets take python-openstackclient as an example, looking at minimal versions of setup.cfg and doc/source/conf.py:

[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source

[pbr]
autodoc_tree_index_modules = True
autodoc_tree_excludes =
  setup.py
  openstackclient/volume/v3
  openstackclient/tests/
  openstackclient/tests/*
api_doc_dir = contributor/api
extensions = ['']

Once migrated, this would look like so:

[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source
extensions = ['sphinxcontrib.apidoc']

apidoc_module_dir = '../../openstack'
apidoc_excluded_paths = [
  'volume',
  'tests'
]
apidoc_output_dir = 'contributor/api'

There are a couple of changes here:

  1. Configure apidoc_module_dir in conf.py

    With the autodoc_tree feature, API documentation is always generated for the directory in which setup.cfg exists, which is typically the top-level directory. With this extension, you must explicitly state which directory you wish to build documentation for using the apidoc_module_dir setting. You should configure this to point to your actual package rather than the top level directory as this means you don't need to worry about skipping unrelated files like setup.py.

  2. Configure apidoc_excluded_paths in conf.py

    The apidoc_excluded_paths setting in conf.py works exactly like the [pbr] autodoc_tree_excludes setting in setup.cfg; namely, it's a list of fnmatch-style paths describing files and directories to exclude relative to the source directory. This means you can use the values from the [pbr] autodoc_tree_excludes setting, though you may need to update these if you configured apidoc_module_dir to point to something other than the top-level directory.

  3. Configure apidoc_output_dir in conf.py

    The apidoc_output_dir setting in conf.py works exactly like the [pbr] api_doc_dir setting in setup.cfg; namely, it's a path relative to the documentation source directory to which all API documentation should be written. You can just copy the value from the [pbr] api_doc_dir setting.

  4. Remove settings from setup.cfg

    Remove the following settings from the [pbr] section of the setup.cfg file:

    • autodoc_tree_index_modules
    • autodoc_tree_excludes
    • api_doc_dir

    You may also wish to remove the entirety of the [build_sphinx] section, should you wish to build docs using sphinx-build instead.

Once done, your output should work exactly as before.

autodoc

autodoc is not based on sphinx-apidoc. Fortunately it is possible to generate something very similar (although not identical!). Let's take oslo.privsep as an example, once again looking at minimal versions of setup.cfg and doc/source/conf.py:

[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source

[pbr]
autodoc_index_modules = True
api_doc_dir = reference/api
autodoc_exclude_modules =
  oslo_privsep.tests.*
  oslo_privsep._*
extensions = ['']

Once migrated, this would look like so:

[build_sphinx]
all_files = 1
build-dir = doc/build
source-dir = doc/source
extensions = ['sphinxcontrib.apidoc']

apidoc_module_dir = '../../oslo_privsep'
apidoc_excluded_paths = ['tests', '_*']
apidoc_output_dir = 'reference/api'
apidoc_separate_modules = True

Most of the changes necessary are the same as autodoc_tree, with some exceptions.

  1. Configure apidoc_module_dir in conf.py

    With the autodoc feature, API documentation is always generated for the directory in which setup.cfg exists, which is typically the top-level directory. With this extension, you must explicitly state which directory you wish to build documentation for using the apidoc_module_dir setting. You should configure this to point to your actual package rather than the top level directory as this means you don't need to worry about skipping unrelated files like setup.py.

  2. Configure apidoc_excluded_paths in conf.py

    The apidoc_excluded_paths setting in conf.py differs from the [pbr] autodoc_exclude_modules setting in setup.cfg in that the former is a list of fnmatch-style file paths, while the latter is a list of fnmatch-style module paths. As a result, you can reuse most of the values from the [pbr] autodoc_exclude_modules setting but you must switch from x.y format to x/y. You may also need to update these paths if you configured apidoc_module_dir to point to something other than the top-level directory.

  3. Configure apidoc_output_dir in conf.py

    The apidoc_output_dir setting in conf.py works exactly like the [pbr] api_doc_dir setting in setup.cfg; namely, it's a path relative to the documentation source directory to which all API documentation should be written. You can just copy the value from the [pbr] api_doc_dir setting.

  4. Configure apidoc_separate_modules=True in conf.py

    By default, sphinx-apidoc generates a document per package while autodoc generates a document per (sub)module. By setting this attribute to True, we ensure the latter behavior is used.

  5. Replace references to autoindex.rst with modules.rst

    The autodoc feature generates a list of modules in a file called autoindex.rst located in the output directory. By comparison, sphinx-apidoc and this extension call this file modules.rst. You must update all references to autoindex.rst with modules.rst instead. You may also wish to configure the depth option of any toctrees that include this document as modules.rst is nested.

  6. Remove settings from setup.cfg

    Remove the following settings from the [pbr] section of the setup.cfg file:

    • autodoc_index_modules
    • autodoc_exclude_modules
    • api_doc_dir

    You may also wish to remove the entirety of the [build_sphinx] section, should you wish to build docs using sphinx-build instead.

Once done, your output should look similar to previously. The main change will be in the aforementioned modules.rst, which uses a nested layout compared to the flat layout of the autoindex.rst file.

apidoc's People

Contributors

dhellmann avatar itsmoosh avatar meggycal avatar paulmelnikow avatar stephenfin avatar zaneb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

apidoc's Issues

validate function/method params against docstrings

hi, we are using sphinx-apidoc to generate docs

sphinx-apidoc --output-dir docs/kw --no-toc kw && make --directory=docs html

but devs often forget to update docstrings after changing methods/functions. Is there a tool that is able to validate this? I saw stackoverflow question, but no answer there.

Maybe out of scope, but we are at least checking for warnings/errors from sphinx with this CI step:

docs-valid:
  stage: test
  image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA-py3
  cache:
    key: ${CI_PROJECT_PATH_SLUG}-sphinx
    paths:
      - .doctree/
  script:
    - >
      sphinx-apidoc --output-dir docs/kw --no-toc kw
      && BOOKING_LOGGING_STD_DISABLED=1
      OUTPUT=$(sphinx-build -M html docs docs/_build -q 2>&1 -d .doctree -j 8) || (echo "$OUTPUT"; exit 1);
      echo "$OUTPUT" | grep -E ':[0-9]+: (WARNING|ERROR|SEVERE):' || exit 0;
      echo 'Invalid docstring syntax. Check syntax here -> http://rst.ninjs.org'
      && exit 1

Would be cool if there would be single flag on sphinx-build that would fail on errors/warnings.

Is it possible to run example code to generate an image?

Hello,
Foremost, thanks a lot for this great extension! I was wondering if there is a way to execute example blocks in the docstrings. For example, I have one routine, which modifies input data in a certain way and the result is best seen in form of a plot. I would like to have an example as part of my docstring, e.g.

def myplotting_routine()
    """My docstring

    Example
    ------------
    >>> myplotting_routine()
    """

and have the generated plot attached in the rendered documentation.
Is this possible with apidoc?

Call to `pbr.version.VersionInfo` raises exception on import

I'm using this extension in Docker on a PaaS provider, Zeit Now, where I'm running documentation builds in response to pull requests.

I'm getting this exception when Sphinx starts up:

# Sphinx version: 1.8.1
# Python version: 2.7.15 (CPython)
# Docutils version: 0.14 
  File "/usr/local/lib/python2.7/site-packages/sphinx/registry.py", line 472, in load_extension
    mod = __import__(extname, None, None, ['setup'])
  File "/usr/local/lib/python2.7/site-packages/sphinxcontrib/apidoc/__init__.py", line 15, in <module>
    __version__ = pbr.version.VersionInfo('apidoc').version_string()
  File "/usr/local/lib/python2.7/site-packages/pbr/version.py", line 467, in version_string
    return self.semantic_version().brief_string()
  File "/usr/local/lib/python2.7/site-packages/pbr/version.py", line 462, in semantic_version
    self._semantic = self._get_version_from_pkg_resources()
  File "/usr/local/lib/python2.7/site-packages/pbr/version.py", line 449, in _get_version_from_pkg_resources
    result_string = packaging.get_version(self.package)
  File "/usr/local/lib/python2.7/site-packages/pbr/packaging.py", line 839, in get_version
    name=package_name))
Exception: Versioning for this project requires either an sdist tarball, or access to an upstream git repository. It's also possible that there is a mismatch between the package name in setup.cfg and the argument given to pbr.version.VersionInfo. Project name apidoc was given, but was not able to be found.

This line seems to be causing it:

https://github.com/sphinx-contrib/apidoc/blob/master/sphinxcontrib/apidoc/__init__.py#L15

The docker image is based on the official Python docker image. It doesn't do anything fancy, just running apt-get install and pip install and cleaning the cache. (I tried disabling the cache clean; no dice.)

Turning off the extension makes the problem go away.

I can't reproduce it locally, even in docker. Here's the Zeit build: https://zeit.co/lace/entente/jcdveazgph/logs

The last thing I tried was reinstalling sphinxcontrib-apidoc while the container was running. That didn't work either.

I'd really appreciate any ideas you might have!

Output of pip freeze:
alabaster==0.7.11
Babel==2.6.0
backports.functools-lru-cache==1.5
baiji==2.10.0
baiji-serialization==2.1.0
boto==2.49.0
cached-property==1.5.1
certifi==2018.8.24
chardet==3.0.4
Click==7.0
coloredlogs==10.0
CommonMark==0.5.4
cycler==0.10.0
docutils==0.14
env-flag==1.0.1
fasteners==0.14.1
futures==3.2.0
harrison==1.1.1
humanfriendly==4.16.1
idna==2.7
imagesize==1.1.0
Jinja2==2.10
kiwisolver==1.0.1
lxml==4.2.5
MarkupSafe==1.0
matplotlib==2.2.3
metabaiji-pod==1.0.7.post1
metablmath==1.2.5.post2
metabochumpy==0.67.6.post2
metabolace==1.1.8.dev1
metabolexecutor==20.0.post1
monotonic==1.5
nose2==0.5.0
numpy==1.15.2
packaging==18.0
pbr==4.2.0
plumbum==1.6.7
progressbar==2.5
property-manager==2.3.1
py-windows-exe==1.0.0
Pygments==2.2.0

setuptools version:

setuptools 40.2.0 from /usr/local/lib/python2.7/site-packages (Python 2.7)

Add support for Python 3.12

Currently this project relies on pbr which internally still uses distutils which got removed in Python 3.12. Since pbr is a openstack project, it probably will take a while until it support newer python versions. It is probably a good idea to migrate to a standard pyproject setup in the meantime to support Python 3.12.

Cython modules that use __init__.pyx instead of __init__.py are broken

Cython modules that use __init__.pyx instead of __init__.py are broken and are not picked up properly. (I get a weird entry for the parent folder of the actual module and not the module itself, and that entry is not clickable so it's kind of unbrowseable)

Let me know if this is an upstream bug e.g. in sphinx.ext.autodocor somewhere else, I am a little confused about sphinx's project structure ๐Ÿ˜ฌ

0.3.0: test suite fails

+ py.test
========================================================================================= test session starts ==========================================================================================
platform linux -- Python 3.8.3, pytest-4.6.9, py-1.8.0, pluggy-0.13.1
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/tkloczko/rpmbuild/BUILD/sphinxcontrib-apidoc-0.3.0/.hypothesis/examples')
rootdir: /home/tkloczko/rpmbuild/BUILD/sphinxcontrib-apidoc-0.3.0
plugins: forked-1.1.1, mock-1.10.4, expect-1.1.0, hypothesis-4.23.8, xdist-1.31.0, httpbin-0.3.0, cov-2.8.1, timeout-1.3.4, rerunfailures-8.0, flaky-3.6.1
collected 5 items

tests/test_ext.py EEEEE                                                                                                                                                                          [100%]

================================================================================================ ERRORS ================================================================================================
____________________________________________________________________________________ ERROR at setup of test_basics _____________________________________________________________________________________

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f859000a8b0>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f8590006070>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
>               mod = import_module(extname)

/usr/lib/python3.8/site-packages/sphinx/registry.py:475:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None

    def import_module(name, package=None):
        """Import a module.

        The 'package' argument is required when performing a relative import. It
        specifies the package to use as the anchor point from which to resolve the
        relative import to an absolute import.

        """
        level = 0
        if name.startswith('.'):
            if not package:
                msg = ("the 'package' argument is required to perform a relative "
                       "import for {!r}")
                raise TypeError(msg.format(name))
            for character in name:
                if character != '.':
                    break
                level += 1
>       return _bootstrap._gcd_import(name[level:], package, level)

/usr/lib64/python3.8/importlib/__init__.py:127:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None, level = 0

>   ???

<frozen importlib._bootstrap>:1014:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???

<frozen importlib._bootstrap>:991:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???
E   ModuleNotFoundError: No module named 'sphinxcontrib.apidoc'

<frozen importlib._bootstrap>:973: ModuleNotFoundError

During handling of the above exception, another exception occurred:

test_params = {'shared_result': None}, app_params = app_params(args=['html'], kwargs={'srcdir': path('/tmp/apidoc-yxbzqtx6/basics')}), make_app = <function make_app.<locals>.make at 0x7f858ffcd700>
shared_result = <sphinx.testing.fixtures.SharedResult object at 0x7f858fe5d6d0>

    @pytest.fixture(scope='function')
    def app(test_params, app_params, make_app, shared_result):
        """
        provides sphinx.application.Sphinx object
        """
        args, kwargs = app_params
>       app_ = make_app(*args, **kwargs)

/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:110:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:156: in make
    app_ = util.SphinxTestApp(*args, **kwargs)  # type: Any
/usr/lib/python3.8/site-packages/sphinx/testing/util.py:130: in __init__
    super().__init__(srcdir, confdir, outdir, doctreedir,
/usr/lib/python3.8/site-packages/sphinx/application.py:243: in __init__
    self.setup_extension(extension)
/usr/lib/python3.8/site-packages/sphinx/application.py:402: in setup_extension
    self.registry.load_extension(self, extname)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f859000a8b0>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f8590006070>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
                mod = import_module(extname)
            except ImportError as err:
                logger.verbose(__('Original exception:\n') + traceback.format_exc())
>               raise ExtensionError(__('Could not import extension %s') % extname, err)
E               sphinx.errors.ExtensionError: Could not import extension sphinxcontrib.apidoc (exception: No module named 'sphinxcontrib.apidoc')

/usr/lib/python3.8/site-packages/sphinx/registry.py:478: ExtensionError
___________________________________________________________________________________ ERROR at setup of test_advanced ____________________________________________________________________________________

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858fa0d370>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858fa0daf0>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
>               mod = import_module(extname)

/usr/lib/python3.8/site-packages/sphinx/registry.py:475:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None

    def import_module(name, package=None):
        """Import a module.

        The 'package' argument is required when performing a relative import. It
        specifies the package to use as the anchor point from which to resolve the
        relative import to an absolute import.

        """
        level = 0
        if name.startswith('.'):
            if not package:
                msg = ("the 'package' argument is required to perform a relative "
                       "import for {!r}")
                raise TypeError(msg.format(name))
            for character in name:
                if character != '.':
                    break
                level += 1
>       return _bootstrap._gcd_import(name[level:], package, level)

/usr/lib64/python3.8/importlib/__init__.py:127:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None, level = 0

>   ???

<frozen importlib._bootstrap>:1014:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???

<frozen importlib._bootstrap>:991:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???
E   ModuleNotFoundError: No module named 'sphinxcontrib.apidoc'

<frozen importlib._bootstrap>:973: ModuleNotFoundError

During handling of the above exception, another exception occurred:

test_params = {'shared_result': None}, app_params = app_params(args=['html'], kwargs={'srcdir': path('/tmp/apidoc-yxbzqtx6/advanced')}), make_app = <function make_app.<locals>.make at 0x7f858faaf9d0>
shared_result = <sphinx.testing.fixtures.SharedResult object at 0x7f858fa0db50>

    @pytest.fixture(scope='function')
    def app(test_params, app_params, make_app, shared_result):
        """
        provides sphinx.application.Sphinx object
        """
        args, kwargs = app_params
>       app_ = make_app(*args, **kwargs)

/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:110:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:156: in make
    app_ = util.SphinxTestApp(*args, **kwargs)  # type: Any
/usr/lib/python3.8/site-packages/sphinx/testing/util.py:130: in __init__
    super().__init__(srcdir, confdir, outdir, doctreedir,
/usr/lib/python3.8/site-packages/sphinx/application.py:243: in __init__
    self.setup_extension(extension)
/usr/lib/python3.8/site-packages/sphinx/application.py:402: in setup_extension
    self.registry.load_extension(self, extname)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858fa0d370>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858fa0daf0>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
                mod = import_module(extname)
            except ImportError as err:
                logger.verbose(__('Original exception:\n') + traceback.format_exc())
>               raise ExtensionError(__('Could not import extension %s') % extname, err)
E               sphinx.errors.ExtensionError: Could not import extension sphinxcontrib.apidoc (exception: No module named 'sphinxcontrib.apidoc')

/usr/lib/python3.8/site-packages/sphinx/registry.py:478: ExtensionError
_______________________________________________________________________________ ERROR at setup of test_advanced_negative _______________________________________________________________________________

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858f9aeee0>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858f9aea30>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
>               mod = import_module(extname)

/usr/lib/python3.8/site-packages/sphinx/registry.py:475:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None

    def import_module(name, package=None):
        """Import a module.

        The 'package' argument is required when performing a relative import. It
        specifies the package to use as the anchor point from which to resolve the
        relative import to an absolute import.

        """
        level = 0
        if name.startswith('.'):
            if not package:
                msg = ("the 'package' argument is required to perform a relative "
                       "import for {!r}")
                raise TypeError(msg.format(name))
            for character in name:
                if character != '.':
                    break
                level += 1
>       return _bootstrap._gcd_import(name[level:], package, level)

/usr/lib64/python3.8/importlib/__init__.py:127:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None, level = 0

>   ???

<frozen importlib._bootstrap>:1014:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???

<frozen importlib._bootstrap>:991:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???
E   ModuleNotFoundError: No module named 'sphinxcontrib.apidoc'

<frozen importlib._bootstrap>:973: ModuleNotFoundError

During handling of the above exception, another exception occurred:

test_params = {'shared_result': None}, app_params = app_params(args=['html'], kwargs={'srcdir': path('/tmp/apidoc-yxbzqtx6/advanced-negative')})
make_app = <function make_app.<locals>.make at 0x7f858faff670>, shared_result = <sphinx.testing.fixtures.SharedResult object at 0x7f858f9aeac0>

    @pytest.fixture(scope='function')
    def app(test_params, app_params, make_app, shared_result):
        """
        provides sphinx.application.Sphinx object
        """
        args, kwargs = app_params
>       app_ = make_app(*args, **kwargs)

/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:110:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:156: in make
    app_ = util.SphinxTestApp(*args, **kwargs)  # type: Any
/usr/lib/python3.8/site-packages/sphinx/testing/util.py:130: in __init__
    super().__init__(srcdir, confdir, outdir, doctreedir,
/usr/lib/python3.8/site-packages/sphinx/application.py:243: in __init__
    self.setup_extension(extension)
/usr/lib/python3.8/site-packages/sphinx/application.py:402: in setup_extension
    self.registry.load_extension(self, extname)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858f9aeee0>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858f9aea30>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
                mod = import_module(extname)
            except ImportError as err:
                logger.verbose(__('Original exception:\n') + traceback.format_exc())
>               raise ExtensionError(__('Could not import extension %s') % extname, err)
E               sphinx.errors.ExtensionError: Could not import extension sphinxcontrib.apidoc (exception: No module named 'sphinxcontrib.apidoc')

/usr/lib/python3.8/site-packages/sphinx/registry.py:478: ExtensionError
_____________________________________________________________________________ ERROR at setup of test_missing_configuration _____________________________________________________________________________

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858f99b0a0>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858f99bbe0>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
>               mod = import_module(extname)

/usr/lib/python3.8/site-packages/sphinx/registry.py:475:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None

    def import_module(name, package=None):
        """Import a module.

        The 'package' argument is required when performing a relative import. It
        specifies the package to use as the anchor point from which to resolve the
        relative import to an absolute import.

        """
        level = 0
        if name.startswith('.'):
            if not package:
                msg = ("the 'package' argument is required to perform a relative "
                       "import for {!r}")
                raise TypeError(msg.format(name))
            for character in name:
                if character != '.':
                    break
                level += 1
>       return _bootstrap._gcd_import(name[level:], package, level)

/usr/lib64/python3.8/importlib/__init__.py:127:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None, level = 0

>   ???

<frozen importlib._bootstrap>:1014:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???

<frozen importlib._bootstrap>:991:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???
E   ModuleNotFoundError: No module named 'sphinxcontrib.apidoc'

<frozen importlib._bootstrap>:973: ModuleNotFoundError

During handling of the above exception, another exception occurred:

test_params = {'shared_result': None}, app_params = app_params(args=['html'], kwargs={'srcdir': path('/tmp/apidoc-yxbzqtx6/missing-configuration')})
make_app = <function make_app.<locals>.make at 0x7f858f9fbdc0>, shared_result = <sphinx.testing.fixtures.SharedResult object at 0x7f858f99be50>

    @pytest.fixture(scope='function')
    def app(test_params, app_params, make_app, shared_result):
        """
        provides sphinx.application.Sphinx object
        """
        args, kwargs = app_params
>       app_ = make_app(*args, **kwargs)

/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:110:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:156: in make
    app_ = util.SphinxTestApp(*args, **kwargs)  # type: Any
/usr/lib/python3.8/site-packages/sphinx/testing/util.py:130: in __init__
    super().__init__(srcdir, confdir, outdir, doctreedir,
/usr/lib/python3.8/site-packages/sphinx/application.py:243: in __init__
    self.setup_extension(extension)
/usr/lib/python3.8/site-packages/sphinx/application.py:402: in setup_extension
    self.registry.load_extension(self, extname)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858f99b0a0>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858f99bbe0>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
                mod = import_module(extname)
            except ImportError as err:
                logger.verbose(__('Original exception:\n') + traceback.format_exc())
>               raise ExtensionError(__('Could not import extension %s') % extname, err)
E               sphinx.errors.ExtensionError: Could not import extension sphinxcontrib.apidoc (exception: No module named 'sphinxcontrib.apidoc')

/usr/lib/python3.8/site-packages/sphinx/registry.py:478: ExtensionError
_______________________________________________________________________________ ERROR at setup of test_invalid_directory _______________________________________________________________________________

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858fa17130>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858fa17ca0>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
>               mod = import_module(extname)

/usr/lib/python3.8/site-packages/sphinx/registry.py:475:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None

    def import_module(name, package=None):
        """Import a module.

        The 'package' argument is required when performing a relative import. It
        specifies the package to use as the anchor point from which to resolve the
        relative import to an absolute import.

        """
        level = 0
        if name.startswith('.'):
            if not package:
                msg = ("the 'package' argument is required to perform a relative "
                       "import for {!r}")
                raise TypeError(msg.format(name))
            for character in name:
                if character != '.':
                    break
                level += 1
>       return _bootstrap._gcd_import(name[level:], package, level)

/usr/lib64/python3.8/importlib/__init__.py:127:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', package = None, level = 0

>   ???

<frozen importlib._bootstrap>:1014:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???

<frozen importlib._bootstrap>:991:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

name = 'sphinxcontrib.apidoc', import_ = <function _gcd_import at 0x7f860c3c04c0>

>   ???
E   ModuleNotFoundError: No module named 'sphinxcontrib.apidoc'

<frozen importlib._bootstrap>:973: ModuleNotFoundError

During handling of the above exception, another exception occurred:

test_params = {'shared_result': None}, app_params = app_params(args=['html'], kwargs={'srcdir': path('/tmp/apidoc-yxbzqtx6/invalid-directory')})
make_app = <function make_app.<locals>.make at 0x7f858fa05820>, shared_result = <sphinx.testing.fixtures.SharedResult object at 0x7f858fa17d00>

    @pytest.fixture(scope='function')
    def app(test_params, app_params, make_app, shared_result):
        """
        provides sphinx.application.Sphinx object
        """
        args, kwargs = app_params
>       app_ = make_app(*args, **kwargs)

/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:110:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.8/site-packages/sphinx/testing/fixtures.py:156: in make
    app_ = util.SphinxTestApp(*args, **kwargs)  # type: Any
/usr/lib/python3.8/site-packages/sphinx/testing/util.py:130: in __init__
    super().__init__(srcdir, confdir, outdir, doctreedir,
/usr/lib/python3.8/site-packages/sphinx/application.py:243: in __init__
    self.setup_extension(extension)
/usr/lib/python3.8/site-packages/sphinx/application.py:402: in setup_extension
    self.registry.load_extension(self, extname)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <sphinx.registry.SphinxComponentRegistry object at 0x7f858fa17130>, app = <[AttributeError("'NoneType' object has no attribute 'name'") raised in repr()] SphinxTestApp object at 0x7f858fa17ca0>
extname = 'sphinxcontrib.apidoc'

    def load_extension(self, app, extname):
        # type: (Sphinx, str) -> None
        """Load a Sphinx extension."""
        if extname in app.extensions:  # alread loaded
            return
        if extname in EXTENSION_BLACKLIST:
            logger.warning(__('the extension %r was already merged with Sphinx since '
                              'version %s; this extension is ignored.'),
                           extname, EXTENSION_BLACKLIST[extname])
            return

        # update loading context
        prefix = __('while setting up extension %s:') % extname
        with prefixed_warnings(prefix):
            try:
                mod = import_module(extname)
            except ImportError as err:
                logger.verbose(__('Original exception:\n') + traceback.format_exc())
>               raise ExtensionError(__('Could not import extension %s') % extname, err)
E               sphinx.errors.ExtensionError: Could not import extension sphinxcontrib.apidoc (exception: No module named 'sphinxcontrib.apidoc')

/usr/lib/python3.8/site-packages/sphinx/registry.py:478: ExtensionError
=========================================================================================== warnings summary ===========================================================================================
/usr/lib/python3.8/site-packages/_pytest/mark/structures.py:331
  /usr/lib/python3.8/site-packages/_pytest/mark/structures.py:331: PytestUnknownMarkWarning: Unknown pytest.mark.sphinx - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
    warnings.warn(

-- Docs: https://docs.pytest.org/en/latest/warnings.html
================================================================================= 1 warnings, 5 error in 0.66 seconds ==================================================================================

Support more config options

Thanks for this useful shim!

I'm getting some warnings because there isn't a way to set --no-toc, and I'd also like to be able to set --module-first. It would be great to have a way to provide those options.

warnings created from derived classes when constructor has no docstring

We recently ran into a subtle but reproducible problem generating api docs for some of our projects. The problem case is as follows.

  1. Create a base class with a constructor, and provide both a class doc string and a constructor doc string, something like this:
class MyBase:
    """Base class docstring"""

    def __init__(self, fubar):
        """
        Args:
            fubar (str):
                parameter description here
        """
  1. Create a derived class that has an overloaded constructor, with no doc string, something like this:
class MyDerived(MyBase):
    def __init__(self):
        pass
  1. Enable the auto content option in Sphinx to combine the class doc string and constructor doc strings together (ie: autoclass_content = "both")

  2. Attempt to generate the docs using the apidoc extension.
    Expected behavior: the API docs for both the base and derived classes should be generated without warnings.
    Actual behavior: docs for the base class generate correctly, but docs for the derived class produce the following warning
    "docstring of sample.MyDerived: WARNING: Unexpected indentation."

This is a rather insidious warning because it points the developer to the derived class as the root cause of the problem, but there is no obvious correlation between the code of the derived class and the actual cause of the problem. This is further exacerbated when the derived class is in a different project or library from the base class, because then the developer needs to review the contents of the full inheritance stack. However, even after reviewing the code for the base class (which may or may not be the direct parent class of the one exposing the error) there is no obvious correlation between the warning and the code because the doc strings on the base class(es) are defined and correctly formatted, and Sphinx/apidoc do not complain when generating the docs for the base class.

After much ad-hoc testing I was able to deduce that simply adding a doc string to the constructor on the derived class was sufficient to circumvent the error, but when working on a large number of Python projects and sharing those projects across many developers, problems like this are hard to avoid in practice.

My team manages dozens of Python projects, and are trying to build docs for all without having any warnings produced, so we've opted to disable the combined auto content flag to avoid these superfluous warnings. However, this is just a hack/workaround to get us by for now. I hope someone can investigate the root cause of this problem and fix it so that docs for derived classes can be generated without warnings.

Support templating

Hello

Is it possible to add an option to change the default menu item names:

  • the first level has the name of the module while I'd like to have something like "API Reference". Could be an option such as apidoc_title = "API Reference"
  • is it possible to display the content of a module before its sub-modules?
  • using a lot git submodules, seeing "Submodules" in the menu is weird. Is it possible to simply skip this level and gather all sub-module inside the package?

So instead of:

mypackage_name
    mypackage_name package
        Submodules
            mypackage_name.one_module module
            mypackage_name.another_module module
            mypackage_name.yet_another_one module
        Module contents

We would have something like:

API Reference
    mypackage_name package
        Module contents
        mypackage_name.one_module module
        mypackage_name.another_module module
        mypackage_name.yet_another_one module

Passing multiple apidoc_module_dir's

We have a centralized doc generation where sphinx-apidoc is called on each repo and the generated rst files are used to generate the documentation,

Given this is a pre-stage operation done outside sphinx, current extension seemed like a good way to avoid the pre-stage creation of rst.
Only caveat here is that apidoc_module_dir only accepts a single path. Can this be enhanced to accept a list of paths?

WARNING: Inline literal start-string without end-string.

I am using Sphinx to create HTML documentation for project U-Boot (http://git.denx.de/u-boot.git). The documentation is created via

make htmldocs

The documentation for some functions is rendered incorrectly.

File lib/efi_loader/efi_boottime.c has the following:

/**
 * efi_create_event() - create an event
 * @type:            type of the event to create
 * @notify_tpl:      task priority level of the event
 * @notify_function: notification function of the event
 * @notify_context:  pointer passed to the notification function
 * @group:           event group
 * @event:           created event
 *
 * This function is used inside U-Boot code to create an event.
 *
 * For the API function implementing the CreateEvent service see
 * efi_create_event_ext.
 *
 * Return: status code
 */
efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
                              void (EFIAPI *notify_function) (
                                        struct efi_event *event,
                                        void *context),
                              void *notify_context, efi_guid_t *group,
                              struct efi_event **event)
{

This produces a warning:

./lib/efi_loader/efi_boottime.c:583: WARNING: Inline literal start-string without end-string.
./lib/efi_loader/efi_boottime.c:583: WARNING: Inline emphasis start-string without end-string.
./lib/efi_loader/efi_boottime.c:583: WARNING: Inline emphasis start-string without end-string.
./lib/efi_loader/efi_boottime.c:583: WARNING: Inline emphasis start-string without end-string.

The parameter notfiy_function is rendered as:

``void (EFIAPI *notify_function) ( struct efi_event *event, void *context) ``
    undescribed

Why is the warning thrown?
Why is the parameter description missing?
Why do I get the superfluous ``?

Best regards

Heinrich Schuchardt

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.