dycw-utilities 0.162.6__tar.gz → 0.162.8__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 (211) hide show
  1. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/PKG-INFO +1 -1
  2. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/pyproject.toml +2 -2
  3. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_orjson.py +10 -2
  4. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_redis.py +99 -2
  5. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/__init__.py +1 -1
  6. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/orjson.py +22 -2
  7. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/redis.py +69 -23
  8. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/.gitignore +0 -0
  9. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/LICENSE +0 -0
  10. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/README.md +0 -0
  11. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/__init__.py +0 -0
  12. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/conftest.py +0 -0
  13. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/__init__.py +0 -0
  14. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_missing/__init__.py +0 -0
  15. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_missing/module.py +0 -0
  16. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/__init__.py +0 -0
  17. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/outer_1.py +0 -0
  18. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/outer_2.py +0 -0
  19. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  20. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  21. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  22. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  23. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_without/__init__.py +0 -0
  24. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_without/module_1.py +0 -0
  25. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/package_without/module_2.py +0 -0
  26. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/standalone.py +0 -0
  27. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/modules/with_imports.py +0 -0
  28. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  29. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  30. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  31. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  32. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  33. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  34. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  35. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  36. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_aeventkit.py +0 -0
  37. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_altair.py +0 -0
  38. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_asyncio.py +0 -0
  39. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_atomicwrites.py +0 -0
  40. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_atools.py +0 -0
  41. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_cachetools.py +0 -0
  42. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_click.py +0 -0
  43. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_concurrent.py +0 -0
  44. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_contextlib.py +0 -0
  45. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_contextvars.py +0 -0
  46. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_cryptography.py +0 -0
  47. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_cvxpy.py +0 -0
  48. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_dataclasses.py +0 -0
  49. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_enum.py +0 -0
  50. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_errors.py +0 -0
  51. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_fastapi.py +0 -0
  52. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_fpdf2.py +0 -0
  53. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_functions.py +0 -0
  54. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_functools.py +0 -0
  55. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_getpass.py +0 -0
  56. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_gzip.py +0 -0
  57. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_hashlib.py +0 -0
  58. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_http.py +0 -0
  59. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_hypothesis.py +0 -0
  60. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_importlib.py +0 -0
  61. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_inflect.py +0 -0
  62. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_ipython.py +0 -0
  63. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_iterables.py +0 -0
  64. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_json.py +0 -0
  65. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_jupyter.py +0 -0
  66. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_libcst.py +0 -0
  67. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_lightweight_charts.py +0 -0
  68. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_logging.py +0 -0
  69. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_math.py +0 -0
  70. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_memory_profiler.py +0 -0
  71. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_modules.py +0 -0
  72. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_more_itertools.py +0 -0
  73. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_numpy.py +0 -0
  74. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_objects/__init__.py +0 -0
  75. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_objects/objects.py +0 -0
  76. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_operator.py +0 -0
  77. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_optuna.py +0 -0
  78. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_os.py +0 -0
  79. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_parse.py +0 -0
  80. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pathlib.py +0 -0
  81. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pickle.py +0 -0
  82. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_platform.py +0 -0
  83. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_polars.py +0 -0
  84. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_polars_ols.py +0 -0
  85. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_postgres.py +0 -0
  86. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pottery.py +0 -0
  87. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pqdm.py +0 -0
  88. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_psutil.py +0 -0
  89. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pyinstrument.py +0 -0
  90. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pytest.py +0 -0
  91. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pytest_randomly.py +0 -0
  92. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_pytest_regressions.py +0 -0
  93. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_random.py +0 -0
  94. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_re.py +0 -0
  95. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_reprlib.py +0 -0
  96. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_scipy.py +0 -0
  97. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_sentinel.py +0 -0
  98. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_shelve.py +0 -0
  99. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_slack_sdk.py +0 -0
  100. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_socket.py +0 -0
  101. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_sqlalchemy.py +0 -0
  102. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_sqlalchemy_polars.py +0 -0
  103. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_statsmodels.py +0 -0
  104. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_string.py +0 -0
  105. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_tempfile.py +0 -0
  106. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_testbook.py +0 -0
  107. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_text.py +0 -0
  108. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_threading.py +0 -0
  109. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_timer.py +0 -0
  110. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_traceback.py +0 -0
  111. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_typed_settings.py +0 -0
  112. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_types.py +0 -0
  113. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_typing.py +0 -0
  114. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_typing_funcs/__init__.py +0 -0
  115. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_typing_funcs/no_future.py +0 -0
  116. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_typing_funcs/with_future.py +0 -0
  117. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_tzdata.py +0 -0
  118. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_tzlocal.py +0 -0
  119. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_uuid.py +0 -0
  120. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_version.py +0 -0
  121. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_warnings.py +0 -0
  122. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_whenever.py +0 -0
  123. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_zipfile.py +0 -0
  124. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/tests/test_zoneinfo.py +0 -0
  125. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/aeventkit.py +0 -0
  126. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/altair.py +0 -0
  127. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/asyncio.py +0 -0
  128. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/atomicwrites.py +0 -0
  129. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/atools.py +0 -0
  130. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/cachetools.py +0 -0
  131. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/click.py +0 -0
  132. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/concurrent.py +0 -0
  133. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/contextlib.py +0 -0
  134. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/contextvars.py +0 -0
  135. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/cryptography.py +0 -0
  136. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/cvxpy.py +0 -0
  137. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/dataclasses.py +0 -0
  138. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/enum.py +0 -0
  139. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/errors.py +0 -0
  140. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/fastapi.py +0 -0
  141. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/fpdf2.py +0 -0
  142. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/functions.py +0 -0
  143. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/functools.py +0 -0
  144. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/getpass.py +0 -0
  145. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/gzip.py +0 -0
  146. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/hashlib.py +0 -0
  147. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/http.py +0 -0
  148. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/hypothesis.py +0 -0
  149. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/importlib.py +0 -0
  150. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/inflect.py +0 -0
  151. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/ipython.py +0 -0
  152. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/iterables.py +0 -0
  153. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/json.py +0 -0
  154. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/jupyter.py +0 -0
  155. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/libcst.py +0 -0
  156. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/lightweight_charts.py +0 -0
  157. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/logging.py +0 -0
  158. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/math.py +0 -0
  159. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/memory_profiler.py +0 -0
  160. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/modules.py +0 -0
  161. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/more_itertools.py +0 -0
  162. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/numpy.py +0 -0
  163. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/operator.py +0 -0
  164. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/optuna.py +0 -0
  165. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/os.py +0 -0
  166. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/parse.py +0 -0
  167. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pathlib.py +0 -0
  168. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pickle.py +0 -0
  169. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/platform.py +0 -0
  170. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/polars.py +0 -0
  171. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/polars_ols.py +0 -0
  172. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/postgres.py +0 -0
  173. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pottery.py +0 -0
  174. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pqdm.py +0 -0
  175. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/psutil.py +0 -0
  176. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/py.typed +0 -0
  177. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pyinstrument.py +0 -0
  178. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pytest.py +0 -0
  179. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pytest_plugins/__init__.py +0 -0
  180. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  181. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  182. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/pytest_regressions.py +0 -0
  183. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/random.py +0 -0
  184. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/re.py +0 -0
  185. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/reprlib.py +0 -0
  186. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/scipy.py +0 -0
  187. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/sentinel.py +0 -0
  188. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/shelve.py +0 -0
  189. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/slack_sdk.py +0 -0
  190. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/socket.py +0 -0
  191. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/sqlalchemy.py +0 -0
  192. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/sqlalchemy_polars.py +0 -0
  193. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/statsmodels.py +0 -0
  194. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/string.py +0 -0
  195. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/tempfile.py +0 -0
  196. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/testbook.py +0 -0
  197. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/text.py +0 -0
  198. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/threading.py +0 -0
  199. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/timer.py +0 -0
  200. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/traceback.py +0 -0
  201. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/typed_settings.py +0 -0
  202. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/types.py +0 -0
  203. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/typing.py +0 -0
  204. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/tzdata.py +0 -0
  205. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/tzlocal.py +0 -0
  206. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/uuid.py +0 -0
  207. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/version.py +0 -0
  208. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/warnings.py +0 -0
  209. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/whenever.py +0 -0
  210. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/zipfile.py +0 -0
  211. {dycw_utilities-0.162.6 → dycw_utilities-0.162.8}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.162.6
