danielutils 1.0.45__tar.gz → 1.0.46__tar.gz

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 (284) hide show
  1. {danielutils-1.0.45/danielutils.egg-info → danielutils-1.0.46}/PKG-INFO +5 -4
  2. {danielutils-1.0.45 → danielutils-1.0.46}/README.md +2 -2
  3. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/__init__.py +0 -1
  4. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/__init__.py +1 -1
  5. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/database.py +41 -19
  6. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/database_exceptions.py +7 -0
  7. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/database_factory.py +10 -10
  8. danielutils-1.0.46/danielutils/abstractions/db/database_initializer.py +256 -0
  9. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/dependencies.py +0 -1
  10. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/implementations/in_memory_database.py +24 -25
  11. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/implementations/persistent_in_memory_database.py +15 -17
  12. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/implementations/redis_database.py +62 -72
  13. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/implementations/sqlite_database.py +32 -31
  14. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/async_layered_command.py +1 -1
  15. danielutils-1.0.46/danielutils/mock_/__init__.py +1 -0
  16. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/testing/unittest_/always_teardown_testcase.py +4 -4
  17. {danielutils-1.0.45 → danielutils-1.0.46/danielutils.egg-info}/PKG-INFO +5 -4
  18. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils.egg-info/SOURCES.txt +3 -6
  19. {danielutils-1.0.45 → danielutils-1.0.46}/pyproject.toml +1 -1
  20. danielutils-1.0.45/danielutils/abstractions/database/__init__.py +0 -2
  21. danielutils-1.0.45/danielutils/abstractions/database/cached_database.py +0 -75
  22. danielutils-1.0.45/danielutils/abstractions/database/database.py +0 -104
  23. danielutils-1.0.45/danielutils/abstractions/database/redis_database.py +0 -46
  24. danielutils-1.0.45/danielutils/abstractions/db/initializer.py +0 -260
  25. danielutils-1.0.45/danielutils/mock_/__init__.py +0 -2
  26. danielutils-1.0.45/danielutils/mock_/mock_database.py +0 -72
  27. {danielutils-1.0.45 → danielutils-1.0.46}/LICENSE +0 -0
  28. {danielutils-1.0.45 → danielutils-1.0.46}/MANIFEST.in +0 -0
  29. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/__init__.py +0 -0
  30. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/database_definitions.py +0 -0
  31. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/database_models.py +0 -0
  32. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/db/implementations/__init__.py +0 -0
  33. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/multiprogramming/__init__.py +0 -0
  34. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/multiprogramming/multi_id.py +0 -0
  35. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/multiprogramming/worker.py +0 -0
  36. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/multiprogramming/worker_pool.py +0 -0
  37. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/abstractions/repl.py +0 -0
  38. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/aliases.py +0 -0
  39. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/__init__.py +0 -0
  40. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/async_cmd.py +0 -0
  41. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/async_retry_executor.py +0 -0
  42. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/async_worker_pool.py +0 -0
  43. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/time_strategy.py +0 -0
  44. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/async_/utils.py +0 -0
  45. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/__init__.py +0 -0
  46. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/counter.py +0 -0
  47. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/frange.py +0 -0
  48. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/typed_builtins/__init__.py +0 -0
  49. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/typed_builtins/factory.py +0 -0
  50. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/typed_builtins/tdict.py +0 -0
  51. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/typed_builtins/tlist.py +0 -0
  52. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/typed_builtins/tset.py +0 -0
  53. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/better_builtins/typed_builtins/ttuple.py +0 -0
  54. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/colors.py +0 -0
  55. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/context_managers/__init__.py +0 -0
  56. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/context_managers/attr_context.py +0 -0
  57. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/context_managers/multi_context.py +0 -0
  58. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/context_managers/optional_context.py +0 -0
  59. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/context_managers/state_context.py +0 -0
  60. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/context_managers/temporary_file.py +0 -0
  61. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/convenience.py +0 -0
  62. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/conversions/__init__.py +0 -0
  63. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/conversions/main_conversions.py +0 -0
  64. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/conversions/specialized_conversions/__init__.py +0 -0
  65. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/conversions/specialized_conversions/to_hex.py +0 -0
  66. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/conversions/specialized_conversions/to_int.py +0 -0
  67. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/custom_types.py +0 -0
  68. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/__init__.py +0 -0
  69. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/algorithms.py +0 -0
  70. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/comparer.py +0 -0
  71. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/default_dict.py +0 -0
  72. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/functions.py +0 -0
  73. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/graph/__init__.py +0 -0
  74. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/graph/binary_node.py +0 -0
  75. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/graph/graph.py +0 -0
  76. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/graph/multinode.py +0 -0
  77. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/graph/node.py +0 -0
  78. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/heap/__init__.py +0 -0
  79. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/heap/heap.py +0 -0
  80. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/heap/max_heap.py +0 -0
  81. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/heap/min_heap.py +0 -0
  82. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/queue/__init__.py +0 -0
  83. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/queue/atomic_queue.py +0 -0
  84. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/queue/priority_queue.py +0 -0
  85. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/queue/queue.py +0 -0
  86. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/stack.py +0 -0
  87. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/trees/__init__.py +0 -0
  88. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/trees/binary_syntax_tree.py +0 -0
  89. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/data_structures/trees/binary_tree.py +0 -0
  90. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/date.py +0 -0
  91. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/date_time.py +0 -0
  92. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/__init__.py +0 -0
  93. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/atomic.py +0 -0
  94. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/attach.py +0 -0
  95. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/chain_decorators.py +0 -0
  96. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/decorate_conditionally.py +0 -0
  97. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/delay_call.py +0 -0
  98. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/deprecate.py +0 -0
  99. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/final.py +0 -0
  100. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/limit_recursion.py +0 -0
  101. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/memo.py +0 -0
  102. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/normalize_decorator.py +0 -0
  103. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/overload.py +0 -0
  104. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/partially_implemented.py +0 -0
  105. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/processify.py +0 -0
  106. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/property.py +0 -0
  107. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/singleton.py +0 -0
  108. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/threadify.py +0 -0
  109. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/timeout.py +0 -0
  110. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/total_ordering.py +0 -0
  111. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/decorators/validate.py +0 -0
  112. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/exceptions.py +0 -0
  113. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/file_specifications/__init__.py +0 -0
  114. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/__init__.py +0 -0
  115. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/areoneof.py +0 -0
  116. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/check_foreach.py +0 -0
  117. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/factorial.py +0 -0
  118. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/flatten.py +0 -0
  119. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/foreach.py +0 -0
  120. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/isoftype.py +0 -0
  121. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/isoneof.py +0 -0
  122. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/multiloop.py +0 -0
  123. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/parallel_for.py +0 -0
  124. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/partition.py +0 -0
  125. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/powerset.py +0 -0
  126. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/subseteq.py +0 -0
  127. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/functions/types_subseteq.py +0 -0
  128. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/generators/__init__.py +0 -0
  129. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/generators/conditional_generator.py +0 -0
  130. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/generators/generator_from_stream.py +0 -0
  131. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/generators/join_generators.py +0 -0
  132. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/internet.py +0 -0
  133. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/io_.py +0 -0
  134. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/java/__init__.py +0 -0
  135. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/java/interfaces/__init__.py +0 -0
  136. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/java/interfaces/comparable.py +0 -0
  137. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/java/java_interface.py +0 -0
  138. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/__init__.py +0 -0
  139. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/builtin_impls/__init__.py +0 -0
  140. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/builtin_impls/file_logger.py +0 -0
  141. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/builtin_impls/print_logger.py +0 -0
  142. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/log_level.py +0 -0
  143. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/logger.py +0 -0
  144. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/logging_/logger_strategy_impl_base.py +0 -0
  145. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/lombok/__init__.py +0 -0
  146. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/lombok/builder.py +0 -0
  147. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/math_/__init__.py +0 -0
  148. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/math_/constants.py +0 -0
  149. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/math_/functions.py +0 -0
  150. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/math_/math_print.py +0 -0
  151. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/math_/math_symbols.py +0 -0
  152. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/metaclasses/__init__.py +0 -0
  153. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/metaclasses/atomic_class_meta.py +0 -0
  154. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/metaclasses/implicit_data_deleter_meta.py +0 -0
  155. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/metaclasses/instance_cache_meta.py +0 -0
  156. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/metaclasses/interface.py +0 -0
  157. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/metaclasses/overload_meta.py +0 -0
  158. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/mock_/mock_module.py +0 -0
  159. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/path.py +0 -0
  160. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/print_.py +0 -0
  161. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/progress_bar/__init__.py +0 -0
  162. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/progress_bar/ascii_progress_bar.py +0 -0
  163. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/progress_bar/progress_bar.py +0 -0
  164. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/progress_bar/progress_bar_pool.py +0 -0
  165. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/protocols/__init__.py +0 -0
  166. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/protocols/dictable.py +0 -0
  167. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/protocols/evaluable.py +0 -0
  168. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/protocols/serializable.py +0 -0
  169. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/py.typed +0 -0
  170. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/random_.py +0 -0
  171. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/__init__.py +0 -0
  172. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/argument_info.py +0 -0
  173. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/class_/__init__.py +0 -0
  174. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/class_/class_reflection.py +0 -0
  175. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/class_info.py +0 -0
  176. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/decoration_info.py +0 -0
  177. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/file/__init__.py +0 -0
  178. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/file/file_reflection.py +0 -0
  179. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/function/__init__.py +0 -0
  180. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/function/function_reflections.py +0 -0
  181. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/function_info.py +0 -0
  182. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/__init__.py +0 -0
  183. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/callstack.py +0 -0
  184. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/get_traceback.py +0 -0
  185. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/interpreter.py +0 -0
  186. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/is_debugging.py +0 -0
  187. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/os_.py +0 -0
  188. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/packages.py +0 -0
  189. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/python_version.py +0 -0
  190. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/signals.py +0 -0
  191. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/interpreter/tracer.py +0 -0
  192. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/module/__init__.py +0 -0
  193. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/module/module_reflections.py +0 -0
  194. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/reflection/module/package_reflection.py +0 -0
  195. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/__init__.py +0 -0
  196. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/__init__.py +0 -0
  197. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/constant_backoff.py +0 -0
  198. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/exponential_backoff.py +0 -0
  199. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/functional_backoff.py +0 -0
  200. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/linear_backoff.py +0 -0
  201. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +0 -0
  202. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategies/no_backoff.py +0 -0
  203. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/backoff_strategy.py +0 -0
  204. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/retry_executor/retry_executor.py +0 -0
  205. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/snippets/__init__.py +0 -0
  206. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/snippets/try_get.py +0 -0
  207. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/__init__.py +0 -0
  208. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/independent.py +0 -0
  209. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/layered_command.py +0 -0
  210. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/windows/__init__.py +0 -0
  211. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/windows/utils/__init__.py +0 -0
  212. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/windows/utils/filetime.py +0 -0
  213. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/windows/win32_ctime.py +0 -0
  214. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/system/windows/windows.py +0 -0
  215. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/testing/__init__.py +0 -0
  216. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/testing/unittest_/__init__.py +0 -0
  217. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/testing/unittest_/auto_cwd_testcase.py +0 -0
  218. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/text.py +0 -0
  219. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/time.py +0 -0
  220. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/__init__.py +0 -0
  221. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/computability_and_complexity/__init__.py +0 -0
  222. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/computability_and_complexity/discreate_finite_automaton.py +0 -0
  223. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
  224. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/computability_and_complexity/languages/language.py +0 -0
  225. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/computability_and_complexity/languages/sat.py +0 -0
  226. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/computability_and_complexity/turing_machine.py +0 -0
  227. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/databases/__init__.py +0 -0
  228. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/databases/all.py +0 -0
  229. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/__init__.py +0 -0
  230. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/__init__.py +0 -0
  231. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/encoding.py +0 -0
  232. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossless/__init__.py +0 -0
  233. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
  234. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +0 -0
  235. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
  236. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossless/run_length.py +0 -0
  237. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossy/__init__.py +0 -0
  238. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +0 -0
  239. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
  240. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
  241. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
  242. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/tansformations/hough.py +0 -0
  243. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
  244. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/image_proccesing/tansformations/transformation.py +0 -0
  245. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/linear_algebra/__init__.py +0 -0
  246. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/linear_algebra/matrix.py +0 -0
  247. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/machine_learning/__init__.py +0 -0
  248. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/machine_learning/activation_functions/__init__.py +0 -0
  249. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/machine_learning/activation_functions/activation_function.py +0 -0
  250. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/machine_learning/activation_functions/relu.py +0 -0
  251. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/machine_learning/neuron.py +0 -0
  252. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/oop/__init__.py +0 -0
  253. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/oop/observer.py +0 -0
  254. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/oop/strategy.py +0 -0
  255. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/__init__.py +0 -0
  256. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/__init__.py +0 -0
  257. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/conditional_variable.py +0 -0
  258. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
  259. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/__init__.py +0 -0
  260. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/bernoulli.py +0 -0
  261. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/binomial.py +0 -0
  262. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +0 -0
  263. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/discrete.py +0 -0
  264. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/geometric.py +0 -0
  265. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/poisson.py +0 -0
  266. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/conditional_variable/discrete/uniform.py +0 -0
  267. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/distributions.py +0 -0
  268. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/expressions/__init__.py +0 -0
  269. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/expressions/accumulation_expression.py +0 -0
  270. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/expressions/probability_expression.py +0 -0
  271. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/funcs/__init__.py +0 -0
  272. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/funcs/covariance.py +0 -0
  273. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/funcs/expected_value.py +0 -0
  274. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/funcs/probability_function.py +0 -0
  275. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/funcs/variance.py +0 -0
  276. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/operator.py +0 -0
  277. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/protocols.py +0 -0
  278. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/supp.py +0 -0
  279. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/university/probability/transformation.py +0 -0
  280. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils/versioned_imports.py +0 -0
  281. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils.egg-info/dependency_links.txt +0 -0
  282. {danielutils-1.0.45 → danielutils-1.0.46}/danielutils.egg-info/top_level.txt +0 -0
  283. {danielutils-1.0.45 → danielutils-1.0.46}/setup.cfg +0 -0
  284. {danielutils-1.0.45 → danielutils-1.0.46}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: danielutils
