dycw-utilities 0.110.3__tar.gz → 0.110.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/PKG-INFO +1 -1
  2. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/pyproject.toml +2 -2
  3. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_dataclasses.py +97 -64
  4. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_orjson.py +1 -0
  5. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_parse.py +154 -77
  6. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_text.py +1 -0
  7. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_typing_funcs/with_future.py +16 -0
  8. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/__init__.py +1 -1
  9. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/dataclasses.py +39 -10
  10. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/parse.py +45 -3
  11. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/text.py +4 -1
  12. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/types.py +2 -0
  13. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/.gitignore +0 -0
  14. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/LICENSE +0 -0
  15. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/README.md +0 -0
  16. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/__init__.py +0 -0
  17. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/conftest.py +0 -0
  18. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/__init__.py +0 -0
  19. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_missing/__init__.py +0 -0
  20. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_missing/module.py +0 -0
  21. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/__init__.py +0 -0
  22. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/outer_1.py +0 -0
  23. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/outer_2.py +0 -0
  24. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  25. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  26. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  27. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  28. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_without/__init__.py +0 -0
  29. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_without/module_1.py +0 -0
  30. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/package_without/module_2.py +0 -0
  31. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/standalone.py +0 -0
  32. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/modules/with_imports.py +0 -0
  33. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  34. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  35. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  36. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  37. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  38. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  39. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  40. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  41. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/__init__.py +0 -0
  42. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/test_async_service/__init__.py +0 -0
  43. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/test_async_service/__main__.py +0 -0
  44. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/test_async_service/run.sh +0 -0
  45. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
  46. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
  47. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/scripts/test_queue_processor/run.sh +0 -0
  48. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_altair.py +0 -0
  49. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_astor.py +0 -0
  50. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_asyncio.py +0 -0
  51. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_atomicwrites.py +0 -0
  52. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_atools.py +0 -0
  53. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_cachetools.py +0 -0
  54. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_click.py +0 -0
  55. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_concurrent.py +0 -0
  56. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_contextlib.py +0 -0
  57. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_contextvars.py +0 -0
  58. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_cryptography.py +0 -0
  59. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_cvxpy.py +0 -0
  60. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_datetime.py +0 -0
  61. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_enum.py +0 -0
  62. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_errors.py +0 -0
  63. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_eventkit.py +0 -0
  64. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_fastapi.py +0 -0
  65. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_fpdf2.py +0 -0
  66. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_functions.py +0 -0
  67. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_functools.py +0 -0
  68. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_getpass.py +0 -0
  69. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_git.py +0 -0
  70. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_hashlib.py +0 -0
  71. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_http.py +0 -0
  72. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_hypothesis.py +0 -0
  73. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_ipython.py +0 -0
  74. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_iterables.py +0 -0
  75. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_jupyter.py +0 -0
  76. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_lightweight_charts.py +0 -0
  77. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_logging.py +0 -0
  78. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_loguru.py +0 -0
  79. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_luigi.py +0 -0
  80. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_math.py +0 -0
  81. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_memory_profiler.py +0 -0
  82. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_modules.py +0 -0
  83. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_more_itertools.py +0 -0
  84. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_numpy.py +0 -0
  85. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_operator.py +0 -0
  86. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_optuna.py +0 -0
  87. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_os.py +0 -0
  88. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pathlib.py +0 -0
  89. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_period.py +0 -0
  90. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pickle.py +0 -0
  91. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_platform.py +0 -0
  92. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_polars.py +0 -0
  93. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_polars_ols.py +0 -0
  94. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pqdm.py +0 -0
  95. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pydantic.py +0 -0
  96. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pyinstrument.py +0 -0
  97. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pyrsistent.py +0 -0
  98. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pytest.py +0 -0
  99. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_pytest_regressions.py +0 -0
  100. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_python_dotenv.py +0 -0
  101. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_random.py +0 -0
  102. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_re.py +0 -0
  103. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_redis.py +0 -0
  104. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_reprlib.py +0 -0
  105. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_rich.py +0 -0
  106. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_scipy.py +0 -0
  107. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_sentinel.py +0 -0
  108. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_shelve.py +0 -0
  109. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_slack_sdk.py +0 -0
  110. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_socket.py +0 -0
  111. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_sqlalchemy.py +0 -0
  112. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_statsmodel.py +0 -0
  114. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_sys.py +0 -0
  116. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_tempfile.py +0 -0
  117. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_tenacity.py +0 -0
  118. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_threading.py +0 -0
  119. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_timer.py +0 -0
  120. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback.py +0 -0
  121. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/__init__.py +0 -0
  122. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/chain.py +0 -0
  123. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  124. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  125. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  126. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/many.py +0 -0
  127. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/one.py +0 -0
  128. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/recursive.py +0 -0
  129. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  130. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  131. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/two.py +0 -0
  132. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_traceback_funcs/untraced.py +0 -0
  133. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_types.py +0 -0
  134. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_typing.py +0 -0
  135. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_typing_funcs/__init__.py +0 -0
  136. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_typing_funcs/no_future.py +0 -0
  137. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_tzdata.py +0 -0
  138. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_tzlocal.py +0 -0
  139. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_uuid.py +0 -0
  140. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_version.py +0 -0
  141. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_warnings.py +0 -0
  142. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_whenever.py +0 -0
  143. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_zipfile.py +0 -0
  144. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/tests/test_zoneinfo.py +0 -0
  145. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/altair.py +0 -0
  146. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/astor.py +0 -0
  147. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/asyncio.py +0 -0
  148. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/atomicwrites.py +0 -0
  149. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/atools.py +0 -0
  150. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/cachetools.py +0 -0
  151. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/click.py +0 -0
  152. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/concurrent.py +0 -0
  153. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/contextlib.py +0 -0
  154. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/contextvars.py +0 -0
  155. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/cryptography.py +0 -0
  156. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/cvxpy.py +0 -0
  157. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/datetime.py +0 -0
  158. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/enum.py +0 -0
  159. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/errors.py +0 -0
  160. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/eventkit.py +0 -0
  161. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/fastapi.py +0 -0
  162. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/fpdf2.py +0 -0
  163. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/functions.py +0 -0
  164. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/functools.py +0 -0
  165. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/getpass.py +0 -0
  166. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/git.py +0 -0
  167. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/hashlib.py +0 -0
  168. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/http.py +0 -0
  169. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/hypothesis.py +0 -0
  170. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/ipython.py +0 -0
  171. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/iterables.py +0 -0
  172. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/jupyter.py +0 -0
  173. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/lightweight_charts.py +0 -0
  174. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/logging.py +0 -0
  175. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/loguru.py +0 -0
  176. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/luigi.py +0 -0
  177. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/math.py +0 -0
  178. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/memory_profiler.py +0 -0
  179. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/modules.py +0 -0
  180. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/more_itertools.py +0 -0
  181. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/numpy.py +0 -0
  182. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/operator.py +0 -0
  183. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/optuna.py +0 -0
  184. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/orjson.py +0 -0
  185. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/os.py +0 -0
  186. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pathlib.py +0 -0
  187. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/period.py +0 -0
  188. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pickle.py +0 -0
  189. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/platform.py +0 -0
  190. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/polars.py +0 -0
  191. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/polars_ols.py +0 -0
  192. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pqdm.py +0 -0
  193. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/py.typed +0 -0
  194. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pydantic.py +0 -0
  195. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pyinstrument.py +0 -0
  196. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pyrsistent.py +0 -0
  197. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pytest.py +0 -0
  198. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/pytest_regressions.py +0 -0
  199. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/python_dotenv.py +0 -0
  200. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/random.py +0 -0
  201. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/re.py +0 -0
  202. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/redis.py +0 -0
  203. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/reprlib.py +0 -0
  204. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/rich.py +0 -0
  205. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/scipy.py +0 -0
  206. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/sentinel.py +0 -0
  207. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/shelve.py +0 -0
  208. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/slack_sdk.py +0 -0
  209. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/socket.py +0 -0
  210. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/sqlalchemy.py +0 -0
  211. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/sqlalchemy_polars.py +0 -0
  212. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/statsmodels.py +0 -0
  213. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/streamlit.py +0 -0
  214. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/sys.py +0 -0
  215. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/tempfile.py +0 -0
  216. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/tenacity.py +0 -0
  217. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/threading.py +0 -0
  218. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/timer.py +0 -0
  219. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/traceback.py +0 -0
  220. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/typing.py +0 -0
  221. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/tzdata.py +0 -0
  222. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/tzlocal.py +0 -0
  223. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/uuid.py +0 -0
  224. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/version.py +0 -0
  225. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/warnings.py +0 -0
  226. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/whenever.py +0 -0
  227. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/src/utilities/zipfile.py +0 -0
  228. {dycw_utilities-0.110.3 → dycw_utilities-0.110.5}/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.3
