dycw-utilities 0.109.1__py3-none-any.whl → 0.109.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.109.1
3
+ Version: 0.109.3
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=hXFp40lgEbzHJocIbjY5_v_hKGMzhE6Os9g06SFuZME,60
1
+ utilities/__init__.py,sha256=XHS4BHXBA86Rjy07zPtJwGLkB2QC4JUa0pQjTRZS2zE,60
2
2
  utilities/altair.py,sha256=NSyDsm8QlkAGmsGdxVwCkHnPxt_35yJBa9Lg7bz9Ays,9054
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=12Cxq0ZXFuc4lPD6Y92Ny-b25C0EnkAZ3Hz59tetcbQ,21709
14
+ utilities/dataclasses.py,sha256=4siQUalzjYckhJ7R9Cn4Pmerxzx18_mjPIwUmeuwwKs,23049
15
15
  utilities/datetime.py,sha256=GOs-MIEW_A49kzqa1yhIoeNeSqqPVgGO-h2AThtgTDk,37326
16
16
  utilities/enum.py,sha256=HoRwVCWzsnH0vpO9ZEcAAIZLMv0Sn2vJxxA4sYMQgDs,5793
17
17
  utilities/errors.py,sha256=BtSNP0JC3ik536ddPyTerLomCRJV9f6kdMe6POz0QHM,361
@@ -38,22 +38,22 @@ utilities/more_itertools.py,sha256=CPUxrMAcTwRxbzbhiqPKi3Xx9hxqI0t6gkWjutaibGk,5
38
38
  utilities/numpy.py,sha256=rA1b0_GkBUSMjnv77tinRM70KRnkcmZxI9xbrsXFDRg,21819
39
39
  utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
40
40
  utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
41
- utilities/orjson.py,sha256=DW5pOpMyrR5Q8caQYly9AqRPazDBqrWv5GRWfULqka4,36291
41
+ utilities/orjson.py,sha256=Wj5pzG_VdgoAy14a7Luhem-BgYrRtRFvvl_POiszRd0,36930
42
42
  utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
43
- utilities/parse.py,sha256=yLLH51VNwmcWbEvwqh6M-weWt7NIayd7No67Oe80S3k,4585
43
+ utilities/parse.py,sha256=-rnx9qBKo9UZbE1HtvBwYOs4vF7YiJiciPAM6IKmfRc,5183
44
44
  utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
45
45
  utilities/period.py,sha256=ikHXsWtDLr553cfH6p9mMaiCnIAP69B7q84ckWV3HaA,10884
46
46
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
47
47
  utilities/platform.py,sha256=NU7ycTvAXAG-fdYmDXaM1m4EOml2cGiaYwaUzfzSqyU,1767
48
- utilities/polars.py,sha256=ZXiHLkn6CbRh0_e0db5KRjHPU0LAedwzGno7k9fsiIo,48917
48
+ utilities/polars.py,sha256=USK_Rck8nmFYg2Rs-akqN9jV4w52lpz4rgkWUMQdLMk,49087
49
49
  utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
50
50
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  utilities/pydantic.py,sha256=f6qtR5mO2YMuyvNmbaEj5YeD9eGA4YYfb7Bjzh9jUs0,1845
52
52
  utilities/pyinstrument.py,sha256=ROq2txPwbe2ZUuYJ2IDNbfT97lu2ca0v5_C_yn6sSlM,800
53
53
  utilities/pyrsistent.py,sha256=TLJfiiKO4cKNU_pCoM3zDqmSM421qpuoaeaBNnyC_Ac,2489
54
54
  utilities/pytest.py,sha256=85QUax4g2VBBAqAHtM9wekcSLB7_9O8AKFTaCshztL8,7989
55
- utilities/pytest_regressions.py,sha256=Kp1NS_cyXvBFqyiF_oSzYmSJzIOdAZ0SFcSGmbL_UtI,5001
56
- utilities/python_dotenv.py,sha256=-d2bQ3Ayyv9JUK59k6F3-mTzQmb2SV0HzqG9fpsD8C8,2976
55
+ utilities/pytest_regressions.py,sha256=-SVT9647Dg6-JcdsiaDKXe3NdOmmrvGevLKWwGjxq3c,5088
56
+ utilities/python_dotenv.py,sha256=10DHEB7AVeZqH7I4wr6nACdJQYQJanlEj6EsyLvCN9w,3059
57
57
  utilities/random.py,sha256=lYdjgxB7GCfU_fwFVl5U-BIM_HV3q6_urL9byjrwDM8,4157
