Compare commits

..

7 Commits

Author SHA1 Message Date
Nicolas a413d771b3 readme updated 2025-05-26 12:58:15 +02:00
Nicolas 727ab55b3d CI: test-dependencies lxml and requests added for "build-ubuntu-no-optional-dependencies" 2025-05-26 12:36:20 +02:00
Nicolas b20773364c path format in ci.yml fixed 2025-05-26 12:33:24 +02:00
Nicolas ad6fe7c6e7 Test for missing optional dependencies added 2025-05-26 12:31:35 +02:00
Nicolas 37ad1231c8 type: ignore[truthy-function] added in latex.py for checking optional dependency 2025-05-26 10:35:48 +02:00
Nicolas 250885d44f fixed missing optional dependency of pandas/Styler 2025-05-26 10:13:34 +02:00
Nicolas 76944d1829 docs added 2025-05-26 09:45:58 +02:00
14 changed files with 262 additions and 11 deletions

View File

@ -7,6 +7,41 @@ on:
branches: [main] branches: [main]
jobs: jobs:
build-ubuntu-no-optional-dependencies:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.12]
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install from source and install pytest
run: |
python -m pip install --upgrade pip
python -m pip install -e .
python -m pip install pytest lxml requests
- name: Run tests with pytest (no matplotlib, no pandas)
run: |
pytest tests/test_rendering_markdown.py::test_markdown_styling
pytest tests/test_rendering_markdown.py::test_markdown_table
- name: Install matplotlib
run: |
python -m pip install matplotlib
- name: Run tests with pytest rendering equations (with matplotlib)
run: |
pytest tests/test_rendering_markdown.py::test_markdown_equations
build-ubuntu: build-ubuntu:
runs-on: ubuntu-latest runs-on: ubuntu-latest

