dycw-utilities 0.110.2__tar.gz → 0.110.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/PKG-INFO +1 -1
  2. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/pyproject.toml +2 -2
  3. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_dataclasses.py +97 -64
  4. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_orjson.py +1 -0
  5. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_parse.py +46 -26
  6. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_text.py +11 -2
  7. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_typing_funcs/with_future.py +16 -0
  8. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/__init__.py +1 -1
  9. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/dataclasses.py +42 -17
  10. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/parse.py +62 -9
  11. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/text.py +48 -21
  12. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/types.py +2 -0
  13. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/.gitignore +0 -0
  14. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/LICENSE +0 -0
  15. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/README.md +0 -0
  16. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/__init__.py +0 -0
  17. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/conftest.py +0 -0
  18. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/__init__.py +0 -0
  19. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_missing/__init__.py +0 -0
  20. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_missing/module.py +0 -0
  21. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/__init__.py +0 -0
  22. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/outer_1.py +0 -0
  23. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/outer_2.py +0 -0
  24. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  25. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  26. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  27. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  28. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_without/__init__.py +0 -0
  29. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_without/module_1.py +0 -0
  30. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/package_without/module_2.py +0 -0
  31. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/standalone.py +0 -0
  32. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/modules/with_imports.py +0 -0
  33. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  34. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  35. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  36. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  37. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  38. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  39. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  40. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  41. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/__init__.py +0 -0
  42. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/test_async_service/__init__.py +0 -0
  43. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/test_async_service/__main__.py +0 -0
  44. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/test_async_service/run.sh +0 -0
  45. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
  46. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
  47. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/scripts/test_queue_processor/run.sh +0 -0
  48. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_altair.py +0 -0
  49. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_astor.py +0 -0
  50. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_asyncio.py +0 -0
  51. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_atomicwrites.py +0 -0
  52. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_atools.py +0 -0
  53. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_cachetools.py +0 -0
  54. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_click.py +0 -0
  55. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_concurrent.py +0 -0
  56. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_contextlib.py +0 -0
  57. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_contextvars.py +0 -0
  58. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_cryptography.py +0 -0
  59. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_cvxpy.py +0 -0
  60. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_datetime.py +0 -0
  61. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_enum.py +0 -0
  62. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_errors.py +0 -0
  63. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_eventkit.py +0 -0
  64. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_fastapi.py +0 -0
  65. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_fpdf2.py +0 -0
  66. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_functions.py +0 -0
  67. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_functools.py +0 -0
  68. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_getpass.py +0 -0
  69. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_git.py +0 -0
  70. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_hashlib.py +0 -0
  71. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_http.py +0 -0
  72. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_hypothesis.py +0 -0
  73. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_ipython.py +0 -0
  74. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_iterables.py +0 -0
  75. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_jupyter.py +0 -0
  76. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_lightweight_charts.py +0 -0
  77. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_logging.py +0 -0
  78. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_loguru.py +0 -0
  79. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_luigi.py +0 -0
  80. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_math.py +0 -0
  81. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_memory_profiler.py +0 -0
  82. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_modules.py +0 -0
  83. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_more_itertools.py +0 -0
  84. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_numpy.py +0 -0
  85. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_operator.py +0 -0
  86. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_optuna.py +0 -0
  87. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_os.py +0 -0
  88. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pathlib.py +0 -0
  89. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_period.py +0 -0
  90. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pickle.py +0 -0
  91. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_platform.py +0 -0
  92. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_polars.py +0 -0
  93. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_polars_ols.py +0 -0
  94. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pqdm.py +0 -0
  95. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pydantic.py +0 -0
  96. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pyinstrument.py +0 -0
  97. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pyrsistent.py +0 -0
  98. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pytest.py +0 -0
  99. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_pytest_regressions.py +0 -0
  100. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_python_dotenv.py +0 -0
  101. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_random.py +0 -0
  102. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_re.py +0 -0
  103. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_redis.py +0 -0
  104. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_reprlib.py +0 -0
  105. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_rich.py +0 -0
  106. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_scipy.py +0 -0
  107. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_sentinel.py +0 -0
  108. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_shelve.py +0 -0
  109. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_slack_sdk.py +0 -0
  110. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_socket.py +0 -0
  111. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_sqlalchemy.py +0 -0
  112. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_statsmodel.py +0 -0
  114. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_sys.py +0 -0
  116. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_tempfile.py +0 -0
  117. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_tenacity.py +0 -0
  118. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_threading.py +0 -0
  119. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_timer.py +0 -0
  120. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback.py +0 -0
  121. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/__init__.py +0 -0
  122. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/chain.py +0 -0
  123. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  124. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  125. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  126. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/many.py +0 -0
  127. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/one.py +0 -0
  128. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/recursive.py +0 -0
  129. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  130. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  131. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/two.py +0 -0
  132. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_traceback_funcs/untraced.py +0 -0
  133. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_types.py +0 -0
  134. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_typing.py +0 -0
  135. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_typing_funcs/__init__.py +0 -0
  136. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_typing_funcs/no_future.py +0 -0
  137. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_tzdata.py +0 -0
  138. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_tzlocal.py +0 -0
  139. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_uuid.py +0 -0
  140. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_version.py +0 -0
  141. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_warnings.py +0 -0
  142. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_whenever.py +0 -0
  143. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_zipfile.py +0 -0
  144. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/tests/test_zoneinfo.py +0 -0
  145. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/altair.py +0 -0
  146. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/astor.py +0 -0
  147. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/asyncio.py +0 -0
  148. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/atomicwrites.py +0 -0
  149. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/atools.py +0 -0
  150. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/cachetools.py +0 -0
  151. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/click.py +0 -0
  152. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/concurrent.py +0 -0
  153. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/contextlib.py +0 -0
  154. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/contextvars.py +0 -0
  155. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/cryptography.py +0 -0
  156. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/cvxpy.py +0 -0
  157. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/datetime.py +0 -0
  158. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/enum.py +0 -0
  159. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/errors.py +0 -0
  160. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/eventkit.py +0 -0
  161. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/fastapi.py +0 -0
  162. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/fpdf2.py +0 -0
  163. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/functions.py +0 -0
  164. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/functools.py +0 -0
  165. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/getpass.py +0 -0
  166. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/git.py +0 -0
  167. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/hashlib.py +0 -0
  168. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/http.py +0 -0
  169. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/hypothesis.py +0 -0
  170. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/ipython.py +0 -0
  171. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/iterables.py +0 -0
  172. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/jupyter.py +0 -0
  173. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/lightweight_charts.py +0 -0
  174. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/logging.py +0 -0
  175. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/loguru.py +0 -0
  176. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/luigi.py +0 -0
  177. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/math.py +0 -0
  178. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/memory_profiler.py +0 -0
  179. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/modules.py +0 -0
  180. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/more_itertools.py +0 -0
  181. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/numpy.py +0 -0
  182. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/operator.py +0 -0
  183. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/optuna.py +0 -0
  184. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/orjson.py +0 -0
  185. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/os.py +0 -0
  186. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pathlib.py +0 -0
  187. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/period.py +0 -0
  188. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pickle.py +0 -0
  189. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/platform.py +0 -0
  190. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/polars.py +0 -0
  191. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/polars_ols.py +0 -0
  192. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pqdm.py +0 -0
  193. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/py.typed +0 -0
  194. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pydantic.py +0 -0
  195. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pyinstrument.py +0 -0
  196. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pyrsistent.py +0 -0
  197. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pytest.py +0 -0
  198. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/pytest_regressions.py +0 -0
  199. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/python_dotenv.py +0 -0
  200. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/random.py +0 -0
  201. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/re.py +0 -0
  202. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/redis.py +0 -0
  203. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/reprlib.py +0 -0
  204. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/rich.py +0 -0
  205. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/scipy.py +0 -0
  206. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/sentinel.py +0 -0
  207. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/shelve.py +0 -0
  208. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/slack_sdk.py +0 -0
  209. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/socket.py +0 -0
  210. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/sqlalchemy.py +0 -0
  211. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/sqlalchemy_polars.py +0 -0
  212. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/statsmodels.py +0 -0
  213. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/streamlit.py +0 -0
  214. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/sys.py +0 -0
  215. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/tempfile.py +0 -0
  216. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/tenacity.py +0 -0
  217. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/threading.py +0 -0
  218. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/timer.py +0 -0
  219. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/traceback.py +0 -0
  220. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/typing.py +0 -0
  221. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/tzdata.py +0 -0
  222. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/tzlocal.py +0 -0
  223. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/uuid.py +0 -0
  224. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/version.py +0 -0
  225. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/warnings.py +0 -0
  226. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/whenever.py +0 -0
  227. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/zipfile.py +0 -0
  228. {dycw_utilities-0.110.2 → dycw_utilities-0.110.4}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.110.2
