danielutils 1.1.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. danielutils/__init__.py +50 -0
  2. danielutils/abstractions/__init__.py +3 -0
  3. danielutils/abstractions/db/__init__.py +10 -0
  4. danielutils/abstractions/db/database.py +206 -0
  5. danielutils/abstractions/db/database_definitions.py +219 -0
  6. danielutils/abstractions/db/database_exceptions.py +34 -0
  7. danielutils/abstractions/db/database_factory.py +70 -0
  8. danielutils/abstractions/db/database_initializer.py +261 -0
  9. danielutils/abstractions/db/database_models.py +213 -0
  10. danielutils/abstractions/db/dependencies.py +6 -0
  11. danielutils/abstractions/db/implementations/__init__.py +4 -0
  12. danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
  13. danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
  14. danielutils/abstractions/db/implementations/redis_database.py +403 -0
  15. danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
  16. danielutils/abstractions/multiprogramming/__init__.py +3 -0
  17. danielutils/abstractions/multiprogramming/multi_id.py +26 -0
  18. danielutils/abstractions/multiprogramming/worker.py +104 -0
  19. danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
  20. danielutils/abstractions/repl.py +125 -0
  21. danielutils/aliases.py +174 -0
  22. danielutils/async_/__init__.py +7 -0
  23. danielutils/async_/async_cmd.py +28 -0
  24. danielutils/async_/async_command.py +879 -0
  25. danielutils/async_/async_layered_command.py +125 -0
  26. danielutils/async_/async_retry_executor.py +86 -0
  27. danielutils/async_/async_worker_pool.py +110 -0
  28. danielutils/async_/time_strategy.py +73 -0
  29. danielutils/async_/utils.py +57 -0
  30. danielutils/better_builtins/__init__.py +3 -0
  31. danielutils/better_builtins/counter.py +48 -0
  32. danielutils/better_builtins/frange.py +266 -0
  33. danielutils/better_builtins/typed_builtins/__init__.py +4 -0
  34. danielutils/better_builtins/typed_builtins/factory.py +126 -0
  35. danielutils/better_builtins/typed_builtins/tdict.py +36 -0
  36. danielutils/better_builtins/typed_builtins/tlist.py +223 -0
  37. danielutils/better_builtins/typed_builtins/tset.py +67 -0
  38. danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
  39. danielutils/colors.py +203 -0
  40. danielutils/context_managers/__init__.py +5 -0
  41. danielutils/context_managers/attr_context.py +40 -0
  42. danielutils/context_managers/multi_context.py +34 -0
  43. danielutils/context_managers/optional_context.py +33 -0
  44. danielutils/context_managers/state_context.py +25 -0
  45. danielutils/context_managers/temporary_file.py +108 -0
  46. danielutils/convenience.py +12 -0
  47. danielutils/conversions/__init__.py +2 -0
  48. danielutils/conversions/main_conversions.py +131 -0
  49. danielutils/conversions/specialized_conversions/__init__.py +2 -0
  50. danielutils/conversions/specialized_conversions/to_hex.py +25 -0
  51. danielutils/conversions/specialized_conversions/to_int.py +19 -0
  52. danielutils/custom_types.py +18 -0
  53. danielutils/data_structures/__init__.py +8 -0
  54. danielutils/data_structures/algorithms.py +52 -0
  55. danielutils/data_structures/comparer.py +47 -0
  56. danielutils/data_structures/default_dict.py +23 -0
  57. danielutils/data_structures/functions.py +35 -0
  58. danielutils/data_structures/graph/__init__.py +4 -0
  59. danielutils/data_structures/graph/binary_node.py +71 -0
  60. danielutils/data_structures/graph/graph.py +208 -0
  61. danielutils/data_structures/graph/multinode.py +101 -0
  62. danielutils/data_structures/graph/node.py +60 -0
  63. danielutils/data_structures/heap/__init__.py +3 -0
  64. danielutils/data_structures/heap/heap.py +119 -0
  65. danielutils/data_structures/heap/max_heap.py +18 -0
  66. danielutils/data_structures/heap/min_heap.py +18 -0
  67. danielutils/data_structures/queue/__init__.py +3 -0
  68. danielutils/data_structures/queue/atomic_queue.py +15 -0
  69. danielutils/data_structures/queue/priority_queue.py +110 -0
  70. danielutils/data_structures/queue/queue.py +92 -0
  71. danielutils/data_structures/stack.py +102 -0
  72. danielutils/data_structures/trees/__init__.py +2 -0
  73. danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
  74. danielutils/data_structures/trees/binary_tree.py +63 -0
  75. danielutils/date.py +43 -0
  76. danielutils/date_time.py +23 -0
  77. danielutils/decorators/__init__.py +18 -0
  78. danielutils/decorators/atomic.py +45 -0
  79. danielutils/decorators/attach.py +61 -0
  80. danielutils/decorators/chain_decorators.py +39 -0
  81. danielutils/decorators/decorate_conditionally.py +32 -0
  82. danielutils/decorators/delay_call.py +47 -0
  83. danielutils/decorators/deprecate.py +46 -0
  84. danielutils/decorators/final.py +39 -0
  85. danielutils/decorators/limit_recursion.py +63 -0
  86. danielutils/decorators/memo.py +68 -0
  87. danielutils/decorators/normalize_decorator.py +55 -0
  88. danielutils/decorators/overload.py +204 -0
  89. danielutils/decorators/partially_implemented.py +32 -0
  90. danielutils/decorators/processify.py +74 -0
  91. danielutils/decorators/property.py +42 -0
  92. danielutils/decorators/singleton.py +59 -0
  93. danielutils/decorators/threadify.py +40 -0
  94. danielutils/decorators/timeout.py +82 -0
  95. danielutils/decorators/total_ordering.py +82 -0
  96. danielutils/decorators/validate.py +227 -0
  97. danielutils/exceptions.py +90 -0
  98. danielutils/functions/__init__.py +13 -0
  99. danielutils/functions/areoneof.py +47 -0
  100. danielutils/functions/check_foreach.py +39 -0
  101. danielutils/functions/factorial.py +24 -0
  102. danielutils/functions/flatten.py +33 -0
  103. danielutils/functions/foreach.py +20 -0
  104. danielutils/functions/isoftype.py +475 -0
  105. danielutils/functions/isoneof.py +71 -0
  106. danielutils/functions/multiloop.py +51 -0
  107. danielutils/functions/parallel_for.py +47 -0
  108. danielutils/functions/partition.py +45 -0
  109. danielutils/functions/powerset.py +31 -0
  110. danielutils/functions/subseteq.py +25 -0
  111. danielutils/functions/types_subseteq.py +51 -0
  112. danielutils/generators/__init__.py +3 -0
  113. danielutils/generators/conditional_generator.py +59 -0
  114. danielutils/generators/generator_from_stream.py +31 -0
  115. danielutils/generators/join_generators.py +109 -0
  116. danielutils/internet.py +92 -0
  117. danielutils/io_.py +451 -0
  118. danielutils/java/__init__.py +18 -0
  119. danielutils/java/interfaces/__init__.py +1 -0
  120. danielutils/java/interfaces/comparable.py +31 -0
  121. danielutils/java/java_interface.py +59 -0
  122. danielutils/logging_/__init__.py +2 -0
  123. danielutils/logging_/_impl/__init__.py +4 -0
  124. danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
  125. danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
  126. danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
  127. danielutils/logging_/_impl/log_level.py +13 -0
  128. danielutils/logging_/_impl/logger.py +109 -0
  129. danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
  130. danielutils/logging_/utils.py +199 -0
  131. danielutils/lombok/__init__.py +1 -0
  132. danielutils/lombok/builder.py +68 -0
  133. danielutils/math_/__init__.py +2 -0
  134. danielutils/math_/constants.py +103 -0
  135. danielutils/math_/functions.py +34 -0
  136. danielutils/math_/math_print.py +52 -0
  137. danielutils/math_/math_symbols.py +125 -0
  138. danielutils/metaclasses/__init__.py +5 -0
  139. danielutils/metaclasses/atomic_class_meta.py +36 -0
  140. danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
  141. danielutils/metaclasses/instance_cache_meta.py +66 -0
  142. danielutils/metaclasses/interface.py +328 -0
  143. danielutils/metaclasses/overload_meta.py +48 -0
  144. danielutils/mock_/__init__.py +1 -0
  145. danielutils/mock_/mock_module.py +27 -0
  146. danielutils/path.py +55 -0
  147. danielutils/print_.py +123 -0
  148. danielutils/progress_bar/__init__.py +3 -0
  149. danielutils/progress_bar/ascii_progress_bar.py +161 -0
  150. danielutils/progress_bar/progress_bar.py +75 -0
  151. danielutils/progress_bar/progress_bar_pool.py +72 -0
  152. danielutils/protocols/__init__.py +3 -0
  153. danielutils/protocols/dictable.py +38 -0
  154. danielutils/protocols/evaluable.py +26 -0
  155. danielutils/protocols/serializable.py +51 -0
  156. danielutils/py.typed +0 -0
  157. danielutils/random_.py +47 -0
  158. danielutils/reflection/__init__.py +6 -0
  159. danielutils/reflection/declarations.py +288 -0
  160. danielutils/reflection/file/__init__.py +1 -0
  161. danielutils/reflection/file/file_reflection.py +85 -0
  162. danielutils/reflection/function/__init__.py +1 -0
  163. danielutils/reflection/function/function_reflections.py +152 -0
  164. danielutils/reflection/info_classes/__init__.py +6 -0
  165. danielutils/reflection/info_classes/argument_info.py +278 -0
  166. danielutils/reflection/info_classes/class_info.py +204 -0
  167. danielutils/reflection/info_classes/decorator_info.py +54 -0
  168. danielutils/reflection/info_classes/file_info.py +1180 -0
  169. danielutils/reflection/info_classes/folder_info.py +592 -0
  170. danielutils/reflection/info_classes/function_info.py +551 -0
  171. danielutils/reflection/info_classes/import_info.py +207 -0
  172. danielutils/reflection/interpreter/__init__.py +9 -0
  173. danielutils/reflection/interpreter/callstack.py +57 -0
  174. danielutils/reflection/interpreter/get_traceback.py +27 -0
  175. danielutils/reflection/interpreter/interpreter.py +41 -0
  176. danielutils/reflection/interpreter/is_debugging.py +13 -0
  177. danielutils/reflection/interpreter/os_.py +33 -0
  178. danielutils/reflection/interpreter/packages.py +33 -0
  179. danielutils/reflection/interpreter/python_version.py +33 -0
  180. danielutils/reflection/interpreter/signals.py +18 -0
  181. danielutils/reflection/interpreter/tracer.py +175 -0
  182. danielutils/reflection/module/__init__.py +3 -0
  183. danielutils/reflection/module/lazy_module.py +104 -0
  184. danielutils/reflection/module/module_reflections.py +34 -0
  185. danielutils/reflection/module/package_reflection.py +161 -0
  186. danielutils/retry_executor/__init__.py +3 -0
  187. danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
  188. danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
  189. danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
  190. danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
  191. danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
  192. danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
  193. danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
  194. danielutils/retry_executor/backoff_strategy.py +28 -0
  195. danielutils/retry_executor/retry_executor.py +55 -0
  196. danielutils/snippets/__init__.py +1 -0
  197. danielutils/snippets/try_get.py +28 -0
  198. danielutils/system/__init__.py +3 -0
  199. danielutils/system/independent.py +197 -0
  200. danielutils/system/layered_command.py +130 -0
  201. danielutils/system/windows/__init__.py +2 -0
  202. danielutils/system/windows/utils/__init__.py +1 -0
  203. danielutils/system/windows/utils/filetime.py +19 -0
  204. danielutils/system/windows/win32_ctime.py +183 -0
  205. danielutils/system/windows/windows.py +78 -0
  206. danielutils/testing/__init__.py +1 -0
  207. danielutils/testing/unittest_/__init__.py +2 -0
  208. danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
  209. danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
  210. danielutils/text.py +169 -0
  211. danielutils/time.py +64 -0
  212. danielutils/tools/__init__.py +1 -0
  213. danielutils/tools/unittest_test_runner/__init__.py +9 -0
  214. danielutils/tools/unittest_test_runner/cli.py +70 -0
  215. danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
  216. danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
  217. danielutils/tools/unittest_test_runner/core/execution.py +244 -0
  218. danielutils/tools/unittest_test_runner/core/parser.py +163 -0
  219. danielutils/tools/unittest_test_runner/core/runner.py +561 -0
  220. danielutils/tools/unittest_test_runner/models.py +82 -0
  221. danielutils/university/__init__.py +7 -0
  222. danielutils/university/computability_and_complexity/__init__.py +2 -0
  223. danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
  224. danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
  225. danielutils/university/computability_and_complexity/languages/language.py +17 -0
  226. danielutils/university/computability_and_complexity/languages/sat.py +71 -0
  227. danielutils/university/computability_and_complexity/turing_machine.py +47 -0
  228. danielutils/university/databases/__init__.py +1 -0
  229. danielutils/university/databases/all.py +756 -0
  230. danielutils/university/image_proccesing/__init__.py +1 -0
  231. danielutils/university/image_proccesing/encoding/__init__.py +3 -0
  232. danielutils/university/image_proccesing/encoding/encoding.py +35 -0
  233. danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
  234. danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
  235. danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
  236. danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
  237. danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
  238. danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
  239. danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
  240. danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
  241. danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
  242. danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
  243. danielutils/university/image_proccesing/tansformations/hough.py +0 -0
  244. danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
  245. danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
  246. danielutils/university/linear_algebra/__init__.py +1 -0
  247. danielutils/university/linear_algebra/matrix.py +404 -0
  248. danielutils/university/machine_learning/__init__.py +0 -0
  249. danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
  250. danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
  251. danielutils/university/machine_learning/activation_functions/relu.py +11 -0
  252. danielutils/university/machine_learning/neuron.py +15 -0
  253. danielutils/university/oop/__init__.py +1 -0
  254. danielutils/university/oop/observer.py +31 -0
  255. danielutils/university/oop/strategy.py +9 -0
  256. danielutils/university/probability/__init__.py +7 -0
  257. danielutils/university/probability/conditional_variable/__init__.py +3 -0
  258. danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
  259. danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
  260. danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
  261. danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
  262. danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
  263. danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
  264. danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
  265. danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
  266. danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
  267. danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
  268. danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
  269. danielutils/university/probability/distributions.py +35 -0
  270. danielutils/university/probability/expressions/__init__.py +2 -0
  271. danielutils/university/probability/expressions/accumulation_expression.py +291 -0
  272. danielutils/university/probability/expressions/probability_expression.py +110 -0
  273. danielutils/university/probability/funcs/__init__.py +4 -0
  274. danielutils/university/probability/funcs/covariance.py +12 -0
  275. danielutils/university/probability/funcs/expected_value.py +41 -0
  276. danielutils/university/probability/funcs/probability_function.py +14 -0
  277. danielutils/university/probability/funcs/variance.py +17 -0
  278. danielutils/university/probability/operator.py +79 -0
  279. danielutils/university/probability/protocols.py +32 -0
  280. danielutils/university/probability/supp.py +101 -0
  281. danielutils/university/probability/transformation.py +44 -0
  282. danielutils/versioned_imports.py +31 -0
  283. danielutils-1.1.28.dist-info/METADATA +390 -0
  284. danielutils-1.1.28.dist-info/RECORD +287 -0
  285. danielutils-1.1.28.dist-info/WHEEL +5 -0
  286. danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
  287. danielutils-1.1.28.dist-info/top_level.txt +1 -0