3
+ Version: 0.110.5
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.3"
97
+ version = "0.110.5"
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.3"
340
+ current_version = "0.110.5"
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
  )
@@ -57,11 +61,11 @@ from utilities.version import Version
57
61
 
58
62
 
59
63
  class TestSerializeAndParseObject:
60
- @given(value=booleans())
61
- def test_bool(self, *, value: bool) -> None:
62
- serialized = serialize_object(value)
64
+ @given(bool_=booleans())
65
+ def test_bool(self, *, bool_: bool) -> None:
66
+ serialized = serialize_object(bool_)
63
67
  result = parse_object(bool, serialized)
64
- assert result is value
68
+ assert result is bool_
65
69
 
66
70
  @given(date=dates())
67
71
  def test_date(self, *, date: dt.date) -> None:
@@ -75,11 +79,11 @@ class TestSerializeAndParseObject:
75
79
  result = parse_object(dt.datetime, serialized)
76
80
  assert result == datetime
77
81
 
78
- @given(value=dictionaries(dates(), zoned_datetimes()))
79
- def test_dict(self, *, value: dict[dt.date, dt.datetime]) -> None:
80
- serialized = serialize_object(value)
82
+ @given(mapping=dictionaries(dates(), zoned_datetimes()))
83
+ def test_dict(self, *, mapping: dict[dt.date, dt.datetime]) -> None:
84
+ serialized = serialize_object(mapping)
81
85
  result = parse_object(dict[dt.date, dt.datetime], serialized)
