dycw-utilities 0.129.10__py3-none-any.whl → 0.175.17__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.
Files changed (103) hide show
  1. dycw_utilities-0.175.17.dist-info/METADATA +34 -0
  2. dycw_utilities-0.175.17.dist-info/RECORD +103 -0
  3. dycw_utilities-0.175.17.dist-info/WHEEL +4 -0
  4. dycw_utilities-0.175.17.dist-info/entry_points.txt +4 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +14 -14
  7. utilities/asyncio.py +350 -819
  8. utilities/atomicwrites.py +18 -6
  9. utilities/atools.py +77 -22
  10. utilities/cachetools.py +24 -29
  11. utilities/click.py +393 -237
  12. utilities/concurrent.py +8 -11
  13. utilities/contextlib.py +216 -17
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +83 -118
  17. utilities/docker.py +293 -0
  18. utilities/enum.py +26 -23
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +29 -65
  21. utilities/fpdf2.py +3 -3
  22. utilities/functions.py +169 -416
  23. utilities/functools.py +18 -19
  24. utilities/git.py +9 -30
  25. utilities/grp.py +28 -0
  26. utilities/gzip.py +31 -0
  27. utilities/http.py +3 -2
  28. utilities/hypothesis.py +738 -589
  29. utilities/importlib.py +17 -1
  30. utilities/inflect.py +25 -0
  31. utilities/iterables.py +194 -262
  32. utilities/jinja2.py +148 -0
  33. utilities/json.py +70 -0
  34. utilities/libcst.py +38 -17
  35. utilities/lightweight_charts.py +5 -9
  36. utilities/logging.py +345 -543
  37. utilities/math.py +18 -13
  38. utilities/memory_profiler.py +11 -15
  39. utilities/more_itertools.py +200 -131
  40. utilities/operator.py +33 -29
  41. utilities/optuna.py +6 -6
  42. utilities/orjson.py +272 -137
  43. utilities/os.py +61 -4
  44. utilities/parse.py +59 -61
  45. utilities/pathlib.py +281 -40
  46. utilities/permissions.py +298 -0
  47. utilities/pickle.py +2 -2
  48. utilities/platform.py +24 -5
  49. utilities/polars.py +1214 -430
  50. utilities/polars_ols.py +1 -1
  51. utilities/postgres.py +408 -0
  52. utilities/pottery.py +113 -26
  53. utilities/pqdm.py +10 -11
  54. utilities/psutil.py +6 -57
  55. utilities/pwd.py +28 -0
  56. utilities/pydantic.py +4 -54
  57. utilities/pydantic_settings.py +240 -0
  58. utilities/pydantic_settings_sops.py +76 -0
  59. utilities/pyinstrument.py +8 -10
  60. utilities/pytest.py +227 -121
  61. utilities/pytest_plugins/__init__.py +1 -0
  62. utilities/pytest_plugins/pytest_randomly.py +23 -0
  63. utilities/pytest_plugins/pytest_regressions.py +56 -0
  64. utilities/pytest_regressions.py +26 -46
  65. utilities/random.py +13 -9
  66. utilities/re.py +58 -28
  67. utilities/redis.py +401 -550
  68. utilities/scipy.py +1 -1
  69. utilities/sentinel.py +10 -0
  70. utilities/shelve.py +4 -1
  71. utilities/shutil.py +25 -0
  72. utilities/slack_sdk.py +36 -106
  73. utilities/sqlalchemy.py +502 -473
  74. utilities/sqlalchemy_polars.py +38 -94
  75. utilities/string.py +2 -3
  76. utilities/subprocess.py +1572 -0
  77. utilities/tempfile.py +86 -4
  78. utilities/testbook.py +50 -0
  79. utilities/text.py +165 -42
  80. utilities/timer.py +37 -65
  81. utilities/traceback.py +158 -929
  82. utilities/types.py +146 -116
  83. utilities/typing.py +531 -71
  84. utilities/tzdata.py +1 -53
  85. utilities/tzlocal.py +6 -23
  86. utilities/uuid.py +43 -5
  87. utilities/version.py +27 -26
  88. utilities/whenever.py +1776 -386
  89. utilities/zoneinfo.py +84 -22
  90. dycw_utilities-0.129.10.dist-info/METADATA +0 -241
  91. dycw_utilities-0.129.10.dist-info/RECORD +0 -96
  92. dycw_utilities-0.129.10.dist-info/WHEEL +0 -4
  93. dycw_utilities-0.129.10.dist-info/licenses/LICENSE +0 -21
  94. utilities/datetime.py +0 -1409
  95. utilities/eventkit.py +0 -402
  96. utilities/loguru.py +0 -144
  97. utilities/luigi.py +0 -228
  98. utilities/period.py +0 -324
  99. utilities/pyrsistent.py +0 -89
  100. utilities/python_dotenv.py +0 -105
  101. utilities/streamlit.py +0 -105
  102. utilities/sys.py +0 -87
  103. utilities/tenacity.py +0 -145