3
- Version: 1.0.45
3
+ Version: 1.0.46
4
4
  Summary: A python utils library for things I find useful
5
5
  Author-email: danielnachumdev <danielnachumdev@gmail.com>
6
6
  License: MIT License
@@ -33,6 +33,7 @@ Classifier: Operating System :: Microsoft :: Windows
33
33
  Requires-Python: >=3.8.0
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
+ Dynamic: license-file
36
37
 
37
38
  [![Python package](https://github.com/danielnachumdev/danielutils/actions/workflows/python-package.yml/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/python-package.yml)
38
39
  [![Pylint](https://github.com/danielnachumdev/danielutils/actions/workflows/pylint.yml/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/pylint.yml)
@@ -40,7 +41,7 @@ License-File: LICENSE
40
41
  [![gitleaks](https://github.com/danielnachumdev/danielutils/actions/workflows/gitleaks.yml/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/gitleaks.yml)
41
42
  [![CodeQL](https://github.com/danielnachumdev/danielutils/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/github-code-scanning/codeql)
42
43
 
43
- # danielutils v1.0.39
44
+ # danielutils v1.0.46
44
45
 
45
46
  A comprehensive Python utilities library designed to enhance your development workflow with powerful tools for type safety, async programming, database operations, and much more.
46
47
 
@@ -53,7 +54,7 @@ A comprehensive Python utilities library designed to enhance your development wo
53
54
  - 🎨 **Developer Experience**: Progress bars, logging, reflection, and debugging tools
54
55
  - 🧮 **Academic Tools**: Probability theory and statistical functions
55
56
 
56
- **Tested Python versions**: `3.8.0+`, `3.9.0`, `3.10.13`, `3.11+`
57
+ **Tested Python versions**: `3.8.0+`, `3.9.0`, `3.10.13`
57
58
 
58
59
  > **Note**: This package is actively developed and subject to change. Use at your own risk!
59
60
 
@@ -4,7 +4,7 @@
4
4
  [![gitleaks](https://github.com/danielnachumdev/danielutils/actions/workflows/gitleaks.yml/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/gitleaks.yml)
5
5
  [![CodeQL](https://github.com/danielnachumdev/danielutils/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/danielnachumdev/danielutils/actions/workflows/github-code-scanning/codeql)
6
6
 
7
- # danielutils v1.0.39
7
+ # danielutils v1.0.46
8
8
 
9
9
  A comprehensive Python utilities library designed to enhance your development workflow with powerful tools for type safety, async programming, database operations, and much more.
10
10
 
@@ -17,7 +17,7 @@ A comprehensive Python utilities library designed to enhance your development wo
17
17
  - 🎨 **Developer Experience**: Progress bars, logging, reflection, and debugging tools
18
18
  - 🧮 **Academic Tools**: Probability theory and statistical functions
19
19
 
20
- **Tested Python versions**: `3.8.0+`, `3.9.0`, `3.10.13`, `3.11+`
20
+ **Tested Python versions**: `3.8.0+`, `3.9.0`, `3.10.13`
21
21
 
22
22
  > **Note**: This package is actively developed and subject to change. Use at your own risk!
23
23
 
@@ -1,4 +1,3 @@
1
- from .database import *
2
1
  from .db import *
3
2
  from .multiprogramming import *
4
3
  from .repl import *
@@ -7,4 +7,4 @@ from .database_definitions import *
7
7
  from .database_exceptions import *
8
8
  from .database_factory import *
9
9
  from .dependencies import *
10
- from .initializer import *
10
+ from .database_initializer import *
@@ -1,6 +1,6 @@
1
1
  import functools
2
2
  from abc import ABC, abstractmethod
3
- from typing import Dict, Any, List, Callable, TypeVar, cast, Optional
3
+ from typing import Dict, Any, List, Callable, TypeVar, cast, Optional, Set
4
4
  from .database_exceptions import DBException
5
5
  from .database_definitions import TableSchema, SelectQuery, UpdateQuery, DeleteQuery
6
6
 
@@ -17,23 +17,41 @@ class Database(ABC):
17
17
  Private decorator to wrap database implementation methods and convert implementation-specific
18
18
  exceptions to our standard database exceptions.
19
19
  """
20
+
20
21
  @functools.wraps(db_method)
21
- def wrapper(self: 'Database', *args: Any, **kwargs: Any) -> Any:
22
+ async def wrapper(self: 'Database', *args: Any, **kwargs: Any) -> Any:
22
23
  try:
23
- return db_method(self, *args, **kwargs)
24
+ return await db_method(self, *args, **kwargs)
24
25
  except DBException:
25
26
  raise
26
27
  except Exception as e:
27
28
  raise cls._default_class_exception_conversion(e)
29
+
28
30
  return cast(F, wrapper)
29
31
 
32
+ @classmethod
33
+ def _get_functions_with_auto_converted_exceptions(cls) -> Set[str]:
34
+ return {
35
+ "connect",
36
+ "disconnect",
37
+ "get_schemas",
38
+ "create_table",
39
+ "insert",
40
+ "get",
41
+ "update",
42
+ "delete"
43
+ }
44
+
30
45
  @classmethod
31
46
  def __init_subclass__(cls) -> None:
32
47
  """Initialize subclass by wrapping all public methods with exception handling"""
33
48
  for name, method in cls.__dict__.items():
34
- if (callable(method) and
35
- not name.startswith('_') and
36
- not isinstance(method, (classmethod, staticmethod))):
49
+ if (
50
+ callable(method) and
51
+ not name.startswith('_') and
52
+ not isinstance(method, (classmethod, staticmethod))
53
+ and name in cls._get_functions_with_auto_converted_exceptions()
54
+ ):
37
55
  setattr(cls, name, cls._wrap_db_exceptions(method))
38
56
 
39
57
  @classmethod
@@ -48,19 +66,19 @@ class Database(ABC):
48
66
  Returns:
49
67
  Exception: The converted exception
50
68
  """
51
- return DBException(f"Database error: {str(e)}", e)
69
+ return DBException(f"Database error: {str(e)}")
52
70
 
53
- def __enter__(self) -> 'Database':
71
+ async def __aenter__(self) -> 'Database':
54
72
  """
55
73
  Context manager entry point. Connects to the database.
56
74
 
57
75
  Returns:
58
76
  Database: The database instance for use in the context
59
77
  """
60
- self.connect()
78
+ await self.connect()
61
79
  return self
62
80
 
63
- def __exit__(self, exc_type: Optional[type], exc_val: Optional[Exception], exc_tb: Optional[Any]) -> None:
81
+ async def __aexit__(self, exc_type: Optional[type], exc_val: Optional[Exception], exc_tb: Optional[Any]) -> None:
64
82
  """
65
83
  Context manager exit point. Disconnects from the database.
66
84
 
@@ -69,18 +87,22 @@ class Database(ABC):
69
87
  exc_val: The exception instance that was raised, if any
70
88
  exc_tb: The traceback for the exception, if any
71
89
  """
72
- self.disconnect()
90
+ await self.disconnect()
73
91
 
74
92
  @abstractmethod
75
- def connect(self) -> None:
93
+ async def connect(self) -> None:
76
94
  """Establish connection to the database"""
77
95
 
78
96
  @abstractmethod
79
- def disconnect(self) -> None:
97
+ async def disconnect(self) -> None:
80
98
  """Close the database connection"""
81
99
 
82
100
  @abstractmethod
83
- def get_schemas(self) -> Dict[str, TableSchema]:
101
+ def is_connected(self) -> bool:
102
+ """Check if the database connection is open"""
103
+
104
+ @abstractmethod
105
+ async def get_schemas(self) -> Dict[str, TableSchema]:
84
106
  """
85
107
  Get the complete database schema
86
108
 
@@ -89,7 +111,7 @@ class Database(ABC):
89
111
  """
90
112
 
91
113
  @abstractmethod
92
- def create_table(self, schema: TableSchema) -> None:
114
+ async def create_table(self, schema: TableSchema) -> None:
93
115
  """
94
116
  Create a new table in the database
95
117
 
@@ -98,7 +120,7 @@ class Database(ABC):
98
120
  """
99
121
 
100
122
  @abstractmethod
101
- def insert(self, table: str, data: Dict[str, Any]) -> Any:
123
+ async def insert(self, table: str, data: Dict[str, Any]) -> Any:
102
124
  """
103
125
  Insert a record into the specified table
104
126
 
@@ -111,7 +133,7 @@ class Database(ABC):
111
133
  """
112
134
 
113
135
  @abstractmethod
114
- def get(self, query: SelectQuery) -> List[Dict[str, Any]]:
136
+ async def get(self, query: SelectQuery) -> List[Dict[str, Any]]:
115
137
  """
116
138
  Get records from the database
117
139
 
@@ -123,7 +145,7 @@ class Database(ABC):
123
145
  """
124
146
 
125
147
  @abstractmethod
126
- def update(self, query: UpdateQuery) -> int:
148
+ async def update(self, query: UpdateQuery) -> int:
127
149
  """
128
150
  Update records in the database
129
151
 
@@ -135,7 +157,7 @@ class Database(ABC):
135
157
  """
136
158
 
137
159
  @abstractmethod
138
- def delete(self, query: DeleteQuery) -> int:
160
+ async def delete(self, query: DeleteQuery) -> int:
139
161
  """
140
162
  Delete records from the database
141
163
 
@@ -1,6 +1,13 @@
1
+ from typing import Optional
2
+
3
+
1
4
  class DBException(Exception):
2
5
  """Base class for all database exceptions"""
3
6
 
7
+ def __init__(self, message: Optional[str] = None, status_code: Optional[int] = None) -> None:
8
+ super().__init__(message)
9
+ self.status_code = status_code
10
+
4
11
 
5
12
  class DBConnectionError(DBException):
6
13
  """Raised when there is an error connecting to the database"""
@@ -3,6 +3,13 @@ from typing import Literal, Dict, Any, Optional, Tuple
3
3
  from .database import Database
4
4
  from .implementations import InMemoryDatabase, SQLiteDatabase, PersistentInMemoryDatabase, RedisDatabase
5
5
 
6
+ MAPPING = {
7
+ "sqlite": SQLiteDatabase,
8
+ "memory": InMemoryDatabase,
9
+ "persistent_memory": PersistentInMemoryDatabase,
10
+ "redis": RedisDatabase
11
+ }
12
+
6
13
 
7
14
  class DatabaseFactory(ABC):
8
15
  """Factory class for creating database instances"""
@@ -12,8 +19,7 @@ class DatabaseFactory(ABC):
12
19
  @classmethod
13
20
  def get_database(
14
21
  cls,
15
- db_type: Literal["sqlite", "memory",
16
- "persistent_memory", "redis"] = "persistent_memory",
22
+ db_type: Literal["sqlite", "memory", "persistent_memory", "redis"] = "persistent_memory",
17
23
  db_args: Optional[Tuple[Any, ...]] = None,
18
24
  db_kwargs: Optional[Dict[str, Any]] = None
19
25
  ) -> Database:
@@ -29,19 +35,13 @@ class DatabaseFactory(ABC):
29
35
  Database: Database instance
30
36
  """
31
37
  if db_type not in cls._instances:
32
- mapping = {
33
- "sqlite": SQLiteDatabase,
34
- "memory": InMemoryDatabase,
35
- "persistent_memory": PersistentInMemoryDatabase,
36
- "redis": RedisDatabase
37
- }
38
- if db_type not in mapping:
38
+ if db_type not in MAPPING:
39
39
  raise ValueError(f"Unsupported database type: '{db_type}'")
40
40
 
41
41
  # Convert None to empty tuple/dict for database initialization
42
42
  args = db_args or ()
43
43
  kwargs = db_kwargs or {}
44
- cls._instances[db_type] = mapping[db_type](*args, **kwargs)
44
+ cls._instances[db_type] = MAPPING[db_type](*args, **kwargs)
45
45
  return cls._instances[db_type]
46
46
 
47
47
  @classmethod
@@ -0,0 +1,256 @@
1
+ import logging
2
+ from abc import ABC, abstractmethod
3
+ from typing import Dict, Optional, Type, Any, Sequence
4
+
5
+ try:
6
+ from sqlalchemy import inspect, Column
7
+ from sqlalchemy.orm import DeclarativeBase
8
+ except ImportError:
9
+ from ...mock_ import MockImportObject
10
+
11
+ inspect = MockImportObject("'sqlalchemy' is not installed") # type:ignore
12
+ Column = MockImportObject("'sqlalchemy' is not installed") # type:ignore
13
+ DeclarativeBase = type("DeclarativeBase", (object,), {}) # type:ignore
14
+
15
+ from .database import Database
16
+ from .database_definitions import ColumnType, TableSchema, TableColumn as ColumnSchema, TableIndex as IndexSchema
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ TYPE_MAPPING = {
21
+ 'INTEGER': ColumnType.INTEGER,
22
+ 'BIGINT': ColumnType.INTEGER,
23
+ 'FLOAT': ColumnType.FLOAT,
24
+ 'NUMERIC': ColumnType.FLOAT,
25
+ 'DECIMAL': ColumnType.FLOAT,
26
+ 'BOOLEAN': ColumnType.BOOLEAN,
27
+ 'TEXT': ColumnType.TEXT,
28
+ 'VARCHAR': ColumnType.TEXT,
29
+ 'CHAR': ColumnType.TEXT,
30
+ 'UUID': ColumnType.TEXT,
31
+ 'DATETIME': ColumnType.DATETIME,
32
+ 'DATE': ColumnType.DATE,
33
+ 'TIME': ColumnType.TIME,
34
+ 'JSON': ColumnType.JSON,
35
+ 'BLOB': ColumnType.BLOB,
36
+ }
37
+
38
+
39
+ async def validate_schema(db: Database, table_name: str, expected_schema: TableSchema) -> bool:
40
+ """
41
+ Validate that the existing table schema matches the expected schema.
42
+
43
+ Args:
44
+ db: Database instance
45
+ table_name: Name of the table to validate
46
+ expected_schema: Expected table schema
47
+
48
+ Returns:
49
+ bool: True if schema matches, False otherwise
50
+ """
51
+ try:
52
+ # Get existing schema
53
+ existing_schemas = await db.get_schemas()
54
+ if table_name not in existing_schemas:
55
+ logger.warning(f"Table '{table_name}' does not exist")
56
+ return False
57
+
58
+ existing_schema = existing_schemas[table_name]
59
+
60
+ # Compare columns
61
+ if len(existing_schema.columns) != len(expected_schema.columns):
62
+ logger.warning(
63
+ f"Column count mismatch in '{table_name}': "
64
+ f"expected {len(expected_schema.columns)}, "
65
+ f"got {len(existing_schema.columns)}"
66
+ )
67
+ return False
68
+
69
+ # Compare each column
70
+ for expected_col, existing_col in zip(expected_schema.columns, existing_schema.columns
71
+ ):
72
+ if (
73
+ expected_col.name != existing_col.name or
74
+ expected_col.type != existing_col.type or
75
+ expected_col.primary_key != existing_col.primary_key or
76
+ expected_col.nullable != existing_col.nullable or
77
+ expected_col.unique != existing_col.unique or
78
+ expected_col.foreign_key != existing_col.foreign_key
79
+ ):
80
+ logger.warning(
81
+ f"Column mismatch in '{table_name}': expected {expected_col}, got {existing_col}"
82
+ )
83
+ return False
84
+
85
+ # Compare indexes
86
+ if len(existing_schema.indexes) != len(expected_schema.indexes):
87
+ logger.warning(
88
+ f"Index count mismatch in '{table_name}': "
89
+ f"expected {len(expected_schema.indexes)}, "
90
+ f"got {len(existing_schema.indexes)}"
91
+ )
92
+ return False
93
+
94
+ # Compare each index
95
+ for expected_idx, existing_idx in zip(expected_schema.indexes, existing_schema.indexes):
96
+ if (
97
+ expected_idx.name != existing_idx.name or
98
+ expected_idx.columns != existing_idx.columns or
99
+ expected_idx.unique != existing_idx.unique
100
+ ):
101
+ logger.warning(f"Index mismatch in '{table_name}': expected {expected_idx}, got {existing_idx}")
102
+ return False
103
+
104
+ return True
105
+
106
+ except Exception as e:
107
+ logger.error(f"Error validating schema for '{table_name}': {str(e)}")
108
+ return False
109
+
110
+
111
+ def get_column_type(column: Column) -> ColumnType:
112
+ """Convert SQLAlchemy column type to our ColumnType enum"""
113
+ # Ensure autoincrement for integer primary key named 'id'
114
+ if (
115
+ column.name == 'id'
116
+ and getattr(column, 'primary_key', False)
117
+ and ('INTEGER' in str(column.type).upper() or 'BIGINT' in str(column.type).upper())
118
+ ):
119
+ return ColumnType.AUTOINCREMENT
120
+ # Check for autoincrement attribute
121
+ if getattr(column, 'autoincrement', False) == True:
122
+ return ColumnType.AUTOINCREMENT
123
+ # Then check the column type
124
+ type_name = str(column.type).upper()
125
+ for sql_type, our_type in TYPE_MAPPING.items():
126
+ if sql_type in type_name:
127
+ return our_type
128
+ return ColumnType.TEXT # Default to TEXT if type not found
129
+
130
+
131
+ def get_foreign_key(column: Column) -> Optional[Dict[str, str]]:
132
+ """Extract foreign key information from a column"""
133
+ for fk in column.foreign_keys:
134
+ return {
135
+ "table": fk.column.table.name,
136
+ "column": fk.column.name
137
+ }
138
+ return None
139
+
140
+
141
+ def get_default_value(column: Column) -> Optional[Any]:
142
+ """Extract default value from a column"""
143
+ if column.default is not None:
144
+ if hasattr(column.default, 'arg'):
145
+ return column.default.arg
146
+ return column.default
147
+ return None
148
+
149
+
150
+ def model_to_schema(model_class: Type[DeclarativeBase]) -> TableSchema:
151
+ """Convert a SQLAlchemy model to our TableSchema"""
152
+ mapper = inspect(model_class)
153
+ table_name = mapper.local_table.name # type: ignore
154
+
155
+ # Convert columns
156
+ columns = []
157
+ for column in mapper.columns:
158
+ # Get unique constraint from column
159
+ is_unique = False
160
+ if column.unique:
161
+ is_unique = True
162
+ elif column.index and column.index.unique: # type: ignore
163
+ is_unique = True
164
+
165
+ col_schema = ColumnSchema(
166
+ name=column.name,
167
+ type=get_column_type(column),
168
+ primary_key=column.primary_key,
169
+ nullable=column.nullable, # type: ignore
170
+ unique=is_unique,
171
+ foreign_key=get_foreign_key(column),
172
+ default=get_default_value(column)
173
+ )
174
+ columns.append(col_schema)
175
+
176
+ # Convert indexes
177
+ indexes = []
178
+ for index in mapper.local_table.indexes: # type: ignore
179
+ # Skip indexes that are already handled by unique constraints
180
+ if len(index.columns) == 1 and index.columns[0].unique:
181
+ continue
182
+
183
+ idx_schema = IndexSchema(
184
+ name=index.name,
185
+ columns=[col.name for col in index.columns],
186
+ unique=index.unique
187
+ )
188
+ indexes.append(idx_schema)
189
+
190
+ return TableSchema(
191
+ name=table_name,
192
+ columns=columns,
193
+ indexes=indexes
194
+ )
195
+
196
+
197
+ class DatabaseInitializer(ABC):
198
+ # Map SQLAlchemy types to our ColumnType enum
199
+
200
+ @classmethod
201
+ @abstractmethod
202
+ def _get_models(cls) -> Sequence[Type[DeclarativeBase]]:
203
+ """Get all pydantic models from our models"""
204
+
205
+ @classmethod
206
+ def _get_table_schemas(cls) -> Dict[str, TableSchema]:
207
+ """Get all table schemas from our models"""
208
+ models = cls._get_models()
209
+ res = {}
210
+ for model in models:
211
+ try:
212
+ res[model.__tablename__] = model_to_schema(model)
213
+ except Exception as e:
214
+ raise Exception(f"Failed parsing '{model.__tablename__}'") from e
215
+ return res
216
+
217
+ @classmethod
218
+ async def init_db(cls, db: Database) -> None:
219
+ """
220
+ Initialize the database by creating or validating all required tables.
221
+
222
+ Args:
223
+ db: Database instance to initialize
224
+ """
225
+ try:
226
+ await db.connect()
227
+
228
+ # Get existing schemas
229
+ existing_schemas = await db.get_schemas()
230
+
231
+ # Get table schemas from models
232
+ table_schemas = cls._get_table_schemas()
233
+
234
+ # Create or validate each table
235
+ for table_name, expected_schema in table_schemas.items():
236
+ if table_name not in existing_schemas:
237
+ logger.info(f"Creating table '{table_name}'")
238
+ await db.create_table(expected_schema)
239
+ else:
240
+ logger.info(f"Validating table '{table_name}'")
241
+ if not validate_schema(db, table_name, expected_schema):
242
+ raise ValueError(
243
+ f"Schema validation failed for table '{table_name}'. "
244
+ "Please check the logs for details."
245
+ )
246
+
247
+ logger.info("Database initialization completed successfully")
248
+
249
+ except Exception as e:
250
+ logger.error(f"Error initializing database: {str(e)}")
251
+ raise
252
+
253
+
254
+ __all__ = [
255
+ "DatabaseInitializer"
256
+ ]
@@ -4,4 +4,3 @@ from .database_factory import DatabaseFactory
4
4
 
5
5
  def get_db() -> Database:
6
6
  return DatabaseFactory.get_database_from_settings()
7
-
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Dict, Any, List
2
+ from typing import Dict, Any, List, Type, Union, Sequence
3
3
  from datetime import datetime
4
4
 
5
5
  try:
@@ -18,16 +18,19 @@ from ..database_exceptions import DBValidationError, DBQueryError, DBConnectionE
18
18
  class InMemoryDatabase(Database):
19
19
  """In-memory database implementation using dictionaries"""
20
20
 
21
+ def is_connected(self) -> bool:
22
+ return self._connected
23
+
21
24
  @classmethod
22
25
  def _default_class_exception_conversion(cls, e: Exception) -> Exception:
23
26
  """
24
27
  Convert in-memory database specific exceptions to standard database exceptions.
25
28
  """
26
29
  if isinstance(e, ValueError):
27
- return DBValidationError(f"Validation error: {str(e)}", e)
30
+ return DBValidationError(f"Validation error: {str(e)}")
28
31
  if isinstance(e, RuntimeError):
29
- return DBQueryError(f"Query error: {str(e)}", e)
30
- return DBException(f"Database error: {str(e)}", e)
32
+ return DBQueryError(f"Query error: {str(e)}")
33
+ return DBException(f"Database error: {str(e)}")
31
34
 
32
35
  def __init__(self, *args, **kwargs) -> None:
33
36
  try:
@@ -42,23 +45,19 @@ class InMemoryDatabase(Database):
42
45
  self._connected = False
43
46
  self.logger = logging.getLogger(__name__)
44
47
 
45
- def connect(self) -> None:
48
+ async def connect(self) -> None:
46
49
  """Connect to the database (no-op for in-memory)"""
47
50
  self._connected = True
48
51
  self.logger.info("Connected to in-memory database")
49
52
 
50
- def disconnect(self) -> None:
53
+ async def disconnect(self) -> None:
51
54
  """Disconnect from the database (no-op for in-memory)"""
52
55
  self._connected = False
53
56
  self.logger.info("Disconnected from in-memory database")
54
57
 
55
- def is_connected(self) -> bool:
56
- """Check if connected to the database"""
57
- return self._connected
58
-
59
- def create_table(self, schema: TableSchema) -> None:
58
+ async def create_table(self, schema: TableSchema) -> None:
60
59
  """Create a new table with the given schema"""
61
- if not self._connected:
60
+ if not self.is_connected():
62
61
  raise RuntimeError("Not connected to database")
63
62
 
64
63
  if schema.name in self.tables:
@@ -98,16 +97,16 @@ class InMemoryDatabase(Database):
98
97
  f"Column '{column}' is not an auto-increment column")
99
98
  self.auto_increment_counters[table][column] -= 1
100
99
 
101
- def get_schemas(self) -> Dict[str, TableSchema]:
100
+ async def get_schemas(self) -> Dict[str, TableSchema]:
102
101
  """Get all table schemas"""
103
- if not self._connected:
102
+ if not self.is_connected():
104
103
  raise RuntimeError("Not connected to database")
105
104
 
106
105
  return {schema.name: schema for schema in self.schemas.values()}
107
106
 
108
- def insert(self, table: str, data: Dict[str, Any]) -> Any:
107
+ async def insert(self, table: str, data: Dict[str, Any]) -> Any:
109
108
  """Insert a new row into the table"""
110
- if not self._connected:
109
+ if not self.is_connected():
111
110
  raise RuntimeError("Not connected to database")
112
111
 
113
112
  if table not in self.tables:
@@ -165,9 +164,9 @@ class InMemoryDatabase(Database):
165
164
  f"Inserted row into '{table}' with ID '{row_data['id']}'")
166
165
  return row_data['id']
167
166
 
168
- def get(self, query: SelectQuery) -> List[Dict[str, Any]]:
167
+ async def get(self, query: SelectQuery) -> List[Dict[str, Any]]:
169
168
  """Get rows from the table matching the query"""
170
- if not self._connected:
169
+ if not self.is_connected():
171
170
  raise DBConnectionError("Not connected to database")
172
171
 
173
172
  if query.table not in self.tables:
@@ -203,9 +202,9 @@ class InMemoryDatabase(Database):
203
202
 
204
203
  return rows
205
204
 
206
- def update(self, query: UpdateQuery) -> int:
205
+ async def update(self, query: UpdateQuery) -> int:
207
206
  """Update rows in the table matching the query"""
208
- if not self._connected:
207
+ if not self.is_connected():
209
208
  raise RuntimeError("Not connected to database")
210
209
 
211
210
  if query.table not in self.tables:
@@ -236,9 +235,9 @@ class InMemoryDatabase(Database):
236
235
  self.logger.info(f"Updated '{updated_count}' rows in '{query.table}'")
237
236
  return updated_count
238
237
 
239
- def delete(self, query: DeleteQuery) -> int:
238
+ async def delete(self, query: DeleteQuery) -> int:
240
239
  """Delete rows from the table matching the query"""
241
- if not self._connected:
240
+ if not self.is_connected():
242
241
  raise RuntimeError("Not connected to database")
243
242
 
244
243
  if query.table not in self.tables:
@@ -263,7 +262,7 @@ class InMemoryDatabase(Database):
263
262
  if value is None:
264
263
  return column.nullable
265
264
 
266
- type_map = {
265
+ type_map: Dict[str, Union[Type, Sequence[Type]]] = {
267
266
  "INTEGER": int,
268
267
  "BIGINT": int,
269
268
  "FLOAT": float,
@@ -351,9 +350,9 @@ class InMemoryDatabase(Database):
351
350
  elif condition.operator == Operator.CONTAINS_CS:
352
351
  return str(condition.value) in str(value)
353
352
  elif condition.operator == Operator.IN:
354
- return value in condition.values
353
+ return value in condition.values # type: ignore
355
354
  elif condition.operator == Operator.NOT_IN:
356
- return value not in condition.values
355
+ return value not in condition.values # type: ignore
357
356
  elif condition.operator == Operator.IS_NULL:
358
357
  return value is None
359
358
  elif condition.operator == Operator.IS_NOT_NULL: