|
| 1 | +import sys |
1 | 2 | from importlib import import_module |
2 | 3 | from inspect import isfunction, signature |
3 | 4 | from pkgutil import walk_packages |
|
7 | 8 |
|
8 | 9 | # Module-level constants |
9 | 10 | _STYLE_SUCCESS = "\033[92m" |
| 11 | +_STYLE_FAILURE = "\033[91m" |
10 | 12 | _STYLE_BOLD = "\033[1m" |
11 | 13 | _STYLE_END = "\033[0m" |
12 | 14 | _RUNNER_PROGRESS = "->" |
13 | 15 | _RUNNER_MAIN = "main" |
14 | 16 |
|
15 | 17 |
|
16 | | -def success_text(text: str) -> str: |
17 | | - """Get success text.""" |
18 | | - return f"{_STYLE_SUCCESS}{bold_text(text)}{_STYLE_END}" |
| 18 | +def style_text(text: str, color: str = "") -> str: |
| 19 | + """Get styled text.""" |
| 20 | + return f"{color}{_STYLE_BOLD}{text}{_STYLE_END}" |
19 | 21 |
|
20 | 22 |
|
21 | | -def bold_text(text: str) -> str: |
22 | | - """Get bold text.""" |
23 | | - return f"{_STYLE_BOLD}{text}{_STYLE_END}" |
| 23 | +def main() -> None: |
| 24 | + # Get filter from command line arguments |
| 25 | + filter_str = sys.argv[1] if len(sys.argv) > 1 else None |
24 | 26 |
|
| 27 | + print(style_text(f"Start {root_name} runner")) |
25 | 28 |
|
26 | | -def main() -> None: |
27 | | - print(bold_text(f"Start {root_name} runner")) |
| 29 | + stats = {"passed": 0, "failed": 0, "skipped": 0} |
28 | 30 |
|
29 | 31 | for item in walk_packages(root_path, f"{root_name}."): |
| 32 | + # Skip packages (folders), only run modules (files) |
| 33 | + if item.ispkg: |
| 34 | + continue |
| 35 | + |
| 36 | + # Filter modules based on command line argument |
| 37 | + if filter_str and filter_str not in item.name: |
| 38 | + continue |
| 39 | + |
30 | 40 | try: |
31 | 41 | mod = import_module(item.name) |
32 | 42 | except (ImportError, SyntaxError) as e: |
33 | 43 | print(f"{_RUNNER_PROGRESS} Skip {item.name}: {e}") |
| 44 | + stats["skipped"] += 1 |
34 | 45 | continue |
35 | 46 |
|
36 | | - # Skip modules without a main object |
37 | | - if not hasattr(mod, _RUNNER_MAIN): |
| 47 | + # Skip modules without a valid main object |
| 48 | + mod_main = getattr(mod, _RUNNER_MAIN, None) |
| 49 | + if not isfunction(mod_main) or len(signature(mod_main).parameters) != 0: |
| 50 | + print(f"{_RUNNER_PROGRESS} Skip {item.name}: No valid {_RUNNER_MAIN}() function") |
| 51 | + stats["skipped"] += 1 |
38 | 52 | continue |
39 | 53 |
|
40 | | - # By this point, there is a main object in the module |
41 | | - mod_main = getattr(mod, _RUNNER_MAIN) |
42 | | - |
43 | | - # The main object is a function |
44 | | - assert isfunction(mod_main) |
45 | | - |
46 | | - # The main function has zero parameters |
47 | | - assert len(signature(mod_main).parameters) == 0 |
48 | | - |
49 | | - # The main function should not throw any errors |
| 54 | + # Execution phase |
50 | 55 | print(f"{_RUNNER_PROGRESS} Run {mod.__name__}:{_RUNNER_MAIN}", end="") |
51 | | - mod_main() |
52 | | - print(" [PASS]") |
53 | | - |
54 | | - print(success_text(f"Finish {root_name} runner")) |
| 56 | + try: |
| 57 | + mod_main() |
| 58 | + print(style_text(" [PASS]", _STYLE_SUCCESS)) |
| 59 | + stats["passed"] += 1 |
| 60 | + except Exception as e: |
| 61 | + print(style_text(" [FAIL]", _STYLE_FAILURE)) |
| 62 | + print(f" Error in {item.name}: {e}") |
| 63 | + stats["failed"] += 1 |
| 64 | + |
| 65 | + # Summary report |
| 66 | + print("\n" + "=" * 30) |
| 67 | + print(style_text(f"Finish {root_name} runner", _STYLE_SUCCESS)) |
| 68 | + print(f"Passed: {stats['passed']} | Failed: {stats['failed']} | Skipped: {stats['skipped']}") |
| 69 | + print("=" * 30) |
55 | 70 |
|
56 | 71 |
|
57 | 72 | if __name__ == "__main__": |
|
0 commit comments