utilities/typing.py CHANGED
@@ -1,21 +1,25 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
- from collections.abc import Mapping, Sequence
5
- from dataclasses import dataclass
4
+ from collections.abc import Callable, Iterable, Mapping, Sequence
5
+ from dataclasses import dataclass, is_dataclass
6
+ from functools import partial
6
7
  from itertools import chain
7
8
  from pathlib import Path
9
+ from re import search
8
10
  from types import NoneType, UnionType
9
11
  from typing import (
10
12
  Any,
13
+ ForwardRef,
11
14
  Literal,
12
15
  NamedTuple,
16
+ NotRequired,
13
17
  Optional, # pyright: ignore[reportDeprecated]
14
- Self,
15
18
  TypeAliasType,
16
19
  TypeGuard,
17
- TypeVar,
18
20
  Union, # pyright: ignore[reportDeprecated]
21
+ _TypedDictMeta, # pyright: ignore[reportAttributeAccessIssue]
22
+ cast,
19
23
  get_origin,
20
24
  overload,
21
25
  override,
@@ -25,27 +29,26 @@ from typing import get_type_hints as _get_type_hints
25
29
  from uuid import UUID
26
30
  from warnings import warn
27
31
 
32
+ import whenever
33
+ from whenever import (
34
+ Date,
35
+ DateDelta,
36
+ DateTimeDelta,
37
+ PlainDateTime,
38
+ Time,
39
+ TimeDelta,
40
+ ZonedDateTime,
41
+ )
42
+
28
43
  from utilities.iterables import unique_everseen
29
44
  from utilities.sentinel import Sentinel
30
- from utilities.types import StrMapping
31
-
32
- _T = TypeVar("_T")
33
- _T1 = TypeVar("_T1")
34
- _T2 = TypeVar("_T2")
35
- _T3 = TypeVar("_T3")
36
- _T4 = TypeVar("_T4")
37
- _T5 = TypeVar("_T5")
38
-
39
-
40
- ##
41
-
42
-
43
- def contains_self(obj: Any, /) -> bool:
44
- """Check if an annotation contains `Self`."""
45
- return (obj is Self) or any(map(contains_self, get_args(obj)))
46
-
47
-
48
- ##
45
+ from utilities.types import (
46
+ Dataclass,
47
+ StrMapping,
48
+ StrStrMapping,
49
+ TupleOrStrMapping,
50
+ TypeLike,
51
+ )
49
52
 
50
53
 
51
54
  def get_args(obj: Any, /, *, optional_drop_none: bool = False) -> tuple[Any, ...]:
@@ -61,6 +64,18 @@ def get_args(obj: Any, /, *, optional_drop_none: bool = False) -> tuple[Any, ...
61
64
  ##
62
65
 
63
66
 
67
+ def get_forward_ref_args(obj: Any, /) -> StrStrMapping:
68
+ """Get the forward args."""
69
+ return {
70
+ k: v.__forward_arg__
71
+ for k, v in obj.__annotations__.items()
72
+ if isinstance(v, ForwardRef)
73
+ }
74
+
75
+
76
+ ##
77
+
78
+
64
79
  def get_literal_elements(obj: Any, /) -> list[Any]:
65
80
  """Get the elements of a literal annotation."""
66
81
  return _get_literal_elements_inner(obj)
@@ -132,21 +147,44 @@ def get_type_hints(
132
147
  warn_name_errors: bool = False,
133
148
  ) -> dict[str, Any]:
134
149
  """Get the type hints of an object."""
135
- result: dict[str, Any] = obj.__annotations__
136
- _ = {Literal, Path, Sentinel, StrMapping, UUID, dt}
150
+ _ = {
151
+ Date,
152
+ DateDelta,
153
+ DateTimeDelta,
154
+ Literal,
155
+ Path,
156
+ PlainDateTime,
157
+ Sentinel,
158
+ StrMapping,
159
+ Time,
160
+ TimeDelta,
161
+ UUID,
162
+ ZonedDateTime,
163
+ whenever.Date,
164
+ whenever.DateDelta,
165
+ whenever.DateTimeDelta,
166
+ whenever.PlainDateTime,
167
+ whenever.Time,
168
+ whenever.TimeDelta,
169
+ whenever.ZonedDateTime,
170
+ }
137
171
  globalns_use = globals() | ({} if globalns is None else dict(globalns))
138
172
  localns_use = {} if localns is None else dict(localns)
173
+ result: dict[str, Any] = obj.__annotations__
174
+ result = result | dict(get_forward_ref_args(obj))
139
175
  try:
140
- hints = _get_type_hints(obj, globalns=globalns_use, localns=localns_use)
176
+ hints = _get_type_hints(
177
+ obj, globalns=globalns_use, localns=localns_use, include_extras=True
178
+ )
141
179
  except NameError as error:
142
180
  if warn_name_errors:
143
181
  warn(f"Error getting type hints for {obj!r}; {error}", stacklevel=2)
144
182
  else:
145
- result.update({
183
+ result = result | {
146
184
  key: value
147
185
  for key, value in hints.items()
148
186
  if (key not in result) or isinstance(result[key], str)
149
- })
187
+ }
150
188
  return result
151
189
 
152
190
 
@@ -159,12 +197,9 @@ def get_union_type_classes(obj: Any, /) -> tuple[type[Any], ...]:
159
197
  raise _GetUnionTypeClassesUnionTypeError(obj=obj)
160
198
  types_: Sequence[type[Any]] = []
161
199
  for arg in get_args(obj):
162
- if isinstance(arg, type):
163
- types_.append(arg)
164
- elif is_union_type(arg):
165
- types_.extend(get_union_type_classes(arg))
166
- else:
200
+ if not isinstance(arg, type):
167
201
  raise _GetUnionTypeClassesInternalTypeError(obj=obj, inner=arg)
202
+ types_.append(arg)
168
203
  return tuple(types_)
169
204
 
170
205
 
@@ -194,6 +229,22 @@ class _GetUnionTypeClassesInternalTypeError(GetUnionTypeClassesError):
194
229
  ##
195
230
 
196
231
 
232
+ def is_dataclass_class(obj: Any, /) -> TypeGuard[type[Dataclass]]:
233
+ """Check if an object is a dataclass."""
234
+ return isinstance(obj, type) and is_dataclass(obj)
235
+
236
+
237
+ ##
238
+
239
+
240
+ def is_dataclass_instance(obj: Any, /) -> TypeGuard[Dataclass]:
241
+ """Check if an object is an instance of a dataclass."""
242
+ return (not isinstance(obj, type)) and is_dataclass(obj)
243
+
244
+
245
+ ##
246
+
247
+
197
248
  def is_dict_type(obj: Any, /) -> bool:
198
249
  """Check if an object is a dict type annotation."""
199
250
  return _is_annotation_of_type(obj, dict)
@@ -211,49 +262,175 @@ def is_frozenset_type(obj: Any, /) -> bool:
211
262
 
212
263
 
213
264
  @overload
214
- def is_instance_gen(obj: Any, type_: type[_T], /) -> TypeGuard[_T]: ...
265
+ def is_instance_gen[T](
266
+ obj: Any,
267
+ type_: type[T],
268
+ /,
269
+ *,
270
+ globalns: StrMapping | None = None,
271
+ localns: StrMapping | None = None,
272
+ warn_name_errors: bool = False,
273
+ ) -> TypeGuard[T]: ...
215
274
  @overload
216
- def is_instance_gen(obj: Any, type_: tuple[_T1], /) -> TypeGuard[_T1]: ...
275
+ def is_instance_gen[T1](
276
+ obj: Any,
277
+ type_: tuple[T1],
278
+ /,
279
+ *,
280
+ globalns: StrMapping | None = None,
281
+ localns: StrMapping | None = None,
282
+ warn_name_errors: bool = False,
283
+ ) -> TypeGuard[T1]: ...
217
284
  @overload
218
- def is_instance_gen(obj: Any, type_: tuple[_T1, _T2], /) -> TypeGuard[_T1 | _T2]: ...
285
+ def is_instance_gen[T1, T2](
286
+ obj: Any,
287
+ type_: tuple[T1, T2],
288
+ /,
289
+ *,
290
+ globalns: StrMapping | None = None,
291
+ localns: StrMapping | None = None,
292
+ warn_name_errors: bool = False,
293
+ ) -> TypeGuard[T1 | T2]: ...
219
294
  @overload
220
- def is_instance_gen(
221
- obj: Any, type_: tuple[_T1, _T2, _T3], /
222
- ) -> TypeGuard[_T1 | _T2 | _T3]: ...
295
+ def is_instance_gen[T1, T2, T3](
296
+ obj: Any,
297
+ type_: tuple[T1, T2, T3],
298
+ /,
299
+ *,
300
+ globalns: StrMapping | None = None,
301
+ localns: StrMapping | None = None,
302
+ warn_name_errors: bool = False,
303
+ ) -> TypeGuard[T1 | T2 | T3]: ...
223
304
  @overload
224
- def is_instance_gen(
225
- obj: Any, type_: tuple[_T1, _T2, _T3, _T4], /
226
- ) -> TypeGuard[_T1 | _T2 | _T3 | _T4]: ...
305
+ def is_instance_gen[T1, T2, T3, T4](
306
+ obj: Any,
307
+ type_: tuple[T1, T2, T3, T4],
308
+ /,
309
+ *,
310
+ globalns: StrMapping | None = None,
311
+ localns: StrMapping | None = None,
312
+ warn_name_errors: bool = False,
313
+ ) -> TypeGuard[T1 | T2 | T3 | T4]: ...
227
314
  @overload
228
- def is_instance_gen(
229
- obj: Any, type_: tuple[_T1, _T2, _T3, _T4, _T5], /
230
- ) -> TypeGuard[_T1 | _T2 | _T3 | _T4 | _T5]: ...
315
+ def is_instance_gen[T1, T2, T3, T4, T5](
316
+ obj: Any,
317
+ type_: tuple[T1, T2, T3, T4, T5],
318
+ /,
319
+ *,
320
+ globalns: StrMapping | None = None,
321
+ localns: StrMapping | None = None,
322
+ warn_name_errors: bool = False,
323
+ ) -> TypeGuard[T1 | T2 | T3 | T4 | T5]: ...
231
324
  @overload
232
- def is_instance_gen(obj: Any, type_: Any, /) -> bool: ...
233
- def is_instance_gen(obj: Any, type_: Any, /) -> bool:
325
+ def is_instance_gen(
326
+ obj: Any,
327
+ type_: Any,
328
+ /,
329
+ *,
330
+ globalns: StrMapping | None = None,
331
+ localns: StrMapping | None = None,
332
+ warn_name_errors: bool = False,
333
+ ) -> bool: ...
334
+ def is_instance_gen(
335
+ obj: Any,
336
+ type_: Any,
337
+ /,
338
+ *,
339
+ globalns: StrMapping | None = None,
340
+ localns: StrMapping | None = None,
341
+ warn_name_errors: bool = False,
342
+ ) -> bool:
234
343
  """Check if an instance relationship holds, except bool<int."""
235
344
  # parent
236
345
  if isinstance(type_, tuple):
237
- return any(is_instance_gen(obj, t) for t in type_)
346
+ return any(
347
+ is_instance_gen(
348
+ obj,
349
+ t,
350
+ globalns=globalns,
351
+ localns=localns,
352
+ warn_name_errors=warn_name_errors,
353
+ )
354
+ for t in type_
355
+ )
238
356
  if is_literal_type(type_):
239
357
  return obj in get_args(type_)
240
358
  if is_union_type(type_):
241
- return any(is_instance_gen(obj, t) for t in get_args(type_))
359
+ return any(
360
+ is_instance_gen(
361
+ obj,
362
+ t,
363
+ globalns=globalns,
364
+ localns=localns,
365
+ warn_name_errors=warn_name_errors,
366
+ )
367
+ for t in get_args(type_)
368
+ )
242
369
  # tuple vs tuple
243
370
  if isinstance(obj, tuple) and is_tuple_type(type_):
244
371
  type_args = get_args(type_)
245
372
  return (len(obj) == len(type_args)) and all(
246
- is_instance_gen(o, t) for o, t in zip(obj, type_args, strict=True)
373
+ is_instance_gen(
374
+ o,
375
+ t,
376
+ globalns=globalns,
377
+ localns=localns,
378
+ warn_name_errors=warn_name_errors,
379
+ )
380
+ for o, t in zip(obj, type_args, strict=True)
247
381
  )
248
382
  if isinstance(obj, tuple) is not is_tuple_type(type_):
249
383
  return False
250
384
  # basic
385
+ if isinstance(type_, _TypedDictMeta):
386
+ return _is_instance_typed_dict(
387
+ obj,
388
+ type_,
389
+ globalns=globalns,
390
+ localns=localns,
391
+ warn_name_errors=warn_name_errors,
392
+ )
251
393
  if isinstance(type_, type):
252
394
  return any(_is_instance_gen_type(obj, t) for t in get_type_classes(type_))
253
395
  raise IsInstanceGenError(obj=obj, type_=type_)
254
396
 
255
397
 
256
- def _is_instance_gen_type(obj: Any, type_: type[_T], /) -> TypeGuard[_T]:
398
+ def _is_instance_typed_dict[T: _TypedDictMeta](
399
+ obj: Any,
400
+ type_: type[T],
401
+ /,
402
+ *,
403
+ globalns: StrMapping | None = None,
404
+ localns: StrMapping | None = None,
405
+ warn_name_errors: bool = False,
406
+ ) -> TypeGuard[T]:
407
+ if not isinstance(obj, dict):
408
+ return False
409
+ if not all(isinstance(k, str) for k in obj):
410
+ return False
411
+ obj = cast("dict[str, Any]", obj)
412
+ hints = get_type_hints(
413
+ type_, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
414
+ )
415
+ optional = {
416
+ k for k, v in type_.__annotations__.items() if is_not_required_annotation(v)
417
+ }
418
+ required = {k: v for k, v in hints.items() if k not in optional}
419
+ if not set(obj).issuperset(required):
420
+ return False
421
+ return all(
422
+ is_instance_gen(
423
+ obj[k],
424
+ required[k],
425
+ globalns=globalns,
426
+ localns=localns,
427
+ warn_name_errors=warn_name_errors,
428
+ )
429
+ for k in required
430
+ )
431
+
432
+
433
+ def _is_instance_gen_type[T](obj: Any, type_: type[T], /) -> TypeGuard[T]:
257
434
  return (
258
435
  isinstance(obj, type_)
259
436
  and not (
@@ -282,6 +459,101 @@ class IsInstanceGenError(Exception):
282
459
  ##
283
460
 
284
461
 
462
+ @overload
463
+ def is_iterable_of[T](
464
+ obj: Any,
465
+ cls: type[T],
466
+ /,
467
+ *,
468
+ globalns: StrMapping | None = None,
469
+ localns: StrMapping | None = None,
470
+ warn_name_errors: bool = False,
471
+ ) -> TypeGuard[Iterable[T]]: ...
472
+ @overload
473
+ def is_iterable_of[T1](
474
+ obj: Any,
475
+ cls: tuple[type[T1]],
476
+ /,
477
+ *,
478
+ globalns: StrMapping | None = None,
479
+ localns: StrMapping | None = None,
480
+ warn_name_errors: bool = False,
481
+ ) -> TypeGuard[Iterable[T1]]: ...
482
+ @overload
483
+ def is_iterable_of[T1, T2](
484
+ obj: Any,
485
+ cls: tuple[type[T1], type[T2]],
486
+ /,
487
+ *,
488
+ globalns: StrMapping | None = None,
489
+ localns: StrMapping | None = None,
490
+ warn_name_errors: bool = False,
491
+ ) -> TypeGuard[Iterable[T1 | T2]]: ...
492
+ @overload
493
+ def is_iterable_of[T1, T2, T3](
494
+ obj: Any,
495
+ cls: tuple[type[T1], type[T2], type[T3]],
496
+ /,
497
+ *,
498
+ globalns: StrMapping | None = None,
499
+ localns: StrMapping | None = None,
500
+ warn_name_errors: bool = False,
501
+ ) -> TypeGuard[Iterable[T1 | T2 | T3]]: ...
502
+ @overload
503
+ def is_iterable_of[T1, T2, T3, T4](
504
+ obj: Any,
505
+ cls: tuple[type[T1], type[T2], type[T3], type[T4]],
506
+ /,
507
+ *,
508
+ globalns: StrMapping | None = None,
509
+ localns: StrMapping | None = None,
510
+ warn_name_errors: bool = False,
511
+ ) -> TypeGuard[Iterable[T1 | T2 | T3 | T4]]: ...
512
+ @overload
513
+ def is_iterable_of[T1, T2, T3, T4, T5](
514
+ obj: Any,
515
+ cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]],
516
+ /,
517
+ *,
518
+ globalns: StrMapping | None = None,
519
+ localns: StrMapping | None = None,
520
+ warn_name_errors: bool = False,
521
+ ) -> TypeGuard[Iterable[T1 | T2 | T3 | T4 | T5]]: ...
522
+ @overload
523
+ def is_iterable_of[T](
524
+ obj: Any,
525
+ cls: TypeLike[T],
526
+ /,
527
+ *,
528
+ globalns: StrMapping | None = None,
529
+ localns: StrMapping | None = None,
530
+ warn_name_errors: bool = False,
531
+ ) -> TypeGuard[Iterable[T]]: ...
532
+ def is_iterable_of[T](
533
+ obj: Any,
534
+ cls: TypeLike[T],
535
+ /,
536
+ *,
537
+ globalns: StrMapping | None = None,
538
+ localns: StrMapping | None = None,
539
+ warn_name_errors: bool = False,
540
+ ) -> TypeGuard[Iterable[T]]:
541
+ """Check if an object is a iterable of tuple or string mappings."""
542
+ return isinstance(obj, Iterable) and all(
543
+ is_instance_gen(
544
+ o,
545
+ cls,
546
+ globalns=globalns,
547
+ localns=localns,
548
+ warn_name_errors=warn_name_errors,
549
+ )
550
+ for o in obj
551
+ )
552
+
553
+
554
+ ##
555
+
556
+
285
557
  def is_list_type(obj: Any, /) -> bool:
286
558
  """Check if an object is a list type annotation."""
287
559
  return _is_annotation_of_type(obj, list)
@@ -328,6 +600,30 @@ def _is_namedtuple_core(obj: Any, /) -> bool:
328
600
  ##
329
601
 
330
602
 
603
+ def is_not_required_annotation(obj: Any) -> bool:
604
+ """Check if an annotation is not required."""
605
+ if is_not_required_type(obj):
606
+ return True
607
+ match obj:
608
+ case str() as text:
609
+ return bool(search("NotRequired", text))
610
+ case ForwardRef() as fr:
611
+ return is_not_required_annotation(fr.__forward_arg__)
612
+ case _:
613
+ return False
614
+
615
+
616
+ ##
617
+
618
+
619
+ def is_not_required_type(obj: Any, /) -> bool:
620
+ """Check if an object is a not-required type annotation."""
621
+ return (obj is NotRequired) or _is_annotation_of_type(obj, NotRequired)
622
+
623
+
624
+ ##
625
+
626
+
331
627
  def is_optional_type(obj: Any, /) -> bool:
332
628
  """Check if an object is an optional type annotation."""
333
629
  is_optional = _is_annotation_of_type(obj, Optional) # pyright: ignore[reportDeprecated]
@@ -339,6 +635,104 @@ def is_optional_type(obj: Any, /) -> bool:
339
635
  ##
340
636
 
341
637
 
638
+ @overload
639
+ def is_sequence_of[T](
640
+ obj: Any,
641
+ cls: type[T],
642
+ /,
643
+ *,
644
+ globalns: StrMapping | None = None,
645
+ localns: StrMapping | None = None,
646
+ warn_name_errors: bool = False,
647
+ ) -> TypeGuard[Sequence[T]]: ...
648
+ @overload
649
+ def is_sequence_of[T1](
650
+ obj: Any,
651
+ cls: tuple[type[T1]],
652
+ /,
653
+ *,
654
+ globalns: StrMapping | None = None,
655
+ localns: StrMapping | None = None,
656
+ warn_name_errors: bool = False,
657
+ ) -> TypeGuard[Sequence[T1]]: ...
658
+ @overload
659
+ def is_sequence_of[T1, T2](
660
+ obj: Any,
661
+ cls: tuple[type[T1], type[T2]],
662
+ /,
663
+ *,
664
+ globalns: StrMapping | None = None,
665
+ localns: StrMapping | None = None,
666
+ warn_name_errors: bool = False,
667
+ ) -> TypeGuard[Sequence[T1 | T2]]: ...
668
+ @overload
669
+ def is_sequence_of[T1, T2, T3](
670
+ obj: Any,
671
+ cls: tuple[type[T1], type[T2], type[T3]],
672
+ /,
673
+ *,
674
+ globalns: StrMapping | None = None,
675
+ localns: StrMapping | None = None,
676
+ warn_name_errors: bool = False,
677
+ ) -> TypeGuard[Sequence[T1 | T2 | T3]]: ...
678
+ @overload
679
+ def is_sequence_of[T1, T2, T3, T4](
680
+ obj: Any,
681
+ cls: tuple[type[T1], type[T2], type[T3], type[T4]],
682
+ /,
683
+ *,
684
+ globalns: StrMapping | None = None,
685
+ localns: StrMapping | None = None,
686
+ warn_name_errors: bool = False,
687
+ ) -> TypeGuard[Sequence[T1 | T2 | T3 | T4]]: ...
688
+ @overload
689
+ def is_sequence_of[T1, T2, T3, T4, T5](
690
+ obj: Any,
691
+ cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]],
692
+ /,
693
+ *,
694
+ globalns: StrMapping | None = None,
695
+ localns: StrMapping | None = None,
696
+ warn_name_errors: bool = False,
697
+ ) -> TypeGuard[Sequence[T1 | T2 | T3 | T4 | T5]]: ...
698
+ @overload
699
+ def is_sequence_of[T](
700
+ obj: Any,
701
+ cls: TypeLike[T],
702
+ /,
703
+ *,
704
+ globalns: StrMapping | None = None,
705
+ localns: StrMapping | None = None,
706
+ warn_name_errors: bool = False,
707
+ ) -> TypeGuard[Sequence[T]]: ...
708
+ def is_sequence_of[T](
709
+ obj: Any,
710
+ cls: TypeLike[T],
711
+ /,
712
+ *,
713
+ globalns: StrMapping | None = None,
714
+ localns: StrMapping | None = None,
715
+ warn_name_errors: bool = False,
716
+ ) -> TypeGuard[Sequence[T]]:
717
+ """Check if an object is a sequence of tuple or string mappings."""
718
+ return isinstance(obj, Sequence) and is_iterable_of(
719
+ obj, cls, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
720
+ )
721
+
722
+
723
+ ##
724
+
725
+
726
+ def is_sequence_of_tuple_or_str_mapping(
727
+ obj: Any, /
728
+ ) -> TypeGuard[Sequence[TupleOrStrMapping]]:
729
+ """Check if an object is a sequence of tuple or string mappings."""
730
+ return isinstance(obj, Sequence) and all(map(is_tuple_or_str_mapping, obj))
731
+
732
+
733
+ ##
734
+
735
+
342
736
  def is_sequence_type(obj: Any, /) -> bool:
