diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:12:08 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:12:08 +0000 |
commit | 0d286042a60f80eff5ced1f1800395537429b9d2 (patch) | |
tree | ba4a4b89d414179cf1dfac3354f317797e3fd832 /src/pallets_sphinx_themes/themes/click | |
parent | Initial commit. (diff) | |
download | python-pallets-sphinx-themes-upstream.tar.xz python-pallets-sphinx-themes-upstream.zip |
Adding upstream version 2.0.3.upstream/2.0.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/pallets_sphinx_themes/themes/click')
4 files changed, 315 insertions, 0 deletions
diff --git a/src/pallets_sphinx_themes/themes/click/__init__.py b/src/pallets_sphinx_themes/themes/click/__init__.py new file mode 100644 index 0000000..30f3935 --- /dev/null +++ b/src/pallets_sphinx_themes/themes/click/__init__.py @@ -0,0 +1,8 @@ +def setup(app): + """Load the Click extension if Click is installed.""" + try: + from . import domain + except ImportError: + return + + domain.setup(app) diff --git a/src/pallets_sphinx_themes/themes/click/domain.py b/src/pallets_sphinx_themes/themes/click/domain.py new file mode 100644 index 0000000..b690cbf --- /dev/null +++ b/src/pallets_sphinx_themes/themes/click/domain.py @@ -0,0 +1,275 @@ +import contextlib +import shlex +import subprocess +import sys +import tempfile +from functools import partial + +import click +from click._compat import text_type +from click.testing import CliRunner +from click.testing import EchoingStdin +from docutils import nodes +from docutils.parsers.rst import Directive +from docutils.statemachine import ViewList +from sphinx.domains import Domain + + +class EofEchoingStdin(EchoingStdin): + """Like :class:`click.testing.EchoingStdin` but adds a visible + ``^D`` in place of the EOT character (``\x04``). + + :meth:`ExampleRunner.invoke` adds ``\x04`` when + ``terminate_input=True``. + """ + + def _echo(self, rv): + eof = rv[-1] == b"\x04"[0] + + if eof: + rv = rv[:-1] + + if not self._paused: + self._output.write(rv) + + if eof: + self._output.write(b"^D\n") + + return rv + + +@contextlib.contextmanager +def patch_modules(): + """Patch modules to work better with :meth:`ExampleRunner.invoke`. + + ``subprocess.call` output is redirected to ``click.echo`` so it + shows up in the example output. + """ + old_call = subprocess.call + + def dummy_call(*args, **kwargs): + with tempfile.TemporaryFile("wb+") as f: + kwargs["stdout"] = f + kwargs["stderr"] = f + rv = subprocess.Popen(*args, **kwargs).wait() + f.seek(0) + click.echo(f.read().decode("utf-8", "replace").rstrip()) + return rv + + subprocess.call = dummy_call + + try: + yield + finally: + subprocess.call = old_call + + +class ExampleRunner(CliRunner): + def __init__(self): + super(ExampleRunner, self).__init__(echo_stdin=True) + self.namespace = {"click": click, "__file__": "dummy.py", "str": text_type} + + @contextlib.contextmanager + def isolation(self, *args, **kwargs): + iso = super(ExampleRunner, self).isolation(*args, **kwargs) + + with iso as streams: + try: + buffer = sys.stdin.buffer + except AttributeError: + buffer = sys.stdin + + # FIXME: We need to replace EchoingStdin with our custom + # class that outputs "^D". At this point we know sys.stdin + # has been patched so it's safe to reassign the class. + # Remove this once EchoingStdin is overridable. + buffer.__class__ = EofEchoingStdin + yield streams + + def invoke( + self, + cli, + args=None, + prog_name=None, + input=None, + terminate_input=False, + env=None, + _output_lines=None, + **extra + ): + """Like :meth:`CliRunner.invoke` but displays what the user + would enter in the terminal for env vars, command args, and + prompts. + + :param terminate_input: Whether to display "^D" after a list of + input. + :param _output_lines: A list used internally to collect lines to + be displayed. + """ + output_lines = _output_lines if _output_lines is not None else [] + + if env: + for key, value in sorted(env.items()): + value = shlex.quote(value) + output_lines.append("$ export {}={}".format(key, value)) + + args = args or [] + + if prog_name is None: + prog_name = cli.name.replace("_", "-") + + output_lines.append( + "$ {} {}".format(prog_name, " ".join(shlex.quote(x) for x in args)).rstrip() + ) + # remove "python" from command + prog_name = prog_name.rsplit(" ", 1)[-1] + + if isinstance(input, (tuple, list)): + input = "\n".join(input) + "\n" + + if terminate_input: + input += "\x04" + + result = super(ExampleRunner, self).invoke( + cli=cli, args=args, input=input, env=env, prog_name=prog_name, **extra + ) + output_lines.extend(result.output.splitlines()) + return result + + def declare_example(self, source): + """Execute the given code, adding it to the runner's namespace.""" + with patch_modules(): + code = compile(source, "<docs>", "exec") + exec(code, self.namespace) + + def run_example(self, source): + """Run commands by executing the given code, returning the lines + of input and output. The code should be a series of the + following functions: + + * :meth:`invoke`: Invoke a command, adding env vars, input, + and output to the output. + * ``println(text="")``: Add a line of text to the output. + * :meth:`isolated_filesystem`: A context manager that changes + to a temporary directory while executing the block. + """ + code = compile(source, "<docs>", "exec") + buffer = [] + invoke = partial(self.invoke, _output_lines=buffer) + + def println(text=""): + buffer.append(text) + + exec( + code, + self.namespace, + { + "invoke": invoke, + "println": println, + "isolated_filesystem": self.isolated_filesystem, + }, + ) + return buffer + + def close(self): + """Clean up the runner once the document has been read.""" + pass + + +def get_example_runner(document): + """Get or create the :class:`ExampleRunner` instance associated with + a document. + """ + runner = getattr(document, "click_example_runner", None) + if runner is None: + runner = document.click_example_runner = ExampleRunner() + return runner + + +class DeclareExampleDirective(Directive): + """Add the source contained in the directive's content to the + document's :class:`ExampleRunner`, to be run using + :class:`RunExampleDirective`. + + See :meth:`ExampleRunner.declare_example`. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + + def run(self): + doc = ViewList() + runner = get_example_runner(self.state.document) + + try: + runner.declare_example("\n".join(self.content)) + except BaseException: + runner.close() + raise + + doc.append(".. sourcecode:: python", "") + doc.append("", "") + + for line in self.content: + doc.append(" " + line, "") + + node = nodes.section() + self.state.nested_parse(doc, self.content_offset, node) + return node.children + + +class RunExampleDirective(Directive): + """Run commands from :class:`DeclareExampleDirective` and display + the input and output. + + See :meth:`ExampleRunner.run_example`. + """ + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + + def run(self): + doc = ViewList() + runner = get_example_runner(self.state.document) + + try: + rv = runner.run_example("\n".join(self.content)) + except BaseException: + runner.close() + raise + + doc.append(".. sourcecode:: text", "") + doc.append("", "") + + for line in rv: + doc.append(" " + line, "") + + node = nodes.section() + self.state.nested_parse(doc, self.content_offset, node) + return node.children + + +class ClickDomain(Domain): + name = "click" + label = "Click" + directives = {"example": DeclareExampleDirective, "run": RunExampleDirective} + + +def delete_example_runner_state(app, doctree): + """Close and remove the :class:`ExampleRunner` instance once the + document has been read. + """ + runner = getattr(doctree, "click_example_runner", None) + + if runner is not None: + runner.close() + del doctree.click_example_runner + + +def setup(app): + app.add_domain(ClickDomain) + app.connect("doctree-read", delete_example_runner_state) diff --git a/src/pallets_sphinx_themes/themes/click/static/click.css b/src/pallets_sphinx_themes/themes/click/static/click.css new file mode 100644 index 0000000..1c0eee4 --- /dev/null +++ b/src/pallets_sphinx_themes/themes/click/static/click.css @@ -0,0 +1,28 @@ +@import url("pocoo.css"); +@import url("https://fonts.googleapis.com/css?family=Ubuntu+Mono"); +@import url("https://fonts.googleapis.com/css?family=Open+Sans"); + +body, pre, code { + font-family: "Ubuntu Mono", "Consolas", "Menlo", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", monospace; + font-size: 15px; +} + +h1, h2, h3, h4, h5, h6, p.admonition-title, div.sphinxsidebar input { + font-family: "Open Sans", "Helvetica", "Arial", sans-serif; +} + +div.body { + color: #3e4349; +} + +a { + color: #5d2cd1; +} + +a:hover { + color: #7546e3; +} + +p.version-warning { + background-color: #7546e3; +} diff --git a/src/pallets_sphinx_themes/themes/click/theme.conf b/src/pallets_sphinx_themes/themes/click/theme.conf new file mode 100644 index 0000000..049292f --- /dev/null +++ b/src/pallets_sphinx_themes/themes/click/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = pocoo +stylesheet = click.css +pygments_style = tango |