dycw-utilities 0.150.0__py3-none-any.whl → 0.150.2__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.150.0
3
+ Version: 0.150.2
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=-FKXlj49n14usgRRgsXp-6U1_cXzP-blAYQWGuSGKL8,60
1
+ utilities/__init__.py,sha256=bBUlYYM1VrEWyFEkC5ZlwXnej-DEWPaWXPM1YmuRbVk,60
2
2
  utilities/altair.py,sha256=92E2lCdyHY4Zb-vCw6rEJIsWdKipuu-Tu2ab1ufUfAk,9079
3
3
  utilities/asyncio.py,sha256=2m2a2C-Qgc6OHTTHL332-t66A7xDITt_SORT7a1DJWo,16792
4
4
  utilities/atomicwrites.py,sha256=xcOWenTBRS0oat3kg7Sqe51AohNThMQ2ixPL7QCG8hw,5795
@@ -49,7 +49,7 @@ utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
49
49
  utilities/platform.py,sha256=Ue9LSxYvg9yUXGKuz5aZoy_qkUEXde-v6B09exgSctU,2813
50
50
  utilities/polars.py,sha256=BgiDryAVOapi41ddfJqN0wYh_sDj8BNEYtPB36LaHdo,71824
51
51
  utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
52
- utilities/postgres.py,sha256=C3TxpQymM7S5ZivxIN_AFTM5g5TtENptzx8-XYf7pow,12182
52
+ utilities/postgres.py,sha256=_LJDtzw50gHaPyW8lu_Z9zBh7eXK7A10yx-hJZ0f4nc,13084
53
53
  utilities/pottery.py,sha256=u0uvyGgYyujxftEMlsv6ppYTKQoVVjHt5jnVxxYz9s4,6596
54
54
  utilities/pqdm.py,sha256=BTsYPtbKQWwX-iXF4qCkfPG7DPxIB54J989n83bXrIo,3092
55
55
  utilities/psutil.py,sha256=KUlu4lrUw9Zg1V7ZGetpWpGb9DB8l_SSDWGbANFNCPU,2104
@@ -66,7 +66,7 @@ utilities/sentinel.py,sha256=3jIwgpMekWgDAxPDA_hXMP2St43cPhciKN3LWiZ7kv0,1248
66
66
  utilities/shelve.py,sha256=4OzjQI6kGuUbJciqf535rwnao-_IBv66gsT6tRGiUt0,759
67
67
  utilities/slack_sdk.py,sha256=ppFBvKgfg5IRWiIoKPtpTyzBtBF4XmwEvU3I5wLJikM,2140
68
68
  utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
69
- utilities/sqlalchemy.py,sha256=M__UwdIiXDc6W8oRHQ6u3h_zEb9uTKwfl506KJCrOl4,37231
69
+ utilities/sqlalchemy.py,sha256=5TPqPvJUCuxIeiMD7IKTptYg11UXNy7ThGw81reCIRU,37290
70
70
  utilities/sqlalchemy_polars.py,sha256=18AoEbeNJUKF3-5hroNy9J5LQwS_QJAXbMfKc9sChtk,14250
71
71
  utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
72
72
  utilities/string.py,sha256=MB0X6UPTUc06JdAdj-PctZ238IXeCjE5dAJibNw6ZrU,587
@@ -89,8 +89,8 @@ utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
89
89
  utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
90
90
  utilities/pytest_plugins/pytest_randomly.py,sha256=NXzCcGKbpgYouz5yehKb4jmxmi2SexKKpgF4M65bi10,414
91
91
  utilities/pytest_plugins/pytest_regressions.py,sha256=Iwhfv_OJH7UCPZCfoh7ugZ2Xjqjil-BBBsOb8sDwiGI,1471
