Pre-release
This release is a pre-release and may not be stable for production use.
= AsciiDoctest: Verifiable, Stateful, and Interactive Documentation with AsciiDoc
:toc: left
:idprefix:
:idseparator: -
image:https://github.com/webmaven/asciidoctest/actions/workflows/ci.yml/badge.svg[CI Status, link=https://github.com/webmaven/asciidoctest/actions/workflows/ci.yml]
image:https://img.shields.io/pypi/v/asciidoctest.svg[PyPI Version, link=https://pypi.org/project/asciidoctest/]
image:https://img.shields.io/badge/python-3.14-blue.svg[Python Version, link=https://www.python.org/]
image:https://img.shields.io/badge/coverage-97%25-green.svg[Test Coverage]
image:https://img.shields.io/badge/License-Apache_2.0-blue.svg[License, link=https://opensource.org/licenses/Apache-2.0]
link:https://github.com/webmaven/asciidoctest[AsciiDoctest] is an executable documentation runner and narrative testing tool designed to parse, collect, and verify link:https://www.python.org/[Python] code blocks directly from link:https://asciidoc.org/[AsciiDoc] (`.adoc`) files and Python docstrings. It is published on link:https://pypi.org/project/asciidoctest/[PyPI].
It integrates AST-based parsing using link:https://github.com/webmaven/asciidoctrine[asciidoctrine] and link:https://github.com/webmaven/asciidocstring[asciidocstring] to provide accurate, standard-compliant testing of your documentation's code examples.
== Why AsciiDoctest?
Traditional doctest extractors rely on fragile regular expressions to parse block structures and find code examples. This approach frequently breaks when encountering inline styles, unparsed attributes, nested lists, or complex block boundaries.
AsciiDoctest solves this by relying on native **Abstract Syntax Tree (AST)** parsing:
* Leverages full structural documents parsed via link:https://github.com/webmaven/asciidoctrine[asciidoctrine] and link:https://github.com/webmaven/asciidocstring[asciidocstring].
* Fully honors standard link:https://asciidoc.org/[AsciiDoc] roles, positional arguments, attributes, and includes.
* Maintains high-fidelity source coordinates (line and column mappings) for precise failure reporting.
== Executable Documentation State Models
Writing a multi-step tutorial, interactive guide, or documentation book often requires code examples that build on top of each other. At the same time, writers need the ability to run isolated one-off checks or explore alternative paths without leaking state.
AsciiDoctest provides a unified, symmetric state model across both interactive REPL blocks and non-interactive script blocks to facilitate predictable, stateful documentation.
A Python code block is considered **marked** when it contains the `test` or `shared` keyword inside its block header—either as a positional argument (e.g., `[source,python,test]`), an explicit attribute (e.g., `[source,python,test="true"]`), or a block role (e.g., `[source,python,role="shared"]`).
Based on these markers, blocks are executed under the following models:
* **No Marker or Attribute**: Treated as an ordinary, non-executable code listing.
+
--
*Exception*: In `eager` mode, unmarked listings are executed as `test` blocks (fully isolated and ephemeral) ONLY if no block in the entire document has any explicit markers of either sort.
--
* **`test` (Isolated & Ephemeral)**: Runs in a completely clean, isolated namespace (`{}`). Any variables or state changes created during its execution are immediately discarded. This aligns with standard unit-testing isolation principles.
* **`shared` (Read-Write & Persistent)**: Participates in a continuous, stateful document timeline (like a stateful notebook). Any classes, variables, or functions defined in early `shared` blocks are fully accessible and modifiable in subsequent `shared` blocks.
* **`shared, test` (Ephemeral Copy)**: Gets access to a read-only copy of the shared state *at that point in the document*, but any mutations or local bindings created within the block are discarded when the block completes.
This model is extremely consistent, easy to reason about, and ensures that your documentation's code examples are always accurate and tested.
== Features
* **AST-Based Parsing**: Structural parsing using link:https://github.com/webmaven/asciidoctrine[asciidoctrine] (AsciiDoc parser) and link:https://github.com/webmaven/asciidocstring[asciidocstring] (AsciiDoc docstring parser).
* **Execution Modes**:
- `explicit` (default): Only executes blocks with the `test` or `shared` markers.
- `eager`: Falls back to executing all `[source,python]` listings as isolated `test` blocks, but only if the document contains zero explicit markers.
* **Pytest Integration**: Automatic discovery and execution of `.adoc` files and Python docstrings via registered link:https://docs.pytest.org/[pytest] collectors.
* **Unittest Compatibility**: Suite wrappers (`DocTestSuite` and `DocFileSuite`) designed to integrate with the standard library link:https://docs.python.org/3/library/unittest.html[unittest] runner.
== Installation
[source,bash]
----
pip install asciidoctest
----
== Pytest Integration
AsciiDoctest automatically registers as a link:https://docs.pytest.org/[pytest] plugin. Simply execute `pytest` in your project directory:
[source,bash]
----
pytest
----
=== Configuration
You can configure collection behavior in your `pyproject.toml` or `pytest.ini`:
[source,ini]
----
[pytest]
asciidoctest_mode = eager
----
Alternatively, you can supply the `--asciidoctest-mode` flag:
[source,bash]
----
pytest --asciidoctest-mode=eager
----
== Unittest Integration
To use the standard library link:https://docs.python.org/3/library/unittest.html[unittest] package, load tests using `DocFileSuite` or `DocTestSuite`:
[source,python]
----
import unittest
from asciidoctest import DocFileSuite
def suite():
return DocFileSuite("README.adoc")
if __name__ == "__main__":
unittest.main(defaultTest="suite")
----
== Examples
Below are standard test blocks demonstrating interactive and script-based execution.
=== Interactive Session
We can run interactive Python sessions with expected outputs. We mark this block with `[source,python,shared]` to allow this interactive block's setup state (`x`) to be persisted for subsequent blocks:
[source,python,shared]
----
>>> x = "asciidoctest"
>>> x.upper()
'ASCIIDOCTEST'
----
=== Sequential Script Block
We can run script-based test blocks with standard Python assertions. Since they share the same continuous narrative namespace, variables defined in previous `[source,python,shared]` blocks are accessible. We mark this block with `[source,python,shared]`:
[source,python,shared]
----
assert x == "asciidoctest"
y = len(x)
assert y == 12
----
=== Directives Support
Standard `doctest` directives such as `ELLIPSIS` are supported. We mark this block with `[source,python,test]` so that it is independent and runs with a clean, isolated namespace:
[source,python,test]
----
>>> print(x)
Traceback (most recent call last):
...
NameError: name 'x' is not defined
>>> x = "Hello, beautiful world!"
>>> print(x)
Hello, ... world!
----
=== Ephemeral Copy of Shared State
We can grant a test block access to an ephemeral copy of the accumulated shared state at that point in the document. Any modifications made within the block are discarded afterwards. We mark this block with `[source,python,shared,test]`:
[source,python,shared,test]
----
>>> print(x)
asciidoctest
>>> x = "Hello, beautiful world!"
>>> print(x)
Hello, ... world!
----
To demonstrate that the changes in the `shared, test` block were indeed discarded and did not modify the persistent shared namespace, a subsequent `[source,python,shared]` block shows that `x` remains unchanged:
[source,python,shared]
----
>>> print(x)
asciidoctest
----
== Contributing
We welcome contributions to link:https://github.com/webmaven/asciidoctest[AsciiDoctest]! Please review our link:https://github.com/webmaven/asciidoctest/blob/main/SECURITY.adoc[Security Policy] before running untrusted code blocks during development.
AsciiDoctest maintains strict quality standards:
* Fully annotated link:https://www.python.org/[Python] types checked via link:https://mypy-lang.org/[mypy].
* Code formatting and linting verified with link:https://github.com/astral-sh/ruff[Ruff].
* Comprehensive unit and integration test coverage kept above **95%**.
To get started on development locally:
1. Clone the repository and set up the virtual environment:
+
[source,bash]
----
$ git clone https://github.com/webmaven/asciidoctest.git
$ cd asciidoctest
$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install -e ".[test,docs]"
----
2. Run the complete test suite:
+
[source,bash]
----
$ pytest
----
3. Check code coverage reports:
+
[source,bash]
----
$ coverage run -m pytest
$ coverage report -m
----
== License
Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at:
link:https://www.apache.org/licenses/LICENSE-2.0[https://www.apache.org/licenses/LICENSE-2.0]
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
asciidoctest-0.2.0a2.tar.gz
(23.2 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file asciidoctest-0.2.0a2.tar.gz.
File metadata
- Download URL: asciidoctest-0.2.0a2.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32473539561a57abb544062737b44f4b27e9267da449a7bf4fe54c322150161f
|
|
| MD5 |
0e97064dc9d40504a37183583aba4c89
|
|
| BLAKE2b-256 |
0320bba194acd38e85379b70183a781ba67214129179b9d55db6d84834192d23
|
File details
Details for the file asciidoctest-0.2.0a2-py3-none-any.whl.
File metadata
- Download URL: asciidoctest-0.2.0a2-py3-none-any.whl
- Upload date:
- Size: 16.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed11ec0298f6aa754b37a5dd3597cdc49b920b6162e4bacdd2ed6f10387de7f1
|
|
| MD5 |
a914b147ee9890542f9c71a7e4b6ed75
|
|
| BLAKE2b-256 |
7899464019205460ac7c64b8658599e1b6b93654838925e1199bf7cf3bd38f9f
|