40
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Build and Deploy Docs
on:
push:
branches:
- main
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.x"
- name: Install dependencies
run: pip install sphinx sphinx_rtd_theme sphinx-autodoc-typehints myst-parser
- name: Generate Class List
run: |
pip install .
python ./docs/source/generate_class_list.py
- name: Build Docs
run: |
cd docs
sphinx-apidoc -o source/ ../src/ -M --no-toc
rm source/*.rst
make html
touch build/html/.nojekyll
mkdir -p build/html/media
cp ../media/output_example.png build/html/media/
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: docs/build/html

3
.gitignore vendored
View File

@ -132,3 +132,6 @@ pyModbusTCP_old/
test.py test.py
test_*.ipynb test_*.ipynb
settings.json settings.json
# Autogenerated doc file
docs/source/modules.md

View File

@ -21,7 +21,7 @@ is a hard problem where LaTeX is superior.
## Example outputs ## Example outputs
[![example output](docs/output_example.png)](https://raw.githubusercontent.com/Nonannet/pyladoc/refs/heads/main/tests/out/test_latex_render1.pdf) [![example output](media/output_example.png)](https://raw.githubusercontent.com/Nonannet/pyladoc/refs/heads/main/tests/out/test_latex_render1.pdf)
- HTML: [test_html_render1.html](https://html-preview.github.io/?url=https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.html) ([code](https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.html)) - HTML: [test_html_render1.html](https://html-preview.github.io/?url=https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.html) ([code](https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.html))
- PDF: [test_latex_render1.pdf](https://raw.githubusercontent.com/Nonannet/pyladoc/refs/heads/main/tests/out/test_latex_render1.pdf) ([code](https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.tex)) - PDF: [test_latex_render1.pdf](https://raw.githubusercontent.com/Nonannet/pyladoc/refs/heads/main/tests/out/test_latex_render1.pdf) ([code](https://github.com/Nonannet/pyladoc/blob/main/tests/out/test_html_render1.tex))
@ -60,13 +60,15 @@ Pyladoc depends on the markdown package.
Optional dependencies are: Optional dependencies are:
- Matplotlib python package for rendering LaTeX equations for HTML output - Matplotlib python package for rendering LaTeX equations for HTML output
- LaTeX for exporting to PDF or exporting Matplotlib figures to LaTeX (PGF/TikZ rendering) - LaTeX for exporting to PDF or exporting Matplotlib figures to LaTeX (PGF/TikZ rendering)
- Pandas and Matplotlib for including Pandas Tables and Matplotlib figures (obviously) - Pandas and Jinja2 for rendering pandas tables
- Matplotlib for rendering matplotlib figures (obviously)
For the included template the following LaTeX setup works on Ubuntu: For the included template the `miktex`-LaTeX distribution works on Windows
and the following LaTeX setup works on Ubuntu (both tested in CI):
```bash ```bash
sudo apt-get update sudo apt-get update
sudo apt-get install -y texlive-latex-extra texlive-fonts-extra lmodern texlive-xetex texlive-science sudo apt-get install -y texlive-latex-extra texlive-fonts-recommended lmodern texlive-xetex texlive-science
``` ```
## Usage ## Usage
@ -121,8 +123,8 @@ cd pyladoc
It's recommended to setup an venv: It's recommended to setup an venv:
```bash ```bash
python -m venv venv python -m venv .venv
source venv/bin/activate # On Windows use `venv\Scripts\activate` source .venv/bin/activate # On Windows use `.venv\Scripts\activate`
``` ```
Install the package and dev-dependencies while keeping files in the Install the package and dev-dependencies while keeping files in the

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

32
docs/source/conf.py Normal file
View File

@ -0,0 +1,32 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import os
import sys
sys.path.insert(0, os.path.abspath("../src/"))
project = 'pyladoc'
copyright = '2025, Nicolas Kruse'
author = 'Nicolas Kruse'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx_autodoc_typehints", "myst_parser"]
templates_path = ['_templates']
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
# html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
autodoc_inherit_docstrings = True

View File

@ -0,0 +1,61 @@
import importlib
import inspect
import fnmatch
from io import TextIOWrapper
def write_classes(f: TextIOWrapper, patterns: list[str], module_name: str, title: str, description: str = '', exclude: list[str] = []) -> None:
module = importlib.import_module(module_name)
classes = [
name for name, obj in inspect.getmembers(module, inspect.isclass)
if (obj.__module__ == module_name and
any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude) and
obj.__doc__ and '(Automatic generated stub)' not in obj.__doc__)
]
"""Write the classes to the file."""
f.write(f'## {title}\n\n')
if description:
f.write(f'{description}\n\n')
for cls in classes:
f.write('```{eval-rst}\n')
f.write(f'.. autoclass:: {module_name}.{cls}\n')
f.write(' :members:\n')
f.write(' :undoc-members:\n')
f.write(' :show-inheritance:\n')
f.write(' :inherited-members:\n')
if title != 'Base classes':
f.write(' :exclude-members: select\n')
f.write('```\n\n')
def write_functions(f: TextIOWrapper, patterns: list[str], module_name: str, title: str, description: str = '', exclude: list[str] = []) -> None:
module = importlib.import_module(module_name)
classes = [
name for name, obj in inspect.getmembers(module, inspect.isfunction)
if (obj.__module__ == module_name and
any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude))
]
"""Write the classes to the file."""
f.write(f'## {title}\n\n')
if description:
f.write(f'{description}\n\n')
for func in classes:
if not func.startswith('_'):
f.write('```{eval-rst}\n')
f.write(f'.. autofunction:: {module_name}.{func}\n')
f.write('```\n\n')
with open('docs/source/modules.md', 'w') as f:
f.write('# Pyladoc classes, functions and submodules\n\n')
write_classes(f, ['DocumentWriter'], 'pyladoc', title='DocumentWriter Class')
write_functions(f, ['*'], 'pyladoc', title='Functions')
write_functions(f, ['*'], 'pyladoc.latex', title='Submodule latex')

10
docs/source/index.md Normal file
View File

@ -0,0 +1,10 @@
```{toctree}
:maxdepth: 2
:caption: Contents:
readme
modules
```
```{include} ../../README.md
```

2
docs/source/readme.md Normal file
View File

@ -0,0 +1,2 @@
```{include} ../../README.md
```

View File

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

@ -29,6 +29,7 @@ dev = [
Homepage = "https://github.com/Nonannet/pyladoc" Homepage = "https://github.com/Nonannet/pyladoc"
Repository = "https://github.com/Nonannet/pyladoc" Repository = "https://github.com/Nonannet/pyladoc"
Issues = "https://github.com/Nonannet/pyladoc/issues" Issues = "https://github.com/Nonannet/pyladoc/issues"
documentation = "https://nonannet.github.io/pyladoc/"
[build-system] [build-system]
requires = ["setuptools>=61.0", "wheel"] requires = ["setuptools>=61.0", "wheel"]

View File

@ -33,6 +33,7 @@ else:
Table = DataFrame | Styler Table = DataFrame | Styler
except ImportError: except ImportError:
Table = DataFrame Table = DataFrame
Styler = None
try: try:
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@ -475,8 +476,8 @@ class DocumentWriter():
centered: Whether to center the table in LaTeX output centered: Whether to center the table in LaTeX output
""" """
assert Table and isinstance(table, Table), 'Table has to be a pandas DataFrame oder DataFrame Styler' assert Table and isinstance(table, Table), 'Table has to be a pandas DataFrame oder DataFrame Styler'
styler = table if isinstance(table, Styler) else getattr(table, 'style', None) styler = table if Styler and isinstance(table, Styler) else getattr(table, 'style', None) # type: ignore[truthy-function]
assert isinstance(styler, Styler), 'Jinja2 package is required for rendering tables' assert Styler and isinstance(styler, Styler), 'Jinja2 package is required for rendering pandas tables' # type: ignore[truthy-function]
def render_to_html() -> str: def render_to_html() -> str:
caption_prefix, reference = self._add_item(ref_id, ref_type, prefix_pattern) caption_prefix, reference = self._add_item(ref_id, ref_type, prefix_pattern)

View File

@ -1,6 +1,5 @@
from html.parser import HTMLParser from html.parser import HTMLParser
from typing import Generator, Any, Literal, get_args from typing import Generator, Any, Literal, get_args, TYPE_CHECKING
from pandas.io.formats.style import Styler
import re import re
import os import os
import shutil import shutil
@ -8,6 +7,13 @@ import subprocess
import tempfile import tempfile
from .latex_escaping import unicode_to_latex_dict, latex_escape_dict from .latex_escaping import unicode_to_latex_dict, latex_escape_dict
if TYPE_CHECKING:
from pandas.io.formats.style import Styler
else:
try:
from pandas.io.formats.style import Styler
except ImportError:
Styler = None
LatexEngine = Literal['pdflatex', 'lualatex', 'xelatex', 'tectonic'] LatexEngine = Literal['pdflatex', 'lualatex', 'xelatex', 'tectonic']
@ -116,6 +122,9 @@ def render_pandas_styler_table(df_style: Styler, caption: str = '', label: str =
Returns: Returns:
The LaTeX code. The LaTeX code.
""" """
assert Styler, 'Jinja2 package is required for rendering pandas tables' # type: ignore[truthy-function]
assert isinstance(df_style, Styler), 'df_style has to be of type Styler'
def iter_table(table: dict[str, Any]) -> Generator[str, None, None]: def iter_table(table: dict[str, Any]) -> Generator[str, None, None]:
yield '\\begin{table}\n' yield '\\begin{table}\n'
if centering: if centering: