Tutorial 8 — REPL / doctest mode¶
The Python interactive shell format (>>> prompts, expected output on the
following lines) is widely recognised by developers. markdown-pytest can
run blocks written in this format using Python’s built-in doctest module.
Prerequisites¶
Completed Tutorial 7 — Subprocess mode. No additional packages are needed for sync REPL tests.
Step 1 — Write a REPL block¶
Add repl: true to the comment. Write the block as if you were typing
into a Python shell:
<!-- name: test_greet; repl: true -->
```python
>>> def greet(name):
... return f"Hello, {name}!"
>>> greet("world")
'Hello, world!'
>>> print(greet("pytest"))
Hello, pytest!
```
Rules:
Lines starting with
>>>are executed.Lines starting with
...are continuation lines (inside blocks or multi-line expressions).Lines without a prompt that immediately follow
>>>or...lines are the expected output. The test fails if the actual output differs.Blank lines between
>>>statements are fine — they do not end the block.
Run:
pytest guide.md::test_greet -v
guide.md::test_greet PASSED
Step 2 — Return values vs print output¶
repr() of a return value is shown automatically (just like in a real
shell). print() output appears as plain text without quotes.
<!-- name: test_output_types; repl: true -->
```python
>>> [1, 2, 3]
[1, 2, 3]
>>> print("hello")
hello
>>> 42
42
>>> x = 10
>>> x
10
```
When a statement produces no return value (assignment, import, for
loops), nothing is expected on the next line.
Step 3 — Standard doctest directives¶
All # doctest: +DIRECTIVE flags work:
<!-- name: test_ellipsis; repl: true -->
```python
>>> [1, 2, 3, 4, 5] # doctest: +ELLIPSIS
[1, 2, ...]
>>> "hello world" # doctest: +NORMALIZE_WHITESPACE
'hello world'
```
Useful directives:
Directive |
What it does |
|---|---|
|
|
|
Any run of whitespace matches any other |
|
Skip this specific example |
|
Match exception type but not message |
Step 4 — Expecting exceptions¶
Write the exception type and message as it would appear in the shell:
<!-- name: test_exception; repl: true -->
```python
>>> 1 / 0
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
```
The Traceback (most recent call last): header and ... on the next line
are required by the doctest format. The module path in the exception
message is optional — doctest matches only the last line by default.
Step 5 — Split REPL blocks¶
Multiple blocks with the same name share a session — variables defined in an earlier block are available in later ones:
<!-- name: test_session; repl: true -->
```python
>>> items = [1, 2, 3]
>>> len(items)
3
```
Add an item and verify:
<!-- name: test_session; repl: true -->
```python
>>> items.append(4)
>>> items
[1, 2, 3, 4]
```
Both blocks carry repl: true. The session (namespace) is preserved
across them — items is available in the second block.
Step 6 — Async REPL¶
Prefix the name with async to enable top-level await inside REPL
blocks:
<!-- name: async test_async_repl; repl: true -->
```python
>>> import asyncio
>>> async def fetch():
... await asyncio.sleep(0)
... return 42
>>> await fetch()
42
```
Requires an async pytest plugin (same as regular async tests — see Tutorial 6 — Async tests).
State is preserved across split async REPL blocks within the same event loop session.
When to use REPL mode vs regular mode¶
Situation |
Use |
|---|---|
Checking printed output inline |
|
Showing interactive exploration |
|
Running a sequence of statements |
regular |
Using fixtures |
regular |
Using subtests |
regular |
REPL mode cannot use fixtures or case: subtests. If you need those
features, use a regular block with explicit assert statements.
Common mistakes¶
Missing ... continuation lines
Multi-line >>> statements require ... on continuation lines:
>>> def f(x):
... return x + 1 # correct
>>> def f(x):
return x + 1 # SyntaxError — missing ...
Expected output on wrong line
The expected output must appear on the line immediately after the
>>> or ... block. A blank line ends the expected output and starts a
new statement.
repl: true on only one block of a split pair
All blocks in a split REPL test must carry repl: true. If one block
lacks it, that block is treated as a regular test block instead of a
doctest block.
What you learned¶
Add
repl: trueto run a block as a Python interactive-shell session.>>>lines are code; following lines without a prompt are expected output.All standard
# doctest: +DIRECTIVEflags are supported.Split REPL blocks share a session across the same name.
Prefix the name with
asyncfor top-levelawaitin REPL blocks.
You have now completed all eight tutorials. For task-specific guidance, move on to the How-to guides.