3
+ Version: 0.162.8
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -230,7 +230,7 @@ dependencies = [
230
230
  name = "dycw-utilities"
231
231
  readme = "README.md"
232
232
  requires-python = ">= 3.12"
233
- version = "0.162.6"
233
+ version = "0.162.8"
234
234
 
235
235
  [project.entry-points.pytest11]
236
236
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -263,7 +263,7 @@ test = [
263
263
  # bump-my-version
264
264
  [tool.bumpversion]
265
265
  allow_dirty = true
266
- current_version = "0.162.6"
266
+ current_version = "0.162.8"
267
267
 
268
268
  [[tool.bumpversion.files]]
269
269
  filename = "src/utilities/__init__.py"
@@ -20,7 +20,6 @@ from hypothesis.strategies import (
20
20
  sets,
21
21
  tuples,
22
22
  )
23
- from orjson import JSONDecodeError
24
23
  from polars import Object, String, UInt64
25
24
  from pytest import approx, mark, param, raises
26
25
  from whenever import ZonedDateTime
@@ -73,6 +72,7 @@ from utilities.orjson import (
73
72
  OrjsonFormatter,
74
73
  OrjsonLogRecord,
75
74
  Unserializable,
75
+ _DeserializeInvalidJSONError,
76
76
  _DeserializeNoObjectsError,
77
77
  _DeserializeObjectNotFoundError,
78
78
  _object_hook_get_object,
@@ -428,7 +428,7 @@ class TestGetLogRecords:
428
428
  assert result.num_lines_error == 1
429
429
  assert result.missing == set()
430
430
  assert len(result.other_errors) == 1
431
- assert isinstance(one(result.other_errors), JSONDecodeError)
431
+ assert isinstance(one(result.other_errors), _DeserializeInvalidJSONError)
432
432
 
433
433
 
434
434
  class TestOrjsonFormatter:
@@ -753,6 +753,14 @@ class TestSerialize:
753
753
  assert result == expected
754
754
 
755
755
 
756
+ class TestDeserialize:
757
+ def test_error_invalid_json(self) -> None:
758
+ with raises(
759
+ _DeserializeInvalidJSONError, match="Invalid JSON: b'invalid json'"
760
+ ):
761
+ _ = deserialize(b"invalid json")
762
+
763
+
756
764
  class TestObjectHookGetObject:
757
765
  def test_main(self) -> None:
758
766
  result = _object_hook_get_object(Sentinel.__qualname__, objects={Sentinel})
@@ -2,7 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from asyncio import Queue
4
4
  from itertools import chain, repeat
5
- from typing import TYPE_CHECKING, Any
5
+ from logging import getLogger
6
+ from typing import TYPE_CHECKING, Any, ClassVar, cast
6
7
 
7
8
  from hypothesis import HealthCheck, Phase, given, settings
8
9
  from hypothesis.strategies import (
@@ -15,18 +16,22 @@ from hypothesis.strategies import (
15
16
  permutations,
16
17
  sampled_from,
17
18
  )
18
- from pytest import mark, param, raises
19
+ from pytest import LogCaptureFixture, fixture, mark, param, raises
19
20
  from redis.asyncio import Redis
20
21
  from redis.asyncio.client import PubSub
21
22
 
22
23
  from tests.conftest import SKIPIF_CI_AND_NOT_LINUX
23
24
  from tests.test_objects.objects import objects
24
25
  from utilities.asyncio import get_items_nowait, sleep_td
26
+ from utilities.functions import get_class_name, identity
25
27
  from utilities.hypothesis import int64s, pairs, text_ascii
28
+ from utilities.iterables import one
26
29
  from utilities.operator import is_equal
27
30
  from utilities.orjson import deserialize, serialize
28
31
  from utilities.redis import (
29
32
  PublishError,
33
+ _decoded_data,
34
+ _handle_message,
30
35
  _is_message,
31
36
  _RedisMessage,
32
37
  publish,
@@ -50,6 +55,98 @@ if TYPE_CHECKING:
50
55
  _PUB_SUB_SLEEP: TimeDelta = 0.1 * SECOND
51
56
 
52
57
 
58
+ @fixture
59
+ def queue() -> Queue[Any]:
60
+ return Queue()
61
+
62
+
63
+ class TestHandleMessage:
64
+ message: ClassVar[_RedisMessage] = _RedisMessage(
65
+ type="message", pattern=None, channel=b"channel", data=b"data"
66
+ )
67
+
68
+ def test_main(self, *, queue: Queue[Any]) -> None:
69
+ _handle_message(self.message, identity, queue)
70
+ assert queue.qsize() == 1
71
+ assert queue.get_nowait() == self.message
72
+
73
+ def test_transform(self, *, queue: Queue[Any]) -> None:
74
+ _handle_message(self.message, _decoded_data, queue)
75
+ assert queue.qsize() == 1
76
+ assert queue.get_nowait() == "data"
77
+
78
+ def test_error_transform_no_handler(self, *, queue: Queue[Any]) -> None:
79
+ class CustomError(Exception): ...
80
+
81
+ def transform(message: _RedisMessage, /) -> None:
82
+ raise CustomError(message)
83
+
84
+ _handle_message(self.message, transform, queue)
85
+ assert queue.empty()
86
+
87
+ def test_error_transform_with_handler(
88
+ self, *, caplog: LogCaptureFixture, queue: Queue[Any]
89
+ ) -> None:
90
+ logger = getLogger(name := unique_str())
91
+
92
+ class CustomError(Exception): ...
93
+
94
+ def transform(message: _RedisMessage, /) -> None:
95
+ raise CustomError(message)
96
+
97
+ def error_transform(message: _RedisMessage, error: Exception, /) -> None:
98
+ logger.warning("Got %r transforming %r", get_class_name(error), message)
99
+
100
+ _handle_message(self.message, transform, queue, error_transform=error_transform)
101
+ assert queue.empty()
102
+ record = one(r for r in caplog.records if r.name == name)
103
+ assert record.message == f"Got 'CustomError' transforming {self.message}"
104
+
105
+ @mark.parametrize(
106
+ ("min_length", "expected"),
107
+ [param(3, False), param(4, False), param(5, True), param(6, True)],
108
+ )
109
+ def test_filter(
110
+ self, *, queue: Queue[Any], min_length: int, expected: bool
111
+ ) -> None:
112
+ _handle_message(
113
+ self.message,
114
+ _decoded_data,
115
+ queue,
116
+ filter_=lambda text: len(text) >= min_length,
117
+ )
118
+ result = queue.empty()
119
+ assert result is expected
120
+
121
+ def test_error_filter_no_handler(self, *, queue: Queue[Any]) -> None:
122
+ _handle_message(
123
+ self.message,
124
+ identity,
125
+ queue,
126
+ filter_=lambda text: cast("Any", text)["invalid"],
127
+ )
128
+ assert queue.empty()
129
+
130
+ def test_error_filter_with_handler(
131
+ self, *, caplog: LogCaptureFixture, queue: Queue[Any]
132
+ ) -> None:
133
+ logger = getLogger(name := unique_str())
134
+
135
+ def error_filter(message: _RedisMessage, error: Exception, /) -> None:
136
+ logger.warning("Got %r filtering %r", get_class_name(error), message)
137
+
138
+ _handle_message(
139
+ self.message,
140
+ identity,
141
+ queue,
142
+ filter_=lambda text: cast("Any", text)["invalid"],
143
+ error_filter=error_filter,
144
+ )
145
+ assert queue.empty()
146
+ record = one(r for r in caplog.records if r.name == name)
147
+ assert record.message == f"Got 'KeyError' filtering {self.message}"
148
+
149
+
53
150
  class TestIsMessage:
54
151
  @mark.parametrize(
55
152
  ("message", "channels", "expected"),
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.162.6"
3
+ __version__ = "0.162.8"
@@ -20,6 +20,7 @@ from orjson import (
20
20
  OPT_PASSTHROUGH_DATACLASS,
21
21
  OPT_PASSTHROUGH_DATETIME,
22
22
  OPT_SORT_KEYS,
23
+ JSONDecodeError,
23
24
  dumps,
24
25
  loads,
25
26
  )
@@ -371,8 +372,12 @@ def deserialize(
371
372
  redirects: Mapping[str, type[Any]] | None = None,
372
373
  ) -> Any:
373
374
  """Deserialize an object."""
375
+ try:
376
+ obj = loads(data)
377
+ except JSONDecodeError:
378
+ raise _DeserializeInvalidJSONError(data=data) from None
374
379
  return _object_hook(
375
- loads(data),
380
+ obj,
376
381
  data=data,
377
382
  dataclass_hook=dataclass_hook,
378
383
  objects=objects,
@@ -380,6 +385,11 @@ def deserialize(
380
385
  )
381
386
 
382
387
 
388
+ @dataclass(kw_only=True, slots=True)
389
+ class DeerializeError(Exception):
390
+ obj: Any
391
+
392
+
383
393
  (
384
394
  _DATE_PATTERN,
385
395
  _DATE_DELTA_PATTERN,
@@ -739,11 +749,19 @@ def _object_hook_get_object(
739
749
  @dataclass(kw_only=True, slots=True)
740
750
  class DeserializeError(Exception):
741
751
  data: bytes
742
- qualname: str
752
+
753
+
754
+ @dataclass(kw_only=True, slots=True)
755
+ class _DeserializeInvalidJSONError(DeserializeError):
756
+ @override
757
+ def __str__(self) -> str:
758
+ return f"Invalid JSON: {self.data!r}"
743
759
 
744
760
 
745
761
  @dataclass(kw_only=True, slots=True)
746
762
  class _DeserializeNoObjectsError(DeserializeError):
763
+ qualname: str
764
+
747
765
  @override
748
766
  def __str__(self) -> str:
749
767
  return f"Objects required to deserialize {self.qualname!r} from {self.data!r}"
@@ -751,6 +769,8 @@ class _DeserializeNoObjectsError(DeserializeError):
751
769
 
752
770
  @dataclass(kw_only=True, slots=True)
753
771
  class _DeserializeObjectNotFoundError(DeserializeError):
772
+ qualname: str
773
+
754
774
  @override
755
775
  def __str__(self) -> str:
756
776
  return (
@@ -639,9 +639,11 @@ def subscribe(
639
639
  /,
640
640
  *,
641
641
  timeout: Delta | None = _SUBSCRIBE_TIMEOUT,
642
- sleep: Delta = _SUBSCRIBE_SLEEP,
643
642
  output: Literal["raw"],
644
- filter_: Callable[[_RedisMessage], bool] | None = None,
643
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
644
+ filter_: Callable[[bytes], bool] | None = None,
645
+ error_filter: Callable[[bytes, Exception], None] | None = None,
646
+ sleep: Delta = _SUBSCRIBE_SLEEP,
645
647
  ) -> AsyncIterator[Task[None]]: ...
646
648
  @overload
647
649
  @enhanced_async_context_manager
@@ -652,9 +654,11 @@ def subscribe(
652
654
  /,
653
655
  *,
654
656
  timeout: Delta | None = _SUBSCRIBE_TIMEOUT,
655
- sleep: Delta = _SUBSCRIBE_SLEEP,
656
657
  output: Literal["bytes"],
658
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
657
659
  filter_: Callable[[bytes], bool] | None = None,
660
+ error_filter: Callable[[bytes, Exception], None] | None = None,
661
+ sleep: Delta = _SUBSCRIBE_SLEEP,
658
662
  ) -> AsyncIterator[Task[None]]: ...
659
663
  @overload
660
664
  @enhanced_async_context_manager
@@ -665,9 +669,11 @@ def subscribe(
665
669
  /,
666
670
  *,
667
671
  timeout: Delta | None = _SUBSCRIBE_TIMEOUT,
668
- sleep: Delta = _SUBSCRIBE_SLEEP,
669
672
  output: Literal["text"] = "text",
673
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
670
674
  filter_: Callable[[str], bool] | None = None,
675
+ error_filter: Callable[[str, Exception], None] | None = None,
676
+ sleep: Delta = _SUBSCRIBE_SLEEP,
671
677
  ) -> AsyncIterator[Task[None]]: ...
672
678
  @overload
673
679
  @enhanced_async_context_manager
@@ -678,34 +684,35 @@ def subscribe[T](
678
684
  /,
679
685
  *,
680
686
  timeout: Delta | None = _SUBSCRIBE_TIMEOUT,
681
- sleep: Delta = _SUBSCRIBE_SLEEP,
682
687
  output: Callable[[bytes], T],
688
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
683
689
  filter_: Callable[[T], bool] | None = None,
690
+ error_filter: Callable[[T, Exception], None] | None = None,
691
+ sleep: Delta = _SUBSCRIBE_SLEEP,
684
692
  ) -> AsyncIterator[Task[None]]: ...
685
693
  @enhanced_async_context_manager
686
- async def subscribe[T](
694
+ async def subscribe[T, U](
687
695
  redis: Redis,
688
696
  channels: MaybeIterable[str],
689
697
  queue: Queue[_RedisMessage] | Queue[bytes] | Queue[T],
690
698
  /,
691
699
  *,
692
700
  timeout: Delta | None = _SUBSCRIBE_TIMEOUT,
693
- sleep: Delta = _SUBSCRIBE_SLEEP,
694
701
  output: Literal["raw", "bytes", "text"] | Callable[[bytes], T] = "text",
695
- filter_: Callable[[Any], bool] | None = None,
702
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
703
+ filter_: Callable[[T], bool] | None = None,
704
+ error_filter: Callable[[T, Exception], None] | None = None,
705
+ sleep: Delta = _SUBSCRIBE_SLEEP,
696
706
  ) -> AsyncIterator[Task[None]]:
697
707
  """Subscribe to the data of a given channel(s)."""
698
708
  channels = list(always_iterable(channels)) # skipif-ci-and-not-linux
699
709
  match output: # skipif-ci-and-not-linux
700
710
  case "raw":
701
- transform = cast("Any", identity)
711
+ transform = cast("Callable[[_RedisMessage], T]", identity)
702
712
  case "bytes":
703
- transform = cast("Any", itemgetter("data"))
713
+ transform = cast("Callable[[_RedisMessage], T]", itemgetter("data"))
704
714
  case "text":
705
-
706
- def transform(message: _RedisMessage, /) -> str: # pyright: ignore[reportRedeclaration]
707
- return message["data"].decode()
708
-
715
+ transform = cast("Callable[[_RedisMessage], T]", _decoded_data)
709
716
  case Callable() as deserialize:
710
717
 
711
718
  def transform(message: _RedisMessage, /) -> T:
@@ -721,8 +728,10 @@ async def subscribe[T](
721
728
  transform,
722
729
  queue,
723
730
  timeout=timeout,
724
- sleep=sleep,
731
+ error_transform=error_transform,
725
732
  filter_=filter_,
733
+ error_filter=error_filter,
734
+ sleep=sleep,
726
735
  )
727
736
  )
728
737
  try: # skipif-ci-and-not-linux
@@ -737,16 +746,22 @@ async def subscribe[T](
737
746
  await task
738
747
 
739
748
 
740
- async def _subscribe_core(
749
+ def _decoded_data(message: _RedisMessage, /) -> str:
750
+ return message["data"].decode()
751
+
752
+
753
+ async def _subscribe_core[T](
741
754
  redis: Redis,
742
755
  channels: MaybeIterable[str],
743
- transform: Callable[[_RedisMessage], Any],
756
+ transform: Callable[[_RedisMessage], T],
744
757
  queue: Queue[Any],
745
758
  /,
746
759
  *,
747
760
  timeout: Delta | None = _SUBSCRIBE_TIMEOUT,
761
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
762
+ filter_: Callable[[T], bool] | None = None,
763
+ error_filter: Callable[[T, Exception], None] | None = None,
748
764
  sleep: Delta = _SUBSCRIBE_SLEEP,
749
- filter_: Callable[[Any], bool] | None = None,
750
765
  ) -> None:
751
766
  timeout_use = ( # skipif-ci-and-not-linux
752
767
  None if timeout is None else to_seconds(timeout)
@@ -758,11 +773,15 @@ async def _subscribe_core(
758
773
  while True:
759
774
  message = await pubsub.get_message(timeout=timeout_use)
760
775
  if is_subscribe_message(message):
761
- transformed = transform(message)
762
- if (filter_ is None) or filter_(transformed):
763
- queue.put_nowait(transformed)
764
- else:
765
- await sleep_td(sleep)
776
+ _handle_message(
777
+ message,
778
+ transform,
779
+ queue,
780
+ error_transform=error_transform,
781
+ filter_=filter_,
782
+ error_filter=error_filter,
783
+ )
784
+ await sleep_td(sleep)
766
785
 
767
786
 
768
787
  def _is_message(
@@ -781,6 +800,33 @@ def _is_message(
781
800
  )
782
801
 
783
802
 
803
+ def _handle_message[T](
804
+ message: _RedisMessage,
805
+ transform: Callable[[_RedisMessage], T],
806
+ queue: Queue[Any],
807
+ /,
808
+ *,
809
+ error_transform: Callable[[_RedisMessage, Exception], None] | None = None,
810
+ filter_: Callable[[T], bool] | None = None,
811
+ error_filter: Callable[[T, Exception], None] | None = None,
812
+ ) -> None:
813
+ try:
814
+ transformed = transform(message)
815
+ except Exception as error: # noqa: BLE001
816
+ if error_transform is not None:
817
+ error_transform(message, error)
818
+ return
819
+ if filter_ is None:
820
+ queue.put_nowait(transformed)
821
+ return
822
+ try:
823
+ if filter_(transformed):
824
+ queue.put_nowait(transformed)
825
+ except Exception as error: # noqa: BLE001
826
+ if error_filter is not None:
827
+ error_filter(transformed, error)
828
+
829
+
784
830
  class _RedisMessage(TypedDict):
785
831
  type: Literal["subscribe", "psubscribe", "message", "pmessage"]
786
832
  pattern: str | None