343
737
  """Check if an object is a sequence type annotation."""
344
738
  return _is_annotation_of_type(obj, Sequence)
@@ -355,30 +749,36 @@ def is_set_type(obj: Any, /) -> bool:
355
749
  ##
356
750
 
357
751
 
752
+ def is_string_mapping(obj: Any, /) -> TypeGuard[StrMapping]:
753
+ """Check if an object is a string mapping."""
754
+ return isinstance(obj, Mapping) and is_iterable_of(obj, str)
755
+
756
+
757
+ ##
758
+
759
+
358
760
  @overload
359
- def is_subclass_gen(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[_T]]: ...
761
+ def is_subclass_gen[T](cls: type[Any], parent: type[T], /) -> TypeGuard[type[T]]: ...
360
762
  @overload
361
- def is_subclass_gen(
362
- cls: type[Any], parent: tuple[type[_T1]], /
363
- ) -> TypeGuard[type[_T1]]: ...
763
+ def is_subclass_gen[T1](
764
+ cls: type[Any], parent: tuple[type[T1]], /
765
+ ) -> TypeGuard[type[T1]]: ...
364
766
  @overload
365
- def is_subclass_gen(
366
- cls: type[Any], parent: tuple[type[_T1], type[_T2]], /
367
- ) -> TypeGuard[type[_T1 | _T2]]: ...
767
+ def is_subclass_gen[T1, T2](
768
+ cls: type[Any], parent: tuple[type[T1], type[T2]], /
769
+ ) -> TypeGuard[type[T1 | T2]]: ...
368
770
  @overload
