dycw-utilities 0.125.24__tar.gz → 0.125.26__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 (227) hide show
  1. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/PKG-INFO +1 -1
  2. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/pyproject.toml +2 -2
  3. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_asyncio.py +10 -8
  4. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_slack_sdk.py +46 -2
  5. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sqlalchemy.py +30 -0
  6. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/__init__.py +1 -1
  7. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/asyncio.py +2 -0
  8. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/slack_sdk.py +93 -3
  9. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sqlalchemy.py +39 -1
  10. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/.gitignore +0 -0
  11. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/LICENSE +0 -0
  12. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/README.md +0 -0
  13. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/__init__.py +0 -0
  14. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/conftest.py +0 -0
  15. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/__init__.py +0 -0
  16. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_missing/__init__.py +0 -0
  17. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_missing/module.py +0 -0
  18. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/__init__.py +0 -0
  19. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/outer_1.py +0 -0
  20. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/outer_2.py +0 -0
  21. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  22. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  23. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  24. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  25. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/__init__.py +0 -0
  26. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/module_1.py +0 -0
  27. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/module_2.py +0 -0
  28. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/standalone.py +0 -0
  29. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/with_imports.py +0 -0
  30. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  31. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  32. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  33. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  34. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  35. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  36. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  37. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  38. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_altair.py +0 -0
  39. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_atomicwrites.py +0 -0
  40. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_atools.py +0 -0
  41. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_cachetools.py +0 -0
  42. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_click.py +0 -0
  43. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_concurrent.py +0 -0
  44. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_contextlib.py +0 -0
  45. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_contextvars.py +0 -0
  46. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_cryptography.py +0 -0
  47. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_cvxpy.py +0 -0
  48. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_dataclasses.py +0 -0
  49. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_datetime.py +0 -0
  50. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_enum.py +0 -0
  51. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_errors.py +0 -0
  52. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_eventkit.py +0 -0
  53. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_fastapi.py +0 -0
  54. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_fpdf2.py +0 -0
  55. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_functions.py +0 -0
  56. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_functools.py +0 -0
  57. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_getpass.py +0 -0
  58. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_git.py +0 -0
  59. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_hashlib.py +0 -0
  60. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_http.py +0 -0
  61. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_hypothesis.py +0 -0
  62. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_importlib.py +0 -0
  63. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_ipython.py +0 -0
  64. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_iterables.py +0 -0
  65. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_jupyter.py +0 -0
  66. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_libcst.py +0 -0
  67. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_lightweight_charts.py +0 -0
  68. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_logging.py +0 -0
  69. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_loguru.py +0 -0
  70. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_luigi.py +0 -0
  71. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_math.py +0 -0
  72. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_memory_profiler.py +0 -0
  73. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_modules.py +0 -0
  74. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_more_itertools.py +0 -0
  75. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_numpy.py +0 -0
  76. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_operator.py +0 -0
  77. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_optuna.py +0 -0
  78. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_orjson.py +0 -0
  79. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_os.py +0 -0
  80. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_parse.py +0 -0
  81. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pathlib.py +0 -0
  82. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_period.py +0 -0
  83. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pickle.py +0 -0
  84. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_platform.py +0 -0
  85. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_polars.py +0 -0
  86. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_polars_ols.py +0 -0
  87. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pqdm.py +0 -0
  88. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_psutil.py +0 -0
  89. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pydantic.py +0 -0
  90. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pyinstrument.py +0 -0
  91. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pyrsistent.py +0 -0
  92. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pytest.py +0 -0
  93. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pytest_regressions.py +0 -0
  94. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_python_dotenv.py +0 -0
  95. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_random.py +0 -0
  96. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_re.py +0 -0
  97. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_redis.py +0 -0
  98. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_reprlib.py +0 -0
  99. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_rich.py +0 -0
  100. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_scipy.py +0 -0
  101. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sentinel.py +0 -0
  102. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_shelve.py +0 -0
  103. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_socket.py +0 -0
  104. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sqlalchemy_polars.py +0 -0
  105. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_statsmodel.py +0 -0
  106. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_streamlit.py +0 -0
  107. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_string.py +0 -0
  108. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sys.py +0 -0
  109. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tempfile.py +0 -0
  110. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tenacity.py +0 -0
  111. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_text.py +0 -0
  112. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_threading.py +0 -0
  113. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_timer.py +0 -0
  114. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback.py +0 -0
  115. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/__init__.py +0 -0
  116. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/chain.py +0 -0
  117. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  118. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  119. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  120. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/many.py +0 -0
  121. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/one.py +0 -0
  122. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/recursive.py +0 -0
  123. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  124. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  125. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/two.py +0 -0
  126. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/untraced.py +0 -0
  127. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_types.py +0 -0
  128. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing.py +0 -0
  129. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing_funcs/__init__.py +0 -0
  130. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing_funcs/no_future.py +0 -0
  131. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing_funcs/with_future.py +0 -0
  132. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tzdata.py +0 -0
  133. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tzlocal.py +0 -0
  134. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_uuid.py +0 -0
  135. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_version.py +0 -0
  136. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_warnings.py +0 -0
  137. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_whenever.py +0 -0
  138. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_zipfile.py +0 -0
  139. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_zoneinfo.py +0 -0
  140. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/altair.py +0 -0
  141. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/atomicwrites.py +0 -0
  142. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/atools.py +0 -0
  143. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/cachetools.py +0 -0
  144. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/click.py +0 -0
  145. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/concurrent.py +0 -0
  146. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/contextlib.py +0 -0
  147. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/contextvars.py +0 -0
  148. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/cryptography.py +0 -0
  149. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/cvxpy.py +0 -0
  150. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/dataclasses.py +0 -0
  151. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/datetime.py +0 -0
  152. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/enum.py +0 -0
  153. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/errors.py +0 -0
  154. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/eventkit.py +0 -0
  155. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/fastapi.py +0 -0
  156. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/fpdf2.py +0 -0
  157. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/functions.py +0 -0
  158. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/functools.py +0 -0
  159. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/getpass.py +0 -0
  160. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/git.py +0 -0
  161. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/hashlib.py +0 -0
  162. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/http.py +0 -0
  163. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/hypothesis.py +0 -0
  164. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/importlib.py +0 -0
  165. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/ipython.py +0 -0
  166. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/iterables.py +0 -0
  167. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/jupyter.py +0 -0
  168. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/libcst.py +0 -0
  169. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/lightweight_charts.py +0 -0
  170. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/logging.py +0 -0
  171. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/loguru.py +0 -0
  172. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/luigi.py +0 -0
  173. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/math.py +0 -0
  174. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/memory_profiler.py +0 -0
  175. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/modules.py +0 -0
  176. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/more_itertools.py +0 -0
  177. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/numpy.py +0 -0
  178. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/operator.py +0 -0
  179. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/optuna.py +0 -0
  180. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/orjson.py +0 -0
  181. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/os.py +0 -0
  182. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/parse.py +0 -0
  183. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pathlib.py +0 -0
  184. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/period.py +0 -0
  185. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pickle.py +0 -0
  186. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/platform.py +0 -0
  187. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/polars.py +0 -0
  188. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/polars_ols.py +0 -0
  189. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pqdm.py +0 -0
  190. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/psutil.py +0 -0
  191. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/py.typed +0 -0
  192. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pydantic.py +0 -0
  193. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pyinstrument.py +0 -0
  194. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pyrsistent.py +0 -0
  195. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pytest.py +0 -0
  196. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pytest_regressions.py +0 -0
  197. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/python_dotenv.py +0 -0
  198. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/random.py +0 -0
  199. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/re.py +0 -0
  200. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/redis.py +0 -0
  201. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/reprlib.py +0 -0
  202. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/rich.py +0 -0
  203. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/scipy.py +0 -0
  204. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sentinel.py +0 -0
  205. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/shelve.py +0 -0
  206. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/socket.py +0 -0
  207. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sqlalchemy_polars.py +0 -0
  208. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/statsmodels.py +0 -0
  209. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/streamlit.py +0 -0
  210. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/string.py +0 -0
  211. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sys.py +0 -0
  212. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tempfile.py +0 -0
  213. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tenacity.py +0 -0
  214. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/text.py +0 -0
  215. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/threading.py +0 -0
  216. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/timer.py +0 -0
  217. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/traceback.py +0 -0
  218. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/types.py +0 -0
  219. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/typing.py +0 -0
  220. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tzdata.py +0 -0
  221. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tzlocal.py +0 -0
  222. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/uuid.py +0 -0
  223. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/version.py +0 -0
  224. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/warnings.py +0 -0
  225. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/whenever.py +0 -0
  226. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/zipfile.py +0 -0
  227. {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.125.24
3
+ Version: 0.125.26
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -94,7 +94,7 @@ dependencies = [
94
94
  name = "dycw-utilities"
95
95
  readme = "README.md"
96
96
  requires-python = ">= 3.12"
97
- version = "0.125.24"
97
+ version = "0.125.26"
98
98
 
99
99
  [project.optional-dependencies]
100
100
  test = [
@@ -335,7 +335,7 @@ zzz-test-zoneinfo = [
335
335
  # bump-my-version
336
336
  [tool.bumpversion]
337
337
  allow_dirty = true
338
- current_version = "0.125.24"
338
+ current_version = "0.125.26"
339
339
 
340
340
  [[tool.bumpversion.files]]
341
341
  filename = "src/utilities/__init__.py"
@@ -1381,25 +1381,27 @@ class TestLooper:
1381
1381
  stats = looper.stats
1382
1382
  assert stats.entries == entries
1383
1383
  assert stats.core_attempts == (stats.core_successes + stats.core_failures)
1384
- assert stats.core_successes == approx(core_successes, rel=0.5)
1385
- assert stats.core_failures == approx(core_failures, rel=0.5)
1384
+ assert stats.core_successes == approx(core_successes, rel=0.75)
1385
+ assert stats.core_failures == approx(core_failures, rel=0.75)
1386
1386
  assert stats.initialization_attempts == (
1387
1387
  stats.initialization_successes + stats.initialization_failures
1388
1388
  )
1389
1389
  assert stats.initialization_successes == approx(
1390
- initialization_successes, rel=0.5
1390
+ initialization_successes, rel=0.75
1391
+ )
1392
+ assert stats.initialization_failures == approx(
1393
+ initialization_failures, rel=0.75
1391
1394
  )
1392
- assert stats.initialization_failures == approx(initialization_failures, rel=0.5)
1393
1395
  assert stats.tear_down_attempts == (
1394
1396
  stats.tear_down_successes + stats.tear_down_failures
1395
1397
  )
1396
- assert stats.tear_down_successes == approx(tear_down_successes, rel=0.5)
1397
- assert stats.tear_down_failures == approx(tear_down_failures, rel=0.5)
1398
+ assert stats.tear_down_successes == approx(tear_down_successes, rel=0.75)
1399
+ assert stats.tear_down_failures == approx(tear_down_failures, rel=0.75)
1398
1400
  assert stats.restart_attempts == (
1399
1401
  stats.restart_successes + stats.restart_failures
1400
1402
  )
1401
- assert stats.restart_successes == approx(restart_successes, rel=0.5)
1402
- assert stats.restart_failures == approx(restart_failures, rel=0.5)
1403
+ assert stats.restart_successes == approx(restart_successes, rel=0.75)
1404
+ assert stats.restart_failures == approx(restart_failures, rel=0.75)
1403
1405
  assert stats.stops == stops
1404
1406
 
1405
1407
 
@@ -5,13 +5,18 @@ from logging import getLogger
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  from aiohttp import InvalidUrlClientError
8
- from pytest import mark, raises
8
+ from pytest import mark, param, raises
9
9
  from slack_sdk.webhook.async_client import AsyncWebhookClient
10
10
 
11
11
  from utilities.datetime import MINUTE
12
12
  from utilities.os import get_env_var
13
13
  from utilities.pytest import throttle
14
- from utilities.slack_sdk import SlackHandler, _get_client, send_to_slack
14
+ from utilities.slack_sdk import (
15
+ SlackHandler,
16
+ SlackHandlerService,
17
+ _get_client,
18
+ send_to_slack,
19
+ )
15
20
 
16
21
  if TYPE_CHECKING:
17
22
  from collections.abc import Sequence
@@ -64,3 +69,42 @@ class TestSlackHandler:
64
69
  logger.warning(
65
70
  "message %d from %s", i, TestSlackHandler.test_real.__qualname__
66
71
  )
72
+
73
+ # service
74
+
75
+ @mark.parametrize("auto_start", [param(True), param(False)])
76
+ async def test_main_service(self, *, tmp_path: Path, auto_start: bool) -> None:
77
+ messages: Sequence[str] = []
78
+
79
+ async def sender(_: str, text: str, /) -> None:
80
+ messages.append(text)
81
+
82
+ logger = getLogger(str(tmp_path))
83
+ logger.addHandler(
84
+ handler := SlackHandlerService(
85
+ auto_start=auto_start, url="url", freq=0.05, sender=sender, timeout=1.0
86
+ )
87
+ )
88
+ async with handler:
89
+ logger.warning("message")
90
+ assert messages == ["message"]
91
+
92
+ @mark.skipif(get_env_var("SLACK", nullable=True) is None, reason="'SLACK' not set")
93
+ @throttle(duration=5 * MINUTE)
94
+ async def test_real_service(self, *, tmp_path: Path) -> None:
95
+ url = get_env_var("SLACK")
96
+ logger = getLogger(str(tmp_path))
97
+ logger.addHandler(handler := SlackHandlerService(url=url, freq=0.05))
98
+ async with timeout(1.0), handler:
99
+ for i in range(10):
100
+ logger.warning(
101
+ "message %d from %s",
102
+ i,
103
+ TestSlackHandler.test_real_service.__qualname__,
104
+ )
105
+
106
+ async def test_replace(self) -> None:
107
+ handler = SlackHandlerService(url="url")
108
+ new = handler.replace(freq=10.0)
109
+ assert new.url == handler.url
110
+ assert new.freq == 10.0
@@ -45,6 +45,7 @@ from utilities.sqlalchemy import (
45
45
  Upserter,
46
46
  UpserterError,
47
47
  UpsertItemsError,
48
+ UpsertService,
48
49
  _get_dialect,
49
50
  _get_dialect_max_params,
50
51
  _InsertItem,
@@ -1183,6 +1184,35 @@ class TestUpserter:
1183
1184
  with raises(UpserterError, match="Error running 'Upserter'"):
1184
1185
  raise UpserterError(upserter=upserter)
1185
1186
 
1187
+ # service
1188
+
1189
+ @given(
1190
+ data=data(),
1191
+ name=_table_names(),
1192
+ triples=_upsert_lists(nullable=True, min_size=1),
1193
+ )
1194
+ @mark.flaky
1195
+ @settings(max_examples=1, phases={Phase.generate})
1196
+ async def test_main_service(
1197
+ self, *, data: DataObject, name: str, triples: list[tuple[int, bool, bool]]
1198
+ ) -> None:
1199
+ table = Table(
1200
+ name,
1201
+ MetaData(),
1202
+ Column("id_", Integer, primary_key=True),
1203
+ Column("value", Boolean, nullable=True),
1204
+ )
1205
+ engine = await sqlalchemy_engines(data, table)
1206
+ service = UpsertService(freq=0.1, timeout=1.0, engine=engine)
1207
+ pairs = [(id_, init) for id_, init, _ in triples]
1208
+ async with service:
1209
+ service.put_right_nowait((pairs, table))
1210
+
1211
+ sel = select(table)
1212
+ async with engine.begin() as conn:
1213
+ res = (await conn.execute(sel)).all()
1214
+ assert set(res) == set(pairs)
1215
+
1186
1216
 
1187
1217
  class TestUpsertItems:
1188
1218
  @given(data=data(), name=_table_names(), triple=_upsert_triples(nullable=True))
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.125.24"
3
+ __version__ = "0.125.26"
@@ -901,6 +901,7 @@ class Looper(Generic[_T]):
901
901
  timeout: Duration | None | Sentinel = sentinel,
902
902
  timeout_error: type[Exception] | Sentinel = sentinel,
903
903
  _debug: bool | Sentinel = sentinel,
904
+ **kwargs: Any,
904
905
  ) -> Self:
905
906
  """Replace elements of the looper."""
906
907
  return replace_non_sentinel(
@@ -913,6 +914,7 @@ class Looper(Generic[_T]):
913
914
  timeout=timeout,
914
915
  timeout_error=timeout_error,
915
916
  _debug=_debug,
917
+ **kwargs,
916
918
  )
917
919
 
918
920
  def request_restart(self) -> None:
@@ -3,14 +3,20 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from http import HTTPStatus
5
5
  from logging import NOTSET, Handler, LogRecord
6
- from typing import TYPE_CHECKING, override
6
+ from typing import TYPE_CHECKING, Any, Self, override
7
7
 
8
8
  from slack_sdk.webhook.async_client import AsyncWebhookClient
9
9
 
10
- from utilities.asyncio import InfiniteQueueLooper, timeout_dur
10
+ from utilities.asyncio import (
11
+ InfiniteQueueLooper,
12
+ Looper,
13
+ LooperTimeoutError,
14
+ timeout_dur,
15
+ )
11
16
  from utilities.datetime import MINUTE, SECOND, datetime_duration_to_float
12
17
  from utilities.functools import cache
13
18
  from utilities.math import safe_round
19
+ from utilities.sentinel import Sentinel, sentinel
14
20
 
15
21
  if TYPE_CHECKING:
16
22
  from collections.abc import Callable
@@ -73,6 +79,90 @@ class SlackHandler(Handler, InfiniteQueueLooper[None, str]):
73
79
  await self.sender(self.url, text)
74
80
 
75
81
 
82
+ @dataclass(init=False, unsafe_hash=True)
83
+ class SlackHandlerService(Handler, Looper[str]):
84
+ """Service to send messages to Slack."""
85
+
86
+ @override
87
+ def __init__(
88
+ self,
89
+ *,
90
+ url: str,
91
+ auto_start: bool = False,
92
+ empty_upon_exit: bool = True,
93
+ freq: Duration = SECOND,
94
+ backoff: Duration = SECOND,
95
+ logger: str | None = None,
96
+ timeout: Duration | None = None,
97
+ timeout_error: type[Exception] = LooperTimeoutError,
98
+ _debug: bool = False,
99
+ level: int = NOTSET,
100
+ sender: Callable[[str, str], Coroutine1[None]] = _send_adapter,
101
+ send_timeout: Duration = SECOND,
102
+ ) -> None:
103
+ Looper.__init__( # Looper first
104
+ self,
105
+ auto_start=auto_start,
106
+ freq=freq,
107
+ empty_upon_exit=empty_upon_exit,
108
+ backoff=backoff,
109
+ logger=logger,
110
+ timeout=timeout,
111
+ timeout_error=timeout_error,
112
+ _debug=_debug,
113
+ )
114
+ Looper.__post_init__(self)
115
+ Handler.__init__(self, level=level) # Handler next
116
+ self.url = url
117
+ self.sender = sender
118
+ self.send_timeout = send_timeout
119
+
120
+ @override
121
+ def emit(self, record: LogRecord) -> None:
122
+ fmtted = self.format(record)
123
+ try:
124
+ self.put_right_nowait(fmtted)
125
+ except Exception: # noqa: BLE001 # pragma: no cover
126
+ self.handleError(record)
127
+
128
+ @override
129
+ async def core(self) -> None:
130
+ await super().core()
131
+ if self.empty():
132
+ return
133
+ text = "\n".join(self.get_all_nowait())
134
+ async with timeout_dur(duration=self.send_timeout):
135
+ await self.sender(self.url, text)
136
+
137
+ @override
138
+ def replace(
139
+ self,
140
+ *,
141
+ auto_start: bool | Sentinel = sentinel,
142
+ empty_upon_exit: bool | Sentinel = sentinel,
143
+ freq: Duration | Sentinel = sentinel,
144
+ backoff: Duration | Sentinel = sentinel,
145
+ logger: str | None | Sentinel = sentinel,
146
+ timeout: Duration | None | Sentinel = sentinel,
147
+ timeout_error: type[Exception] | Sentinel = sentinel,
148
+ _debug: bool | Sentinel = sentinel,
149
+ **kwargs: Any,
150
+ ) -> Self:
151
+ """Replace elements of the looper."""
152
+ return super().replace(
153
+ url=self.url,
154
+ auto_start=auto_start,
155
+ empty_upon_exit=empty_upon_exit,
156
+ freq=freq,
157
+ backoff=backoff,
158
+ logger=logger,
159
+ timeout=timeout,
160
+ timeout_error=timeout_error,
161
+ _debug=_debug,
162
+ **kwargs,
163
+ )
164
+
165
+
76
166
  ##
77
167
 
78
168
 
@@ -106,4 +196,4 @@ def _get_client(url: str, /, *, timeout: Duration = _TIMEOUT) -> AsyncWebhookCli
106
196
  return AsyncWebhookClient(url, timeout=timeout_use)
107
197
 
108
198
 
109
- __all__ = ["SendToSlackError", "SlackHandler", "send_to_slack"]
199
+ __all__ = ["SendToSlackError", "SlackHandler", "SlackHandlerService", "send_to_slack"]
@@ -57,7 +57,8 @@ from sqlalchemy.orm import (
57
57
  from sqlalchemy.orm.exc import UnmappedClassError
58
58
  from sqlalchemy.pool import NullPool, Pool
59
59
 
60
- from utilities.asyncio import InfiniteQueueLooper, timeout_dur
60
+ from utilities.asyncio import InfiniteQueueLooper, Looper, timeout_dur
61
+ from utilities.datetime import SECOND
61
62
  from utilities.functions import (
62
63
  ensure_str,
63
64
  get_class_name,
@@ -654,6 +655,42 @@ class UpserterError(Exception):
654
655
  return f"Error running {get_class_name(self.upserter)!r}"
655
656
 
656
657
 
658
+ @dataclass(kw_only=True)
659
+ class UpsertService(Looper[_InsertItem]):
660
+ """Service to upsert items to a database."""
661
+
662
+ # base
663
+ freq: Duration = field(default=SECOND, repr=False)
664
+ backoff: Duration = field(default=SECOND, repr=False)
665
+ empty_upon_exit: bool = field(default=True, repr=False)
666
+ # self
667
+ engine: AsyncEngine
668
+ snake: bool = False
669
+ selected_or_all: _SelectedOrAll = "selected"
670
+ chunk_size_frac: float = CHUNK_SIZE_FRAC
671
+ assume_tables_exist: bool = False
672
+ timeout_create: Duration | None = None
673
+ error_create: type[Exception] = TimeoutError
674
+ timeout_insert: Duration | None = None
675
+ error_insert: type[Exception] = TimeoutError
676
+
677
+ @override
678
+ async def core(self) -> None:
679
+ await super().core()
680
+ await upsert_items(
681
+ self.engine,
682
+ *self.get_all_nowait(),
683
+ snake=self.snake,
684
+ selected_or_all=self.selected_or_all,
685
+ chunk_size_frac=self.chunk_size_frac,
686
+ assume_tables_exist=self.assume_tables_exist,
687
+ timeout_create=self.timeout_create,
688
+ error_create=self.error_create,
689
+ timeout_insert=self.timeout_insert,
690
+ error_insert=self.error_insert,
691
+ )
692
+
693
+
657
694
  ##
658
695
 
659
696
 
@@ -1109,6 +1146,7 @@ __all__ = [
1109
1146
  "InsertItemsError",
1110
1147
  "TablenameMixin",
1111
1148
  "UpsertItemsError",
1149
+ "UpsertService",
1112
1150
  "Upserter",
1113
1151
  "UpserterError",
1114
1152
  "check_engine",