58
58
  utilities/re.py,sha256=5J4d8VwIPFVrX2Eb8zfoxImDv7IwiN_U7mJ07wR2Wvs,3958
59
59
  utilities/redis.py,sha256=CsDQqc9V6ASLzLQwtbQXZQEndyG9pJiCOhPlPeszt7Y,21203
@@ -75,7 +75,7 @@ utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
75
75
  utilities/timer.py,sha256=Rkc49KSpHuC8s7vUxGO9DU55U9I6yDKnchsQqrUCVBs,4075
76
76
  utilities/traceback.py,sha256=KwHPLdEbdj0fFhXo8MBfxcvem8A-VXYDwFMNJ6f0cTM,27328
77
77
  utilities/types.py,sha256=QK8kgH80TJdh_vktaZHrCEk7f1f8kHiDr8dJlK8aSac,17814
78
- utilities/typing.py,sha256=qX0o3NfImTXt40iGpERIVjuOQGb0XINfWUOxMmlh1s4,5157
78
+ utilities/typing.py,sha256=gLg4EbE1FX52fJ1d3ji4i08qolwu9qgWt8w_w_Y5DTk,5512
79
79
  utilities/tzdata.py,sha256=2ZsPmhTVM9Ptrxb4QrWKtKOB9RiH8IOO-A1u7ULdVbg,176
80
80
  utilities/tzlocal.py,sha256=42BCquGF54oIqIKe5RGziP4K8Nbm3Ey7uqcNn6m5ge8,534
81
81
  utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
@@ -84,7 +84,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
84
84
  utilities/whenever.py,sha256=5x2t47VJmJRWcd_NLFy54NkB3uom-XQYxEbLtEfL1bs,17775
