dycw-utilities 0.125.11__tar.gz → 0.125.12__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 (223) hide show
  1. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/PKG-INFO +5 -5
  2. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/pyproject.toml +10 -10
  3. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_asyncio.py +422 -2
  4. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/__init__.py +1 -1
  5. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/asyncio.py +453 -9
  6. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/.gitignore +0 -0
  7. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/LICENSE +0 -0
  8. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/README.md +0 -0
  9. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/__init__.py +0 -0
  10. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/conftest.py +0 -0
  11. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/__init__.py +0 -0
  12. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_missing/__init__.py +0 -0
  13. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_missing/module.py +0 -0
  14. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/__init__.py +0 -0
  15. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/outer_1.py +0 -0
  16. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/outer_2.py +0 -0
  17. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  18. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  19. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  20. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  21. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_without/__init__.py +0 -0
  22. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_without/module_1.py +0 -0
  23. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/package_without/module_2.py +0 -0
  24. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/standalone.py +0 -0
  25. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/modules/with_imports.py +0 -0
  26. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  27. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  28. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  29. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  30. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  31. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  32. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  33. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  34. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_altair.py +0 -0
  35. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_atomicwrites.py +0 -0
  36. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_atools.py +0 -0
  37. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_cachetools.py +0 -0
  38. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_click.py +0 -0
  39. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_concurrent.py +0 -0
  40. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_contextlib.py +0 -0
  41. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_contextvars.py +0 -0
  42. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_cryptography.py +0 -0
  43. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_cvxpy.py +0 -0
  44. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_dataclasses.py +0 -0
  45. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_datetime.py +0 -0
  46. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_enum.py +0 -0
  47. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_errors.py +0 -0
  48. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_eventkit.py +0 -0
  49. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_fastapi.py +0 -0
  50. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_fpdf2.py +0 -0
  51. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_functions.py +0 -0
  52. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_functools.py +0 -0
  53. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_getpass.py +0 -0
  54. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_git.py +0 -0
  55. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_hashlib.py +0 -0
  56. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_http.py +0 -0
  57. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_hypothesis.py +0 -0
  58. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_importlib.py +0 -0
  59. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_ipython.py +0 -0
  60. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_iterables.py +0 -0
  61. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_jupyter.py +0 -0
  62. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_libcst.py +0 -0
  63. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_lightweight_charts.py +0 -0
  64. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_logging.py +0 -0
  65. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_loguru.py +0 -0
  66. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_luigi.py +0 -0
  67. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_math.py +0 -0
  68. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_memory_profiler.py +0 -0
  69. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_modules.py +0 -0
  70. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_more_itertools.py +0 -0
  71. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_numpy.py +0 -0
  72. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_operator.py +0 -0
  73. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_optuna.py +0 -0
  74. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_orjson.py +0 -0
  75. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_os.py +0 -0
  76. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_parse.py +0 -0
  77. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pathlib.py +0 -0
  78. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_period.py +0 -0
  79. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pickle.py +0 -0
  80. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_platform.py +0 -0
  81. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_polars.py +0 -0
  82. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_polars_ols.py +0 -0
  83. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pqdm.py +0 -0
  84. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pydantic.py +0 -0
  85. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pyinstrument.py +0 -0
  86. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pyrsistent.py +0 -0
  87. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pytest.py +0 -0
  88. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_pytest_regressions.py +0 -0
  89. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_python_dotenv.py +0 -0
  90. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_random.py +0 -0
  91. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_re.py +0 -0
  92. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_redis.py +0 -0
  93. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_reprlib.py +0 -0
  94. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_rich.py +0 -0
  95. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_scipy.py +0 -0
  96. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_sentinel.py +0 -0
  97. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_shelve.py +0 -0
  98. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_slack_sdk.py +0 -0
  99. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_socket.py +0 -0
  100. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_sqlalchemy.py +0 -0
  101. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_sqlalchemy_polars.py +0 -0
  102. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_statsmodel.py +0 -0
  103. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_streamlit.py +0 -0
  104. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_sys.py +0 -0
  105. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_tempfile.py +0 -0
  106. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_tenacity.py +0 -0
  107. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_text.py +0 -0
  108. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_threading.py +0 -0
  109. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_timer.py +0 -0
  110. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback.py +0 -0
  111. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/__init__.py +0 -0
  112. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/chain.py +0 -0
  113. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  114. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  115. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  116. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/many.py +0 -0
  117. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/one.py +0 -0
  118. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/recursive.py +0 -0
  119. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  120. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  121. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/two.py +0 -0
  122. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_traceback_funcs/untraced.py +0 -0
  123. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_types.py +0 -0
  124. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_typing.py +0 -0
  125. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_typing_funcs/__init__.py +0 -0
  126. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_typing_funcs/no_future.py +0 -0
  127. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_typing_funcs/with_future.py +0 -0
  128. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_tzdata.py +0 -0
  129. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_tzlocal.py +0 -0
  130. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_uuid.py +0 -0
  131. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_version.py +0 -0
  132. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_warnings.py +0 -0
  133. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_whenever.py +0 -0
  134. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_zipfile.py +0 -0
  135. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/tests/test_zoneinfo.py +0 -0
  136. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/altair.py +0 -0
  137. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/click.py +0 -0
  141. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/concurrent.py +0 -0
  142. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/contextlib.py +0 -0
  143. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/contextvars.py +0 -0
  144. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/cryptography.py +0 -0
  145. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/cvxpy.py +0 -0
  146. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/dataclasses.py +0 -0
  147. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/datetime.py +0 -0
  148. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/enum.py +0 -0
  149. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/errors.py +0 -0
  150. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/eventkit.py +0 -0
  151. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/fastapi.py +0 -0
  152. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/fpdf2.py +0 -0
  153. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/functions.py +0 -0
  154. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/functools.py +0 -0
  155. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/getpass.py +0 -0
  156. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/git.py +0 -0
  157. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/hashlib.py +0 -0
  158. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/http.py +0 -0
  159. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/hypothesis.py +0 -0
  160. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/importlib.py +0 -0
  161. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/ipython.py +0 -0
  162. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/iterables.py +0 -0
  163. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/jupyter.py +0 -0
  164. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/libcst.py +0 -0
  165. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/lightweight_charts.py +0 -0
  166. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/logging.py +0 -0
  167. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/loguru.py +0 -0
  168. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/luigi.py +0 -0
  169. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/math.py +0 -0
  170. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/memory_profiler.py +0 -0
  171. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/modules.py +0 -0
  172. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/more_itertools.py +0 -0
  173. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/numpy.py +0 -0
  174. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/operator.py +0 -0
  175. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/optuna.py +0 -0
  176. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/orjson.py +0 -0
  177. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/os.py +0 -0
  178. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/parse.py +0 -0
  179. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pathlib.py +0 -0
  180. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/period.py +0 -0
  181. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pickle.py +0 -0
  182. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/platform.py +0 -0
  183. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/polars.py +0 -0
  184. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/polars_ols.py +0 -0
  185. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pqdm.py +0 -0
  186. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/py.typed +0 -0
  187. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pydantic.py +0 -0
  188. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pyinstrument.py +0 -0
  189. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pyrsistent.py +0 -0
  190. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pytest.py +0 -0
  191. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/pytest_regressions.py +0 -0
  192. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/python_dotenv.py +0 -0
  193. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/random.py +0 -0
  194. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/re.py +0 -0
  195. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/redis.py +0 -0
  196. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/reprlib.py +0 -0
  197. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/rich.py +0 -0
  198. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/scipy.py +0 -0
  199. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/sentinel.py +0 -0
  200. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/shelve.py +0 -0
  201. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/slack_sdk.py +0 -0
  202. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/socket.py +0 -0
  203. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/sqlalchemy.py +0 -0
  204. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/sqlalchemy_polars.py +0 -0
  205. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/statsmodels.py +0 -0
  206. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/streamlit.py +0 -0
  207. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/sys.py +0 -0
  208. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/tempfile.py +0 -0
  209. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/tenacity.py +0 -0
  210. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/text.py +0 -0
  211. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/threading.py +0 -0
  212. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/timer.py +0 -0
  213. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/traceback.py +0 -0
  214. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/types.py +0 -0
  215. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/typing.py +0 -0
  216. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/tzdata.py +0 -0
  217. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/tzlocal.py +0 -0
  218. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/uuid.py +0 -0
  219. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/version.py +0 -0
  220. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/warnings.py +0 -0
  221. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/whenever.py +0 -0
  222. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/src/utilities/zipfile.py +0 -0
  223. {dycw_utilities-0.125.11 → dycw_utilities-0.125.12}/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.11