3
+ Version: 0.110.4
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.110.2"
97
+ version = "0.110.4"
98
98
 
99
99
  [project.optional-dependencies]
100
100
  test = [
@@ -337,7 +337,7 @@ zzz-test-zoneinfo = [
337
337
  # bump-my-version
338
338
  [tool.bumpversion]
339
339
  allow_dirty = true
340
- current_version = "0.110.2"
340
+ current_version = "0.110.4"
341
341
 
342
342
  [[tool.bumpversion.files]]
343
343
  filename = "src/utilities/__init__.py"
@@ -38,6 +38,7 @@ from utilities.dataclasses import (
38
38
  OneFieldNonUniqueError,
39
39
  StrMappingToFieldMappingError,
40
40
  YieldFieldsError,
41
+ _parse_dataclass_split_key_value_pairs,
41
42
  _ParseDataClassParseValueError,
42
43
  _ParseDataClassSplitKeyValuePairsDuplicateKeysError,
43
44
  _ParseDataClassSplitKeyValuePairsSplitError,
@@ -364,6 +365,14 @@ class TestOneField:
364
365
  )
365
366
 
366
367
 
368
+ class TestParseDataClassSplitKeyValuePairs:
369
+ @given(text=sampled_from(["a=1,b=22,c=333", "{a=1,b=22,c=333}"]))
370
+ def test_main(self, *, text: str) -> None:
371
+ result = _parse_dataclass_split_key_value_pairs(text, DataClassFutureInt)
372
+ expected = {"a": "1", "b": "22", "c": "333"}
373
+ assert result == expected
374
+
375
+
367
376
  class TestReplaceNonSentinel:
368
377
  def test_main(self) -> None:
369
378
  obj = DataClassFutureIntDefault()
@@ -382,6 +391,94 @@ class TestReplaceNonSentinel:
382
391
  assert obj.int_ == 1
383
392
 
384
393
 
394
+ class TestSerializeAndParseDataClass:
395
+ @given(int_=integers())
396
+ def test_main_future_int(self, *, int_: int) -> None:
397
+ obj = DataClassFutureInt(int_=int_)
398
+ serialized = serialize_dataclass(obj)
399
+ result = parse_dataclass(serialized, DataClassFutureInt)
400
+ assert result == obj
401
+
402
+ def test_main_future_int_default(self) -> None:
403
+ obj = DataClassFutureIntDefault()
404
+ serialized = serialize_dataclass(obj)
405
+ result = parse_dataclass(serialized, DataClassFutureIntDefault)
406
+ assert result == obj
407
+
408
+ @given(int_=integers())
409
+ def test_extra_type(self, *, int_: int) -> None:
410
+ obj = DataClassFutureNestedOuterFirstOuter(
411
+ inner=DataClassFutureNestedOuterFirstInner(int_=int_)
412
+ )
413
+
414
+ def serializer(obj: DataClassFutureNestedOuterFirstInner, /) -> str:
415
+ return serialize_dataclass(obj)
416
+
417
+ serialized = serialize_dataclass(
418
+ obj, extra_serializers={DataClassFutureNestedOuterFirstInner: serializer}
419
+ )
420
+ result = parse_dataclass(
421
+ serialized,
422
+ DataClassFutureNestedOuterFirstOuter,
423
+ globalns=globals(),
424
+ extra_parsers={
425
+ DataClassFutureNestedOuterFirstInner: lambda text: parse_dataclass(
426
+ text, DataClassFutureNestedOuterFirstInner
427
+ )
428
+ },
429
+ )
430
+ assert result == obj
431
+
432
+ @given(key=sampled_from(["int_", "INT_"]), int_=integers())
433
+ def test_parse_text_case_insensitive(self, *, key: str, int_: int) -> None:
434
+ result = parse_dataclass(f"{key}={int_}", DataClassFutureInt)
435
+ expected = DataClassFutureInt(int_=int_)
436
+ assert result == expected
437
+
438
+ @given(int_=integers())
439
+ def test_parse_text_case_sensitive(self, *, int_: int) -> None:
440
+ result = parse_dataclass(
441
+ f"int_={int_}", DataClassFutureInt, case_sensitive=True
442
+ )
443
+ expected = DataClassFutureInt(int_=int_)
444
+ assert result == expected
445
+
446
+ @given(key=sampled_from(["int_", "INT_"]), int_=integers())
447
+ def test_parse_mapping_case_insensitive(self, *, key: str, int_: int) -> None:
448
+ result = parse_dataclass({key: str(int_)}, DataClassFutureInt)
449
+ expected = DataClassFutureInt(int_=int_)
450
+ assert result == expected
451
+
452
+ @given(int_=integers())
453
+ def test_parse_mapping_case_sensitive(self, *, int_: int) -> None:
454
+ result = parse_dataclass(
455
+ {"int_": str(int_)}, DataClassFutureInt, case_sensitive=True
456
+ )
457
+ expected = DataClassFutureInt(int_=int_)
458
+ assert result == expected
459
+
460
+ def test_parser_split_key_value_pairs_split(self) -> None:
461
+ with raises(
462
+ _ParseDataClassSplitKeyValuePairsSplitError,
463
+ match="Unable to construct 'DataClassFutureInt'; failed to split key-value pair 'bbb'",
464
+ ):
465
+ _ = parse_dataclass("a=1,bbb,c=333", DataClassFutureInt)
466
+
467
+ def test_error_parse_split_key_value_pairs_duplicate(self) -> None:
468
+ with raises(
469
+ _ParseDataClassSplitKeyValuePairsDuplicateKeysError,
470
+ match=r"Unable to construct 'DataClassFutureInt' since there are duplicate keys; got \{'b': 2\}",
471
+ ):
472
+ _ = parse_dataclass("a=1,b=22a,b=22b,c=3", DataClassFutureInt)
473
+
474
+ def test_error_parse_value(self) -> None:
475
+ with raises(
476
+ _ParseDataClassParseValueError,
477
+ match="Unable to construct 'DataClassFutureInt'; unable to parse field 'int_' of type <class 'int'>; got 'invalid'",
478
+ ):
479
+ _ = parse_dataclass("int_=invalid", DataClassFutureInt)
480
+
481
+
385
482
  class TestStrMappingToFieldMapping:
386
483
  @given(key=sampled_from(["int_", "INT_"]), int_=integers())
387
484
  def test_main_text_case_insensitive(self, *, key: str, int_: int) -> None:
@@ -467,70 +564,6 @@ class TestStrMappingToFieldMapping:
467
564
  )
468
565
 
469
566
 
470
- class TestSerializeAndParseDataClass:
471
- @given(int_=integers())
472
- def test_main_future_int(self, *, int_: int) -> None:
473
- obj = DataClassFutureInt(int_=int_)
474
- serialized = serialize_dataclass(obj)
475
- result = parse_dataclass(serialized, DataClassFutureInt)
476
- assert result == obj
477
-
478
- def test_main_future_int_default(self) -> None:
479
- obj = DataClassFutureIntDefault()
480
- serialized = serialize_dataclass(obj)
481
- result = parse_dataclass(serialized, DataClassFutureIntDefault)
482
- assert result == obj
483
-
484
- @given(key=sampled_from(["int_", "INT_"]), int_=integers())
485
- def test_parse_text_case_insensitive(self, *, key: str, int_: int) -> None:
486
- result = parse_dataclass(f"{key}={int_}", DataClassFutureInt)
487
- expected = DataClassFutureInt(int_=int_)
488
- assert result == expected
489
-
490
- @given(int_=integers())
491
- def test_parse_text_case_sensitive(self, *, int_: int) -> None:
492
- result = parse_dataclass(
493
- f"int_={int_}", DataClassFutureInt, case_sensitive=True
494
- )
495
- expected = DataClassFutureInt(int_=int_)
496
- assert result == expected
497
-
498
- @given(key=sampled_from(["int_", "INT_"]), int_=integers())
499
- def test_parse_mapping_case_insensitive(self, *, key: str, int_: int) -> None:
500
- result = parse_dataclass({key: str(int_)}, DataClassFutureInt)
501
- expected = DataClassFutureInt(int_=int_)
502
- assert result == expected
503
-
504
- @given(int_=integers())
505
- def test_parse_mapping_case_sensitive(self, *, int_: int) -> None:
506
- result = parse_dataclass(
507
- {"int_": str(int_)}, DataClassFutureInt, case_sensitive=True
508
- )
509
- expected = DataClassFutureInt(int_=int_)
510
- assert result == expected
511
-
512
- def test_parser_split_key_value_pairs_split(self) -> None:
513
- with raises(
514
- _ParseDataClassSplitKeyValuePairsSplitError,
515
- match="Unable to construct 'DataClassFutureInt'; failed to split key-value pair 'bbb'",
516
- ):
517
- _ = parse_dataclass("a=1,bbb,c=333", DataClassFutureInt)
518
-
519
- def test_error_parse_split_key_value_pairs_duplicate(self) -> None:
520
- with raises(
521
- _ParseDataClassSplitKeyValuePairsDuplicateKeysError,
522
- match=r"Unable to construct 'DataClassFutureInt' since there are duplicate keys; got \{'b': 2\}",
523
- ):
524
- _ = parse_dataclass("a=1,b=22a,b=22b,c=3", DataClassFutureInt)
525
-
526
- def test_error_parse_value(self) -> None:
527
- with raises(
528
- _ParseDataClassParseValueError,
529
- match="Unable to construct 'DataClassFutureInt'; unable to parse field 'int_' of type <class 'int'>; got 'invalid'",
530
- ):
531
- _ = parse_dataclass("int_=invalid", DataClassFutureInt)
532
-
533
-
534
567
  class TestYieldFields:
535
568
  def test_class_no_future_int(self) -> None:
536
569
  result = one(yield_fields(DataClassNoFutureInt))
@@ -147,6 +147,7 @@ class TestGetLogRecords:
147
147
  ),