82
- assert result == value
86
+ assert result == mapping
83
87
 
84
88
  @given(duration=datetime_durations(two_way=True))
85
89
  def test_duration(self, *, duration: Duration) -> None:
@@ -93,26 +97,22 @@ class TestSerializeAndParseObject:
93
97
  result = parse_object(TruthEnum, serialized)
94
98
  assert result is truth
95
99
 
96
- @given(value=integers())
97
- def test_extra_type(self, *, value: int) -> None:
98
- serialized = serialize_object(value)
100
+ @given(int_=integers())
101
+ def test_extra_type(self, *, int_: int) -> None:
102
+ serialized = serialize_object(int_)
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
- expected = DataClassFutureInt(int_=value)
108
+ expected = DataClassFutureInt(int_=int_)
109
109
  assert result == expected
110
110
 
111
- @given(value=floats())
112
- def test_float(self, *, value: float) -> None:
113
- serialized = serialize_object(value)
111
+ @given(float_=floats())
112
+ def test_float(self, *, float_: float) -> None:
113
+ serialized = serialize_object(float_)
114
114
  result = parse_object(float, serialized)
115
- assert is_equal(result, value)
115
+ assert is_equal(result, float_)
116
116
 
117
117
  @given(values=frozensets(dates()))
118
118
  def test_frozenset(self, *, values: frozenset[dt.date]) -> None:
@@ -120,11 +120,11 @@ class TestSerializeAndParseObject:
120
120
  result = parse_object(frozenset[dt.date], serialized)
121
121
  assert result == values
122
122
 
123
- @given(value=integers())
124
- def test_int(self, *, value: int) -> None:
125
- serialized = serialize_object(value)
123
+ @given(int_=integers())
124
+ def test_int(self, *, int_: int) -> None:
125
+ serialized = serialize_object(int_)
126
126
  result = parse_object(int, serialized)
127
- assert result == value
127
+ assert result == int_
128
128
 
129
129
  @given(values=lists(dates()))
130
130
  def test_list(self, *, values: list[dt.date]) -> None:
@@ -193,11 +193,11 @@ class TestSerializeAndParseObject:
193
193
  result = parse_object(int | None, serialized)
194
194
  assert result is None
195
195
 
196
- @given(value=integers())
197
- def test_nullable_int_int(self, *, value: int) -> None:
198
- serialized = serialize_object(value)
196
+ @given(int_=integers())
197
+ def test_nullable_int_int(self, *, int_: int) -> None:
198
+ serialized = serialize_object(int_)
199
199
  result = parse_object(int | None, serialized)
