Coverage for pycommons / dev / tests / examples_in_dir.py: 100%
32 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 03:04 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 03:04 +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 >>> try:
24 ... check_examples_in_dir(None, True)
25 ... except TypeError as te:
26 ... print(te)
27 descriptor '__len__' requires a 'str' object but received a 'NoneType'
29 >>> try:
30 ... check_examples_in_dir(1, True)
31 ... except TypeError as te:
32 ... print(te)
33 descriptor '__len__' requires a 'str' object but received a 'int'
35 >>> try:
36 ... check_examples_in_dir("x", None)
37 ... except TypeError as te:
38 ... print(te)
39 recurse should be an instance of bool but is None.
41 >>> try:
42 ... check_examples_in_dir("y", 1)
43 ... except TypeError as te:
44 ... print(te)
45 recurse should be an instance of bool but is int, namely 1.
47 >>> from pycommons.io.temp import temp_dir
48 >>> from contextlib import redirect_stdout
49 >>> with temp_dir() as td, redirect_stdout(None) as rd:
50 ... try:
51 ... check_examples_in_dir(td, True)
52 ... except ValueError as ve:
53 ... s = str(ve)
54 >>> print(s[:30])
55 No examples found in directory
57 >>> with temp_dir() as td, redirect_stdout(None) as rd:
58 ... pystring = "print('hello world!')"
59 ... td.resolve_inside("1.py").write_all_str(pystring)
60 ... td.resolve_inside("2.py").write_all_str(pystring)
61 ... tdx = td.resolve_inside("second")
62 ... tdx.ensure_dir_exists()
63 ... tdx.resolve_inside("1.py").write_all_str(pystring)
64 ... tdx.resolve_inside("2.py").write_all_str(pystring)
65 ... r1 = check_examples_in_dir(td, True)
66 ... r2 = check_examples_in_dir(td, False)
67 >>> print(r1)
68 4
69 >>> print(r2)
70 2
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 ... pyerrstring = "1 / 0"
76 ... td.resolve_inside("2.py").write_all_str(pyerrstring)
77 ... tdx = td.resolve_inside("second")
78 ... res = ""
79 ... try:
80 ... check_examples_in_dir(td, True)
81 ... except ValueError as ve:
82 ... res = str(ve)
83 >>> print(res[:20])
84 Error when executing
85 """
86 # First, we resolve the directories
87 if not isinstance(recurse, bool):
88 raise type_error(recurse, "recurse", bool)
89 examples_dir: Final[Path] = directory_path(directory)
90 logger(f"Executing all examples in directory {examples_dir!r}.")
92 files: Final[list[str]] = list(examples_dir.list_dir())
93 count: int = list.__len__(files)
94 total: int = 0
95 logger(f"Got {count} potential files.")
97 while count > 0:
98 choice: int = randint(0, count - 1) # noqa: S311
99 current: Path = examples_dir.resolve_inside(files[choice])
100 del files[choice]
101 count -= 1
103 if current.is_file():
104 if current.endswith(".py"): # if it is a python file
105 chars: str = current.read_all_str()
106 logger(f"Read {str.__len__(chars)} from file {files!r}.")
107 compile_and_run(chars, current)
108 total += 1
109 elif recurse and current.is_dir() and (
110 "pycache" not in current.lower()):
111 total += check_examples_in_dir(current, True)
113 if total <= 0:
114 raise ValueError(f"No examples found in directory {directory!r}!")
115 logger(f"Finished executing {total} examples in directory {directory!r}.")
116 return total