Skip to content

Commit

Permalink
fix #27, fix #28
Browse files Browse the repository at this point in the history
  • Loading branch information
bckohan committed Jun 2, 2024
1 parent df024e2 commit 54f502a
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 64 deletions.
3 changes: 2 additions & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ build:
# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: doc/source/conf.py

extra_build_args:
- "--fresh-env"
# Optionally build your docs in additional formats such as PDF and ePub
formats:
- pdf
2 changes: 1 addition & 1 deletion doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXOPTS ?= --fresh-env
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
Expand Down
7 changes: 7 additions & 0 deletions doc/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
Change Log
==========

v0.3.0
======

* Implemented `Allow function hooks to be specified as import strings. <https://github.com/sphinx-contrib/typer/issues/28>`_
* Fixed `pdf builds are broken. <https://github.com/sphinx-contrib/typer/issues/27>`_


v0.2.5
======

Expand Down
36 changes: 2 additions & 34 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from datetime import datetime
import sys
from pathlib import Path
from contextlib import contextmanager
import platform
from sphinxcontrib import typer as sphinxcontrib_typer

# Configuration file for the Sphinx documentation builder.
Expand All @@ -14,6 +12,7 @@
# -- Path setup --------------------------------------------------------------

sys.path.append(str(Path(__file__).parent.parent))
sys.path.append(str(Path(__file__).parent / 'typer_doc_ext'))


# -- Project information -----------------------------------------------------
Expand Down Expand Up @@ -64,35 +63,4 @@

latex_engine = 'xelatex'


@contextmanager
def typer_get_web_driver(directive):
from pathlib import Path
import os

if not Path('~/.rtd.build').expanduser().is_file():
with sphinxcontrib_typer.typer_get_web_driver(directive) as driver:
yield driver
return

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# Set up headless browser options
options=Options()
os.environ['PATH'] += os.pathsep + os.path.expanduser("~/chrome/opt/google/chrome")
options.binary_location = os.path.expanduser("~/chrome/opt/google/chrome/google-chrome")
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920x1080")

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
yield driver
driver.quit()
typer_get_web_driver = 'web_driver.typer_get_web_driver'
27 changes: 27 additions & 0 deletions doc/source/howto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@ The examples below all reference this example Typer_ application:

|
Build to Multiple Formats
-------------------------

Sphinx caches directive output and reuses the results when building the documentation to
different formats (e.g. html, pdf or text). This causes problems with the way the typer
directive dynamically determines which render target to use based on the active builder.
This can mean that if you run sphinx-build for html and latexpdf at the same time the
pdf may not render all typer helps as expected. To work around this you can do one of
three things

1. Run sphinx-build for each format separately.
2. Use the `only directive <https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-only>`_
in combination with :ref:`:preferred: <directive_options>` to specify builder specific content.
3. Use the `--fresh-env <https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-E>`_
option to force sphinx to rebuild the directive output for each builder.

If also building a PDF on `readthedocs <https://readthedocs.org/>`_ you can do 3 by adding the
following to your readthedocs.yml:

.. code-block:: yaml
sphinx:
configuration: path/to/conf.py
extra_build_args:
- "--fresh-env"
Change the Width
----------------

Expand Down
8 changes: 8 additions & 0 deletions doc/source/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Reference
=========

.. _directive_options:

Directive Options
-----------------

Expand Down Expand Up @@ -206,6 +208,12 @@ Function Hooks
These functions may all be redefined in `conf.py` to override default behaviors. Your override
functions must conform to these function signatures.

.. warning::

Sphinx will warn that these functions are not pickleable. This messes up sphinx's caching but
that wont break the doc build. You can either suppress the warning or specify these configuration
values as import strings instead.

.. autofunction:: sphinxcontrib.typer.typer_render_html
.. autofunction:: sphinxcontrib.typer.typer_get_iframe_height
.. autofunction:: sphinxcontrib.typer.typer_svg2pdf
Expand Down
Empty file.
35 changes: 35 additions & 0 deletions doc/source/typer_doc_ext/web_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from contextlib import contextmanager
from sphinxcontrib import typer as sphinxcontrib_typer


@contextmanager
def typer_get_web_driver(directive):
from pathlib import Path
import os

if not Path('~/.rtd.build').expanduser().is_file():
with sphinxcontrib_typer.typer_get_web_driver(directive) as driver:
yield driver
return

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# Set up headless browser options
options=Options()
os.environ['PATH'] += os.pathsep + os.path.expanduser("~/chrome/opt/google/chrome")
options.binary_location = os.path.expanduser("~/chrome/opt/google/chrome/google-chrome")
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920x1080")

driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
yield driver
driver.quit()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "sphinxcontrib-typer"
version = "0.2.5"
version = "0.3.0"
description = "Auto generate docs for typer commands."
authors = ["Brian Kohan <[email protected]>"]
license = "MIT"
Expand Down
47 changes: 20 additions & 27 deletions sphinxcontrib/typer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
from typer.main import get_command as get_typer_command
from typer.models import Context as TyperContext