200
- assert result == value
200
+ assert result == int_
201
201
 
202
202
  def test_sentinel(self) -> None:
203
203
  serialized = serialize_object(sentinel)
@@ -238,58 +238,58 @@ class TestSerializeAndParseObject:
238
238
  result = parse_object(TrueOrFalseFutureTypeLit, truth)
239
239
  assert result == truth
240
240
 
241
- @given(value=integers())
242
- def test_type_union_with_extra(self, *, value: int) -> None:
243
- def parse_even_or_odd(text: str, /) -> DataClassFutureIntEvenOrOddTypeUnion:
244
- value = int(text)
245
- match value % 2:
241
+ @given(int_=integers())
242
+ def test_type_union_with_extra(self, *, int_: int) -> None:
243
+ def parser(text: str, /) -> DataClassFutureIntEvenOrOddTypeUnion:
244
+ int_ = int(text)
245
+ match int_ % 2:
246
246
  case 0:
247
- return DataClassFutureIntEven(even_int=value)
247
+ return DataClassFutureIntEven(even_int=int_)
248
248
  case 1:
249
- return DataClassFutureIntOdd(odd_int=value)
249
+ return DataClassFutureIntOdd(odd_int=int_)
250
250
  case _:
251
- raise ImpossibleCaseError(case=[f"{value=}"])
251
+ raise ImpossibleCaseError(case=[f"{int_=}"])
252
252
 
253
- serialized = serialize_object(value)
253
+ serialized = serialize_object(int_)
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
- match value % 2:
259
+ match int_ % 2:
260
260
  case 0:
261
- expected = DataClassFutureIntEven(even_int=value)
261
+ expected = DataClassFutureIntEven(even_int=int_)
262
262
  case 1:
263
- expected = DataClassFutureIntOdd(odd_int=value)
263
+ expected = DataClassFutureIntOdd(odd_int=int_)
264
264
  case _:
265
- raise ImpossibleCaseError(case=[f"{value=}"])
265
+ raise ImpossibleCaseError(case=[f"{int_=}"])
266
266
  assert result == expected
267
267
 
268
- @given(value=integers())
269
- def test_union_with_extra(self, *, value: int) -> None:
270
- def parse_even_or_odd(text: str, /) -> DataClassFutureIntEvenOrOddUnion:
271
- value = int(text)
272
- match value % 2:
268
+ @given(int_=integers())
269
+ def test_union_with_extra(self, *, int_: int) -> None:
270
+ def parser(text: str, /) -> DataClassFutureIntEvenOrOddUnion:
271
+ int_ = int(text)
272
+ match int_ % 2:
273
273
  case 0:
274
- return DataClassFutureIntEven(even_int=value)
274
+ return DataClassFutureIntEven(even_int=int_)
275
275
  case 1:
276
- return DataClassFutureIntOdd(odd_int=value)
276
+ return DataClassFutureIntOdd(odd_int=int_)
277
277
  case _:
278
- raise ImpossibleCaseError(case=[f"{value=}"])
278
+ raise ImpossibleCaseError(case=[f"{int_=}"])
279
279
 
280
- serialized = serialize_object(value)
280
+ serialized = serialize_object(int_)
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
- match value % 2:
286
+ match int_ % 2:
287
287
  case 0:
288
- expected = DataClassFutureIntEven(even_int=value)
288
+ expected = DataClassFutureIntEven(even_int=int_)
289
289
  case 1:
290
- expected = DataClassFutureIntOdd(odd_int=value)
290
+ expected = DataClassFutureIntOdd(odd_int=int_)
291
291
  case _:
292
- raise ImpossibleCaseError(case=[f"{value=}"])
292
+ raise ImpossibleCaseError(case=[f"{int_=}"])
293
293
  assert result == expected
294
294
 
295
295
  @given(version=versions())
@@ -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,
@@ -355,29 +355,22 @@ class TestParseSerialized:
355
355
  ):
356
356
  _ = parse_object(DataClassFutureInt, "invalid", extra={})
357
357
 
358
- @given(value=integers())
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
-
358
+ @given(int_=integers())
359
+ def test_error_extra_non_unique(self, *, int_: int) -> None:
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,
377
- serialize_object(value),
365
+ DataClassFutureIntChild,
366
+ serialize_object(int_),
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,90 @@ class TestParseSerialized:
538
531
 