@@ -0,0 +1,561 @@
1
+ """
2
+ Main UnittestRunner orchestration functionality.
3
+ """
4
+
5
+ import logging
6
+ import os
7
+ import sys
8
+ import time
9
+ import json
10
+ import subprocess
11
+ from typing import List, Optional, Literal, Tuple
12
+ from ..models import UnittestFunctionState, ModuleState, UnittestResult, UnittestRunSummary, UnittestDiscovery
13
+ from .discovery import UnittestDiscoveryService
14
+ from .execution import UnittestExecutor
15
+ from .parser import UnittestOutputParser
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ VerboseLevel = Literal["module", "file", "class", "function"]
20
+
21
+
22
+ class UnittestRunner:
23
+ """Smart test runner that executes tests module by module and collects detailed statistics."""
24
+
25
+ def __init__(self,
26
+ python_path: Optional[str] = None,
27
+ results_file: str = "test_results.json",
28
+ skip_threshold_hours: int = 24,
29
+ force_run: bool = False,
30
+ test_name: Optional[str] = None,
31
+ target: Optional[str] = None,
32
+ verbose: VerboseLevel = "class",
33
+ show_function_results: bool = False):
34
+ logger.info("Initializing UnittestRunner with python_path=%s, results_file=%s, skip_threshold_hours=%d, force_run=%s, target=%s, verbose=%s",
35
+ python_path, results_file, skip_threshold_hours, force_run, target, verbose)
36
+
37
+ self.python_path = python_path or sys.executable
38
+ self.results_file = results_file
39
+ self.skip_threshold_hours = skip_threshold_hours
40
+ self.force_run = force_run
41
+ self.test_name = test_name
42
+ self.target = target
43
+ self.verbose = verbose
44
+ self.show_function_results = show_function_results
45
+
46
+ # Ensure we have a valid Python executable
47
+ if not self.python_path or not os.path.exists(self.python_path):
48
+ logger.warning("Invalid Python path provided, using sys.executable: %s", sys.executable)
49
+ self.python_path = sys.executable
50
+
51
+ # Validate verbose level (this should be caught by type checker, but good to have runtime check)
52
+ valid_levels: List[VerboseLevel] = ["module", "file", "class", "function"]
53
+ if self.verbose not in valid_levels:
54
+ logger.error("Invalid verbose level '%s'. Must be one of: %s", self.verbose, valid_levels)
55
+ raise ValueError(f"Invalid verbose level '{self.verbose}'. Must be one of: {valid_levels}")
56
+
57
+ # Debug output (only in function level)
58
+ if self.verbose == "function":
59
+ logger.debug("Using Python executable: %s", self.python_path)
60
+ print(f"DEBUG: Using Python executable: {self.python_path}")
61
+
62
+ # Initialize services
63
+ logger.debug("Initializing discovery service and parser")
64
+ self.discovery_service = UnittestDiscoveryService(self.python_path, self.verbose)
65
+ self.parser = UnittestOutputParser(self.verbose)
66
+
67
+ # Initialize state
68
+ self.results: List[UnittestResult] = []
69
+ self.previous_results: Optional[UnittestRunSummary] = None
70
+ logger.debug("Loading previous results from: %s", self.results_file)
71
+ self._load_previous_results()
72
+
73
+ # Discover test modules based on target
74
+ logger.info("Starting test discovery phase")
75
+ if self.verbose in ["module", "file", "class", "function"]:
76
+ print("🔍 Test Discovery Phase")
77
+ print("=" * 50)
78
+
79
+ if self.target:
80
+ # If target is specified, discover modules relative to target
81
+ logger.info("Target specified, discovering modules for target: %s", self.target)
82
+ if self.verbose in ["module", "file", "class", "function"]:
83
+ print(f"🎯 Target specified: {self.target}")
84
+ self.test_modules = self.discovery_service.discover_test_modules(target=self.target)
85
+ else:
86
+ # No target, discover all modules in default tests directory
87
+ logger.info("No target specified, discovering all test modules")
88
+ if self.verbose in ["module", "file", "class", "function"]:
89
+ print("📁 No target specified, discovering all test modules")
90
+ self.test_modules = self.discovery_service.discover_test_modules()
91
+
92
+ logger.info("Discovered %d test modules", len(self.test_modules))
93
+ self.test_discovery: Optional[UnittestDiscovery] = None
94
+ self._discover_test_structure()
95
+
96
+ # Apply target filtering if specified
97
+ if self.target and self.test_discovery:
98
+ logger.debug("Applying target filtering for: %s", self.target)
99
+ target_module, target_class, target_function = self.discovery_service.parse_target(self.target)
100
+ if target_module:
101
+ self.test_discovery = self.discovery_service.create_focused_discovery(
102
+ target_module, target_class, target_function, self.test_discovery
103
+ )
104
+ logger.debug("Focused discovery created for target: %s (module: %s, class: %s, function: %s)",
105
+ self.target, target_module, target_class, target_function)
106
+ if self.verbose == "function":
107
+ print(f"DEBUG: Focused discovery created for target: {self.target}")
108
+ print(f"DEBUG: Target components - module: {target_module}, class: {target_class}, function: {target_function}")
109
+
110
+ # Initialize executor
111
+ logger.debug("Initializing test executor")
112
+ self.executor = UnittestExecutor(self.python_path, self.verbose, self.test_discovery)
113
+
114
+ def _discover_test_structure(self):
115
+ """Discover the complete test structure for progress tracking."""
116
+ logger.debug("Discovering test structure for %d modules", len(self.test_modules))
117
+ self.test_discovery = self.discovery_service.discover_test_structure(self.test_modules)
118
+ if self.test_discovery:
119
+ logger.info("Test structure discovery complete: %d modules, %d classes, %d functions",
120
+ self.test_discovery.total_modules, self.test_discovery.total_classes, self.test_discovery.total_functions)
121
+
122
+ def _load_previous_results(self):
123
+ """Load previous test results from JSON file."""
124
+ if not os.path.exists(self.results_file):
125
+ logger.info("No previous results file found: %s", self.results_file)
126
+ print(f"No previous results file found: {self.results_file}")
127
+ return
128
+
129
+ try:
130
+ logger.debug("Loading previous results from: %s", self.results_file)
131
+ with open(self.results_file, 'r') as f:
132
+ data = json.load(f)
133
+
134
+ # Convert results list from dicts to UnittestResult objects
135
+ if 'results' in data and isinstance(data['results'], list):
136
+ results = []
137
+ logger.debug("Converting %d previous results from dicts to objects", len(data['results']))
138
+ for result_dict in data['results']:
139
+ # Convert nested dicts to proper objects
140
+ if 'module_state' in result_dict:
141
+ module_state_dict = result_dict['module_state']
142
+ # Convert test_functions dict to UnittestFunctionState objects
143
+ test_functions = {}
144
+ if 'test_functions' in module_state_dict:
145
+ for func_name, func_dict in module_state_dict['test_functions'].items():
146
+ test_functions[func_name] = UnittestFunctionState(**func_dict)
147
+
148
+ # Create ModuleState objects
149
+ initial_state = UnittestFunctionState(**module_state_dict['initial_state']) if module_state_dict.get('initial_state') else None
150
+ current_state = UnittestFunctionState(**module_state_dict['current_state']) if module_state_dict.get('current_state') else None
151
+
152
+ module_state = ModuleState(
153
+ initial_state=initial_state,
154
+ current_state=current_state,
155
+ test_functions=test_functions
156
+ )
157
+
158
+ result = UnittestResult(
159
+ module_path=result_dict['module_path'],
160
+ module_state=module_state,
161
+ errors=result_dict.get('errors', []),
162
+ warnings=result_dict.get('warnings', [])
163
+ )
164
+ results.append(result)
165
+
166
+ data['results'] = results
167
+
168
+ self.previous_results = UnittestRunSummary(**data)
169
+ logger.info("Successfully loaded previous results: %d modules, %d tests",
170
+ self.previous_results.total_modules, self.previous_results.total_tests)
171
+ except Exception as e:
172
+ logger.error("Error loading previous results: %s", e)
173
+ print(f"Error loading previous results: {e}")
174
+ self.previous_results = None
175
+
176
+ def _should_skip_module(self, module_path: str) -> Tuple[bool, str]:
177
+ """Determine if a module should be skipped based on previous results."""
178
+ logger.debug("Checking if module should be skipped: %s", module_path)
179
+
180
+ if self.force_run:
181
+ logger.debug("Force run enabled, not skipping module: %s", module_path)
182
+ return False, "Force run enabled"
183
+
184
+ if not self.previous_results:
185
+ logger.debug("No previous results available, not skipping module: %s", module_path)
186
+ return False, "No previous results"
187
+
188
+ # Find previous result for this module
189
+ for result in self.previous_results.results:
190
+ if result.module_path == module_path:
191
+ current_state = result.current_state
192
+
193
+ # Check if all tests passed
194
+ if current_state.failed == 0 and current_state.errors == 0:
195
+ # Check if recent enough
196
+ try:
197
+ from datetime import datetime
198
+ result_time = datetime.strptime(current_state.timestamp, '%Y-%m-%d %H:%M:%S')
199
+ current_time = datetime.now()
200
+ hours_ago = (current_time - result_time).total_seconds() / 3600
201
+
202
+ if hours_ago < self.skip_threshold_hours:
203
+ logger.debug("Skipping module %s: all tests passed %.1f hours ago", module_path, hours_ago)
204
+ return True, f"All tests passed {hours_ago:.1f}h ago"
205
+ else:
206
+ logger.debug("Not skipping module %s: tests passed %.1f hours ago (threshold: %d)",
207
+ module_path, hours_ago, self.skip_threshold_hours)
208
+ except Exception as e:
209
+ logger.debug("Error parsing timestamp for module %s: %s", module_path, e)
210
+ pass
211
+ else:
212
+ logger.debug("Not skipping module %s: has failures or errors", module_path)
213
+
214
+ break
215
+
216
+ logger.debug("Module %s needs attention", module_path)
217
+ return False, "Module needs attention"
218
+
219
+ def _create_module_state(self, module_path: str, test_functions: List[UnittestFunctionState],
220
+ total_runtime: float) -> ModuleState:
221
+ """Create a ModuleState from test function results."""
222
+ if not test_functions:
223
+ return ModuleState(
224
+ module_path=module_path,
225
+ test_functions=[],
226
+ total_tests=0,
227
+ passed=0,
228
+ failed=0,
229
+ skipped=0,
230
+ errors=0,
231
+ total_runtime=total_runtime,
232
+ average_runtime=0.0,
233
+ slowest_test="",
234
+ fastest_test="",
235
+ success_rate=0.0,
236
+ timestamp=time.strftime('%Y-%m-%d %H:%M:%S'),
237
+ should_skip_module=False,
238
+ skip_reason="",
239
+ improvement_from_initial=False
240
+ )
241
+
242
+ # Calculate statistics
243
+ total_tests = len(test_functions)
244
+ passed = sum(1 for t in test_functions if t.status == "passed")
245
+ failed = sum(1 for t in test_functions if t.status == "failed")
246
+ skipped = sum(1 for t in test_functions if t.status == "skipped")
247
+ errors = sum(1 for t in test_functions if t.status == "error")
248
+
249
+ # Calculate runtimes
250
+ runtimes = [t.runtime for t in test_functions if t.runtime > 0]
251
+ average_runtime = sum(runtimes) / len(runtimes) if runtimes else 0.0
252
+
253
+ # Find slowest and fastest tests
254
+ if runtimes:
255
+ slowest_idx = max(range(len(runtimes)), key=lambda i: runtimes[i])
256
+ fastest_idx = min(range(len(runtimes)), key=lambda i: runtimes[i])
257
+ slowest_test = test_functions[slowest_idx].function_name
258
+ fastest_test = test_functions[fastest_idx].function_name
259
+ else:
260
+ slowest_test = ""
261
+ fastest_test = ""
262
+
263
+ success_rate = (passed / total_tests * 100) if total_tests > 0 else 0.0
264
+
265
+ return ModuleState(
266
+ module_path=module_path,
267
+ test_functions=test_functions,
268
+ total_tests=total_tests,
269
+ passed=passed,
270
+ failed=failed,
271
+ skipped=skipped,
272
+ errors=errors,
273
+ total_runtime=total_runtime,
274
+ average_runtime=average_runtime,
275
+ slowest_test=slowest_test,
276
+ fastest_test=fastest_test,
277
+ success_rate=success_rate,
278
+ timestamp=time.strftime('%Y-%m-%d %H:%M:%S'),
279
+ should_skip_module=False,
280
+ skip_reason="",
281
+ improvement_from_initial=False
282
+ )
283
+
284
+ def run_all_tests(self):
285
+ """Run all tests with smart skipping and detailed reporting."""
286
+ logger.info("Starting test execution for %d modules", len(self.test_modules))
287
+ print("Loaded previous results from test_results.json")
288
+ print(f"Found {len(self.test_modules)} test modules to evaluate")
289
+ print("=" * 80)
290
+ print()
291
+
292
+ # Determine which modules to run
293
+ modules_to_run = []
294
+ modules_to_skip = []
295
+
296
+ logger.debug("Evaluating %d modules for skipping", len(self.test_modules))
297
+ for module_path in self.test_modules:
298
+ should_skip, reason = self._should_skip_module(module_path)
299
+ if should_skip:
300
+ modules_to_skip.append((module_path, reason))
301
+ else:
302
+ modules_to_run.append(module_path)
303
+
304
+ logger.info("Module evaluation complete: %d to run, %d to skip", len(modules_to_run), len(modules_to_skip))
305
+
306
+ # Print skipping summary
307
+ if modules_to_skip:
308
+ print(f"SKIPPING {len(modules_to_skip)} modules that are in good shape:")
309
+ for module_path, reason in modules_to_skip:
310
+ print(f" → {module_path}: {reason}")
311
+ print()
312
+
313
+ # Print running summary
314
+ if modules_to_run:
315
+ print(f"RUNNING {len(modules_to_run)} modules that need attention:")
316
+ for module_path in modules_to_run:
317
+ should_skip, reason = self._should_skip_module(module_path)
318
+ print(f" → {module_path}: {reason}")
319
+ print()
320
+ print("=" * 80)
321
+ print()
322
+
323
+ # Run the modules
324
+ logger.info("Starting execution of %d modules", len(modules_to_run))
325
+ for i, module_path in enumerate(modules_to_run, 1):
326
+ logger.info("Running module %d/%d: %s", i, len(modules_to_run), module_path)
327
+ if self.verbose in ["module", "file", "class", "function"]:
328
+ print(f"[{i}/{len(modules_to_run)}] Testing module: {module_path}")
329
+ print("-" * 60)
330
+
331
+ result = self._run_test_module(module_path)
332
+ self.results.append(result)
333
+
334
+ # Print module summary based on verbose level
335
+ state = result.current_state
336
+ logger.info("Module %s completed: %d passed, %d failed, %d errors, %d skipped",
337
+ module_path, state.passed, state.failed, state.errors, state.skipped)
338
+
339
+ if self.verbose == "module":
340
+ # Minimal output for module level
341
+ status = "✓" if state.failed == 0 and state.errors == 0 else "✗"
342
+ print(f"{status} {module_path}: {state.passed} passed, {state.failed} failed, {state.errors} errors")
343
+ else:
344
+ # Detailed output for file/class/function levels
345
+ print(f"Results: {state.passed} passed, {state.failed} failed, {state.skipped} skipped, {state.errors} errors")
346
+ print(f"Runtime: {state.total_runtime:.3f}s total, {state.average_runtime:.3f}s average")
347
+
348
+ if result.errors:
349
+ print(f"Errors: {len(result.errors)} issues found")
350
+
351
+ if result.overall_improvement:
352
+ print("✓ Module improved from previous run!")
353
+
354
+ print()
355
+
356
+ # Print final summary
357
+ logger.info("All test execution completed")
358
+ self._print_final_summary()
359
+
360
+ def _run_test_module(self, module_path: str) -> UnittestResult:
361
+ """Run a single test module and collect detailed statistics."""
362
+ logger.debug("Running test module: %s", module_path)
363
+ if self.verbose in ["module", "file", "class", "function"]:
364
+ print(f"Running tests for module: {module_path}")
365
+
366
+ # Get initial state from previous results if available
367
+ initial_state: Optional[ModuleState] = None
368
+ if self.previous_results:
369
+ for result in self.previous_results.results:
370
+ if result.module_path == module_path:
371
+ initial_state = result.current_state
372
+ logger.debug("Found previous state for module %s: %d passed, %d failed",
373
+ module_path, initial_state.passed, initial_state.failed)
374
+ break
375
+
376
+ # Run the test and collect results
377
+ start_time = time.time()
378
+ errors: List[str] = []
379
+ warnings: List[str] = []
380
+
381
+ try:
382
+ logger.debug("Executing test module %s with verbose level: %s", module_path, self.verbose)
383
+ # Use hierarchical approach based on verbose level
384
+ if self.verbose == "function":
385
+ # Run individual test functions (most verbose)
386
+ test_functions = self.executor.run_test_file(module_path)
387
+ elif self.verbose == "class":
388
+ # Run test classes (default)
389
+ test_functions = self.executor.run_test_file(module_path)
390
+ elif self.verbose == "file":
391
+ # Run test file
392
+ test_functions = self.executor.run_test_file(module_path)
393
+ else: # module level
394
+ # Run entire module at once (least verbose)
395
+ cmd = [self.python_path, "-m", "unittest", module_path, "-v"]
396
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
397
+
398
+ # Parse the output to extract test statistics
399
+ output_lines = result.stdout.split('\n')
400
+ stderr_lines = result.stderr.split('\n')
401
+
402
+ # If stdout is empty but stderr has test output, use stderr
403
+ if not any("test_" in line for line in output_lines) and any("test_" in line for line in stderr_lines):
404
+ output_lines = stderr_lines
405
+ stderr_lines = []
406
+
407
+ # Parse individual test functions
408
+ test_functions, parse_errors, parse_warnings = self.parser.parse_test_output(output_lines, stderr_lines)
409
+ errors.extend(parse_errors)
410
+ warnings.extend(parse_warnings)
411
+
412
+ total_runtime = time.time() - start_time
413
+
414
+ # Show individual function results if requested
415
+ if self.show_function_results and test_functions:
416
+ print(f" Individual test results for {module_path}:")
417
+ for test_func in test_functions:
418
+ status_symbol = "✓" if test_func.status == "passed" else "✗" if test_func.status == "failed" else "⚠" if test_func.status == "error" else "⏭" if test_func.status == "skipped" else "?"
419
+ print(f" {status_symbol} {test_func.function_name} ({test_func.status}) - {test_func.runtime:.3f}s")
420
+
421
+ # Create current state
422
+ current_state = self._create_module_state(module_path, test_functions, total_runtime)
423
+
424
+ # Determine if module improved
425
+ improvement_from_initial = False
426
+ if initial_state:
427
+ improvement_from_initial = (
428
+ current_state.passed > initial_state.passed or
429
+ current_state.failed < initial_state.failed or
430
+ current_state.errors < initial_state.errors
431
+ )
432
+
433
+ return UnittestResult(
434
+ module_path=module_path,
435
+ initial_state=initial_state,
436
+ current_state=current_state,
437
+ errors=errors,
438
+ warnings=warnings,
439
+ overall_improvement=improvement_from_initial
440
+ )
441
+
442
+ except subprocess.TimeoutExpired:
443
+ error_msg = f"Test module {module_path} timed out after 5 minutes"
444
+ logger.error("Test module %s timed out after 5 minutes", module_path)
445
+ errors.append(error_msg)
446
+ print(f"ERROR: {error_msg}")
447
+
448
+ # Create error state
449
+ current_state = self._create_module_state(module_path, [], time.time() - start_time)
450
+ return UnittestResult(
451
+ module_path=module_path,
452
+ initial_state=initial_state,
453
+ current_state=current_state,
454
+ errors=errors,
455
+ warnings=warnings,
456
+ overall_improvement=False
457
+ )
458
+ except Exception as e:
459
+ error_msg = f"Error running test module {module_path}: {e}"
460
+ logger.error("Error running test module %s: %s", module_path, e)
461
+ errors.append(error_msg)
462
+ print(f"ERROR: {error_msg}")
463
+
464
+ # Create error state
465
+ current_state = self._create_module_state(module_path, [], time.time() - start_time)
466
+ return UnittestResult(
467
+ module_path=module_path,
468
+ initial_state=initial_state,
469
+ current_state=current_state,
470
+ errors=errors,
471
+ warnings=warnings,
472
+ overall_improvement=False
473
+ )
474
+
475
+ def _print_final_summary(self):
476
+ """Print final summary of test run."""
477
+ if not self.results:
478
+ logger.warning("No test results to summarize")
479
+ print("No test results to summarize.")
480
+ return
481
+
482
+ total_modules = len(self.results)
483
+ modules_run = len([r for r in self.results if r.current_state.total_tests > 0])
484
+ modules_skipped = total_modules - modules_run
485
+
486
+ total_tests = sum(r.current_state.total_tests for r in self.results)
487
+ total_passed = sum(r.current_state.passed for r in self.results)
488
+ total_failed = sum(r.current_state.failed for r in self.results)
489
+ total_skipped = sum(r.current_state.skipped for r in self.results)
490
+ total_errors = sum(r.current_state.errors for r in self.results)
491
+
492
+ success_rate = (total_passed / total_tests * 100) if total_tests > 0 else 0.0
493
+ total_execution_time = sum(r.current_state.total_runtime for r in self.results)
494
+
495
+ logger.info("Final test summary: %d modules, %d tests, %d passed, %d failed, %d errors, %.1f%% success rate",
496
+ total_modules, total_tests, total_passed, total_failed, total_errors, success_rate)
497
+
498
+ print("=" * 80)
499
+ print("All tests completed!")
500
+ print(f"Results saved to JSON: {self.results_file}")
501
+ print()
502
+ print("FINAL SUMMARY:")
503
+ print(f"Total modules evaluated: {total_modules}")
504
+ print(f"Modules run: {modules_run}")
505
+ print(f"Modules skipped: {modules_skipped}")
506
+ print(f"Total tests: {total_tests}")
507
+ print(f"Passed: {total_passed}")
508
+ print(f"Failed: {total_failed}")
509
+ print(f"Errors: {total_errors}")
510
+ print(f"Success rate: {success_rate:.1f}%")
511
+ print(f"Results saved to {self.results_file}")
512
+
513
+ def save_results_json(self):
514
+ """Save test results to JSON file."""
515
+ if not self.results:
516
+ logger.warning("No results to save")
517
+ print("No results to save.")
518
+ return
519
+
520
+ logger.info("Saving %d test results to JSON file: %s", len(self.results), self.results_file)
521
+
522
+ # Calculate summary statistics
523
+ total_modules = len(self.results)
524
+ modules_run = len([r for r in self.results if r.current_state.total_tests > 0])
525
+ modules_skipped = total_modules - modules_run
526
+
527
+ total_tests = sum(r.current_state.total_tests for r in self.results)
528
+ total_passed = sum(r.current_state.passed for r in self.results)
529
+ total_failed = sum(r.current_state.failed for r in self.results)
530
+ total_skipped = sum(r.current_state.skipped for r in self.results)
531
+ total_errors = sum(r.current_state.errors for r in self.results)
532
+
533
+ success_rate = (total_passed / total_tests * 100) if total_tests > 0 else 0.0
534
+ total_execution_time = sum(r.current_state.total_runtime for r in self.results)
535
+
536
+ # Create summary
537
+ summary = UnittestRunSummary(
538
+ timestamp=time.strftime('%Y-%m-%d %H:%M:%S'),
539
+ python_executable=self.python_path,
540
+ total_modules=total_modules,
541
+ modules_run=modules_run,
542
+ modules_skipped=modules_skipped,
543
+ total_tests=total_tests,
544
+ total_passed=total_passed,
545
+ total_failed=total_failed,
546
+ total_skipped=total_skipped,
547
+ total_errors=total_errors,
548
+ success_rate=success_rate,
549
+ total_execution_time=total_execution_time,
550
+ results=self.results
551
+ )
552
+
553
+ # Save to JSON
554
+ try:
555
+ with open(self.results_file, 'w') as f:
556
+ json.dump(summary.__dict__, f, indent=2, default=str)
557
+ logger.info("Successfully saved results to: %s", self.results_file)
558
+ print(f"Results saved to {self.results_file}")
559
+ except Exception as e:
560
+ logger.error("Error saving results to %s: %s", self.results_file, e)
561
+ print(f"Error saving results: {e}")
@@ -0,0 +1,82 @@
1
+ """
2
+ Data models for the test runner.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import List, Optional, Dict
7
+ import time
8
+
9
+
10
+ @dataclass
11
+ class UnittestFunctionState:
12
+ """State of an individual test function."""
13
+ function_name: str
14
+ status: str # "passed", "failed", "skipped", "error", "unknown"
15
+ runtime: float
16
+ timestamp: str
17
+ error_message: str = ""
18
+ warning_count: int = 0
19
+ should_skip: bool = False
20
+ skip_reason: str = ""
21
+ improvement_from_initial: bool = False
22
+
23
+
24
+ @dataclass
25
+ class ModuleState:
26
+ """State of a test module containing multiple test functions."""
27
+ module_path: str
28
+ test_functions: List[UnittestFunctionState]
29
+ total_tests: int
30
+ passed: int
31
+ failed: int
32
+ skipped: int
33
+ errors: int
34
+ total_runtime: float
35
+ average_runtime: float
36
+ slowest_test: str
37
+ fastest_test: str
38
+ success_rate: float
39
+ timestamp: str
40
+ should_skip_module: bool = False
41
+ skip_reason: str = ""
42
+ improvement_from_initial: bool = False
43
+
44
+
45
+ @dataclass
46
+ class UnittestResult:
47
+ """Result of running a test module with initial and current states."""
48
+ module_path: str
49
+ initial_state: Optional[ModuleState]
50
+ current_state: ModuleState
51
+ errors: List[str]
52
+ warnings: List[str]
53
+ overall_improvement: bool = False
54
+
55
+
56
+ @dataclass
57
+ class UnittestRunSummary:
58
+ """Summary of a complete test run across all modules."""
59
+ timestamp: str
60
+ python_executable: str
61
+ total_modules: int
62
+ modules_run: int
63
+ modules_skipped: int
64
+ total_tests: int
65
+ total_passed: int
66
+ total_failed: int
67
+ total_skipped: int
68
+ total_errors: int
69
+ success_rate: float
70
+ total_execution_time: float
71
+ results: List[UnittestResult]
72
+
73
+
74
+ @dataclass
75
+ class UnittestDiscovery:
76
+ """Represents discovered test structure for progress tracking."""
77
+ modules: List[str]
78
+ classes: Dict[str, List[str]] # module -> list of classes
79
+ functions: Dict[str, List[str]] # class -> list of functions
80
+ total_functions: int
81
+ total_classes: int
82
+ total_modules: int
@@ -0,0 +1,7 @@
1
+ from .databases import *
2
+ from .probability import *
3
+ from .oop import *
4
+ from .image_proccesing import *
5
+ from .linear_algebra import *
6
+ from .machine_learning import *
7
+ from .computability_and_complexity import *
@@ -0,0 +1,2 @@
1
+ from .discreate_finite_automaton import *
2
+ from .turing_machine import *