dycw-utilities 0.113.3__tar.gz → 0.113.5__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 (228) hide show
  1. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/PKG-INFO +7 -7
  2. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/pyproject.toml +13 -13
  3. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_asyncio.py +87 -1
  4. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_click.py +7 -7
  5. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/__init__.py +1 -1
  6. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/asyncio.py +73 -3
  7. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/click.py +6 -5
  8. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/redis.py +5 -5
  9. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/.gitignore +0 -0
  10. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/LICENSE +0 -0
  11. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/README.md +0 -0
  12. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/__init__.py +0 -0
  13. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/conftest.py +0 -0
  14. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/__init__.py +0 -0
  15. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_missing/__init__.py +0 -0
  16. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_missing/module.py +0 -0
  17. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/__init__.py +0 -0
  18. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/outer_1.py +0 -0
  19. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/outer_2.py +0 -0
  20. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  21. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  22. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  23. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  24. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_without/__init__.py +0 -0
  25. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_without/module_1.py +0 -0
  26. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/package_without/module_2.py +0 -0
  27. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/standalone.py +0 -0
  28. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/modules/with_imports.py +0 -0
  29. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  30. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  31. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  32. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  33. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  34. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  35. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  36. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  37. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/__init__.py +0 -0
  38. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/test_async_service/__init__.py +0 -0
  39. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/test_async_service/__main__.py +0 -0
  40. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/test_async_service/run.sh +0 -0
  41. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
  42. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
  43. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/scripts/test_queue_processor/run.sh +0 -0
  44. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_altair.py +0 -0
  45. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_astor.py +0 -0
  46. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_atomicwrites.py +0 -0
  47. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_atools.py +0 -0
  48. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_cachetools.py +0 -0
  49. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_concurrent.py +0 -0
  50. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_contextlib.py +0 -0
  51. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_contextvars.py +0 -0
  52. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_cryptography.py +0 -0
  53. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_cvxpy.py +0 -0
  54. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_dataclasses.py +0 -0
  55. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_datetime.py +0 -0
  56. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_enum.py +0 -0
  57. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_errors.py +0 -0
  58. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_eventkit.py +0 -0
  59. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_fastapi.py +0 -0
  60. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_fpdf2.py +0 -0
  61. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_functions.py +0 -0
  62. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_functools.py +0 -0
  63. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_getpass.py +0 -0
  64. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_git.py +0 -0
  65. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_hashlib.py +0 -0
  66. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_http.py +0 -0
  67. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_hypothesis.py +0 -0
  68. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_ipython.py +0 -0
  69. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_iterables.py +0 -0
  70. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_jupyter.py +0 -0
  71. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_lightweight_charts.py +0 -0
  72. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_logging.py +0 -0
  73. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_loguru.py +0 -0
  74. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_luigi.py +0 -0
  75. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_math.py +0 -0
  76. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_memory_profiler.py +0 -0
  77. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_modules.py +0 -0
  78. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_more_itertools.py +0 -0
  79. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_numpy.py +0 -0
  80. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_operator.py +0 -0
  81. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_optuna.py +0 -0
  82. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_orjson.py +0 -0
  83. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_os.py +0 -0
  84. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_parse.py +0 -0
  85. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pathlib.py +0 -0
  86. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_period.py +0 -0
  87. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pickle.py +0 -0
  88. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_platform.py +0 -0
  89. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_polars.py +0 -0
  90. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_polars_ols.py +0 -0
  91. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pqdm.py +0 -0
  92. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pydantic.py +0 -0
  93. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pyinstrument.py +0 -0
  94. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pyrsistent.py +0 -0
  95. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pytest.py +0 -0
  96. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_pytest_regressions.py +0 -0
  97. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_python_dotenv.py +0 -0
  98. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_random.py +0 -0
  99. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_re.py +0 -0
  100. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_redis.py +0 -0
  101. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_reprlib.py +0 -0
  102. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_rich.py +0 -0
  103. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_scipy.py +0 -0
  104. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_sentinel.py +0 -0
  105. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_shelve.py +0 -0
  106. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_slack_sdk.py +0 -0
  107. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_socket.py +0 -0
  108. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_sqlalchemy.py +0 -0
  109. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_sqlalchemy_polars.py +0 -0
  110. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_statsmodel.py +0 -0
  111. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_streamlit.py +0 -0
  112. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_sys.py +0 -0
  113. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_tempfile.py +0 -0
  114. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_tenacity.py +0 -0
  115. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_text.py +0 -0
  116. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_threading.py +0 -0
  117. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_timer.py +0 -0
  118. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback.py +0 -0
  119. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/__init__.py +0 -0
  120. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/chain.py +0 -0
  121. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  122. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  123. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  124. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/many.py +0 -0
  125. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/one.py +0 -0
  126. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/recursive.py +0 -0
  127. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  128. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  129. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/two.py +0 -0
  130. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_traceback_funcs/untraced.py +0 -0
  131. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_types.py +0 -0
  132. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_typing.py +0 -0
  133. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_typing_funcs/__init__.py +0 -0
  134. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_typing_funcs/no_future.py +0 -0
  135. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_typing_funcs/with_future.py +0 -0
  136. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_tzdata.py +0 -0
  137. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_tzlocal.py +0 -0
  138. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_uuid.py +0 -0
  139. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_version.py +0 -0
  140. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_warnings.py +0 -0
  141. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_whenever.py +0 -0
  142. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_zipfile.py +0 -0
  143. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/tests/test_zoneinfo.py +0 -0
  144. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/altair.py +0 -0
  145. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/astor.py +0 -0
  146. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/atomicwrites.py +0 -0
  147. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/atools.py +0 -0
  148. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/cachetools.py +0 -0
  149. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/concurrent.py +0 -0
  150. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/contextlib.py +0 -0
  151. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/contextvars.py +0 -0
  152. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/cryptography.py +0 -0
  153. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/cvxpy.py +0 -0
  154. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/dataclasses.py +0 -0
  155. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/datetime.py +0 -0
  156. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/enum.py +0 -0
  157. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/errors.py +0 -0
  158. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/eventkit.py +0 -0
  159. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/fastapi.py +0 -0
  160. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/fpdf2.py +0 -0
  161. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/functions.py +0 -0
  162. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/functools.py +0 -0
  163. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/getpass.py +0 -0
  164. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/git.py +0 -0
  165. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/hashlib.py +0 -0
  166. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/http.py +0 -0
  167. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/hypothesis.py +0 -0
  168. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/ipython.py +0 -0
  169. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/iterables.py +0 -0
  170. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/jupyter.py +0 -0
  171. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/lightweight_charts.py +0 -0
  172. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/logging.py +0 -0
  173. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/loguru.py +0 -0
  174. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/luigi.py +0 -0
  175. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/math.py +0 -0
  176. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/memory_profiler.py +0 -0
  177. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/modules.py +0 -0
  178. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/more_itertools.py +0 -0
  179. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/numpy.py +0 -0
  180. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/operator.py +0 -0
  181. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/optuna.py +0 -0
  182. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/orjson.py +0 -0
  183. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/os.py +0 -0
  184. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/parse.py +0 -0
  185. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pathlib.py +0 -0
  186. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/period.py +0 -0
  187. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pickle.py +0 -0
  188. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/platform.py +0 -0
  189. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/polars.py +0 -0
  190. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/polars_ols.py +0 -0
  191. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pqdm.py +0 -0
  192. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/py.typed +0 -0
  193. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pydantic.py +0 -0
  194. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pyinstrument.py +0 -0
  195. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pyrsistent.py +0 -0
  196. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pytest.py +0 -0
  197. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/pytest_regressions.py +0 -0
  198. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/python_dotenv.py +0 -0
  199. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/random.py +0 -0
  200. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/re.py +0 -0
  201. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/reprlib.py +0 -0
  202. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/rich.py +0 -0
  203. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/scipy.py +0 -0
  204. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/sentinel.py +0 -0
  205. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/shelve.py +0 -0
  206. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/slack_sdk.py +0 -0
  207. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/socket.py +0 -0
  208. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/sqlalchemy.py +0 -0
  209. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/sqlalchemy_polars.py +0 -0
  210. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/statsmodels.py +0 -0
  211. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/streamlit.py +0 -0
  212. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/sys.py +0 -0
  213. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/tempfile.py +0 -0
  214. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/tenacity.py +0 -0
  215. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/text.py +0 -0
  216. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/threading.py +0 -0
  217. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/timer.py +0 -0
  218. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/traceback.py +0 -0
  219. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/types.py +0 -0
  220. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/typing.py +0 -0
  221. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/tzdata.py +0 -0
  222. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/tzlocal.py +0 -0
  223. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/uuid.py +0 -0
  224. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/version.py +0 -0
  225. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/warnings.py +0 -0
  226. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/whenever.py +0 -0
  227. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/zipfile.py +0 -0
  228. {dycw_utilities-0.113.3 → dycw_utilities-0.113.5}/src/utilities/zoneinfo.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.113.3
