dycw-utilities 0.150.7__tar.gz → 0.150.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 (216) hide show
  1. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/PKG-INFO +1 -1
  2. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/pyproject.toml +2 -2
  3. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_hypothesis.py +44 -0
  4. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_postgres.py +9 -53
  5. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_sqlalchemy.py +55 -1
  6. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_text.py +59 -51
  7. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/__init__.py +1 -1
  8. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/hypothesis.py +37 -0
  9. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/postgres.py +19 -72
  10. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/sqlalchemy.py +77 -1
  11. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/text.py +20 -1
  12. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/.gitignore +0 -0
  13. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/LICENSE +0 -0
  14. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/README.md +0 -0
  15. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/__init__.py +0 -0
  16. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/conftest.py +0 -0
  17. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/__init__.py +0 -0
  18. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_missing/__init__.py +0 -0
  19. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_missing/module.py +0 -0
  20. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/__init__.py +0 -0
  21. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/outer_1.py +0 -0
  22. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/outer_2.py +0 -0
  23. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  24. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  25. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  26. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  27. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_without/__init__.py +0 -0
  28. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_without/module_1.py +0 -0
  29. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/package_without/module_2.py +0 -0
  30. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/standalone.py +0 -0
  31. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/modules/with_imports.py +0 -0
  32. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  33. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  34. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  35. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  36. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  37. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  38. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  39. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  40. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_altair.py +0 -0
  41. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_asyncio.py +0 -0
  42. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_atomicwrites.py +0 -0
  43. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_atools.py +0 -0
  44. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_cachetools.py +0 -0
  45. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_click.py +0 -0
  46. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_concurrent.py +0 -0
  47. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_contextlib.py +0 -0
  48. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_contextvars.py +0 -0
  49. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_cryptography.py +0 -0
  50. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_cvxpy.py +0 -0
  51. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_dataclasses.py +0 -0
  52. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_dnspython.py +0 -0
  53. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_enum.py +0 -0
  54. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_errors.py +0 -0
  55. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_eventkit.py +0 -0
  56. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_fastapi.py +0 -0
  57. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_fpdf2.py +0 -0
  58. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_functions.py +0 -0
  59. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_functools.py +0 -0
  60. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_getpass.py +0 -0
  61. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_gzip.py +0 -0
  62. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_hashlib.py +0 -0
  63. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_http.py +0 -0
  64. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_importlib.py +0 -0
  65. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_inflect.py +0 -0
  66. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_ipython.py +0 -0
  67. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_iterables.py +0 -0
  68. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_json.py +0 -0
  69. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_jupyter.py +0 -0
  70. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_libcst.py +0 -0
  71. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_lightweight_charts.py +0 -0
  72. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_logging.py +0 -0
  73. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_math.py +0 -0
  74. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_memory_profiler.py +0 -0
  75. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_modules.py +0 -0
  76. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_more_itertools.py +0 -0
  77. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_numpy.py +0 -0
  78. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_objects/__init__.py +0 -0
  79. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_objects/objects.py +0 -0
  80. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_operator.py +0 -0
  81. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_optuna.py +0 -0
  82. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_orjson.py +0 -0
  83. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_os.py +0 -0
  84. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_parse.py +0 -0
  85. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pathlib.py +0 -0
  86. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_period.py +0 -0
  87. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pickle.py +0 -0
  88. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_platform.py +0 -0
  89. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_polars.py +0 -0
  90. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_polars_ols.py +0 -0
  91. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pottery.py +0 -0
  92. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pqdm.py +0 -0
  93. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_psutil.py +0 -0
  94. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pyinstrument.py +0 -0
  95. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pytest.py +0 -0
  96. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pytest_randomly.py +0 -0
  97. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_pytest_regressions.py +0 -0
  98. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_random.py +0 -0
  99. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_re.py +0 -0
  100. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_redis.py +0 -0
  101. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_reprlib.py +0 -0
  102. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_scipy.py +0 -0
  103. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_sentinel.py +0 -0
  104. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_shelve.py +0 -0
  105. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_slack_sdk.py +0 -0
  106. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_socket.py +0 -0
  107. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_sqlalchemy_polars.py +0 -0
  108. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_statsmodels.py +0 -0
  109. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_string.py +0 -0
  110. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_tempfile.py +0 -0
  111. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_threading.py +0 -0
  112. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_timer.py +0 -0
  113. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_traceback.py +0 -0
  114. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_typed_settings.py +0 -0
  115. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_types.py +0 -0
  116. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_typing.py +0 -0
  117. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_typing_funcs/__init__.py +0 -0
  118. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_typing_funcs/no_future.py +0 -0
  119. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_typing_funcs/with_future.py +0 -0
  120. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_tzdata.py +0 -0
  121. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_tzlocal.py +0 -0
  122. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_uuid.py +0 -0
  123. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_version.py +0 -0
  124. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_warnings.py +0 -0
  125. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_whenever.py +0 -0
  126. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_yield_access/__init__.py +0 -0
  127. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_yield_access/script.py +0 -0
  128. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_yield_access/script.sh +0 -0
  129. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_zipfile.py +0 -0
  130. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/tests/test_zoneinfo.py +0 -0
  131. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/altair.py +0 -0
  132. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/asyncio.py +0 -0
  133. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/atomicwrites.py +0 -0
  134. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/atools.py +0 -0
  135. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/cachetools.py +0 -0
  136. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/click.py +0 -0
  137. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/concurrent.py +0 -0
  138. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/contextlib.py +0 -0
  139. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/contextvars.py +0 -0
  140. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/cryptography.py +0 -0
  141. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/cvxpy.py +0 -0
  142. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/dataclasses.py +0 -0
  143. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/dnspython.py +0 -0
  144. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/enum.py +0 -0
  145. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/errors.py +0 -0
  146. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/eventkit.py +0 -0
  147. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/fastapi.py +0 -0
  148. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/fpdf2.py +0 -0
  149. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/functions.py +0 -0
  150. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/functools.py +0 -0
  151. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/getpass.py +0 -0
  152. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/gzip.py +0 -0
  153. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/hashlib.py +0 -0
  154. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/http.py +0 -0
  155. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/importlib.py +0 -0
  156. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/inflect.py +0 -0
  157. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/ipython.py +0 -0
  158. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/iterables.py +0 -0
  159. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/json.py +0 -0
  160. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/jupyter.py +0 -0
  161. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/libcst.py +0 -0
  162. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/lightweight_charts.py +0 -0
  163. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/logging.py +0 -0
  164. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/math.py +0 -0
  165. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/memory_profiler.py +0 -0
  166. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/modules.py +0 -0
  167. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/more_itertools.py +0 -0
  168. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/numpy.py +0 -0
  169. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/operator.py +0 -0
  170. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/optuna.py +0 -0
  171. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/orjson.py +0 -0
  172. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/os.py +0 -0
  173. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/parse.py +0 -0
  174. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pathlib.py +0 -0
  175. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/period.py +0 -0
  176. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pickle.py +0 -0
  177. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/platform.py +0 -0
  178. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/polars.py +0 -0
  179. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/polars_ols.py +0 -0
  180. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pottery.py +0 -0
  181. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pqdm.py +0 -0
  182. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/psutil.py +0 -0
  183. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/py.typed +0 -0
  184. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pyinstrument.py +0 -0
  185. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pytest.py +0 -0
  186. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pytest_plugins/__init__.py +0 -0
  187. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  188. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  189. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/pytest_regressions.py +0 -0
  190. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/random.py +0 -0
  191. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/re.py +0 -0
  192. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/redis.py +0 -0
  193. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/reprlib.py +0 -0
  194. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/scipy.py +0 -0
  195. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/sentinel.py +0 -0
  196. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/shelve.py +0 -0
  197. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/slack_sdk.py +0 -0
  198. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/socket.py +0 -0
  199. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/sqlalchemy_polars.py +0 -0
  200. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/statsmodels.py +0 -0
  201. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/string.py +0 -0
  202. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/tempfile.py +0 -0
  203. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/threading.py +0 -0
  204. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/timer.py +0 -0
  205. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/traceback.py +0 -0
  206. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/typed_settings.py +0 -0
  207. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/types.py +0 -0
  208. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/typing.py +0 -0
  209. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/tzdata.py +0 -0
  210. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/tzlocal.py +0 -0
  211. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/uuid.py +0 -0
  212. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/version.py +0 -0
  213. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/warnings.py +0 -0
  214. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/whenever.py +0 -0
  215. {dycw_utilities-0.150.7 → dycw_utilities-0.150.9}/src/utilities/zipfile.py +0 -0
  216. {dycw_utilities-0.150.7 → dycw_utilities-0.150.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.150.7
3
+ Version: 0.150.9
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -103,7 +103,7 @@ dependencies = [
103
103
  name = "dycw-utilities"
104
104
  readme = "README.md"
105
105
  requires-python = ">= 3.12"
106
- version = "0.150.7"
106
+ version = "0.150.9"
107
107
 
108
108
  [project.entry-points.pytest11]
109
109
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -136,7 +136,7 @@ test = [
136
136
  # bump-my-version
137
137
  [tool.bumpversion]
138
138
  allow_dirty = true
139
- current_version = "0.150.7"
139
+ current_version = "0.150.9"
140
140
 
141
141
  [[tool.bumpversion.files]]
142
142
  filename = "src/utilities/__init__.py"
@@ -86,6 +86,7 @@ from utilities.hypothesis import (
86
86
  triples,
87
87
  uint32s,
88
88
  uint64s,
89
+ urls,
89
90
  versions,
90
91
  year_months,
91
92
  zoned_datetimes,
@@ -964,6 +965,49 @@ class TestUInt64s:
964
965
  assert max(min_value, MIN_UINT64) <= x <= min(max_value, MAX_UINT64)
965
966
 
966
967
 
968
+ class TestURLs:
969
+ @given(
970
+ data=data(),
971
+ all_=booleans(),
972
+ username=booleans(),
973
+ password=booleans(),
974
+ host=booleans(),
975
+ port=booleans(),
976
+ database=booleans(),
977
+ )
978
+ def test_main(
979
+ self,
980
+ *,
981
+ data: DataObject,
982
+ all_: bool,
983
+ username: bool,
984
+ password: bool,
985
+ host: bool,
986
+ port: bool,
987
+ database: bool,
988
+ ) -> None:
989
+ url = data.draw(
990
+ urls(
991
+ all_=all_,
992
+ username=username,
993
+ password=password,
994
+ host=host,
995
+ port=port,
996
+ database=database,
997
+ )
998
+ )
999
+ if all_ or username:
1000
+ assert url.username is not None
1001
+ if all_ or password:
1002
+ assert url.password is not None
1003
+ if all_ or host:
1004
+ assert url.host is not None
1005
+ if all_ or port:
1006
+ assert url.port is not None
1007
+ if all_ or database:
1008
+ assert url.database is not None
1009
+
1010
+
967
1011
  class TestVersions:
968
1012
  @given(data=data(), suffix=booleans())
969
1013
  def test_main(self, *, data: DataObject, suffix: bool) -> None:
@@ -7,14 +7,10 @@ from hypothesis.strategies import DrawFn, booleans, composite, lists, none, samp
7
7
  from pytest import raises
8
8
  from sqlalchemy import URL, Column, Integer, MetaData, Table
9
9
 
10
- from utilities.hypothesis import integers, temp_paths, text_ascii
10
+ from utilities.hypothesis import integers, temp_paths, text_ascii, urls
11
11
  from utilities.postgres import (
12
12
  _build_pg_dump,
13
13
  _build_pg_restore_or_psql,
14
- _extract_url,
15
- _ExtractURLDatabaseError,
16
- _ExtractURLHostError,
17
- _ExtractURLPortError,
18
14
  _path_pg_dump,
19
15
  _PGDumpFormat,
20
16
  _resolve_data_only_and_clean,
@@ -40,30 +36,15 @@ def tables(draw: DrawFn, /) -> list[Table | str]:
40
36
  return [draw(sampled_from([n, t])) for n, t in zip(names, tables, strict=True)]
41
37
 
42
38
 
43
- @composite
44
- def urls(draw: DrawFn, /) -> URL:
45
- username = draw(text_ascii(min_size=1) | none())
46
- password = draw(text_ascii(min_size=1) | none())
47
- host = draw(text_ascii(min_size=1))
48
- port = draw(integers(min_value=1))
49
- database = draw(text_ascii(min_size=1))
50
- return URL.create(
51
- drivername="postgres",
52
- username=username,
53
- password=password,
54
- host=host,
55
- port=port,
56
- database=database,
57
- )
58
-
59
-
60
39
  class TestPGDump:
61
- @given(url=urls(), path=temp_paths(), logger=text_ascii(min_size=1) | none())
40
+ @given(
41
+ url=urls(all_=True), path=temp_paths(), logger=text_ascii(min_size=1) | none()
42
+ )
62
43
  async def test_main(self, *, url: URL, path: Path, logger: str | None) -> None:
63
44
  _ = await pg_dump(url, path, dry_run=True, logger=logger)
64
45
 
65
46
  @given(
66
- url=urls(),
47
+ url=urls(all_=True),
67
48
  path=temp_paths(),
68
49
  format_=sampled_from(get_literal_elements(_PGDumpFormat)),
69
50
  jobs=integers(min_value=0) | none(),
@@ -137,15 +118,16 @@ class TestResolveDataOnlyAndClean:
137
118
 
138
119
 
139
120
  class TestRestore:
140
- @given(url=urls(), path=temp_paths(), logger=text_ascii(min_size=1) | none())
121
+ @given(
122
+ url=urls(all_=True), path=temp_paths(), logger=text_ascii(min_size=1) | none()
123
+ )
141
124
  async def test_main(self, *, url: URL, path: Path, logger: str | None) -> None:
142
125
  _ = await restore(url, path, dry_run=True, logger=logger)
143
126
 
144
127
  @given(
145
- url=urls(),
128
+ url=urls(all_=True),
146
129
  path=temp_paths(),
147
130
  psql=booleans(),
148
- database=text_ascii(min_size=1) | none(),
149
131
  create=booleans(),
150
132
  jobs=integers(min_value=0) | none(),
151
133
  schema=lists(text_ascii(min_size=1)) | none(),
@@ -160,7 +142,6 @@ class TestRestore:
160
142
  url: URL,
161
143
  path: Path,
162
144
  psql: bool,
163
- database: str | None,
164
145
  create: bool,
165
146
  jobs: int | None,
166
147
  schema: list[str] | None,
@@ -173,7 +154,6 @@ class TestRestore:
173
154
  url,
174
155
  path,
175
156
  psql=psql,
176
- database=database,
177
157
  create=create,
178
158
  jobs=jobs,
179
159
  schema=schema,
@@ -182,27 +162,3 @@ class TestRestore:
182
162
  role=role,
183
163
  docker=docker,
184
164
  )
185
-
186
-
187
- class TestExtractURL:
188
- def test_database(self) -> None:
189
- url = URL.create("postgres")
190
- with raises(
191
- _ExtractURLDatabaseError,
192
- match="Expected URL to contain a 'database'; got .*",
193
- ):
194
- _ = _extract_url(url)
195
-
196
- def test_host(self) -> None:
197
- url = URL.create("postgres", database="database")
198
- with raises(
199
- _ExtractURLHostError, match="Expected URL to contain a 'host'; got .*"
200
- ):
201
- _ = _extract_url(url)
202
-
203
- def test_port(self) -> None:
204
- url = URL.create("postgres", database="database", host="host")
205
- with raises(
206
- _ExtractURLPortError, match="Expected URL to contain a 'port'; got .*"
207
- ):
208
- _ = _extract_url(url)
@@ -9,6 +9,7 @@ from hypothesis import HealthCheck, Phase, assume, given, settings
9
9
  from hypothesis.strategies import SearchStrategy, booleans, lists, none, sets, tuples
10
10
  from pytest import mark, param, raises
11
11
  from sqlalchemy import (
12
+ URL,
12
13
  Boolean,
13
14
  Column,
14
15
  Engine,
@@ -28,7 +29,7 @@ from sqlalchemy.orm import (
28
29
  relationship,
29
30
  )
30
31
 
31
- from utilities.hypothesis import int32s, pairs
32
+ from utilities.hypothesis import int32s, pairs, urls
32
33
  from utilities.iterables import one
33
34
  from utilities.modules import is_installed
34
35
  from utilities.sqlalchemy import (
@@ -39,6 +40,11 @@ from utilities.sqlalchemy import (
39
40
  TablenameMixin,
40
41
  TableOrORMInstOrClass,
41
42
  UpsertItemsError,
43
+ _ExtractURLDatabaseError,
44
+ _ExtractURLHostError,
45
+ _ExtractURLPasswordError,
46
+ _ExtractURLPortError,
47
+ _ExtractURLUsernameError,
42
48
  _get_dialect,
43
49
  _get_dialect_max_params,
44
50
  _InsertItem,
@@ -69,6 +75,7 @@ from utilities.sqlalchemy import (
69
75
  ensure_tables_dropped,
70
76
  enum_name,
71
77
  enum_values,
78
+ extract_url,
72
79
  get_chunk_size,
73
80
  get_column_names,
74
81
  get_columns,
@@ -313,6 +320,53 @@ class TestEnumValues:
313
320
  assert result == expected
314
321
 
315
322
 
323
+ class TestExtractURL:
324
+ @given(url=urls(all_=True))
325
+ def test_main(self, *, url: URL) -> None:
326
+ extracted = extract_url(url)
327
+ assert extracted.username == url.username
328
+ assert extracted.password == url.password
329
+ assert extracted.host == url.host
330
+ assert extracted.port == url.port
331
+ assert extracted.database == url.database
332
+
333
+ @given(url=urls(username=False))
334
+ def test_username(self, *, url: URL) -> None:
335
+ with raises(
336
+ _ExtractURLUsernameError,
337
+ match="Expected URL to contain a user name; got .*",
338
+ ):
339
+ _ = extract_url(url)
340
+
341
+ @given(url=urls(username=True, password=False))
342
+ def test_password(self, *, url: URL) -> None:
343
+ with raises(
344
+ _ExtractURLPasswordError, match="Expected URL to contain a password; got .*"
345
+ ):
346
+ _ = extract_url(url)
347
+
348
+ @given(url=urls(username=True, password=True, host=False))
349
+ def test_host(self, *, url: URL) -> None:
350
+ with raises(
351
+ _ExtractURLHostError, match="Expected URL to contain a host; got .*"
352
+ ):
353
+ _ = extract_url(url)
354
+
355
+ @given(url=urls(username=True, password=True, host=True, port=False))
356
+ def test_port(self, *, url: URL) -> None:
357
+ with raises(
358
+ _ExtractURLPortError, match="Expected URL to contain a port; got .*"
359
+ ):
360
+ _ = extract_url(url)
361
+
362
+ @given(url=urls(username=True, password=True, host=True, port=True, database=False))
363
+ def test_database(self, *, url: URL) -> None:
364
+ with raises(
365
+ _ExtractURLDatabaseError, match="Expected URL to contain a database; got .*"
366
+ ):
367
+ _ = extract_url(url)
368
+
369
+
316
370
  class TestGetChunkSize:
317
371
  @mark.parametrize(
318
372
  ("num_cols", "chunk_size_frac", "expected"),
@@ -31,6 +31,7 @@ from utilities.text import (
31
31
  parse_bool,
32
32
  parse_none,
33
33
  repr_encode,
34
+ secret_str,
34
35
  snake_case,
35
36
  split_key_value_pairs,
36
37
  split_str,
@@ -105,6 +106,64 @@ class TestReprEncode:
105
106
  assert result == expected
106
107
 
107
108
 
109
+ class TestSecretStr:
110
+ def test_main(self) -> None:
111
+ s = secret_str("text")
112
+ assert repr(s) == secret_str._REPR
113
+ assert str(s) == secret_str._REPR
114
+
115
+
116
+ class TestSnakeCase:
117
+ @given(
118
+ case=sampled_from([
119
+ ("API", "api"),
120
+ ("APIResponse", "api_response"),
121
+ ("ApplicationController", "application_controller"),
122
+ ("Area51Controller", "area51_controller"),
123
+ ("FreeBSD", "free_bsd"),
124
+ ("HTML", "html"),
125
+ ("HTMLTidy", "html_tidy"),
126
+ ("HTMLTidyGenerator", "html_tidy_generator"),
127
+ ("HTMLVersion", "html_version"),
128
+ ("NoHTML", "no_html"),
129
+ ("One Two", "one_two"),
130
+ ("One Two", "one_two"),
131
+ ("One Two", "one_two"),
132
+ ("OneTwo", "one_two"),
133
+ ("One_Two", "one_two"),
134
+ ("One__Two", "one_two"),
135
+ ("One___Two", "one_two"),
136
+ ("Product", "product"),
137
+ ("SpecialGuest", "special_guest"),
138
+ ("Text", "text"),
139
+ ("Text123", "text123"),
140
+ ("_APIResponse_", "_api_response_"),
141
+ ("_API_", "_api_"),
142
+ ("__APIResponse__", "_api_response_"),
143
+ ("__API__", "_api_"),
144
+ ("__impliedVolatility_", "_implied_volatility_"),
145
+ ("_itemID", "_item_id"),
146
+ ("_lastPrice__", "_last_price_"),
147
+ ("_symbol", "_symbol"),
148
+ ("aB", "a_b"),
149
+ ("changePct", "change_pct"),
150
+ ("changePct_", "change_pct_"),
151
+ ("impliedVolatility", "implied_volatility"),
152
+ ("lastPrice", "last_price"),
153
+ ("memMB", "mem_mb"),
154
+ ("sizeX", "size_x"),
155
+ ("symbol", "symbol"),
156
+ ("testNTest", "test_n_test"),
157
+ ("text", "text"),
158
+ ("text123", "text123"),
159
+ ])
160
+ )
161
+ def test_main(self, *, case: tuple[str, str]) -> None:
162
+ text, expected = case
163
+ result = snake_case(text)
164
+ assert result == expected
165
+
166
+
108
167
  class TestSplitKeyValuePairs:
109
168
  @given(
110
169
  case=sampled_from([
@@ -237,57 +296,6 @@ class TestSplitAndJoinStr:
237
296
  _ = split_str("1,(22,333", brackets=[("(", ")")])
238
297
 
239
298
 
240
- class TestSnakeCase:
241
- @given(
242
- case=sampled_from([
243
- ("API", "api"),
244
- ("APIResponse", "api_response"),
245
- ("ApplicationController", "application_controller"),
246
- ("Area51Controller", "area51_controller"),
247
- ("FreeBSD", "free_bsd"),
248
- ("HTML", "html"),
249
- ("HTMLTidy", "html_tidy"),
250
- ("HTMLTidyGenerator", "html_tidy_generator"),
251
- ("HTMLVersion", "html_version"),
252
- ("NoHTML", "no_html"),
253
- ("One Two", "one_two"),
254
- ("One Two", "one_two"),
255
- ("One Two", "one_two"),
256
- ("OneTwo", "one_two"),
257
- ("One_Two", "one_two"),
258
- ("One__Two", "one_two"),
259
- ("One___Two", "one_two"),
260
- ("Product", "product"),
261
- ("SpecialGuest", "special_guest"),
262
- ("Text", "text"),
263
- ("Text123", "text123"),
264
- ("_APIResponse_", "_api_response_"),
265
- ("_API_", "_api_"),
266
- ("__APIResponse__", "_api_response_"),
267
- ("__API__", "_api_"),
268
- ("__impliedVolatility_", "_implied_volatility_"),
269
- ("_itemID", "_item_id"),
270
- ("_lastPrice__", "_last_price_"),
271
- ("_symbol", "_symbol"),
272
- ("aB", "a_b"),
273
- ("changePct", "change_pct"),
274
- ("changePct_", "change_pct_"),
275
- ("impliedVolatility", "implied_volatility"),
276
- ("lastPrice", "last_price"),
277
- ("memMB", "mem_mb"),
278
- ("sizeX", "size_x"),
279
- ("symbol", "symbol"),
280
- ("testNTest", "test_n_test"),
281
- ("text", "text"),
282
- ("text123", "text123"),
283
- ])
284
- )
285
- def test_main(self, *, case: tuple[str, str]) -> None:
286
- text, expected = case
287
- result = snake_case(text)
288
- assert result == expected
289
-
290
-
291
299
  class TestStrEncode:
292
300
  @given(n=integers())
293
301
  def test_main(self, *, n: int) -> None:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.150.7"
3
+ __version__ = "0.150.9"
@@ -98,6 +98,7 @@ if TYPE_CHECKING:
98
98
  from hypothesis.database import ExampleDatabase
99
99
  from libcst import Import, ImportFrom
100
100
  from numpy.random import RandomState
101
+ from sqlalchemy import URL
101
102
 
102
103
  from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
103
104
  from utilities.types import Number, TimeZoneLike
@@ -1258,6 +1259,41 @@ def uint64s(
1258
1259
  ##
1259
1260
 
1260
1261
 
1262
+ @composite
1263
+ def urls(
1264
+ draw: DrawFn,
1265
+ /,
1266
+ *,
1267
+ all_: MaybeSearchStrategy[bool] = False,
1268
+ username: MaybeSearchStrategy[bool] = False,
1269
+ password: MaybeSearchStrategy[bool] = False,
1270
+ host: MaybeSearchStrategy[bool] = False,
1271
+ port: MaybeSearchStrategy[bool] = False,
1272
+ database: MaybeSearchStrategy[bool] = False,
1273
+ ) -> URL:
1274
+ from sqlalchemy import URL
1275
+
1276
+ have_all, have_username, have_password, have_host, have_port, have_database = [
1277
+ draw2(draw, b) for b in [all_, username, password, host, port, database]
1278
+ ]
1279
+ username_use = draw(text_ascii(min_size=1)) if have_all or have_username else None
1280
+ password_use = draw(text_ascii(min_size=1)) if have_all or have_password else None
1281
+ host_use = draw(text_ascii(min_size=1)) if have_all or have_host else None
1282
+ port_use = draw(integers(min_value=1)) if have_all or have_port else None
1283
+ database_use = draw(text_ascii(min_size=1)) if have_all or have_database else None
1284
+ return URL.create(
1285
+ drivername="sqlite",
1286
+ username=username_use,
1287
+ password=password_use,
1288
+ host=host_use,
1289
+ port=port_use,
1290
+ database=database_use,
1291
+ )
1292
+
1293
+
1294
+ ##
1295
+
1296
+
1261
1297
  @composite
1262
1298
  def versions(draw: DrawFn, /, *, suffix: MaybeSearchStrategy[bool] = False) -> Version:
1263
1299
  """Strategy for generating versions."""
@@ -1392,6 +1428,7 @@ __all__ = [
1392
1428
  "triples",
1393
1429
  "uint32s",
1394
1430
  "uint64s",
1431
+ "urls",
1395
1432
  "versions",
1396
1433
  "year_months",
1397
1434
  "zoned_datetimes",
@@ -13,7 +13,7 @@ from utilities.iterables import always_iterable
13
13
  from utilities.logging import get_logger
14
14
  from utilities.os import temp_environ
15
15
  from utilities.pathlib import ensure_suffix
16
- from utilities.sqlalchemy import get_table_name
16
+ from utilities.sqlalchemy import extract_url, get_table_name
17
17
  from utilities.timer import Timer
18
18
  from utilities.types import PathLike
19
19
 
@@ -124,12 +124,11 @@ def _build_pg_dump(
124
124
  role: str | None = None,
125
125
  docker: str | None = None,
126
126
  ) -> str:
127
- database, host, port = _extract_url(url)
127
+ extracted = extract_url(url)
128
128
  path = _path_pg_dump(path, format_=format_)
129
129
  parts: list[str] = [
130
130
  "pg_dump",
131
131
  # general options
132
- f"--dbname={database}",
133
132
  f"--file={str(path)!r}",
134
133
  f"--format={format_}",
135
134
  "--verbose",
@@ -139,8 +138,10 @@ def _build_pg_dump(
139
138
  "--no-owner",
140
139
  "--no-privileges",
141
140
  # connection options
142
- f"--host={host}",
143
- f"--port={port}",
141
+ f"--dbname={extracted.database}",
142
+ f"--host={extracted.host}",
143
+ f"--port={extracted.port}",
144
+ f"--username={extracted.username}",
144
145
  "--no-password",
145
146
  ]
146
147
  if (format_ == "directory") and (jobs is not None):
@@ -167,8 +168,6 @@ def _build_pg_dump(
167
168
  parts.append("--inserts")
168
169
  if on_conflict_do_nothing:
169
170
  parts.append("--on-conflict-do-nothing")
170
- if url.username is not None:
171
- parts.append(f"--username={url.username}")
172
171
  if role is not None:
173
172
  parts.append(f"--role={role}")
174
173
  if docker is not None:
@@ -203,7 +202,6 @@ async def restore(
203
202
  /,
204
203
  *,
205
204
  psql: bool = False,
206
- database: str | None = None,
207
205
  data_only: bool = False,
208
206
  clean: bool = False,
209
207
  create: bool = False,
@@ -221,7 +219,6 @@ async def restore(
221
219
  url,
222
220
  path,
223
221
  psql=psql,
224
- database=database,
225
222
  data_only=data_only,
226
223
  clean=clean,
227
224
  create=create,
@@ -270,7 +267,6 @@ def _build_pg_restore_or_psql(
270
267
  /,
271
268
  *,
272
269
  psql: bool = False,
273
- database: str | None = None,
274
270
  data_only: bool = False,
275
271
  clean: bool = False,
276
272
  create: bool = False,
@@ -283,11 +279,10 @@ def _build_pg_restore_or_psql(
283
279
  ) -> str:
284
280
  path = Path(path)
285
281
  if (path.suffix == ".sql") or psql:
286
- return _build_psql(url, path, database=database, docker=docker)
282
+ return _build_psql(url, path, docker=docker)
287
283
  return _build_pg_restore(
288
284
  url,
289
285
  path,
290
- database=database,
291
286
  data_only=data_only,
292
287
  clean=clean,
293
288
  create=create,
@@ -305,7 +300,6 @@ def _build_pg_restore(
305
300
  path: PathLike,
306
301
  /,
307
302
  *,
308
- database: str | None = None,
309
303
  data_only: bool = False,
310
304
  clean: bool = False,
311
305
  create: bool = False,
@@ -317,12 +311,10 @@ def _build_pg_restore(
317
311
  docker: str | None = None,
318
312
  ) -> str:
319
313
  """Run `pg_restore`."""
320
- url_database, host, port = _extract_url(url)
321
- database_use = url_database if database is None else database
314
+ extracted = extract_url(url)
322
315
  parts: list[str] = [
323
316
  "pg_restore",
324
317
  # general options
325
- f"--dbname={database_use}",
326
318
  "--verbose",
327
319
  # restore options
328
320
  *_resolve_data_only_and_clean(data_only=data_only, clean=clean),
@@ -330,8 +322,10 @@ def _build_pg_restore(
330
322
  "--no-owner",
331
323
  "--no-privileges",
332
324
  # connection options
333
- f"--host={host}",
334
- f"--port={port}",
325
+ f"--host={extracted.host}",
326
+ f"--port={extracted.port}",
327
+ f"--username={extracted.username}",
328
+ f"--dbname={extracted.database}",
335
329
  "--no-password",
336
330
  ]
337
331
  if create:
@@ -344,8 +338,6 @@ def _build_pg_restore(
344
338
  parts.extend([f"--exclude-schema={s}" for s in always_iterable(schemas_exc)])
345
339
  if tables is not None:
346
340
  parts.extend([f"--table={_get_table_name(t)}" for t in always_iterable(tables)])
347
- if url.username is not None:
348
- parts.append(f"--username={url.username}")
349
341
  if role is not None:
350
342
  parts.append(f"--role={role}")
351
343
  if docker is not None:
@@ -354,29 +346,20 @@ def _build_pg_restore(
354
346
  return " ".join(parts)
355
347
 
356
348
 
357
- def _build_psql(
358
- url: URL,
359
- path: PathLike,
360
- /,
361
- *,
362
- database: str | None = None,
363
- docker: str | None = None,
364
- ) -> str:
349
+ def _build_psql(url: URL, path: PathLike, /, *, docker: str | None = None) -> str:
365
350
  """Run `psql`."""
366
- url_database, host, port = _extract_url(url)
367
- database_use = url_database if database is None else database
351
+ extracted = extract_url(url)
368
352
  parts: list[str] = [
369
353
  "psql",
370
354
  # general options
371
- f"--dbname={database_use}",
355
+ f"--dbname={extracted.database}",
372
356
  f"--file={str(path)!r}",
373
357
  # connection options
374
- f"--host={host}",
375
- f"--port={port}",
358
+ f"--host={extracted.host}",
359
+ f"--port={extracted.port}",
360
+ f"--username={extracted.username}",
376
361
  "--no-password",
377
362
  ]
378
- if url.username is not None:
379
- parts.append(f"--username={url.username}")
380
363
  if docker is not None:
381
364
  parts = _wrap_docker(parts, docker)
382
365
  return " ".join(parts)
@@ -385,42 +368,6 @@ def _build_psql(
385
368
  ##
386
369
 
387
370
 
388
- def _extract_url(url: URL, /) -> tuple[str, str, int]:
389
- if url.database is None:
390
- raise _ExtractURLDatabaseError(url=url)
391
- if url.host is None:
392
- raise _ExtractURLHostError(url=url)
393
- if url.port is None:
394
- raise _ExtractURLPortError(url=url)
395
- return url.database, url.host, url.port
396
-
397
-
398
- @dataclass(kw_only=True, slots=True)
399
- class ExtractURLError(Exception):
400
- url: URL
401
-
402
-
403
- @dataclass(kw_only=True, slots=True)
404
- class _ExtractURLDatabaseError(ExtractURLError):
405
- @override
406
- def __str__(self) -> str:
407
- return f"Expected URL to contain a 'database'; got {self.url}"
408
-
409
-
410
- @dataclass(kw_only=True, slots=True)
411
- class _ExtractURLHostError(ExtractURLError):
412
- @override
413
- def __str__(self) -> str:
414
- return f"Expected URL to contain a 'host'; got {self.url}"
415
-
416
-
417
- @dataclass(kw_only=True, slots=True)
418
- class _ExtractURLPortError(ExtractURLError):
419
- @override
420
- def __str__(self) -> str:
421
- return f"Expected URL to contain a 'port'; got {self.url}"
422
-
423
-
424
371
  def _get_table_name(obj: TableOrORMInstOrClass | str, /) -> str:
425
372
  match obj:
426
373
  case Table() | DeclarativeBase() | type() as table_or_orm:
@@ -458,4 +405,4 @@ def _wrap_docker(parts: list[str], container: str, /) -> list[str]:
458
405
  return ["docker", "exec", "-it", container, *parts]
459
406
 
460
407
 
461
- __all__ = ["ExtractURLError", "pg_dump", "restore"]
408
+ __all__ = ["pg_dump", "restore"]