Description
I would like to generate documentation for dynamically generated click commands, depending on a Flask context. Running sphinx-build
raises RuntimeError: There is no active click context.
, while running the commands works as expected (for instance python commands.py sub foo
)
The code consists in a ParametrizedCommand
class that dynamically generate a list of commands. For the sake of simplicity on this snippet, this is not quite dynamical, but at least this generate the very same exception I meet with my more complex real world use case.
Removing @with_appcontext
would make sphinx-build
work, but I cannot do this since I need the application context in get_command
in my real use case.
commands.py
import click
from flask import Flask
from flask.cli import FlaskGroup
from flask.cli import with_appcontext
def create_app():
return Flask(__name__)
class ParametrizedCommand(click.Group):
valid_command_names = ["foo", "bar", "baz"]
def list_commands(self, ctx):
base = super().list_commands(ctx)
return base + self.valid_command_names
@with_appcontext
def get_command(self, ctx, cmd_name):
@click.command(name=cmd_name, help=f"Help for {cmd_name}")
def command(*args, **kwargs):
click.echo(f"Executing {cmd_name}")
return command
@click.group(cls=FlaskGroup, create_app=create_app)
def cli():
...
@cli.command(cls=ParametrizedCommand)
def sub():
...
if __name__ == "__main__":
cli()
index.rst
.. click:: commands:cli
:prog: cli
:nested: full
traceback
# Platform: linux; (Linux-6.8.9-arch1-2-x86_64-with-glibc2.39)
# Sphinx version: 7.3.7
# Python version: 3.12.3 (CPython)
# Docutils version: 0.21.2
# Jinja2 version: 3.1.4
# Pygments version: 2.18.0
# Last messages:
# writing output...
#
# building [html]: targets for 1 source files that are out of date
# updating environment:
# [new config]
# 1 added, 0 changed, 0 removed
#
# reading sources... [100%]
# index
#
# Loaded extensions:
# sphinx.ext.mathjax (7.3.7)
# alabaster (0.7.16)
# sphinxcontrib.applehelp (1.0.8)
# sphinxcontrib.devhelp (1.0.6)
# sphinxcontrib.htmlhelp (2.0.5)
# sphinxcontrib.serializinghtml (1.1.10)
# sphinxcontrib.qthelp (1.0.7)
# sphinx.ext.autodoc.preserve_defaults (7.3.7)
# sphinx.ext.autodoc.type_comment (7.3.7)
# sphinx.ext.autodoc.typehints (7.3.7)
# sphinx.ext.autodoc (7.3.7)
# sphinx.ext.autosectionlabel (7.3.7)
# sphinx.ext.doctest (7.3.7)
# sphinx.ext.graphviz (7.3.7)
# sphinx.ext.intersphinx (7.3.7)
# sphinx.ext.todo (7.3.7)
# sphinx.ext.viewcode (7.3.7)
# sphinx_click (unknown version)
# Traceback:
Traceback (most recent call last):
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/click/globals.py", line 37, in get_current_context
return t.cast("Context", _local.stack[-1])
^^^^^^^^^^^^
AttributeError: '_thread._local' object has no attribute 'stack'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/cmd/build.py", line 337, in build_main
app.build(args.force_all, args.filenames)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/application.py", line 351, in build
self.builder.build_update()
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 293, in build_update
self.build(to_build,
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 313, in build
updated_docnames = set(self.read())
^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 419, in read
self._read_serial(docnames)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 440, in _read_serial
self.read_doc(docname)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/builders/__init__.py", line 497, in read_doc
publisher.publish()
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/core.py", line 234, in publish
self.document = self.reader.read(self.source, self.parser,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/io.py", line 107, in read
self.parse()
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/readers/__init__.py", line 76, in parse
self.parser.parse(self.input, document)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx/parsers.py", line 83, in parse
self.statemachine.run(inputlines, document, inliner=self.inliner)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 169, in run
results = StateMachineWS.run(self, input_lines, input_offset,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/statemachine.py", line 233, in run
context, next_state, result = self.check_line(
^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/statemachine.py", line 445, in check_line
return method(match, context, next_state)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 2790, in underline
self.section(title, source, style, lineno - 1, messages)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 325, in section
self.new_subsection(title, lineno, messages)
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 391, in new_subsection
newabsoffset = self.nested_parse(
^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 279, in nested_parse
state_machine.run(block, input_offset, memo=self.memo,
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 195, in run
results = StateMachineWS.run(self, input_lines, input_offset)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/statemachine.py", line 233, in run
context, next_state, result = self.check_line(
^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/statemachine.py", line 445, in check_line
return method(match, context, next_state)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 2357, in explicit_markup
nodelist, blank_finish = self.explicit_construct(match)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 2369, in explicit_construct
return method(self, expmatch)
^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 2106, in directive
return self.run_directive(
^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/docutils/parsers/rst/states.py", line 2156, in run_directive
result = directive_instance.run()
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx_click/ext.py", line 563, in run
return self._generate_nodes(prog_name, command, None, nested, commands)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx_click/ext.py", line 526, in _generate_nodes
self._generate_nodes(
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx_click/ext.py", line 522, in _generate_nodes
commands = _filter_commands(ctx, commands)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx_click/ext.py", line 308, in _filter_commands
lookup = _get_lazyload_commands(ctx)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/sphinx_click/ext.py", line 296, in _get_lazyload_commands
commands[command] = ctx.command.get_command(ctx, command)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/click/decorators.py", line 33, in new_func
return f(get_current_context(), *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/eloi/.virtualenvs/test-soai/lib/python3.12/site-packages/click/globals.py", line 40, in get_current_context
raise RuntimeError("There is no active click context.") from e
RuntimeError: There is no active click context.