dycw-utilities 0.109.7__tar.gz → 0.109.9__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.109.7 → dycw_utilities-0.109.9}/PKG-INFO +1 -1
  2. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/pyproject.toml +2 -2
  3. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_parse.py +70 -3
  4. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_polars.py +93 -2
  5. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_typing_funcs/with_future.py +16 -0
  6. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/__init__.py +1 -1
  7. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/dataclasses.py +3 -3
  8. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/parse.py +15 -12
  9. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/polars.py +115 -0
  10. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/python_dotenv.py +4 -7
  11. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/types.py +5 -0
  12. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/.gitignore +0 -0
  13. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/LICENSE +0 -0
  14. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/README.md +0 -0
  15. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/__init__.py +0 -0
  16. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/conftest.py +0 -0
  17. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/__init__.py +0 -0
  18. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_missing/__init__.py +0 -0
  19. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_missing/module.py +0 -0
  20. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/__init__.py +0 -0
  21. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/outer_1.py +0 -0
  22. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/outer_2.py +0 -0
  23. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  24. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  25. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  26. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  27. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_without/__init__.py +0 -0
  28. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_without/module_1.py +0 -0
  29. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/package_without/module_2.py +0 -0
  30. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/standalone.py +0 -0
  31. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/modules/with_imports.py +0 -0
  32. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  33. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  34. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  35. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  36. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  37. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  38. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  39. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  40. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/__init__.py +0 -0
  41. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/test_async_service/__init__.py +0 -0
  42. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/test_async_service/__main__.py +0 -0
  43. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/test_async_service/run.sh +0 -0
  44. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
  45. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
  46. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/scripts/test_queue_processor/run.sh +0 -0
  47. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_altair.py +0 -0
  48. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_astor.py +0 -0
  49. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_asyncio.py +0 -0
  50. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_atomicwrites.py +0 -0
  51. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_atools.py +0 -0
  52. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_cachetools.py +0 -0
  53. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_click.py +0 -0
  54. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_concurrent.py +0 -0
  55. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_contextlib.py +0 -0
  56. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_contextvars.py +0 -0
  57. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_cryptography.py +0 -0
  58. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_cvxpy.py +0 -0
  59. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_dataclasses.py +0 -0
  60. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_datetime.py +0 -0
  61. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_enum.py +0 -0
  62. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_errors.py +0 -0
  63. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_eventkit.py +0 -0
  64. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_fastapi.py +0 -0
  65. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_fpdf2.py +0 -0
  66. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_functions.py +0 -0
  67. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_functools.py +0 -0
  68. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_getpass.py +0 -0
  69. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_git.py +0 -0
  70. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_hashlib.py +0 -0
  71. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_http.py +0 -0
  72. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_hypothesis.py +0 -0
  73. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_ipython.py +0 -0
  74. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_iterables.py +0 -0
  75. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_jupyter.py +0 -0
  76. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_logging.py +0 -0
  77. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_loguru.py +0 -0
  78. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_luigi.py +0 -0
  79. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_math.py +0 -0
  80. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_memory_profiler.py +0 -0
  81. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_modules.py +0 -0
  82. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_more_itertools.py +0 -0
  83. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_numpy.py +0 -0
  84. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_operator.py +0 -0
  85. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_optuna.py +0 -0
  86. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_orjson.py +0 -0
  87. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_os.py +0 -0
  88. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pathlib.py +0 -0
  89. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_period.py +0 -0
  90. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pickle.py +0 -0
  91. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_platform.py +0 -0
  92. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pqdm.py +0 -0
  93. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pydantic.py +0 -0
  94. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pyinstrument.py +0 -0
  95. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pyrsistent.py +0 -0
  96. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pytest.py +0 -0
  97. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_pytest_regressions.py +0 -0
  98. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_python_dotenv.py +0 -0
  99. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_random.py +0 -0
  100. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_re.py +0 -0
  101. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_redis.py +0 -0
  102. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_reprlib.py +0 -0
  103. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_rich.py +0 -0
  104. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_scipy.py +0 -0
  105. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_sentinel.py +0 -0
  106. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_shelve.py +0 -0
  107. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_slack_sdk.py +0 -0
  108. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_socket.py +0 -0
  109. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_sqlalchemy.py +0 -0
  110. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_sqlalchemy_polars.py +0 -0
  111. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_streamlit.py +0 -0
  112. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_sys.py +0 -0
  113. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_tempfile.py +0 -0
  114. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_tenacity.py +0 -0
  115. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_text.py +0 -0
  116. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_threading.py +0 -0
  117. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_timer.py +0 -0
  118. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback.py +0 -0
  119. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/__init__.py +0 -0
  120. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/chain.py +0 -0
  121. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  122. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  123. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  124. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/many.py +0 -0
  125. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/one.py +0 -0
  126. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/recursive.py +0 -0
  127. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  128. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  129. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/two.py +0 -0
  130. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_traceback_funcs/untraced.py +0 -0
  131. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_types.py +0 -0
  132. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_typing.py +0 -0
  133. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_typing_funcs/__init__.py +0 -0
  134. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_typing_funcs/no_future.py +0 -0
  135. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_tzdata.py +0 -0
  136. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_tzlocal.py +0 -0
  137. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_uuid.py +0 -0
  138. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_version.py +0 -0
  139. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_warnings.py +0 -0
  140. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_whenever.py +0 -0
  141. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_zipfile.py +0 -0
  142. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/tests/test_zoneinfo.py +0 -0
  143. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/altair.py +0 -0
  144. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/astor.py +0 -0
  145. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/asyncio.py +0 -0
  146. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/atomicwrites.py +0 -0
  147. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/atools.py +0 -0
  148. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/cachetools.py +0 -0
  149. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/click.py +0 -0
  150. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/concurrent.py +0 -0
  151. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/contextlib.py +0 -0
  152. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/contextvars.py +0 -0
  153. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/cryptography.py +0 -0
  154. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/cvxpy.py +0 -0
  155. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/datetime.py +0 -0
  156. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/enum.py +0 -0
  157. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/errors.py +0 -0
  158. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/eventkit.py +0 -0
  159. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/fastapi.py +0 -0
  160. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/fpdf2.py +0 -0
  161. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/functions.py +0 -0
  162. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/functools.py +0 -0
  163. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/getpass.py +0 -0
  164. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/git.py +0 -0
  165. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/hashlib.py +0 -0
  166. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/http.py +0 -0
  167. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/hypothesis.py +0 -0
  168. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/ipython.py +0 -0
  169. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/iterables.py +0 -0
  170. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/jupyter.py +0 -0
  171. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/logging.py +0 -0
  172. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/loguru.py +0 -0
  173. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/luigi.py +0 -0
  174. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/math.py +0 -0
  175. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/memory_profiler.py +0 -0
  176. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/modules.py +0 -0
  177. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/more_itertools.py +0 -0
  178. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/numpy.py +0 -0
  179. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/operator.py +0 -0
  180. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/optuna.py +0 -0
  181. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/orjson.py +0 -0
  182. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/os.py +0 -0
  183. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pathlib.py +0 -0
  184. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/period.py +0 -0
  185. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pickle.py +0 -0
  186. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/platform.py +0 -0
  187. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pqdm.py +0 -0
  188. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/py.typed +0 -0
  189. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pydantic.py +0 -0
  190. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pyinstrument.py +0 -0
  191. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pyrsistent.py +0 -0
  192. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pytest.py +0 -0
  193. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/pytest_regressions.py +0 -0
  194. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/random.py +0 -0
  195. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/re.py +0 -0
  196. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/redis.py +0 -0
  197. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/reprlib.py +0 -0
  198. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/rich.py +0 -0
  199. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/scipy.py +0 -0
  200. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/sentinel.py +0 -0
  201. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/shelve.py +0 -0
  202. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/slack_sdk.py +0 -0
  203. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/socket.py +0 -0
  204. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/sqlalchemy.py +0 -0
  205. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/sqlalchemy_polars.py +0 -0
  206. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/streamlit.py +0 -0
  207. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/sys.py +0 -0
  208. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/tempfile.py +0 -0
  209. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/tenacity.py +0 -0
  210. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/text.py +0 -0
  211. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/threading.py +0 -0
  212. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/timer.py +0 -0
  213. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/traceback.py +0 -0
  214. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/typing.py +0 -0
  215. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/tzdata.py +0 -0
  216. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/tzlocal.py +0 -0
  217. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/uuid.py +0 -0
  218. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/version.py +0 -0
  219. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/warnings.py +0 -0
  220. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/whenever.py +0 -0
  221. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/zipfile.py +0 -0
  222. {dycw_utilities-0.109.7 → dycw_utilities-0.109.9}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.109.7
