# Doctest w/ docutils Source: https://gp-libs.git-pull.com/doctest/ (doctest_docutils)= (doctest_docutils_module)= # Doctest w/ docutils Built on {mod}`doctest`. ::::{grid} 1 1 2 2 :gutter: 2 2 3 3 :::{grid-item-card} pytest plugin :link: pytest :link-type: doc Run doctests in `.rst` and `.md` files via pytest. ::: :::: :::{note} Before you begin, acquaint yourself with: - {mod}`doctest` - normal usage via: ```console $ python -m doctest [file] ``` - Know what [docutils] does: parses reStructuredText (.rst). With the helper of [myst-parser], it also parses markdown (.md) ::: ## reStructuredText ```console $ python -m doctest_docutils README.rst ``` That's what `doctest` does by design. Pass `-v` for verbose output. ## Markdown If you install [myst-parser], doctest will run on .md files. ```console $ python -m doctest_docutils README.md ``` As with the reST example above, no output. ```{toctree} :hidden: pytest ``` [docutils]: https://www.docutils.org/ ## Internals :::{note} To get a deeper understanding, dig into: - {mod}`doctest` - All console arguments by seeing the help command: ```console $ python -m doctest --help ``` - data structures, e.g. {class}`doctest.DocTest` - source code: https://github.com/python/cpython/blob/3.11/Lib/doctest.py - documentation: https://docs.python.org/3/library/doctest.html - typings: https://github.com/python/typeshed/blob/master/stdlib/doctest.pyi - [docutils]: which parses reStructuredText (.rst) and markdown (.md, with the help of [myst-parser]) ::: ## API ```{eval-rst} .. automodule:: doctest_docutils :members: :show-inheritance: :undoc-members: ``` --- # pytest plugin Source: https://gp-libs.git-pull.com/doctest/pytest/ (pytest_plugin)= (pytest_doctest_docutils)= # pytest plugin :::{note} This plugin disables {ref}`pytest's standard doctest plugin `. ::: The pytest plugin is built on top of the {ref}`doctest_docutils` module, which is in turn compatible with {mod}`doctest`. ## Using fixtures Normal pytest convention apply, {ref}`a visible conftest.py must be available ` for the file being tested. This requires a understanding of `confest.py` - in the same way {ref}`pytest's vanilla doctest plugin ` does. You know your project's package structure best, the follow just serve as examples of new places conftest.py will be needed: ### Example: Root-level `README` In other words, if you want to test a project's `README.md` in the project root, a `conftest.py` would be needed at the root-level. This also has the benefit of reducing duplication: conftest.py # content of conftest.py import pytest @pytest.fixture def username(): return 'username' README.rst Our project ----------- .. doctest:: # content of tests/test_something.py def test_username(username): assert username == 'username' Now you can do: ```console $ pytest README.rst ``` ### Examples: `docs/` Let's assume `.md` and `.rst` files in `docs/`, this means you need to import `conftest.py` docs/ conftest.py # import conftest of project import pytest @pytest.fixture def username(): return 'username' usage.rst Our project ----------- .. doctest:: # content of tests/test_something.py def test_username(username): assert username == 'username' Now you can do: ```console $ pytest docs/ ``` ## File support ### reStructuredText (`.rst`) ```console $ pytest README.rst ``` ```console $ pytest docs/ ``` ### Markdown (`.md`) If you install [myst-parser], doctest will run on .md files. ```console $ pytest README.md ``` ```console $ pytest docs/ ``` [myst-parser]: https://myst-parser.readthedocs.io/en/latest/ ## Scanning python files You can retain {ref}`pytest's standard doctest plugin ` of running doctests in `.py` by passing `--doctest-docutils-modules`: ```console $ py.test src/ --doctest-docutils-modules ``` You can disable it via `--no-doctest-docutils-modules`: ```console $ py.test src/ --no-doctest-docutils-modules ``` ## Case examples - [libtmux] ([source](https://github.com/tmux-python/libtmux/tree/v0.15.0post0)): - Documentation w/ docutils: - [README.md](https://github.com/tmux-python/libtmux/blob/v0.15.0post0/README.md) ([raw](https://github.com/tmux-python/libtmux/raw/v0.15.0post0/README.md)) _Note: `pytest README.md` requires you have a `conftest.py` directly in the project root_ - [docs/topics/traversal.md](https://github.com/tmux-python/libtmux/blob/v0.15.0post0/docs/topics/traversal.md) ([raw](https://github.com/tmux-python/libtmux/raw/v0.15.0post0/docs/topics/traversal.md)) - Configuration: - Doctests support pytest fixtures through [`doctest_namespace`](https://docs.pytest.org/en/7.1.x/how-to/doctest.html#doctest-namespace-fixture) See [`add_doctest_fixtures()`](https://github.com/tmux-python/libtmux/blob/v0.15.0post0/src/libtmux/conftest.py#L15-L26) in [`src/libtmux/conftest.py`](https://github.com/tmux-python/libtmux/blob/v0.15.0post0/src/libtmux/conftest.py) [libtmux]: https://libtmux.git-pull.com/ ## API ```{eval-rst} .. automodule:: pytest_doctest_docutils :members: :show-inheritance: :undoc-members: ``` --- # Changelog Source: https://gp-libs.git-pull.com/history/ (history)= ```{include} ../CHANGES ``` --- # gp-libs Source: https://gp-libs.git-pull.com/ (index)= # gp-libs Test and documentation utilities for [git-pull](https://github.com/git-pull) projects. ::::{grid} 1 1 2 2 :gutter: 2 2 3 3 :::{grid-item-card} Quickstart :link: quickstart :link-type: doc Install and get started in minutes. ::: :::{grid-item-card} doctest_docutils :link: doctest/index :link-type: doc Run doctests in reStructuredText and Markdown files. ::: :::{grid-item-card} linkify_issues :link: linkify_issues/index :link-type: doc Automatically link `#123` to GitHub issues in Sphinx docs. ::: :::{grid-item-card} Contributing :link: project/index :link-type: doc Development setup, code style, release process. ::: :::: ## Install ```console $ pip install gp-libs ``` ```console $ uv add gp-libs ``` ## At a glance Run doctests in Markdown and reStructuredText files: ```console $ python -m doctest_docutils README.md -v ``` Auto-link issue references in Sphinx documentation: ```python # conf.py extensions = ["linkify_issues"] issue_url_tpl = "https://github.com/myorg/myrepo/issues/{issue_id}" ``` ```{toctree} :hidden: quickstart doctest/index linkify_issues/index project/index history GitHub ``` --- # Autolink GitHub issues Source: https://gp-libs.git-pull.com/linkify_issues/ (linkify_issues)= (linkify-issues)= # Autolink GitHub issues Automatically link plaintext issues, e.g. `\#1`, as [#1](https://github.com/git-pull/gp-libs/issues/1). This is a perfectly legitimately request, even sphinx's own conf.py does a [non-docutils hack](https://github.com/sphinx-doc/sphinx/blob/v5.1.1/doc/conf.py#L151-L170) to link plain-text nodes. ## Configuration In your _conf.py_: 1. Add `'linkify_issues'` to `extensions` ```python extensions = [ # ... "linkify_issues", ] ``` 2. Configure your issue URL, `issue_url_tpl`: ```python # linkify_issues issue_url_tpl = 'https://github.com/git-pull/gp-libs/issues/{issue_id}' ``` The config variable is formatted via {meth}`str.format` where `issue_id` is `42` if the text is \#42. ### Issue pattern `issue_re` is available to optionally adjust the pattern plain text is matched against. By default it is :var:`linkify_issues` ```python r"#(?P\d+)" ``` Where `^\` negates matches (as seen below) and numbers are matched via `\d`. You can pass a `str` - which is automatically upcasted when parsing - or a :class:`re.Pattern`. In conf.py, to catch letters and dashes too: ```python issue_re = r"#(?P[\da-z-]+)" ``` That will match patterns like #ISSUE-34, where `'ISSUE-34'` will be captured. What you may prefer is just capturing the `34`: ```python issue_re = r"#ISSUE-(?P\d+)" ``` `issue_url_tpl`'s regex patterns can be extended and passed into `issue_re`'s string formatting: ```python issue_re = r"#(?P(issue|pull)+)-(?P\d+)" issue_url_tpl = "https://github.com/git-pull/gp-libs/{page_type}/{issue_id}" ``` \#issue-1 will be [#issue-1](https://github.com/git-pull/gp-libs/issue/1) \#pull-1 will be [#pull-1](https://github.com/git-pull/gp-libs/pull/1) If your needs are more complex, you may need to fork it for yourself or suggest a PR. ## API ```{eval-rst} .. automodule:: linkify_issues :members: :show-inheritance: :undoc-members: ``` --- # Code Style Source: https://gp-libs.git-pull.com/project/code-style/ # Code Style ## Formatting gp-libs uses [ruff](https://github.com/astral-sh/ruff) for both linting and formatting. ```console $ uv run ruff format . ``` ```console $ uv run ruff check . --fix --show-fixes ``` ## Type Checking Strict [mypy](https://mypy-lang.org/) is enforced across `src/` and `tests/`. ```console $ uv run mypy . ``` ## Docstrings Follow [NumPy docstring style](https://numpydoc.readthedocs.io/en/latest/format.html) for all public functions, methods, and classes. --- # Contributing Source: https://gp-libs.git-pull.com/project/contributing/ # Contributing Install [git] and [uv]. Clone: ```console $ git clone https://github.com/git-pull/gp-libs.git ``` ```console $ cd gp-libs ``` Install packages: ```console $ uv sync --all-extras --dev ``` ## Tests ```console $ uv run py.test ``` ### Automatically run tests on file save 1. `make start` (via [pytest-watcher]) 2. `make watch_test` (requires installing [entr(1)]) [pytest-watcher]: https://github.com/olzhasar/pytest-watcher ## Documentation Default preview server: http://localhost:8034 [sphinx-autobuild] will automatically build the docs, watch for file changes and launch a server. From home directory: `make start_docs` From inside `docs/`: `make start` [sphinx-autobuild]: https://github.com/executablebooks/sphinx-autobuild ### Manual documentation (the hard way) `cd docs/` and `make html` to build. `make serve` to start http server. Helpers: `make build_docs`, `make serve_docs` Rebuild docs on file change: `make watch_docs` (requires [entr(1)]) Rebuild docs and run server via one terminal: `make dev_docs` (requires above, and a `make(1)` with `-J` support, e.g. GNU Make) [git]: https://git-scm.com/ [uv]: https://github.com/astral-sh/uv [entr(1)]: http://eradman.com/entrproject/ [`entr(1)`]: http://eradman.com/entrproject/ --- # Project Source: https://gp-libs.git-pull.com/project/ (project)= # Project Information for contributors and maintainers. ::::{grid} 1 1 2 2 :gutter: 2 2 3 3 :::{grid-item-card} Contributing :link: contributing :link-type: doc Development setup, running tests, submitting PRs. ::: :::{grid-item-card} Code Style :link: code-style :link-type: doc Ruff, mypy, NumPy docstrings, import conventions. ::: :::{grid-item-card} Releasing :link: releasing :link-type: doc Release checklist and version policy. ::: :::: ```{toctree} :hidden: contributing code-style releasing ``` --- # Releasing Source: https://gp-libs.git-pull.com/project/releasing/ # Releasing ## Version Policy gp-libs is pre-1.0. Minor version bumps may include breaking changes. ## Release Process [uv] handles virtualenv creation, package requirements, versioning, building, and publishing. There is no setup.py or requirements files. 1. Update `CHANGES` with release notes 2. Bump version in `src/gp_libs.py` and `pyproject.toml` 3. Commit and tag: ```console $ git commit -m 'build(gp_libs): Tag v0.1.1' ``` ```console $ git tag v0.1.1 ``` 4. Push: ```console $ git push ``` ```console $ git push --tags ``` [uv]: https://github.com/astral-sh/uv --- # Quickstart Source: https://gp-libs.git-pull.com/quickstart/ (quickstart)= # Quickstart ## Installation For latest official version: ```console $ pip install --user gp-libs ``` Upgrading: ```console $ pip install --user --upgrade gp-libs ``` (developmental-releases)= ### Developmental releases New versions of gp-libs are published to PyPI as alpha, beta, or release candidates. In their versions you will see notification like `a1`, `b1`, and `rc1`, respectively. `1.10.0b4` would mean the 4th beta release of `1.10.0` before general availability. - [pip]\: ```console $ pip install --user --upgrade --pre gp-libs ``` - [pipx]\: ```console $ pipx install --suffix=@next 'gp-libs' --pip-args '\--pre' --force ``` - [uv]\: ```console $ uv add gp-libs --prerelease allow ``` - [uvx]\: ```console $ uvx --from 'gp-libs' --prerelease allow gp-libs ``` via trunk (can break easily): - [pip]\: ```console $ pip install --user -e git+https://github.com/git-pull/gp-libs.git#egg=gp-libs ``` - [pipx]\: ```console $ pipx install --suffix=@master 'gp-libs @ git+https://github.com/git-pull/gp-libs.git@master' --force ``` - [uv]\: ```console $ uv add gp-libs --from git+https://github.com/git-pull/gp-libs.git ``` [pip]: https://pip.pypa.io/en/stable/ [pipx]: https://pypa.github.io/pipx/docs/ [uv]: https://docs.astral.sh/uv/ [uvx]: https://docs.astral.sh/uv/guides/tools/ ---