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,551 @@
1
+ import inspect
2
+ import re
3
+ import ast
4
+ import logging
5
+ import types
6
+ from dataclasses import dataclass
7
+ from typing import Type, List, Callable, Optional, Tuple, Dict, Any, Union
8
+ from .decorator_info import DecoratorInfo
9
+ from .argument_info import ArgumentInfo
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ @dataclass
15
+ class FunctionComplexityStats:
16
+ """Statistics about function complexity and structure."""
17
+ max_indentation_level: int
18
+ cyclomatic_complexity: int
19
+ nesting_depth: int
20
+ has_nested_functions: bool
21
+ has_nested_classes: bool
22
+ has_lambda_expressions: bool
23
+ has_list_comprehensions: bool
24
+ has_dict_comprehensions: bool
25
+ has_set_comprehensions: bool
26
+ has_generator_expressions: bool
27
+
28
+ def to_dict(self) -> Dict[str, Any]:
29
+ """Convert to dictionary for JSON serialization."""
30
+ return {
31
+ "max_indentation_level": self.max_indentation_level,
32
+ "cyclomatic_complexity": self.cyclomatic_complexity,
33
+ "nesting_depth": self.nesting_depth,
34
+ "has_nested_functions": self.has_nested_functions,
35
+ "has_nested_classes": self.has_nested_classes,
36
+ "has_lambda_expressions": self.has_lambda_expressions,
37
+ "has_list_comprehensions": self.has_list_comprehensions,
38
+ "has_dict_comprehensions": self.has_dict_comprehensions,
39
+ "has_set_comprehensions": self.has_set_comprehensions,
40
+ "has_generator_expressions": self.has_generator_expressions
41
+ }
42
+
43
+
44
+ @dataclass
45
+ class FunctionTypeStats:
46
+ """Statistics about function typing."""
47
+ is_fully_typed: bool
48
+ has_return_type: bool
49
+ has_argument_types: bool
50
+ typed_arguments_count: int
51
+ total_arguments_count: int
52
+ typing_score: float # 0.0 to 1.0
53
+ missing_type_arguments: List[str]
54
+ has_generic_types: bool
55
+ has_union_types: bool
56
+ has_optional_types: bool
57
+
58
+ def to_dict(self) -> Dict[str, Any]:
59
+ """Convert to dictionary for JSON serialization."""
60
+ return {
61
+ "is_fully_typed": self.is_fully_typed,
62
+ "has_return_type": self.has_return_type,
63
+ "has_argument_types": self.has_argument_types,
64
+ "typed_arguments_count": self.typed_arguments_count,
65
+ "total_arguments_count": self.total_arguments_count,
66
+ "typing_score": round(self.typing_score, 2),
67
+ "missing_type_arguments": self.missing_type_arguments,
68
+ "has_generic_types": self.has_generic_types,
69
+ "has_union_types": self.has_union_types,
70
+ "has_optional_types": self.has_optional_types
71
+ }
72
+
73
+
74
+ @dataclass
75
+ class FunctionCodeStats:
76
+ """Statistics about function code structure."""
77
+ total_lines: int
78
+ code_lines: int
79
+ comment_lines: int
80
+ empty_lines: int
81
+ docstring_lines: int
82
+ average_line_length: float
83
+ max_line_length: int
84
+ min_line_length: int
85
+ has_docstring: bool
86
+ docstring_length: int
87
+ function_signature_line: int
88
+
89
+ def to_dict(self) -> Dict[str, Any]:
90
+ """Convert to dictionary for JSON serialization."""
91
+ return {
92
+ "total_lines": self.total_lines,
93
+ "code_lines": self.code_lines,
94
+ "comment_lines": self.comment_lines,
95
+ "empty_lines": self.empty_lines,
96
+ "docstring_lines": self.docstring_lines,
97
+ "average_line_length": round(self.average_line_length, 2),
98
+ "max_line_length": self.max_line_length,
99
+ "min_line_length": self.min_line_length,
100
+ "has_docstring": self.has_docstring,
101
+ "docstring_length": self.docstring_length,
102
+ "function_signature_line": self.function_signature_line
103
+ }
104
+
105
+
106
+ @dataclass
107
+ class FunctionStats:
108
+ """Comprehensive function statistics."""
109
+ complexity: FunctionComplexityStats
110
+ typing: FunctionTypeStats
111
+ code: FunctionCodeStats
112
+ overall_score: float # 0.0 to 1.0
113
+ quality_assessment: str # "excellent", "good", "fair", "poor"
114
+
115
+ def to_dict(self) -> Dict[str, Any]:
116
+ """Convert to dictionary for JSON serialization."""
117
+ return {
118
+ "complexity": self.complexity.to_dict(),
119
+ "typing": self.typing.to_dict(),
120
+ "code": self.code.to_dict(),
121
+ "overall_score": round(self.overall_score, 2),
122
+ "quality_assessment": self.quality_assessment
123
+ }
124
+
125
+
126
+ class FunctionInfo:
127
+ FUNCTION_DEFINITION_REGEX: re.Pattern = re.compile(
128
+ r"(?P<decorators>(?:@[\s\S]+?)+?)?\s*(?P<async>async )?def (?P<name>\w[\w\d]*)\s*\((?P<arguments>[\s\S]+?)?\)\s*(?:\s*\-\>\s*(?P<return_type>[\s\S]+?)\s*)?:(?P<body>[\s\S]+)",
129
+ re.MULTILINE)
130
+
131
+ def __init__(
132
+ self,
133
+ func: Callable,
134
+ owner: Optional[Union[Type, types.ModuleType]] = None,
135
+ ) -> None:
136
+ func = inspect.unwrap(func)
137
+
138
+ # Check for lambda functions
139
+ if getattr(func, '__name__', None) == '<lambda>':
140
+ raise TypeError(
141
+ f"'{func.__name__}' is not a user defined function")
142
+
143
+ self._func = func
144
+ self._decorators: List[DecoratorInfo] = []
145
+ self._arguments: List[ArgumentInfo] = []
146
+ self._return_type: str = ""
147
+ self._owner = owner
148
+ self._source_code: str = ""
149
+ self._ast_tree: Optional[ast.AST] = None
150
+ self._is_async = False
151
+ self._is_property = False
152
+ self._name = getattr(func, "__name__", "<unknown>")
153
+
154
+ if getattr(func, '__isabstractmethod__', False):
155
+ try:
156
+ if inspect.isdatadescriptor(func):
157
+ inspect.getsource(func.fget) # type: ignore
158
+ self._is_property = True
159
+ else:
160
+ inspect.getsource(func)
161
+ self._parse_src_code()
162
+ except (OSError, TypeError):
163
+ pass
164
+ return
165
+
166
+ try:
167
+ if inspect.isdatadescriptor(func):
168
+ inspect.getsource(func.fget) # type: ignore
169
+ self._is_property = True
170
+ else:
171
+ inspect.getsource(func)
172
+ self._is_property = False # type: ignore
173
+ except (OSError, TypeError):
174
+ raise TypeError(
175
+ f"'{getattr(func, '__name__', type(func).__name__)}' is not a user defined function")
176
+ self._parse_src_code()
177
+
178
+ def _parse_src_code(self) -> None:
179
+ f = self._func if not self.is_property else self._func.fget # type:ignore
180
+ self._source_code = inspect.getsource(f).strip()
181
+ m = FunctionInfo.FUNCTION_DEFINITION_REGEX.match(self._source_code)
182
+ if m is None:
183
+ raise ValueError("Invalid function source code")
184
+ decorators, async_, name, arguments, return_type, body = m.groups()
185
+ if decorators is not None:
186
+ for substr in decorators.strip().splitlines():
187
+ try:
188
+ self._decorators.append(
189
+ DecoratorInfo.from_str(substr.strip()))
190
+ except ValueError as e:
191
+ raise ValueError(
192
+ f"Failed to parse decorator for function '{name}': {e}\n"
193
+ f"Decorator string: {repr(substr.strip())}"
194
+ ) from e
195
+
196
+ self._is_async = async_ is not None
197
+
198
+ self._name = name
199
+ if arguments is not None:
200
+ try:
201
+ all_args = ArgumentInfo.from_str(arguments)
202
+ except ValueError as e:
203
+ raise ValueError(
204
+ f"Failed to parse arguments for function '{name}': {e}\n"
205
+ f"Arguments string: {repr(arguments)}"
206
+ ) from e
207
+ # Filter out separator arguments (/, *) - they are syntax markers, not actual arguments
208
+ self._arguments = [arg for arg in all_args if not (
209
+ arg.is_kwargs_only and arg.name == "/") and not (arg.is_args and arg.name is None)]
210
+
211
+ self._return_type = "None"
212
+ if return_type is not None:
213
+ self._return_type = return_type
214
+
215
+ # Parse AST for detailed analysis
216
+ try:
217
+ self._ast_tree = ast.parse(self._source_code)
218
+ except SyntaxError:
219
+ self._ast_tree = None
220
+
221
+ def _analyze_complexity(self) -> FunctionComplexityStats:
222
+ """Analyze function complexity metrics."""
223
+ if not self._ast_tree:
224
+ return FunctionComplexityStats(
225
+ max_indentation_level=0,
226
+ cyclomatic_complexity=1,
227
+ nesting_depth=0,
228
+ has_nested_functions=False,
229
+ has_nested_classes=False,
230
+ has_lambda_expressions=False,
231
+ has_list_comprehensions=False,
232
+ has_dict_comprehensions=False,
233
+ has_set_comprehensions=False,
234
+ has_generator_expressions=False
235
+ )
236
+
237
+ max_indent = 0
238
+ nesting_depth = 0
239
+ cyclomatic = 1 # Base complexity
240
+ has_nested_funcs = False
241
+ has_nested_classes = False
242
+ has_lambdas = False
243
+ has_list_comp = False
244
+ has_dict_comp = False
245
+ has_set_comp = False
246
+ has_gen_exp = False
247
+
248
+ for node in ast.walk(self._ast_tree):
249
+ # Calculate indentation level
250
+ if hasattr(node, 'lineno') and hasattr(node, 'col_offset'):
251
+ # Estimate indentation from column offset
252
+ indent_level = node.col_offset // 4 # Assuming 4 spaces per indent
253
+ max_indent = max(max_indent, indent_level)
254
+
255
+ # Count complexity factors
256
+ if isinstance(node, (ast.If, ast.While, ast.For, ast.AsyncFor, ast.ExceptHandler)):
257
+ cyclomatic += 1
258
+ nesting_depth = max(nesting_depth, 1)
259
+ elif isinstance(node, ast.BoolOp):
260
+ cyclomatic += len(node.values) - 1
261
+ elif isinstance(node, ast.FunctionDef):
262
+ has_nested_funcs = True
263
+ nesting_depth = max(nesting_depth, 2)
264
+ elif isinstance(node, ast.ClassDef):
265
+ has_nested_classes = True
266
+ nesting_depth = max(nesting_depth, 2)
267
+ elif isinstance(node, ast.Lambda):
268
+ has_lambdas = True
269
+ elif isinstance(node, ast.ListComp):
270
+ has_list_comp = True
271
+ elif isinstance(node, ast.DictComp):
272
+ has_dict_comp = True
273
+ elif isinstance(node, ast.SetComp):
274
+ has_set_comp = True
275
+ elif isinstance(node, ast.GeneratorExp):
276
+ has_gen_exp = True
277
+
278
+ return FunctionComplexityStats(
279
+ max_indentation_level=max_indent,
280
+ cyclomatic_complexity=cyclomatic,
281
+ nesting_depth=nesting_depth,
282
+ has_nested_functions=has_nested_funcs,
283
+ has_nested_classes=has_nested_classes,
284
+ has_lambda_expressions=has_lambdas,
285
+ has_list_comprehensions=has_list_comp,
286
+ has_dict_comprehensions=has_dict_comp,
287
+ has_set_comprehensions=has_set_comp,
288
+ has_generator_expressions=has_gen_exp
289
+ )
290
+
291
+ def _analyze_typing(self) -> FunctionTypeStats:
292
+ """Analyze function typing statistics."""
293
+ typed_args = sum(1 for arg in self._arguments if arg.type is not None)
294
+ total_args = len(self._arguments)
295
+
296
+ has_return = self._return_type != "None"
297
+ has_arg_types = typed_args > 0
298
+ is_fully_typed = has_return and has_arg_types and typed_args == total_args
299
+
300
+ missing_types = [
301
+ arg.name for arg in self._arguments if arg.type is None and arg.name]
302
+
303
+ # Calculate typing score (0.0 to 1.0)
304
+ typing_score = 0.0
305
+ if total_args > 0:
306
+ typing_score += (typed_args / total_args) * \
307
+ 0.6 # 60% weight for arguments
308
+ if has_return:
309
+ typing_score += 0.4 # 40% weight for return type
310
+
311
+ # Check for advanced typing features
312
+ has_generics = any('[' in str(
313
+ arg.type) for arg in self._arguments if arg.type) or '[' in self._return_type
314
+ has_unions = any('Union' in str(arg.type) or '|' in str(
315
+ arg.type) for arg in self._arguments if arg.type) or 'Union' in self._return_type or '|' in self._return_type
316
+ has_optionals = any('Optional' in str(
317
+ arg.type) for arg in self._arguments if arg.type) or 'Optional' in self._return_type
318
+
319
+ return FunctionTypeStats(
320
+ is_fully_typed=is_fully_typed,
321
+ has_return_type=has_return,
322
+ has_argument_types=has_arg_types,
323
+ typed_arguments_count=typed_args,
324
+ total_arguments_count=total_args,
325
+ typing_score=typing_score,
326
+ missing_type_arguments=missing_types,
327
+ has_generic_types=has_generics,
328
+ has_union_types=has_unions,
329
+ has_optional_types=has_optionals
330
+ )
331
+
332
+ def _analyze_code_structure(self) -> FunctionCodeStats:
333
+ """Analyze function code structure statistics."""
334
+ lines = self._source_code.splitlines()
335
+ total_lines = len(lines)
336
+
337
+ # Find function signature line
338
+ signature_line = 0
339
+ for i, line in enumerate(lines):
340
+ if line.strip().startswith('def ') or line.strip().startswith('async def '):
341
+ signature_line = i + 1
342
+ break
343
+
344
+ # Analyze lines
345
+ code_lines = 0
346
+ comment_lines = 0
347
+ empty_lines = 0
348
+ docstring_lines = 0
349
+ line_lengths = []
350
+
351
+ in_docstring = False
352
+ docstring_delimiter = None
353
+
354
+ for line in lines:
355
+ stripped = line.strip()
356
+ line_lengths.append(len(line))
357
+
358
+ if not stripped:
359
+ empty_lines += 1
360
+ continue
361
+
362
+ # Check for docstring start/end
363
+ if '"""' in line or "'''" in line:
364
+ if not in_docstring:
365
+ in_docstring = True
366
+ docstring_delimiter = '"""' if '"""' in line else "'''"
367
+ docstring_lines += 1
368
+ elif docstring_delimiter is not None and docstring_delimiter in line:
369
+ in_docstring = False
370
+ docstring_delimiter = None
371
+ else:
372
+ docstring_lines += 1
373
+ continue
374
+ elif in_docstring:
375
+ docstring_lines += 1
376
+ continue
377
+
378
+ # Check for comments
379
+ if stripped.startswith('#'):
380
+ comment_lines += 1
381
+ else:
382
+ code_lines += 1
383
+
384
+ has_docstring = docstring_lines > 0
385
+ avg_line_length = sum(line_lengths) / \
386
+ len(line_lengths) if line_lengths else 0
387
+ max_line_length = max(line_lengths) if line_lengths else 0
388
+ min_line_length = min(line_lengths) if line_lengths else 0
389
+
390
+ return FunctionCodeStats(
391
+ total_lines=total_lines,
392
+ code_lines=code_lines,
393
+ comment_lines=comment_lines,
394
+ empty_lines=empty_lines,
395
+ docstring_lines=docstring_lines,
396
+ average_line_length=avg_line_length,
397
+ max_line_length=max_line_length,
398
+ min_line_length=min_line_length,
399
+ has_docstring=has_docstring,
400
+ docstring_length=docstring_lines,
401
+ function_signature_line=signature_line
402
+ )
403
+
404
+ def _calculate_overall_score(self, complexity: FunctionComplexityStats,
405
+ typing: FunctionTypeStats,
406
+ code: FunctionCodeStats) -> Tuple[float, str]:
407
+ """Calculate overall function quality score."""
408
+ score = 0.0
409
+
410
+ # Complexity score (30% weight)
411
+ if complexity.cyclomatic_complexity <= 5:
412
+ score += 0.3
413
+ elif complexity.cyclomatic_complexity <= 10:
414
+ score += 0.2
415
+ elif complexity.cyclomatic_complexity <= 20:
416
+ score += 0.1
417
+
418
+ if complexity.max_indentation_level <= 3:
419
+ score += 0.1
420
+ elif complexity.max_indentation_level <= 5:
421
+ score += 0.05
422
+
423
+ # Typing score (30% weight)
424
+ score += typing.typing_score * 0.3
425
+
426
+ # Code structure score (40% weight)
427
+ if code.has_docstring:
428
+ score += 0.1
429
+
430
+ if code.total_lines <= 50:
431
+ score += 0.1
432
+ elif code.total_lines <= 100:
433
+ score += 0.05
434
+
435
+ if code.average_line_length <= 80:
436
+ score += 0.1
437
+ elif code.average_line_length <= 120:
438
+ score += 0.05
439
+
440
+ if code.empty_lines > 0: # Some whitespace is good
441
+ score += 0.05
442
+
443
+ if code.comment_lines > 0:
444
+ score += 0.05
445
+
446
+ # Determine quality level
447
+ if score >= 0.8:
448
+ quality = "excellent"
449
+ elif score >= 0.6:
450
+ quality = "good"
451
+ elif score >= 0.4:
452
+ quality = "fair"
453
+ else:
454
+ quality = "poor"
455
+
456
+ return score, quality
457
+
458
+ @property
459
+ def stats(self) -> FunctionStats:
460
+ """Returns comprehensive function statistics."""
461
+ # TODO: FunctionInfo.stats property is not yet ready for production use
462
+ # This functionality is experimental and may not provide accurate results
463
+ # The complexity analysis, typing analysis, and code structure analysis
464
+ # need further testing and refinement before being considered stable
465
+ logger.warning(
466
+ "FunctionInfo.stats property is experimental and not yet ready for production use")
467
+
468
+ complexity = self._analyze_complexity()
469
+ typing = self._analyze_typing()
470
+ code = self._analyze_code_structure()
471
+ overall_score, quality = self._calculate_overall_score(
472
+ complexity, typing, code)
473
+
474
+ return FunctionStats(
475
+ complexity=complexity,
476
+ typing=typing,
477
+ code=code,
478
+ overall_score=overall_score,
479
+ quality_assessment=quality
480
+ )
481
+
482
+ @property
483
+ def source_code(self) -> str:
484
+ """Returns the function's source code."""
485
+ return self._source_code
486
+
487
+ def __str__(self) -> str:
488
+ # body = json.dumps({
489
+ # "name": self.name,
490
+ # "decorators": self.decorators,
491
+ # "arguments": self.arguments
492
+ # }, default=str, indent=4)
493
+ return f"{self.__class__.__name__}(name=\"{self.name}\", decorators={self.decorators}, arguments={self.arguments})"
494
+
495
+ def __repr__(self) -> str:
496
+ return f"{self.__class__.__name__}(name=\"{self.name}\")"
497
+
498
+ @property
499
+ def is_async(self) -> bool:
500
+ return self._is_async
501
+
502
+ @property
503
+ def is_inherited(self) -> bool:
504
+ if self._owner is None:
505
+ return False
506
+ return self._func in set(self._owner.__dict__.keys())
507
+
508
+ @property
509
+ def is_class_method(self) -> bool:
510
+ return "classmethod" in set(d.name for d in self.decorators)
511
+
512
+ @property
513
+ def is_static_method(self) -> bool:
514
+ return "staticmethod" in set(d.name for d in self.decorators)
515
+
516
+ @property
517
+ def is_instance_method(self) -> bool:
518
+ return not self.is_class_method and not self.is_static_method
519
+
520
+ @property
521
+ def is_abstract(self) -> bool:
522
+ return getattr(self._func, '__isabstractmethod__', False)
523
+
524
+ @property
525
+ def is_property(self) -> bool:
526
+ return self._is_property
527
+
528
+ @property
529
+ def name(self) -> str:
530
+ return self._name
531
+
532
+ @property
533
+ def return_type(self) -> str:
534
+ return self._return_type
535
+
536
+ @property
537
+ def arguments(self) -> List[ArgumentInfo]:
538
+ return self._arguments
539
+
540
+ @property
541
+ def decorators(self) -> List[DecoratorInfo]:
542
+ return self._decorators
543
+
544
+
545
+ __all__ = [
546
+ "FunctionInfo",
547
+ "FunctionStats",
548
+ "FunctionComplexityStats",
549
+ "FunctionTypeStats",
550
+ "FunctionCodeStats",
551
+ ]