dycw-utilities 0.117.0__tar.gz → 0.118.0__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 (231) hide show
  1. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/PKG-INFO +1 -1
  2. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/pyproject.toml +2 -2
  3. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_asyncio.py +60 -398
  4. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_fastapi.py +11 -3
  5. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_redis.py +0 -75
  6. dycw_utilities-0.118.0/src/tests/test_slack_sdk.py +83 -0
  7. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sqlalchemy.py +0 -30
  8. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/__init__.py +1 -1
  9. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/asyncio.py +40 -234
  10. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/fastapi.py +3 -8
  11. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/redis.py +1 -18
  12. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/slack_sdk.py +2 -68
  13. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sqlalchemy.py +1 -44
  14. dycw_utilities-0.117.0/src/tests/scripts/test_async_service/__init__.py +0 -45
  15. dycw_utilities-0.117.0/src/tests/scripts/test_async_service/__main__.py +0 -6
  16. dycw_utilities-0.117.0/src/tests/scripts/test_async_service/run.sh +0 -3
  17. dycw_utilities-0.117.0/src/tests/scripts/test_queue_processor/__init__.py +0 -51
  18. dycw_utilities-0.117.0/src/tests/scripts/test_queue_processor/__main__.py +0 -6
  19. dycw_utilities-0.117.0/src/tests/scripts/test_queue_processor/run.sh +0 -3
  20. dycw_utilities-0.117.0/src/tests/test_slack_sdk.py +0 -190
  21. dycw_utilities-0.117.0/src/tests/test_traceback_funcs/__init__.py +0 -1
  22. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/.gitignore +0 -0
  23. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/LICENSE +0 -0
  24. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/README.md +0 -0
  25. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/__init__.py +0 -0
  26. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/conftest.py +0 -0
  27. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/__init__.py +0 -0
  28. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_missing/__init__.py +0 -0
  29. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_missing/module.py +0 -0
  30. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/__init__.py +0 -0
  31. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/outer_1.py +0 -0
  32. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/outer_2.py +0 -0
  33. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  34. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  35. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  36. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  37. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_without/__init__.py +0 -0
  38. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_without/module_1.py +0 -0
  39. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_without/module_2.py +0 -0
  40. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/standalone.py +0 -0
  41. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/with_imports.py +0 -0
  42. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  43. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  44. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  45. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  46. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  47. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  48. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  49. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  50. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_altair.py +0 -0
  51. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_astor.py +0 -0
  52. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_atomicwrites.py +0 -0
  53. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_atools.py +0 -0
  54. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_cachetools.py +0 -0
  55. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_click.py +0 -0
  56. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_concurrent.py +0 -0
  57. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_contextlib.py +0 -0
  58. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_contextvars.py +0 -0
  59. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_cryptography.py +0 -0
  60. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_cvxpy.py +0 -0
  61. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_dataclasses.py +0 -0
  62. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_datetime.py +0 -0
  63. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_enum.py +0 -0
  64. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_errors.py +0 -0
  65. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_eventkit.py +0 -0
  66. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_fpdf2.py +0 -0
  67. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_functions.py +0 -0
  68. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_functools.py +0 -0
  69. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_getpass.py +0 -0
  70. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_git.py +0 -0
  71. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_hashlib.py +0 -0
  72. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_http.py +0 -0
  73. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_hypothesis.py +0 -0
  74. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_importlib.py +0 -0
  75. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_ipython.py +0 -0
  76. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_iterables.py +0 -0
  77. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_jupyter.py +0 -0
  78. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_lightweight_charts.py +0 -0
  79. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_logging.py +0 -0
  80. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_loguru.py +0 -0
  81. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_luigi.py +0 -0
  82. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_math.py +0 -0
  83. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_memory_profiler.py +0 -0
  84. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_modules.py +0 -0
  85. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_more_itertools.py +0 -0
  86. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_numpy.py +0 -0
  87. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_operator.py +0 -0
  88. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_optuna.py +0 -0
  89. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_orjson.py +0 -0
  90. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_os.py +0 -0
  91. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_parse.py +0 -0
  92. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pathlib.py +0 -0
  93. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_period.py +0 -0
  94. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pickle.py +0 -0
  95. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_platform.py +0 -0
  96. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_polars.py +0 -0
  97. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_polars_ols.py +0 -0
  98. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pqdm.py +0 -0
  99. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pydantic.py +0 -0
  100. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pyinstrument.py +0 -0
  101. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pyrsistent.py +0 -0
  102. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pytest.py +0 -0
  103. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pytest_regressions.py +0 -0
  104. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_python_dotenv.py +0 -0
  105. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_random.py +0 -0
  106. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_re.py +0 -0
  107. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_reprlib.py +0 -0
  108. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_rich.py +0 -0
  109. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_scipy.py +0 -0
  110. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sentinel.py +0 -0
  111. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_shelve.py +0 -0
  112. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_socket.py +0 -0
  113. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sqlalchemy_polars.py +0 -0
  114. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_statsmodel.py +0 -0
  115. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_streamlit.py +0 -0
  116. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sys.py +0 -0
  117. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tempfile.py +0 -0
  118. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tenacity.py +0 -0
  119. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_text.py +0 -0
  120. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_threading.py +0 -0
  121. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_timer.py +0 -0
  122. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback.py +0 -0
  123. {dycw_utilities-0.117.0/src/tests/scripts → dycw_utilities-0.118.0/src/tests/test_traceback_funcs}/__init__.py +0 -0
  124. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/chain.py +0 -0
  125. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  126. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  127. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  128. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/many.py +0 -0
  129. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/one.py +0 -0
  130. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/recursive.py +0 -0
  131. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  132. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  133. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/two.py +0 -0
  134. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/untraced.py +0 -0
  135. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_types.py +0 -0
  136. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing.py +0 -0
  137. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/__init__.py +0 -0
  138. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/no_future.py +0 -0
  139. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/with_future.py +0 -0
  140. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tzdata.py +0 -0
  141. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tzlocal.py +0 -0
  142. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_uuid.py +0 -0
  143. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_version.py +0 -0
  144. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_warnings.py +0 -0
  145. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_whenever.py +0 -0
  146. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_zipfile.py +0 -0
  147. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_zoneinfo.py +0 -0
  148. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/altair.py +0 -0
  149. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/astor.py +0 -0
  150. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/atomicwrites.py +0 -0
  151. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/atools.py +0 -0
  152. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/cachetools.py +0 -0
  153. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/click.py +0 -0
  154. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/concurrent.py +0 -0
  155. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/contextlib.py +0 -0
  156. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/contextvars.py +0 -0
  157. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/cryptography.py +0 -0
  158. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/cvxpy.py +0 -0
  159. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/dataclasses.py +0 -0
  160. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/datetime.py +0 -0
  161. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/enum.py +0 -0
  162. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/errors.py +0 -0
  163. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/eventkit.py +0 -0
  164. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/fpdf2.py +0 -0
  165. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/functions.py +0 -0
  166. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/functools.py +0 -0
  167. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/getpass.py +0 -0
  168. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/git.py +0 -0
  169. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/hashlib.py +0 -0
  170. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/http.py +0 -0
  171. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/hypothesis.py +0 -0
  172. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/importlib.py +0 -0
  173. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/ipython.py +0 -0
  174. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/iterables.py +0 -0
  175. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/jupyter.py +0 -0
  176. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/lightweight_charts.py +0 -0
  177. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/logging.py +0 -0
  178. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/loguru.py +0 -0
  179. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/luigi.py +0 -0
  180. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/math.py +0 -0
  181. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/memory_profiler.py +0 -0
  182. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/modules.py +0 -0
  183. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/more_itertools.py +0 -0
  184. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/numpy.py +0 -0
  185. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/operator.py +0 -0
  186. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/optuna.py +0 -0
  187. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/orjson.py +0 -0
  188. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/os.py +0 -0
  189. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/parse.py +0 -0
  190. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pathlib.py +0 -0
  191. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/period.py +0 -0
  192. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pickle.py +0 -0
  193. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/platform.py +0 -0
  194. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/polars.py +0 -0
  195. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/polars_ols.py +0 -0
  196. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pqdm.py +0 -0
  197. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/py.typed +0 -0
  198. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pydantic.py +0 -0
  199. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pyinstrument.py +0 -0
  200. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pyrsistent.py +0 -0
  201. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pytest.py +0 -0
  202. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pytest_regressions.py +0 -0
  203. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/python_dotenv.py +0 -0
  204. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/random.py +0 -0
  205. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/re.py +0 -0
  206. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/reprlib.py +0 -0
  207. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/rich.py +0 -0
  208. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/scipy.py +0 -0
  209. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sentinel.py +0 -0
  210. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/shelve.py +0 -0
  211. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/socket.py +0 -0
  212. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sqlalchemy_polars.py +0 -0
  213. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/statsmodels.py +0 -0
  214. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/streamlit.py +0 -0
  215. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sys.py +0 -0
  216. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tempfile.py +0 -0
  217. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tenacity.py +0 -0
  218. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/text.py +0 -0
  219. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/threading.py +0 -0
  220. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/timer.py +0 -0
  221. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/traceback.py +0 -0
  222. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/types.py +0 -0
  223. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/typing.py +0 -0
  224. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tzdata.py +0 -0
  225. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tzlocal.py +0 -0
  226. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/uuid.py +0 -0
  227. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/version.py +0 -0
  228. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/warnings.py +0 -0
  229. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/whenever.py +0 -0
  230. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/zipfile.py +0 -0
  231. {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.117.0
3
+ Version: 0.118.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -92,7 +92,7 @@ dependencies = [
92
92
  name = "dycw-utilities"
93
93
  readme = "README.md"
94
94
  requires-python = ">= 3.12"
95
- version = "0.117.0"
95
+ version = "0.118.0"
96
96
 
97
97
  [project.optional-dependencies]
98
98
  test = [
@@ -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.117.0"
337
+ current_version = "0.118.0"
338
338
 
339
339
  [[tool.bumpversion.files]]
340
340
  filename = "src/utilities/__init__.py"
@@ -1,23 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from asyncio import (
4
- CancelledError,
5
- Event,
6
- PriorityQueue,
7
- Queue,
8
- TaskGroup,
9
- run,
10
- sleep,
11
- timeout,
12
- )
13
- from collections import Counter
3
+ from asyncio import CancelledError, Event, Queue, TaskGroup, run, sleep, timeout
14
4
  from dataclasses import dataclass, field
15
5
  from functools import partial
16
6
  from itertools import chain, count
17
7
  from re import search
18
8
  from typing import TYPE_CHECKING, Self, override
19
9
 
20
- from hypothesis import Phase, given, settings
10
+ from hypothesis import HealthCheck, Phase, given, settings
21
11
  from hypothesis.strategies import (
22
12
  DataObject,
23
13
  data,
@@ -28,20 +18,17 @@ from hypothesis.strategies import (
28
18
  permutations,
29
19
  sampled_from,
30
20
  )
31
- from pytest import approx, mark, param, raises
21
+ from pytest import LogCaptureFixture, mark, raises
32
22
 
33
23
  from utilities.asyncio import (
34
- AsyncLoopingService,
35
- AsyncService,
36
24
  EnhancedTaskGroup,
37
- ExceptionProcessor,
38
25
  InfiniteLooper,
39
26
  InfiniteLooperError,
40
27
  InfiniteQueueLooper,
41
28
  InfiniteQueueLooperError,
42
- QueueProcessor,
43
29
  UniquePriorityQueue,
44
30
  UniqueQueue,
31
+ _DurationOrEvery,
45
32
  get_event,
46
33
  get_items,
47
34
  get_items_nowait,
@@ -54,7 +41,12 @@ from utilities.asyncio import (
54
41
  timeout_dur,
55
42
  )
56
43
  from utilities.dataclasses import replace_non_sentinel
57
- from utilities.datetime import MILLISECOND, datetime_duration_to_timedelta, get_now
44
+ from utilities.datetime import (
45
+ MILLISECOND,
46
+ MINUTE,
47
+ datetime_duration_to_timedelta,
48
+ get_now,
49
+ )
58
50
  from utilities.hypothesis import sentinels, text_ascii
59
51
  from utilities.iterables import one, unique_everseen
60
52
  from utilities.pytest import skipif_windows
@@ -67,200 +59,6 @@ if TYPE_CHECKING:
67
59
  from utilities.types import Coroutine1, Duration, MaybeCallableEvent, MaybeType
68
60
 
69
61
 
70
- class TestAsyncLoopingService:
71
- async def test_main(self) -> None:
72
- @dataclass(kw_only=True)
73
- class Example(AsyncLoopingService):
74
- counter: int = 0
75
-
76
- @override
77
- async def _run(self) -> None:
78
- self.counter += 1
79
-
80
- async with Example(duration=1.0, sleep=0.1) as service:
81
- pass
82
- assert 5 <= service.counter <= 15
83
-
84
- async def test_cancel(self) -> None:
85
- @dataclass(kw_only=True)
86
- class Example(AsyncLoopingService):
87
- counter: int = 0
88
-
89
- @override
90
- async def _run(self) -> None:
91
- self.counter += 1
92
- if self.counter >= 10:
93
- raise CancelledError
94
-
95
- async with Example(sleep=0.1) as service:
96
- pass
97
- assert 5 <= service.counter <= 15
98
-
99
- async def test_sleep_after_failure(self) -> None:
100
- @dataclass(kw_only=True)
101
- class Example(AsyncLoopingService):
102
- counter: int = 0
103
- errors: Counter[type[Exception]] = field(default_factory=Counter)
104
-
105
- @override
106
- async def _run(self) -> None:
107
- self.counter += 1
108
- if self.counter % 2 == 0:
109
- raise ValueError
110
-
111
- @override
112
- async def _run_failure(self, error: Exception, /) -> None:
113
- self.errors.update([type(error)])
114
-
115
- async with Example(duration=1.0, sleep=0.1) as service:
116
- pass
117
- assert 5 <= service.counter <= 15
118
- assert 3 <= service.errors[ValueError] <= 7
119
-
120
- async def test_failure(self) -> None:
121
- class CustomError(Exception): ...
122
-
123
- @dataclass(kw_only=True)
124
- class Example(AsyncLoopingService):
125
- counter: int = 0
126
- failed: bool = False
127
-
128
- @override
129
- async def _run(self) -> None:
130
- self.counter += 1
131
- if self.counter >= 5:
132
- raise CustomError
133
-
134
- with raises(CustomError):
135
- async with Example(sleep=0.1):
136
- pass
137
-
138
-
139
- class TestAsyncService:
140
- async def test_main(self) -> None:
141
- @dataclass(kw_only=True)
142
- class Example(AsyncService):
143
- running: bool = False
144
-
145
- @override
146
- async def _start(self) -> None:
147
- self.running = True
148
-
149
- @override
150
- async def stop(self) -> None:
151
- self.running = False
152
- await super().stop()
153
-
154
- service = Example(duration=0.1)
155
- for _ in range(2):
156
- assert not service.running
157
- async with service:
158
- assert service.running
159
- async with service:
160
- assert service.running
161
- assert service.running
162
- assert not service.running
163
-
164
- async def test_timeout(self) -> None:
165
- @dataclass(kw_only=True)
166
- class Example(AsyncService):
167
- running: bool = False
168
-
169
- @override
170
- async def _start(self) -> None:
171
- self.running = True
172
-
173
- @override
174
- async def stop(self) -> None:
175
- self.running = False
176
- await super().stop()
177
-
178
- service = Example()
179
- try:
180
- async with timeout_dur(duration=0.05), service:
181
- await sleep(0.1)
182
- except TimeoutError:
183
- assert not service.running
184
-
185
- @mark.parametrize(
186
- ("duration", "expected"),
187
- [
188
- param(0.5, approx(5, abs=1)),
189
- param(1.0, approx(10, abs=1)),
190
- param(1.5, 10),
191
- param(None, 10),
192
- ],
193
- )
194
- async def test_cancellation(
195
- self, *, duration: Duration | None, expected: int
196
- ) -> None:
197
- class Example(AsyncService):
198
- counter: int = 0
199
-
200
- @override
201
- async def _start(self) -> None:
202
- for _ in range(10):
203
- self.counter += 1
204
- await sleep(0.1)
205
- raise CancelledError
206
-
207
- async with Example(duration=duration) as service:
208
- ...
209
- assert service.counter == expected
210
-
211
- async def test_extra_context_managers(self) -> None:
212
- @dataclass(kw_only=True)
213
- class Inner(AsyncService):
214
- duration: Duration | None = 0.1
215
- running: bool = False
216
-
217
- @override
218
- async def _start(self) -> None:
219
- self.running = True
220
-
221
- @override
222
- async def stop(self) -> None:
223
- self.running = False
224
- await super().stop()
225
-
226
- @dataclass(kw_only=True)
227
- class Outer(AsyncService):
228
- duration: Duration | None = 0.1
229
- running: bool = False
230
- inner: Inner = field(default_factory=Inner, init=False, repr=False)
231
-
232
- @override
233
- async def _start(self) -> None:
234
- self.running = True
235
- _ = await self._stack.enter_async_context(self.inner)
236
-
237
- @override
238
- async def stop(self) -> None:
239
- self.running = False
240
- await super().stop()
241
-
242
- outer = Outer()
243
- for _ in range(2):
244
- assert not outer.running
245
- assert not outer.inner.running
246
- async with outer:
247
- assert outer.running
248
- assert outer.inner.running
249
- assert not outer.running
250
- assert not outer.inner.running
251
-
252
- def test_repr(self) -> None:
253
- class Example(AsyncService):
254
- @override
255
- async def _start(self) -> None:
256
- await sleep(0.01)
257
-
258
- service = Example()
259
- result = repr(service)
260
- expected = "TestAsyncService.test_repr.<locals>.Example(duration=None)"
261
- assert result == expected
262
-
263
-
264
62
  class TestEnhancedTaskGroup:
265
63
  async def test_max_tasks_disabled(self) -> None:
266
64
  with Timer() as timer:
@@ -299,18 +97,6 @@ class TestEnhancedTaskGroup:
299
97
  assert isinstance(error, CustomError)
300
98
 
301
99
 
302
- class TestExceptionProcessor:
303
- async def test_main(self) -> None:
304
- processor = ExceptionProcessor()
305
-
306
- class CustomError(Exception): ...
307
-
308
- with raises(CustomError): # noqa: PT012
309
- async with processor:
310
- processor.enqueue(CustomError)
311
- await sleep(0.1)
312
-
313
-
314
100
  class TestGetEvent:
315
101
  def test_event(self) -> None:
316
102
  event = Event()
@@ -343,8 +129,8 @@ class TestGetEvent:
343
129
 
344
130
 
345
131
  class TestInfiniteLooper:
346
- @given(n=integers(10, 11))
347
- async def test_main(self, *, n: int) -> None:
132
+ @given(n=integers(10, 11), sleep_core=sampled_from([0.1, ("every", 0.1)]))
133
+ async def test_main(self, *, n: int, sleep_core: _DurationOrEvery) -> None:
348
134
  class TrueError(BaseException): ...
349
135
 
350
136
  class FalseError(BaseException): ...
@@ -370,7 +156,7 @@ class TestInfiniteLooper:
370
156
  yield (True, TrueError)
371
157
  yield (False, FalseError)
372
158
 
373
- looper = Example(sleep_core=0.1)
159
+ looper = Example(sleep_core=sleep_core)
374
160
  match n % 2 == 0:
375
161
  case True:
376
162
  with raises(TrueError):
@@ -522,7 +308,24 @@ class TestInfiniteLooper:
522
308
  assert 1 <= looper.counter <= 6
523
309
 
524
310
  @given(logger=just("logger") | none())
525
- async def test_error_upon_initialize(self, *, logger: str | None) -> None:
311
+ @mark.parametrize(
312
+ ("sleep_restart", "desc"),
313
+ [
314
+ (60.0, "for 0:01:00"),
315
+ (MINUTE, "for 0:01:00"),
316
+ (("every", 60), "until next 0:01:00"),
317
+ (("every", MINUTE), "until next 0:01:00"),
318
+ ],
319
+ )
320
+ @settings(suppress_health_check={HealthCheck.function_scoped_fixture})
321
+ async def test_error_upon_initialize(
322
+ self,
323
+ *,
324
+ sleep_restart: _DurationOrEvery,
325
+ desc: str,
326
+ logger: str | None,
327
+ caplog: LogCaptureFixture,
328
+ ) -> None:
526
329
  class CustomError(Exception): ...
527
330
 
528
331
  @dataclass(kw_only=True)
@@ -535,13 +338,34 @@ class TestInfiniteLooper:
535
338
  async def _core(self) -> None:
536
339
  raise NotImplementedError
537
340
 
538
- looper = Example(sleep_core=0.1, logger=logger)
341
+ looper = Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger)
539
342
  with raises(TimeoutError):
540
343
  async with timeout_dur(duration=0.5):
541
344
  _ = await looper()
345
+ if logger is not None:
346
+ message = caplog.messages[0]
347
+ expected = f"'Example' encountered 'CustomError()' whilst initializing; sleeping {desc}..."
348
+ assert message == expected
542
349
 
543
350
  @given(logger=just("logger") | none())
544
- async def test_error_group_upon_coroutines(self, *, logger: str | None) -> None:
351
+ @mark.parametrize(
352
+ ("sleep_restart", "desc"),
353
+ [
354
+ (60.0, "for 0:01:00"),
355
+ (MINUTE, "for 0:01:00"),
356
+ (("every", 60), "until next 0:01:00"),
357
+ (("every", MINUTE), "until next 0:01:00"),
358
+ ],
359
+ )
360
+ @settings(suppress_health_check={HealthCheck.function_scoped_fixture})
361
+ async def test_error_group_upon_coroutines(
362
+ self,
363
+ *,
364
+ sleep_restart: _DurationOrEvery,
365
+ desc: str,
366
+ logger: str | None,
367
+ caplog: LogCaptureFixture,
368
+ ) -> None:
545
369
  class CustomError(Exception): ...
546
370
 
547
371
  @dataclass(kw_only=True)
@@ -556,10 +380,14 @@ class TestInfiniteLooper:
556
380
  ) -> Iterator[tuple[None, MaybeType[BaseException]]]:
557
381
  yield (None, CustomError)
558
382
 
559
- looper = Example(sleep_core=0.1, logger=logger)
383
+ looper = Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger)
560
384
  with raises(TimeoutError):
561
385
  async with timeout_dur(duration=0.5):
562
386
  _ = await looper()
387
+ if logger is not None:
388
+ message = caplog.messages[0]
389
+ expected = f"'Example' encountered 'CustomError()'; sleeping {desc}..."
390
+ assert message == expected
563
391
 
564
392
  async def test_error_no_event_found(self) -> None:
565
393
  @dataclass(kw_only=True)
@@ -746,169 +574,6 @@ class TestPutAndGetItemsNoWait:
746
574
  assert result == xs[:max_size]
747
575
 
748
576
 
749
- class TestQueueProcessor:
750
- async def test_one_processor_slow_tasks(self) -> None:
751
- @dataclass(kw_only=True)
752
- class Example(QueueProcessor[int]):
753
- output: set[int] = field(default_factory=set)
754
-
755
- @override
756
- async def _process_item(self, item: int, /) -> None:
757
- self.output.add(item)
758
-
759
- async with Example() as processor:
760
-
761
- async def add_tasks() -> None:
762
- for i in range(10):
763
- processor.enqueue(i)
764
- await sleep(0.1)
765
-
766
- async def run_until_empty() -> None:
767
- await sleep(0.5)
768
- await processor.run_until_empty()
769
-
770
- async with TaskGroup() as tg:
771
- _ = tg.create_task(add_tasks())
772
- _ = tg.create_task(run_until_empty())
773
-
774
- assert len(processor.output) == 10
775
-
776
- async def test_one_processor_slow_run(self) -> None:
777
- @dataclass(kw_only=True)
778
- class Example(QueueProcessor[int]):
779
- output: set[int] = field(default_factory=set)
780
-
781
- @override
782
- async def _process_item(self, item: int, /) -> None:
783
- self.output.add(item)
784
- await sleep(0.01)
785
-
786
- async with Example() as processor:
787
- processor.enqueue(*range(10))
788
- await processor.run_until_empty()
789
- assert len(processor.output) == 10
790
-
791
- @given(n=integers(1, 10))
792
- async def test_one_processor_continually_adding(self, *, n: int) -> None:
793
- @dataclass(kw_only=True)
794
- class Example(QueueProcessor[int]):
795
- output: set[int] = field(default_factory=set)
796
-
797
- @override
798
- async def _process_item(self, item: int, /) -> None:
799
- self.output.add(item)
800
-
801
- async with Example() as processor:
802
- for i in range(n):
803
- processor.enqueue(i)
804
- await sleep(0.01)
805
- assert len(processor.output) == n
806
-
807
- async def test_two_processors(self) -> None:
808
- @dataclass(kw_only=True)
809
- class First(QueueProcessor[int]):
810
- second: Second
811
- output: set[int] = field(default_factory=set)
812
-
813
- @override
814
- async def _process_item(self, item: int, /) -> None:
815
- self.second.enqueue(item)
816
- self.output.add(item)
817
- await sleep(0.1)
818
-
819
- @dataclass(kw_only=True)
820
- class Second(QueueProcessor[int]):
821
- output: set[int] = field(default_factory=set)
822
-
823
- @override
824
- async def _process_item(self, item: int, /) -> None:
825
- self.output.add(item)
826
- await sleep(0.01)
827
-
828
- async with Second() as second, First(second=second) as first:
829
-
830
- async def yield_tasks() -> None:
831
- first.enqueue(*range(10))
832
- await first.run_until_empty()
833
-
834
- await yield_tasks()
835
- assert len(first.output) == 10
836
- assert len(second.output) == 10
837
-
838
- @mark.parametrize("duration", [param(0.1), param(0.5), param(1.0), param(1.5)])
839
- async def test_cancellation(self, *, duration: float) -> None:
840
- @dataclass(kw_only=True)
841
- class Example(QueueProcessor[int]):
842
- output: set[int] = field(default_factory=set)
843
-
844
- @override
845
- async def _process_item(self, item: int, /) -> None:
846
- self.output.add(item)
847
- await sleep(0.1)
848
-
849
- async with Example(duration=duration) as processor:
850
- processor.enqueue(*range(10))
851
- assert processor.output == set(range(10))
852
-
853
- async def test_empty(self) -> None:
854
- class Example(QueueProcessor[int]):
855
- @override
856
- async def _process_item(self, item: int, /) -> None:
857
- _ = item
858
-
859
- processor = Example()
860
- assert processor.empty()
861
- processor.enqueue(0)
862
- assert not processor.empty()
863
-
864
- @given(n=integers(0, 10))
865
- async def test_get_items_nowait(self, *, n: int) -> None:
866
- @dataclass(kw_only=True)
867
- class Example(QueueProcessor[int]):
868
- output: set[int] = field(default_factory=set)
869
-
870
- @override
871
- async def _process_item(self, _: int, /) -> None:
872
- items = self._get_items_nowait()
873
- self.output.add(len(items))
874
-
875
- processor = Example()
876
- processor.enqueue(*range(n + 1))
877
- await processor._run()
878
- result = one(processor.output)
879
- assert result == n
880
-
881
- @given(n=integers(0, 10))
882
- async def test_len(self, *, n: int) -> None:
883
- class Example(QueueProcessor[int]):
884
- @override
885
- async def _process_item(self, item: int) -> None:
886
- _ = item
887
-
888
- processor = Example()
889
- assert len(processor) == 0
890
- processor.enqueue(*range(n))
891
- assert len(processor) == n
892
-
893
- @given(data=data(), texts=lists(text_ascii(min_size=1), min_size=1))
894
- async def test_priority_queue(self, *, data: DataObject, texts: list[str]) -> None:
895
- @dataclass(kw_only=True)
896
- class Example(QueueProcessor[tuple[int, str]]):
897
- output: set[str] = field(default_factory=set)
898
-
899
- @override
900
- async def _process_item(self, item: tuple[int, str]) -> None:
901
- _, text = item
902
- self.output.add(text)
903
-
904
- processor = Example(queue_type=PriorityQueue)
905
- items = data.draw(permutations(list(enumerate(texts))))
906
- processor.enqueue(*items)
907
- await processor._run()
908
- result = one(processor.output)
909
- assert result == texts[0]
910
-
911
-
912
577
  class TestUniquePriorityQueue:
913
578
  @given(data=data(), texts=lists(text_ascii(min_size=1), min_size=1, unique=True))
914
579
  async def test_main(self, *, data: DataObject, texts: list[str]) -> None:
@@ -955,10 +620,7 @@ class TestSleepDur:
955
620
 
956
621
  class TestSleepUntil:
957
622
  async def test_main(self) -> None:
958
- now = get_now()
959
- with Timer() as timer:
960
- await sleep_until(now + 10 * MILLISECOND)
961
- assert timer >= datetime_duration_to_timedelta(5 * MILLISECOND)
623
+ await sleep_until(get_now() + 10 * MILLISECOND)
962
624
 
963
625
 
964
626
  class TestSleepUntilRounded:
@@ -3,7 +3,10 @@ from __future__ import annotations
3
3
  from asyncio import sleep
4
4
  from re import search
5
5
 
6
+ from pytest import raises
7
+
6
8
  from tests.conftest import SKIPIF_CI
9
+ from utilities.asyncio import EnhancedTaskGroup
7
10
  from utilities.fastapi import PingReceiver
8
11
 
9
12
 
@@ -11,14 +14,19 @@ class TestPingReceiver:
11
14
  @SKIPIF_CI
12
15
  async def test_main(self) -> None:
13
16
  port = 5465
17
+ receiver = PingReceiver(port=port)
14
18
  assert await PingReceiver.ping(port) is False
15
19
  await sleep(0.1)
16
- async with PingReceiver(port=port):
20
+
21
+ async def run_test() -> None:
17
22
  await sleep(0.1)
18
23
  result = await PingReceiver.ping(port)
19
24
  assert isinstance(result, str)
20
25
  assert search(
21
26
  r"pong @ \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,6}", result
22
27
  )
23
- await sleep(0.1)
24
- assert await PingReceiver.ping(port) is False
28
+
29
+ with raises(ExceptionGroup): # noqa: PT012
30
+ async with EnhancedTaskGroup(timeout=1.0) as tg:
31
+ _ = tg.create_task(receiver())
32
+ _ = tg.create_task(run_test())
@@ -29,7 +29,6 @@ from utilities.hypothesis import (
29
29
  )
30
30
  from utilities.orjson import deserialize, serialize
31
31
  from utilities.redis import (
32
- Publisher,
33
32
  PublisherIQL,
34
33
  PublisherIQLError,
35
34
  publish,
@@ -119,80 +118,6 @@ class TestPublishAndSubscribe:
119
118
  _ = task.cancel()
120
119
 
121
120
 
122
- class TestPublisher:
123
- @given(
124
- data=data(),
125
- channel=text_ascii(min_size=1).map(
126
- lambda c: f"{get_class_name(TestPublisher)}_obj_ser_{c}"
127
- ),
128
- obj=make_objects(),
129
- )
130
- @mark.flaky
131
- @settings(
132
- max_examples=1,
133
- phases={Phase.generate},
134
- suppress_health_check={HealthCheck.function_scoped_fixture},
135
- )
136
- @SKIPIF_CI_AND_NOT_LINUX
137
- async def test_main(
138
- self, *, capsys: CaptureFixture, data: DataObject, channel: str, obj: Any
139
- ) -> None:
140
- async with yield_test_redis(data) as test:
141
-
142
- async def listener() -> None:
143
- async for msg in subscribe(
144
- test.redis.pubsub(), channel, deserializer=deserialize
145
- ):
146
- print(msg) # noqa: T201
147
-
148
- task = create_task(listener())
149
- await sleep(0.1)
150
-
151
- async with Publisher(redis=test.redis, serializer=serialize) as publisher:
152
- publisher.enqueue((channel, obj))
153
- await sleep(0.1)
154
-
155
- try:
156
- out = capsys.readouterr().out
157
- expected = f"{obj}\n"
158
- assert out == expected
159
- finally:
160
- _ = task.cancel()
161
-
162
- @given(
163
- data=data(),
164
- channel=text_ascii(min_size=1).map(
165
- lambda c: f"{get_class_name(TestPublisher)}_text_no_ser_{c}"
166
- ),
167
- text=text_ascii(min_size=1),
168
- )
169
- @settings(
170
- max_examples=1,
171
- phases={Phase.generate},
172
- suppress_health_check={HealthCheck.function_scoped_fixture},
173
- )
174
- @SKIPIF_CI_AND_NOT_LINUX
175
- async def test_text_without_serialize(
176
- self, *, capsys: CaptureFixture, data: DataObject, channel: str, text: str
177
- ) -> None:
178
- async with yield_test_redis(data) as test:
179
-
180
- async def listener() -> None:
181
- async for msg in subscribe(test.redis.pubsub(), channel):
182
- print(msg) # noqa: T201
183
-
184
- task = create_task(listener())
185
- await sleep(0.1)
186
- _ = await publish(test.redis, channel, text)
187
- await sleep(0.1)
188
- try:
189
- out = capsys.readouterr().out
190
- expected = f"{text.encode()}\n"
191
- assert out == expected
192
- finally:
193
- _ = task.cancel()
194
-
195
-
196
121
  class TestPublisherIQL:
197
122
  @given(
198
123
  data=data(),