dycw-utilities 0.126.10__tar.gz → 0.126.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 (235) hide show
  1. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/PKG-INFO +1 -1
  2. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/pyproject.toml +2 -2
  3. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_asyncio.py +86 -57
  4. dycw_utilities-0.126.12/src/tests/test_asyncio_classes/loopers.py +310 -0
  5. dycw_utilities-0.126.12/src/tests/test_asyncio_classes/redis.py +28 -0
  6. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_redis.py +140 -37
  7. dycw_utilities-0.126.12/src/tests/test_reprlib.py +87 -0
  8. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_sqlalchemy.py +32 -1
  9. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/__init__.py +1 -1
  10. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/redis.py +115 -5
  11. dycw_utilities-0.126.12/src/utilities/reprlib.py +158 -0
  12. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/sqlalchemy.py +65 -1
  13. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/traceback.py +3 -5
  14. dycw_utilities-0.126.10/src/tests/test_asyncio_classes/loopers.py +0 -168
  15. dycw_utilities-0.126.10/src/tests/test_reprlib.py +0 -38
  16. dycw_utilities-0.126.10/src/tests/test_rich.py +0 -24
  17. dycw_utilities-0.126.10/src/utilities/reprlib.py +0 -74
  18. dycw_utilities-0.126.10/src/utilities/rich.py +0 -74
  19. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/.gitignore +0 -0
  20. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/LICENSE +0 -0
  21. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/README.md +0 -0
  22. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/__init__.py +0 -0
  23. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/conftest.py +0 -0
  24. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/__init__.py +0 -0
  25. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_missing/__init__.py +0 -0
  26. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_missing/module.py +0 -0
  27. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/__init__.py +0 -0
  28. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/outer_1.py +0 -0
  29. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/outer_2.py +0 -0
  30. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  31. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  32. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  33. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  34. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_without/__init__.py +0 -0
  35. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_without/module_1.py +0 -0
  36. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/package_without/module_2.py +0 -0
  37. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/standalone.py +0 -0
  38. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/modules/with_imports.py +0 -0
  39. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  40. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  41. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  42. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  43. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  44. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  45. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  46. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  47. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_altair.py +0 -0
  48. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_asyncio_classes/__init__.py +0 -0
  49. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_atomicwrites.py +0 -0
  50. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_atools.py +0 -0
  51. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_cachetools.py +0 -0
  52. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_click.py +0 -0
  53. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_concurrent.py +0 -0
  54. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_contextlib.py +0 -0
  55. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_contextvars.py +0 -0
  56. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_cryptography.py +0 -0
  57. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_cvxpy.py +0 -0
  58. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_dataclasses.py +0 -0
  59. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_datetime.py +0 -0
  60. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_enum.py +0 -0
  61. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_errors.py +0 -0
  62. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_eventkit.py +0 -0
  63. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_fastapi.py +0 -0
  64. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_fpdf2.py +0 -0
  65. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_functions.py +0 -0
  66. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_functools.py +0 -0
  67. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_getpass.py +0 -0
  68. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_git.py +0 -0
  69. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_hashlib.py +0 -0
  70. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_http.py +0 -0
  71. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_hypothesis.py +0 -0
  72. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_importlib.py +0 -0
  73. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_ipython.py +0 -0
  74. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_iterables.py +0 -0
  75. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_jupyter.py +0 -0
  76. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_libcst.py +0 -0
  77. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_lightweight_charts.py +0 -0
  78. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_logging.py +0 -0
  79. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_loguru.py +0 -0
  80. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_luigi.py +0 -0
  81. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_math.py +0 -0
  82. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_memory_profiler.py +0 -0
  83. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_modules.py +0 -0
  84. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_more_itertools.py +0 -0
  85. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_numpy.py +0 -0
  86. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_operator.py +0 -0
  87. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_optuna.py +0 -0
  88. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_orjson.py +0 -0
  89. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_os.py +0 -0
  90. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_parse.py +0 -0
  91. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pathlib.py +0 -0
  92. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_period.py +0 -0
  93. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pickle.py +0 -0
  94. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_platform.py +0 -0
  95. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_polars.py +0 -0
  96. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_polars_ols.py +0 -0
  97. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pottery.py +0 -0
  98. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pqdm.py +0 -0
  99. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_psutil.py +0 -0
  100. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pydantic.py +0 -0
  101. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pyinstrument.py +0 -0
  102. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pyrsistent.py +0 -0
  103. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pytest.py +0 -0
  104. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_pytest_regressions.py +0 -0
  105. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_python_dotenv.py +0 -0
  106. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_random.py +0 -0
  107. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_re.py +0 -0
  108. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_scipy.py +0 -0
  109. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_sentinel.py +0 -0
  110. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_shelve.py +0 -0
  111. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_slack_sdk.py +0 -0
  112. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_socket.py +0 -0
  113. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_sqlalchemy_polars.py +0 -0
  114. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_statsmodel.py +0 -0
  115. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_streamlit.py +0 -0
  116. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_string.py +0 -0
  117. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_sys.py +0 -0
  118. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_tempfile.py +0 -0
  119. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_tenacity.py +0 -0
  120. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_text.py +0 -0
  121. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_threading.py +0 -0
  122. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_timer.py +0 -0
  123. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback.py +0 -0
  124. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/__init__.py +0 -0
  125. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/chain.py +0 -0
  126. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  127. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  128. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  129. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/many.py +0 -0
  130. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/one.py +0 -0
  131. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/recursive.py +0 -0
  132. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  133. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  134. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/two.py +0 -0
  135. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_traceback_funcs/untraced.py +0 -0
  136. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_types.py +0 -0
  137. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_typing.py +0 -0
  138. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_typing_funcs/__init__.py +0 -0
  139. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_typing_funcs/no_future.py +0 -0
  140. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_typing_funcs/with_future.py +0 -0
  141. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_tzdata.py +0 -0
  142. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_tzlocal.py +0 -0
  143. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_uuid.py +0 -0
  144. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_version.py +0 -0
  145. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_warnings.py +0 -0
  146. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_whenever.py +0 -0
  147. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_zipfile.py +0 -0
  148. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/tests/test_zoneinfo.py +0 -0
  149. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/altair.py +0 -0
  150. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/asyncio.py +0 -0
  151. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/atomicwrites.py +0 -0
  152. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/atools.py +0 -0
  153. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/cachetools.py +0 -0
  154. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/click.py +0 -0
  155. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/concurrent.py +0 -0
  156. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/contextlib.py +0 -0
  157. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/contextvars.py +0 -0
  158. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/cryptography.py +0 -0
  159. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/cvxpy.py +0 -0
  160. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/dataclasses.py +0 -0
  161. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/datetime.py +0 -0
  162. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/enum.py +0 -0
  163. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/errors.py +0 -0
  164. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/eventkit.py +0 -0
  165. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/fastapi.py +0 -0
  166. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/fpdf2.py +0 -0
  167. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/functions.py +0 -0
  168. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/functools.py +0 -0
  169. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/getpass.py +0 -0
  170. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/git.py +0 -0
  171. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/hashlib.py +0 -0
  172. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/http.py +0 -0
  173. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/hypothesis.py +0 -0
  174. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/importlib.py +0 -0
  175. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/ipython.py +0 -0
  176. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/iterables.py +0 -0
  177. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/jupyter.py +0 -0
  178. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/libcst.py +0 -0
  179. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/lightweight_charts.py +0 -0
  180. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/logging.py +0 -0
  181. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/loguru.py +0 -0
  182. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/luigi.py +0 -0
  183. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/math.py +0 -0
  184. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/memory_profiler.py +0 -0
  185. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/modules.py +0 -0
  186. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/more_itertools.py +0 -0
  187. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/numpy.py +0 -0
  188. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/operator.py +0 -0
  189. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/optuna.py +0 -0
  190. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/orjson.py +0 -0
  191. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/os.py +0 -0
  192. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/parse.py +0 -0
  193. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pathlib.py +0 -0
  194. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/period.py +0 -0
  195. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pickle.py +0 -0
  196. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/platform.py +0 -0
  197. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/polars.py +0 -0
  198. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/polars_ols.py +0 -0
  199. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pottery.py +0 -0
  200. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pqdm.py +0 -0
  201. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/psutil.py +0 -0
  202. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/py.typed +0 -0
  203. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pydantic.py +0 -0
  204. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pyinstrument.py +0 -0
  205. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pyrsistent.py +0 -0
  206. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pytest.py +0 -0
  207. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/pytest_regressions.py +0 -0
  208. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/python_dotenv.py +0 -0
  209. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/random.py +0 -0
  210. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/re.py +0 -0
  211. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/scipy.py +0 -0
  212. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/sentinel.py +0 -0
  213. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/shelve.py +0 -0
  214. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/slack_sdk.py +0 -0
  215. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/socket.py +0 -0
  216. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/sqlalchemy_polars.py +0 -0
  217. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/statsmodels.py +0 -0
  218. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/streamlit.py +0 -0
  219. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/string.py +0 -0
  220. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/sys.py +0 -0
  221. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/tempfile.py +0 -0
  222. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/tenacity.py +0 -0
  223. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/text.py +0 -0
  224. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/threading.py +0 -0
  225. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/timer.py +0 -0
  226. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/types.py +0 -0
  227. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/typing.py +0 -0
  228. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/tzdata.py +0 -0
  229. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/tzlocal.py +0 -0
  230. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/uuid.py +0 -0
  231. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/version.py +0 -0
  232. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/warnings.py +0 -0
  233. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/whenever.py +0 -0
  234. {dycw_utilities-0.126.10 → dycw_utilities-0.126.12}/src/utilities/zipfile.py +0 -0
  235. {dycw_utilities-0.126.10 → dycw_utilities-0.126.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.126.10
3
+ Version: 0.126.12
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -94,7 +94,7 @@ dependencies = [
94
94
  name = "dycw-utilities"
95
95
  readme = "README.md"
96
96
  requires-python = ">= 3.12"
97
- version = "0.126.10"
97
+ version = "0.126.12"
98
98
 
99
99
  [project.optional-dependencies]
100
100
  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.126.10"
337
+ current_version = "0.126.12"
338
338
 
339
339
  [[tool.bumpversion.files]]
340
340
  filename = "src/utilities/__init__.py"
@@ -21,16 +21,20 @@ from hypothesis.strategies import (
21
21
  permutations,
22
22
  sampled_from,
23
23
  )
24
- from pytest import LogCaptureFixture, approx, mark, param, raises
24
+ from pytest import LogCaptureFixture, mark, param, raises
25
25
 
26
26
  from tests.test_asyncio_classes.loopers import (
27
+ _REL,
27
28
  CountingLooper,
28
29
  CountingLooperError,
29
30
  LooperWithCounterMixin,
31
+ LooperWithCounterMixins,
30
32
  MultipleSubLoopers,
31
33
  Outer2CountingLooper,
32
34
  OuterCountingLooper,
33
35
  QueueLooper,
36
+ assert_looper_full,
37
+ assert_looper_stats,
34
38
  )
35
39
  from utilities.asyncio import (
36
40
  EnhancedQueue,
@@ -195,6 +199,7 @@ class TestEnhancedTaskGroup:
195
199
  assert looper.running
196
200
  assert not looper.running
197
201
 
202
+ @mark.flaky
198
203
  async def test_max_tasks_disabled(self) -> None:
199
204
  with Timer() as timer:
200
205
  async with EnhancedTaskGroup() as tg:
@@ -607,8 +612,8 @@ class TestInfiniteLooper:
607
612
 
608
613
  async with timeout(1.0), Parent(sleep_core=0.05) as parent:
609
614
  ...
610
- assert 15 <= parent.counter <= 25
611
- assert 15 <= parent.child.counter <= 25
615
+ assert 12 <= parent.counter <= 25
616
+ assert 12 <= parent.child.counter <= 25
612
617
 
613
618
  async def test_error_default_event(self) -> None:
614
619
  @dataclass(kw_only=True)
@@ -864,6 +869,7 @@ class TestInfiniteQueueLooper:
864
869
  assert looper.empty()
865
870
 
866
871
  @given(logger=just("logger") | none())
872
+ @mark.flaky
867
873
  @settings(suppress_health_check={HealthCheck.function_scoped_fixture})
868
874
  async def test_error_process_items(
869
875
  self, *, logger: str | None, caplog: LogCaptureFixture
@@ -1007,6 +1013,70 @@ class TestLooper:
1007
1013
  ...
1008
1014
  self._assert_stats_half(looper._counter, stops=1)
1009
1015
 
1016
+ @mark.parametrize("counter_auto_start", [param(True), param(False)])
1017
+ async def test_mixin_in_task_group(self, *, counter_auto_start: bool) -> None:
1018
+ looper = LooperWithCounterMixin(
1019
+ auto_start=True, timeout=1.0, counter_auto_start=counter_auto_start
1020
+ )
1021
+ with raises(ExceptionGroup) as exc_info:
1022
+ async with EnhancedTaskGroup(timeout=looper.timeout) as tg:
1023
+ _ = tg.create_task_context(looper)
1024
+ error = one(exc_info.value.exceptions)
1025
+ assert isinstance(error, TimeoutError)
1026
+ self._assert_stats_half(looper._counter)
1027
+
1028
+ @mark.parametrize("counter1_auto_start", [param(True), param(False)])
1029
+ @mark.parametrize("counter2_auto_start", [param(True), param(False)])
1030
+ async def test_mixins(
1031
+ self, *, counter1_auto_start: bool, counter2_auto_start: bool
1032
+ ) -> None:
1033
+ looper = LooperWithCounterMixins(
1034
+ auto_start=True,
1035
+ timeout=1.0,
1036
+ counter1_auto_start=counter1_auto_start,
1037
+ counter2_auto_start=counter2_auto_start,
1038
+ )
1039
+ match counter1_auto_start, counter2_auto_start:
1040
+ case _, True:
1041
+ with raises(TimeoutError):
1042
+ async with timeout(1.0), looper:
1043
+ ...
1044
+ assert_looper_full(looper)
1045
+ self._assert_stats_no_runs(looper._counter1)
1046
+ self._assert_stats_third(looper._counter2)
1047
+ case True, False:
1048
+ with raises(TimeoutError):
1049
+ async with timeout(1.0), looper:
1050
+ ...
1051
+ assert_looper_full(looper)
1052
+ self._assert_stats_half(looper._counter1)
1053
+ self._assert_stats_third(looper._counter2)
1054
+ case False, False:
1055
+ async with looper:
1056
+ ...
1057
+ assert_looper_full(looper, stops=1)
1058
+ self._assert_stats_half(looper._counter1, stops=1)
1059
+ self._assert_stats_third(looper._counter2, stops=1)
1060
+
1061
+ @mark.parametrize("counter_auto_start", [param(True), param(False)])
1062
+ async def test_mixins_in_task_group(self, *, counter_auto_start: bool) -> None:
1063
+ looper1 = LooperWithCounterMixin(
1064
+ auto_start=True, timeout=1.0, counter_auto_start=counter_auto_start
1065
+ )
1066
+ looper2 = LooperWithCounterMixin(
1067
+ auto_start=True, timeout=1.0, counter_auto_start=counter_auto_start
1068
+ )
1069
+ with raises(ExceptionGroup) as exc_info: # noqa: PT012
1070
+ async with EnhancedTaskGroup(timeout=1.0) as tg:
1071
+ _ = tg.create_task_context(looper1)
1072
+ _ = tg.create_task_context(looper2)
1073
+ errors = exc_info.value.exceptions
1074
+ assert 1 <= len(errors) <= 2
1075
+ for error in errors:
1076
+ assert isinstance(error, TimeoutError)
1077
+ self._assert_stats_half(looper1._counter)
1078
+ self._assert_stats_half(looper2._counter)
1079
+
1010
1080
  def test_replace(self) -> None:
1011
1081
  looper = CountingLooper().replace(freq=10.0)
1012
1082
  assert looper.freq == 10.0
@@ -1024,7 +1094,7 @@ class TestLooper:
1024
1094
  looper = Example(auto_start=True, timeout=1.0)
1025
1095
  async with looper:
1026
1096
  ...
1027
- self._assert_stats(
1097
+ assert_looper_stats(
1028
1098
  looper,
1029
1099
  entries=1,
1030
1100
  core_successes=79,
@@ -1058,7 +1128,7 @@ class TestLooper:
1058
1128
  looper = Example(auto_start=True, timeout=1.0)
1059
1129
  async with looper:
1060
1130
  ...
1061
- self._assert_stats(
1131
+ assert_looper_stats(
1062
1132
  looper,
1063
1133
  entries=1,
1064
1134
  core_successes=14,
@@ -1098,7 +1168,7 @@ class TestLooper:
1098
1168
  looper.put_right_nowait(i)
1099
1169
  async with looper:
1100
1170
  ...
1101
- self._assert_stats(
1171
+ assert_looper_stats(
1102
1172
  looper,
1103
1173
  entries=1,
1104
1174
  core_successes=25,
@@ -1301,47 +1371,6 @@ class TestLooper:
1301
1371
  assert not looper.auto_start
1302
1372
  assert looper.with_auto_start.auto_start
1303
1373
 
1304
- def _assert_stats(
1305
- self,
1306
- looper: Looper[Any],
1307
- /,
1308
- *,
1309
- entries: int = 0,
1310
- core_successes: int = 0,
1311
- core_failures: int = 0,
1312
- initialization_successes: int = 0,
1313
- initialization_failures: int = 0,
1314
- tear_down_successes: int = 0,
1315
- tear_down_failures: int = 0,
1316
- restart_successes: int = 0,
1317
- restart_failures: int = 0,
1318
- stops: int = 0,
1319
- rel: float = 0.5,
1320
- ) -> None:
1321
- stats = looper.stats
1322
- assert stats.entries == entries
1323
- assert stats.core_attempts == (stats.core_successes + stats.core_failures)
1324
- assert stats.core_successes == approx(core_successes, rel=rel)
1325
- assert stats.core_failures == approx(core_failures, rel=rel)
1326
- assert stats.initialization_attempts == (
1327
- stats.initialization_successes + stats.initialization_failures
1328
- )
1329
- assert stats.initialization_successes == approx(
1330
- initialization_successes, rel=rel
1331
- )
1332
- assert stats.initialization_failures == approx(initialization_failures, rel=rel)
1333
- assert stats.tear_down_attempts == (
1334
- stats.tear_down_successes + stats.tear_down_failures
1335
- )
1336
- assert stats.tear_down_successes == approx(tear_down_successes, rel=rel)
1337
- assert stats.tear_down_failures == approx(tear_down_failures, rel=rel)
1338
- assert stats.restart_attempts == (
1339
- stats.restart_successes + stats.restart_failures
1340
- )
1341
- assert stats.restart_successes == approx(restart_successes, rel=rel)
1342
- assert stats.restart_failures == approx(restart_failures, rel=rel)
1343
- assert stats.stops == stops
1344
-
1345
1374
  def _assert_stats_no_runs(
1346
1375
  self,
1347
1376
  looper: Looper[Any],
@@ -1349,14 +1378,14 @@ class TestLooper:
1349
1378
  *,
1350
1379
  entries: int = 0,
1351
1380
  stops: int = 0,
1352
- rel: float = 0.75,
1381
+ rel: float = _REL,
1353
1382
  ) -> None:
1354
- self._assert_stats(looper, entries=entries, stops=stops, rel=rel)
1383
+ assert_looper_stats(looper, entries=entries, stops=stops, rel=rel)
1355
1384
 
1356
1385
  def _assert_stats_full(
1357
- self, looper: Looper[Any], /, *, stops: int = 0, rel: float = 0.75
1386
+ self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
1358
1387
  ) -> None:
1359
- self._assert_stats(
1388
+ assert_looper_stats(
1360
1389
  looper,
1361
1390
  entries=1,
1362
1391
  core_successes=45,
@@ -1369,9 +1398,9 @@ class TestLooper:
1369
1398
  )
1370
1399
 
1371
1400
  def _assert_stats_half(
1372
- self, looper: Looper[Any], /, *, stops: int = 0, rel: float = 0.75
1401
+ self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
1373
1402
  ) -> None:
1374
- self._assert_stats(
1403
+ assert_looper_stats(
1375
1404
  looper,
1376
1405
  entries=1,
1377
1406
  core_successes=56,
@@ -1384,9 +1413,9 @@ class TestLooper:
1384
1413
  )
1385
1414
 
1386
1415
  def _assert_stats_third(
1387
- self, looper: Looper[Any], /, *, stops: int = 0, rel: float = 0.75
1416
+ self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
1388
1417
  ) -> None:
1389
- self._assert_stats(
1418
+ assert_looper_stats(
1390
1419
  looper,
1391
1420
  entries=1,
1392
1421
  core_successes=49,
@@ -1399,9 +1428,9 @@ class TestLooper:
1399
1428
  )
1400
1429
 
1401
1430
  def _assert_stats_quarter(
1402
- self, looper: Looper[Any], /, *, stops: int = 0, rel: float = 0.75
1431
+ self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
1403
1432
  ) -> None:
1404
- self._assert_stats(
1433
+ assert_looper_stats(
1405
1434
  looper,
1406
1435
  entries=1,
1407
1436
  core_successes=35,
@@ -0,0 +1,310 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING, Any, Literal, override
5
+
6
+ from pytest import approx
7
+
8
+ from utilities.asyncio import Looper
9
+ from utilities.contextlib import suppress_super_object_attribute_error
10
+ from utilities.datetime import MILLISECOND
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import Iterator
14
+
15
+ from utilities.types import Duration
16
+
17
+ _FREQ: Duration = 10 * MILLISECOND
18
+ _BACKOFF: Duration = 100 * MILLISECOND
19
+ _REL: float = 0.75
20
+
21
+
22
+ # assert
23
+
24
+
25
+ def assert_looper_stats(
26
+ looper: Looper[Any],
27
+ /,
28
+ *,
29
+ entries: int = 0,
30
+ core_successes: int | tuple[Literal[">="], int] = 0,
31
+ core_failures: int = 0,
32
+ initialization_successes: int = 0,
33
+ initialization_failures: int = 0,
34
+ tear_down_successes: int = 0,
35
+ tear_down_failures: int = 0,
36
+ restart_successes: int = 0,
37
+ restart_failures: int = 0,
38
+ stops: int = 0,
39
+ rel: float = _REL,
40
+ ) -> None:
41
+ stats = looper.stats
42
+ assert stats.entries == entries, f"{stats=}, {entries=}"
43
+ assert stats.core_attempts == (stats.core_successes + stats.core_failures), (
44
+ f"{stats=}"
45
+ )
46
+ match core_successes:
47
+ case int():
48
+ assert stats.core_successes == approx(core_successes, rel=rel), (
49
+ f"{stats=}, {core_successes=}"
50
+ )
51
+ case ">=", int() as min_successes:
52
+ assert stats.core_successes >= min_successes, f"{stats=}, {min_successes=}"
53
+ assert stats.core_failures == approx(core_failures, rel=rel), (
54
+ f"{stats=}, {core_failures=}"
55
+ )
56
+ assert stats.initialization_attempts == (
57
+ stats.initialization_successes + stats.initialization_failures
58
+ ), f"{stats=}"
59
+ assert stats.initialization_successes == approx(
60
+ initialization_successes, rel=rel
61
+ ), f"{stats=}, {initialization_successes=}"
62
+ assert stats.initialization_failures == approx(initialization_failures, rel=rel), (
63
+ f"{stats=}, {initialization_failures=}"
64
+ )
65
+ assert stats.tear_down_attempts == (
66
+ stats.tear_down_successes + stats.tear_down_failures
67
+ ), f"{stats=}"
68
+ assert stats.tear_down_successes == approx(tear_down_successes, rel=rel), (
69
+ f"{stats=}, {tear_down_successes=}"
70
+ )
71
+ assert stats.tear_down_failures == approx(tear_down_failures, rel=rel), (
72
+ f"{stats=}, {initialization_failures=}"
73
+ )
74
+ assert stats.restart_attempts == (
75
+ stats.restart_successes + stats.restart_failures
76
+ ), f"{stats=}"
77
+ assert stats.restart_successes == approx(restart_successes, rel=rel), (
78
+ f"{stats=}, {restart_successes=}"
79
+ )
80
+ assert stats.restart_failures == approx(restart_failures, rel=rel), (
81
+ f"{stats=}, {restart_failures=}"
82
+ )
83
+ assert stats.stops == stops, f"{stats=}, {stops=}"
84
+
85
+
86
+ def assert_looper_full(
87
+ looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
88
+ ) -> None:
89
+ assert_looper_stats(
90
+ looper,
91
+ entries=1,
92
+ core_successes=99,
93
+ initialization_successes=1,
94
+ stops=stops,
95
+ rel=rel,
96
+ )
97
+
98
+
99
+ # counting looper
100
+
101
+
102
+ @dataclass(kw_only=True)
103
+ class CountingLooper(Looper[Any]):
104
+ freq: Duration = field(default=_FREQ, repr=False)
105
+ backoff: Duration = field(default=_BACKOFF, repr=False)
106
+ _debug: bool = field(default=True, repr=False)
107
+ count: int = 0
108
+ max_count: int = 10
109
+
110
+ @override
111
+ async def _initialize_core(self) -> None:
112
+ await super()._initialize_core()
113
+ self.count = 0
114
+
115
+ @override
116
+ async def core(self) -> None:
117
+ await super().core()
118
+ self.count += 1
119
+ if self.count >= self.max_count:
120
+ raise CountingLooperError
121
+
122
+
123
+ class CountingLooperError(Exception): ...
124
+
125
+
126
+ # one sub looper
127
+
128
+
129
+ @dataclass(kw_only=True)
130
+ class OuterCountingLooper(CountingLooper):
131
+ inner: CountingLooper = field(init=False, repr=False)
132
+ inner_auto_start: bool = False
133
+
134
+ @override
135
+ def __post_init__(self) -> None:
136
+ super().__post_init__()
137
+ self.inner = CountingLooper(
138
+ auto_start=self.inner_auto_start,
139
+ freq=self.freq / 2,
140
+ backoff=self.backoff / 2,
141
+ max_count=round(self.max_count / 2),
142
+ )
143
+
144
+ @override
145
+ def _yield_sub_loopers(self) -> Iterator[Looper]:
146
+ yield from super()._yield_sub_loopers()
147
+ yield self.inner
148
+
149
+
150
+ # two sub loopers
151
+
152
+
153
+ @dataclass(kw_only=True)
154
+ class MultipleSubLoopers(CountingLooper):
155
+ inner1: CountingLooper = field(init=False, repr=False)
156
+ inner2: CountingLooper = field(init=False, repr=False)
157
+ inner1_auto_start: bool = False
158
+ inner2_auto_start: bool = False
159
+
160
+ @override
161
+ def __post_init__(self) -> None:
162
+ super().__post_init__()
163
+ self.inner1 = CountingLooper(
164
+ auto_start=self.inner1_auto_start,
165
+ freq=self.freq / 2,
166
+ backoff=self.backoff / 2,
167
+ max_count=round(self.max_count / 2),
168
+ )
169
+ self.inner2 = CountingLooper(
170
+ auto_start=self.inner2_auto_start,
171
+ freq=self.freq / 3,
172
+ backoff=self.backoff / 3,
173
+ max_count=round(self.max_count / 3),
174
+ )
175
+
176
+ @override
177
+ def _yield_sub_loopers(self) -> Iterator[Looper]:
178
+ yield from super()._yield_sub_loopers()
179
+ yield self.inner1
180
+ yield self.inner2
181
+
182
+
183
+ # nested sub loopers
184
+
185
+
186
+ @dataclass(kw_only=True)
187
+ class Outer2CountingLooper(CountingLooper):
188
+ middle: OuterCountingLooper = field(init=False, repr=False)
189
+ middle_auto_start: bool = False
190
+ inner_auto_start: bool = False
191
+
192
+ @override
193
+ def __post_init__(self) -> None:
194
+ super().__post_init__()
195
+ self.middle = OuterCountingLooper(
196
+ auto_start=self.middle_auto_start,
197
+ freq=self.freq / 2,
198
+ backoff=self.backoff / 2,
199
+ max_count=round(self.max_count / 2),
200
+ inner_auto_start=self.inner_auto_start,
201
+ )
202
+
203
+ @override
204
+ def _yield_sub_loopers(self) -> Iterator[Looper]:
205
+ yield from super()._yield_sub_loopers()
206
+ yield self.middle
207
+
208
+
209
+ # one mixin
210
+
211
+
212
+ @dataclass(kw_only=True)
213
+ class CounterMixin:
214
+ freq: Duration = field(default=_FREQ, repr=False)
215
+ backoff: Duration = field(default=_BACKOFF, repr=False)
216
+ _debug: bool = field(default=True, repr=False)
217
+ count: int = 0
218
+ max_count: int = 10
219
+ counter_auto_start: bool = False
220
+ _counter: CountingLooper = field(init=False, repr=False)
221
+
222
+ def __post_init__(self) -> None:
223
+ with suppress_super_object_attribute_error():
224
+ super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
225
+ self._counter = CountingLooper(
226
+ auto_start=self.counter_auto_start,
227
+ freq=self.freq / 2,
228
+ backoff=self.backoff / 2,
229
+ max_count=round(self.max_count / 2),
230
+ )
231
+
232
+ def _yield_sub_loopers(self) -> Iterator[Looper[Any]]:
233
+ with suppress_super_object_attribute_error():
234
+ yield from super()._yield_sub_loopers() # pyright: ignore[reportAttributeAccessIssue]
235
+ yield self._counter
236
+
237
+
238
+ @dataclass(kw_only=True)
239
+ class LooperWithCounterMixin(CounterMixin, Looper): ...
240
+
241
+
242
+ # two mixins
243
+
244
+
245
+ @dataclass(kw_only=True)
246
+ class CounterMixin1:
247
+ freq: Duration = field(default=_FREQ, repr=False)
248
+ backoff: Duration = field(default=_BACKOFF, repr=False)
249
+ _debug: bool = field(default=True, repr=False)
250
+ count: int = 0
251
+ max_count: int = 10
252
+ counter1_auto_start: bool = False
253
+ _counter1: CountingLooper = field(init=False, repr=False)
254
+
255
+ def __post_init__(self) -> None:
256
+ with suppress_super_object_attribute_error():
257
+ super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
258
+ self._counter1 = CountingLooper(
259
+ auto_start=self.counter1_auto_start,
260
+ freq=self.freq / 2,
261
+ backoff=self.backoff / 2,
262
+ max_count=round(self.max_count / 2),
263
+ )
264
+
265
+ def _yield_sub_loopers(self) -> Iterator[Looper[Any]]:
266
+ with suppress_super_object_attribute_error():
267
+ yield from super()._yield_sub_loopers() # pyright: ignore[reportAttributeAccessIssue]
268
+ yield self._counter1
269
+
270
+
271
+ @dataclass(kw_only=True)
272
+ class CounterMixin2:
273
+ freq: Duration = field(default=_FREQ, repr=False)
274
+ backoff: Duration = field(default=_BACKOFF, repr=False)
275
+ _debug: bool = field(default=True, repr=False)
276
+ count: int = 0
277
+ max_count: int = 10
278
+ counter2_auto_start: bool = False
279
+ _counter2: CountingLooper = field(init=False, repr=False)
280
+
281
+ def __post_init__(self) -> None:
282
+ with suppress_super_object_attribute_error():
283
+ super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
284
+ self._counter2 = CountingLooper(
285
+ auto_start=self.counter2_auto_start,
286
+ freq=self.freq / 3,
287
+ backoff=self.backoff / 3,
288
+ max_count=round(self.max_count / 3),
289
+ )
290
+
291
+ def _yield_sub_loopers(self) -> Iterator[Looper[Any]]:
292
+ with suppress_super_object_attribute_error():
293
+ yield from super()._yield_sub_loopers() # pyright: ignore[reportAttributeAccessIssue]
294
+ yield self._counter2
295
+
296
+
297
+ @dataclass(kw_only=True)
298
+ class LooperWithCounterMixins(CounterMixin1, CounterMixin2, Looper): ...
299
+
300
+
301
+ # queue looper
302
+
303
+
304
+ @dataclass(kw_only=True)
305
+ class QueueLooper(Looper[int]):
306
+ @override
307
+ async def core(self) -> None:
308
+ await super().core()
309
+ if not self.empty():
310
+ _ = self.get_left_nowait()
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ from tests.test_asyncio_classes.loopers import _BACKOFF, _FREQ
7
+ from utilities.asyncio import Looper
8
+ from utilities.redis import PublishServiceMixin, SubscribeServiceMixin
9
+ from utilities.text import unique_str
10
+
11
+ if TYPE_CHECKING:
12
+ from utilities.types import Duration
13
+
14
+
15
+ @dataclass(kw_only=True)
16
+ class LooperWithPublishAndSubscribeMixins(
17
+ PublishServiceMixin[Any], SubscribeServiceMixin[Any], Looper[Any]
18
+ ):
19
+ freq: Duration = field(default=_FREQ, repr=False)
20
+ backoff: Duration = field(default=_BACKOFF, repr=False)
21
+ _debug: bool = field(default=True, repr=False)
22
+ publish_service_freq: Duration = field(default=_FREQ, repr=False)
23
+ publish_service_backoff: Duration = field(default=_BACKOFF, repr=False)
24
+ publish_service_debug: bool = field(default=True, repr=False)
25
+ subscribe_service_freq: Duration = field(default=_FREQ, repr=False)
26
+ subscribe_service_backoff: Duration = field(default=_BACKOFF, repr=False)
27
+ subscribe_service_debug: bool = field(default=True, repr=False)
28
+ subscribe_service_channel: str = field(default_factory=unique_str)