3
+ Version: 0.125.12
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -25,7 +25,7 @@ Requires-Dist: altair<5.6,>=5.5.0; extra == 'zzz-test-altair'
25
25
  Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-altair'
26
26
  Requires-Dist: img2pdf<0.7,>=0.6.0; extra == 'zzz-test-altair'
27
27
  Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-altair'
28
- Requires-Dist: vl-convert-python<1.8,>=1.7.0; extra == 'zzz-test-altair'
28
+ Requires-Dist: vl-convert-python<1.9,>=1.8.0; extra == 'zzz-test-altair'
29
29
  Requires-Dist: whenever<0.9,>=0.8.4; extra == 'zzz-test-altair'
30
30
  Provides-Extra: zzz-test-asyncio
31
31
  Provides-Extra: zzz-test-atomicwrites
@@ -82,7 +82,7 @@ Requires-Dist: hypothesis<6.132,>=6.131.30; extra == 'zzz-test-hypothesis'
82
82
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
83
83
  Requires-Dist: numpy<2.3,>=2.2.6; extra == 'zzz-test-hypothesis'
84
84
  Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
85
- Requires-Dist: redis<6.2,>=6.1.0; extra == 'zzz-test-hypothesis'
85
+ Requires-Dist: redis<6.3,>=6.2.0; extra == 'zzz-test-hypothesis'
86
86
  Requires-Dist: sqlalchemy<2.1,>=2.0.41; extra == 'zzz-test-hypothesis'
