pax_global_header00006660000000000000000000000064146211252320014510gustar00rootroot0000000000000052 comment=7f29172b10d4dc251af414ae01e38c3b970686a1 sphinx-click-6.0.0/000077500000000000000000000000001462112523200141075ustar00rootroot00000000000000sphinx-click-6.0.0/.editorconfig000066400000000000000000000012001462112523200165550ustar00rootroot00000000000000# -*- coding: utf-8 -*- root = true [*] indent_style = space end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 # Python files [*.py] indent_size = 4 # isort plugin configuration known_first_party = sphinx_click multi_line_output = 2 default_section = THIRDPARTY skip = .eggs docs # RST files (used by sphinx) [*.rst] indent_size = 3 # CSS, HTML, JS, JSON, YML [*.{css,html,js,json,yml}] indent_size = 2 # Matches the exact files either package.json or .travis.yml [{package.json,.travis.yml}] indent_size = 2 # Dockerfile [Dockerfile] indent_size = 4 # Makefile [Makefile] indent_size = 4 sphinx-click-6.0.0/.github/000077500000000000000000000000001462112523200154475ustar00rootroot00000000000000sphinx-click-6.0.0/.github/pull_request_template.md000066400000000000000000000006541462112523200224150ustar00rootroot00000000000000## Summary ## Tasks - [ ] Added unit tests - [ ] Added documentation for new features (where applicable) - [ ] Added release notes (using [`reno`](https://pypi.org/project/reno/)) - [ ] Ran test suite and style checks and built documentation (`tox`) ## Further details sphinx-click-6.0.0/.github/workflows/000077500000000000000000000000001462112523200175045ustar00rootroot00000000000000sphinx-click-6.0.0/.github/workflows/ci.yaml000066400000000000000000000046721462112523200207740ustar00rootroot00000000000000--- name: CI on: - push - pull_request jobs: lint: name: Run linters runs-on: ubuntu-latest steps: - name: Checkout source code uses: actions/checkout@v3 - name: Set up Python 3.12 uses: actions/setup-python@v4 with: python-version: "3.12" - name: Install dependencies run: python -m pip install tox - name: Run tox run: tox -e style test: name: Run unit tests runs-on: ubuntu-latest strategy: matrix: python: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout source code uses: actions/checkout@v3 # We need history to build the package with: fetch-depth: 0 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - name: Install dependencies run: python -m pip install tox tox-gh-actions - name: Run unit tests (via tox) run: tox docs: name: Build docs runs-on: ubuntu-latest steps: - name: Checkout source code uses: actions/checkout@v3 # We need history for release notes with: fetch-depth: 0 - name: Set up Python 3.12 uses: actions/setup-python@v4 with: python-version: "3.12" - name: Install dependencies run: python -m pip install tox - name: Build docs (via tox) run: tox -e docs - name: Archive build results uses: actions/upload-artifact@v3 with: name: html-docs-build path: docs/_build/html retention-days: 7 release: name: Upload release artifacts runs-on: ubuntu-latest needs: test if: github.event_name == 'push' steps: - name: Checkout source code uses: actions/checkout@v3 # We need history to build the package with: fetch-depth: 0 - name: Set up Python 3.12 uses: actions/setup-python@v4 with: python-version: "3.12" - name: Install dependencies run: python -m pip install build - name: Build a binary wheel and a source tarball run: python -m build --sdist --wheel --outdir dist/ . - name: Publish distribution to PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.PYPI_API_TOKEN }} sphinx-click-6.0.0/.gitignore000066400000000000000000000014311462112523200160760ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Sphinx documentation docs/_build/ # reno RELEASENOTES.rst releasenotes/notes/reno.cache # pbr AUTHORS ChangeLog # virtualenv /.venv # vim *.swp # Intellij .idea sphinx-click-6.0.0/.pre-commit-config.yaml000066400000000000000000000021341462112523200203700ustar00rootroot00000000000000# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks --- default_language_version: # force all unspecified python hooks to run python3 python: python3 repos: - repo: https://github.com/ambv/black rev: 23.7.0 hooks: - id: black - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: mixed-line-ending args: ['--fix', 'lf'] - id: check-byte-order-marker - id: check-executables-have-shebangs - id: check-merge-conflict - id: debug-statements - id: end-of-file-fixer - id: check-yaml files: .*\.(yaml|yml)$ - id: check-added-large-files - repo: https://github.com/pycqa/flake8 rev: 6.1.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.4.1 hooks: - id: mypy additional_dependencies: - types-docutils args: ['--explicit-package-bases'] exclude: | (?x)( docs/.* \ | tests/.* ) sphinx-click-6.0.0/.readthedocs.yaml000066400000000000000000000003201462112523200173310ustar00rootroot00000000000000--- version: 2 python: install: - method: pip path: . sphinx: builder: "dirhtml" build: os: "ubuntu-22.04" tools: python: "3.11" jobs: post_checkout: - git fetch --unshallow sphinx-click-6.0.0/LICENSE000066400000000000000000000021071462112523200151140ustar00rootroot00000000000000The MIT License Copyright (c) 2017 Stephen Finucane http://that.guru/ 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-click-6.0.0/README.rst000066400000000000000000000041121462112523200155740ustar00rootroot00000000000000============ sphinx-click ============ .. image:: https://github.com/click-contrib/sphinx-click/actions/workflows/ci.yaml/badge.svg :target: https://github.com/click-contrib/sphinx-click/actions/workflows/ci.yaml :alt: Build Status .. image:: https://readthedocs.org/projects/sphinx-click/badge/?version=latest :target: https://sphinx-click.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status ``sphinx-click`` is a `Sphinx`__ plugin that allows you to automatically extract documentation from a `Click-based`__ application and include it in your docs. __ http://www.sphinx-doc.org/ __ http://click.pocoo.org/ Installation ------------ Install the plugin using ``pip``: .. code-block:: shell $ pip install sphinx-click Alternatively, install from source by cloning this repo then running ``pip`` locally: .. code-block:: shell $ pip install . Usage ----- .. important:: To document a Click-based application, both the application itself and any additional dependencies required by that application **must be installed**. Enable the plugin in your Sphinx ``conf.py`` file: .. code-block:: python extensions = ['sphinx_click'] Once enabled, you can now use the plugin wherever necessary in the documentation. .. code-block:: .. click:: module:parser :prog: hello-world :nested: full Detailed information on the various options available is provided in the `documentation `_. Alternative ----------- This plugin is perfect to document a Click-based CLI in Sphinx, as it properly renders the help screen and its options in nice HTML with deep links and styling. However, if you are looking to document the source code of a Click-based CLI, and the result of its execution, you might want to check out `click-extra`__. The latter provides the ``.. click:example::`` and ``.. click:run::`` Sphinx directives so you can `capture and render, with full colors, the result of your CLI in your documentation`__. __ https://github.com/kdeldycke/click-extra/ __ https://kdeldycke.github.io/click-extra/sphinx.html sphinx-click-6.0.0/docs/000077500000000000000000000000001462112523200150375ustar00rootroot00000000000000sphinx-click-6.0.0/docs/changelog.rst000066400000000000000000000000751462112523200175220ustar00rootroot00000000000000Changes ======= .. include:: ../ChangeLog :start-line: 2 sphinx-click-6.0.0/docs/conf.py000066400000000000000000000034331462112523200163410ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # sphinx-click documentation build configuration file import os import sys sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('../examples')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '2.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx_click'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'sphinx-click' copyright = '2017-, Stephen Finucane' author = 'Stephen Finucane' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '' # The full version, including alpha/beta/rc tags. release = '' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' sphinx-click-6.0.0/docs/contributing.rst000066400000000000000000000022601462112523200203000ustar00rootroot00000000000000Contribution ============ We welcome all contributions to `sphinx-click`. Support ------- Open and issue in the `issue tracker`_ for all support requests. `StackOverflow`_ is also worth considering. Reporting Issues ---------------- Report all issues in the `issue tracker`_. When doing so, please include version information for: - Python - `click` - `sphinx-click` Submitting Patches ------------------ All patches should be submitted as pull requests on the `GitHub project`_. - Include tests if fixing a bug - Clearly explain what you're trying to accomplish - Follow :pep:`8`. You can use the `pep8` tox target for this Testing ------- `sphinx-click` uses `tox` and `unittest` for testing. To run all tests, run: .. code-block:: shell $ tox We support a number of Python versions. To list available environments, run: .. code-block:: shell $ tox --list To run one of these environments, such as `py27` which runs tests under Python 2.7, run: .. code-block:: shell $ tox -e py27 .. _issue tracker: https://github.com/click-contrib/sphinx-click/issues .. _StackOverflow: https://stackoverflow.com .. _GitHub project: https://github.com/click-contrib/sphinx-click sphinx-click-6.0.0/docs/examples/000077500000000000000000000000001462112523200166555ustar00rootroot00000000000000sphinx-click-6.0.0/docs/examples/commandcollections.rst000066400000000000000000000011431462112523200232630ustar00rootroot00000000000000Documenting command collections =============================== Consider the following sample application, using |CommandCollection|_: .. literalinclude:: ../../examples/commandcollections/cli.py This can be documented using *sphinx-click* like so: .. code-block:: rst .. click:: commandcollections.cli:cli :prog: cli :nested: full The rendered example is shown below. ---- .. click:: commandcollections.cli:cli :prog: cli :nested: full .. |CommandCollection| replace:: ``CommandCollection`` .. _CommandCollection: https://click.palletsprojects.com/en/7.x/api/#click.CommandCollection sphinx-click-6.0.0/docs/examples/commands.rst000066400000000000000000000007751462112523200212210ustar00rootroot00000000000000Documenting commands ==================== Consider the following sample application, using |Command|_: .. literalinclude:: ../../examples/commands/cli.py This can be documented using *sphinx-click* like so: .. code-block:: rst .. click:: commands.cli:cli :prog: cli :nested: full The rendered example is shown below. ---- .. click:: commands.cli:cli :prog: cli :nested: full .. |Command| replace:: ``Command`` .. _Command: https://click.palletsprojects.com/en/7.x/api/#click.Command sphinx-click-6.0.0/docs/examples/groups.rst000066400000000000000000000007521462112523200207320ustar00rootroot00000000000000Documenting groups ================== Consider the following sample application, using |Group|_: .. literalinclude:: ../../examples/groups/cli.py This can be documented using *sphinx-click* like so: .. code-block:: rst .. click:: groups.cli:cli :prog: cli :nested: full The rendered example is shown below. ---- .. click:: groups.cli:cli :prog: cli :nested: full .. |Group| replace:: ``Groups`` .. _Group: https://click.palletsprojects.com/en/7.x/api/#click.Group sphinx-click-6.0.0/docs/examples/index.rst000066400000000000000000000001351462112523200205150ustar00rootroot00000000000000Examples ======== .. toctree:: :maxdepth: 1 commands groups commandcollections sphinx-click-6.0.0/docs/index.rst000066400000000000000000000011041462112523200166740ustar00rootroot00000000000000sphinx-click ============ ``sphinx-click`` is a `Sphinx`__ plugin that allows you to automatically extract documentation from a `click-based`__ application and include it in your docs. __ http://www.sphinx-doc.org/ __ http://click.pocoo.org/ .. toctree:: :maxdepth: 2 installation usage contributing changelog examples/index .. seealso:: Module ``click`` This extension assumes you are using ``click`` to create your command line application. Module ``sphinxcontrib.autoprogram`` An equivalent library for use with ``argparse``. sphinx-click-6.0.0/docs/installation.rst000066400000000000000000000010301462112523200202640ustar00rootroot00000000000000Installation ============ Install the plugin using `pip`: .. code-block:: shell $ pip install sphinx-click Alternatively, install from source by cloning this repo then running `pip` locally: .. code-block:: shell $ pip install . *sphinx-click* supports both `click`__ and `asyncclick`__. If *asyncclick* is found, it will be preferred. .. important:: Both the package you're referencing and any dependencies **must be installed**. .. __: https://pypi.org/project/click/ .. __: https://pypi.org/project/asyncclick/ sphinx-click-6.0.0/docs/usage.rst000066400000000000000000000235411462112523200167020ustar00rootroot00000000000000Usage ===== To enable the plugin, add the extension to the list of extensions in your Sphinx `conf.py` file: .. code-block:: python extensions = ['sphinx_click'] Once enabled, *sphinx-click* enables automatic documentation for `click-based`_ applications by way of a `Sphinx directive`_. .. rst:directive:: .. click:: module:parser Automatically extract documentation from a `click-based`_ application and include it in your docs. .. code-block:: rst .. click:: module:parser :prog: hello-world :nested: full The directive takes the import name of a *click* object as its sole argument. This should be a subclass of |click.core.BaseCommand|_, such as ``click.Command``, ``click.Group``, ``click.MultiCommand``, etc. In addition, the following options are required: ``:prog:`` The name of your tool (or how it should appear in your documentation). For example, if you run your script as ``./boo --opts args`` then ``:prog:`` will be ``boo``. If this is not given, the module name is used. The following options are optional: ``:nested:`` Whether subcommands should also be shown. One of: ``full`` List sub-commands with full documentation. ``short`` List sub-commands with short documentation. ``none`` Do not list sub-commands. Defaults to ``short`` unless ``show-nested`` (deprecated) is set. ``:commands:`` Document only listed commands. ``:show-nested:`` This option is deprecated; use ``nested`` instead. The generated documentation includes anchors for the generated commands, their options and their environment variables using the `Sphinx standard domain`_. .. _cross-referencing: Cross-referencing ----------------- As discussed above, the documentation generated by *sphinx-click* includes anchors for the generated commands, their options and their environment variables using the `Sphinx standard domain`_. Specifically, it uses the |program directive|_, |option directive|_, and |envvar directive|_ directives. Options (e.g. ``--param``) The ``option`` directive can be cross-referenced using the |option role|_ role. Environment variables The ``envvar`` directive can be cross-references using the |ref role|_ role. *sphinx-click* generates labels in the format ``{command_name}-{param_name}-{envvar}``. It is **not** currently possible to use the |envvar role|_ role because the default labels generated by Sphinx are not namespaced and will generate conflicts if the same environment variable is used in multiple commands. See `issue #26`__ for more information. __ https://github.com/click-contrib/sphinx-click/issues/26 Programs Sphinx currently does not allow you to cross-reference programs. See `Sphinx issue #880`__ for more information. __ https://github.com/sphinx-doc/sphinx/issues/880 .. _mocking: Mocking ------- *sphinx-click* allows for modules to be mocked out using the same method used by `sphinx.ext.autodoc`_. Modules to mock while the documentation is being built can be specified using the ``sphinx_click_mock_imports`` config value, if specified. Otherwise the value of ``autodoc_mock_imports`` is used, following the behavior of ``sphinx.ext.autosummary``. The value of this config option should be a list of module names; see `sphinx.ext.autodoc`_ for more information. .. _events: Events ------ *sphinx-click* provides the following additional events: .. py:function:: sphinx-click-process-description(app, ctx, lines) .. py:function:: sphinx-click-process-usage(app, ctx, lines) .. py:function:: sphinx-click-process-options(app, ctx, lines) .. py:function:: sphinx-click-process-arguments(app, ctx, lines) .. py:function:: sphinx-click-process-envvars(app, ctx, lines) .. py:function:: sphinx-click-process-epilog(app, ctx, lines) :param app: the Sphinx application object :param ctx: the ``click.Context`` object used to generate the description :param lines: the lines of the documentation, see below Events are emitted when sphinx-click has read and processed part of a command's documentation. *lines* is a list of strings -- the lines of the documentation that was processed -- that the event handler can modify **in place** to change what Sphinx puts into the output. .. code-block:: python def process_description(app, ctx, lines): """Append some text to the "example" command description.""" if ctx.command.name == "example": lines.extend(["Hello, World!", ""]) def setup(app): app.connect("sphinx-click-process-description", process_description) Example ------- Take the below ``click`` application, which is defined in the ``hello_world`` module: .. code-block:: python import click @click.group() def greet(): """A sample command group.""" pass @greet.command() @click.argument('user', envvar='USER') def hello(user): """Greet a user.""" click.echo('Hello %s' % user) @greet.command() def world(): """Greet the world.""" click.echo('Hello world!') To document this, use the following: .. code-block:: rst .. click:: hello_world:greet :prog: hello-world By default, the subcommand, ``hello``, is listed but no documentation provided. If you wish to include full documentation for the subcommand in the output, configure the ``nested`` flag to ``full``. .. code-block:: rst .. click:: hello_world:greet :prog: hello-world :nested: full .. note:: The ``nested`` flag replaces the deprecated ``show-nested`` flag. Conversely, if you do not wish to list these subcommands or wish to handle them separately, configure the ``nested`` flag to ``none``. .. code-block:: rst .. click:: hello_world:greet :prog: hello-world :nested: none You can also document only selected commands by using ``:commands:`` option. .. code-block:: rst .. click:: hello_world:greet :prog: hello-world :commands: hello You can cross-reference the commands, option and environment variables using the roles provided by the `Sphinx standard domain`_. See :ref:`cross-referencing` for more information. .. code-block:: rst .. click:: hello_world:greet :prog: hello-world The :program:`hello` command accepts a :option:`user` argument. If this is not provided, the :envvar:`USER` environment variable will be used. .. note:: Cross-referencing using the ``:program:`` directive is not currently supported by Sphinx. Refer to the `Sphinx issue`__ for more information. __ https://github.com/sphinx-doc/sphinx/issues/880 Documenting |CommandCollection|_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When building more complex CLI, one might need to bring together multiple groups of commands and make them accessible using a single client with |CommandCollection|_. *sphinx-click* renders collection of commands with multiple sections, one for each group listed in the command ``sources``. The group names are used as section titles and the help string from the description are used as section description. Thus, a client defined using a |CommandCollection| as ``cli`` can be rendered using *sphinx-click* and the following directive: .. code-block:: rst .. click:: cli:cli :prog: cli :nested: full This will render the subcommands of each group in different sections, one for each group in ``sources``. An example is provided in :doc:`examples/commandcollections`. Modifying ``sys.path`` ---------------------- If the application or script you wish to document is not installed (i.e. you have not installed it with *pip* or run ``python setup.py``), then you may need to modify ``sys.path``. For example, given the following application:: git |- git | |- __init__.py | \- git.py \- docs |- git.rst |- index.rst \- conf.py then it would be necessary to add the following to ``git/docs/conf.py``: .. code-block:: python import os import sys sys.path.insert(0, os.path.abspath('..')) Once done, you could include the following in ``git/docs/git.rst`` to document the application: .. code-block:: rst .. click:: git.git:cli :prog: git :nested: full assuming the group or command in ``git.git`` is named ``cli``. Refer to `issue #2 `__ for more information. .. URLs .. _Sphinx directive: http://www.sphinx-doc.org/en/stable/extdev/markupapi.html .. _click-based: https://click.palletsprojects.com/en/8.0.x .. _Sphinx standard domain: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#the-standard-domain .. |click.core.BaseCommand| replace:: ``click.core.BaseCommand`` .. _click.core.BaseCommand: https://click.palletsprojects.com/en/8.0.x/api/#click.BaseCommand .. |CommandCollection| replace:: :code:`CommandCollection` .. _CommandCollection: https://click.palletsprojects.com/en/7.x/api/#click.CommandCollection .. |program directive| replace:: ``program`` .. _program directive: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-program .. |option directive| replace:: ``option`` .. _option directive: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-option .. |envvar directive| replace:: ``envvar`` .. _envvar directive: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-envvar .. |option role| replace:: ``:option:`` .. _option role: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-option .. |ref role| replace:: ``:ref:`` .. _ref role: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-ref .. |envvar role| replace:: ``:envvar:`` .. _envvar role: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-envvar .. _sphinx.ext.autodoc: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_mock_imports sphinx-click-6.0.0/examples/000077500000000000000000000000001462112523200157255ustar00rootroot00000000000000sphinx-click-6.0.0/examples/commandcollections/000077500000000000000000000000001462112523200216025ustar00rootroot00000000000000sphinx-click-6.0.0/examples/commandcollections/__init__.py000066400000000000000000000000001462112523200237010ustar00rootroot00000000000000sphinx-click-6.0.0/examples/commandcollections/cli.py000066400000000000000000000014741462112523200227310ustar00rootroot00000000000000# file: cli.py import click main = click.Group( name='Principal Commands', help=( "Principal commands that are used in ``cli``.\n\n" "The section name and description are obtained using the name and " "description of the group passed as sources for |CommandCollection|_." ), ) @main.command(help='CMD 1') def cmd1() -> None: print('call cmd 1') helpers = click.Group( name='Helper Commands', help="Helper commands for ``cli``.", ) @helpers.command() def cmd2() -> None: "Helper command that has no option." pass @helpers.command() @click.option('--user', type=str) def cmd3(user: str) -> None: "Helper command with an option." pass cli = click.CommandCollection( name='cli', sources=[main, helpers], help='Some general info on ``cli``.', ) sphinx-click-6.0.0/examples/commands/000077500000000000000000000000001462112523200175265ustar00rootroot00000000000000sphinx-click-6.0.0/examples/commands/cli.py000066400000000000000000000013061462112523200206470ustar00rootroot00000000000000# file: cli.py import click @click.command() @click.option('--param', envvar='PARAM', help='A sample option') @click.option('--another', metavar='[FOO]', help='Another option') @click.option( '--choice', help='A sample option with choices', type=click.Choice(['Option1', 'Option2']), ) @click.option( '--numeric-choice', metavar='', help='A sample option with numeric choices', type=click.Choice([1, 2, 3]), ) @click.option( '--flag', is_flag=True, help='A boolean flag', ) @click.argument('ARG', envvar='ARG') def cli( param: str, another: str, choice: str, numeric_choice: int, flag: bool, ) -> None: """A sample command.""" pass sphinx-click-6.0.0/examples/groups/000077500000000000000000000000001462112523200172445ustar00rootroot00000000000000sphinx-click-6.0.0/examples/groups/cli.py000066400000000000000000000007351462112523200203720ustar00rootroot00000000000000# file: cli.py import click @click.group() @click.option( '--debug', default=False, is_flag=True, help="Output more information about what's going on.", ) def cli(debug: bool) -> None: """A sample command group.""" pass @cli.command() @click.option('--param', envvar='PARAM', help='A sample option') @click.option('--another', metavar='[FOO]', help='Another option') def hello(param: str, another: str) -> None: """A sample command.""" pass sphinx-click-6.0.0/examples/setup.py000066400000000000000000000002311462112523200174330ustar00rootroot00000000000000from setuptools import find_packages, setup setup( name='sphinx_click_examples', packages=find_packages('.'), install_requires=['click'], ) sphinx-click-6.0.0/pyproject.toml000066400000000000000000000003011462112523200170150ustar00rootroot00000000000000[tool.black] line-length = 88 target-version = ['py37'] skip-string-normalization = true exclude = ''' ( /( \.eggs | \.git | \.tox | \.venv | build | dist ) ) ''' sphinx-click-6.0.0/releasenotes/000077500000000000000000000000001462112523200166005ustar00rootroot00000000000000sphinx-click-6.0.0/releasenotes/notes/000077500000000000000000000000001462112523200177305ustar00rootroot00000000000000sphinx-click-6.0.0/releasenotes/notes/add-auto_envvar_prefix-support-a08e68aba792ee26.yaml000066400000000000000000000005371462112523200312700ustar00rootroot00000000000000--- features: - | The ``auto_envvar_prefix`` parameter, which enables automatic environment variables for all parameters in a click application, is now supported. For more information on this feature, refer to the `click documentation`__. .. __: https://click.palletsprojects.com/en/8.1.x/options/#values-from-environment-variables sphinx-click-6.0.0/releasenotes/notes/add-event-support-e55edb86d86d1082.yaml000066400000000000000000000010751462112523200264260ustar00rootroot00000000000000--- features: - | A number of events are now supported: - ``sphinx-click-process-description`` - ``sphinx-click-process-usage`` - ``sphinx-click-process-options`` - ``sphinx-click-process-arguments`` - ``sphinx-click-process-envvars`` - ``sphinx-click-process-epilog`` These allow you to plug in to the build process for documentation and post-process the generated command documentation. For more information, refer to the `documentation`__. .. __: https://sphinx-click.readthedocs.io/en/latest/usage/#docstring-processing sphinx-click-6.0.0/releasenotes/notes/add-mock-modules-support-d4663c3265eeba5e.yaml000066400000000000000000000003671462112523200277570ustar00rootroot00000000000000--- features: - | Added support for mocking imported modules during the build process. Mocked modules can either be specified from a ``sphinx_click_mock_imports`` variable, if specified, or by default using ``autodoc_mock_imports``. sphinx-click-6.0.0/releasenotes/notes/add-show_default-from-context-support-ab4aeffcc513502d.yaml000066400000000000000000000004111462112523200325770ustar00rootroot00000000000000--- fixes: - | Added support for ``show_default`` as defined in click context settings. This allows for option defaults to be rendered in the output. Consistent with click version 8.1.x, ``show_default`` is overridden by ``Command.show_default``. sphinx-click-6.0.0/releasenotes/notes/better-string-defaults-3664ae102b044972.yaml000066400000000000000000000004351462112523200272010ustar00rootroot00000000000000--- features: - | Now when the default value of an option is a string it will be rendered with quotes around it. fixes: - | If an option has an empty string default, docutils will no longer emit warnings saying ``CRITICAL: Unexpected section title or transition.`` sphinx-click-6.0.0/releasenotes/notes/drop-click-7-cbdaccc6a64029d0.yaml000066400000000000000000000002401462112523200254230ustar00rootroot00000000000000--- upgrade: - | click 7.x is no longer supported. The minimum click version now supported is 8.0.0. - | sphinx < 4.0.0 is no longer supported. sphinx-click-6.0.0/releasenotes/notes/drop-python-3-7-support-3d3cab951fc6ae82.yaml000066400000000000000000000001301462112523200274620ustar00rootroot00000000000000--- upgrade: - | Python 3.7 is no longer supported as it has reached end-of-life. sphinx-click-6.0.0/releasenotes/notes/python-3.11-support-64013e70ae9926f4.yaml000066400000000000000000000001011462112523200263000ustar00rootroot00000000000000--- features: - | Python 3.11 is now officially supported. sphinx-click-6.0.0/releasenotes/notes/python-3.12-support-f2ed1dc438bd42ee.yaml000066400000000000000000000001011462112523200266530ustar00rootroot00000000000000--- features: - | Python 3.12 is now officially supported. sphinx-click-6.0.0/releasenotes/notes/show-typer-arg-help-text-fae802f6878fa100.yaml000066400000000000000000000003201462112523200276250ustar00rootroot00000000000000--- features: - | ``typer.main.TyperArgument`` is a subclass of ``click.Argument`` that, among other things, adds a ``help`` attribute. If present this attribute will now be included in output. sphinx-click-6.0.0/requirements.txt000066400000000000000000000000401462112523200173650ustar00rootroot00000000000000sphinx>=4.0 click>=8.0 docutils sphinx-click-6.0.0/setup.cfg000066400000000000000000000030001462112523200157210ustar00rootroot00000000000000[metadata] name = sphinx-click summary = Sphinx extension that automatically documents click applications description_file = README.rst description_content_type = text/x-rst; charset=UTF-8 author = Stephen Finucane author_email = stephen@that.guru url = https://github.com/click-contrib/sphinx-click project_urls = Bug Tracker = https://github.com/click-contrib/sphinx-click/issues Documentation = https://sphinx-click.readthedocs.io/en/latest Source Code = https://github.com/click-contrib/sphinx-click license = MIT License classifiers = Development Status :: 5 - Production/Stable Environment :: Console Framework :: Sphinx :: Extension Intended Audience :: Developers Intended Audience :: Information Technology License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Topic :: Documentation Topic :: Documentation :: Sphinx Topic :: Utilities python_requires = >=3.8 keywords = sphinx click [files] packages = sphinx_click [flake8] max-line-length = 88 ignore = E203,E501,E741,W503 [mypy] # python_version = 3.9 disallow_untyped_defs = true disallow_incomplete_defs = true no_implicit_optional = true show_column_numbers = true show_error_context = true ignore_missing_imports = true check_untyped_defs = true warn_unused_ignores = true warn_return_any = true strict_optional = true exclude = (?x)( docs/.* \ | tests/.* ) sphinx-click-6.0.0/setup.py000066400000000000000000000001561462112523200156230ustar00rootroot00000000000000#!/usr/bin/env python3 from setuptools import setup setup( setup_requires=['pbr>=2.0'], pbr=True, ) sphinx-click-6.0.0/sphinx_click/000077500000000000000000000000001462112523200165655ustar00rootroot00000000000000sphinx-click-6.0.0/sphinx_click/__init__.py000066400000000000000000000000531462112523200206740ustar00rootroot00000000000000from sphinx_click.ext import setup # noqa sphinx-click-6.0.0/sphinx_click/ext.py000066400000000000000000000447101462112523200177450ustar00rootroot00000000000000import inspect import functools import re import traceback import typing as ty import warnings try: import asyncclick as click except ImportError: import click import click.core from docutils import nodes from docutils.parsers import rst from docutils.parsers.rst import directives from docutils import statemachine from sphinx import application from sphinx.util import logging from sphinx.util import nodes as sphinx_nodes from sphinx.ext.autodoc import mock LOG = logging.getLogger(__name__) NESTED_FULL = 'full' NESTED_SHORT = 'short' NESTED_NONE = 'none' NestedT = ty.Literal['full', 'short', 'none', None] ANSI_ESC_SEQ_RE = re.compile(r'\x1B\[\d+(;\d+){0,2}m', flags=re.MULTILINE) _T_Formatter = ty.Callable[[click.Context], ty.Generator[str, None, None]] def _process_lines(event_name: str) -> ty.Callable[[_T_Formatter], _T_Formatter]: def decorator(func: _T_Formatter) -> _T_Formatter: @functools.wraps(func) def process_lines(ctx: click.Context) -> ty.Generator[str, None, None]: lines = list(func(ctx)) if "sphinx-click-env" in ctx.meta: ctx.meta["sphinx-click-env"].app.events.emit(event_name, ctx, lines) for line in lines: yield line return process_lines return decorator def _indent(text: str, level: int = 1) -> str: prefix = ' ' * (4 * level) def prefixed_lines() -> ty.Generator[str, None, None]: for line in text.splitlines(True): yield (prefix + line if line.strip() else line) return ''.join(prefixed_lines()) def _get_usage(ctx: click.Context) -> str: """Alternative, non-prefixed version of 'get_usage'.""" formatter = ctx.make_formatter() pieces = ctx.command.collect_usage_pieces(ctx) formatter.write_usage(ctx.command_path, ' '.join(pieces), prefix='') return formatter.getvalue().rstrip('\n') # type: ignore def _get_help_record(ctx: click.Context, opt: click.core.Option) -> ty.Tuple[str, str]: """Re-implementation of click.Opt.get_help_record. The variant of 'get_help_record' found in Click makes uses of slashes to separate multiple opts, and formats option arguments using upper case. This is not compatible with Sphinx's 'option' directive, which expects comma-separated opts and option arguments surrounded by angle brackets [1]. [1] http://www.sphinx-doc.org/en/stable/domains.html#directive-option """ def _write_opts(opts: ty.List[str]) -> str: rv, _ = click.formatting.join_options(opts) if not opt.is_flag and not opt.count: name = opt.name if opt.metavar: name = opt.metavar.lstrip('<[{($').rstrip('>]})$') rv += ' <{}>'.format(name) return rv # type: ignore rv = [_write_opts(opt.opts)] if opt.secondary_opts: rv.append(_write_opts(opt.secondary_opts)) out = [] if opt.help: if opt.required: out.append('**Required** %s' % opt.help) else: out.append(opt.help) else: if opt.required: out.append('**Required**') extras = [] if opt.show_default is not None: show_default = opt.show_default else: show_default = ctx.show_default if isinstance(show_default, str): # Starting from Click 7.0 show_default can be a string. This is # mostly useful when the default is not a constant and # documentation thus needs a manually written string. extras.append(':default: ``%r``' % ANSI_ESC_SEQ_RE.sub('', show_default)) elif show_default and opt.default is not None: extras.append( ':default: ``%s``' % ( ', '.join(repr(d) for d in opt.default) if isinstance(opt.default, (list, tuple)) else repr(opt.default), ) ) if isinstance(opt.type, click.Choice): extras.append(':options: %s' % ' | '.join(str(x) for x in opt.type.choices)) if extras: if out: out.append('') out.extend(extras) return ', '.join(rv), '\n'.join(out) def _format_help(help_string: str) -> ty.Generator[str, None, None]: help_string = inspect.cleandoc(ANSI_ESC_SEQ_RE.sub('', help_string)) bar_enabled = False for line in statemachine.string2lines( help_string, tab_width=4, convert_whitespace=True ): if line == '\b': bar_enabled = True continue if line == '': bar_enabled = False line = '| ' + line if bar_enabled else line yield line yield '' @_process_lines("sphinx-click-process-description") def _format_description(ctx: click.Context) -> ty.Generator[str, None, None]: """Format the description for a given `click.Command`. We parse this as reStructuredText, allowing users to embed rich information in their help messages if they so choose. """ help_string = ctx.command.help or ctx.command.short_help if help_string: yield from _format_help(help_string) @_process_lines("sphinx-click-process-usage") def _format_usage(ctx: click.Context) -> ty.Generator[str, None, None]: """Format the usage for a `click.Command`.""" yield '.. code-block:: shell' yield '' for line in _get_usage(ctx).splitlines(): yield _indent(line) yield '' def _format_option( ctx: click.Context, opt: click.core.Option ) -> ty.Generator[str, None, None]: """Format the output for a `click.core.Option`.""" opt_help = _get_help_record(ctx, opt) yield '.. option:: {}'.format(opt_help[0]) if opt_help[1]: yield '' bar_enabled = False for line in statemachine.string2lines( ANSI_ESC_SEQ_RE.sub('', opt_help[1]), tab_width=4, convert_whitespace=True ): if line == '\b': bar_enabled = True continue if line == '': bar_enabled = False line = '| ' + line if bar_enabled else line yield _indent(line) @_process_lines("sphinx-click-process-options") def _format_options(ctx: click.Context) -> ty.Generator[str, None, None]: """Format all `click.Option` for a `click.Command`.""" # the hidden attribute is part of click 7.x only hence use of getattr params = [ param for param in ctx.command.params if isinstance(param, click.core.Option) and not getattr(param, 'hidden', False) ] for param in params: for line in _format_option(ctx, param): yield line yield '' def _format_argument(arg: click.Argument) -> ty.Generator[str, None, None]: """Format the output of a `click.Argument`.""" yield '.. option:: {}'.format(arg.human_readable_name) yield '' yield _indent( '{} argument{}'.format( 'Required' if arg.required else 'Optional', '(s)' if arg.nargs != 1 else '' ) ) # Subclasses of click.Argument may add a `help` attribute (like typer.main.TyperArgument) help = getattr(arg, 'help', None) if help: yield '' help_string = ANSI_ESC_SEQ_RE.sub('', help) for line in _format_help(help_string): yield _indent(line) @_process_lines("sphinx-click-process-arguments") def _format_arguments(ctx: click.Context) -> ty.Generator[str, None, None]: """Format all `click.Argument` for a `click.Command`.""" params = [x for x in ctx.command.params if isinstance(x, click.Argument)] for param in params: for line in _format_argument(param): yield line yield '' def _format_envvar( param: ty.Union[click.core.Option, click.Argument] ) -> ty.Generator[str, None, None]: """Format the envvars of a `click.Option` or `click.Argument`.""" yield '.. envvar:: {}'.format(param.envvar) yield ' :noindex:' yield '' if isinstance(param, click.Argument): param_ref = param.human_readable_name else: # if a user has defined an opt with multiple "aliases", always use the # first. For example, if '--foo' or '-f' are possible, use '--foo'. param_ref = param.opts[0] yield _indent('Provide a default for :option:`{}`'.format(param_ref)) @_process_lines("sphinx-click-process-envars") def _format_envvars(ctx: click.Context) -> ty.Generator[str, None, None]: """Format all envvars for a `click.Command`.""" auto_envvar_prefix = ctx.auto_envvar_prefix if auto_envvar_prefix is not None: params = [] for param in ctx.command.params: if not param.envvar: param.envvar = f"{auto_envvar_prefix}_{param.name.upper()}" params.append(param) else: params = [x for x in ctx.command.params if x.envvar] for param in params: yield '.. _{command_name}-{param_name}-{envvar}:'.format( command_name=ctx.command_path.replace(' ', '-'), param_name=param.name, envvar=param.envvar, ) yield '' for line in _format_envvar(param): yield line yield '' def _format_subcommand(command: click.Command) -> ty.Generator[str, None, None]: """Format a sub-command of a `click.Command` or `click.Group`.""" yield '.. object:: {}'.format(command.name) short_help = command.get_short_help_str() if short_help: yield '' for line in statemachine.string2lines( short_help, tab_width=4, convert_whitespace=True ): yield _indent(line) @_process_lines("sphinx-click-process-epilog") def _format_epilog(ctx: click.Context) -> ty.Generator[str, None, None]: """Format the epilog for a given `click.Command`. We parse this as reStructuredText, allowing users to embed rich information in their help messages if they so choose. """ if ctx.command.epilog: yield from _format_help(ctx.command.epilog) def _get_lazyload_commands(ctx: click.Context) -> ty.Dict[str, click.Command]: commands = {} for command in ctx.command.list_commands(ctx): commands[command] = ctx.command.get_command(ctx, command) return commands def _filter_commands( ctx: click.Context, commands: ty.Optional[ty.List[str]] = None, ) -> ty.List[click.Command]: """Return list of used commands.""" lookup = getattr(ctx.command, 'commands', {}) if not lookup and isinstance(ctx.command, click.MultiCommand): lookup = _get_lazyload_commands(ctx) if commands is None: return sorted(lookup.values(), key=lambda item: item.name) return [lookup[command] for command in commands if command in lookup] def _format_command( ctx: click.Context, nested: NestedT, commands: ty.Optional[ty.List[str]] = None, ) -> ty.Generator[str, None, None]: """Format the output of `click.Command`.""" if ctx.command.hidden: return None # description for line in _format_description(ctx): yield line yield '.. program:: {}'.format(ctx.command_path) # usage for line in _format_usage(ctx): yield line # options lines = list(_format_options(ctx)) if lines: # we use rubric to provide some separation without exploding the table # of contents yield '.. rubric:: Options' yield '' for line in lines: yield line # arguments lines = list(_format_arguments(ctx)) if lines: yield '.. rubric:: Arguments' yield '' for line in lines: yield line # environment variables lines = list(_format_envvars(ctx)) if lines: yield '.. rubric:: Environment variables' yield '' for line in lines: yield line # description for line in _format_epilog(ctx): yield line # if we're nesting commands, we need to do this slightly differently if nested in (NESTED_FULL, NESTED_NONE): return command_objs = _filter_commands(ctx, commands) if command_objs: yield '.. rubric:: Commands' yield '' for command_obj in command_objs: # Don't show hidden subcommands if command_obj.hidden: continue for line in _format_subcommand(command_obj): yield line yield '' def nested(argument: ty.Optional[str]) -> NestedT: values = (NESTED_FULL, NESTED_SHORT, NESTED_NONE, None) if argument not in values: raise ValueError( "%s is not a valid value for ':nested:'; allowed values: %s" % directives.format_values(values) ) return ty.cast(NestedT, argument) class ClickDirective(rst.Directive): has_content = False required_arguments = 1 option_spec = { 'prog': directives.unchanged_required, 'nested': nested, 'commands': directives.unchanged, 'show-nested': directives.flag, } def _load_module(self, module_path: str) -> ty.Union[click.Command, click.Group]: """Load the module.""" try: module_name, attr_name = module_path.split(':', 1) except ValueError: # noqa raise self.error( '"{}" is not of format "module:parser"'.format(module_path) ) try: with mock(self.env.config.sphinx_click_mock_imports): mod = __import__(module_name, globals(), locals(), [attr_name]) except (Exception, SystemExit) as exc: # noqa err_msg = 'Failed to import "{}" from "{}". '.format(attr_name, module_name) if isinstance(exc, SystemExit): err_msg += 'The module appeared to call sys.exit()' else: err_msg += 'The following exception was raised:\n{}'.format( traceback.format_exc() ) raise self.error(err_msg) if not hasattr(mod, attr_name): raise self.error( 'Module "{}" has no attribute "{}"'.format(module_name, attr_name) ) parser = getattr(mod, attr_name) if not isinstance(parser, (click.Command, click.Group)): raise self.error( '"{}" of type "{}" is not click.Command or click.Group.' '"click.BaseCommand"'.format(type(parser), module_path) ) return parser def _generate_nodes( self, name: str, command: click.Command, parent: ty.Optional[click.Context], nested: NestedT, commands: ty.Optional[ty.List[str]] = None, semantic_group: bool = False, ) -> ty.List[nodes.section]: """Generate the relevant Sphinx nodes. Format a `click.Group` or `click.Command`. :param name: Name of command, as used on the command line :param command: Instance of `click.Group` or `click.Command` :param parent: Instance of `click.Context`, or None :param nested: The granularity of subcommand details. :param commands: Display only listed commands or skip the section if empty :param semantic_group: Display command as title and description for `click.CommandCollection`. :returns: A list of nested docutil nodes """ ctx = click.Context(command, info_name=name, parent=parent) if command.hidden: return [] # Title section = nodes.section( '', nodes.title(text=name), ids=[nodes.make_id(ctx.command_path)], names=[nodes.fully_normalize_name(ctx.command_path)], ) # Summary source_name = ctx.command_path result = statemachine.StringList() ctx.meta["sphinx-click-env"] = self.env if semantic_group: lines = _format_description(ctx) else: lines = _format_command(ctx, nested, commands) for line in lines: LOG.debug(line) result.append(line, source_name) sphinx_nodes.nested_parse_with_titles(self.state, result, section) # Subcommands if nested == NESTED_FULL: if isinstance(command, click.CommandCollection): for source in command.sources: section.extend( self._generate_nodes( source.name, source, parent=ctx, nested=nested, semantic_group=True, ) ) else: commands = _filter_commands(ctx, commands) for command in commands: parent = ctx if not semantic_group else ctx.parent section.extend( self._generate_nodes( command.name, command, parent=parent, nested=nested ) ) return [section] def run(self) -> ty.Sequence[nodes.section]: self.env = self.state.document.settings.env command = self._load_module(self.arguments[0]) if 'prog' not in self.options: raise self.error(':prog: must be specified') prog_name = self.options['prog'] show_nested = 'show-nested' in self.options nested = self.options.get('nested') if show_nested: if nested: raise self.error( "':nested:' and ':show-nested:' are mutually exclusive" ) else: warnings.warn( "':show-nested:' is deprecated; use ':nested: full'", DeprecationWarning, ) nested = NESTED_FULL if show_nested else NESTED_SHORT commands = None if self.options.get('commands'): commands = [ command.strip() for command in self.options['commands'].split(',') ] return self._generate_nodes(prog_name, command, None, nested, commands) def setup(app: application.Sphinx) -> ty.Dict[str, ty.Any]: # Need autodoc to support mocking modules app.setup_extension('sphinx.ext.autodoc') app.add_directive('click', ClickDirective) app.add_event("sphinx-click-process-description") app.add_event("sphinx-click-process-usage") app.add_event("sphinx-click-process-options") app.add_event("sphinx-click-process-arguments") app.add_event("sphinx-click-process-envvars") app.add_event("sphinx-click-process-epilog") app.add_config_value( 'sphinx_click_mock_imports', lambda config: config.autodoc_mock_imports, 'env' ) return { 'parallel_read_safe': True, 'parallel_write_safe': True, } sphinx-click-6.0.0/tests/000077500000000000000000000000001462112523200152515ustar00rootroot00000000000000sphinx-click-6.0.0/tests/__init__.py000066400000000000000000000000001462112523200173500ustar00rootroot00000000000000sphinx-click-6.0.0/tests/conftest.py000066400000000000000000000015271462112523200174550ustar00rootroot00000000000000import pathlib import shutil import sphinx import pytest # this is necessary because Sphinx isn't exposing its fixtures # https://docs.pytest.org/en/7.1.x/how-to/writing_plugins.html#requiring-loading-plugins-in-a-test-module-or-conftest-file pytest_plugins = ['sphinx.testing.fixtures'] # TODO: Remove when we no longer care about Sphinx < 7.2 @pytest.fixture def rootdir(tmpdir): if sphinx.version_info >= (7, 2, 0): src = pathlib.Path(__file__).parent.absolute().joinpath('roots') dst = tmpdir.join('roots') shutil.copytree(src, dst) roots = pathlib.Path(dst) else: from sphinx.testing import path src = path.path(__file__).parent.abspath() / 'roots' dst = tmpdir.join('roots') shutil.copytree(src, dst) roots = path.path(dst) yield roots shutil.rmtree(dst) sphinx-click-6.0.0/tests/roots/000077500000000000000000000000001462112523200164175ustar00rootroot00000000000000sphinx-click-6.0.0/tests/roots/basics/000077500000000000000000000000001462112523200176635ustar00rootroot00000000000000sphinx-click-6.0.0/tests/roots/basics/conf.py000066400000000000000000000002401462112523200211560ustar00rootroot00000000000000import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) extensions = ['sphinx_click'] autodoc_mock_imports = ["fake_dependency"] sphinx-click-6.0.0/tests/roots/basics/greet.py000066400000000000000000000006751462112523200213530ustar00rootroot00000000000000"""The greet example taken from the README.""" import click import fake_dependency # Used to test that mocking works @click.group() def greet(): """A sample command group.""" fake_dependency.do_stuff("hello!") @greet.command() @click.argument('user', envvar='USER') def hello(user): """Greet a user.""" click.echo('Hello %s' % user) @greet.command() def world(): """Greet the world.""" click.echo('Hello world!') sphinx-click-6.0.0/tests/roots/basics/index.rst000066400000000000000000000000661462112523200215260ustar00rootroot00000000000000Basics ====== .. click:: greet:greet :prog: greet sphinx-click-6.0.0/tests/roots/commands/000077500000000000000000000000001462112523200202205ustar00rootroot00000000000000sphinx-click-6.0.0/tests/roots/commands/conf.py000066400000000000000000000001641462112523200215200ustar00rootroot00000000000000import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) extensions = ['sphinx_click'] sphinx-click-6.0.0/tests/roots/commands/greet.py000066400000000000000000000005451462112523200217040ustar00rootroot00000000000000"""The greet example taken from the README.""" import click @click.group() def greet(): """A sample command group.""" pass @greet.command() @click.argument('user', envvar='USER') def hello(user): """Greet a user.""" click.echo('Hello %s' % user) @greet.command() def world(): """Greet the world.""" click.echo('Hello world!') sphinx-click-6.0.0/tests/roots/commands/index.rst000066400000000000000000000001161462112523200220570ustar00rootroot00000000000000Commands ======== .. click:: greet:greet :prog: greet :commands: world sphinx-click-6.0.0/tests/roots/nested-full/000077500000000000000000000000001462112523200206415ustar00rootroot00000000000000sphinx-click-6.0.0/tests/roots/nested-full/conf.py000066400000000000000000000001641462112523200221410ustar00rootroot00000000000000import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) extensions = ['sphinx_click'] sphinx-click-6.0.0/tests/roots/nested-full/greet.py000066400000000000000000000005451462112523200223250ustar00rootroot00000000000000"""The greet example taken from the README.""" import click @click.group() def greet(): """A sample command group.""" pass @greet.command() @click.argument('user', envvar='USER') def hello(user): """Greet a user.""" click.echo('Hello %s' % user) @greet.command() def world(): """Greet the world.""" click.echo('Hello world!') sphinx-click-6.0.0/tests/roots/nested-full/index.rst000066400000000000000000000001251462112523200225000ustar00rootroot00000000000000Nested (full) ============= .. click:: greet:greet :prog: greet :nested: full sphinx-click-6.0.0/tests/test_extension.py000066400000000000000000000106441462112523200207030ustar00rootroot00000000000000import pickle from docutils import nodes from sphinx import addnodes as sphinx_nodes def test_basics(make_app, rootdir): srcdir = rootdir / 'basics' app = make_app('xml', srcdir=srcdir) app.build() # TODO: rather than using the pickled doctree, we should decode the XML content = pickle.loads((app.doctreedir / 'index.doctree').read_bytes()) # doc has format like so: # # document: # section: # title: # section: # title: # paragraph: # literal_block: # rubric: # index: # desc: # desc_signature: # desc_signature: # index: # desc: # desc_signature: # desc_signature: section = content[0][1] assert isinstance(section, nodes.section) assert isinstance(section[0], nodes.title) assert section[0].astext() == 'greet' assert isinstance(section[1], nodes.paragraph) assert section[1].astext() == 'A sample command group.' assert isinstance(section[2], nodes.literal_block) assert isinstance(section[3], nodes.rubric) assert section[3].astext() == 'Commands' assert isinstance(section[4], sphinx_nodes.index) assert isinstance(section[5], sphinx_nodes.desc) assert section[5].astext() == 'hello\n\nGreet a user.' assert isinstance(section[6], sphinx_nodes.index) assert isinstance(section[7], sphinx_nodes.desc) assert section[7].astext() == 'world\n\nGreet the world.' def test_commands(make_app, rootdir): srcdir = rootdir / 'commands' app = make_app('xml', srcdir=srcdir) app.build() # TODO: rather than using the pickled doctree, we should decode the XML content = pickle.loads((app.doctreedir / 'index.doctree').read_bytes()) # doc has format like so: # # document: # section: # title: # section: # title: # paragraph: # literal_block: # rubric: # index: # desc: # desc_signature: # desc_signature: section = content[0][1] assert isinstance(section, nodes.section) assert isinstance(section[0], nodes.title) assert section[0].astext() == 'greet' assert isinstance(section[1], nodes.paragraph) assert section[1].astext() == 'A sample command group.' assert isinstance(section[2], nodes.literal_block) # we should only show a single command, 'world' assert isinstance(section[3], nodes.rubric) assert section[3].astext() == 'Commands' assert isinstance(section[4], sphinx_nodes.index) assert isinstance(section[5], sphinx_nodes.desc) assert section[5].astext() == 'world\n\nGreet the world.' def test_nested_full(make_app, rootdir): srcdir = rootdir / 'nested-full' app = make_app('xml', srcdir=srcdir) app.build() # TODO: rather than using the pickled doctree, we should decode the XML content = pickle.loads((app.doctreedir / 'index.doctree').read_bytes()) # doc has format like so: # # document: # section: # title: # section: # title: # paragraph: # literal_block: # section: # title # paragraph # literal_block # ... # section: # title # paragraph # literal_block section = content[0][1] assert isinstance(section, nodes.section) assert isinstance(section[0], nodes.title) assert section[0].astext() == 'greet' assert isinstance(section[1], nodes.paragraph) assert section[1].astext() == 'A sample command group.' assert isinstance(section[2], nodes.literal_block) subsection_a = section[3] assert isinstance(subsection_a, nodes.section) assert isinstance(subsection_a[0], nodes.title) assert subsection_a[0].astext() == 'hello' assert isinstance(subsection_a[1], nodes.paragraph) assert subsection_a[1].astext() == 'Greet a user.' assert isinstance(subsection_a[2], nodes.literal_block) # we don't need to verify the rest of this: that's done elsewhere subsection_b = section[4] assert isinstance(subsection_b, nodes.section) assert isinstance(subsection_b[0], nodes.title) assert subsection_b[0].astext() == 'world' assert isinstance(subsection_b[1], nodes.paragraph) assert subsection_b[1].astext() == 'Greet the world.' assert isinstance(subsection_b[2], nodes.literal_block) sphinx-click-6.0.0/tests/test_formatter.py000066400000000000000000000652011462112523200206710ustar00rootroot00000000000000import textwrap import unittest import click from sphinx_click import ext CLICK_VERSION = tuple(int(x) for x in click.__version__.split('.')[0:2]) class CommandTestCase(unittest.TestCase): """Validate basic ``click.Command`` instances.""" maxDiff = None def test_no_parameters(self): """Validate a `click.Command` with no parameters. This exercises the code paths for a command with *no* arguments, *no* options and *no* environment variables. """ @click.command() def foobar(): """A sample command.""" pass ctx = click.Context(foobar, info_name='foobar') output = list(ext._format_command(ctx, nested='short')) self.assertEqual( textwrap.dedent( """ A sample command. .. program:: foobar .. code-block:: shell foobar [OPTIONS] """ ).lstrip(), '\n'.join(output), ) def test_basic_parameters(self): """Validate a combination of parameters. This exercises the code paths for a command with arguments, options and environment variables. """ @click.command() @click.option('--param', envvar='PARAM', help='A sample option') @click.option('--another', metavar='[FOO]', help='Another option') @click.option( '--choice', help='A sample option with choices', type=click.Choice(['Option1', 'Option2']), ) @click.option( '--numeric-choice', metavar='', help='A sample option with numeric choices', type=click.Choice([1, 2, 3]), ) @click.option( '--flag', is_flag=True, help='A boolean flag', ) @click.argument('ARG', envvar='ARG') def foobar(bar): """A sample command.""" pass ctx = click.Context(foobar, info_name='foobar') output = list(ext._format_command(ctx, nested='short')) self.assertEqual( textwrap.dedent( """ A sample command. .. program:: foobar .. code-block:: shell foobar [OPTIONS] ARG .. rubric:: Options .. option:: --param A sample option .. option:: --another Another option .. option:: --choice A sample option with choices :options: Option1 | Option2 .. option:: --numeric-choice A sample option with numeric choices :options: 1 | 2 | 3 .. option:: --flag A boolean flag .. rubric:: Arguments .. option:: ARG Required argument .. rubric:: Environment variables .. _foobar-param-PARAM: .. envvar:: PARAM :noindex: Provide a default for :option:`--param` .. _foobar-arg-ARG: .. envvar:: ARG :noindex: Provide a default for :option:`ARG` """ ).lstrip(), '\n'.join(output), ) def test_help_epilog(self): """Validate formatting of explicit help and epilog strings.""" @click.command(help='A sample command.', epilog='A sample epilog.') @click.option('--param', help='A sample option') def foobar(bar): pass ctx = click.Context(foobar, info_name='foobar') output = list(ext._format_command(ctx, nested='short')) self.assertEqual( textwrap.dedent( """ A sample command. .. program:: foobar .. code-block:: shell foobar [OPTIONS] .. rubric:: Options .. option:: --param A sample option A sample epilog. """ ).lstrip(), '\n'.join(output), ) def test_help_argument(self): """Validate a help text for arguments. While click only provides the help attribute for options, but not for arguments, it allows customization with subclasses. """ class CustomArgument(click.Argument): def __init__(self, *args, help=None, **kwargs): super().__init__(*args, **kwargs) self.help = help @click.command() @click.option('--option', help='A sample option') @click.argument('ARG', help='A sample argument', cls=CustomArgument) @click.argument('ARG_NO_HELP', cls=CustomArgument) def foobar(bar): """A sample command.""" pass ctx = click.Context(foobar, info_name='foobar') output = list(ext._format_command(ctx, nested='short')) self.assertEqual( textwrap.dedent( """ A sample command. .. program:: foobar .. code-block:: shell foobar [OPTIONS] ARG ARG_NO_HELP .. rubric:: Options .. option:: --option