Coverage for pycommons/dev/tests/examples_in_dir.py: 100%
32 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-07 11:10 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-07 11:10 +0000
1"""Test all the example code in a directory."""
2from random import randint
3from typing import Final
5from pycommons.dev.tests.compile_and_run import compile_and_run
6from pycommons.io.console import logger
7from pycommons.io.path import Path, directory_path
8from pycommons.types import type_error
11def check_examples_in_dir(directory: str, recurse: bool = True) -> int:
12 """
13 Test all the examples in a directory.
15 :param directory: the directory
16 :param recurse: should we recurse into sub-directories (if any)?
17 :returns: the total number of examples executed
18 :raises TypeError: if `directory` is not a string or `recurse` is not a
19 Boolean
20 :raises ValueError: if executing the examples fails or if no examples were
21 found
23 >>> from contextlib import redirect_stdout
24 >>> from io import StringIO
25 >>> ex_dir = Path(__file__).up(4).resolve_inside("examples")
26 >>> with redirect_stdout(StringIO()):
27 ... check_examples_in_dir(ex_dir)
29 >>> from pycommons.io.temp import temp_dir
30 >>> with temp_dir() as td:
31 ... f = td.resolve_inside("test.py")
32 ... f.write_all_str(ex_dir.resolve_inside(
33 ... "compile_and_run_dummy.py").read_all_str())
34 ... print(check_examples_in_dir(td))
35 test.py
36 1
38 >>> try:
39 ... check_examples_in_dir(None, True)
40 ... except TypeError as te:
41 ... print(te)
42 descriptor '__len__' requires a 'str' object but received a 'NoneType'
44 >>> try:
45 ... check_examples_in_dir(1, True)
46 ... except TypeError as te:
47 ... print(te)
48 descriptor '__len__' requires a 'str' object but received a 'int'
50 >>> try:
51 ... check_examples_in_dir("x", None)
52 ... except TypeError as te:
53 ... print(te)
54 recurse should be an instance of bool but is None.
56 >>> try:
57 ... check_examples_in_dir("y", 1)
58 ... except TypeError as te:
59 ... print(te)
60 recurse should be an instance of bool but is int, namely 1.
62 >>> from pycommons.io.temp import temp_dir
63 >>> from contextlib import redirect_stdout
64 >>> with temp_dir() as td, redirect_stdout(None) as rd:
65 ... try:
66 ... check_examples_in_dir(td, True)
67 ... except ValueError as ve:
68 ... s = str(ve)
69 >>> print(s[:30])
70 No examples found in directory
72 >>> with temp_dir() as td, redirect_stdout(None) as rd:
73 ... pystring = "print('hello world!')"
74 ... td.resolve_inside("1.py").write_all_str(pystring)
75 ... td.resolve_inside("2.py").write_all_str(pystring)
76 ... tdx = td.resolve_inside("second")
77 ... tdx.ensure_dir_exists()
78 ... tdx.resolve_inside("1.py").write_all_str(pystring)
79 ... tdx.resolve_inside("2.py").write_all_str(pystring)
80 ... r1 = check_examples_in_dir(td, True)
81 ... r2 = check_examples_in_dir(td, False)
82 >>> print(r1)
83 4
84 >>> print(r2)
85 2
87 >>> with temp_dir() as td, redirect_stdout(None) as rd:
88 ... pystring = "print('hello world!')"
89 ... td.resolve_inside("1.py").write_all_str(pystring)
90 ... pyerrstring = "1 / 0"
91 ... td.resolve_inside("2.py").write_all_str(pyerrstring)
92 ... tdx = td.resolve_inside("second")
93 ... res = ""
94 ... try:
95 ... check_examples_in_dir(td, True)
96 ... except ValueError as ve:
97 ... res = str(ve)
98 >>> print(res[:20])
99 Error when executing
100 """
101 # First, we resolve the directories
102 if not isinstance(recurse, bool):
103 raise type_error(recurse, "recurse", bool)
104 examples_dir: Final[Path] = directory_path(directory)
105 logger(f"Executing all examples in directory {examples_dir!r}.")
107 files: Final[list[str]] = list(examples_dir.list_dir())
108 count: int = list.__len__(files)
109 total: int = 0
110 logger(f"Got {count} potential files.")
112 while count > 0:
113 choice: int = randint(0, count - 1) # noqa: S311
114 current: Path = examples_dir.resolve_inside(files[choice])
115 del files[choice]
116 count -= 1
118 if current.is_file():
119 if current.endswith(".py"): # if it is a python file
120 chars: str = current.read_all_str()
121 logger(f"Read {str.__len__(chars)} from file {files!r}.")
122 compile_and_run(chars, current)
123 total += 1
124 elif recurse and current.is_dir() and (
125 "pycache" not in current.lower()):
126 total += check_examples_in_dir(current, True)
128 if total <= 0:
129 raise ValueError(f"No examples found in directory {directory!r}!")
130 logger(f"Finished executing {total} examples in directory {directory!r}.")
131 return total