369
- def is_subclass_gen(
370
- cls: type[Any], parent: tuple[type[_T1], type[_T2], type[_T3]], /
371
- ) -> TypeGuard[type[_T1 | _T2 | _T3]]: ...
771
+ def is_subclass_gen[T1, T2, T3](
772
+ cls: type[Any], parent: tuple[type[T1], type[T2], type[T3]], /
773
+ ) -> TypeGuard[type[T1 | T2 | T3]]: ...
372
774
  @overload
373
- def is_subclass_gen(
374
- cls: type[Any], parent: tuple[type[_T1], type[_T2], type[_T3], type[_T4]], /
375
- ) -> TypeGuard[type[_T1 | _T2 | _T3 | _T4]]: ...
775
+ def is_subclass_gen[T1, T2, T3, T4](
776
+ cls: type[Any], parent: tuple[type[T1], type[T2], type[T3], type[T4]], /
777
+ ) -> TypeGuard[type[T1 | T2 | T3 | T4]]: ...
376
778
  @overload
377
- def is_subclass_gen(
378
- cls: type[Any],
379
- parent: tuple[type[_T1], type[_T2], type[_T3], type[_T4], type[_T5]],
380
- /,
381
- ) -> TypeGuard[type[_T1 | _T2 | _T3 | _T4 | _T5]]: ...
779
+ def is_subclass_gen[T1, T2, T3, T4, T5](
780
+ cls: type[Any], parent: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
781
+ ) -> TypeGuard[type[T1 | T2 | T3 | T4 | T5]]: ...
382
782
  @overload