3
+ Version: 0.113.5
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
7
  Requires-Dist: typing-extensions<4.14,>=4.13.1
8
8
  Provides-Extra: test
9
- Requires-Dist: hypothesis<6.132,>=6.131.14; extra == 'test'
9
+ Requires-Dist: hypothesis<6.132,>=6.131.15; extra == 'test'
10
10
  Requires-Dist: pytest-asyncio<0.27,>=0.26.0; extra == 'test'
11
11
  Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
12
12
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
@@ -14,7 +14,7 @@ Requires-Dist: pytest-lazy-fixtures<1.2,>=1.1.2; extra == 'test'
14
14
  Requires-Dist: pytest-only<2.2,>=2.1.2; extra == 'test'
15
15
  Requires-Dist: pytest-randomly<3.17,>=3.16.0; extra == 'test'
16
16
  Requires-Dist: pytest-regressions<2.8,>=2.7.0; extra == 'test'
17
- Requires-Dist: pytest-rerunfailures<16,>=15.0; extra == 'test'
17
+ Requires-Dist: pytest-rerunfailures<16,>=15.1; extra == 'test'
18
18
  Requires-Dist: pytest-rng<1.1,>=1.0.0; extra == 'test'
19
19
  Requires-Dist: pytest-timeout<2.5,>=2.4.0; extra == 'test'
