dycw-utilities 0.112.5__py3-none-any.whl → 0.112.7__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.112.5.dist-info → dycw_utilities-0.112.7.dist-info}/METADATA +1 -1
- {dycw_utilities-0.112.5.dist-info → dycw_utilities-0.112.7.dist-info}/RECORD +9 -9
- utilities/__init__.py +1 -1
- utilities/dataclasses.py +284 -90
- utilities/parse.py +1 -3
- utilities/python_dotenv.py +2 -2
- utilities/typing.py +2 -0
- {dycw_utilities-0.112.5.dist-info → dycw_utilities-0.112.7.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.112.5.dist-info → dycw_utilities-0.112.7.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=OrvHk0ybPjcSwc2XsWqqzN9yhl5ksF0jAsvGwGFRa2I,60
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
3
|
utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
|
4
4
|
utilities/asyncio.py,sha256=41oQUurWMvadFK5gFnaG21hMM0Vmfn2WS6OpC0R9mas,14757
|
@@ -11,7 +11,7 @@ utilities/contextlib.py,sha256=OOIIEa5lXKGzFAnauaul40nlQnQko6Na4ryiMJcHkIg,478
|
|
11
11
|
utilities/contextvars.py,sha256=RsSGGrbQqqZ67rOydnM7WWIsM2lIE31UHJLejnHJPWY,505
|
12
12
|
utilities/cryptography.py,sha256=HyOewI20cl3uRXsKivhIaeLVDInQdzgXZGaly7hS5dE,771
|
13
13
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
14
|
-
utilities/dataclasses.py,sha256=
|
14
|
+
utilities/dataclasses.py,sha256=2BloGVlH85tNZjFQiKXNk00Qwe9aovoV7cwxqFRG2l8,32598
|
15
15
|
utilities/datetime.py,sha256=OF7jZE702UecnwAbq9D3N-GINpp9gSGoidki1RhimCE,35752
|
16
16
|
utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
|
17
17
|
utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
|
@@ -41,7 +41,7 @@ utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
|
|
41
41
|
utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
|
42
42
|
utilities/orjson.py,sha256=DBm2zPP04kcHpY3l1etL24ksNynu-R3duFyx3U-RjqQ,36948
|
43
43
|
utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
|
44
|
-
utilities/parse.py,sha256=
|
44
|
+
utilities/parse.py,sha256=vsZ2jf_ceSI_Kta9titixufysJaVXh0Whjz1T4awJZw,18938
|
45
45
|
utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
|
46
46
|
utilities/period.py,sha256=RWfcNVoNlW07RNdU47g_zuLZMKbtgfK4bE6G-9tVjY8,11024
|
47
47
|
utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
|
@@ -55,7 +55,7 @@ utilities/pyinstrument.py,sha256=OJFDh4o1CWIa4aYPYURdQjgap_nvP45KUsCEe94rQHY,829
|
|
55
55
|
utilities/pyrsistent.py,sha256=MoDcAqQGlSNkmlS32DCJLw-cZFAfHB6K9kpox_iyI4k,2512
|
56
56
|
utilities/pytest.py,sha256=85QUax4g2VBBAqAHtM9wekcSLB7_9O8AKFTaCshztL8,7989
|
57
57
|
utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq3c,5088
|
58
|
-
utilities/python_dotenv.py,sha256=
|
58
|
+
utilities/python_dotenv.py,sha256=iWcnpXbH7S6RoXHiLlGgyuH6udCupAcPd_gQ0eAenQ0,3190
|
59
59
|
utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
|
60
60
|
utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
|
61
61
|
utilities/redis.py,sha256=fAUbfOlCmxcxhh47PXQX63w0CU5iOFKfdUJ7jDn9ntM,22096
|
@@ -78,7 +78,7 @@ utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
|
78
78
|
utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
|
79
79
|
utilities/traceback.py,sha256=secexUnBsecfWV4ZuqP1W4pGF3prOeO1CRyJK-8zQDU,27402
|
80
80
|
utilities/types.py,sha256=kVY71hZkcnyYNIlYSse0mLm8yeP3OBkzhDPMME6jXxo,18126
|
81
|
-
utilities/typing.py,sha256=
|
81
|
+
utilities/typing.py,sha256=H6ysJkI830aRwLsMKz0SZIw4cpcsm7d6KhQOwr-SDh0,13817
|
82
82
|
utilities/tzdata.py,sha256=yCf70NICwAeazN3_JcXhWvRqCy06XJNQ42j7r6gw3HY,1217
|
83
83
|
utilities/tzlocal.py,sha256=3upDNFBvGh1l9njmLR2z2S6K6VxQSb7QizYGUbAH3JU,960
|
84
84
|
utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
|
@@ -87,7 +87,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
87
87
|
utilities/whenever.py,sha256=iLRP_-8CZtBpHKbGZGu-kjSMg1ZubJ-VSmgSy7Eudxw,17787
|
88
88
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
89
89
|
utilities/zoneinfo.py,sha256=-Xm57PMMwDTYpxJdkiJG13wnbwK--I7XItBh5WVhD-o,1874
|
90
|
-
dycw_utilities-0.112.
|
91
|
-
dycw_utilities-0.112.
|
92
|
-
dycw_utilities-0.112.
|
93
|
-
dycw_utilities-0.112.
|
90
|
+
dycw_utilities-0.112.7.dist-info/METADATA,sha256=NcBDjpG25EDCH04c9Cw1w6Im4LjtQILGKXPHqlTmbro,13004
|
91
|
+
dycw_utilities-0.112.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
92
|
+
dycw_utilities-0.112.7.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
93
|
+
dycw_utilities-0.112.7.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/dataclasses.py
CHANGED
@@ -240,17 +240,31 @@ def mapping_to_dataclass(
|
|
240
240
|
)
|
241
241
|
else:
|
242
242
|
fields_use = fields
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
243
|
+
try:
|
244
|
+
fields_to_values = str_mapping_to_field_mapping(
|
245
|
+
cls,
|
246
|
+
mapping,
|
247
|
+
fields=fields_use,
|
248
|
+
globalns=globalns,
|
249
|
+
localns=localns,
|
250
|
+
warn_name_errors=warn_name_errors,
|
251
|
+
head=head,
|
252
|
+
case_sensitive=case_sensitive,
|
253
|
+
allow_extra=allow_extra,
|
254
|
+
)
|
255
|
+
except _StrMappingToFieldMappingEmptyError as error:
|
256
|
+
raise _MappingToDataClassEmptyError(
|
257
|
+
cls=cls, key=error.key, head=head, case_sensitive=case_sensitive
|
258
|
+
) from None
|
259
|
+
except _StrMappingToFieldMappingNonUniqueError as error:
|
260
|
+
raise _MappingToDataClassNonUniqueError(
|
261
|
+
cls=cls,
|
262
|
+
key=error.key,
|
263
|
+
head=head,
|
264
|
+
case_sensitive=case_sensitive,
|
265
|
+
first=error.first,
|
266
|
+
second=error.second,
|
267
|
+
) from None
|
254
268
|
field_names_to_values = {f.name: v for f, v in fields_to_values.items()}
|
255
269
|
default = {
|
256
270
|
f.name
|
@@ -261,13 +275,50 @@ def mapping_to_dataclass(
|
|
261
275
|
have = set(field_names_to_values) | default
|
262
276
|
missing = {f.name for f in fields_use} - have
|
263
277
|
if len(missing) >= 1:
|
264
|
-
raise
|
278
|
+
raise _MappingToDataClassMissingValuesError(cls=cls, fields=missing)
|
265
279
|
return cls(**field_names_to_values)
|
266
280
|
|
267
281
|
|
268
282
|
@dataclass(kw_only=True, slots=True)
|
269
283
|
class MappingToDataclassError(Exception, Generic[TDataclass]):
|
270
284
|
cls: type[TDataclass]
|
285
|
+
|
286
|
+
|
287
|
+
@dataclass(kw_only=True, slots=True)
|
288
|
+
class _MappingToDataClassEmptyError(MappingToDataclassError[TDataclass]):
|
289
|
+
key: str
|
290
|
+
head: bool = False
|
291
|
+
case_sensitive: bool = False
|
292
|
+
|
293
|
+
@override
|
294
|
+
def __str__(self) -> str:
|
295
|
+
return _empty_error_str(
|
296
|
+
self.cls, self.key, head=self.head, case_sensitive=self.case_sensitive
|
297
|
+
)
|
298
|
+
|
299
|
+
|
300
|
+
@dataclass(kw_only=True, slots=True)
|
301
|
+
class _MappingToDataClassNonUniqueError(MappingToDataclassError[TDataclass]):
|
302
|
+
key: str
|
303
|
+
head: bool = False
|
304
|
+
case_sensitive: bool = False
|
305
|
+
first: str
|
306
|
+
second: str
|
307
|
+
|
308
|
+
@override
|
309
|
+
def __str__(self) -> str:
|
310
|
+
return _non_unique_error_str(
|
311
|
+
self.cls,
|
312
|
+
self.key,
|
313
|
+
self.first,
|
314
|
+
self.second,
|
315
|
+
head=self.head,
|
316
|
+
case_sensitive=self.case_sensitive,
|
317
|
+
)
|
318
|
+
|
319
|
+
|
320
|
+
@dataclass(kw_only=True, slots=True)
|
321
|
+
class _MappingToDataClassMissingValuesError(MappingToDataclassError[TDataclass]):
|
271
322
|
fields: AbstractSet[str]
|
272
323
|
|
273
324
|
@override
|
@@ -307,11 +358,11 @@ def one_field(
|
|
307
358
|
try:
|
308
359
|
name = one_str(mapping, key, head=head, case_sensitive=case_sensitive)
|
309
360
|
except OneStrEmptyError:
|
310
|
-
raise
|
361
|
+
raise _OneFieldEmptyError(
|
311
362
|
cls=cls, key=key, head=head, case_sensitive=case_sensitive
|
312
363
|
) from None
|
313
364
|
except OneStrNonUniqueError as error:
|
314
|
-
raise
|
365
|
+
raise _OneFieldNonUniqueError(
|
315
366
|
cls=cls,
|
316
367
|
key=key,
|
317
368
|
head=head,
|
@@ -331,46 +382,29 @@ class OneFieldError(Exception, Generic[TDataclass]):
|
|
331
382
|
|
332
383
|
|
333
384
|
@dataclass(kw_only=True, slots=True)
|
334
|
-
class
|
385
|
+
class _OneFieldEmptyError(OneFieldError[TDataclass]):
|
335
386
|
@override
|
336
387
|
def __str__(self) -> str:
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
tail = f"a field {self.key!r}"
|
341
|
-
case False, False:
|
342
|
-
tail = f"a field {self.key!r} (modulo case)"
|
343
|
-
case True, True:
|
344
|
-
tail = f"any field starting with {self.key!r}"
|
345
|
-
case True, False:
|
346
|
-
tail = f"any field starting with {self.key!r} (modulo case)"
|
347
|
-
case _ as never:
|
348
|
-
assert_never(never)
|
349
|
-
return f"{head} {tail}"
|
388
|
+
return _empty_error_str(
|
389
|
+
self.cls, self.key, head=self.head, case_sensitive=self.case_sensitive
|
390
|
+
)
|
350
391
|
|
351
392
|
|
352
393
|
@dataclass(kw_only=True, slots=True)
|
353
|
-
class
|
394
|
+
class _OneFieldNonUniqueError(OneFieldError[TDataclass]):
|
354
395
|
first: str
|
355
396
|
second: str
|
356
397
|
|
357
398
|
@override
|
358
399
|
def __str__(self) -> str:
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
case True, True:
|
368
|
-
mid = f"exactly one field starting with {self.key!r}"
|
369
|
-
case True, False:
|
370
|
-
mid = f"exactly one field starting with {self.key!r} (modulo case)"
|
371
|
-
case _ as never:
|
372
|
-
assert_never(never)
|
373
|
-
return f"{head} {mid}; got {self.first!r}, {self.second!r} and perhaps more"
|
400
|
+
return _non_unique_error_str(
|
401
|
+
self.cls,
|
402
|
+
self.key,
|
403
|
+
self.first,
|
404
|
+
self.second,
|
405
|
+
head=self.head,
|
406
|
+
case_sensitive=self.case_sensitive,
|
407
|
+
)
|
374
408
|
|
375
409
|
|
376
410
|
##
|
@@ -487,17 +521,31 @@ def parse_dataclass(
|
|
487
521
|
cls, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
|
488
522
|
)
|
489
523
|
)
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
524
|
+
try:
|
525
|
+
fields_to_serializes = str_mapping_to_field_mapping(
|
526
|
+
cls,
|
527
|
+
keys_to_serializes,
|
528
|
+
fields=fields,
|
529
|
+
globalns=globalns,
|
530
|
+
localns=localns,
|
531
|
+
warn_name_errors=warn_name_errors,
|
532
|
+
head=head,
|
533
|
+
case_sensitive=case_sensitive,
|
534
|
+
allow_extra=allow_extra_keys,
|
535
|
+
)
|
536
|
+
except _StrMappingToFieldMappingEmptyError as error:
|
537
|
+
raise _ParseDataClassStrMappingToFieldMappingEmptyError(
|
538
|
+
cls=cls, key=error.key, head=head, case_sensitive=case_sensitive
|
539
|
+
) from None
|
540
|
+
except _StrMappingToFieldMappingNonUniqueError as error:
|
541
|
+
raise _ParseDataClassStrMappingToFieldMappingNonUniqueError(
|
542
|
+
cls=cls,
|
543
|
+
key=error.key,
|
544
|
+
head=head,
|
545
|
+
case_sensitive=case_sensitive,
|
546
|
+
first=error.first,
|
547
|
+
second=error.second,
|
548
|
+
) from None
|
501
549
|
field_names_to_values = {
|
502
550
|
f.name: _parse_dataclass_parse_text(
|
503
551
|
f,
|
@@ -511,17 +559,20 @@ def parse_dataclass(
|
|
511
559
|
)
|
512
560
|
for f, t in fields_to_serializes.items()
|
513
561
|
}
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
562
|
+
try:
|
563
|
+
return mapping_to_dataclass(
|
564
|
+
cls,
|
565
|
+
field_names_to_values,
|
566
|
+
fields=fields,
|
567
|
+
globalns=globalns,
|
568
|
+
localns=localns,
|
569
|
+
warn_name_errors=warn_name_errors,
|
570
|
+
head=head,
|
571
|
+
case_sensitive=case_sensitive,
|
572
|
+
allow_extra=allow_extra_keys,
|
573
|
+
)
|
574
|
+
except _MappingToDataClassMissingValuesError as error:
|
575
|
+
raise _ParseDataClassMissingValuesError(cls=cls, fields=error.fields) from None
|
525
576
|
|
526
577
|
|
527
578
|
def _parse_dataclass_split_key_value_pairs(
|
@@ -549,7 +600,7 @@ def _parse_dataclass_split_key_value_pairs(
|
|
549
600
|
) from None
|
550
601
|
except _SplitKeyValuePairsDuplicateKeysError as error:
|
551
602
|
raise _ParseDataClassSplitKeyValuePairsDuplicateKeysError(
|
552
|
-
|
603
|
+
cls=cls, counts=error.counts
|
553
604
|
) from None
|
554
605
|
|
555
606
|
|
@@ -579,25 +630,28 @@ def _parse_dataclass_parse_text(
|
|
579
630
|
raise _ParseDataClassTextParseError(cls=cls, field=field, text=text) from None
|
580
631
|
except _ParseObjectExtraNonUniqueError as error:
|
581
632
|
raise _ParseDataClassTextExtraNonUniqueError(
|
582
|
-
cls=cls, field=field,
|
633
|
+
cls=cls, field=field, first=error.first, second=error.second
|
583
634
|
) from None
|
584
635
|
|
585
636
|
|
586
637
|
@dataclass(kw_only=True, slots=True)
|
587
638
|
class ParseDataClassError(Exception, Generic[TDataclass]):
|
588
|
-
text: str
|
589
639
|
cls: type[TDataclass]
|
590
640
|
|
591
641
|
|
592
642
|
@dataclass(kw_only=True, slots=True)
|
593
|
-
class _ParseDataClassSplitKeyValuePairsSplitError(ParseDataClassError):
|
643
|
+
class _ParseDataClassSplitKeyValuePairsSplitError(ParseDataClassError[TDataclass]):
|
644
|
+
text: str
|
645
|
+
|
594
646
|
@override
|
595
647
|
def __str__(self) -> str:
|
596
648
|
return f"Unable to construct {get_class_name(self.cls)!r}; failed to split key-value pair {self.text!r}"
|
597
649
|
|
598
650
|
|
599
651
|
@dataclass(kw_only=True, slots=True)
|
600
|
-
class _ParseDataClassSplitKeyValuePairsDuplicateKeysError(
|
652
|
+
class _ParseDataClassSplitKeyValuePairsDuplicateKeysError(
|
653
|
+
ParseDataClassError[TDataclass]
|
654
|
+
):
|
601
655
|
counts: Mapping[str, int]
|
602
656
|
|
603
657
|
@override
|
@@ -608,6 +662,7 @@ class _ParseDataClassSplitKeyValuePairsDuplicateKeysError(ParseDataClassError):
|
|
608
662
|
@dataclass(kw_only=True, slots=True)
|
609
663
|
class _ParseDataClassTextParseError(ParseDataClassError[TDataclass]):
|
610
664
|
field: _YieldFieldsClass[Any]
|
665
|
+
text: str
|
611
666
|
|
612
667
|
@override
|
613
668
|
def __str__(self) -> str:
|
@@ -625,6 +680,56 @@ class _ParseDataClassTextExtraNonUniqueError(ParseDataClassError[TDataclass]):
|
|
625
680
|
return f"Unable to construct {get_class_name(self.cls)!r} since the field {self.field.name!r} of type {self.field.type_!r} must contain exactly one parent class in `extra`; got {self.first!r}, {self.second!r} and perhaps more"
|
626
681
|
|
627
682
|
|
683
|
+
@dataclass(kw_only=True, slots=True)
|
684
|
+
class _ParseDataClassStrMappingToFieldMappingEmptyError(
|
685
|
+
ParseDataClassError[TDataclass]
|
686
|
+
):
|
687
|
+
key: str
|
688
|
+
head: bool = False
|
689
|
+
case_sensitive: bool = False
|
690
|
+
|
691
|
+
@override
|
692
|
+
def __str__(self) -> str:
|
693
|
+
head = f"Unable to construct {get_class_name(self.cls)!r} since it does not contain"
|
694
|
+
tail = _empty_error_str_core(
|
695
|
+
self.key, head=self.head, case_sensitive=self.case_sensitive
|
696
|
+
)
|
697
|
+
return f"{head} {tail}"
|
698
|
+
|
699
|
+
|
700
|
+
@dataclass(kw_only=True, slots=True)
|
701
|
+
class _ParseDataClassStrMappingToFieldMappingNonUniqueError(
|
702
|
+
ParseDataClassError[TDataclass]
|
703
|
+
):
|
704
|
+
key: str
|
705
|
+
head: bool = False
|
706
|
+
case_sensitive: bool = False
|
707
|
+
first: str
|
708
|
+
second: str
|
709
|
+
|
710
|
+
@override
|
711
|
+
def __str__(self) -> str:
|
712
|
+
head = f"Unable to construct {get_class_name(self.cls)!r} since it must contain"
|
713
|
+
tail = _non_unique_error_str_core(
|
714
|
+
self.key,
|
715
|
+
self.first,
|
716
|
+
self.second,
|
717
|
+
head=self.head,
|
718
|
+
case_sensitive=self.case_sensitive,
|
719
|
+
)
|
720
|
+
return f"{head} {tail}"
|
721
|
+
|
722
|
+
|
723
|
+
@dataclass(kw_only=True, slots=True)
|
724
|
+
class _ParseDataClassMissingValuesError(ParseDataClassError[TDataclass]):
|
725
|
+
fields: AbstractSet[str]
|
726
|
+
|
727
|
+
@override
|
728
|
+
def __str__(self) -> str:
|
729
|
+
desc = ", ".join(map(repr, sorted(self.fields)))
|
730
|
+
return f"Unable to construct {get_class_name(self.cls)!r}; missing values for {desc}"
|
731
|
+
|
732
|
+
|
628
733
|
##
|
629
734
|
|
630
735
|
|
@@ -655,36 +760,55 @@ def str_mapping_to_field_mapping(
|
|
655
760
|
head=head,
|
656
761
|
case_sensitive=case_sensitive,
|
657
762
|
)
|
658
|
-
except
|
763
|
+
except _OneFieldEmptyError:
|
659
764
|
if not allow_extra:
|
660
|
-
raise
|
765
|
+
raise _StrMappingToFieldMappingEmptyError(
|
661
766
|
cls=cls, key=key, head=head, case_sensitive=case_sensitive
|
662
767
|
) from None
|
768
|
+
except _OneFieldNonUniqueError as error:
|
769
|
+
raise _StrMappingToFieldMappingNonUniqueError(
|
770
|
+
cls=cls,
|
771
|
+
key=key,
|
772
|
+
head=head,
|
773
|
+
case_sensitive=case_sensitive,
|
774
|
+
first=error.first,
|
775
|
+
second=error.second,
|
776
|
+
) from None
|
663
777
|
return {field: mapping[key] for key, field in keys_to_fields.items()}
|
664
778
|
|
665
779
|
|
666
780
|
@dataclass(kw_only=True, slots=True)
|
667
|
-
class StrMappingToFieldMappingError(Exception):
|
668
|
-
cls: type[
|
781
|
+
class StrMappingToFieldMappingError(Exception, Generic[TDataclass]):
|
782
|
+
cls: type[TDataclass]
|
669
783
|
key: str
|
670
784
|
head: bool = False
|
671
785
|
case_sensitive: bool = False
|
672
786
|
|
787
|
+
|
788
|
+
@dataclass(kw_only=True, slots=True)
|
789
|
+
class _StrMappingToFieldMappingEmptyError(StrMappingToFieldMappingError):
|
673
790
|
@override
|
674
791
|
def __str__(self) -> str:
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
return
|
792
|
+
return _empty_error_str(
|
793
|
+
self.cls, self.key, head=self.head, case_sensitive=self.case_sensitive
|
794
|
+
)
|
795
|
+
|
796
|
+
|
797
|
+
@dataclass(kw_only=True, slots=True)
|
798
|
+
class _StrMappingToFieldMappingNonUniqueError(StrMappingToFieldMappingError):
|
799
|
+
first: str
|
800
|
+
second: str
|
801
|
+
|
802
|
+
@override
|
803
|
+
def __str__(self) -> str:
|
804
|
+
return _non_unique_error_str(
|
805
|
+
self.cls,
|
806
|
+
self.key,
|
807
|
+
self.first,
|
808
|
+
self.second,
|
809
|
+
head=self.head,
|
810
|
+
case_sensitive=self.case_sensitive,
|
811
|
+
)
|
688
812
|
|
689
813
|
|
690
814
|
##
|
@@ -852,11 +976,81 @@ class YieldFieldsError(Exception):
|
|
852
976
|
|
853
977
|
##
|
854
978
|
|
979
|
+
|
980
|
+
def _empty_error_str(
|
981
|
+
cls: type[Dataclass],
|
982
|
+
key: str,
|
983
|
+
/,
|
984
|
+
*,
|
985
|
+
head: bool = False,
|
986
|
+
case_sensitive: bool = False,
|
987
|
+
) -> str:
|
988
|
+
head_msg = f"Dataclass {get_class_name(cls)!r} does not contain"
|
989
|
+
tail_msg = _empty_error_str_core(key, head=head, case_sensitive=case_sensitive)
|
990
|
+
return f"{head_msg} {tail_msg}"
|
991
|
+
|
992
|
+
|
993
|
+
def _empty_error_str_core(
|
994
|
+
key: str, /, *, head: bool = False, case_sensitive: bool = False
|
995
|
+
) -> str:
|
996
|
+
match head, case_sensitive:
|
997
|
+
case False, True:
|
998
|
+
return f"a field {key!r}"
|
999
|
+
case False, False:
|
1000
|
+
return f"a field {key!r} (modulo case)"
|
1001
|
+
case True, True:
|
1002
|
+
return f"any field starting with {key!r}"
|
1003
|
+
case True, False:
|
1004
|
+
return f"any field starting with {key!r} (modulo case)"
|
1005
|
+
case _ as never:
|
1006
|
+
assert_never(never)
|
1007
|
+
|
1008
|
+
|
1009
|
+
def _non_unique_error_str(
|
1010
|
+
cls: type[Dataclass],
|
1011
|
+
key: str,
|
1012
|
+
first: str,
|
1013
|
+
second: str,
|
1014
|
+
/,
|
1015
|
+
*,
|
1016
|
+
head: bool = False,
|
1017
|
+
case_sensitive: bool = False,
|
1018
|
+
) -> str:
|
1019
|
+
head_msg = f"Dataclass {get_class_name(cls)!r} must contain"
|
1020
|
+
tail_msg = _non_unique_error_str_core(
|
1021
|
+
key, first, second, head=head, case_sensitive=case_sensitive
|
1022
|
+
)
|
1023
|
+
return f"{head_msg} {tail_msg}"
|
1024
|
+
|
1025
|
+
|
1026
|
+
def _non_unique_error_str_core(
|
1027
|
+
key: str,
|
1028
|
+
first: str,
|
1029
|
+
second: str,
|
1030
|
+
/,
|
1031
|
+
*,
|
1032
|
+
head: bool = False,
|
1033
|
+
case_sensitive: bool = False,
|
1034
|
+
) -> str:
|
1035
|
+
match head, case_sensitive:
|
1036
|
+
case False, True:
|
1037
|
+
raise ImpossibleCaseError( # pragma: no cover
|
1038
|
+
case=[f"{head=}", f"{case_sensitive=}"]
|
1039
|
+
)
|
1040
|
+
case False, False:
|
1041
|
+
head_msg = f"field {key!r} exactly once (modulo case)"
|
1042
|
+
case True, True:
|
1043
|
+
head_msg = f"exactly one field starting with {key!r}"
|
1044
|
+
case True, False:
|
1045
|
+
head_msg = f"exactly one field starting with {key!r} (modulo case)"
|
1046
|
+
case _ as never:
|
1047
|
+
assert_never(never)
|
1048
|
+
return f"{head_msg}; got {first!r}, {second!r} and perhaps more"
|
1049
|
+
|
1050
|
+
|
855
1051
|
__all__ = [
|
856
1052
|
"MappingToDataclassError",
|
857
|
-
"OneFieldEmptyError",
|
858
1053
|
"OneFieldError",
|
859
|
-
"OneFieldNonUniqueError",
|
860
1054
|
"ParseDataClassError",
|
861
1055
|
"StrMappingToFieldMappingError",
|
862
1056
|
"YieldFieldsError",
|
utilities/parse.py
CHANGED
@@ -286,9 +286,7 @@ def _parse_object_extra(cls: Any, text: str, extra: ParseObjectExtra, /) -> Any:
|
|
286
286
|
parser = extra[cls]
|
287
287
|
except KeyError:
|
288
288
|
try:
|
289
|
-
parser = one(
|
290
|
-
p for c, p in extra.items() if (cls is c) or is_subclass_gen(cls, c)
|
291
|
-
)
|
289
|
+
parser = one(p for c, p in extra.items() if is_subclass_gen(cls, c))
|
292
290
|
except (OneEmptyError, TypeError):
|
293
291
|
raise _ParseObjectParseError(type_=cls, text=text) from None
|
294
292
|
except OneNonUniqueError as error:
|
utilities/python_dotenv.py
CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, override
|
|
6
6
|
|
7
7
|
from dotenv import dotenv_values
|
8
8
|
|
9
|
-
from utilities.dataclasses import
|
9
|
+
from utilities.dataclasses import _ParseDataClassMissingValuesError, parse_dataclass
|
10
10
|
from utilities.git import get_repo_root
|
11
11
|
from utilities.iterables import MergeStrMappingsError, merge_str_mappings
|
12
12
|
from utilities.pathlib import PWD
|
@@ -61,7 +61,7 @@ def load_settings(
|
|
61
61
|
allow_extra_keys=True,
|
62
62
|
extra_parsers=extra_parsers,
|
63
63
|
)
|
64
|
-
except
|
64
|
+
except _ParseDataClassMissingValuesError as error:
|
65
65
|
raise _LoadSettingsMissingKeysError(path=path, fields=error.fields) from None
|
66
66
|
|
67
67
|
|
utilities/typing.py
CHANGED
@@ -245,6 +245,8 @@ def is_instance_gen(obj: Any, type_: Any, /) -> bool:
|
|
245
245
|
return (len(obj) == len(type_args)) and all(
|
246
246
|
is_instance_gen(o, t) for o, t in zip(obj, type_args, strict=True)
|
247
247
|
)
|
248
|
+
if isinstance(obj, tuple) is not is_tuple_type(type_):
|
249
|
+
return False
|
248
250
|
# basic
|
249
251
|
if isinstance(type_, type):
|
250
252
|
return any(_is_instance_gen_type(obj, t) for t in get_type_classes(type_))
|
File without changes
|
File without changes
|