87
87
  Requires-Dist: tenacity<9.0,>=8.5.0; extra == 'zzz-test-hypothesis'
88
88
  Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-hypothesis'
@@ -162,7 +162,7 @@ Provides-Extra: zzz-test-re
162
162
  Provides-Extra: zzz-test-redis
163
163
  Requires-Dist: orjson<3.11,>=3.10.15; extra == 'zzz-test-redis'
164
164
  Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-redis'
165
- Requires-Dist: redis<6.2,>=6.1.0; extra == 'zzz-test-redis'
165
+ Requires-Dist: redis<6.3,>=6.2.0; extra == 'zzz-test-redis'
166
166
  Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-redis'
167
167
  Requires-Dist: tenacity<9.0,>=8.5.0; extra == 'zzz-test-redis'
168
168
  Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-redis'
@@ -174,7 +174,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
174
174
  Provides-Extra: zzz-test-sentinel
175
175
  Provides-Extra: zzz-test-shelve
176
176
  Provides-Extra: zzz-test-slack-sdk
177
- Requires-Dist: aiohttp<3.12.3,>=3.12.2; extra == 'zzz-test-slack-sdk'
177
+ Requires-Dist: aiohttp<3.12.5,>=3.12.4; extra == 'zzz-test-slack-sdk'
178
178
  Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
179
179
  Provides-Extra: zzz-test-socket
180
180
  Provides-Extra: zzz-test-sqlalchemy
@@ -8,7 +8,7 @@ requires = ["hatchling"]
8
8
  # dependency groups
9
9
  [dependency-groups]
