From 5263806f678a52df5aac8a88cbf24392402b6578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 27 Oct 2024 11:46:18 +0100 Subject: [PATCH 1/9] fix typehint for PdocStr --- jinja2_pdoc/wrapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jinja2_pdoc/wrapper.py b/jinja2_pdoc/wrapper.py index 4748960..2368cce 100644 --- a/jinja2_pdoc/wrapper.py +++ b/jinja2_pdoc/wrapper.py @@ -101,7 +101,7 @@ class PdocStr(str): re.MULTILINE | re.DOTALL, ) - def dedent(self) -> str: + def dedent(self) -> "PdocStr": """ remove common whitespace from the left of every line in the string, see `textwrap.dedent` for more information. @@ -109,14 +109,14 @@ def dedent(self) -> str: s = textwrap.dedent(self) return self.__class__(s) - def indent(self) -> str: + def indent(self) -> "PdocStr": """ remove leading spaces and change indent size to 2 spaces, instead of 4. """ s = autopep8.fix_code(self.dedent(), options={"indent_size": 2}) return self.__class__(s) - def nodoc(self) -> str: + def nodoc(self) -> "PdocStr": """ remove shebang and docstring and from the string """ From 65ee98607e976e585759c25094a59b93ea8537ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Mon, 28 Oct 2024 21:52:42 +0100 Subject: [PATCH 2/9] updated dependency pdoc --- .pre-commit-config.yaml | 14 ++++++++++++++ docs/__init__.py | 7 +++++++ docs/outdated.py | 31 +++++++++++++++++++++++++++++++ poetry.lock | 34 +++++++++++++++++++++++++--------- pyproject.toml | 9 ++++++--- 5 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 docs/__init__.py create mode 100644 docs/outdated.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 239ba82..fae3e00 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,8 @@ +default_stages: + - pre-commit +default_install_hook_types: + - pre-commit + - pre-push repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 @@ -49,3 +54,12 @@ repos: hooks: - id: flake8 args: ["--max-line-length", "88"] + - repo: local + hooks: + - id: outdated + name: poetry outdated dependencies + entry: poetry run python docs/outdated.py + language: system + pass_filenames: false + stages: + - pre-push diff --git a/docs/__init__.py b/docs/__init__.py new file mode 100644 index 0000000..c941ddf --- /dev/null +++ b/docs/__init__.py @@ -0,0 +1,7 @@ +from .__main__ import main as documentation +from .outdated import main as outdated + +__all__ = [ + "outdated", + "documentation", +] diff --git a/docs/outdated.py b/docs/outdated.py new file mode 100644 index 0000000..d880ac1 --- /dev/null +++ b/docs/outdated.py @@ -0,0 +1,31 @@ +import subprocess +import sys + + +def main(): + """ + Check for outdated dependencies for the project. + """ + + p = subprocess.run( + "poetry show --only=main -T -o", + shell=True, + capture_output=True, + encoding="utf-8", + ) + + try: + p.check_returncode() + except subprocess.CalledProcessError as e: + print(e, e.stderr.replace("\n", " "), sep="\n\n", file=sys.stderr) + return 2 + + if p.stdout.strip(): + print(p.stdout, file=sys.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/poetry.lock b/poetry.lock index 05f7e97..9c74366 100644 --- a/poetry.lock +++ b/poetry.lock @@ -333,6 +333,22 @@ pygments = ">=2.12.0" [package.extras] dev = ["hypothesis", "mypy", "pdoc-pyo3-sample-library (==1.0.11)", "pygments (>=2.14.0)", "pytest", "pytest-cov", "pytest-timeout", "ruff", "tox", "types-pygments"] +[[package]] +name = "pdoc" +version = "15.0.0" +description = "API Documentation for Python Projects" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pdoc-15.0.0-py3-none-any.whl", hash = "sha256:151b0187a25eaf827099e981d6dbe3a4f68aeb18d0d637c24edcab788d5540f1"}, + {file = "pdoc-15.0.0.tar.gz", hash = "sha256:b761220d3ba129cd87e6da1bb7b62c8e799973ab9c595de7ba1a514850d86da5"}, +] + +[package.dependencies] +Jinja2 = ">=2.11.0" +MarkupSafe = ">=1.1.1" +pygments = ">=2.12.0" + [[package]] name = "platformdirs" version = "4.3.6" @@ -432,13 +448,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-cov" -version = "4.1.0" +version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] @@ -446,7 +462,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-mock" @@ -603,13 +619,13 @@ files = [ [[package]] name = "virtualenv" -version = "20.27.0" +version = "20.27.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" files = [ - {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, - {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, + {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, + {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, ] [package.dependencies] @@ -638,4 +654,4 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "5220afe3eeeb780a742b1bfe3a7e40fcfda1e3c70ed31fbcab7c0195353d3ec6" +content-hash = "79d615a79ad564d7091d9c077b24f96e1bee39840bb30dd269c335a806729ff6" diff --git a/pyproject.toml b/pyproject.toml index 688c942..3b474d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,10 @@ documentation = "https://d-chris.github.io/jinja2_pdoc" [tool.poetry.dependencies] python = "^3.8.1" Jinja2 = "^3.1.2" -pdoc = "^14.3.0" +pdoc = [ + { version = "^14.3.0", python = "<3.9" }, + { version = ">=14.3.0", python = "^3.9" }, +] click = "^8.1.7" autopep8 = "^2.0.4" pyyaml = "^6.0.1" @@ -38,9 +41,9 @@ pyyaml = "^6.0.1" jinja2pdoc = "jinja2_pdoc.cli:cli" [tool.poetry.group.test.dependencies] -pytest = "^8.3.3" +pytest = "^8.0.0" pytest-random-order = "^1.1.0" -pytest-cov = "^4.1.0" +pytest-cov = ">=4.1.0" pytest-mock = "^3.14.0" [tool.poetry.group.dev.dependencies] From 26df0286d7521724bec998a281095a1079e23fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 18:57:32 +0100 Subject: [PATCH 3/9] Add shell and include filters to Jinja2Pdoc environment --- docs/__main__.py | 3 ++ jinja2_pdoc/environment.py | 104 +++++++++++++++++++++++++++++++++++++ pyproject.toml | 6 +++ tests/test_filter.py | 104 +++++++++++++++++++++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 tests/test_filter.py diff --git a/docs/__main__.py b/docs/__main__.py index e1153a6..a786b85 100644 --- a/docs/__main__.py +++ b/docs/__main__.py @@ -66,12 +66,15 @@ def main() -> int: "template_directory": cwd / "dark-mode", "show_source": False, "search": False, + "docformat": "google", } modules = [ "jinja2_pdoc", "pdoc", "jinja2", + "pathlib", + "subprocess", ] with warnings.catch_warnings(record=True) as messages: diff --git a/jinja2_pdoc/environment.py b/jinja2_pdoc/environment.py index 71b0462..d99262a 100644 --- a/jinja2_pdoc/environment.py +++ b/jinja2_pdoc/environment.py @@ -1,3 +1,7 @@ +import subprocess +from pathlib import Path +from typing import Callable + import jinja2 from jinja2_pdoc.extension import Jinja2Pdoc @@ -22,3 +26,103 @@ def __init__(self, *args, **kwargs): """ super().__init__(*args, **kwargs) self.add_extension(Jinja2Pdoc) + + self.add_filter("shell", self.shell) + self.add_filter("include", self.include) + + def add_filter(self, name: str, func: Callable) -> None: + """ + Add a new filter to the environment. + + Args: + name (str): The name of the filter. + func (Callable): The filter function. + + Example: + >>> env = jinja2_pdoc.Environment() + >>> env.add_filter('upper', lambda s: s.upper()) + >>> env.from_string('{{ "hello world." | upper }}').render() + 'HELLO WORLD.' + """ + if not callable(func): + raise TypeError(f"{func=} is not a callable") + + self.filters[name] = func + + @staticmethod + def shell( + cmd: str, + result: str = "stdout", + *, + promt: str = None, + ) -> str: + """ + Filter to run a shell command and return the output. + + Args: + cmd (str): The command to run with `subprocess.run`. + result (str, optional): The attribute to return from the + `subprocess.CompletedProcess` instance. Defaults to "stdout". + promt (str, optional): Format string to include the command before the + output, e.g. `">>> %s \\n"`. Defaults to None. + + Returns: + str: The result of the command. + + Example: + ```jinja2 + {{ "python --version" | shell(promt="$ ") }} + ``` + """ + + process = subprocess.run( + cmd, + shell=True, + capture_output=True, + text=True, + ) + + output = str(getattr(process, result)) + + if promt is not None: + try: + prefix = promt % cmd + except TypeError: + prefix = f"{promt}{cmd}\n" + + output = f"{prefix}\n{output}" + return output + + @staticmethod + def include( + file: str, + enc=None, + *, + attr: str = None, + ) -> str: + """ + Filter for jinja2 Template to include the content of a file. + + Args: + file (str): The file to include. + enc (str, optional): The encoding to use with `pathlib.Path.read_text()`. + attr (str, optional): The string method to call on the file + content, e.g. `include(attr="strip")`. + + Returns: + str: The content of the file. + + Example: + ````jinja2 + ```yaml + {{ ".pre-commit-hool.yaml" | include }} + ``` + ```` + """ + + content = Path(file).read_text(encoding=enc) + + if attr is not None: + content = getattr(content, attr)() + + return content diff --git a/pyproject.toml b/pyproject.toml index 3b474d8..cd2a273 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,3 +71,9 @@ addopts = [ "--cov-report=term-missing:skip-covered", "--cov-report=xml", ] + +[tool.coverage.run] +omit = [ + "*/tests/*", + "*/docs/*", +] diff --git a/tests/test_filter.py b/tests/test_filter.py new file mode 100644 index 0000000..cbf6e2d --- /dev/null +++ b/tests/test_filter.py @@ -0,0 +1,104 @@ +import subprocess +from pathlib import Path + +import pytest + +from jinja2_pdoc import Environment + + +@pytest.fixture +def mock_shell(mocker): + return mocker.patch( + "subprocess.run", + return_value=subprocess.CompletedProcess( + args="echo testing..", + stdout="testing..", + stderr="", + returncode=0, + ), + ) + + +@pytest.fixture +def mock_include(mocker): + return mocker.patch.object(Path, "read_text", return_value="testing..") + + +@pytest.mark.parametrize( + "filter", + [ + "shell", + "include", + ], +) +def test_filter(filter): + + assert filter in Environment().filters + + +def test_add_filter(): + + env = Environment() + env.add_filter("upper", lambda s: s.upper()) + s = env.from_string('{{ "hello world." | upper }}').render() + + assert s == "HELLO WORLD." + + +def test_add_filter_raises(): + + with pytest.raises(TypeError): + Environment().add_filter("none", None) + + +def test_render_include(mock_include): + + template = '{{ "LICENSE" | include }}' + + code = Environment().from_string(template).render() + + assert code == "testing.." + + +def test_render_shell(mock_shell): + + template = '{{ "echo testing.." | shell }}' + + code = Environment().from_string(template).render() + + assert code == "testing.." + + +@pytest.mark.parametrize( + "attr", + [ + "stdout", + "stderr", + "returncode", + "args", + ], +) +def test_shell_result(mock_shell, attr): + assert isinstance(Environment.shell("echo testing..", result=attr), str) + + +@pytest.mark.parametrize( + "promt,expected", + [ + (None, "testing.."), + ("> ", "> echo testing..\n\ntesting.."), + ("$ %s", "$ echo testing..\ntesting.."), + ], +) +def test_shell_promt(mock_shell, promt, expected): + + stdout = Environment.shell("echo testing..", promt=promt) + + assert stdout == expected + + +def test_include_attr(mock_include): + + content = Environment.include("", attr="upper") + + assert content == "TESTING.." From 65ae5a334ad9cdb4317b2715b3d756f27579c02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 19:05:14 +0100 Subject: [PATCH 4/9] Remove outdated dependencies check and clean up pre-commit configuration --- .pre-commit-config.yaml | 9 --------- docs/__init__.py | 2 -- docs/outdated.py | 31 ------------------------------- jinja2_pdoc/cli.py | 2 +- 4 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 docs/outdated.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fae3e00..34155b4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,12 +54,3 @@ repos: hooks: - id: flake8 args: ["--max-line-length", "88"] - - repo: local - hooks: - - id: outdated - name: poetry outdated dependencies - entry: poetry run python docs/outdated.py - language: system - pass_filenames: false - stages: - - pre-push diff --git a/docs/__init__.py b/docs/__init__.py index c941ddf..e502aa8 100644 --- a/docs/__init__.py +++ b/docs/__init__.py @@ -1,7 +1,5 @@ from .__main__ import main as documentation -from .outdated import main as outdated __all__ = [ - "outdated", "documentation", ] diff --git a/docs/outdated.py b/docs/outdated.py deleted file mode 100644 index d880ac1..0000000 --- a/docs/outdated.py +++ /dev/null @@ -1,31 +0,0 @@ -import subprocess -import sys - - -def main(): - """ - Check for outdated dependencies for the project. - """ - - p = subprocess.run( - "poetry show --only=main -T -o", - shell=True, - capture_output=True, - encoding="utf-8", - ) - - try: - p.check_returncode() - except subprocess.CalledProcessError as e: - print(e, e.stderr.replace("\n", " "), sep="\n\n", file=sys.stderr) - return 2 - - if p.stdout.strip(): - print(p.stdout, file=sys.stderr) - return 1 - - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/jinja2_pdoc/cli.py b/jinja2_pdoc/cli.py index c4c82cd..b6bbd53 100644 --- a/jinja2_pdoc/cli.py +++ b/jinja2_pdoc/cli.py @@ -220,4 +220,4 @@ def cli(**kwargs): if __name__ == "__main__": - cli() + cli() # pragma: no cover From 7affcaa8733ad2ed567181b4fe659b0e3f0a14c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 20:31:56 +0100 Subject: [PATCH 5/9] Render readme from template - added filters to environemnt --- .gitignore | 1 + .pre-commit-sample.yaml | 6 ++ README.md | 116 +++++++++++++++++++------------------ docs/README.md.jinja2 | 87 ++++++++++++++++++++++++++++ docs/syntax.md | 58 +++++++++++++++++++ examples/example.md.jinja2 | 1 + examples/example.py | 23 ++++++++ jinja2_pdoc/cli.py | 6 +- jinja2_pdoc/environment.py | 23 +++++++- pyproject.toml | 1 + tests/test_cli.py | 4 +- tests/test_filter.py | 17 ++++++ 12 files changed, 278 insertions(+), 65 deletions(-) create mode 100644 .pre-commit-sample.yaml create mode 100644 docs/README.md.jinja2 create mode 100644 docs/syntax.md create mode 100644 examples/example.py diff --git a/.gitignore b/.gitignore index e313c48..089af00 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vscode docs/public/ +examples/*.md # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.pre-commit-sample.yaml b/.pre-commit-sample.yaml new file mode 100644 index 0000000..89f3e3a --- /dev/null +++ b/.pre-commit-sample.yaml @@ -0,0 +1,6 @@ +repos: + - repo: https://github.com/d-chris/jinja2_pdoc/ + rev: v1.1.0 + hooks: + - id: jinja2pdoc + files: docs/.*\.jinja2$ diff --git a/README.md b/README.md index d4be9fd..1358383 100644 --- a/README.md +++ b/README.md @@ -33,25 +33,25 @@ from jinja2_pdoc import Environment env = Environment() -s = """ - # jinja2-pdoc +template = """\ +# jinja2-pdoc - embedd python code directly from pathlib using a jinja2 extension based on pdoc +embedd python code directly from pathlib using a jinja2 extension based on pdoc - ## docstring from pathlib.Path +## docstring from pathlib.Path - {% pdoc pathlib:Path:docstring %} +{% pdoc pathlib:Path:docstring %} - ## source from pathlib.Path.open +## source from pathlib.Path.open - ```python - {% pdoc pathlib:Path.open:source.indent -%} - ``` - """ +```python +{% pdoc pathlib:Path.open:source.indent -%} +``` +""" -code = env.from_string(textwrap.dedent(s)).render() +code = env.from_string(template).render() -Path("example.md").write_text(code) +print(code) ```` ### Markdown @@ -59,7 +59,6 @@ Path("example.md").write_text(code) output of the [python code](#python) above. ````markdown - # jinja2-pdoc embedd python code directly from pathlib using a jinja2 extension based on pdoc @@ -151,46 +150,47 @@ Example: ## Command Line Interface -```console ->>> jinja2pdoc --help - -Usage: jinja2pdoc [OPTIONS] [FILES]... - - Render jinja2 one or multiple template files, wildcards in filenames are - allowed, e.g. `examples/*.jinja2`. - - If no 'filename' is provided in the frontmatter section of your file, e.g. - ''. All files are written to `output` directory - and `suffixes` will be removed. - - To ignore the frontmatter section use the `--no-meta` flag. - -Options: - -o, --output PATH output directory for files, if no 'filename' - is provided in the frontmatter. [default: - cwd] - -e, --encoding TEXT encoding of the files [default: utf-8] - -s, --suffixes TEXT suffixes which will be removed from templates, - if no 'filename' is provided in the - frontmatter [default: .jinja2, .j2] - --fail-fast exit on first error when rendering multiple - file - --meta / --no-meta parse frontmatter from the template, to search - for 'filename' [default: meta] - --rerender / --no-rerender Each file is rendered only once. [default: - no-rerender] - --silent suppress console output - --load-path / --no-load-path add the current working directory to path - [default: load-path] - --help Show this message and exit. +```cmd +$ jinja2pdoc --help + + Usage: jinja2pdoc [OPTIONS] [FILES]... + + Render jinja2 one or multiple template files, wildcards in filenames are + allowed, e.g. `examples/*.jinja2`. + + If no 'filename' is provided in the frontmatter section of your file, e.g. + ''. All files are written to `output` directory + and `suffixes` will be removed. + + To ignore the frontmatter section use the `--no-meta` flag. + + Options: + -o, --output PATH output directory for files, if no 'filename' + is provided in the frontmatter. [default: + cwd] + -e, --encoding TEXT encoding of the files [default: utf-8] + -s, --suffixes TEXT suffixes which will be removed from templates, + if no 'filename' is provided in the + frontmatter [default: .jinja2, .j2] + --fail-fast exit on first error when rendering multiple + file + --meta / --no-meta parse frontmatter from the template, to search + for 'filename' [default: meta] + --rerender / --no-rerender Each file is rendered only once. [default: + no-rerender] + --silent suppress console output + --load-path / --no-load-path add the current working directory to path + [default: load-path] + --help Show this message and exit. ``` -```console ->>> jinja2pdoc .\examples\*.jinja2 -rendered examples\example.md.jinja2...................... .\example.md +```cmd +$ jinja2pdoc .\examples\*.jinja2 + + rendering examples\example.md.jinja2...................... examples\example.md ``` -## pre-commit hook +## pre-commit-config **Per default the hook is not registered to `files`!** @@ -207,18 +207,20 @@ repos: files: docs/.*\.jinja2$ ``` -Use `additional_dependencies` to add extra dependencies to the pre-commit environment. Example see below. +Use [`additional_dependencies`](https://pre-commit.com/#config-additional_dependencies) to add extra dependencies to the pre-commit environment. > This is necessary when a module or source code rendered into your template contains modules that are not part of the standard library. +# pre-commit-hooks + ```yaml -repos: - - repo: https://github.com/d-chris/jinja2_pdoc/ - rev: v1.1.0 - hooks: - - id: jinja2pdoc - files: docs/.*\.jinja2$ - additional_dependencies: [pathlibutil] +- id: jinja2pdoc + name: render jinja2pdoc + description: render jinja2 templates to embedd python code directly from module using pdoc. + entry: jinja2pdoc + language: python + types: [jinja] + files: ^$ ``` ## Dependencies diff --git a/docs/README.md.jinja2 b/docs/README.md.jinja2 new file mode 100644 index 0000000..c2ce0bb --- /dev/null +++ b/docs/README.md.jinja2 @@ -0,0 +1,87 @@ +# jinja2-pdoc + +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/jinja2_pdoc)](https://pypi.org/project/jinja2_pdoc/) +[![PyPI - jinja2_pdoc](https://img.shields.io/pypi/v/jinja2_pdoc)](https://pypi.org/project/jinja2_pdoc/) +[![PyPI - Downloads](https://img.shields.io/pypi/dm/jinja2_pdoc)](https://pypi.org/project/jinja2_pdoc/) +[![PyPI - License](https://img.shields.io/pypi/l/jinja2_pdoc)](https://raw.githubusercontent.com/d-chris/jinja2_pdoc/main/LICENSE) +[![GitHub - pytest](https://img.shields.io/github/actions/workflow/status/d-chris/jinja2_pdoc/pytest.yml?logo=github&label=pytest)](https://github.com/d-chris/jinja2_pdoc/actions/workflows/pytest.yml) +[![GitHub - Page](https://img.shields.io/website?url=https%3A%2F%2Fd-chris.github.io%2Fjinja2_pdoc%2F&up_message=pdoc&logo=github&label=documentation)](https://d-chris.github.io/jinja2_pdoc) +[![GitHub tag (with filter)](https://img.shields.io/github/v/tag/d-chris/jinja2_pdoc?logo=github&label=github)](https://github.com/d-chris/jinja2_pdoc) +[![codecov](https://codecov.io/gh/d-chris/jinja2_pdoc/graph/badge.svg?token=19YB50ZL63)](https://codecov.io/gh/d-chris/jinja2_pdoc) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit) + +--- + +[`jinja2`](https://www.pypi.org/project/jinja2) extension based on [`pdoc`](https://pypi.org/project/pdoc/) to embedd python code directly from modules or files into your `jinja` template. + +Lazy loading of `docstrings`, `code` and `functions` directly from python modules into your `jinja2 template`. + +## Installation + +```cmd +pip install jinja2_pdoc +``` + +## Example + +Create a markdown file with `docstrings` and `source code` from `pathlib.Path` using `jinja2` with `jinja2_pdoc` extension. + +### Python + +````python +{{ "examples/example.py" | include }} +```` + +### Markdown + +output of the [python code](#python) above. + +````markdown +{{ "python examples/example.py" | shell }} +```` + +## Syntax + +{{ "docs/syntax.md" | include }} + +## Command Line Interface + +```cmd +{{ "jinja2pdoc --help" | shell(promt="$ ") | indent(2) }} +``` + +```cmd +{{ "jinja2pdoc .\examples\*.jinja2" | shell(promt="$ ") | indent(2) }} +``` + +## pre-commit-config + +**Per default the hook is not registered to `files`!** + +To render all template files from `docs` using `.pre-commit-config.yaml` add the following. + +You may add a `frontmatter` section at the beginning of in your templates to specify output directory and filename, e.g. ``. If no metadata are at the beginning of the template, the rendered file is written to the `output` directory which is default the current working direktory. + +```yaml +{{ ".pre-commit-sample.yaml" | include }} +``` + +Use [`additional_dependencies`](https://pre-commit.com/#config-additional_dependencies) to add extra dependencies to the pre-commit environment. + +> This is necessary when a module or source code rendered into your template contains modules that are not part of the standard library. + +# pre-commit-hooks + +```yaml +{{ ".pre-commit-hooks.yaml" | include }} +``` + +## Dependencies + +[![PyPI - autopep8](https://img.shields.io/pypi/v/autopep8?logo=pypi&logoColor=white&label=autopep8)](https://pypi.org/project/autopep8/) +[![PyPI - click](https://img.shields.io/pypi/v/click?logo=pypi&logoColor=white&label=click)](https://pypi.org/project/click/) +[![PyPI - jinja2](https://img.shields.io/pypi/v/jinja2?logo=jinja&logoColor=white&label=jinja2)](https://pypi.org/project/jinja2/) +[![PyPI - pdoc](https://img.shields.io/pypi/v/pdoc?logo=pypi&logoColor=white&label=pdoc)](https://pypi.org/project/pdoc/) +[![Pypi - PyYAML](https://img.shields.io/pypi/v/PyYAML?logo=pypi&logoColor=white&label=PyYAML)](https://pypi.org/project/PyYAML/) + +--- diff --git a/docs/syntax.md b/docs/syntax.md new file mode 100644 index 0000000..546b6ce --- /dev/null +++ b/docs/syntax.md @@ -0,0 +1,58 @@ + +`{% pdoc`[``](#module)`:`[``](#object)`:`[``](#pdoc_attr)`%}` + +### `` + +module name or path to python file, e.g.: + +- `pathlib` +- `examples/example.py` + +Example: + +```jinja2 +{% pdoc pathlib %} +``` + +### `` + +class and/or function names, eg. from `pathlib`: + +- `Path` +- `Path.open` + +Example: + +```jinja2 +{% pdoc pathlib:Path %} +``` + +### `` + +`pdoc` attributes: + +- `docstring` - docstring of the object +- `source` - source code of the object +- `code` - plane code from functions, without def and docstring + +Example: + +```jinja2 +{% pdoc pathlib:Path:docstring %} +``` + +### `[.str_attr]` + +optional `str` functions can be added to `` with a dot + +- `dedent` - removes common leading whitespace, see `textwrap.dedent` +- `indent` - format code with 2 spaces for indentation, see `autopep8.fix_code` +- `upper` - converts to upper case +- `lower` - converts to lower case +- `nodoc` - removes shebang and docstring + +Example: + +```jinja2 +{% pdoc pathlib:Path.open:code.dedent %} +``` diff --git a/examples/example.md.jinja2 b/examples/example.md.jinja2 index ea5e53e..2310d9b 100644 --- a/examples/example.md.jinja2 +++ b/examples/example.md.jinja2 @@ -1,3 +1,4 @@ + # jinja2-pdoc embedd python code directly from pathlib using a jinja2 extension based on pdoc diff --git a/examples/example.py b/examples/example.py new file mode 100644 index 0000000..c80ba1f --- /dev/null +++ b/examples/example.py @@ -0,0 +1,23 @@ +from jinja2_pdoc import Environment + +env = Environment() + +template = """\ +# jinja2-pdoc + +embedd python code directly from pathlib using a jinja2 extension based on pdoc + +## docstring from pathlib.Path + +{% pdoc pathlib:Path:docstring %} + +## source from pathlib.Path.open + +```python +{% pdoc pathlib:Path.open:source.indent -%} +``` +""" + +code = env.from_string(template).render() + +print(code) diff --git a/jinja2_pdoc/cli.py b/jinja2_pdoc/cli.py index b6bbd53..b4712f2 100644 --- a/jinja2_pdoc/cli.py +++ b/jinja2_pdoc/cli.py @@ -53,6 +53,8 @@ def echo(tag, file, out, silent=False): if silent: return + color = "yellow" + if isinstance(tag, Exception): out = str(tag)[:48] tag = type(tag).__name__ @@ -63,9 +65,7 @@ def echo(tag, file, out, silent=False): except ValueError: out = str(out)[-48:] - if tag == "skip": - color = "yellow" - else: + if tag != "skip": color = "green" tag = click.style(f"{tag[:16]:<16}", fg=color) diff --git a/jinja2_pdoc/environment.py b/jinja2_pdoc/environment.py index d99262a..5bfd0c2 100644 --- a/jinja2_pdoc/environment.py +++ b/jinja2_pdoc/environment.py @@ -29,6 +29,7 @@ def __init__(self, *args, **kwargs): self.add_filter("shell", self.shell) self.add_filter("include", self.include) + self.add_filter("strip", self.strip) def add_filter(self, name: str, func: Callable) -> None: """ @@ -91,7 +92,8 @@ def shell( prefix = f"{promt}{cmd}\n" output = f"{prefix}\n{output}" - return output + + return Environment.strip(output) @staticmethod def include( @@ -107,7 +109,7 @@ def include( file (str): The file to include. enc (str, optional): The encoding to use with `pathlib.Path.read_text()`. attr (str, optional): The string method to call on the file - content, e.g. `include(attr="strip")`. + content, e.g. `include(attr="upper")`. Returns: str: The content of the file. @@ -125,4 +127,19 @@ def include( if attr is not None: content = getattr(content, attr)() - return content + return Environment.strip(content) + + @staticmethod + def strip(text: str, chars: str = "\n ") -> str: + """ + Strips the specified characters from the beginning and end of the given text. + + Args: + text (str): The text to be stripped. + chars (str, optional): The characters to be stripped from the text. + Defaults to `"\n "`. + + Returns: + str: The stripped text. + """ + return text.strip(chars) diff --git a/pyproject.toml b/pyproject.toml index cd2a273..1eedcc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,4 +76,5 @@ addopts = [ omit = [ "*/tests/*", "*/docs/*", + "*/examples/*", ] diff --git a/tests/test_cli.py b/tests/test_cli.py index ce1cf98..ae62644 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -9,14 +9,14 @@ def test_cli_folder(tmp_path): result = runner.invoke( cli, [ - "examples/*.jinja2", + "docs/*.jinja2", "--output", str(tmp_path), ], ) assert result.exit_code == 0 assert "rendering" in result.output - assert tmp_path.joinpath("example.md").is_file() + assert tmp_path.joinpath("README.md").is_file() def test_cli_nofile(tmp_path): diff --git a/tests/test_filter.py b/tests/test_filter.py index cbf6e2d..5b4931a 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -29,6 +29,7 @@ def mock_include(mocker): [ "shell", "include", + "strip", ], ) def test_filter(filter): @@ -69,6 +70,15 @@ def test_render_shell(mock_shell): assert code == "testing.." +def test_render_strip(): + + template = '{{ " testing.. \n" | strip }}' + + code = Environment().from_string(template).render() + + assert code == "testing.." + + @pytest.mark.parametrize( "attr", [ @@ -102,3 +112,10 @@ def test_include_attr(mock_include): content = Environment.include("", attr="upper") assert content == "TESTING.." + + +def test_strip_chars(): + + content = Environment.strip("testing..", chars=".") + + assert content == "testing" From 3b1a4bc4f7b428e80037fd051f07fc586d7cbe88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 20:54:40 +0100 Subject: [PATCH 6/9] Refactor documentation generation and add README rendering functionality --- docs/__init__.py | 4 +- docs/__main__.py | 104 ++---------------------------------------- docs/documentation.py | 97 +++++++++++++++++++++++++++++++++++++++ docs/readme.py | 37 +++++++++++++++ poetry.lock | 17 ++++++- pyproject.toml | 3 ++ tests/test_cli.py | 19 ++++---- 7 files changed, 171 insertions(+), 110 deletions(-) create mode 100644 docs/documentation.py create mode 100644 docs/readme.py diff --git a/docs/__init__.py b/docs/__init__.py index e502aa8..3b69224 100644 --- a/docs/__init__.py +++ b/docs/__init__.py @@ -1,5 +1,7 @@ -from .__main__ import main as documentation +from .documentation import main as documentation +from .readme import main as readme __all__ = [ "documentation", + "readme", ] diff --git a/docs/__main__.py b/docs/__main__.py index a786b85..829040c 100644 --- a/docs/__main__.py +++ b/docs/__main__.py @@ -1,109 +1,15 @@ -import json -import os -import shutil -import warnings -from contextlib import contextmanager -from pathlib import Path +from . import documentation, readme -@contextmanager -def pushd(path): - cwd = os.getcwd() - try: - os.chdir(path) - yield Path(path) - finally: - os.chdir(cwd) - - -class WarningMessageEncoder(json.JSONEncoder): - def default(self, obj): - - if isinstance(obj, warnings.WarningMessage): - return { - k: v - for k, v in obj.__dict__.items() - if v is not None and not k.startswith("_") - } - - if isinstance(obj, type): - return obj.__name__ - - if isinstance(obj, Exception): - return str(obj).strip().split("\n") - - return super().default(obj) - - -def main() -> int: +def main(): """ - Create a static html documentation for the project in the 'public' directory. - - Requires follwing PyPi packages: - - 'pdoc' - - 'pathlibutil' + Render the README.md file and create a static html documentation for the project + in the 'public' directory. Returns non-zero on failure. """ - try: - from pdoc import pdoc, render - - with pushd(Path(__file__).parent) as cwd: - print(f"\nrunning docs in {cwd=}...") - - documentation = cwd / "public" - - try: - print("cleaning output directory.") - shutil.rmtree(documentation) - except FileNotFoundError: - pass - - documentation.mkdir(parents=True) - - config = { - "template_directory": cwd / "dark-mode", - "show_source": False, - "search": False, - "docformat": "google", - } - - modules = [ - "jinja2_pdoc", - "pdoc", - "jinja2", - "pathlib", - "subprocess", - ] - - with warnings.catch_warnings(record=True) as messages: - print(f"rendering {modules=}.") - - render.configure(**config) - pdoc(*modules, output_directory=documentation) - - log = documentation / "warnings.json" - - with log.open("w", encoding="utf-8") as f: - json.dump( - messages, - f, - cls=WarningMessageEncoder, - ) - - if messages: - print(f"{log=} generated with {len(messages)} warnings.") - except ModuleNotFoundError as e: - print(f"Creation failed, due missing dependency!\n\tpip install {e.name}") - return 2 - except Exception as e: - print(f"{documentation=} creation failed!\n\t{e}") - return 1 - - print(f"{documentation=} generated successfully.\n") - - return 0 + return readme() or documentation() if __name__ == "__main__": diff --git a/docs/documentation.py b/docs/documentation.py new file mode 100644 index 0000000..ea6aec9 --- /dev/null +++ b/docs/documentation.py @@ -0,0 +1,97 @@ +import json +import warnings + + +class WarningMessageEncoder(json.JSONEncoder): + def default(self, obj): + + if isinstance(obj, warnings.WarningMessage): + return { + k: v + for k, v in obj.__dict__.items() + if v is not None and not k.startswith("_") + } + + if isinstance(obj, type): + return obj.__name__ + + if isinstance(obj, Exception): + return str(obj).strip().split("\n") + + return super().default(obj) + + +def main() -> int: + """ + Create a static html documentation for the project in the 'public' directory. + + Requires follwing PyPi packages: + - 'pdoc' + - 'pathlibutil' + + Returns non-zero on failure. + """ + + try: + from pathlibutil import Path + from pdoc import pdoc, render + + with Path(__file__).parent as cwd: + print(f"\nrunning docs in {cwd=}...") + + documentation: Path = cwd / "public" + + try: + print(f"cleaning output directory {documentation.size()}.") + documentation.delete(recursive=True, missing_ok=True) + except FileNotFoundError: + pass + + documentation.mkdir(parents=True) + + config = { + "template_directory": cwd / "dark-mode", + "show_source": False, + "search": False, + "docformat": "google", + } + + modules = [ + "jinja2_pdoc", + "pdoc", + "jinja2", + "pathlib", + "subprocess", + ] + + with warnings.catch_warnings(record=True) as messages: + print(f"rendering {modules=}.") + + render.configure(**config) + pdoc(*modules, output_directory=documentation) + + log = documentation / "warnings.json" + + with log.open("w", encoding="utf-8") as f: + json.dump( + messages, + f, + cls=WarningMessageEncoder, + ) + + if messages: + print(f"{log=} generated with {len(messages)} warnings.") + except ModuleNotFoundError as e: + print(f"Creation failed, due missing dependency!\n\tpip install {e.name}") + return 2 + except Exception as e: + print(f"{documentation=} creation failed!\n\t{e}") + return 1 + + print(f"{documentation=} generated successfully.\n") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/readme.py b/docs/readme.py new file mode 100644 index 0000000..761294b --- /dev/null +++ b/docs/readme.py @@ -0,0 +1,37 @@ +from jinja2_pdoc import Environment + + +def main(): + """ + Render the README.md file using the README.md.jinja2 template. + + Requires following PyPi packages: + - 'pathlibutil' + + Returns non-zero on failure. + """ + + try: + from pathlibutil import Path + + template = Path(__file__).with_name("README.md.jinja2") + readme = Path("README.md") + + with template.open("r") as file: + template = Environment( + keep_trailing_newline=True, + ).from_string(file.read()) + + with readme.open("w") as file: + file.write(template.render()) + + except Exception as e: + print(f"{readme=} creation failed!\n\t{e}") + return 1 + + print(f"{readme=} created successfully!") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/poetry.lock b/poetry.lock index 9c74366..a38ca16 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "astunparse" @@ -313,6 +313,19 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[[package]] +name = "pathlibutil" +version = "0.3.0" +description = "inherits from pathlib.Path with methods for hashing, copying, deleting and more" +optional = false +python-versions = "<4.0.0,>=3.8.1" +files = [ + {file = "pathlibutil-0.3.0-py3-none-any.whl", hash = "sha256:2769b8312b80470c62c1afb408a99af0f20495d8436401bb035e986a295ff747"}, +] + +[package.extras] +7z = ["py7zr (>=0.20.2,<0.21.0)"] + [[package]] name = "pdoc" version = "14.7.0" @@ -654,4 +667,4 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "79d615a79ad564d7091d9c077b24f96e1bee39840bb30dd269c335a806729ff6" +content-hash = "3947637c822f4c7f0ced703d1198cf11033641cc69f2e2311892bf2434c9f8c1" diff --git a/pyproject.toml b/pyproject.toml index 1eedcc8..bb39cb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,9 @@ pytest-mock = "^3.14.0" [tool.poetry.group.dev.dependencies] tox = "^4.11.4" +[tool.poetry.group.docs.dependencies] +pathlibutil = "0.3.0" + [[tool.poetry.source]] name = "testpypi" url = "https://test.pypi.org/legacy/" diff --git a/tests/test_cli.py b/tests/test_cli.py index ae62644..6444f46 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,3 +1,5 @@ +import warnings + import pytest from click.testing import CliRunner @@ -6,14 +8,15 @@ def test_cli_folder(tmp_path): runner = CliRunner() - result = runner.invoke( - cli, - [ - "docs/*.jinja2", - "--output", - str(tmp_path), - ], - ) + with warnings.catch_warnings(record=True, category=DeprecationWarning): + result = runner.invoke( + cli, + [ + "docs/*.jinja2", + "--output", + str(tmp_path), + ], + ) assert result.exit_code == 0 assert "rendering" in result.output assert tmp_path.joinpath("README.md").is_file() From f1eeb3d10b0808d7b083cd342e7dfc3a1d20fce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 21:03:27 +0100 Subject: [PATCH 7/9] Add readme rendering hook to pre-commit configuration --- .pre-commit-config.yaml | 12 ++++++++++-- README.md | 2 +- docs/README.md.jinja2 | 2 +- poetry.lock | 9 +++++---- pyproject.toml | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 34155b4..1ba8c3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: hooks: - id: add-trailing-comma - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.4.3 + rev: v2.5.0 hooks: - id: pyproject-fmt - repo: https://github.com/tox-dev/tox-ini-fmt @@ -46,7 +46,7 @@ repos: - id: poetry-check - id: poetry-lock - repo: https://github.com/google/yamlfmt - rev: v0.13.0 + rev: v0.14.0 hooks: - id: yamlfmt - repo: https://github.com/PyCQA/flake8 @@ -54,3 +54,11 @@ repos: hooks: - id: flake8 args: ["--max-line-length", "88"] + - repo: local + hooks: + - id: readme + name: rendering readme + entry: poetry run python docs/readme.py + pass_filenames: false + always_run: true + language: system diff --git a/README.md b/README.md index 1358383..7f0dc68 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ Use [`additional_dependencies`](https://pre-commit.com/#config-additional_depend > This is necessary when a module or source code rendered into your template contains modules that are not part of the standard library. -# pre-commit-hooks +## pre-commit-hooks ```yaml - id: jinja2pdoc diff --git a/docs/README.md.jinja2 b/docs/README.md.jinja2 index c2ce0bb..882fee5 100644 --- a/docs/README.md.jinja2 +++ b/docs/README.md.jinja2 @@ -70,7 +70,7 @@ Use [`additional_dependencies`](https://pre-commit.com/#config-additional_depend > This is necessary when a module or source code rendered into your template contains modules that are not part of the standard library. -# pre-commit-hooks +## pre-commit-hooks ```yaml {{ ".pre-commit-hooks.yaml" | include }} diff --git a/poetry.lock b/poetry.lock index a38ca16..e2478eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -315,16 +315,17 @@ files = [ [[package]] name = "pathlibutil" -version = "0.3.0" +version = "0.3.4" description = "inherits from pathlib.Path with methods for hashing, copying, deleting and more" optional = false python-versions = "<4.0.0,>=3.8.1" files = [ - {file = "pathlibutil-0.3.0-py3-none-any.whl", hash = "sha256:2769b8312b80470c62c1afb408a99af0f20495d8436401bb035e986a295ff747"}, + {file = "pathlibutil-0.3.4-py3-none-any.whl", hash = "sha256:7f5b9575ca785dc4e61bacf3d707c3eac70df9b8e05103c00fe1727b0f0a777f"}, + {file = "pathlibutil-0.3.4.tar.gz", hash = "sha256:e866c106af6034924f2e7cb4a16d0e543100009381a943c85d20c899af8982ad"}, ] [package.extras] -7z = ["py7zr (>=0.20.2,<0.21.0)"] +7z = ["py7zr (>=0.20.2)"] [[package]] name = "pdoc" @@ -667,4 +668,4 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"] [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "3947637c822f4c7f0ced703d1198cf11033641cc69f2e2311892bf2434c9f8c1" +content-hash = "a10ccf0ec34aadaae32d99e08869fcab391d18270ad960354fe0d87e5bd23bc5" diff --git a/pyproject.toml b/pyproject.toml index bb39cb8..df5aeaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ pytest-mock = "^3.14.0" tox = "^4.11.4" [tool.poetry.group.docs.dependencies] -pathlibutil = "0.3.0" +pathlibutil = "^0.3.0" [[tool.poetry.source]] name = "testpypi" From bde0d25a6196ccdda06156be895d44f1a66b43a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 21:28:41 +0100 Subject: [PATCH 8/9] Enhance documentation with new filter examples --- .github/workflows/publish.yml | 4 ++-- README.md | 32 ++++++++++++++++++++++++++++++-- docs/README.md.jinja2 | 8 ++++++-- docs/filter.md | 25 +++++++++++++++++++++++++ jinja2_pdoc/environment.py | 11 +++++++---- 5 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 docs/filter.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b694913..ebbbda7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,10 +12,10 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.12 + python-version: 3.13 - name: Install dependencies run: | - poetry install --only main + poetry install --only main,docs - name: Build Documentation run: | poetry run python -m docs diff --git a/README.md b/README.md index 7f0dc68..2eee408 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,34 @@ Example: {% pdoc pathlib:Path.open:code.dedent %} ``` +## Filter + +Filter to use in `jinja2` template. + +### include + +`Environment.include` - returns the content of the file. + +```jinja +{{ "path/to/file" | include(enc="utf-8") }} +``` + +### shell + +`Environment.shell` - run shell command and return the selected result from `subprocess.CompletedProcess`. + +```jinja +{{ "python --version" | shell(promt=">>> %s\n") }} +``` + +### strip + +`Environment.strip` - remove leading and trailing whitespace and newlines from a string. + +```jinja +{{ "path/to/file" | include | strip }} +``` + ## Command Line Interface ```cmd @@ -192,8 +220,6 @@ $ jinja2pdoc .\examples\*.jinja2 ## pre-commit-config -**Per default the hook is not registered to `files`!** - To render all template files from `docs` using `.pre-commit-config.yaml` add the following. You may add a `frontmatter` section at the beginning of in your templates to specify output directory and filename, e.g. ``. If no metadata are at the beginning of the template, the rendered file is written to the `output` directory which is default the current working direktory. @@ -213,6 +239,8 @@ Use [`additional_dependencies`](https://pre-commit.com/#config-additional_depend ## pre-commit-hooks +**Per default the hook is not registered to `files`!** + ```yaml - id: jinja2pdoc name: render jinja2pdoc diff --git a/docs/README.md.jinja2 b/docs/README.md.jinja2 index 882fee5..4330cf5 100644 --- a/docs/README.md.jinja2 +++ b/docs/README.md.jinja2 @@ -44,6 +44,10 @@ output of the [python code](#python) above. {{ "docs/syntax.md" | include }} +## Filter + +{{ "docs/filter.md" | include }} + ## Command Line Interface ```cmd @@ -56,8 +60,6 @@ output of the [python code](#python) above. ## pre-commit-config -**Per default the hook is not registered to `files`!** - To render all template files from `docs` using `.pre-commit-config.yaml` add the following. You may add a `frontmatter` section at the beginning of in your templates to specify output directory and filename, e.g. ``. If no metadata are at the beginning of the template, the rendered file is written to the `output` directory which is default the current working direktory. @@ -72,6 +74,8 @@ Use [`additional_dependencies`](https://pre-commit.com/#config-additional_depend ## pre-commit-hooks +**Per default the hook is not registered to `files`!** + ```yaml {{ ".pre-commit-hooks.yaml" | include }} ``` diff --git a/docs/filter.md b/docs/filter.md new file mode 100644 index 0000000..0e9cb2b --- /dev/null +++ b/docs/filter.md @@ -0,0 +1,25 @@ +Filter to use in `jinja2` template. + +### include + +`Environment.include` - returns the content of the file. + +```jinja +{{ "path/to/file" | include(enc="utf-8") }} +``` + +### shell + +`Environment.shell` - run shell command and return the selected result from `subprocess.CompletedProcess`. + +```jinja +{{ "python --version" | shell(promt=">>> %s\n") }} +``` + +### strip + +`Environment.strip` - remove leading and trailing whitespace and newlines from a string. + +```jinja +{{ "path/to/file" | include | strip }} +``` diff --git a/jinja2_pdoc/environment.py b/jinja2_pdoc/environment.py index 5bfd0c2..98746c8 100644 --- a/jinja2_pdoc/environment.py +++ b/jinja2_pdoc/environment.py @@ -115,11 +115,9 @@ def include( str: The content of the file. Example: - ````jinja2 - ```yaml + ```jinja2 {{ ".pre-commit-hool.yaml" | include }} ``` - ```` """ content = Path(file).read_text(encoding=enc) @@ -137,9 +135,14 @@ def strip(text: str, chars: str = "\n ") -> str: Args: text (str): The text to be stripped. chars (str, optional): The characters to be stripped from the text. - Defaults to `"\n "`. + Defaults to `"\\n "`. Returns: str: The stripped text. + + Example: + ```jinja2 + {{ " hello world. \\n" | strip }} + ``` """ return text.strip(chars) From 0662251a63d93dcc6d1fd6878531b3766d111d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=B6rrer?= Date: Sun, 3 Nov 2024 21:34:11 +0100 Subject: [PATCH 9/9] Fix warning handling in CLI tests to capture DeprecationWarnings consistently --- tests/test_cli.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 6444f46..b2b552a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -8,7 +8,11 @@ def test_cli_folder(tmp_path): runner = CliRunner() - with warnings.catch_warnings(record=True, category=DeprecationWarning): + + with warnings.catch_warnings(record=True): + warnings.simplefilter("always", DeprecationWarning) + + # Your code that may trigger a DeprecationWarning result = runner.invoke( cli, [