20
20
  Requires-Dist: pytest-xdist<3.7,>=3.6.1; extra == 'test'
@@ -37,7 +37,7 @@ Requires-Dist: atools<0.15,>=0.14.2; extra == 'zzz-test-atools'
37
37
  Provides-Extra: zzz-test-cachetools
38
38
  Requires-Dist: cachetools<5.6,>=5.5.2; extra == 'zzz-test-cachetools'
39
39
  Provides-Extra: zzz-test-click
40
- Requires-Dist: click<8.2,>=8.1.8; extra == 'zzz-test-click'
40
+ Requires-Dist: click<8.3,>=8.2.0; extra == 'zzz-test-click'
41
41
  Requires-Dist: sqlalchemy<2.1,>=2.0.40; extra == 'zzz-test-click'
42
42
  Requires-Dist: whenever<0.8,>=0.7.3; extra == 'zzz-test-click'
43
43
  Provides-Extra: zzz-test-contextlib
@@ -80,7 +80,7 @@ Provides-Extra: zzz-test-hypothesis
80
80
  Requires-Dist: aiosqlite<0.22,>=0.21.0; extra == 'zzz-test-hypothesis'
81
81
  Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
82
82
  Requires-Dist: greenlet<3.3,>=3.2.0; extra == 'zzz-test-hypothesis'
83
- Requires-Dist: hypothesis<6.132,>=6.131.14; extra == 'zzz-test-hypothesis'
83
+ Requires-Dist: hypothesis<6.132,>=6.131.15; extra == 'zzz-test-hypothesis'
84
84
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
85
85
  Requires-Dist: numpy<2.3,>=2.2.5; extra == 'zzz-test-hypothesis'
86
86
  Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
@@ -101,7 +101,7 @@ Requires-Dist: polars-lts-cpu<1.30,>=1.29.0; extra == 'zzz-test-jupyter'
101
101
  Provides-Extra: zzz-test-logging
