dycw-utilities 0.108.3__tar.gz → 0.108.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 (222) hide show
  1. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/PKG-INFO +5 -5
  2. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/pyproject.toml +8 -8
  3. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_dataclasses.py +82 -20
  4. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/__init__.py +1 -1
  5. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/dataclasses.py +166 -18
  6. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/.gitignore +0 -0
  7. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/LICENSE +0 -0
  8. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/README.md +0 -0
  9. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/__init__.py +0 -0
  10. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/conftest.py +0 -0
  11. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/__init__.py +0 -0
  12. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_missing/__init__.py +0 -0
  13. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_missing/module.py +0 -0
  14. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/__init__.py +0 -0
  15. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/outer_1.py +0 -0
  16. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/outer_2.py +0 -0
  17. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  18. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  19. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  20. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  21. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_without/__init__.py +0 -0
  22. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_without/module_1.py +0 -0
  23. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/package_without/module_2.py +0 -0
  24. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/standalone.py +0 -0
  25. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/modules/with_imports.py +0 -0
  26. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  27. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  28. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  29. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  30. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  31. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  32. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  33. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  34. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/__init__.py +0 -0
  35. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/test_async_service/__init__.py +0 -0
  36. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/test_async_service/__main__.py +0 -0
  37. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/test_async_service/run.sh +0 -0
  38. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
  39. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
  40. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/scripts/test_queue_processor/run.sh +0 -0
  41. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_altair.py +0 -0
  42. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_astor.py +0 -0
  43. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_asyncio.py +0 -0
  44. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_atomicwrites.py +0 -0
  45. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_atools.py +0 -0
  46. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_cachetools.py +0 -0
  47. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_click.py +0 -0
  48. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_concurrent.py +0 -0
  49. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_contextlib.py +0 -0
  50. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_contextvars.py +0 -0
  51. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_cryptography.py +0 -0
  52. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_cvxpy.py +0 -0
  53. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_datetime.py +0 -0
  54. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_enum.py +0 -0
  55. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_errors.py +0 -0
  56. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_eventkit.py +0 -0
  57. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_fastapi.py +0 -0
  58. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_fpdf2.py +0 -0
  59. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_functions.py +0 -0
  60. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_functools.py +0 -0
  61. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_getpass.py +0 -0
  62. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_git.py +0 -0
  63. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_hashlib.py +0 -0
  64. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_http.py +0 -0
  65. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_hypothesis.py +0 -0
  66. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_ipython.py +0 -0
  67. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_iterables.py +0 -0
  68. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_jupyter.py +0 -0
  69. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_logging.py +0 -0
  70. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_loguru.py +0 -0
  71. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_luigi.py +0 -0
  72. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_math.py +0 -0
  73. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_memory_profiler.py +0 -0
  74. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_modules.py +0 -0
  75. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_more_itertools.py +0 -0
  76. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_numpy.py +0 -0
  77. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_operator.py +0 -0
  78. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_optuna.py +0 -0
  79. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_orjson.py +0 -0
  80. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_os.py +0 -0
  81. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_parse.py +0 -0
  82. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pathlib.py +0 -0
  83. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_period.py +0 -0
  84. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pickle.py +0 -0
  85. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_platform.py +0 -0
  86. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_polars.py +0 -0
  87. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pqdm.py +0 -0
  88. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pydantic.py +0 -0
  89. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pyinstrument.py +0 -0
  90. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pyrsistent.py +0 -0
  91. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pytest.py +0 -0
  92. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_pytest_regressions.py +0 -0
  93. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_python_dotenv.py +0 -0
  94. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_random.py +0 -0
  95. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_re.py +0 -0
  96. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_redis.py +0 -0
  97. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_reprlib.py +0 -0
  98. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_rich.py +0 -0
  99. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_scipy.py +0 -0
  100. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_sentinel.py +0 -0
  101. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_shelve.py +0 -0
  102. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_slack_sdk.py +0 -0
  103. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_socket.py +0 -0
  104. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_sqlalchemy.py +0 -0
  105. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_sqlalchemy_polars.py +0 -0
  106. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_streamlit.py +0 -0
  107. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_sys.py +0 -0
  108. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_tempfile.py +0 -0
  109. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_tenacity.py +0 -0
  110. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_text.py +0 -0
  111. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_threading.py +0 -0
  112. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_timer.py +0 -0
  113. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback.py +0 -0
  114. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/__init__.py +0 -0
  115. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/chain.py +0 -0
  116. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  117. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  118. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  119. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/many.py +0 -0
  120. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/one.py +0 -0
  121. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/recursive.py +0 -0
  122. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  123. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  124. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/two.py +0 -0
  125. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_traceback_funcs/untraced.py +0 -0
  126. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_types.py +0 -0
  127. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_typing.py +0 -0
  128. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_typing_funcs/__init__.py +0 -0
  129. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_typing_funcs/no_future.py +0 -0
  130. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_typing_funcs/with_future.py +0 -0
  131. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_tzdata.py +0 -0
  132. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_tzlocal.py +0 -0
  133. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_uuid.py +0 -0
  134. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_version.py +0 -0
  135. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_warnings.py +0 -0
  136. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_whenever.py +0 -0
  137. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_zipfile.py +0 -0
  138. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/tests/test_zoneinfo.py +0 -0
  139. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/altair.py +0 -0
  140. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/astor.py +0 -0
  141. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/asyncio.py +0 -0
  142. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/atomicwrites.py +0 -0
  143. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/atools.py +0 -0
  144. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/cachetools.py +0 -0
  145. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/click.py +0 -0
  146. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/concurrent.py +0 -0
  147. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/contextlib.py +0 -0
  148. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/contextvars.py +0 -0
  149. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/cryptography.py +0 -0
  150. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/cvxpy.py +0 -0
  151. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/datetime.py +0 -0
  152. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/enum.py +0 -0
  153. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/errors.py +0 -0
  154. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/eventkit.py +0 -0
  155. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/fastapi.py +0 -0
  156. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/fpdf2.py +0 -0
  157. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/functions.py +0 -0
  158. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/functools.py +0 -0
  159. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/getpass.py +0 -0
  160. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/git.py +0 -0
  161. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/hashlib.py +0 -0
  162. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/http.py +0 -0
  163. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/hypothesis.py +0 -0
  164. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/ipython.py +0 -0
  165. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/iterables.py +0 -0
  166. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/jupyter.py +0 -0
  167. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/logging.py +0 -0
  168. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/loguru.py +0 -0
  169. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/luigi.py +0 -0
  170. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/math.py +0 -0
  171. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/memory_profiler.py +0 -0
  172. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/modules.py +0 -0
  173. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/more_itertools.py +0 -0
  174. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/numpy.py +0 -0
  175. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/operator.py +0 -0
  176. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/optuna.py +0 -0
  177. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/orjson.py +0 -0
  178. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/os.py +0 -0
  179. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/parse.py +0 -0
  180. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pathlib.py +0 -0
  181. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/period.py +0 -0
  182. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pickle.py +0 -0
  183. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/platform.py +0 -0
  184. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/polars.py +0 -0
  185. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pqdm.py +0 -0
  186. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/py.typed +0 -0
  187. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pydantic.py +0 -0
  188. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pyinstrument.py +0 -0
  189. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pyrsistent.py +0 -0
  190. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pytest.py +0 -0
  191. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/pytest_regressions.py +0 -0
  192. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/python_dotenv.py +0 -0
  193. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/random.py +0 -0
  194. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/re.py +0 -0
  195. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/redis.py +0 -0
  196. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/reprlib.py +0 -0
  197. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/rich.py +0 -0
  198. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/scipy.py +0 -0
  199. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/sentinel.py +0 -0
  200. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/shelve.py +0 -0
  201. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/slack_sdk.py +0 -0
  202. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/socket.py +0 -0
  203. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/sqlalchemy.py +0 -0
  204. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/sqlalchemy_polars.py +0 -0
  205. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/streamlit.py +0 -0
  206. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/sys.py +0 -0
  207. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/tempfile.py +0 -0
  208. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/tenacity.py +0 -0
  209. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/text.py +0 -0
  210. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/threading.py +0 -0
  211. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/timer.py +0 -0
  212. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/traceback.py +0 -0
  213. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/types.py +0 -0
  214. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/typing.py +0 -0
  215. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/tzdata.py +0 -0
  216. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/tzlocal.py +0 -0
  217. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/uuid.py +0 -0
  218. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/version.py +0 -0
  219. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/warnings.py +0 -0
  220. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/whenever.py +0 -0
  221. {dycw_utilities-0.108.3 → dycw_utilities-0.108.4}/src/utilities/zipfile.py +0 -0
  222. {dycw_utilities-0.108.3 → dycw_utilities-0.108.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.108.3
3
+ Version: 0.108.4
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -82,7 +82,7 @@ Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
82
82
  Requires-Dist: greenlet<3.3,>=3.2.0; extra == 'zzz-test-hypothesis'
83
83
  Requires-Dist: hypothesis<6.132,>=6.131.6; extra == 'zzz-test-hypothesis'
84
84
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
85
- Requires-Dist: numpy<2.3,>=2.2.4; extra == 'zzz-test-hypothesis'
85
+ Requires-Dist: numpy<2.3,>=2.2.5; extra == 'zzz-test-hypothesis'
86
86
  Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
87
87
  Requires-Dist: redis<5.3,>=5.2.1; extra == 'zzz-test-hypothesis'
88
88
  Requires-Dist: sqlalchemy<2.1,>=2.0.40; extra == 'zzz-test-hypothesis'
@@ -112,14 +112,14 @@ Provides-Extra: zzz-test-luigi
112
112
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-luigi'
113
113
  Requires-Dist: whenever<0.8,>=0.7.3; extra == 'zzz-test-luigi'
114
114
  Provides-Extra: zzz-test-math
115
- Requires-Dist: numpy<2.3,>=2.2.4; extra == 'zzz-test-math'
115
+ Requires-Dist: numpy<2.3,>=2.2.5; extra == 'zzz-test-math'
116
116
  Provides-Extra: zzz-test-memory-profiler
117
117
  Requires-Dist: memory-profiler<0.62,>=0.61.0; extra == 'zzz-test-memory-profiler'
118
118
  Provides-Extra: zzz-test-modules
119
119
  Provides-Extra: zzz-test-more-itertools
120
120
  Requires-Dist: more-itertools<10.7,>=10.6.0; extra == 'zzz-test-more-itertools'
121
121
  Provides-Extra: zzz-test-numpy
122
- Requires-Dist: numpy<2.3,>=2.2.4; extra == 'zzz-test-numpy'
122
+ Requires-Dist: numpy<2.3,>=2.2.5; extra == 'zzz-test-numpy'
123
123
  Provides-Extra: zzz-test-operator
124
124
  Requires-Dist: polars-lts-cpu<1.28,>=1.27.1; extra == 'zzz-test-operator'
125
125
  Requires-Dist: whenever<0.8,>=0.7.3; extra == 'zzz-test-operator'
@@ -177,7 +177,7 @@ Requires-Dist: scipy<1.16,>=1.15.2; extra == 'zzz-test-scipy'
177
177
  Provides-Extra: zzz-test-sentinel
178
178
  Provides-Extra: zzz-test-shelve
179
179
  Provides-Extra: zzz-test-slack-sdk
180
- Requires-Dist: aiohttp<3.12,>=3.11.16; extra == 'zzz-test-slack-sdk'
180
+ Requires-Dist: aiohttp<3.12,>=3.11.17; extra == 'zzz-test-slack-sdk'
181
181
  Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
182
182
  Provides-Extra: zzz-test-socket
183
183
  Provides-Extra: zzz-test-sqlalchemy
@@ -8,7 +8,7 @@ requires = ["hatchling"]
8
8
  # dependency groups
9
9
  [dependency-groups]
10
10
  dev = [
11
- "aiohttp >= 3.11.16, < 3.12", # for slack
11
+ "aiohttp >= 3.11.17, < 3.12", # for slack
12
12
  "aiosqlite >= 0.21.0, < 0.22",
13
13
  "altair >= 5.5.0, < 5.6",
14
14
  "astor >= 0.8.1, < 0.9",
@@ -35,7 +35,7 @@ dev = [
35
35
  "more-itertools >= 10.6.0, < 10.7",
36
36
  "nest-asyncio >= 1.6.0, < 1.7", # for sqlalchemy async
37
37
  "nox >= 2025.2.9, < 2025.3",
38
- "numpy >= 2.2.4, < 2.3",
38
+ "numpy >= 2.2.5, < 2.3",
39
39
  "optuna >= 4.3.0, < 4.4",
40
40
  "orjson >= 3.10.16, < 3.11",
41
41
  "pathvalidate >= 3.2.3, < 3.3",
@@ -89,7 +89,7 @@ dependencies = [
89
89
  name = "dycw-utilities"
90
90
  readme = "README.md"
91
91
  requires-python = ">= 3.12"
92
- version = "0.108.3"
92
+ version = "0.108.4"
93
93
 
94
94
  [project.optional-dependencies]
95
95
  test = [
@@ -173,7 +173,7 @@ zzz-test-hypothesis = [
173
173
  "greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
174
174
  "hypothesis >= 6.131.6, < 6.132",
175
175
  "luigi >= 3.6.0, < 3.7",
176
- "numpy >= 2.2.4, < 2.3",
176
+ "numpy >= 2.2.5, < 2.3",
177
177
  "pathvalidate >= 3.2.3, < 3.3",
178
178
  "redis >= 5.2.1, < 5.3",
179
179
  "sqlalchemy >= 2.0.40, < 2.1",
@@ -205,11 +205,11 @@ zzz-test-luigi = [
205
205
  "luigi >= 3.6.0, < 3.7",
206
206
  "whenever >= 0.7.3, < 0.8",
207
207
  ]
208
- zzz-test-math = ["numpy >= 2.2.4, < 2.3"]
208
+ zzz-test-math = ["numpy >= 2.2.5, < 2.3"]
209
209
  zzz-test-memory-profiler = ["memory-profiler >= 0.61.0, < 0.62"]
210
210
  zzz-test-modules = []
211
211
  zzz-test-more-itertools = ["more-itertools >= 10.6.0, < 10.7"]
212
- zzz-test-numpy = ["numpy >= 2.2.4, < 2.3"]
212
+ zzz-test-numpy = ["numpy >= 2.2.5, < 2.3"]
213
213
  zzz-test-operator = [
214
214
  "polars-lts-cpu >= 1.27.1, < 1.28",
215
215
  "whenever >= 0.7.3, < 0.8",
@@ -268,7 +268,7 @@ zzz-test-scipy = ["scipy >= 1.15.2, < 1.16"]
268
268
  zzz-test-sentinel = []
269
269
  zzz-test-shelve = []
270
270
  zzz-test-slack-sdk = [
271
- "aiohttp >= 3.11.16, < 3.12", # for slack
271
+ "aiohttp >= 3.11.17, < 3.12", # for slack
272
272
  "slack-sdk >= 3.35.0, < 3.36",
273
273
  ]
274
274
  zzz-test-socket = []
@@ -332,7 +332,7 @@ zzz-test-zoneinfo = [
332
332
  # bump-my-version
333
333
  [tool.bumpversion]
334
334
  allow_dirty = true
335
- current_version = "0.108.3"
335
+ current_version = "0.108.4"
336
336
 
337
337
  [[tool.bumpversion.files]]
338
338
  filename = "src/utilities/__init__.py"
@@ -45,12 +45,17 @@ from utilities.dataclasses import (
45
45
  YieldFieldsError,
46
46
  _MappingToDataclassCaseInsensitiveNonUniqueError,
47
47
  _MappingToDataclassEmptyError,
48
+ _TextToDataClassGetFieldEmptyError,
49
+ _TextToDataClassGetFieldNonUniqueError,
50
+ _TextToDataClassParseValueError,
51
+ _TextToDataClassSplitKeyValuePairError,
48
52
  _YieldFieldsClass,
49
53
  _YieldFieldsInstance,
50
54
  dataclass_repr,
51
55
  dataclass_to_dict,
52
56
  mapping_to_dataclass,
53
57
  replace_non_sentinel,
58
+ text_to_dataclass,
54
59
  yield_fields,
55
60
  )
56
61
  from utilities.functions import get_class_name
@@ -65,6 +70,43 @@ from utilities.typing import get_args, is_list_type, is_literal_type, is_optiona
65
70
  TruthLit = Literal["true", "false"] # in 3.12, use type TruthLit = ...
66
71
 
67
72
 
73
+ class TestDataClassRepr:
74
+ def test_overriding_repr(self) -> None:
75
+ @dataclass(kw_only=True, slots=True)
76
+ class Example(DataClassFutureIntDefault):
77
+ @override
78
+ def __repr__(self) -> str:
79
+ return dataclass_repr(self)
80
+
81
+ obj = Example()
82
+ result = repr(obj)
83
+ expected = "Example()"
84
+ assert result == expected
85
+
86
+ def test_overriding_repr_defaults(self) -> None:
87
+ @dataclass(kw_only=True)
88
+ class Example(DataClassFutureIntDefault):
89
+ @override
90
+ def __repr__(self) -> str:
91
+ return dataclass_repr(self, defaults=True)
92
+
93
+ obj = Example()
94
+ result = repr(obj)
95
+ expected = "Example(int_=0)"
96
+ assert result == expected
97
+
98
+ @given(x=integers())
99
+ def test_non_repr_field(self, *, x: int) -> None:
100
+ @dataclass(kw_only=True, slots=True)
101
+ class Example:
102
+ x: int = field(default=0, repr=False)
103
+
104
+ obj = Example(x=x)
105
+ result = dataclass_repr(obj)
106
+ expected = "Example()"
107
+ assert result == expected
108
+
109
+
68
110
  class TestDataclassToDictAndDataclassRepr:
69
111
  @given(x=integers(), defaults=booleans())
70
112
  def test_field_without_defaults(self, *, x: int, defaults: bool) -> None:
@@ -328,31 +370,51 @@ class TestReplaceNonSentinel:
328
370
  assert obj.int_ == 1
329
371
 
330
372
 
331
- class TestReprWithoutDefaults:
332
- def test_overriding_repr(self) -> None:
333
- @dataclass(kw_only=True, slots=True)
334
- class Example:
335
- x: int = 0
336
-
337
- @override
338
- def __repr__(self) -> str:
339
- return dataclass_repr(self)
373
+ class TestTextToDataClass:
374
+ @given(int_=integers())
375
+ def test_main_text(self, *, int_: int) -> None:
376
+ result = text_to_dataclass(f"int_={int_}", DataClassFutureInt)
377
+ expected = DataClassFutureInt(int_=int_)
378
+ assert result == expected
340
379
 
341
- obj = Example()
342
- result = repr(obj)
343
- expected = "Example()"
380
+ @given(int_=integers())
381
+ def test_main_mapping(self, *, int_: int) -> None:
382
+ result = text_to_dataclass({"int_": str(int_)}, DataClassFutureInt)
383
+ expected = DataClassFutureInt(int_=int_)
344
384
  assert result == expected
345
385
 
346
- @given(x=integers())
347
- def test_non_repr_field(self, *, x: int) -> None:
348
- @dataclass(kw_only=True, slots=True)
386
+ def test_error_split_key_value_pair(self) -> None:
387
+ with raises(
388
+ _TextToDataClassSplitKeyValuePairError,
389
+ match="Unable to construct 'DataClassFutureInt'; failed to split key-value pair 'keyvalue'",
390
+ ):
391
+ _ = text_to_dataclass("keyvalue", DataClassFutureInt)
392
+
393
+ def test_error_get_field_empty(self) -> None:
394
+ with raises(
395
+ _TextToDataClassGetFieldEmptyError,
396
+ match=r"Dataclass 'DataClassFutureInt' does not contain any field starting with 'k' \(modulo case\)",
397
+ ):
398
+ _ = text_to_dataclass("k=value", DataClassFutureInt)
399
+
400
+ def test_error_get_field_non_unique(self) -> None:
401
+ @dataclass(order=True, unsafe_hash=True, kw_only=True)
349
402
  class Example:
350
- x: int = field(default=0, repr=False)
403
+ int1: int
404
+ int2: int
351
405
 
352
- obj = Example(x=x)
353
- result = dataclass_repr(obj)
354
- expected = "Example()"
355
- assert result == expected
406
+ with raises(
407
+ _TextToDataClassGetFieldNonUniqueError,
408
+ match=r"Dataclass 'Example' must contain exactly one field starting with 'int' \(modulo case\); got 'int1', 'int2' and perhaps more",
409
+ ):
410
+ _ = text_to_dataclass("int=value", Example)
411
+
412
+ def test_error_parse_value(self) -> None:
413
+ with raises(
414
+ _TextToDataClassParseValueError,
415
+ match="Unable to construct 'DataClassFutureInt'; unable to parse field 'int_' of type <class 'int'>; got 'invalid'",
416
+ ):
417
+ _ = text_to_dataclass("int_=invalid", DataClassFutureInt)
356
418
 
357
419
 
358
420
  class TestYieldFields:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.108.3"
3
+ __version__ = "0.108.4"
@@ -1,7 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Mapping
3
4
  from dataclasses import MISSING, dataclass, field, fields, replace
4
- from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, overload, override
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ Any,
8
+ Generic,
9
+ Literal,
10
+ TypeVar,
11
+ assert_never,
12
+ overload,
13
+ override,
14
+ )
5
15
 
6
16
  from utilities.errors import ImpossibleCaseError
7
17
  from utilities.functions import (
@@ -11,14 +21,16 @@ from utilities.functions import (
11
21
  )
12
22
  from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
13
23
  from utilities.operator import is_equal
24
+ from utilities.parse import ParseTextError, parse_text
14
25
  from utilities.reprlib import get_repr
15
26
  from utilities.sentinel import Sentinel, sentinel
27
+ from utilities.types import TDataclass
16
28
  from utilities.typing import get_type_hints
17
29
 
18
30
  if TYPE_CHECKING:
19
- from collections.abc import Callable, Iterable, Iterator, Mapping
31
+ from collections.abc import Callable, Iterable, Iterator
20
32
 
21
- from utilities.types import Dataclass, StrMapping, TDataclass
33
+ from utilities.types import Dataclass, StrMapping
22
34
 
23
35
 
24
36
  _T = TypeVar("_T")
@@ -118,7 +130,7 @@ def dataclass_to_dict(
118
130
  recursive: bool = False,
119
131
  ) -> StrMapping:
120
132
  """Convert a dataclass to a dictionary."""
121
- out: dict[str, Any] = {}
133
+ out: StrMapping = {}
122
134
  for fld in yield_fields(obj, globalns=globalns, localns=localns):
123
135
  if fld.keep(
124
136
  include=include,
@@ -233,9 +245,7 @@ class _MappingToDataclassEmptyError(MappingToDataclassError):
233
245
  @override
234
246
  def __str__(self) -> str:
235
247
  desc = f"Mapping {get_repr(self.mapping)} does not contain {self.field!r}"
236
- if not self.case_sensitive:
237
- desc += " (modulo case)"
238
- return desc
248
+ return desc if self.case_sensitive else f"{desc} (modulo case)"
239
249
 
240
250
 
241
251
  @dataclass(kw_only=True, slots=True)
@@ -280,6 +290,142 @@ def replace_non_sentinel(
280
290
  ##
281
291
 
282
292
 
293
+ def text_to_dataclass(
294
+ text_or_mapping: str | Mapping[str, str],
295
+ cls: type[TDataclass],
296
+ /,
297
+ *,
298
+ globalns: StrMapping | None = None,
299
+ localns: StrMapping | None = None,
300
+ case_sensitive: bool = False,
301
+ ) -> TDataclass:
302
+ """Construct a dataclass from a string or a mapping or strings."""
303
+ fields = list(yield_fields(cls, globalns=globalns, localns=localns))
304
+ match text_or_mapping:
305
+ case str() as text:
306
+ text_mapping = _text_to_dataclass_split_text(text, cls)
307
+ case Mapping() as text_mapping:
308
+ ...
309
+ case _ as never:
310
+ assert_never(never)
311
+ value_mapping = dict(
312
+ _text_to_dataclass_get_and_parse(
313
+ fields, key, value, cls, case_sensitive=case_sensitive
314
+ )
315
+ for key, value in text_mapping.items()
316
+ )
317
+ return mapping_to_dataclass(
318
+ cls,
319
+ value_mapping,
320
+ globalns=globalns,
321
+ localns=localns,
322
+ case_sensitive=case_sensitive,
323
+ )
324
+
325
+
326
+ def _text_to_dataclass_split_text(
327
+ text: str, cls: type[TDataclass], /
328
+ ) -> Mapping[str, str]:
329
+ pairs = (t for t in text.split(",") if t != "")
330
+ return dict(_text_to_dataclass_split_key_value_pair(pair, cls) for pair in pairs)
331
+
332
+
333
+ def _text_to_dataclass_split_key_value_pair(
334
+ text: str, cls: type[Dataclass], /
335
+ ) -> tuple[str, str]:
336
+ try:
337
+ key, value = text.split("=")
338
+ except ValueError:
339
+ raise _TextToDataClassSplitKeyValuePairError(cls=cls, text=text) from None
340
+ return key, value
341
+
342
+
343
+ def _text_to_dataclass_get_and_parse(
344
+ fields: Iterable[_YieldFieldsClass[Any]],
345
+ key: str,
346
+ value: str,
347
+ cls: type[Dataclass],
348
+ /,
349
+ *,
350
+ case_sensitive: bool = False,
351
+ ) -> tuple[str, Any]:
352
+ mapping = {f.name: f for f in fields}
353
+ try:
354
+ name = one_str(mapping, key, head=True, case_sensitive=case_sensitive)
355
+ except OneStrEmptyError:
356
+ raise _TextToDataClassGetFieldEmptyError(
357
+ cls=cls, key=key, case_sensitive=case_sensitive
358
+ ) from None
359
+ except OneStrNonUniqueError as error:
360
+ raise _TextToDataClassGetFieldNonUniqueError(
361
+ cls=cls,
362
+ key=key,
363
+ case_sensitive=case_sensitive,
364
+ first=error.first,
365
+ second=error.second,
366
+ ) from None
367
+ field = mapping[name]
368
+ try:
369
+ parsed = parse_text(field.type_, value, case_sensitive=case_sensitive)
370
+ except ParseTextError:
371
+ raise _TextToDataClassParseValueError(
372
+ cls=cls, field=field, text=value
373
+ ) from None
374
+ return key, parsed
375
+
376
+
377
+ @dataclass(kw_only=True, slots=True)
378
+ class TextToDataClassError(Exception, Generic[TDataclass]):
379
+ cls: type[TDataclass]
380
+
381
+
382
+ @dataclass(kw_only=True, slots=True)
383
+ class _TextToDataClassSplitKeyValuePairError(TextToDataClassError):
384
+ text: str
385
+
386
+ @override
387
+ def __str__(self) -> str:
388
+ return f"Unable to construct {get_class_name(self.cls)!r}; failed to split key-value pair {self.text!r}"
389
+
390
+
391
+ @dataclass(kw_only=True, slots=True)
392
+ class _TextToDataClassGetFieldEmptyError(TextToDataClassError[TDataclass]):
393
+ key: str
394
+ case_sensitive: bool = False
395
+
396
+ @override
397
+ def __str__(self) -> str:
398
+ desc = f"Dataclass {get_class_name(self.cls)!r} does not contain any field starting with {self.key!r}"
399
+ return desc if self.case_sensitive else f"{desc} (modulo case)"
400
+
401
+
402
+ @dataclass(kw_only=True, slots=True)
403
+ class _TextToDataClassGetFieldNonUniqueError(TextToDataClassError[TDataclass]):
404
+ key: str
405
+ case_sensitive: bool = False
406
+ first: str
407
+ second: str
408
+
409
+ @override
410
+ def __str__(self) -> str:
411
+ head = f"Dataclass {get_class_name(self.cls)!r} must contain exactly one field starting with {self.key!r}"
412
+ mid = "" if self.case_sensitive else " (modulo case)"
413
+ return f"{head}{mid}; got {self.first!r}, {self.second!r} and perhaps more"
414
+
415
+
416
+ @dataclass(kw_only=True, slots=True)
417
+ class _TextToDataClassParseValueError(TextToDataClassError[TDataclass]):
418
+ field: _YieldFieldsClass[Any]
419
+ text: str
420
+
421
+ @override
422
+ def __str__(self) -> str:
423
+ return f"Unable to construct {get_class_name(self.cls)!r}; unable to parse field {self.field.name!r} of type {self.field.type_!r}; got {self.text!r}"
424
+
425
+
426
+ ##
427
+
428
+
283
429
  @overload
284
430
  def yield_fields(
285
431
  obj: Dataclass,
@@ -346,18 +492,18 @@ def yield_fields(
346
492
  raise YieldFieldsError(obj=obj)
347
493
 
348
494
 
349
- @dataclass(kw_only=True, slots=True)
495
+ @dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
350
496
  class _YieldFieldsInstance(Generic[_T]):
351
497
  name: str
352
- value: _T
353
- type_: Any
354
- default: _T | Sentinel = sentinel
355
- default_factory: Callable[[], _T] | Sentinel = sentinel
498
+ value: _T = field(hash=False)
499
+ type_: Any = field(hash=False)
500
+ default: _T | Sentinel = field(default=sentinel, hash=False)
501
+ default_factory: Callable[[], _T] | Sentinel = field(default=sentinel, hash=False)
356
502
  repr: bool = True
357
503
  hash_: bool | None = None
358
504
  init: bool = True
359
505
  compare: bool = True
360
- metadata: StrMapping = field(default_factory=dict)
506
+ metadata: StrMapping = field(default_factory=dict, hash=False)
361
507
  kw_only: bool | Sentinel = sentinel
362
508
 
363
509
  def equals_default(
@@ -407,17 +553,17 @@ class _YieldFieldsInstance(Generic[_T]):
407
553
  return (defaults and equal) or not equal
408
554
 
409
555
 
410
- @dataclass(kw_only=True, slots=True)
556
+ @dataclass(order=True, unsafe_hash=True, kw_only=True, slots=True)
411
557
  class _YieldFieldsClass(Generic[_T]):
412
558
  name: str
413
- type_: Any
414
- default: _T | Sentinel = sentinel
415
- default_factory: Callable[[], _T] | Sentinel = sentinel
559
+ type_: Any = field(hash=False)
560
+ default: _T | Sentinel = field(default=sentinel, hash=False)
561
+ default_factory: Callable[[], _T] | Sentinel = field(default=sentinel, hash=False)
416
562
  repr: bool = True
417
563
  hash_: bool | None = None
418
564
  init: bool = True
419
565
  compare: bool = True
420
- metadata: StrMapping = field(default_factory=dict)
566
+ metadata: StrMapping = field(default_factory=dict, hash=False)
421
567
  kw_only: bool | Sentinel = sentinel
422
568
 
423
569
 
@@ -434,10 +580,12 @@ class YieldFieldsError(Exception):
434
580
 
435
581
  __all__ = [
436
582
  "MappingToDataclassError",
583
+ "TextToDataClassError",
437
584
  "YieldFieldsError",
438
585
  "dataclass_repr",
439
586
  "dataclass_to_dict",
440
587
  "mapping_to_dataclass",
441
588
  "replace_non_sentinel",
589
+ "text_to_dataclass",
442
590
  "yield_fields",
443
591
  ]