539
532
 
540
533
  class TestSerializeObject:
534
+ @given(int_=integers())
535
+ def test_type_with_extra(self, *, int_: int) -> None:
536
+ obj = DataClassFutureInt(int_=int_)
537
+
538
+ def serializer(obj: DataClassFutureInt, /) -> str:
539
+ return str(obj.int_)
540
+
541
+ serialized = serialize_object(obj, extra={DataClassFutureInt: serializer})
542
+ expected = str(int_)
543
+ assert serialized == expected
544
+
545
+ @given(int_=integers())
546
+ def test_type_union_with_extra(self, *, int_: int) -> None:
547
+ match int_ % 2:
548
+ case 0:
549
+ obj = DataClassFutureIntEven(even_int=int_)
550
+ case 1:
551
+ obj = DataClassFutureIntOdd(odd_int=int_)
552
+ case _:
553
+ raise ImpossibleCaseError(case=[f"{int_=}"])
554
+
555
+ def serializer(obj: DataClassFutureIntEvenOrOddTypeUnion, /) -> str:
556
+ match obj:
557
+ case DataClassFutureIntEven():
558
+ return str(obj.even_int)
559
+ case DataClassFutureIntOdd():
560
+ return str(obj.odd_int)
561
+
562
+ serialized = serialize_object(
563
+ obj, extra={DataClassFutureIntEvenOrOddTypeUnion: serializer}
564
+ )
565
+ expected = str(int_)
566
+ assert serialized == expected
567
+
568
+ @given(int_=integers())
569
+ def test_union_with_extra(self, *, int_: int) -> None:
570
+ match int_ % 2:
571
+ case 0:
572
+ obj = DataClassFutureIntEven(even_int=int_)
573
+ case 1:
574
+ obj = DataClassFutureIntOdd(odd_int=int_)
575
+ case _:
576
+ raise ImpossibleCaseError(case=[f"{int_=}"])
577
+
578
+ def serializer(obj: DataClassFutureIntEvenOrOddUnion, /) -> str:
579
+ match obj:
580
+ case DataClassFutureIntEven():
581
+ return str(obj.even_int)
582
+ case DataClassFutureIntOdd():
583
+ return str(obj.odd_int)
584
+
585
+ serialized = serialize_object(
586
+ obj, extra={DataClassFutureIntEvenOrOddUnion: serializer}
587
+ )
588
+ expected = str(int_)
589
+ assert serialized == expected
590
+
591
+ def test_error_extra_empty(self) -> None:
592
+ with raises(
593
+ _SerializeObjectSerializeError,
594
+ match=r"Unable to serialize object typing\.Final",
595
+ ):
596
+ _ = serialize_object(Final, extra={})
597
+
598
+ @given(int1=integers(), int2=integers())
599
+ def test_error_extra_non_unique(self, *, int1: int, int2: int) -> None:
600
+ def serializer1(obj: DataClassFutureIntParentFirst, /) -> str:
601
+ return str(obj.int1)
602
+
603
+ def serializer2(obj: DataClassFutureIntParentSecond, /) -> str:
604
+ return str(obj.int2)
605
+
606
+ with raises(
607
+ _SerializeObjectExtraNonUniqueError,
608
+ match=r"Unable to serialize object DataClassFutureIntChild\(.*\) since `extra` must contain exactly one parent class; got <function .*>, <function .*> and perhaps more",
609
+ ):
610
+ _ = serialize_object(
611
+ DataClassFutureIntChild(int1=int1, int2=int2),
612
+ extra={
613
+ DataClassFutureIntParentFirst: serializer1,
614
+ DataClassFutureIntParentSecond: serializer2,
615
+ },
616
+ )
617
+
541
618
  def test_error_not_implemented(self) -> None:
542
- with raises(NotImplementedError):
619
+ with raises(_SerializeObjectSerializeError):
543
620
  _ = serialize_object(Final)
@@ -91,6 +91,7 @@ class TestSplitKeyValuePairs:
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
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")]),
94
95
  ])
95
96
  )
96
97
  def test_main(self, *, case: tuple[str, Sequence[tuple[str, str]]]) -> None:
@@ -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.3"
3
+ __version__ = "0.110.5"