148
148
  root=temp_paths(),
149
149
  )
150
+ @mark.flaky
150
151
  def test_dataframe(
151
152
  self, *, root: Path, items: Sequence[tuple[LogLevel, str, StrMapping]]
152
153
  ) -> None:
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
4
  from collections.abc import Iterable
5
- from dataclasses import dataclass
6
5
  from pathlib import Path
7
6
  from types import NoneType
8
7
  from typing import Final, Literal
@@ -25,10 +24,13 @@ from pytest import raises
25
24
  from tests.test_operator import TruthEnum
26
25
  from tests.test_typing_funcs.with_future import (
27
26
  DataClassFutureInt,
27
+ DataClassFutureIntChild,
28
28
  DataClassFutureIntEven,
29
29
  DataClassFutureIntEvenOrOddTypeUnion,
30
30
  DataClassFutureIntEvenOrOddUnion,
31
31
  DataClassFutureIntOdd,
32
+ DataClassFutureIntParentFirst,
33
+ DataClassFutureIntParentSecond,
32
34
  TrueOrFalseFutureLit,
33
35
  TrueOrFalseFutureTypeLit,
34
36
  )
@@ -48,6 +50,8 @@ from utilities.math import is_equal
48
50
  from utilities.parse import (
49
51
  _ParseObjectExtraNonUniqueError,
50
52
  _ParseObjectParseError,
53
+ _SerializeObjectExtraNonUniqueError,
54
+ _SerializeObjectSerializeError,
51
55
  parse_object,
52
56
  serialize_object,
53
57
  )