VERSION = (0, 2, 5)
VERSION = (0, 3, 0)

__title__ = "SphinxContrib Typer"
__version__ = ".".join(str(i) for i in VERSION)
Expand All @@ -61,6 +61,14 @@
SELENIUM_DEFAULT_WINDOW_HEIGHT = 2048


def get_function(function: t.Union[str, t.Callable[..., t.Any]]):
if callable(function):
return function
if isinstance(function, str):
parts = function.split('.')
return getattr(import_module(".".join(parts[0:-1])), parts[-1])


def _filter_commands(ctx: click.Context, cmd_filter: t.Optional[t.List[str]] = None):
return sorted(
[
Expand Down Expand Up @@ -481,15 +489,15 @@ def get_console(stderr: bool = False) -> Console:
png_path = Path(self.env.app.builder.outdir) / (
f'{normal_cmd.replace(":", "_")}_{self.uuid(normal_cmd)}.png'
)
self.env.app.config.typer_convert_png(self, rendered, png_path)
get_function(self.env.app.config.typer_convert_png)(self, rendered, png_path)
section += nodes.image(
uri=os.path.relpath(png_path, Path(self.env.srcdir)),
alt=source_name,
)
elif self.target == RenderTarget.HTML:
section += nodes.raw(
"",
self.env.app.config.typer_render_html(self, normal_cmd, rendered),
get_function(self.env.app.config.typer_render_html)(self, normal_cmd, rendered),
format="html",
)
elif self.target == RenderTarget.SVG:
Expand All @@ -500,7 +508,7 @@ def get_console(stderr: bool = False) -> Console:
out_dir = Path(self.env.app.builder.outdir)
(out_dir / f"{img_name}.svg").write_text(rendered)
pdf_img = out_dir / f"{img_name}.pdf"
self.env.app.config.typer_svg2pdf(self, rendered, pdf_img)
get_function(self.env.app.config.typer_svg2pdf)(self, rendered, pdf_img)
section += nodes.image(
uri=os.path.relpath(pdf_img, Path(self.env.srcdir)),
alt=source_name,
Expand Down Expand Up @@ -637,7 +645,7 @@ def typer_get_iframe_height(
if cache["iframe_heights"].get(normal_cmd):
return cache["iframe_heights"][normal_cmd]

with directive.env.app.config.typer_get_web_driver(directive) as driver:
with get_function(directive.env.app.config.typer_get_web_driver)(directive) as driver:
# use base64 to avoid issues with special characters
driver.get(
f'data:text/html;base64,'
Expand Down Expand Up @@ -671,7 +679,7 @@ def typer_render_html(
:param html_page: The html page rendered by console.export_html
"""

height = directive.env.app.config.typer_get_iframe_height(
height = get_function(directive.env.app.config.typer_get_iframe_height)(
directive, normal_cmd, html_page
)
return (
Expand Down Expand Up @@ -835,7 +843,7 @@ def typer_convert_png(
from selenium.webdriver.common.by import By

tag = "code"
with directive.env.app.config.typer_get_web_driver(directive) as driver:
with get_function(directive.env.app.config.typer_get_web_driver)(directive) as driver:
with tempfile.NamedTemporaryFile(suffix=".html") as tmp:
if directive.target is RenderTarget.TEXT:
tag = "pre"
Expand Down Expand Up @@ -899,34 +907,19 @@ def setup(app: application.Sphinx) -> t.Dict[str, t.Any]:
# Need autodoc to support mocking modules
app.add_directive("typer", TyperDirective)

def get_default_render_html(_):
return typer_render_html

def get_default_get_iframe_height(_):
return typer_get_iframe_height

def get_default_svg2pdf(_):
return typer_svg2pdf

def get_default_convert_png(_):
return typer_convert_png

def get_default_web_driver(_):
return typer_get_web_driver

app.add_config_value("typer_render_html", get_default_render_html, "")
app.add_config_value("typer_render_html", "sphinxcontrib.typer.typer_render_html", "env")

app.add_config_value("typer_get_iframe_height", get_default_get_iframe_height, "")
app.add_config_value("typer_svg2pdf", get_default_svg2pdf, "")
app.add_config_value("typer_get_iframe_height", "sphinxcontrib.typer.typer_get_iframe_height", "env")
app.add_config_value("typer_svg2pdf", "sphinxcontrib.typer.typer_svg2pdf", "env")
app.add_config_value("typer_iframe_height_padding", 30, "env")
app.add_config_value(
"typer_iframe_height_cache_path",
Path(app.confdir) / "typer_cache.json",
"env",
)

app.add_config_value("typer_convert_png", get_default_convert_png, "")
app.add_config_value("typer_get_web_driver", get_default_web_driver, "")
app.add_config_value("typer_convert_png", "sphinxcontrib.typer.typer_convert_png", "env")
app.add_config_value("typer_get_web_driver", "sphinxcontrib.typer.typer_get_web_driver", "env")

return {
"parallel_read_safe": True,
Expand Down

0 comments on commit 54f502a

Please sign in to comment.