92
- dycw_utilities-0.150.0.dist-info/METADATA,sha256=F2t8-Dhx3lTo-_ns45mIJ0vtLW9KTBUHrViS1bR94v0,1697
93
- dycw_utilities-0.150.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
- dycw_utilities-0.150.0.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
95
- dycw_utilities-0.150.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
- dycw_utilities-0.150.0.dist-info/RECORD,,
92
+ dycw_utilities-0.150.2.dist-info/METADATA,sha256=Hp0ONkfWYpv5XkLgy5V-bKY3vVQ1kgMD0FwkS7xsCok,1697
93
+ dycw_utilities-0.150.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
+ dycw_utilities-0.150.2.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
95
+ dycw_utilities-0.150.2.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
+ dycw_utilities-0.150.2.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.150.0"
3
+ __version__ = "0.150.2"
utilities/postgres.py CHANGED
@@ -42,6 +42,7 @@ async def pg_dump(
42
42
  table_exc: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
43
43
  inserts: bool = False,
44
44
  on_conflict_do_nothing: bool = False,
45
+ role: str | None = None,
45
46
  docker: str | None = None,
46
47
  dry_run: bool = False,
47
48
  logger: LoggerOrName | None = None,
@@ -62,6 +63,7 @@ async def pg_dump(
62
63
  table_exc=table_exc,
63
64
  inserts=inserts,
64
65
  on_conflict_do_nothing=on_conflict_do_nothing,
66
+ role=role,
65
67
  docker=docker,
66
68
  )
67
69
  if dry_run:
@@ -110,6 +112,7 @@ def _build_pg_dump(
110
112
  table_exc: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
111
113
  inserts: bool = False,
112
114
  on_conflict_do_nothing: bool = False,
115
+ role: str | None = None,
113
116
  docker: str | None = None,
114
117
  ) -> str:
115
118
  database, host, port = _extract_url(url)
@@ -122,6 +125,7 @@ def _build_pg_dump(
122
125
  f"--format={format_}",
123
126
  "--verbose",
124
127
  # output options
128
+ *_resolve_data_only_and_clean(data_only=data_only, clean=clean),
125
129
  "--large-objects",
126
130
  "--create",
127
131
  "--no-owner",
@@ -133,10 +137,6 @@ def _build_pg_dump(
133
137
  ]
134
138
  if (format_ == "directory") and (jobs is not None):
135
139
  parts.append(f"--jobs={jobs}")
136
- if data_only:
137
- parts.append("--data-only")
138
- if clean:
139
- parts.extend(["--clean", "--if-exists"])
140
140
  if schema is not None:
141
141
  parts.extend([f"--schema={s}" for s in always_iterable(schema)])
142
142
  if schema_exc is not None:
@@ -153,6 +153,8 @@ def _build_pg_dump(
153
153
  parts.append("--on-conflict-do-nothing")
154
154
  if url.username is not None:
155
155
  parts.append(f"--username={url.username}")
156
+ if role is not None:
157
+ parts.append(f"--role={role}")
156
158
  if docker is not None:
157
159
  parts = _wrap_docker(parts, docker)
158
160
  return " ".join(parts)
@@ -192,6 +194,7 @@ async def restore(
192
194
  schema: MaybeListStr | None = None,
193
195
  schema_exc: MaybeListStr | None = None,
194
196
  table: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
197
+ role: str | None = None,
195
198
  docker: str | None = None,
196
199
  dry_run: bool = False,
197
200
  logger: LoggerOrName | None = None,
@@ -208,6 +211,7 @@ async def restore(
208
211
  schema=schema,
209
212
  schema_exc=schema_exc,
210
213
  table=table,
214
+ role=role,
211
215
  docker=docker,
212
216
  )
213
217
  if dry_run:
@@ -255,6 +259,7 @@ def _build_pg_restore_or_psql(
255
259
  schema: MaybeListStr | None = None,
256
260
  schema_exc: MaybeListStr | None = None,
257
261
  table: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
262
+ role: str | None = None,
258
263
  docker: str | None = None,
259
264
  ) -> str:
260
265
  path = Path(path)
@@ -270,6 +275,7 @@ def _build_pg_restore_or_psql(
270
275
  schemas=schema,
271
276
  schemas_exc=schema_exc,
272
277
  tables=table,
278
+ role=role,
273
279
  docker=docker,
274
280
  )
275
281
 
@@ -286,6 +292,7 @@ def _build_pg_restore(
286
292
  schemas: MaybeListStr | None = None,
287
293
  schemas_exc: MaybeListStr | None = None,
288
294
  tables: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
295
+ role: str | None = None,
289
296
  docker: str | None = None,
290
297
  ) -> str:
291
298
  """Run `pg_restore`."""
@@ -297,6 +304,8 @@ def _build_pg_restore(
297
304
  f"--dbname={database_use}",
298
305
  "--verbose",
299
306
  # restore options
307
+ *_resolve_data_only_and_clean(data_only=data_only, clean=clean),
308
+ "--create",
300
309
  "--exit-on-error",
301
310
  "--no-owner",
302
311
  "--no-privileges",
@@ -305,10 +314,6 @@ def _build_pg_restore(
305
314
  f"--port={port}",
306
315
  "--no-password",
307
316
  ]
308
- if data_only:
309
- parts.append("--data-only")
310
- if clean:
311
- parts.extend(["--clean", "--if-exists"])
312
317
  if jobs is not None:
313
318
  parts.append(f"--jobs={jobs}")
314
319
  if schemas is not None:
@@ -319,6 +324,8 @@ def _build_pg_restore(
319
324
  parts.extend([f"--table={_get_table_name(t)}" for t in always_iterable(tables)])
320
325
  if url.username is not None:
321
326
  parts.append(f"--username={url.username}")
327
+ if role is not None:
328
+ parts.append(f"--role={role}")
322
329
  if docker is not None:
323
330
  parts = _wrap_docker(parts, docker)
324
331
  parts.append(str(path))
@@ -402,6 +409,29 @@ def _get_table_name(obj: TableOrORMInstOrClass | str, /) -> str:
402
409
  assert_never(never)
403
410
 
404
411
 
412
+ def _resolve_data_only_and_clean(
413
+ *, data_only: bool = False, clean: bool = False
414
+ ) -> list[str]:
415
+ match data_only, clean:
416
+ case False, False:
417
+ return []
418
+ case True, False:
419
+ return ["--data-only"]
420
+ case False, True:
421
+ return ["--clean", "--if-exists"]
422
+ case True, True:
423
+ raise _ResolveDataOnlyAndCleanError
424
+ case _ as never:
425
+ assert_never(never)
426
+
427
+
428
+ @dataclass(kw_only=True, slots=True)
429
+ class _ResolveDataOnlyAndCleanError(Exception):
430
+ @override
431
+ def __str__(self) -> str:
432
+ return "Cannot use '--data-only' and '--clean' together"
433
+
434
+
405
435
  def _wrap_docker(parts: list[str], container: str, /) -> list[str]:
406
436
  return ["docker", "exec", "-it", container, *parts]
407
437
 
utilities/sqlalchemy.py CHANGED
@@ -57,7 +57,12 @@ from sqlalchemy.dialects.postgresql.psycopg import PGDialect_psycopg
57
57
  from sqlalchemy.dialects.sqlite import Insert as sqlite_Insert
58
58
  from sqlalchemy.dialects.sqlite import dialect as sqlite_dialect
59
59
  from sqlalchemy.dialects.sqlite import insert as sqlite_insert
60
- from sqlalchemy.exc import ArgumentError, DatabaseError, OperationalError
60
+ from sqlalchemy.exc import (
61
+ ArgumentError,
62
+ DatabaseError,
63
+ OperationalError,
64
+ ProgrammingError,
65
+ )
61
66
  from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine
62
67
  from sqlalchemy.orm import (
63
68
  DeclarativeBase,
@@ -115,7 +120,7 @@ def check_connect(engine: Engine, /) -> bool:
115
120
  try:
116
121
  with engine.connect() as conn:
117
122
  return bool(conn.execute(text("SELECT 1")).scalar_one())
118
- except OperationalError:
123
+ except (OperationalError, ProgrammingError):
119
124
  return False
120
125
 
121
126