@@ -99,11 +103,7 @@ class TestSerializeAndParseObject:
99
103
  result = parse_object(
100
104
  DataClassFutureInt,
101
105
  serialized,
102
- extra={
103
- DataClassFutureInt: lambda serialized: DataClassFutureInt(
104
- int_=int(serialized)
105
- )
106
- },
106
+ extra={DataClassFutureInt: lambda text: DataClassFutureInt(int_=int(text))},
107
107
  )
108
108
  expected = DataClassFutureInt(int_=value)
109
109
  assert result == expected
@@ -240,7 +240,7 @@ class TestSerializeAndParseObject:
240
240
 
241
241
  @given(value=integers())
242
242
  def test_type_union_with_extra(self, *, value: int) -> None:
243
- def parse_even_or_odd(text: str, /) -> DataClassFutureIntEvenOrOddTypeUnion:
243
+ def parser(text: str, /) -> DataClassFutureIntEvenOrOddTypeUnion:
244
244
  value = int(text)
245
245
  match value % 2:
246
246
  case 0:
@@ -254,7 +254,7 @@ class TestSerializeAndParseObject:
254
254
  result = parse_object(
255
255
  DataClassFutureIntEvenOrOddTypeUnion,
256
256
  serialized,
257
- extra={DataClassFutureIntEvenOrOddTypeUnion: parse_even_or_odd},
257
+ extra={DataClassFutureIntEvenOrOddTypeUnion: parser},
258
258
  )
