dycw-utilities 0.150.7__py3-none-any.whl → 0.150.9__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.
- {dycw_utilities-0.150.7.dist-info → dycw_utilities-0.150.9.dist-info}/METADATA +1 -1
- {dycw_utilities-0.150.7.dist-info → dycw_utilities-0.150.9.dist-info}/RECORD +10 -10
- utilities/__init__.py +1 -1
- utilities/hypothesis.py +37 -0
- utilities/postgres.py +19 -72
- utilities/sqlalchemy.py +77 -1
- utilities/text.py +20 -1
- {dycw_utilities-0.150.7.dist-info → dycw_utilities-0.150.9.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.150.7.dist-info → dycw_utilities-0.150.9.dist-info}/entry_points.txt +0 -0
- {dycw_utilities-0.150.7.dist-info → dycw_utilities-0.150.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=GsG3Q0h41dPHASCsd70D14gPFgnAp7k4Bz18oGu00SI,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
|
@@ -23,7 +23,7 @@ utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
|
23
23
|
utilities/gzip.py,sha256=fkGP3KdsBfXlstodT4wtlp-PwNyUsogpbDCVVVGdsm4,781
|
24
24
|
utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
|
25
25
|
utilities/http.py,sha256=TsavEfHlRtlLaeV21Z6KZh0qbPw-kvD1zsQdZ7Kep5Q,977
|
26
|
-
utilities/hypothesis.py,sha256=
|
26
|
+
utilities/hypothesis.py,sha256=_DKCFAHgE4NRE1asv-asYVvs5cqqivxTNrKHPeq2RjE,39282
|
27
27
|
utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
|
28
28
|
utilities/inflect.py,sha256=UWOsMRJUJXqcR3rgrQmb35mat-Theu5xo-M9E33eTIE,586
|
29
29
|
utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
|
@@ -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=
|
52
|
+
utilities/postgres.py,sha256=48km6Mb6hRUb1-0FWg7FKEfrW75z8-7AY1jahtdu_KY,12406
|
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,12 +66,12 @@ 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=
|
69
|
+
utilities/sqlalchemy.py,sha256=u2ZVvr_4Yrqsur6FlLHUuW0mPaoNrHvaJ2wj7o_WG3A,39264
|
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
|
73
73
|
utilities/tempfile.py,sha256=HxB2BF28CcecDJLQ3Bx2Ej-Pb6RJc6W9ngSpB9CnP4k,2018
|
74
|
-
utilities/text.py,sha256=
|
74
|
+
utilities/text.py,sha256=bupgC6ILTjmcJKSUGloStzmWuj2Ke0knvVKE2mWLwAM,11619
|
75
75
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
76
76
|
utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
|
77
77
|
utilities/traceback.py,sha256=zofhzIedpUHrzDNiRJDVzm_wuu_tlTQvVqK4quxVlgM,9151
|
@@ -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.
|
93
|
-
dycw_utilities-0.150.
|
94
|
-
dycw_utilities-0.150.
|
95
|
-
dycw_utilities-0.150.
|
96
|
-
dycw_utilities-0.150.
|
92
|
+
dycw_utilities-0.150.9.dist-info/METADATA,sha256=h1ki8nwAV6ET-w-Z9jFMsFrXnEezGVzts3XVeQ9kJps,1696
|
93
|
+
dycw_utilities-0.150.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
94
|
+
dycw_utilities-0.150.9.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
|
95
|
+
dycw_utilities-0.150.9.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
96
|
+
dycw_utilities-0.150.9.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/hypothesis.py
CHANGED
@@ -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",
|
utilities/postgres.py
CHANGED
@@ -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
|
-
|
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"--
|
143
|
-
f"--
|
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,
|
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
|
-
|
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
|
-
|
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={
|
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__ = ["
|
408
|
+
__all__ = ["pg_dump", "restore"]
|
utilities/sqlalchemy.py
CHANGED
@@ -96,7 +96,7 @@ from utilities.iterables import (
|
|
96
96
|
one,
|
97
97
|
)
|
98
98
|
from utilities.reprlib import get_repr
|
99
|
-
from utilities.text import snake_case
|
99
|
+
from utilities.text import secret_str, snake_case
|
100
100
|
from utilities.types import MaybeIterable, MaybeType, StrMapping, TupleOrStrMapping
|
101
101
|
|
102
102
|
if TYPE_CHECKING:
|
@@ -378,6 +378,79 @@ def enum_values(enum: type[StrEnum], /) -> list[str]:
|
|
378
378
|
##
|
379
379
|
|
380
380
|
|
381
|
+
@dataclass(kw_only=True, slots=True)
|
382
|
+
class ExtractURLOutput:
|
383
|
+
username: str
|
384
|
+
password: secret_str
|
385
|
+
host: str
|
386
|
+
port: int
|
387
|
+
database: str
|
388
|
+
|
389
|
+
|
390
|
+
def extract_url(url: URL, /) -> ExtractURLOutput:
|
391
|
+
"""Extract the database, host & port from a URL."""
|
392
|
+
if url.username is None:
|
393
|
+
raise _ExtractURLUsernameError(url=url)
|
394
|
+
if url.password is None:
|
395
|
+
raise _ExtractURLPasswordError(url=url)
|
396
|
+
if url.host is None:
|
397
|
+
raise _ExtractURLHostError(url=url)
|
398
|
+
if url.port is None:
|
399
|
+
raise _ExtractURLPortError(url=url)
|
400
|
+
if url.database is None:
|
401
|
+
raise _ExtractURLDatabaseError(url=url)
|
402
|
+
return ExtractURLOutput(
|
403
|
+
username=url.username,
|
404
|
+
password=secret_str(url.password),
|
405
|
+
host=url.host,
|
406
|
+
port=url.port,
|
407
|
+
database=url.database,
|
408
|
+
)
|
409
|
+
|
410
|
+
|
411
|
+
@dataclass(kw_only=True, slots=True)
|
412
|
+
class ExtractURLError(Exception):
|
413
|
+
url: URL
|
414
|
+
|
415
|
+
|
416
|
+
@dataclass(kw_only=True, slots=True)
|
417
|
+
class _ExtractURLUsernameError(ExtractURLError):
|
418
|
+
@override
|
419
|
+
def __str__(self) -> str:
|
420
|
+
return f"Expected URL to contain a user name; got {self.url}"
|
421
|
+
|
422
|
+
|
423
|
+
@dataclass(kw_only=True, slots=True)
|
424
|
+
class _ExtractURLPasswordError(ExtractURLError):
|
425
|
+
@override
|
426
|
+
def __str__(self) -> str:
|
427
|
+
return f"Expected URL to contain a password; got {self.url}"
|
428
|
+
|
429
|
+
|
430
|
+
@dataclass(kw_only=True, slots=True)
|
431
|
+
class _ExtractURLHostError(ExtractURLError):
|
432
|
+
@override
|
433
|
+
def __str__(self) -> str:
|
434
|
+
return f"Expected URL to contain a host; got {self.url}"
|
435
|
+
|
436
|
+
|
437
|
+
@dataclass(kw_only=True, slots=True)
|
438
|
+
class _ExtractURLPortError(ExtractURLError):
|
439
|
+
@override
|
440
|
+
def __str__(self) -> str:
|
441
|
+
return f"Expected URL to contain a port; got {self.url}"
|
442
|
+
|
443
|
+
|
444
|
+
@dataclass(kw_only=True, slots=True)
|
445
|
+
class _ExtractURLDatabaseError(ExtractURLError):
|
446
|
+
@override
|
447
|
+
def __str__(self) -> str:
|
448
|
+
return f"Expected URL to contain a database; got {self.url}"
|
449
|
+
|
450
|
+
|
451
|
+
##
|
452
|
+
|
453
|
+
|
381
454
|
def get_chunk_size(
|
382
455
|
dialect_or_engine_or_conn: DialectOrEngineOrConnectionOrAsync,
|
383
456
|
table_or_orm_or_num_cols: TableOrORMInstOrClass | Sized | int,
|
@@ -1180,6 +1253,8 @@ __all__ = [
|
|
1180
1253
|
"CheckEngineError",
|
1181
1254
|
"DialectOrEngineOrConnectionOrAsync",
|
1182
1255
|
"EngineOrConnectionOrAsync",
|
1256
|
+
"ExtractURLError",
|
1257
|
+
"ExtractURLOutput",
|
1183
1258
|
"GetTableError",
|
1184
1259
|
"InsertItemsError",
|
1185
1260
|
"TablenameMixin",
|
@@ -1193,6 +1268,7 @@ __all__ = [
|
|
1193
1268
|
"ensure_tables_dropped",
|
1194
1269
|
"enum_name",
|
1195
1270
|
"enum_values",
|
1271
|
+
"extract_url",
|
1196
1272
|
"get_chunk_size",
|
1197
1273
|
"get_column_names",
|
1198
1274
|
"get_columns",
|
utilities/text.py
CHANGED
@@ -9,7 +9,7 @@ from re import IGNORECASE, Match, escape, search
|
|
9
9
|
from textwrap import dedent
|
10
10
|
from threading import get_ident
|
11
11
|
from time import time_ns
|
12
|
-
from typing import TYPE_CHECKING, Any, Literal, overload, override
|
12
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Literal, overload, override
|
13
13
|
from uuid import uuid4
|
14
14
|
|
15
15
|
from utilities.iterables import CheckDuplicatesError, check_duplicates, transpose
|
@@ -385,6 +385,24 @@ def _escape_separator(*, separator: str = DEFAULT_SEPARATOR) -> str:
|
|
385
385
|
##
|
386
386
|
|
387
387
|
|
388
|
+
class secret_str(str): # noqa: N801
|
389
|
+
"""A string with an obfuscated representation."""
|
390
|
+
|
391
|
+
__slots__ = ()
|
392
|
+
_REPR: ClassVar[str] = "***"
|
393
|
+
|
394
|
+
@override
|
395
|
+
def __repr__(self) -> str:
|
396
|
+
return self._REPR
|
397
|
+
|
398
|
+
@override
|
399
|
+
def __str__(self) -> str:
|
400
|
+
return self._REPR
|
401
|
+
|
402
|
+
|
403
|
+
##
|
404
|
+
|
405
|
+
|
388
406
|
def str_encode(obj: Any, /) -> bytes:
|
389
407
|
"""Return the string representation of the object encoded as bytes."""
|
390
408
|
return str(obj).encode()
|
@@ -424,6 +442,7 @@ __all__ = [
|
|
424
442
|
"parse_bool",
|
425
443
|
"parse_none",
|
426
444
|
"repr_encode",
|
445
|
+
"secret_str",
|
427
446
|
"snake_case",
|
428
447
|
"split_key_value_pairs",
|
429
448
|
"split_str",
|
File without changes
|
File without changes
|
File without changes
|