85
85
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
86
86
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
87
- dycw_utilities-0.109.1.dist-info/METADATA,sha256=dCcv9ZcxodWQ1Ze4u30ZUglTEC7NipHnFMOMN9zv6Oc,13004
88
- dycw_utilities-0.109.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
- dycw_utilities-0.109.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
- dycw_utilities-0.109.1.dist-info/RECORD,,
87
+ dycw_utilities-0.109.3.dist-info/METADATA,sha256=TF66WIpVFJnaJA80lxa_GKBZKZUzfz8VWd95FQW8v4A,13004
88
+ dycw_utilities-0.109.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
89
+ dycw_utilities-0.109.3.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
90
+ dycw_utilities-0.109.3.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.1"
3
+ __version__ = "0.109.3"
utilities/dataclasses.py CHANGED
@@ -48,6 +48,7 @@ def dataclass_repr(
48
48
  exclude: Iterable[str] | None = None,
49
49
  globalns: StrMapping | None = None,
50
50
  localns: StrMapping | None = None,
51
+ warn_name_errors: bool = False,
51
52
  rel_tol: float | None = None,
52
53
  abs_tol: float | None = None,
53
54
  extra: Mapping[type[_T], Callable[[_T, _T], bool]] | None = None,
@@ -56,7 +57,9 @@ def dataclass_repr(
56
57
  ) -> str:
57
58
  """Repr a dataclass, without its defaults."""
58
59
  out: dict[str, str] = {}
59
- for fld in yield_fields(obj, globalns=globalns, localns=localns):
60
+ for fld in yield_fields(
61
+ obj, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
62
+ ):
60
63
  if (
61
64
  fld.keep(
62
65
  include=include,
@@ -76,6 +79,7 @@ def dataclass_repr(
76
79
  exclude=exclude,
77
80
  globalns=globalns,
78
81
  localns=localns,
82
+ warn_name_errors=warn_name_errors,
79
83
  rel_tol=rel_tol,
80
84
  abs_tol=abs_tol,
81
85
  extra=extra,
@@ -90,6 +94,7 @@ def dataclass_repr(
90
94
  exclude=exclude,
91
95
  globalns=globalns,
92
96
  localns=localns,
97
+ warn_name_errors=warn_name_errors,
93
98
  rel_tol=rel_tol,
94
99
  abs_tol=abs_tol,
95
100
  extra=extra,
@@ -122,6 +127,7 @@ def dataclass_to_dict(
122
127
  exclude: Iterable[str] | None = None,
123
128
  globalns: StrMapping | None = None,
124
129
  localns: StrMapping | None = None,
130
+ warn_name_errors: bool = False,
125
131
  rel_tol: float | None = None,
126
132
  abs_tol: float | None = None,
127
133
  extra: Mapping[type[_T], Callable[[_T, _T], bool]] | None = None,
@@ -131,7 +137,9 @@ def dataclass_to_dict(
131
137
  ) -> StrMapping:
132
138
  """Convert a dataclass to a dictionary."""
133
139
  out: StrMapping = {}
134
- for fld in yield_fields(obj, globalns=globalns, localns=localns):
140
+ for fld in yield_fields(
141
+ obj, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
142
+ ):
135
143
  if fld.keep(
136
144
  include=include,
137
145
  exclude=exclude,
@@ -146,6 +154,7 @@ def dataclass_to_dict(
146
154
  fld.value,
147
155
  globalns=globalns,
148
156
  localns=localns,
157
+ warn_name_errors=warn_name_errors,
149
158
  rel_tol=rel_tol,
150
159
  abs_tol=abs_tol,
151
160
  extra=extra,
@@ -159,6 +168,7 @@ def dataclass_to_dict(
159
168
  v,
160
169
  globalns=globalns,
161
170
  localns=localns,
171
+ warn_name_errors=warn_name_errors,
162
172
  rel_tol=rel_tol,
163
173
  abs_tol=abs_tol,
164
174
  extra=extra,
@@ -189,13 +199,21 @@ def mapping_to_dataclass(
189
199
  fields: Iterable[_YieldFieldsClass[Any]] | None = None,
190
200
  globalns: StrMapping | None = None,
191
201
  localns: StrMapping | None = None,
202
+ warn_name_errors: bool = False,
192
203
  head: bool = False,
193
204
  case_sensitive: bool = False,
194
205
  allow_extra: bool = False,
195
206
  ) -> TDataclass:
196
207
  """Construct a dataclass from a mapping."""
197
208
  if fields is None:
198
- fields_use = list(yield_fields(cls, globalns=globalns, localns=localns))
209
+ fields_use = list(
210
+ yield_fields(
211
+ cls,
212
+ globalns=globalns,
213
+ localns=localns,
214
+ warn_name_errors=warn_name_errors,
215
+ )
216
+ )
199
217
  else:
200
218
  fields_use = fields
201
219
  fields_to_values = str_mapping_to_field_mapping(
@@ -204,6 +222,7 @@ def mapping_to_dataclass(
204
222
  fields=fields_use,
205
223
  globalns=globalns,
206
224
  localns=localns,
225
+ warn_name_errors=warn_name_errors,
207
226
  head=head,
208
227
  case_sensitive=case_sensitive,
209
228
  allow_extra=allow_extra,
@@ -244,12 +263,20 @@ def one_field(
244
263
  fields: Iterable[_YieldFieldsClass[Any]] | None = None,
245
264
  globalns: StrMapping | None = None,
246
265
  localns: StrMapping | None = None,
266
+ warn_name_errors: bool = False,
247
267
  head: bool = False,
248
268
  case_sensitive: bool = False,
249
269
  ) -> _YieldFieldsClass[Any]:
250
270
  """Get the unique field a key matches to."""
251
271
  if fields is None:
252
- fields_use = list(yield_fields(cls, globalns=globalns, localns=localns))
272
+ fields_use = list(
273
+ yield_fields(
274
+ cls,
275
+ globalns=globalns,
276
+ localns=localns,
277
+ warn_name_errors=warn_name_errors,
278
+ )
279
+ )
253
280
  else:
254
281
  fields_use = fields
255
282
  mapping = {f.name: f for f in fields_use}
@@ -362,6 +389,7 @@ def str_mapping_to_field_mapping(
362
389
  fields: Iterable[_YieldFieldsClass[Any]] | None = None,
363
390
  globalns: StrMapping | None = None,
364
391
  localns: StrMapping | None = None,
392
+ warn_name_errors: bool = False,
365
393
  head: bool = False,
366
394
  case_sensitive: bool = False,
367
395
  allow_extra: bool = False,
@@ -376,6 +404,7 @@ def str_mapping_to_field_mapping(
376
404
  fields=fields,
377
405
  globalns=globalns,
378
406
  localns=localns,
407
+ warn_name_errors=warn_name_errors,
379
408
  head=head,
380
409
  case_sensitive=case_sensitive,
381
410
  )
@@ -421,6 +450,7 @@ def text_to_dataclass(
421
450
  *,
422
451
  globalns: StrMapping | None = None,
423
452
  localns: StrMapping | None = None,
453
+ warn_name_errors: bool = False,
424
454
  head: bool = False,
425
455
  case_sensitive: bool = False,
426
456
  allow_extra: bool = False,
@@ -433,13 +463,18 @@ def text_to_dataclass(
433
463
  ...
434
464
  case _ as never:
435
465
  assert_never(never)
436
- fields = list(yield_fields(cls, globalns=globalns, localns=localns))
466
+ fields = list(
467
+ yield_fields(
468
+ cls, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
469
+ )
470
+ )
437
471
  fields_to_serializes = str_mapping_to_field_mapping(
438
472
  cls,
439
473
  keys_to_serializes,
440
474
  fields=fields,
441
475
  globalns=globalns,
442
476
  localns=localns,
477
+ warn_name_errors=warn_name_errors,
443
478
  head=head,
444
479
  case_sensitive=case_sensitive,
445
480
  allow_extra=allow_extra,
@@ -454,6 +489,7 @@ def text_to_dataclass(
454
489
  fields=fields,
455
490
  globalns=globalns,
456
491
  localns=localns,
492
+ warn_name_errors=warn_name_errors,
457
493
  head=head,
458
494
  case_sensitive=case_sensitive,
459
495
  allow_extra=allow_extra,
@@ -525,6 +561,7 @@ def yield_fields(
525
561
  *,
526
562
  globalns: StrMapping | None = None,
527
563
  localns: StrMapping | None = None,
564
+ warn_name_errors: bool = False,
528
565
  ) -> Iterator[_YieldFieldsInstance[Any]]: ...
529
566
  @overload
530
567
  def yield_fields(
@@ -533,6 +570,7 @@ def yield_fields(
533
570
  *,
534
571
  globalns: StrMapping | None = None,
535
572
  localns: StrMapping | None = None,
573
+ warn_name_errors: bool = False,
536
574
  ) -> Iterator[_YieldFieldsClass[Any]]: ...
537
575
  def yield_fields(
538
576
  obj: Dataclass | type[Dataclass],
@@ -540,10 +578,16 @@ def yield_fields(
540
578
  *,
541
579
  globalns: StrMapping | None = None,
542
580
  localns: StrMapping | None = None,
581
+ warn_name_errors: bool = False,
543
582
  ) -> Iterator[_YieldFieldsInstance[Any]] | Iterator[_YieldFieldsClass[Any]]:
544
583
  """Yield the fields of a dataclass."""
545
584
  if is_dataclass_instance(obj):
546
- for field in yield_fields(type(obj), globalns=globalns, localns=localns):
585
+ for field in yield_fields(
586
+ type(obj),
587
+ globalns=globalns,
588
+ localns=localns,
589
+ warn_name_errors=warn_name_errors,
590
+ ):
547
591
  yield _YieldFieldsInstance(
548
592
  name=field.name,
549
593
  value=getattr(obj, field.name),
@@ -558,7 +602,9 @@ def yield_fields(
558
602
  kw_only=field.kw_only,
559
603
  )
560
604
  elif is_dataclass_class(obj):
561
- hints = get_type_hints(obj, globalns=globalns, localns=localns)
605
+ hints = get_type_hints(
606
+ obj, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
607
+ )
562
608
  for field in fields(obj):
563
609
  if isinstance(field.type, type):
564
610
  type_ = field.type
utilities/orjson.py CHANGED
@@ -114,6 +114,7 @@ def serialize(
114
114
  before: Callable[[Any], Any] | None = None,
115
115
  globalns: StrMapping | None = None,
116
116
  localns: StrMapping | None = None,
117
+ warn_name_errors: bool = False,
117
118
  dataclass_hook: _DataclassHook | None = None,
118
119
  dataclass_defaults: bool = False,
119
120
  ) -> bytes:
@@ -123,6 +124,7 @@ def serialize(
123
124
  before=before,
124
125
  globalns=globalns,
125
126
  localns=localns,
127
+ warn_name_errors=warn_name_errors,
126
128
  dataclass_hook=dataclass_hook,
127
129
  dataclass_defaults=dataclass_defaults,
128
130
  )
@@ -139,6 +141,7 @@ def _pre_process(
139
141
  before: Callable[[Any], Any] | None = None,
140
142
  globalns: StrMapping | None = None,
141
143
  localns: StrMapping | None = None,
144
+ warn_name_errors: bool = False,
142
145
  dataclass_hook: _DataclassHook | None = None,
143
146
  dataclass_defaults: bool = False,
144
147
  error: _ErrorMode = "raise",
@@ -150,6 +153,7 @@ def _pre_process(
150
153
  before=before,
151
154
  globalns=globalns,
152
155
  localns=localns,
156
+ warn_name_errors=warn_name_errors,
153
157
  dataclass_hook=dataclass_hook,
154
158
  dataclass_defaults=dataclass_defaults,
155
159
  error=error,
@@ -196,6 +200,7 @@ def _pre_process(
196
200
  dataclass,
197
201
  globalns=globalns,
198
202
  localns=localns,
203
+ warn_name_errors=warn_name_errors,
199
204
  final=partial(_dataclass_final, hook=dataclass_hook),
200
205
  defaults=dataclass_defaults,
201
206
  )
@@ -212,6 +217,7 @@ def _pre_process(
212
217
  before=before,
213
218
  globalns=globalns,
214
219
  localns=localns,
220
+ warn_name_errors=warn_name_errors,
215
221
  dataclass_hook=dataclass_hook,
216
222
  )
217
223
  case list() as list_:
@@ -222,6 +228,7 @@ def _pre_process(
222
228
  before=before,
223
229
  globalns=globalns,
224
230
  localns=localns,
231
+ warn_name_errors=warn_name_errors,
225
232
  dataclass_hook=dataclass_hook,
226
233
  )
227
234
  case Mapping() as mapping:
@@ -234,6 +241,7 @@ def _pre_process(
234
241
  before=before,
235
242
  globalns=globalns,
236
243
  localns=localns,
244
+ warn_name_errors=warn_name_errors,
237
245
  dataclass_hook=dataclass_hook,
238
246
  )
239
247
  case tuple() as tuple_:
@@ -244,6 +252,7 @@ def _pre_process(
244
252
  before=before,
245
253
  globalns=globalns,
246
254
  localns=localns,
255
+ warn_name_errors=warn_name_errors,
247
256
  dataclass_hook=dataclass_hook,
248
257
  )
249
258
  # other
@@ -263,6 +272,7 @@ def _pre_process_container(
263
272
  before: Callable[[Any], Any] | None = None,
264
273
  globalns: StrMapping | None = None,
265
274
  localns: StrMapping | None = None,
275
+ warn_name_errors: bool = False,
266
276
  dataclass_hook: _DataclassHook | None = None,
267
277
  dataclass_include_defaults: bool = False,
268
278
  ) -> Any:
@@ -272,6 +282,7 @@ def _pre_process_container(
272
282
  before=before,
273
283
  globalns=globalns,
274
284
  localns=localns,
285
+ warn_name_errors=warn_name_errors,
275
286
  dataclass_hook=dataclass_hook,
276
287
  dataclass_defaults=dataclass_include_defaults,
277
288
  )
@@ -716,6 +727,7 @@ class OrjsonFormatter(Formatter):
716
727
  before: Callable[[Any], Any] | None = None,
717
728
  globalns: StrMapping | None = None,
718
729
  localns: StrMapping | None = None,
730
+ warn_name_errors: bool = False,
719
731
  dataclass_hook: _DataclassHook | None = None,
720
732
  dataclass_defaults: bool = False,
721
733
  ) -> None:
@@ -723,6 +735,7 @@ class OrjsonFormatter(Formatter):
723
735
  self._before = before
724
736
  self._globalns = globalns
725
737
  self._localns = localns
738
+ self._warn_name_errors = warn_name_errors
726
739
  self._dataclass_hook = dataclass_hook
727
740
  self._dataclass_defaults = dataclass_defaults
728
741
 
@@ -752,6 +765,7 @@ class OrjsonFormatter(Formatter):
752
765
  before=self._before,
753
766
  globalns=self._globalns,
754
767
  localns=self._localns,
768
+ warn_name_errors=self._warn_name_errors,
755
769
  dataclass_hook=self._dataclass_hook,
756
770
  dataclass_defaults=self._dataclass_defaults,
757
771
  ).decode()
utilities/parse.py CHANGED
@@ -5,6 +5,7 @@ from contextlib import suppress
5
5
  from dataclasses import dataclass
6
6
  from enum import Enum
7
7
  from pathlib import Path
8
+ from re import DOTALL
8
9
  from types import NoneType
9
10
  from typing import Any, override
10
11
 
@@ -12,9 +13,10 @@ from utilities.datetime import is_subclass_date_not_datetime
12
13
  from utilities.enum import ParseEnumError, parse_enum
13
14
  from utilities.functions import is_subclass_int_not_bool
14
15
  from utilities.iterables import one, one_str
16
+ from utilities.re import ExtractGroupError, extract_group
15
17
  from utilities.sentinel import ParseSentinelError, Sentinel, parse_sentinel
16
18
  from utilities.text import ParseBoolError, ParseNoneError, parse_bool, parse_none
17
- from utilities.typing import get_args, is_literal_type, is_optional_type
19
+ from utilities.typing import get_args, is_literal_type, is_optional_type, is_tuple_type
18
20
  from utilities.version import ParseVersionError, Version, parse_version
19
21
 
20
22
 
@@ -42,6 +44,18 @@ def parse_text(
42
44
  return _parse_text_type(inner, text, case_sensitive=case_sensitive)
43
45
  except ParseTextError:
44
46
  raise ParseTextError(obj=obj, text=text) from None
47
+ if is_tuple_type(obj):
48
+ args = get_args(obj)
49
+ try:
50
+ texts = extract_group(r"^\((.*)\)$", text, flags=DOTALL).split(", ")
51
+ except ExtractGroupError:
52
+ raise ParseTextError(obj=obj, text=text) from None
53
+ if len(args) != len(texts):
54
+ raise ParseTextError(obj=obj, text=text)
55
+ return tuple(
56
+ parse_text(arg, text, case_sensitive=case_sensitive, head=head)
57
+ for arg, text in zip(args, texts, strict=True)
58
+ )
45
59
  raise ParseTextError(obj=obj, text=text) from None
46
60
 
47
61
 
utilities/polars.py CHANGED
@@ -726,10 +726,13 @@ def dataclass_to_schema(
726
726
  *,
727
727
  globalns: StrMapping | None = None,
728
728
  localns: StrMapping | None = None,
729
+ warn_name_errors: bool = False,
729
730
  ) -> SchemaDict:
730
731
  """Cast a dataclass as a schema dict."""
731
732
  out: dict[str, Any] = {}
732
- for field in yield_fields(obj, globalns=globalns, localns=localns):
733
+ for field in yield_fields(
734
+ obj, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
735
+ ):
733
736
  if is_dataclass_instance(field.value):
734
737
  dtypes = dataclass_to_schema(
735
738
  field.value, globalns=globalns, localns=localns
@@ -1388,12 +1391,15 @@ def struct_from_dataclass(
1388
1391
  *,
1389
1392
  globalns: StrMapping | None = None,
1390
1393
  localns: StrMapping | None = None,
1394
+ warn_name_errors: bool = False,
1391
1395
  time_zone: TimeZoneLike | None = None,
1392
1396
  ) -> Struct:
1393
1397
  """Construct the Struct data type for a dataclass."""
1394
1398
  if not is_dataclass_class(cls):
1395
1399
  raise _StructFromDataClassNotADataclassError(cls=cls)
1396
- anns = get_type_hints(cls, globalns=globalns, localns=localns)
1400
+ anns = get_type_hints(
1401
+ cls, globalns=globalns, localns=localns, warn_name_errors=warn_name_errors
1402
+ )
1397
1403
  data_types = {
1398
1404
  k: _struct_from_dataclass_one(v, time_zone=time_zone) for k, v in anns.items()
1399
1405
  }
@@ -51,6 +51,7 @@ class OrjsonRegressionFixture:
51
51
  *,
52
52
  globalns: StrMapping | None = None,
53
53
  localns: StrMapping | None = None,
54
+ warn_name_errors: bool = False,
54
55
  dataclass_defaults: bool = False,
55
56
  suffix: str | None = None,
56
57
  ) -> None:
@@ -61,6 +62,7 @@ class OrjsonRegressionFixture:
61
62
  obj,
62
63
  globalns=globalns,
63
64
  localns=localns,
65
+ warn_name_errors=warn_name_errors,
64
66
  dataclass_defaults=dataclass_defaults,
65
67
  )
66
68
  basename = self._basename
@@ -27,6 +27,7 @@ def load_settings(
27
27
  cwd: PathLike = PWD,
28
28
  globalns: StrMapping | None = None,
29
29
  localns: StrMapping | None = None,
30
+ warn_name_errors: bool = False,
30
31
  head: bool = False,
31
32
  case_sensitive: bool = False,
32
33
  ) -> TDataclass:
@@ -53,6 +54,7 @@ def load_settings(
53
54
  cls,
54
55
  globalns=globalns,
55
56
  localns=localns,
57
+ warn_name_errors=warn_name_errors,
56
58
  head=head,
57
59
  case_sensitive=case_sensitive,
58
60
  allow_extra=True,
utilities/typing.py CHANGED
@@ -19,6 +19,7 @@ from typing import (
19
19
  from typing import get_args as _get_args
20
20
  from typing import get_type_hints as _get_type_hints
21
21
  from uuid import UUID
22
+ from warnings import warn
22
23
 
23
24
  from utilities.iterables import unique_everseen
24
25
  from utilities.sentinel import Sentinel
@@ -68,16 +69,18 @@ def get_type_hints(
68
69
  *,
69
70
  globalns: StrMapping | None = None,
70
71
  localns: StrMapping | None = None,
72
+ warn_name_errors: bool = False,
71
73
  ) -> dict[str, Any]:
72
74
  """Get the type hints of an object."""
73
75
  result: dict[str, Any] = cls.__annotations__
74
76
  _ = {Literal, Path, Sentinel, StrMapping, UUID, dt}
75
- globalns = globals() | ({} if globalns is None else dict(globalns))
76
- localns = {} if localns is None else dict(localns)
77
+ globalns_use = globals() | ({} if globalns is None else dict(globalns))
78
+ localns_use = {} if localns is None else dict(localns)
77
79
  try:
78
- hints = _get_type_hints(cls, globalns=globalns, localns=localns)
79
- except NameError:
80
- pass
80
+ hints = _get_type_hints(cls, globalns=globalns_use, localns=localns_use)
81
+ except NameError as error:
82
+ if warn_name_errors:
83
+ warn(f"Error getting type hints for {cls!r}; {error}", stacklevel=2)
81
84
  else:
82
85
  result.update({
83
86
  key: value
@@ -179,6 +182,14 @@ def is_set_type(obj: Any, /) -> bool:
179
182
  ##
180
183
 
181
184
 
185
+ def is_tuple_type(obj: Any, /) -> bool:
186
+ """Check if an object is a tuple type annotation."""
187
+ return _is_annotation_of_type(obj, tuple)
188
+
189
+
190
+ ##
191
+
192
+
182
193
  def is_union_type(obj: Any, /) -> bool:
183
194
  """Check if an object is a union type annotation."""
184
195
  is_old_union = _is_annotation_of_type(obj, Union) # pyright: ignore[reportDeprecated]
@@ -209,5 +220,6 @@ __all__ = [
209
220
  "is_optional_type",
210
221
  "is_sequence_type",
211
222
  "is_set_type",
223
+ "is_tuple_type",
212
224
  "is_union_type",
213
225
  ]