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.

Files changed (97) hide show
  1. dycw_utilities-0.178.1.dist-info/METADATA +34 -0
  2. dycw_utilities-0.178.1.dist-info/RECORD +105 -0
  3. dycw_utilities-0.178.1.dist-info/WHEEL +4 -0
  4. dycw_utilities-0.178.1.dist-info/entry_points.txt +4 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +13 -10
  7. utilities/asyncio.py +312 -787
  8. utilities/atomicwrites.py +18 -6
  9. utilities/atools.py +64 -4
  10. utilities/cachetools.py +9 -6
  11. utilities/click.py +195 -77
  12. utilities/concurrent.py +1 -1
  13. utilities/contextlib.py +216 -17
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +15 -28
  17. utilities/docker.py +387 -0
  18. utilities/enum.py +2 -2
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +28 -59
  21. utilities/fpdf2.py +2 -2
  22. utilities/functions.py +24 -269
  23. utilities/git.py +9 -30
  24. utilities/grp.py +28 -0
  25. utilities/gzip.py +31 -0
  26. utilities/http.py +3 -2
  27. utilities/hypothesis.py +513 -159
  28. utilities/importlib.py +17 -1
  29. utilities/inflect.py +12 -4
  30. utilities/iterables.py +33 -58
  31. utilities/jinja2.py +148 -0
  32. utilities/json.py +70 -0
  33. utilities/libcst.py +38 -17
  34. utilities/lightweight_charts.py +4 -7
  35. utilities/logging.py +136 -93
  36. utilities/math.py +8 -4
  37. utilities/more_itertools.py +43 -45
  38. utilities/operator.py +27 -27
  39. utilities/orjson.py +189 -36
  40. utilities/os.py +61 -4
  41. utilities/packaging.py +115 -0
  42. utilities/parse.py +8 -5
  43. utilities/pathlib.py +269 -40
  44. utilities/permissions.py +298 -0
  45. utilities/platform.py +7 -6
  46. utilities/polars.py +1205 -413
  47. utilities/polars_ols.py +1 -1
  48. utilities/postgres.py +408 -0
  49. utilities/pottery.py +43 -19
  50. utilities/pqdm.py +3 -3
  51. utilities/psutil.py +5 -57
  52. utilities/pwd.py +28 -0
  53. utilities/pydantic.py +4 -52
  54. utilities/pydantic_settings.py +240 -0
  55. utilities/pydantic_settings_sops.py +76 -0
  56. utilities/pyinstrument.py +7 -7
  57. utilities/pytest.py +104 -143
  58. utilities/pytest_plugins/__init__.py +1 -0
  59. utilities/pytest_plugins/pytest_randomly.py +23 -0
  60. utilities/pytest_plugins/pytest_regressions.py +56 -0
  61. utilities/pytest_regressions.py +26 -46
  62. utilities/random.py +11 -6
  63. utilities/re.py +1 -1
  64. utilities/redis.py +220 -343
  65. utilities/sentinel.py +10 -0
  66. utilities/shelve.py +4 -1
  67. utilities/shutil.py +25 -0
  68. utilities/slack_sdk.py +35 -104
  69. utilities/sqlalchemy.py +496 -471
  70. utilities/sqlalchemy_polars.py +29 -54
  71. utilities/string.py +2 -3
  72. utilities/subprocess.py +1977 -0
  73. utilities/tempfile.py +112 -4
  74. utilities/testbook.py +50 -0
  75. utilities/text.py +174 -42
  76. utilities/throttle.py +158 -0
  77. utilities/timer.py +2 -2
  78. utilities/traceback.py +70 -35
  79. utilities/types.py +102 -30
  80. utilities/typing.py +479 -19
  81. utilities/uuid.py +42 -5
  82. utilities/version.py +27 -26
  83. utilities/whenever.py +1559 -361
  84. utilities/zoneinfo.py +80 -22
  85. dycw_utilities-0.135.0.dist-info/METADATA +0 -39
  86. dycw_utilities-0.135.0.dist-info/RECORD +0 -96
  87. dycw_utilities-0.135.0.dist-info/WHEEL +0 -4
  88. dycw_utilities-0.135.0.dist-info/licenses/LICENSE +0 -21
  89. utilities/aiolimiter.py +0 -25
  90. utilities/arq.py +0 -216
  91. utilities/eventkit.py +0 -388
  92. utilities/luigi.py +0 -183
  93. utilities/period.py +0 -152
  94. utilities/pudb.py +0 -62
  95. utilities/python_dotenv.py +0 -101
  96. utilities/streamlit.py +0 -105
  97. utilities/typed_settings.py +0 -123
@@ -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, Literal, assert_never, cast, overload, override
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 zoned_datetime
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).to_dicts()
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
- match upsert:
100
- case None:
101
- await insert_items(
102
- engine,
103
- (items, table_or_orm),
104
- snake=snake,
105
- chunk_size_frac=chunk_size_frac,
106
- assume_tables_exist=assume_tables_exist,
107
- timeout_create=timeout_create,
108
- error_create=error_create,
109
- timeout_insert=timeout_insert,
110
- error_insert=error_insert,
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: TimeDelta | None = None,
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: TimeDelta | None = None,
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: TimeDelta | None = None,
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: TimeDelta | None = None,
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: TimeDelta | None = None,
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 zoned_datetime(time_zone=time_zone) if has_tz else Datetime()
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, max_length=max_length
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__ = ["InsertDataFrameError", "insert_dataframe", "select_to_dataframe"]
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
- with path.open() as fh:
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 _ as never:
16
+ case never:
18
17
  assert_never(never)
19
18
 
20
19