259
259
  match value % 2:
260
260
  case 0:
@@ -267,7 +267,7 @@ class TestSerializeAndParseObject:
267
267
 
268
268
  @given(value=integers())
269
269
  def test_union_with_extra(self, *, value: int) -> None:
270
- def parse_even_or_odd(text: str, /) -> DataClassFutureIntEvenOrOddUnion:
270
+ def parser(text: str, /) -> DataClassFutureIntEvenOrOddUnion:
271
271
  value = int(text)
272
272
  match value % 2:
273
273
  case 0:
@@ -281,7 +281,7 @@ class TestSerializeAndParseObject:
281
281
  result = parse_object(
282
282
  DataClassFutureIntEvenOrOddUnion,
283
283
  serialized,
284
- extra={DataClassFutureIntEvenOrOddUnion: parse_even_or_odd},
284
+ extra={DataClassFutureIntEvenOrOddUnion: parser},
285
285
  )
286
286
  match value % 2:
287
287
  case 0:
@@ -299,7 +299,7 @@ class TestSerializeAndParseObject:
299
299
  assert result == version
300
300
 
301
301
 
302
- class TestParseSerialized:
302
+ class TestParseObject:
303
303
  def test_error_bool(self) -> None:
304
304
  with raises(
305
305
  _ParseObjectParseError,
@@ -357,27 +357,20 @@ class TestParseSerialized:
357
357
 
358
358
  @given(value=integers())
359
359
  def test_error_extra_non_unique(self, *, value: int) -> None:
360
- @dataclass(kw_only=True)
361
- class Parent1:
362
- x: int = 0
363
-
364
- @dataclass(kw_only=True)
365
- class Parent2:
366
- y: int = 0
367
-
368
- @dataclass(kw_only=True)
369
- class Child(Parent1, Parent2): ...
370
-
371
360
  with raises(
372
361
  _ParseObjectExtraNonUniqueError,
373
362
  match="Unable to parse <class '.*'> since `extra` must contain exactly one parent class; got <function .*>, <function .*> and perhaps more",
374
363
  ):
375
364
  _ = parse_object(
376
- Child,
365
+ DataClassFutureIntChild,
377
366
  serialize_object(value),
378
367
  extra={
379
- Parent1: lambda serialized: Child(x=int(serialized)),
380
- Parent2: lambda serialized: Child(y=int(serialized)),
368
+ DataClassFutureIntParentFirst: lambda text: DataClassFutureIntChild(
369
+ int1=int(text), int2=0
370
+ ),
371
+ DataClassFutureIntParentSecond: lambda text: DataClassFutureIntChild(
372
+ int1=0, int2=int(text)
373
+ ),
381
374
  },
382
375
  )
383
376
 
@@ -538,6 +531,33 @@ class TestParseSerialized:
538
531
 
539
532
 
540
533
  class TestSerializeObject:
534
+ def test_error_extra_empty(self) -> None:
535
+ with raises(
536
+ _SerializeObjectSerializeError,
537
+ match=r"Unable to serialize object typing\.Final",
538
+ ):
539
+ _ = serialize_object(Final, extra={})
540
+
541
+ @given(int1=integers(), int2=integers())
542
+ def test_error_extra_non_unique(self, *, int1: int, int2: int) -> None:
543
+ def serializer1(obj: DataClassFutureIntParentFirst, /) -> str:
544
+ return str(obj.int1)
545
+
546
+ def serializer2(obj: DataClassFutureIntParentSecond, /) -> str:
547
+ return str(obj.int2)
548
+
549
+ with raises(
550
+ _SerializeObjectExtraNonUniqueError,
551
+ match=r"Unable to serialize object DataClassFutureIntChild\(.*\) since `extra` must contain exactly one parent class; got <function .*>, <function .*> and perhaps more",
552
+ ):
553
+ _ = serialize_object(
554
+ DataClassFutureIntChild(int1=int1, int2=int2),
555
+ extra={
556
+ DataClassFutureIntParentFirst: serializer1,
557
+ DataClassFutureIntParentSecond: serializer2,
558
+ },
559
+ )
560
+
541
561
  def test_error_not_implemented(self) -> None:
542
- with raises(NotImplementedError):
562
+ with raises(_SerializeObjectSerializeError):
543
563
  _ = serialize_object(Final)
@@ -90,6 +90,8 @@ class TestSplitKeyValuePairs:
90
90
  ("a=", [("a", "")]),
91
91
  ("a=1,=22,c=333", [("a", "1"), ("", "22"), ("c", "333")]),
92
92
  ("a=1,b=,c=333", [("a", "1"), ("b", ""), ("c", "333")]),
93
+ ("a=1,b=(22,22,22),c=333", [("a", "1"), ("b", "(22,22,22)"), ("c", "333")]),
94
+ ("a=1,b=(c=22),c=333", [("a", "1"), ("b", "(c=22)"), ("c", "333")]),
93
95
  ])
