dycw-utilities 0.135.0__py3-none-any.whl → 0.178.1__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.
Potentially problematic release.
This version of dycw-utilities might be problematic. Click here for more details.
- dycw_utilities-0.178.1.dist-info/METADATA +34 -0
- dycw_utilities-0.178.1.dist-info/RECORD +105 -0
- dycw_utilities-0.178.1.dist-info/WHEEL +4 -0
- dycw_utilities-0.178.1.dist-info/entry_points.txt +4 -0
- utilities/__init__.py +1 -1
- utilities/altair.py +13 -10
- utilities/asyncio.py +312 -787
- utilities/atomicwrites.py +18 -6
- utilities/atools.py +64 -4
- utilities/cachetools.py +9 -6
- utilities/click.py +195 -77
- utilities/concurrent.py +1 -1
- utilities/contextlib.py +216 -17
- utilities/contextvars.py +20 -1
- utilities/cryptography.py +3 -3
- utilities/dataclasses.py +15 -28
- utilities/docker.py +387 -0
- utilities/enum.py +2 -2
- utilities/errors.py +17 -3
- utilities/fastapi.py +28 -59
- utilities/fpdf2.py +2 -2
- utilities/functions.py +24 -269
- utilities/git.py +9 -30
- utilities/grp.py +28 -0
- utilities/gzip.py +31 -0
- utilities/http.py +3 -2
- utilities/hypothesis.py +513 -159
- utilities/importlib.py +17 -1
- utilities/inflect.py +12 -4
- utilities/iterables.py +33 -58
- utilities/jinja2.py +148 -0
- utilities/json.py +70 -0
- utilities/libcst.py +38 -17
- utilities/lightweight_charts.py +4 -7
- utilities/logging.py +136 -93
- utilities/math.py +8 -4
- utilities/more_itertools.py +43 -45
- utilities/operator.py +27 -27
- utilities/orjson.py +189 -36
- utilities/os.py +61 -4
- utilities/packaging.py +115 -0
- utilities/parse.py +8 -5
- utilities/pathlib.py +269 -40
- utilities/permissions.py +298 -0
- utilities/platform.py +7 -6
- utilities/polars.py +1205 -413
- utilities/polars_ols.py +1 -1
- utilities/postgres.py +408 -0
- utilities/pottery.py +43 -19
- utilities/pqdm.py +3 -3
- utilities/psutil.py +5 -57
- utilities/pwd.py +28 -0
- utilities/pydantic.py +4 -52
- utilities/pydantic_settings.py +240 -0
- utilities/pydantic_settings_sops.py +76 -0
- utilities/pyinstrument.py +7 -7
- utilities/pytest.py +104 -143
- utilities/pytest_plugins/__init__.py +1 -0
- utilities/pytest_plugins/pytest_randomly.py +23 -0
- utilities/pytest_plugins/pytest_regressions.py +56 -0
- utilities/pytest_regressions.py +26 -46
- utilities/random.py +11 -6
- utilities/re.py +1 -1
- utilities/redis.py +220 -343
- utilities/sentinel.py +10 -0
- utilities/shelve.py +4 -1
- utilities/shutil.py +25 -0
- utilities/slack_sdk.py +35 -104
- utilities/sqlalchemy.py +496 -471
- utilities/sqlalchemy_polars.py +29 -54
- utilities/string.py +2 -3
- utilities/subprocess.py +1977 -0
- utilities/tempfile.py +112 -4
- utilities/testbook.py +50 -0
- utilities/text.py +174 -42
- utilities/throttle.py +158 -0
- utilities/timer.py +2 -2
- utilities/traceback.py +70 -35
- utilities/types.py +102 -30
- utilities/typing.py +479 -19
- utilities/uuid.py +42 -5
- utilities/version.py +27 -26
- utilities/whenever.py +1559 -361
- utilities/zoneinfo.py +80 -22
- dycw_utilities-0.135.0.dist-info/METADATA +0 -39
- dycw_utilities-0.135.0.dist-info/RECORD +0 -96
- dycw_utilities-0.135.0.dist-info/WHEEL +0 -4
- dycw_utilities-0.135.0.dist-info/licenses/LICENSE +0 -21
- utilities/aiolimiter.py +0 -25
- utilities/arq.py +0 -216
- utilities/eventkit.py +0 -388
- utilities/luigi.py +0 -183
- utilities/period.py +0 -152
- utilities/pudb.py +0 -62
- utilities/python_dotenv.py +0 -101
- utilities/streamlit.py +0 -105
- utilities/typed_settings.py +0 -123
utilities/sqlalchemy_polars.py
CHANGED
|
@@ -4,7 +4,7 @@ import datetime as dt
|
|
|
4
4
|
import decimal
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from typing import TYPE_CHECKING, Any,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, cast, overload, override
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
10
|
import polars as pl
|
|
@@ -35,7 +35,7 @@ from utilities.iterables import (
|
|
|
35
35
|
chunked,
|
|
36
36
|
one,
|
|
37
37
|
)
|
|
38
|
-
from utilities.polars import
|
|
38
|
+
from utilities.polars import zoned_date_time_dtype
|
|
39
39
|
from utilities.reprlib import get_repr
|
|
40
40
|
from utilities.sqlalchemy import (
|
|
41
41
|
CHUNK_SIZE_FRAC,
|
|
@@ -44,7 +44,6 @@ from utilities.sqlalchemy import (
|
|
|
44
44
|
get_chunk_size,
|
|
45
45
|
get_columns,
|
|
46
46
|
insert_items,
|
|
47
|
-
upsert_items,
|
|
48
47
|
)
|
|
49
48
|
from utilities.text import snake_case
|
|
50
49
|
from utilities.typing import is_subclass_gen
|
|
@@ -65,7 +64,7 @@ if TYPE_CHECKING:
|
|
|
65
64
|
from sqlalchemy.sql.base import ReadOnlyColumnCollection
|
|
66
65
|
from whenever import TimeDelta
|
|
67
66
|
|
|
68
|
-
from utilities.types import MaybeType, TimeZoneLike
|
|
67
|
+
from utilities.types import Delta, MaybeType, TimeZoneLike
|
|
69
68
|
|
|
70
69
|
|
|
71
70
|
async def insert_dataframe(
|
|
@@ -75,9 +74,9 @@ async def insert_dataframe(
|
|
|
75
74
|
/,
|
|
76
75
|
*,
|
|
77
76
|
snake: bool = False,
|
|
77
|
+
is_upsert: bool = False,
|
|
78
78
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
79
79
|
assume_tables_exist: bool = False,
|
|
80
|
-
upsert: Literal["selected", "all"] | None = None,
|
|
81
80
|
timeout_create: TimeDelta | None = None,
|
|
82
81
|
error_create: type[Exception] = TimeoutError,
|
|
83
82
|
timeout_insert: TimeDelta | None = None,
|
|
@@ -87,43 +86,25 @@ async def insert_dataframe(
|
|
|
87
86
|
mapping = _insert_dataframe_map_df_schema_to_table(
|
|
88
87
|
df.schema, table_or_orm, snake=snake
|
|
89
88
|
)
|
|
90
|
-
items = df.select(mapping).rename(mapping).
|
|
89
|
+
items = df.select(*mapping).rename(mapping).rows(named=True)
|
|
91
90
|
if len(items) == 0:
|
|
92
|
-
if not df.is_empty():
|
|
93
|
-
raise InsertDataFrameError(df=df)
|
|
94
91
|
if not assume_tables_exist:
|
|
95
92
|
await ensure_tables_created(
|
|
96
93
|
engine, table_or_orm, timeout=timeout_create, error=error_create
|
|
97
94
|
)
|
|
98
95
|
return
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
case "selected" | "all" as selected_or_all: # skipif-ci-and-not-linux
|
|
113
|
-
await upsert_items(
|
|
114
|
-
engine,
|
|
115
|
-
(items, table_or_orm),
|
|
116
|
-
snake=snake,
|
|
117
|
-
chunk_size_frac=chunk_size_frac,
|
|
118
|
-
selected_or_all=selected_or_all,
|
|
119
|
-
assume_tables_exist=assume_tables_exist,
|
|
120
|
-
timeout_create=timeout_create,
|
|
121
|
-
error_create=error_create,
|
|
122
|
-
timeout_insert=timeout_insert,
|
|
123
|
-
error_insert=error_insert,
|
|
124
|
-
)
|
|
125
|
-
case _ as never:
|
|
126
|
-
assert_never(never)
|
|
96
|
+
await insert_items(
|
|
97
|
+
engine,
|
|
98
|
+
(items, table_or_orm),
|
|
99
|
+
snake=snake,
|
|
100
|
+
is_upsert=is_upsert,
|
|
101
|
+
chunk_size_frac=chunk_size_frac,
|
|
102
|
+
assume_tables_exist=assume_tables_exist,
|
|
103
|
+
timeout_create=timeout_create,
|
|
104
|
+
error_create=error_create,
|
|
105
|
+
timeout_insert=timeout_insert,
|
|
106
|
+
error_insert=error_insert,
|
|
107
|
+
)
|
|
127
108
|
|
|
128
109
|
|
|
129
110
|
def _insert_dataframe_map_df_schema_to_table(
|
|
@@ -207,15 +188,6 @@ def _insert_dataframe_check_df_and_db_types(
|
|
|
207
188
|
)
|
|
208
189
|
|
|
209
190
|
|
|
210
|
-
@dataclass(kw_only=True, slots=True)
|
|
211
|
-
class InsertDataFrameError(Exception):
|
|
212
|
-
df: DataFrame
|
|
213
|
-
|
|
214
|
-
@override
|
|
215
|
-
def __str__(self) -> str:
|
|
216
|
-
return f"Non-empty DataFrame must resolve to at least 1 item\n\n{self.df}"
|
|
217
|
-
|
|
218
|
-
|
|
219
191
|
@overload
|
|
220
192
|
async def select_to_dataframe(
|
|
221
193
|
sel: Select[Any],
|
|
@@ -228,7 +200,8 @@ async def select_to_dataframe(
|
|
|
228
200
|
in_clauses: tuple[Column[Any], Iterable[Any]] | None = None,
|
|
229
201
|
in_clauses_chunk_size: int | None = None,
|
|
230
202
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
231
|
-
timeout:
|
|
203
|
+
timeout: Delta | None = None,
|
|
204
|
+
error: MaybeType[BaseException] = TimeoutError,
|
|
232
205
|
**kwargs: Any,
|
|
233
206
|
) -> DataFrame: ...
|
|
234
207
|
@overload
|
|
@@ -243,7 +216,8 @@ async def select_to_dataframe(
|
|
|
243
216
|
in_clauses: None = None,
|
|
244
217
|
in_clauses_chunk_size: int | None = None,
|
|
245
218
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
246
|
-
timeout:
|
|
219
|
+
timeout: Delta | None = None,
|
|
220
|
+
error: MaybeType[BaseException] = TimeoutError,
|
|
247
221
|
**kwargs: Any,
|
|
248
222
|
) -> Iterable[DataFrame]: ...
|
|
249
223
|
@overload
|
|
@@ -258,7 +232,8 @@ async def select_to_dataframe(
|
|
|
258
232
|
in_clauses: tuple[Column[Any], Iterable[Any]],
|
|
259
233
|
in_clauses_chunk_size: int | None = None,
|
|
260
234
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
261
|
-
timeout:
|
|
235
|
+
timeout: Delta | None = None,
|
|
236
|
+
error: MaybeType[BaseException] = TimeoutError,
|
|
262
237
|
**kwargs: Any,
|
|
263
238
|
) -> AsyncIterable[DataFrame]: ...
|
|
264
239
|
@overload
|
|
@@ -273,7 +248,8 @@ async def select_to_dataframe(
|
|
|
273
248
|
in_clauses: tuple[Column[Any], Iterable[Any]] | None = None,
|
|
274
249
|
in_clauses_chunk_size: int | None = None,
|
|
275
250
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
276
|
-
timeout:
|
|
251
|
+
timeout: Delta | None = None,
|
|
252
|
+
error: MaybeType[BaseException] = TimeoutError,
|
|
277
253
|
**kwargs: Any,
|
|
278
254
|
) -> DataFrame | Iterable[DataFrame] | AsyncIterable[DataFrame]: ...
|
|
279
255
|
async def select_to_dataframe(
|
|
@@ -287,7 +263,7 @@ async def select_to_dataframe(
|
|
|
287
263
|
in_clauses: tuple[Column[Any], Iterable[Any]] | None = None,
|
|
288
264
|
in_clauses_chunk_size: int | None = None,
|
|
289
265
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
290
|
-
timeout:
|
|
266
|
+
timeout: Delta | None = None,
|
|
291
267
|
error: MaybeType[BaseException] = TimeoutError,
|
|
292
268
|
**kwargs: Any,
|
|
293
269
|
) -> DataFrame | Iterable[DataFrame] | AsyncIterable[DataFrame]:
|
|
@@ -390,7 +366,7 @@ def _select_to_dataframe_map_table_column_type_to_dtype(
|
|
|
390
366
|
return pl.Date
|
|
391
367
|
if is_subclass_gen(py_type, dt.datetime):
|
|
392
368
|
has_tz: bool = type_use.timezone
|
|
393
|
-
return
|
|
369
|
+
return zoned_date_time_dtype(time_zone=time_zone) if has_tz else Datetime()
|
|
394
370
|
if issubclass(py_type, dt.time):
|
|
395
371
|
return Time
|
|
396
372
|
if issubclass(py_type, dt.timedelta):
|
|
@@ -429,15 +405,14 @@ def _select_to_dataframe_yield_selects_with_in_clauses(
|
|
|
429
405
|
in_clauses_chunk_size: int | None = None,
|
|
430
406
|
chunk_size_frac: float = CHUNK_SIZE_FRAC,
|
|
431
407
|
) -> Iterator[Select[Any]]:
|
|
432
|
-
max_length = len(sel.selected_columns)
|
|
433
408
|
in_col, in_values = in_clauses
|
|
434
409
|
if in_clauses_chunk_size is None:
|
|
435
410
|
chunk_size = get_chunk_size(
|
|
436
|
-
engine, chunk_size_frac=chunk_size_frac
|
|
411
|
+
engine, sel.selected_columns, chunk_size_frac=chunk_size_frac
|
|
437
412
|
)
|
|
438
413
|
else:
|
|
439
414
|
chunk_size = in_clauses_chunk_size
|
|
440
415
|
return (sel.where(in_col.in_(values)) for values in chunked(in_values, chunk_size))
|
|
441
416
|
|
|
442
417
|
|
|
443
|
-
__all__ = ["
|
|
418
|
+
__all__ = ["insert_dataframe", "select_to_dataframe"]
|
utilities/string.py
CHANGED
|
@@ -10,11 +10,10 @@ def substitute_environ(path_or_text: Path | str, /, **kwargs: Any) -> str:
|
|
|
10
10
|
"""Substitute the environment variables in a file."""
|
|
11
11
|
match path_or_text:
|
|
12
12
|
case Path() as path:
|
|
13
|
-
|
|
14
|
-
return substitute_environ(fh.read(), **kwargs)
|
|
13
|
+
return substitute_environ(path.read_text(), **kwargs)
|
|
15
14
|
case str() as text:
|
|
16
15
|
return Template(text).substitute(environ, **kwargs)
|
|
17
|
-
case
|
|
16
|
+
case never:
|
|
18
17
|
assert_never(never)
|
|
19
18
|
|
|
20
19
|
|