383
783
  def is_subclass_gen(cls: Any, parent: Any, /) -> bool: ...
384
784
  def is_subclass_gen(cls: Any, parent: Any, /) -> bool:
@@ -414,7 +814,7 @@ def is_subclass_gen(cls: Any, parent: Any, /) -> bool:
414
814
  raise IsSubclassGenError(cls=cls)
415
815
 
416
816
 
417
- def _is_subclass_gen_type(cls: type[Any], parent: type[_T], /) -> TypeGuard[type[_T]]:
817
+ def _is_subclass_gen_type[T](cls: type[Any], parent: type[T], /) -> TypeGuard[type[T]]:
418
818
  return (
419
819
  issubclass(cls, parent)
420
820
  and not (
@@ -442,6 +842,22 @@ class IsSubclassGenError(Exception):
442
842
  ##
443
843
 
444
844
 
845
+ def is_tuple(obj: Any, /) -> TypeGuard[tuple[Any, ...]]:
846
+ """Check if an object is a tuple."""
847
+ return isinstance(obj, tuple)
848
+
849
+
850
+ ##
851
+
852
+
853
+ def is_tuple_or_str_mapping(obj: Any, /) -> TypeGuard[TupleOrStrMapping]:
854
+ """Check if an object is a tuple or string mapping."""
855
+ return is_tuple(obj) or is_string_mapping(obj)
856
+
857
+
858
+ ##
859
+
860
+
445
861
  def is_tuple_type(obj: Any, /) -> bool:
446
862
  """Check if an object is a tuple type annotation."""
447
863
  return _is_annotation_of_type(obj, tuple)
@@ -466,28 +882,72 @@ def _is_annotation_of_type(obj: Any, origin: Any, /) -> bool:
466
882
  )
467
883
 
468
884
 
885
+ ##
886
+
887
+
888
+ @overload
889
+ def make_isinstance[T](cls: type[T], /) -> Callable[[Any], TypeGuard[T]]: ...
890
+ @overload
891
+ def make_isinstance[T1](cls: tuple[type[T1]], /) -> Callable[[Any], TypeGuard[T1]]: ...
892
+ @overload
893
+ def make_isinstance[T1, T2](
894
+ cls: tuple[type[T1], type[T2]], /
895
+ ) -> Callable[[Any], TypeGuard[T1 | T2]]: ...
896
+ @overload
897
+ def make_isinstance[T1, T2, T3](
898
+ cls: tuple[type[T1], type[T2], type[T3]], /
899
+ ) -> Callable[[Any], TypeGuard[T1 | T2 | T3]]: ...
900
+ @overload
901
+ def make_isinstance[T1, T2, T3, T4](
902
+ cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
903
+ ) -> Callable[[Any], TypeGuard[T1 | T2 | T3 | T4]]: ...
904
+ @overload
905
+ def make_isinstance[T1, T2, T3, T4, T5](
906
+ cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
907
+ ) -> Callable[[Any], TypeGuard[T1 | T2 | T3 | T4 | T5]]: ...
908
+ @overload
909
+ def make_isinstance[T](cls: TypeLike[T], /) -> Callable[[Any], TypeGuard[T]]: ...
910
+ def make_isinstance[T](cls: TypeLike[T], /) -> Callable[[Any], TypeGuard[T]]:
911
+ """Make a curried `isinstance` function."""
912
+ return partial(_make_instance_core, cls=cls)
913
+
914
+
915
+ def _make_instance_core[T](obj: Any, /, *, cls: TypeLike[T]) -> TypeGuard[T]:
916
+ return is_instance_gen(obj, cls)
917
+
918
+
469
919
  __all__ = [
470
920
  "GetTypeClassesError",
471
921
  "GetUnionTypeClassesError",
472
922
  "IsInstanceGenError",
473
923
  "IsSubclassGenError",
474
- "contains_self",
475
924
  "get_literal_elements",
476
925
  "get_type_classes",
477
926
  "get_type_hints",
478
927
  "get_union_type_classes",
928
+ "is_dataclass_class",
929
+ "is_dataclass_instance",
479
930
  "is_dict_type",
480
931
  "is_frozenset_type",
481
932
  "is_instance_gen",
933
+ "is_iterable_of",
482
934
  "is_list_type",
483
935
  "is_literal_type",
484
936
  "is_mapping_type",
485
937
  "is_namedtuple_class",
486
938
  "is_namedtuple_instance",
939
+ "is_not_required_annotation",
940
+ "is_not_required_type",
487
941
  "is_optional_type",
942
+ "is_sequence_of",
943
+ "is_sequence_of_tuple_or_str_mapping",
488
944
  "is_sequence_type",
489
945
  "is_set_type",
946
+ "is_string_mapping",
490
947
  "is_subclass_gen",
948
+ "is_tuple",
949
+ "is_tuple_or_str_mapping",
491
950
  "is_tuple_type",
492
951
  "is_union_type",
952
+ "make_isinstance",
493
953
  ]