10
10
  dev = [
11
- "aiohttp >= 3.12.2, < 3.12.3", # for slack
11
+ "aiohttp >= 3.12.4, < 3.12.5", # for slack
12
12
  "aiosqlite >= 0.21.0, < 0.22",
13
13
  "altair >= 5.5.0, < 5.6",
14
14
  "asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
@@ -35,7 +35,7 @@ dev = [
35
35
  "nest-asyncio >= 1.6.0, < 1.7", # for sqlalchemy async
36
36
  "nox >= 2025.2.9, < 2025.3",
37
37
  "numpy >= 2.2.6, < 2.3",
38
- "libcst >= 1.7.0, < 1.8",
38
+ "libcst >= 1.8.0, < 1.9",
39
39
  "optuna >= 4.3.0, < 4.4",
40
40
  "orjson >= 3.10.18, < 3.11",
41
41
  "pathvalidate >= 3.2.3, < 3.3",
@@ -50,7 +50,7 @@ dev = [
50
50
  "pytest >= 8.3.5, < 8.4",
51
51
  "pytest-regressions >= 2.7.0, < 2.8",
52
52
  "python-dotenv >= 1.1.0, < 1.2",
53
- "redis >= 6.1.0, < 6.2",
53
+ "redis >= 6.2.0, < 6.3",
54
54
  "rich >= 14.0.0, < 14.1",
55
55
  "scikit-learn >= 1.6.1, < 1.7",
56
56
  "scipy >= 1.15.3, < 1.16",
@@ -65,7 +65,7 @@ dev = [
65
65
  "uvicorn >= 0.34.1, < 0.35",
66
66
  "vegafusion >= 2.0.2, < 2.1",
67
67
  "vegafusion-python-embed >= 1.6.9, < 1.7",
68
- "vl-convert-python >= 1.7.0, < 1.8",
68
+ "vl-convert-python >= 1.8.0, < 1.9",
69
69
  "whenever >= 0.8.4, < 0.9",
70
70
  # test
71
71
  "coverage-conditional-plugin >= 0.9.0, < 0.10",
@@ -93,7 +93,7 @@ dependencies = [
93
93
  name = "dycw-utilities"
94
94
  readme = "README.md"
95
95
  requires-python = ">= 3.12"
96
- version = "0.125.11"
96
+ version = "0.125.12"
97
97
 
98
98
  [project.optional-dependencies]
99
99
  test = [
@@ -117,7 +117,7 @@ zzz-test-altair = [
117
117
  "atomicwrites >= 1.4.1, < 1.5",
118
118
  "img2pdf >= 0.6.0, < 0.7",
119
119
  "polars-lts-cpu >= 1.30.0, < 1.31",
120
- "vl-convert-python >= 1.7.0, < 1.8",
120
+ "vl-convert-python >= 1.8.0, < 1.9",
121
121
  "whenever >= 0.8.4, < 0.9",
122
122
  ]
123
123
  zzz-test-asyncio = []
@@ -178,7 +178,7 @@ zzz-test-hypothesis = [
178
178
  "luigi >= 3.6.0, < 3.7",
179
179
  "numpy >= 2.2.6, < 2.3",
180
180
  "pathvalidate >= 3.2.3, < 3.3",
181
- "redis >= 6.1.0, < 6.2",
181
+ "redis >= 6.2.0, < 6.3",
182
182
  "sqlalchemy >= 2.0.41, < 2.1",
183
183
  "tenacity >= 8.5.0, < 9.0", # limited by luigi
184
184
  "tzlocal >= 5.3.1, < 5.4",
@@ -259,7 +259,7 @@ zzz-test-re = []
259
259
  zzz-test-redis = [
260
260
  "orjson >= 3.10.15, < 3.11",
261
261
  "polars-lts-cpu >= 1.30.0, < 1.31",
262
- "redis >= 6.1.0, < 6.2",
262
+ "redis >= 6.2.0, < 6.3",
263
263
  "rich >= 14.0.0, < 14.1",
264
264
  "tenacity >= 8.5.0, < 9.0", # limited by luigi
265
265
  "tzlocal >= 5.3.1, < 5.4",
@@ -270,7 +270,7 @@ zzz-test-scipy = ["scipy >= 1.15.3, < 1.16"]
270
270
  zzz-test-sentinel = []
271
271
  zzz-test-shelve = []
272
272
  zzz-test-slack-sdk = [
273
- "aiohttp >= 3.12.2, < 3.12.3", # for slack
273
+ "aiohttp >= 3.12.4, < 3.12.5", # for slack
274
274
  "slack-sdk >= 3.35.0, < 3.36",
275
275
  ]
276
276
  zzz-test-socket = []
@@ -334,7 +334,7 @@ zzz-test-zoneinfo = [
334
334
  # bump-my-version
335
335
  [tool.bumpversion]
336
336
  allow_dirty = true
337
- current_version = "0.125.11"
337
+ current_version = "0.125.12"
338
338
 
339
339
  [[tool.bumpversion.files]]
340
340
  filename = "src/utilities/__init__.py"
@@ -21,17 +21,21 @@ from hypothesis.strategies import (
21
21
  permutations,
22
22
  sampled_from,
23
23
  )
24
- from pytest import LogCaptureFixture, mark, param, raises
24
+ from pytest import LogCaptureFixture, approx, mark, param, raises
25
25
 
26
26
  from utilities.asyncio import (
27
27
  EnhancedQueue,
28
28
  EnhancedTaskGroup,
29
29
  InfiniteLooper,
30
30
  InfiniteQueueLooper,
31
+ Looper,
32
+ LooperTimeoutError,
31
33
  UniquePriorityQueue,
32
34
  UniqueQueue,
33
35
  _InfiniteLooperDefaultEventError,
34
36
  _InfiniteLooperNoSuchEventError,
37
+ _LooperNoTaskError,
38
+ _LooperStats,
35
39
  get_event,
36
40
  get_items,
37
41
  get_items_nowait,
@@ -47,6 +51,7 @@ from utilities.dataclasses import replace_non_sentinel
47
51
  from utilities.datetime import (
48
52
  MILLISECOND,
49
53
  MINUTE,
54
+ SECOND,
50
55
  datetime_duration_to_timedelta,
51
56
  get_now,
52
57
  )
@@ -817,7 +822,7 @@ class TestInfiniteQueueLooper:
817
822
  assert len(looper) == 0
818
823
  assert looper.empty()
819
824
  looper.put_right_nowait(*range(n))
820
- assert len(looper) == n
825
+ assert len(looper) == looper.qsize() == n
821
826
  assert not looper.empty()
822
827
 
823
828
  async def test_run_until_empty_no_stop(self) -> None:
@@ -872,6 +877,421 @@ class TestInfiniteQueueLooper:
872
877
  assert message == expected
873
878
 
874
879
 
880
+ class _ExampleLooperError(Exception): ...
881
+
882
+
883
+ @dataclass(kw_only=True)
884
+ class _ExampleLooper(Looper[Any]):
885
+ freq: Duration = field(default=10 * MILLISECOND, repr=False)
886
+ backoff: Duration = field(default=100 * MILLISECOND, repr=False)
887
+ _debug: bool = field(default=True, repr=False)
888
+ count: int = 0
889
+ max_count: int = 10
890
+
891
+ @override
892
+ async def _initialize_core(self) -> None:
893
+ self.count = 0
894
+
895
+ @override
896
+ async def core(self) -> None:
897
+ self.count += 1
898
+ if self.count >= self.max_count:
899
+ raise _ExampleLooperError
900
+
901
+
902
+ @dataclass(kw_only=True)
903
+ class _ExampleOuterLooper(_ExampleLooper):
904
+ inner: _ExampleLooper = field(init=False, repr=False)
905
+
906
+ @override
907
+ def __post_init__(self) -> None:
908
+ super().__post_init__()
909
+ self.inner = _ExampleLooper(
910
+ freq=self.freq / 2,
911
+ backoff=self.backoff / 2,
912
+ logger=self.logger,
913
+ timeout=self.timeout,
914
+ timeout_error=self.timeout_error,
915
+ max_count=round(self.max_count / 2),
916
+ )
917
+
918
+ @override
919
+ def _yield_sub_loopers(self) -> Iterator[Looper]:
920
+ yield self.inner
921
+
922
+
923
+ class TestLooper:
924
+ async def test_main_with_nothing(self) -> None:
925
+ looper = _ExampleLooper()
926
+ async with looper:
927
+ ...
928
+ assert looper.stats == _LooperStats(entries=1, stops=1)
929
+
930
+ @mark.flaky
931
+ async def test_main_with_explicit_start(self) -> None:
932
+ looper = _ExampleLooper()
933
+ with raises(TimeoutError):
934
+ async with timeout(1.0), looper:
935
+ await looper
936
+ self._assert_stats(looper, stops=1)
937
+
938
+ async def test_main_with_auto_start(self) -> None:
939
+ looper = _ExampleLooper(auto_start=True)
940
+ with raises(TimeoutError):
941
+ async with timeout(1.0), looper:
942
+ ...
943
+ self._assert_stats(looper)
944
+
945
+ @mark.flaky
946
+ async def test_main_with_timeout(self) -> None:
947
+ looper = _ExampleLooper(timeout=SECOND)
948
+ async with looper:
949
+ with raises(LooperTimeoutError, match="Timeout"):
950
+ await looper
951
+ self._assert_stats(looper, stops=1)
952
+
953
+ async def test_main_with_auto_start_and_timeout(self) -> None:
954
+ looper = _ExampleLooper(auto_start=True, timeout=SECOND)
955
+ async with looper:
956
+ ...
957
+ self._assert_stats(looper, stops=1)
958
+
959
+ async def test_await_without_task(self) -> None:
960
+ looper = _ExampleLooper()
961
+ with raises(_LooperNoTaskError, match=".* has no running task"):
962
+ await looper
963
+
964
+ async def test_context_manager_already_entered(
965
+ self, *, caplog: LogCaptureFixture
966
+ ) -> None:
967
+ looper = _ExampleLooper(auto_start=True, timeout=SECOND)
968
+ async with looper, looper:
969
+ ...
970
+ _ = one(m for m in caplog.messages if search(": already entered$", m))
971
+
972
+ def test_empty(self) -> None:
973
+ looper = _ExampleLooper()
974
+ assert looper.empty()
975
+ looper.put_left_nowait(None)
976
+ assert not looper.empty()
977
+
978
+ async def test_initialize_already_initializing(
979
+ self, *, caplog: LogCaptureFixture
980
+ ) -> None:
981
+ class Example(_ExampleLooper):
982
+ @override
983
+ async def _initialize_core(self) -> None:
984
+ if self._initialization_attempts == 1:
985
+ _ = await super().initialize()
986
+ await super()._initialize_core()
987
+
988
+ looper = Example()
989
+ _ = await looper.initialize()
990
+ _ = one(m for m in caplog.messages if search(": already initializing$", m))
991
+
992
+ async def test_initialize_failure(self, *, caplog: LogCaptureFixture) -> None:
993
+ class Example(_ExampleLooper):
994
+ @override
995
+ async def _initialize_core(self) -> None:
996
+ if self._initialization_attempts == 1:
997
+ raise _ExampleLooperError
998
+ await super()._initialize_core()
999
+
1000
+ looper = Example()
1001
+ _ = await looper.initialize()
1002
+ _ = one(
1003
+ m
1004
+ for m in caplog.messages
1005
+ if search(r": encountered _ExampleLooperError\(\) whilst initializing$", m)
1006
+ )
1007
+
1008
+ def test_len_and_qsize(self) -> None:
1009
+ looper = _ExampleLooper()
1010
+ assert len(looper) == looper.qsize() == 0
1011
+ looper.put_left_nowait(None)
1012
+ assert len(looper) == looper.qsize() == 1
1013
+
1014
+ def test_replace(self) -> None:
1015
+ looper = _ExampleLooper().replace(freq=SECOND)
1016
+ assert looper.freq == SECOND
1017
+
1018
+ async def test_request_restart(self) -> None:
1019
+ class Example(_ExampleLooper):
1020
+ @override
1021
+ async def core(self) -> None:
1022
+ await super().core()
1023
+ if (self._initialization_attempts >= 2) and (
1024
+ self.count >= (self.max_count / 2)
1025
+ ):
1026
+ self.request_restart()
1027
+
1028
+ looper = Example(auto_start=True, timeout=SECOND)
1029
+ async with looper:
1030
+ ...
1031
+ self._assert_stats(
1032
+ looper,
1033
+ core_successes=79,
1034
+ core_failures=1,
1035
+ initialization_successes=16,
1036
+ tear_down_successes=15,
1037
+ restart_successes=15,
1038
+ stops=1,
1039
+ )
1040
+
1041
+ def test_request_restart_already_requested(
1042
+ self, *, caplog: LogCaptureFixture
1043
+ ) -> None:
1044
+ looper = _ExampleLooper()
1045
+ for _ in range(2):
1046
+ looper.request_restart()
1047
+ _ = one(
1048
+ m for m in caplog.messages if search(r": already requested restart$", m)
1049
+ )
1050
+
1051
+ async def test_request_stop(self) -> None:
1052
+ class Example(_ExampleLooper):
1053
+ @override
1054
+ async def core(self) -> None:
1055
+ await super().core()
1056
+ if (self._initialization_attempts >= 2) and (
1057
+ self.count >= (self.max_count / 2)
1058
+ ):
1059
+ self.request_stop()
1060
+
1061
+ looper = Example(auto_start=True, timeout=SECOND)
1062
+ async with looper:
1063
+ ...
1064
+ self._assert_stats(
1065
+ looper,
1066
+ core_successes=14,
1067
+ core_failures=1,
1068
+ initialization_successes=2,
1069
+ tear_down_successes=1,
1070
+ restart_successes=1,
1071
+ stops=1,
1072
+ )
1073
+
1074
+ def test_request_stop_already_requested(self, *, caplog: LogCaptureFixture) -> None:
1075
+ looper = _ExampleLooper()
1076
+ for _ in range(2):
1077
+ looper.request_stop()
1078
+ _ = one(m for m in caplog.messages if search(r": already requested stop$", m))
1079
+
1080
+ async def test_request_stop_when_empty(self) -> None:
1081
+ class Example(_ExampleLooper):
1082
+ @override
1083
+ async def core(self) -> None:
1084
+ await super().core()
1085
+ if self.empty():
1086
+ await self.stop()
1087
+ match self.qsize() % 2 == 0:
1088
+ case True:
1089
+ _ = self.get_left_nowait()
1090
+ case False:
1091
+ _ = self.get_right_nowait()
1092
+ self.request_stop_when_empty()
1093
+
1094
+ looper = Example(auto_start=True, timeout=SECOND)
1095
+ for i in range(25):
1096
+ match i % 2 == 0:
1097
+ case True:
1098
+ looper.put_left_nowait(i)
1099
+ case False:
1100
+ looper.put_right_nowait(i)
1101
+ async with looper:
1102
+ ...
1103
+ self._assert_stats(
1104
+ looper,
1105
+ core_successes=25,
1106
+ core_failures=2,
1107
+ initialization_successes=3,
1108
+ tear_down_successes=2,
1109
+ restart_successes=2,
1110
+ stops=1,
1111
+ )
1112
+
1113
+ def test_request_stop_when_empty_already_requested(
1114
+ self, *, caplog: LogCaptureFixture
1115
+ ) -> None:
1116
+ looper = _ExampleLooper()
1117
+ for _ in range(2):
1118
+ looper.request_stop_when_empty()
1119
+ _ = one(
1120
+ m
1121
+ for m in caplog.messages
1122
+ if search(r": already requested stop when empty$", m)
1123
+ )
1124
+
1125
+ async def test_restart_failure_during_initialization(
1126
+ self, *, caplog: LogCaptureFixture
1127
+ ) -> None:
1128
+ class Example(_ExampleLooper):
1129
+ @override
1130
+ async def _initialize_core(self) -> None:
1131
+ if self._initialization_attempts == 1:
1132
+ raise _ExampleLooperError
1133
+ await super()._initialize_core()
1134
+
1135
+ looper = Example()
1136
+ await looper.restart()
1137
+ _ = one(
1138
+ m
1139
+ for m in caplog.messages
1140
+ if search(
1141
+ r": encountered _ExampleLooperError\(\) whilst restarting, during initialization$",
1142
+ m,
1143
+ )
1144
+ )
1145
+
1146
+ async def test_restart_failure_during_tear_down(
1147
+ self, *, caplog: LogCaptureFixture
1148
+ ) -> None:
1149
+ class Example(_ExampleLooper):
1150
+ @override
1151
+ async def _tear_down_core(self) -> None:
1152
+ if self._tear_down_attempts == 1:
1153
+ raise _ExampleLooperError
1154
+ await super()._tear_down_core()
1155
+
1156
+ looper = Example()
1157
+ await looper.restart()
1158
+ _ = one(
1159
+ m
1160
+ for m in caplog.messages
1161
+ if search(
1162
+ r": encountered _ExampleLooperError\(\) whilst restarting, during tear down$",
1163
+ m,
1164
+ )
1165
+ )
1166
+
1167
+ async def test_restart_failure_during_tear_down_and_initialization(
1168
+ self, *, caplog: LogCaptureFixture
1169
+ ) -> None:
1170
+ class Example(_ExampleLooper):
1171
+ @override
1172
+ async def _initialize_core(self) -> None:
1173
+ if self._initialization_attempts == 1:
1174
+ raise _ExampleLooperError
1175
+ await super()._initialize_core()
1176
+
1177
+ @override
1178
+ async def _tear_down_core(self) -> None:
1179
+ if self._tear_down_attempts == 1:
1180
+ raise _ExampleLooperError
1181
+ await super()._tear_down_core()
1182
+
1183
+ looper = Example()
1184
+ await looper.restart()
1185
+ _ = one(
1186
+ m
1187
+ for m in caplog.messages
1188
+ if search(
1189
+ r": encountered _ExampleLooperError\(\) \(tear down\) and then _ExampleLooperError\(\) \(initialization\) whilst restarting$",
1190
+ m,
1191
+ )
1192
+ )
1193
+
1194
+ async def test_sub_looper(self) -> None:
1195
+ looper = _ExampleOuterLooper(auto_start=True, timeout=SECOND)
1196
+ looper.inner.auto_start = True
1197
+ async with looper:
1198
+ ...
1199
+ self._assert_stats(looper, stops=1)
1200
+ self._assert_stats(
1201
+ looper.inner,
1202
+ core_successes=56,
1203
+ core_failures=13,
1204
+ initialization_successes=14,
1205
+ tear_down_successes=13,
1206
+ restart_successes=13,
1207
+ stops=1,
1208
+ )
1209
+
1210
+ async def test_sub_looper_changing_to_auto_start(
1211
+ self, *, caplog: LogCaptureFixture
1212
+ ) -> None:
1213
+ looper = _ExampleOuterLooper(auto_start=True, timeout=SECOND)
1214
+ async with looper:
1215
+ ...
1216
+ _ = one(
1217
+ m
1218
+ for m in caplog.messages
1219
+ if search(
1220
+ r": changing sub-looper _ExampleLooper\(.*?\) to auto-start...$", m
1221
+ )
1222
+ )
1223
+
1224
+ async def test_tear_down_already_initializing(
1225
+ self, *, caplog: LogCaptureFixture
1226
+ ) -> None:
1227
+ class Example(_ExampleLooper):
1228
+ @override
1229
+ async def _tear_down_core(self) -> None:
1230
+ if self._tear_down_attempts == 1:
1231
+ _ = await super().tear_down()
1232
+ await super()._tear_down_core()
1233
+
1234
+ looper = Example()
1235
+ _ = await looper.tear_down()
1236
+ _ = one(m for m in caplog.messages if search(": already tearing down$", m))
1237
+
1238
+ async def test_tear_down_failure(self, *, caplog: LogCaptureFixture) -> None:
1239
+ class Example(_ExampleLooper):
1240
+ @override
1241
+ async def _tear_down_core(self) -> None:
1242
+ if self._tear_down_attempts == 1:
1243
+ raise _ExampleLooperError
1244
+ await super()._tear_down_core()
1245
+
1246
+ looper = Example()
1247
+ _ = await looper.tear_down()
1248
+ _ = one(
1249
+ m
1250
+ for m in caplog.messages
1251
+ if search(r": encountered _ExampleLooperError\(\) whilst tearing down$", m)
1252
+ )
1253
+
1254
+ def _assert_stats(
1255
+ self,
1256
+ looper: _ExampleLooper,
1257
+ /,
1258
+ *,
1259
+ entries: int = 1,
1260
+ core_successes: int = 45,
1261
+ core_failures: int = 5,
1262
+ initialization_successes: int = 6,
1263
+ initialization_failures: int = 0,
1264
+ tear_down_successes: int = 5,
1265
+ tear_down_failures: int = 0,
1266
+ restart_successes: int = 5,
1267
+ restart_failures: int = 0,
1268
+ stops: int = 0,
1269
+ ) -> None:
1270
+ stats = looper.stats
1271
+ assert stats.entries == entries
1272
+ assert stats.core_attempts == (stats.core_successes + stats.core_failures)
1273
+ assert stats.core_successes == approx(core_successes, rel=0.5)
1274
+ assert stats.core_failures == approx(core_failures, rel=0.5)
1275
+ assert stats.initialization_attempts == (
1276
+ stats.initialization_successes + stats.initialization_failures
1277
+ )
1278
+ assert stats.initialization_successes == approx(
1279
+ initialization_successes, rel=0.5
1280
+ )
1281
+ assert stats.initialization_failures == approx(initialization_failures, rel=0.5)
1282
+ assert stats.tear_down_attempts == (
1283
+ stats.tear_down_successes + stats.tear_down_failures
1284
+ )
1285
+ assert stats.tear_down_successes == approx(tear_down_successes, rel=0.5)
1286
+ assert stats.tear_down_failures == approx(tear_down_failures, rel=0.5)
1287
+ assert stats.restart_attempts == (
1288
+ stats.restart_successes + stats.restart_failures
1289
+ )
1290
+ assert stats.restart_successes == approx(restart_successes, rel=0.5)
1291
+ assert stats.restart_failures == approx(restart_failures, rel=0.5)
1292
+ assert stats.stops == stops
1293
+
1294
+
875
1295
  class TestPutItems:
876
1296
  @given(xs=lists(integers(), min_size=1), wait=booleans())
877
1297
  async def test_main(self, *, xs: list[int], wait: bool) -> None:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.125.11"
3
+ __version__ = "0.125.12"