94
96
  )
95
97
  def test_main(self, *, case: tuple[str, Sequence[tuple[str, str]]]) -> None:
@@ -102,10 +104,17 @@ class TestSplitKeyValuePairs:
102
104
  expected = {"a": "1", "b": "22", "c": "333"}
103
105
  assert result == expected
104
106
 
105
- def test_error_split(self) -> None:
107
+ def test_error_split_list(self) -> None:
106
108
  with raises(
107
109
  _SplitKeyValuePairsSplitError,
108
- match=r"Unable to split 'a=1,b=22=22,c=333' into key-value pairs; got 'b=22=22'",
110
+ match=r"Unable to split 'a=1,b=\(c=22\],d=333' into key-value pairs",
111
+ ):
112
+ _ = split_key_value_pairs("a=1,b=(c=22],d=333")
113
+
114
+ def test_error_split_pair(self) -> None:
115
+ with raises(
116
+ _SplitKeyValuePairsSplitError,
117
+ match=r"Unable to split 'a=1,b=22=22,c=333' into key-value pairs",
109
118
  ):
110
119
  _ = split_key_value_pairs("a=1,b=22=22,c=333")
111
120
 
@@ -87,6 +87,22 @@ class DataClassFutureIntOneAndTwo:
87
87
  int2: int
