Coverage for tests/test_readme.py: 100%
36 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-30 23:02 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-30 23:02 +0000
1"""Tests that every example in the README works as documented.
3Code blocks in the README show results as trailing comments (for example,
4`colors.get('purple') # 0`). This test runs each block and checks that every
5expression with such a comment evaluates to the value shown.
6"""
8from __future__ import annotations
10import ast
11import pathlib
12import re
13import sys
14import types
16README = pathlib.Path(__file__).parents[1] / 'README.md'
17_RESULT = re.compile(r'\s{2}# (?P<expected>.+)$')
20def test_readme_examples() -> None:
21 """Runs all of the python code blocks in the README."""
22 text = README.read_text(encoding = 'utf-8')
23 blocks = re.findall(r'```python\n(.*?)```', text, flags = re.DOTALL)
24 assert blocks
25 module = types.ModuleType('readme')
26 sys.modules['readme'] = module
27 namespace = module.__dict__
28 checked = 0
29 try:
30 checked = _run(blocks, namespace)
31 finally:
32 del sys.modules['readme']
33 assert checked > 20
36def _run(blocks: list[str], namespace: dict[str, object]) -> int:
37 """Runs code blocks and checks results shown in comments.
39 Args:
40 blocks: source code of the README code blocks.
41 namespace: namespace shared by all of the blocks.
43 Returns:
44 Number of results checked.
46 """
47 checked = 0
48 for block in blocks:
49 lines = block.splitlines()
50 for node in ast.parse(block).body:
51 if isinstance(node, ast.Expr):
52 match = _RESULT.search(lines[node.end_lineno - 1])
53 if match:
54 code = compile(ast.Expression(node.value), 'README', 'eval')
55 result = eval(code, namespace) # noqa: S307
56 assert repr(result) == match['expected']
57 checked += 1
58 continue
59 module = ast.Module(body = [node], type_ignores = [])
60 exec(compile(module, 'README', 'exec'), namespace) # noqa: S102
61 return checked