102
102
  Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-logging'
103
103
  Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'zzz-test-logging'
104
- Requires-Dist: concurrent-log-handler<0.10,>=0.9.25; extra == 'zzz-test-logging'
104
+ Requires-Dist: concurrent-log-handler<0.10,>=0.9.26; extra == 'zzz-test-logging'
105
105
  Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-logging'
106
106
  Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-logging'
107
107
  Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-logging'
@@ -172,7 +172,7 @@ Requires-Dist: whenever<0.8,>=0.7.3; extra == 'zzz-test-redis'
172
172
  Provides-Extra: zzz-test-rich
173
173
  Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-rich'
174
174
  Provides-Extra: zzz-test-scipy
175
- Requires-Dist: scipy<1.16,>=1.15.2; extra == 'zzz-test-scipy'
175
+ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
176
176
  Provides-Extra: zzz-test-sentinel
177
177
  Provides-Extra: zzz-test-shelve
178
178
  Provides-Extra: zzz-test-slack-sdk
@@ -16,9 +16,9 @@ dev = [
16
16
  "atomicwrites >= 1.4.1, < 1.5",
17
17
  "atools >= 0.14.2, < 0.15",
18
18
  "cachetools >= 5.5.2, < 5.6",
19
- "click >= 8.1.8, < 8.2",
19
+ "click >= 8.2.0, < 8.3",
20
20
  "coloredlogs >= 15.0.1, < 15.1",
21
- "concurrent-log-handler >= 0.9.25, < 0.10",
21
+ "concurrent-log-handler >= 0.9.26, < 0.10",
22
22
  "cryptography >= 44.0.2, < 44.1",
23
23
  "cvxpy >= 1.6.5, < 1.7",
24
24
  "eventkit >= 1.0.3, < 1.1",
@@ -26,7 +26,7 @@ dev = [
26
26
  "fpdf2 >= 2.8.3, < 2.9",
27
27
  "greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
28
28
  "httpx >= 0.28.1, < 0.29", # for fastapi
29
- "hypothesis >= 6.131.14, < 6.132",
29
+ "hypothesis >= 6.131.15, < 6.132",
30
30
  "img2pdf >= 0.6.0, < 0.7",
31
31
  "lightweight-charts >= 2.1, < 2.2",
32
32
  "loguru >= 0.7.3, < 0.8",
@@ -52,7 +52,7 @@ dev = [
52
52
  "redis >= 6.0.0, < 6.1",
53
53
  "rich >= 14.0.0, < 14.1",
54
54
  "scikit-learn >= 1.6.1, < 1.7",
55
- "scipy >= 1.15.2, < 1.16",
55
+ "scipy >= 1.15.3, < 1.16",
56
56
  "slack-sdk >= 3.35.0, < 3.36",
57
57
  "sqlalchemy >= 2.0.40, < 2.1",
58
58
  "statsmodels >= 0.14.4, < 0.15",
@@ -74,7 +74,7 @@ dev = [
74
74
  "pytest-lazy-fixtures >= 1.1.2, < 1.2",
75
75
  "pytest-only >= 2.1.2, < 2.2",
76
76
  "pytest-randomly >= 3.16.0, < 3.17",
77
- "pytest-rerunfailures >= 15.0, < 16",
77
+ "pytest-rerunfailures >= 15.1, < 16",
78
78
  "pytest-rng >= 1.0.0, < 1.1",
79
79
  "pytest-timeout >= 2.4.0, < 2.5",
80
80
  "pytest-xdist >= 3.6.1, < 3.7",
@@ -92,11 +92,11 @@ dependencies = [
92
92
  name = "dycw-utilities"
93
93
  readme = "README.md"
94
94
  requires-python = ">= 3.12"
95
- version = "0.113.3"
95
+ version = "0.113.5"
96
96
 
97
97
  [project.optional-dependencies]
98
98
  test = [
99
- "hypothesis >= 6.131.14, < 6.132",
99
+ "hypothesis >= 6.131.15, < 6.132",
100
100
  "pytest >= 8.3.5, < 8.4",
101
101
  "pytest-asyncio >= 0.26.0, < 0.27",
102
102
  "pytest-cov >= 6.1.1, < 6.2",
@@ -105,7 +105,7 @@ test = [
105
105
  "pytest-only >= 2.1.2, < 2.2",
106
106
  "pytest-randomly >= 3.16.0, < 3.17",
107
107
  "pytest-regressions >= 2.7.0, < 2.8",
108
- "pytest-rerunfailures >= 15.0, < 16",
108
+ "pytest-rerunfailures >= 15.1, < 16",
109
109
  "pytest-rng >= 1.0.0, < 1.1",
110
110
  "pytest-timeout >= 2.4.0, < 2.5",
111
111
  "pytest-xdist >= 3.6.1, < 3.7",
@@ -127,7 +127,7 @@ zzz-test-atools = [
127
127
  ]
128
128
  zzz-test-cachetools = ["cachetools >= 5.5.2, < 5.6"]
129
129
  zzz-test-click = [
130
- "click >= 8.1.8, < 8.2",
130
+ "click >= 8.2.0, < 8.3",
131
131
  "sqlalchemy >= 2.0.40, < 2.1",
132
132
  "whenever >= 0.7.3, < 0.8",
133
133
  ]
@@ -174,7 +174,7 @@ zzz-test-hypothesis = [
174
174
  "aiosqlite >= 0.21.0, < 0.22",
175
175
  "asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
176
176
  "greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
177
- "hypothesis >= 6.131.14, < 6.132",
177
+ "hypothesis >= 6.131.15, < 6.132",
178
178
  "luigi >= 3.6.0, < 3.7",
179
179
  "numpy >= 2.2.5, < 2.3",
180
180
  "pathvalidate >= 3.2.3, < 3.3",
@@ -197,7 +197,7 @@ zzz-test-jupyter = [
197
197
  zzz-test-logging = [
198
198
  "atomicwrites >= 1.4.1, < 1.5",
199
199
  "coloredlogs >= 15.0.1, < 15.1",
200
- "concurrent-log-handler >= 0.9.25, < 0.10",
200
+ "concurrent-log-handler >= 0.9.26, < 0.10",
201
201
  "rich >= 14.0.0, < 14.1",
202
202
  "tomlkit >= 0.13.2, < 0.14",
203
203
  "tzlocal >= 5.3.1, < 5.4",
@@ -266,7 +266,7 @@ zzz-test-redis = [
266
266
  "whenever >= 0.7.3, < 0.8",
267
267
  ]
268
268
  zzz-test-rich = ["rich >= 14.0.0, < 14.1"]
269
- zzz-test-scipy = ["scipy >= 1.15.2, < 1.16"]
269
+ 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 = [
@@ -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.113.3"
337
+ current_version = "0.113.5"
338
338
 
339
339
  [[tool.bumpversion.files]]
340
340
  filename = "src/utilities/__init__.py"
@@ -35,6 +35,7 @@ from utilities.asyncio import (
35
35
  AsyncService,
36
36
  EnhancedTaskGroup,
37
37
  ExceptionProcessor,
38
+ InfiniteLooper,
38
39
  QueueProcessor,
39
40
  UniquePriorityQueue,
40
41
  UniqueQueue,
@@ -54,7 +55,9 @@ from utilities.sentinel import Sentinel, sentinel
54
55
  from utilities.timer import Timer
55
56
 
56
57
  if TYPE_CHECKING:
57
- from utilities.types import Duration, MaybeCallableEvent
58
+ from collections.abc import Mapping
59
+
60
+ from utilities.types import Duration, MaybeCallableEvent, MaybeType
58
61
 
59
62
 
60
63
  class TestAsyncLoopingService:
@@ -401,6 +404,89 @@ class TestGetItemsNoWait:
401
404
  assert result == xs[:max_size]
402
405
 
403
406
 
407
+ class TestInfiniteLooper:
408
+ @given(n=integers(10, 11))
409
+ async def test_main(self, *, n: int) -> None:
410
+ class CustomTrueError(BaseException): ...
411
+
412
+ class CustomFalseError(BaseException): ...
413
+
414
+ @dataclass(kw_only=True)
415
+ class Example(InfiniteLooper[bool]):
416
+ counter: int = field(init=False, repr=False)
417
+
418
+ @override
419
+ async def initialize(self) -> None:
420
+ self.counter = 0
421
+
422
+ @override
423
+ async def core(self) -> None:
424
+ self.counter += 1
425
+ if self.counter >= n:
426
+ self.events[n % 2 == 0].set()
427
+
428
+ @property
429
+ @override
430
+ def events_and_exceptions(self) -> Mapping[bool, MaybeType[BaseException]]:
431
+ return {True: CustomTrueError, False: CustomFalseError}
432
+
433
+ looper = Example(sleep_core=0.1)
434
+ match n % 2 == 0:
435
+ case True:
436
+ with raises(CustomTrueError):
437
+ _ = await looper()
438
+ case False:
439
+ with raises(CustomFalseError):
440
+ _ = await looper()
441
+
442
+ async def test_error_upon_initialize(self) -> None:
443
+ class CustomError(Exception): ...
444
+
445
+ @dataclass(kw_only=True)
446
+ class Example(InfiniteLooper[None]):
447
+ counter: int = field(init=False, repr=False)
448
+
449
+ @override
450
+ async def initialize(self) -> None:
451
+ raise CustomError
452
+
453
+ @override
454
+ async def core(self) -> None:
455
+ raise NotImplementedError
456
+
457
+ @property
458
+ @override
459
+ def events_and_exceptions(self) -> Mapping[None, MaybeType[BaseException]]:
460
+ return {None: CustomError}
461
+
462
+ with raises(TimeoutError):
463
+ async with timeout_dur(duration=0.5):
464
+ _ = await Example(sleep_core=0.1)()
465
+
466
+ async def test_error_upon_core(self) -> None:
467
+ class CustomError(Exception): ...
468
+
469
+ @dataclass(kw_only=True)
470
+ class Example(InfiniteLooper[None]):
471
+ counter: int = field(init=False, repr=False)
472
+
473
+ @override
474
+ async def initialize(self) -> None: ...
475
+
476
+ @override
477
+ async def core(self) -> None:
478
+ raise CustomError
479
+
480
+ @property
481
+ @override
482
+ def events_and_exceptions(self) -> Mapping[None, MaybeType[BaseException]]:
483
+ return {None: CustomError}
484
+
485
+ with raises(TimeoutError):
486
+ async with timeout_dur(duration=0.5):
487
+ _ = await Example(sleep_core=0.1)()
488
+
489
+
404
490
  class TestQueueProcessor:
405
491
  async def test_one_processor_slow_tasks(self) -> None:
406
492
  @dataclass(kw_only=True)
@@ -102,12 +102,12 @@ class TestFileAndDirPaths:
102
102
  file_path.touch()
103
103
  result = CliRunner().invoke(cli, [str(file_path)])
104
104
  assert result.exit_code == 2
105
- assert search("is a file", result.stdout)
105
+ assert search("is a file", result.stderr)
106
106
 
107
107
  non_existent = tmp_path.joinpath("non-existent")
108
108
  result = CliRunner().invoke(cli, [str(non_existent)])
109
109
  assert result.exit_code == 2
110
- assert search("does not exist", result.stdout)
110
+ assert search("does not exist", result.stderr)
111
111
 
112
112
  def test_existing_file_path(self, *, tmp_path: Path) -> None:
113
113
  @command()
@@ -119,7 +119,7 @@ class TestFileAndDirPaths:
119
119
 
120
120
  result = CliRunner().invoke(cli, [str(tmp_path)])
121
121
  assert result.exit_code == 2
122
- assert search("is a directory", result.stdout)
122
+ assert search("is a directory", result.stderr)
123
123
 
124
124
  file_path = tmp_path.joinpath("file.txt")
125
125
  file_path.touch()
@@ -129,7 +129,7 @@ class TestFileAndDirPaths:
129
129
  non_existent = tmp_path.joinpath("non-existent")
130
130
  result = CliRunner().invoke(cli, [str(non_existent)])
131
131
  assert result.exit_code == 2
132
- assert search("does not exist", result.stdout)
132
+ assert search("does not exist", result.stderr)
133
133
 
134
134
  def test_dir_path(self, *, tmp_path: Path) -> None:
135
135
  @command()
@@ -146,7 +146,7 @@ class TestFileAndDirPaths:
146
146
  file_path.touch()
147
147
  result = CliRunner().invoke(cli, [str(file_path)])
148
148
  assert result.exit_code == 2
149
- assert search("is a file", result.stdout)
149
+ assert search("is a file", result.stderr)
150
150
 
151
151
  non_existent = tmp_path.joinpath("non-existent")
152
152
  result = CliRunner().invoke(cli, [str(non_existent)])
@@ -162,7 +162,7 @@ class TestFileAndDirPaths:
162
162
 
163
163
  result = CliRunner().invoke(cli, [str(tmp_path)])
164
164
  assert result.exit_code == 2
165
- assert search("is a directory", result.stdout)
165
+ assert search("is a directory", result.stderr)
166
166
 
167
167
  file_path = tmp_path.joinpath("file.txt")
168
168
  file_path.touch()
@@ -394,7 +394,7 @@ class TestParameters:
394
394
  assert result.exit_code == 2
395
395
  assert search(
396
396
  "Invalid value for '--value': Object '0' of type 'int' must be a string",
397
- result.stdout,
397
+ result.stderr,
398
398
  )
399
399
 
400
400
 
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.113.3"
3
+ __version__ = "0.113.5"
@@ -17,7 +17,7 @@ from asyncio import (
17
17
  sleep,
18
18
  timeout,
19
19
  )
20
- from collections.abc import Callable
20
+ from collections.abc import Callable, Mapping
21
21
  from contextlib import (
22
22
  AsyncExitStack,
23
23
  _AsyncGeneratorContextManager,
@@ -39,11 +39,17 @@ from typing import (
39
39
  override,
40
40
  )
41
41
 
42
- from utilities.datetime import MILLISECOND, datetime_duration_to_float
42
+ from utilities.datetime import MILLISECOND, MINUTE, SECOND, datetime_duration_to_float
43
43
  from utilities.errors import ImpossibleCaseError
44
44
  from utilities.functions import ensure_int, ensure_not_none
45
45
  from utilities.sentinel import Sentinel, sentinel
46
- from utilities.types import MaybeCallableEvent, THashable, TSupportsRichComparison
46
+ from utilities.types import (
47
+ Coroutine1,
48
+ MaybeCallableEvent,
49
+ MaybeType,
50
+ THashable,
51
+ TSupportsRichComparison,
52
+ )
47
53
 
48
54
  if TYPE_CHECKING:
49
55
  from asyncio import _CoroutineLike
@@ -320,6 +326,70 @@ class ExceptionProcessor(QueueProcessor[Exception | type[Exception]]):
320
326
  ##
321
327
 
322
328
 
329
+ @dataclass(kw_only=True)
330
+ class InfiniteLooper(ABC, Generic[THashable]):
331
+ """An infinite loop which can throw exceptions by setting events."""
332
+
333
+ events: Mapping[THashable, Event] = field(
334
+ default_factory=dict, init=False, repr=False
335
+ )
336
+ sleep_core: Duration = SECOND
337
+ sleep_restart: Duration = MINUTE
338
+
339
+ def __post_init__(self) -> None:
340
+ self._reset_events()
341
+
342
+ async def __call__(self) -> Coroutine1[None]:
343
+ while True:
344
+ try:
345
+ self._reset_events()
346
+ try:
347
+ await self.initialize()
348
+ except Exception as error: # noqa: BLE001
349
+ self.error_upon_initialize(error)
350
+ await sleep_dur(duration=self.sleep_restart)
351
+ else:
352
+ while True:
353
+ try:
354
+ event = next(
355
+ key
356
+ for (key, value) in self.events.items()
357
+ if value.is_set()
358
+ )
359
+ except StopIteration:
360
+ await self.core()
361
+ await sleep_dur(duration=self.sleep_core)
362
+ else:
363
+ raise self.events_and_exceptions[event]
364
+ except Exception as error: # noqa: BLE001
365
+ self.error_upon_core(error)
366
+ await sleep_dur(duration=self.sleep_restart)
367
+
368
+ @property
369
+ @abstractmethod
370
+ def events_and_exceptions(self) -> Mapping[THashable, MaybeType[BaseException]]:
371
+ """A mapping of events to exceptions."""
372
+
373
+ async def initialize(self) -> None:
374
+ """Initialize the loop."""
375
+
376
+ async def core(self) -> None:
377
+ """Run the core."""
378
+
379
+ def error_upon_initialize(self, error: Exception, /) -> None:
380
+ _ = error
381
+
382
+ def error_upon_core(self, error: Exception, /) -> None:
383
+ _ = error
384
+
385
+ def _reset_events(self) -> None:
386
+ """Reset the events."""
387
+ self.events = {event: Event() for event in self.events_and_exceptions}
388
+
389
+
390
+ ##
391
+
392
+
323
393
  class UniquePriorityQueue(PriorityQueue[tuple[TSupportsRichComparison, THashable]]):
324
394
  """Priority queue with unique tasks."""
325
395
 
@@ -125,7 +125,8 @@ class Enum(ParamType, Generic[TEnum]):
125
125
  self.fail(str(error), param, ctx)
126
126
 
127
127
  @override
128
- def get_metavar(self, param: Parameter) -> str | None:
128
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
129
+ _ = ctx
129
130
  desc = ",".join(e.name for e in self._enum)
130
131
  return _make_metavar(param, desc)
131
132
 
@@ -273,8 +274,8 @@ class FrozenSetParameter(ParamType, Generic[_TParam, _T]):
273
274
  return frozenset(self._param.convert(v, param, ctx) for v in values)
274
275
 
275
276
  @override
276
- def get_metavar(self, param: Parameter) -> str | None:
277
- if (metavar := self._param.get_metavar(param)) is None:
277
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
278
+ if (metavar := self._param.get_metavar(param, ctx)) is None:
278
279
  name = self.name.upper()
279
280
  else:
280
281
  name = f"FROZENSET{metavar}"
@@ -397,8 +398,8 @@ class ListParameter(ParamType, Generic[_TParam, _T]):
397
398
  return [self._param.convert(v, param, ctx) for v in values]
398
399
 
399
400
  @override
400
- def get_metavar(self, param: Parameter) -> str | None:
401
- if (metavar := self._param.get_metavar(param)) is None:
401
+ def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
402
+ if (metavar := self._param.get_metavar(param, ctx)) is None:
402
403
  name = self.name.upper()
403
404
  else:
404
405
  name = f"LIST{metavar}"
@@ -607,7 +607,7 @@ class Publisher(QueueProcessor[tuple[str, EncodableT]]):
607
607
 
608
608
 
609
609
  _SUBSCRIBE_TIMEOUT: Duration = SECOND
610
- _SUBSCRIBE_SLEEP: Duration = 10 * MILLISECOND
610
+ _SUBSCRIBE_SLEEP: Duration = MILLISECOND
611
611
 
612
612
 
613
613
  @overload
@@ -617,8 +617,8 @@ def subscribe(
617
617
  /,
618
618
  *,
619
619
  deserializer: Callable[[bytes], _T],
620
- timeout: Duration | None = ...,
621
- sleep: Duration = ...,
620
+ timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
621
+ sleep: Duration = _SUBSCRIBE_SLEEP,
622
622
  ) -> AsyncIterator[_T]: ...
623
623
  @overload
624
624
  def subscribe(
@@ -627,8 +627,8 @@ def subscribe(
627
627
  /,
628
628
  *,
629
629
  deserializer: None = None,
630
- timeout: Duration | None = ...,
631
- sleep: Duration = ...,
630
+ timeout: Duration | None = _SUBSCRIBE_TIMEOUT,
631
+ sleep: Duration = _SUBSCRIBE_SLEEP,
632
632
  ) -> AsyncIterator[bytes]: ...
633
633
  async def subscribe(
634
634
  pubsub: PubSub,