3
+ Version: 0.109.9
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -89,7 +89,7 @@ dependencies = [
89
89
  name = "dycw-utilities"
90
90
  readme = "README.md"
91
91
  requires-python = ">= 3.12"
92
- version = "0.109.7"
92
+ version = "0.109.9"
93
93
 
94
94
  [project.optional-dependencies]
95
95
  test = [
@@ -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.109.7"
335
+ current_version = "0.109.9"
336
336
 
337
337
  [[tool.bumpversion.files]]
338
338
  filename = "src/utilities/__init__.py"
@@ -14,9 +14,14 @@ from pytest import raises
14
14
  from tests.test_operator import TruthEnum
15
15
  from tests.test_typing_funcs.with_future import (
16
16
  DataClassFutureInt,
17
+ DataClassFutureIntEven,
18
+ DataClassFutureIntEvenOrOddTypeUnion,
19
+ DataClassFutureIntEvenOrOddUnion,
20
+ DataClassFutureIntOdd,
17
21
  TrueOrFalseFutureLit,
18
22
  TrueOrFalseFutureTypeLit,
19
23
  )
24
+ from utilities.errors import ImpossibleCaseError
20
25
  from utilities.functions import ensure_path
21
26
  from utilities.hypothesis import (
22
27
  datetime_durations,
@@ -78,7 +83,7 @@ class TestParseText:
78
83
  assert result is truth
79
84
 
80
85
  @given(value=integers())
81
- def test_extra(self, *, value: int) -> None:
86
+ def test_extra_type(self, *, value: int) -> None:
82
87
  text = str(value)
83
88
  result = parse_text(
84
89
  DataClassFutureInt,
@@ -200,6 +205,60 @@ class TestParseText:
200
205
  result = parse_text(TrueOrFalseFutureTypeLit, truth)
201
206
  assert result == truth
202
207
 
208
+ @given(value=integers())
209
+ def test_type_union_with_extra(self, *, value: int) -> None:
210
+ def parse_even_or_odd(text: str, /) -> DataClassFutureIntEvenOrOddTypeUnion:
211
+ value = int(text)
212
+ match value % 2:
213
+ case 0:
214
+ return DataClassFutureIntEven(even_int=value)
215
+ case 1:
216
+ return DataClassFutureIntOdd(odd_int=value)
217
+ case _:
218
+ raise ImpossibleCaseError(case=[f"{value=}"])
219
+
220
+ text = str(value)
221
+ result = parse_text(
222
+ DataClassFutureIntEvenOrOddTypeUnion,
223
+ text,
224
+ extra={DataClassFutureIntEvenOrOddTypeUnion: parse_even_or_odd},
225
+ )
226
+ match value % 2:
227
+ case 0:
228
+ expected = DataClassFutureIntEven(even_int=value)
229
+ case 1:
230
+ expected = DataClassFutureIntOdd(odd_int=value)
231
+ case _:
232
+ raise ImpossibleCaseError(case=[f"{value=}"])
233
+ assert result == expected
234
+
235
+ @given(value=integers())
236
+ def test_union_with_extra(self, *, value: int) -> None:
237
+ def parse_even_or_odd(text: str, /) -> DataClassFutureIntEvenOrOddUnion:
238
+ value = int(text)
239
+ match value % 2:
240
+ case 0:
241
+ return DataClassFutureIntEven(even_int=value)
242
+ case 1:
243
+ return DataClassFutureIntOdd(odd_int=value)
244
+ case _:
245
+ raise ImpossibleCaseError(case=[f"{value=}"])
246
+
247
+ text = str(value)
248
+ result = parse_text(
249
+ DataClassFutureIntEvenOrOddUnion,
250
+ text,
251
+ extra={DataClassFutureIntEvenOrOddUnion: parse_even_or_odd},
252
+ )
253
+ match value % 2:
254
+ case 0:
255
+ expected = DataClassFutureIntEven(even_int=value)
256
+ case 1:
257
+ expected = DataClassFutureIntOdd(odd_int=value)
258
+ case _:
259
+ raise ImpossibleCaseError(case=[f"{value=}"])
260
+ assert result == expected
261
+
203
262
  @given(version=versions())
204
263
  def test_version(self, *, version: Version) -> None:
205
264
  text = str(version)
@@ -272,6 +331,13 @@ class TestParseText:
272
331
  },
273
332
  )
274
333
 
334
+ def test_error_union_type_extra(self) -> None:
335
+ with raises(
336
+ _ParseTextParseError,
337
+ match=r"Unable to parse tests\.test_typing_funcs\.with_future\.DataClassFutureIntEven \| tests\.test_typing_funcs\.with_future\.DataClassFutureIntOdd; got 'invalid'",
338
+ ):
339
+ _ = parse_text(DataClassFutureIntEvenOrOddUnion, "invalid", extra={})
340
+
275
341
  def test_error_float(self) -> None:
276
342
  with raises(
277
343
  _ParseTextParseError, match="Unable to parse <class 'float'>; got 'invalid'"
@@ -364,9 +430,10 @@ class TestParseText:
364
430
 
365
431
  def test_error_unknown_union_type(self) -> None:
366
432
  with raises(
367
- _ParseTextParseError, match=r"Unable to parse <class '.*'>; got 'invalid'"
433
+ _ParseTextParseError,
434
+ match=r"Unable to parse tests\.test_typing_funcs\.with_future\.DataClassFutureIntEven \| tests\.test_typing_funcs\.with_future\.DataClassFutureIntOdd; got 'invalid'",
368
435
  ):
369
- _ = parse_text(DataClassFutureInt | DataClassFutureInt, "invalid")
436
+ _ = parse_text(DataClassFutureIntEvenOrOddUnion, "invalid")
370
437
 
371
438
  def test_error_version(self) -> None:
372
439
  with raises(
@@ -11,6 +11,7 @@ from pathlib import Path
11
11
  from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast
12
12
  from uuid import UUID, uuid4
13
13
 
14
+ import hypothesis.strategies
14
15
  import polars as pl
15
16
  from hypothesis import given
16
17
  from hypothesis.strategies import (
@@ -20,7 +21,6 @@ from hypothesis.strategies import (
20
21
  data,
21
22
  fixed_dictionaries,
22
23
  floats,
23
- integers,
24
24
  lists,
25
25
  none,
26
26
  sampled_from,
@@ -50,10 +50,12 @@ from polars import (
50
50
  from polars.testing import assert_frame_equal, assert_series_equal
51
51
  from pytest import raises
52
52
 
53
+ import utilities.polars
53
54
  from utilities.datetime import get_now, get_today
54
55
  from utilities.hypothesis import (
55
56
  assume_does_not_raise,
56
57
  int64s,
58
+ pairs,
57
59
  text_ascii,
58
60
  zoned_datetimes,
59
61
  )
@@ -129,11 +131,13 @@ from utilities.polars import (
129
131
  map_over_columns,
130
132
  nan_sum_agg,
131
133
  nan_sum_cols,
134
+ normal,
132
135
  replace_time_zone,
133
136
  set_first_row_as_columns,
134
137
  struct_dtype,
135
138
  struct_from_dataclass,
136
139
  touch,
140
+ uniform,
137
141
  unique_element,
138
142
  week_num,
139
143
  yield_struct_series_dataclasses,
@@ -1210,7 +1214,7 @@ class TestGetDataTypeOrSeriesTimeZone:
1210
1214
 
1211
1215
 
1212
1216
  class TestGetSeriesNumberOfDecimals:
1213
- @given(data=data(), n=integers(1, 10), nullable=booleans())
1217
+ @given(data=data(), n=hypothesis.strategies.integers(1, 10), nullable=booleans())
1214
1218
  def test_main(self, *, data: DataObject, n: int, nullable: bool) -> None:
1215
1219
  strategy = int64s() | none() if nullable else int64s()
1216
1220
  ints_or_none = data.draw(lists(strategy, min_size=1, max_size=10))
@@ -1312,6 +1316,37 @@ class TestInsertBetween:
1312
1316
  _ = insert_between(self.df, "a", "c", lit(None).alias("new"))
1313
1317
 
1314
1318
 
1319
+ class TestIntegers:
1320
+ @given(
1321
+ length=hypothesis.strategies.integers(0, 10),
1322
+ high=hypothesis.strategies.integers(1, 10),
1323
+ )
1324
+ def test_int(self, *, length: int, high: int) -> None:
1325
+ series = utilities.polars.integers(length, high)
1326
+ assert series.len() == length
1327
+ assert series.is_between(0, high, closed="left").all()
1328
+
1329
+ @given(
1330
+ length=hypothesis.strategies.integers(0, 10),
1331
+ high=hypothesis.strategies.integers(1, 10),
1332
+ )
1333
+ def test_series(self, *, length: int, high: int) -> None:
1334
+ orig = int_range(end=length, eager=True)
1335
+ series = utilities.polars.integers(orig, high)
1336
+ assert series.len() == length
1337
+ assert series.is_between(0, high, closed="left").all()
1338
+
1339
+ @given(
1340
+ length=hypothesis.strategies.integers(0, 10),
1341
+ high=hypothesis.strategies.integers(1, 10),
1342
+ )
1343
+ def test_dataframe(self, *, length: int, high: int) -> None:
1344
+ df = int_range(end=length, eager=True).to_frame()
1345
+ series = utilities.polars.integers(df, high)
1346
+ assert series.len() == length
1347
+ assert series.is_between(0, high, closed="left").all()
1348
+
1349
+
1315
1350
  class TestIsNullAndIsNotNullStructSeries:
1316
1351
  @given(
1317
1352
  case=sampled_from([
@@ -1526,6 +1561,28 @@ class TestNanSumCols:
1526
1561
  assert df["z"].item() == expected
1527
1562
 
1528
1563
 
1564
+ class TestNormal:
1565
+ @given(length=hypothesis.strategies.integers(0, 10))
1566
+ def test_int(self, *, length: int) -> None:
1567
+ series = normal(length)
1568
+ assert series.len() == length
1569
+ assert series.is_finite().all()
1570
+
1571
+ @given(length=hypothesis.strategies.integers(0, 10))
1572
+ def test_series(self, *, length: int) -> None:
1573
+ orig = int_range(end=length, eager=True)
1574
+ series = normal(orig)
1575
+ assert series.len() == length
1576
+ assert series.is_finite().all()
1577
+
1578
+ @given(length=hypothesis.strategies.integers(0, 10))
1579
+ def test_dataframe(self, *, length: int) -> None:
1580
+ df = int_range(end=length, eager=True).to_frame()
1581
+ series = normal(df)
1582
+ assert series.len() == length
1583
+ assert series.is_finite().all()
1584
+
1585
+
1529
1586
  class TestReplaceTimeZone:
1530
1587
  def test_datetime(self) -> None:
1531
1588
  now_utc = get_now()
@@ -1711,6 +1768,40 @@ class TestStructFromDataClass:
1711
1768
  _ = struct_from_dataclass(Example)
1712
1769
 
1713
1770
 
1771
+ class TestUniform:
1772
+ @given(
1773
+ length=hypothesis.strategies.integers(0, 10),
1774
+ bounds=pairs(floats(0.0, 1.0), sorted=True),
1775
+ )
1776
+ def test_int(self, *, length: int, bounds: tuple[float, float]) -> None:
1777
+ low, high = bounds
1778
+ series = uniform(length, low=low, high=high)
1779
+ assert series.len() == length
1780
+ assert series.is_between(low, high).all()
1781
+
1782
+ @given(
1783
+ length=hypothesis.strategies.integers(0, 10),
1784
+ bounds=pairs(floats(0.0, 1.0), sorted=True),
1785
+ )
1786
+ def test_series(self, *, length: int, bounds: tuple[float, float]) -> None:
1787
+ low, high = bounds
1788
+ orig = int_range(end=length, eager=True)
1789
+ series = uniform(orig, low=low, high=high)
1790
+ assert series.len() == length
1791
+ assert series.is_between(low, high).all()
1792
+
1793
+ @given(
1794
+ length=hypothesis.strategies.integers(0, 10),
1795
+ bounds=pairs(floats(0.0, 1.0), sorted=True),
1796
+ )
1797
+ def test_dataframe(self, *, length: int, bounds: tuple[float, float]) -> None:
1798
+ low, high = bounds
1799
+ df = int_range(end=length, eager=True).to_frame()
1800
+ series = uniform(df, low=low, high=high)
1801
+ assert series.len() == length
1802
+ assert series.is_between(low, high).all()
1803
+
1804
+
1714
1805
  class TestUniqueElement:
1715
1806
  def test_main(self) -> None:
1716
1807
  series = Series(
@@ -54,6 +54,22 @@ class DataClassFutureIntDefault:
54
54
  int_: int = 0
55
55
 
56
56
 
57
+ @dataclass(order=True, unsafe_hash=True, kw_only=True)
58
+ class DataClassFutureIntEven:
59
+ even_int: int
60
+
61
+
62
+ @dataclass(order=True, unsafe_hash=True, kw_only=True)
63
+ class DataClassFutureIntOdd:
64
+ odd_int: int
65
+
66
+
67
+ DataClassFutureIntEvenOrOddUnion = DataClassFutureIntEven | DataClassFutureIntOdd
68
+ type DataClassFutureIntEvenOrOddTypeUnion = (
69
+ DataClassFutureIntEven | DataClassFutureIntOdd
70
+ )
71
+
72
+
57
73
  @dataclass(order=True, unsafe_hash=True, kw_only=True)
58
74
  class DataClassFutureIntNullable:
59
75
  int_: int | None = None
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.7"
3
+ __version__ = "0.109.9"
@@ -24,7 +24,7 @@ from utilities.iterables import OneStrEmptyError, OneStrNonUniqueError, one_str
24
24
  from utilities.operator import is_equal
25
25
  from utilities.parse import ParseTextError, parse_text
26
26
  from utilities.sentinel import Sentinel, sentinel
27
- from utilities.types import TDataclass
27
+ from utilities.types import ParseTextExtra, TDataclass
28
28
  from utilities.typing import get_type_hints
29
29
 
30
30
  if TYPE_CHECKING:
@@ -454,7 +454,7 @@ def text_to_dataclass(
454
454
  head: bool = False,
455
455
  case_sensitive: bool = False,
456
456
  allow_extra_keys: bool = False,
457
- extra_parsers: Mapping[type[_T], Callable[[str], _T]] | None = None,
457
+ extra_parsers: ParseTextExtra | None = None,
458
458
  ) -> TDataclass:
459
459
  """Construct a dataclass from a string or a mapping or strings."""
460
460
  match text_or_mapping:
@@ -524,7 +524,7 @@ def _text_to_dataclass_parse(
524
524
  *,
525
525
  head: bool = False,
526
526
  case_sensitive: bool = False,
527
- extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
527
+ extra: ParseTextExtra | None = None,
528
528
  ) -> Any:
529
529
  try:
530
530
  return parse_text(
@@ -7,7 +7,7 @@ from enum import Enum
7
7
  from pathlib import Path
8
8
  from re import DOTALL
9
9
  from types import NoneType
10
- from typing import TYPE_CHECKING, Any, TypeVar, override
10
+ from typing import Any, override
11
11
 
12
12
  from utilities.datetime import is_subclass_date_not_datetime
13
13
  from utilities.enum import ParseEnumError, parse_enum
@@ -17,7 +17,7 @@ from utilities.math import ParseNumberError, parse_number
17
17
  from utilities.re import ExtractGroupError, extract_group
18
18
  from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
19
19
  from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
20
- from utilities.types import Duration, Number
20
+ from utilities.types import Duration, Number, ParseTextExtra
21
21
  from utilities.typing import (
22
22
  get_args,
23
23
  is_literal_type,
@@ -27,12 +27,6 @@ from utilities.typing import (
27
27
  )
28
28
  from utilities.version import ParseVersionError, Version, parse_version
29
29
 
30
- if TYPE_CHECKING:
31
- from collections.abc import Callable, Mapping
32
-
33
-
34
- _T = TypeVar("_T")
35
-
36
30
 
37
31
  def parse_text(
38
32
  obj: Any,
@@ -41,7 +35,7 @@ def parse_text(
41
35
  *,
42
36
  head: bool = False,
43
37
  case_sensitive: bool = False,
44
- extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
38
+ extra: ParseTextExtra | None = None,
45
39
  ) -> Any:
46
40
  """Parse text."""
47
41
  if obj is None:
@@ -76,7 +70,7 @@ def parse_text(
76
70
  for arg, text in zip(args, texts, strict=True)
77
71
  )
78
72
  if is_union_type(obj):
79
- return _parse_text_union_type(obj, text)
73
+ return _parse_text_union_type(obj, text, extra=extra)
80
74
  raise _ParseTextParseError(obj=obj, text=text) from None
81
75
 
82
76
 
@@ -86,7 +80,7 @@ def _parse_text_type(
86
80
  /,
87
81
  *,
88
82
  case_sensitive: bool = False,
89
- extra: Mapping[type[_T], Callable[[str], _T]] | None = None,
83
+ extra: ParseTextExtra | None = None,
90
84
  ) -> Any:
91
85
  """Parse text."""
92
86
  if issubclass(cls, NoneType):
@@ -170,7 +164,9 @@ def _parse_text_type(
170
164
  raise _ParseTextParseError(obj=cls, text=text) from None
171
165
 
172
166
 
173
- def _parse_text_union_type(obj: Any, text: str, /) -> Any:
167
+ def _parse_text_union_type(
168
+ obj: Any, text: str, /, *, extra: ParseTextExtra | None = None
169
+ ) -> Any:
174
170
  if obj is Number:
175
171
  try:
176
172
  return parse_number(text)
@@ -183,6 +179,13 @@ def _parse_text_union_type(obj: Any, text: str, /) -> Any:
183
179
  return parse_duration(text)
184
180
  except ParseDurationError:
185
181
  raise _ParseTextParseError(obj=obj, text=text) from None
182
+ if extra is not None:
183
+ try:
184
+ parser = one(p for c, p in extra.items() if c is obj)
185
+ except OneEmptyError:
186
+ pass
187
+ else:
188
+ return parser(text)
186
189
  raise _ParseTextParseError(obj=obj, text=text) from None
187
190
 
188
191
 
@@ -1165,6 +1165,52 @@ class _InsertBetweenNonConsecutiveError(InsertBetweenError):
1165
1165
  ##
1166
1166
 
1167
1167
 
1168
+ def integers(
1169
+ obj: int | Series | DataFrame,
1170
+ low: int,
1171
+ /,
1172
+ *,
1173
+ high: int | None = None,
1174
+ seed: int | None = None,
1175
+ endpoint: bool = False,
1176
+ name: str | None = None,
1177
+ dtype: PolarsDataType = Int64,
1178
+ ) -> Series:
1179
+ """Construct a series of normally-distributed numbers."""
1180
+ match obj:
1181
+ case int() as height:
1182
+ from numpy.random import default_rng
1183
+
1184
+ rng = default_rng(seed=seed)
1185
+ values = rng.integers(low, high=high, size=height, endpoint=endpoint)
1186
+ return Series(name=name, values=values, dtype=dtype)
1187
+ case Series() as series:
1188
+ return integers(
1189
+ series.len(),
1190
+ low,
1191
+ high=high,
1192
+ seed=seed,
1193
+ endpoint=endpoint,
1194
+ name=name,
1195
+ dtype=dtype,
1196
+ )
1197
+ case DataFrame() as df:
1198
+ return integers(
1199
+ df.height,
1200
+ low,
1201
+ high=high,
1202
+ seed=seed,
1203
+ endpoint=endpoint,
1204
+ name=name,
1205
+ dtype=dtype,
1206
+ )
1207
+ case _ as never:
1208
+ assert_never(never)
1209
+
1210
+
1211
+ ##
1212
+
1213
+
1168
1214
  def is_not_null_struct_series(series: Series, /) -> Series:
1169
1215
  """Check if a struct-dtype Series is not null as per the <= 1.1 definition."""
1170
1216
  try:
@@ -1324,6 +1370,39 @@ def nan_sum_cols(
1324
1370
  ##
1325
1371
 
1326
1372
 
1373
+ def normal(
1374
+ obj: int | Series | DataFrame,
1375
+ /,
1376
+ *,
1377
+ loc: float = 0.0,
1378
+ scale: float = 1.0,
1379
+ seed: int | None = None,
1380
+ name: str | None = None,
1381
+ dtype: PolarsDataType = Float64,
1382
+ ) -> Series:
1383
+ """Construct a series of normally-distributed numbers."""
1384
+ match obj:
1385
+ case int() as height:
1386
+ from numpy.random import default_rng
1387
+
1388
+ rng = default_rng(seed=seed)
1389
+ values = rng.normal(loc=loc, scale=scale, size=height)
1390
+ return Series(name=name, values=values, dtype=dtype)
1391
+ case Series() as series:
1392
+ return normal(
1393
+ series.len(), loc=loc, scale=scale, seed=seed, name=name, dtype=dtype
1394
+ )
1395
+ case DataFrame() as df:
1396
+ return normal(
1397
+ df.height, loc=loc, scale=scale, seed=seed, name=name, dtype=dtype
1398
+ )
1399
+ case _ as never:
1400
+ assert_never(never)
1401
+
1402
+
1403
+ ##
1404
+
1405
+
1327
1406
  @overload
1328
1407
  def replace_time_zone(
1329
1408
  obj: Series, /, *, time_zone: TimeZoneLike | None = UTC
@@ -1461,6 +1540,39 @@ class _StructFromDataClassTypeError(StructFromDataClassError):
1461
1540
  ##
1462
1541
 
1463
1542
 
1543
+ def uniform(
1544
+ obj: int | Series | DataFrame,
1545
+ /,
1546
+ *,
1547
+ low: float = 0.0,
1548
+ high: float = 1.0,
1549
+ seed: int | None = None,
1550
+ name: str | None = None,
1551
+ dtype: PolarsDataType = Float64,
1552
+ ) -> Series:
1553
+ """Construct a series of uniformly-distributed numbers."""
1554
+ match obj:
1555
+ case int() as height:
1556
+ from numpy.random import default_rng
1557
+
1558
+ rng = default_rng(seed=seed)
1559
+ values = rng.uniform(low=low, high=high, size=height)
1560
+ return Series(name=name, values=values, dtype=dtype)
1561
+ case Series() as series:
1562
+ return uniform(
1563
+ series.len(), low=low, high=high, seed=seed, name=name, dtype=dtype
1564
+ )
1565
+ case DataFrame() as df:
1566
+ return uniform(
1567
+ df.height, low=low, high=high, seed=seed, name=name, dtype=dtype
1568
+ )
1569
+ case _ as never:
1570
+ assert_never(never)
1571
+
1572
+
1573
+ ##
1574
+
1575
+
1464
1576
  def unique_element(column: ExprLike, /) -> Expr:
1465
1577
  """Get the unique element in a list."""
1466
1578
  column = ensure_expr_or_series(column)
@@ -1645,17 +1757,20 @@ __all__ = [
1645
1757
  "insert_after",
1646
1758
  "insert_before",
1647
1759
  "insert_between",
1760
+ "integers",
1648
1761
  "is_not_null_struct_series",
1649
1762
  "is_null_struct_series",
1650
1763
  "join",
1651
1764
  "map_over_columns",
1652
1765
  "nan_sum_agg",
1653
1766
  "nan_sum_cols",
1767
+ "normal",
1654
1768
  "replace_time_zone",
1655
1769
  "set_first_row_as_columns",
1656
1770
  "struct_dtype",
1657
1771
  "struct_from_dataclass",
1658
1772
  "touch",
1773
+ "uniform",
1659
1774
  "unique_element",
1660
1775
  "yield_struct_series_dataclasses",
1661
1776
  "yield_struct_series_elements",
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from os import environ
5
- from typing import TYPE_CHECKING, TypeVar, override
5
+ from typing import TYPE_CHECKING, override
6
6
 
7
7
  from dotenv import dotenv_values
8
8
 
@@ -13,14 +13,11 @@ from utilities.pathlib import PWD
13
13
  from utilities.reprlib import get_repr
14
14
 
15
15
  if TYPE_CHECKING:
16
- from collections.abc import Callable, Mapping
16
+ from collections.abc import Mapping
17
17
  from collections.abc import Set as AbstractSet
18
18
  from pathlib import Path
19
19
 
20
- from utilities.types import PathLike, StrMapping, TDataclass
21
-
22
-
23
- _T = TypeVar("_T")
20
+ from utilities.types import ParseTextExtra, PathLike, StrMapping, TDataclass
24
21
 
25
22
 
26
23
  def load_settings(
@@ -33,7 +30,7 @@ def load_settings(
33
30
  warn_name_errors: bool = False,
34
31
  head: bool = False,
35
32
  case_sensitive: bool = False,
36
- extra_parsers: Mapping[type[_T], Callable[[str], _T]] | None = None,
33
+ extra_parsers: ParseTextExtra | None = None,
37
34
  ) -> TDataclass:
38
35
  """Load a set of settings from the `.env` file."""
39
36
  path = get_repo_root(cwd=cwd).joinpath(".env")
@@ -229,6 +229,10 @@ class SupportsRound(Protocol[_T_co]):
229
229
  def __round__(self, ndigits: int, /) -> _T_co: ...
230
230
 
231
231
 
232
+ # parse
233
+ type ParseTextExtra = Mapping[Any, Callable[[str], Any]]
234
+
235
+
232
236
  # pathlib
233
237
  type PathLike = MaybeStr[Path]
234
238
  type PathLikeOrCallable = PathLike | Callable[[], PathLike]
@@ -278,6 +282,7 @@ __all__ = [
278
282
  "OpenMode",
279
283
  "OptExcInfo",
280
284
  "Parallelism",
285
+ "ParseTextExtra",
281
286
  "PathLike",
282
287
  "PathLikeOrCallable",
283
288
  "RoundMode",