88
88
 
89
89
 
90
+ @dataclass(order=True, unsafe_hash=True, kw_only=True)
91
+ class DataClassFutureIntParentFirst:
92
+ int1: int
93
+
94
+
95
+ @dataclass(order=True, unsafe_hash=True, kw_only=True)
96
+ class DataClassFutureIntParentSecond:
97
+ int2: int
98
+
99
+
100
+ @dataclass(order=True, unsafe_hash=True, kw_only=True)
101
+ class DataClassFutureIntChild(
102
+ DataClassFutureIntParentFirst, DataClassFutureIntParentSecond
103
+ ): ...
104
+
105
+
90
106
  @dataclass(order=True, unsafe_hash=True, kw_only=True)
91
107
  class DataClassFutureListInts:
92
108
  ints: list[int]
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.110.2"
3
+ __version__ = "0.110.4"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Mapping
4
+ from contextlib import suppress
4
5
  from dataclasses import MISSING, dataclass, field, fields, replace
5
6
  from typing import (
6
7
  TYPE_CHECKING,
@@ -21,21 +22,23 @@ from utilities.functions import (
21
22
  )
22
23
  from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
23
24
  from utilities.operator import is_equal
24
- from utilities.parse import (
25
- LIST_SEPARATOR,
26
- PAIR_SEPARATOR,
27
- ParseObjectError,
28
- parse_object,
29
- serialize_object,
30
- )
25
+ from utilities.parse import ParseObjectError, parse_object, serialize_object
26
+ from utilities.re import ExtractGroupError, extract_group
31
27
  from utilities.sentinel import Sentinel, sentinel
32
28
  from utilities.text import (
29
+ BRACKETS,
30
+ LIST_SEPARATOR,
31
+ PAIR_SEPARATOR,
33
32
  _SplitKeyValuePairsDuplicateKeysError,
34
33
  _SplitKeyValuePairsSplitError,
35
- join_strs,
36
34
  split_key_value_pairs,
37
35
  )
38
- from utilities.types import ParseObjectExtra, StrStrMapping, TDataclass
36
+ from utilities.types import (
37
+ ParseObjectExtra,
38
+ SerializeObjectExtra,
39
+ StrStrMapping,
40
+ TDataclass,
41
+ )
39
42
  from utilities.typing import get_type_hints
40
43
 
41
44
  if TYPE_CHECKING:
@@ -408,10 +411,11 @@ def serialize_dataclass(
408
411
  exclude: Iterable[str] | None = None,
409
412
  rel_tol: float | None = None,
410
413
  abs_tol: float | None = None,
411
- extra: Mapping[type[_U], Callable[[_U, _U], bool]] | None = None,
414
+ extra_equal: Mapping[type[_U], Callable[[_U, _U], bool]] | None = None,
412
415
  defaults: bool = False,
413
416
  list_separator: str = LIST_SEPARATOR,
414
417
  pair_separator: str = PAIR_SEPARATOR,
418
+ extra_serializers: SerializeObjectExtra | None = None,
415
419
  ) -> str:
416
420
  """Serialize a Dataclass."""
417
421
  mapping: StrStrMapping = {}
@@ -426,16 +430,21 @@ def serialize_dataclass(
426
430
  exclude=exclude,
427
431
  rel_tol=rel_tol,
428
432
  abs_tol=abs_tol,
429
- extra=extra,
433
+ extra=extra_equal,
430
434
  defaults=defaults,
431
435
  ):
432
436
  mapping[fld.name] = serialize_object(
433
- fld.value, list_separator=list_separator, pair_separator=pair_separator
437
+ fld.value,
438
+ list_separator=list_separator,
439
+ pair_separator=pair_separator,
440
+ extra=extra_serializers,
434
441
  )
435
- joined_items = (
436
- join_strs(item, separator=pair_separator) for item in mapping.items()
442
+ return serialize_object(
443
+ mapping,
444
+ list_separator=list_separator,
445
+ pair_separator=pair_separator,
446
+ extra=extra_serializers,
437
447
  )
438
- return join_strs(joined_items, separator=list_separator)
439
448
 
440
449
 
441
450
  def parse_dataclass(
@@ -445,6 +454,7 @@ def parse_dataclass(
445
454
  *,
446
455
  list_separator: str = LIST_SEPARATOR,
447
456
  pair_separator: str = PAIR_SEPARATOR,
457
+ brackets: Iterable[tuple[str, str]] | None = BRACKETS,
448
458
  globalns: StrMapping | None = None,
449
459
  localns: StrMapping | None = None,
450
460
  warn_name_errors: bool = False,
@@ -457,7 +467,11 @@ def parse_dataclass(
457
467
  match text_or_mapping:
458
468
  case str() as text:
459
469
  keys_to_serializes = _parse_dataclass_split_key_value_pairs(
460
- text, cls, list_separator=list_separator, pair_separator=pair_separator
470
+ text,
471
+ cls,
472
+ list_separator=list_separator,
473
+ pair_separator=pair_separator,
474
+ brackets=brackets,
461
475
  )
462
476
  case Mapping() as keys_to_serializes:
463
477
  ...
@@ -481,7 +495,14 @@ def parse_dataclass(
481
495
  )
482
496
  field_names_to_values = {
483
497
  f.name: _parse_dataclass_parse_text(
484
- f, t, cls, head=head, case_sensitive=case_sensitive, extra=extra_parsers
498
+ f,
499
+ t,
500
+ cls,
501
+ list_separator=list_separator,
502
+ pair_separator=pair_separator,
503
+ head=head,
504
+ case_sensitive=case_sensitive,
505
+ extra=extra_parsers,
485
506
  )
486
507
  for f, t in fields_to_serializes.items()
487
508
  }
@@ -505,12 +526,16 @@ def _parse_dataclass_split_key_value_pairs(
505
526
  *,
506
527
  list_separator: str = LIST_SEPARATOR,
507
528
  pair_separator: str = PAIR_SEPARATOR,
529
+ brackets: Iterable[tuple[str, str]] | None = BRACKETS,
508
530
  ) -> StrStrMapping:
531
+ with suppress(ExtractGroupError):
532
+ text = extract_group(r"^\{?(.*?)\}?$", text)
509
533
  try:
510
534
  return split_key_value_pairs(
511
535
  text,
512
536
  list_separator=list_separator,
513
537
  pair_separator=pair_separator,
538
+ brackets=brackets,
514
539
  mapping=True,
515
540
  )
516
541
  except _SplitKeyValuePairsSplitError as error: