faster-eth-utils 5.3.8__cp313-cp313-win_amd64.whl → 5.3.23__cp313-cp313-win_amd64.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 (45) hide show
  1. faster_eth_utils/abi.cp313-win_amd64.pyd +0 -0
  2. faster_eth_utils/abi.py +95 -70
  3. faster_eth_utils/address.cp313-win_amd64.pyd +0 -0
  4. faster_eth_utils/address.py +7 -14
  5. faster_eth_utils/applicators.cp313-win_amd64.pyd +0 -0
  6. faster_eth_utils/applicators.py +73 -56
  7. faster_eth_utils/conversions.cp313-win_amd64.pyd +0 -0
  8. faster_eth_utils/conversions.py +18 -20
  9. faster_eth_utils/crypto.cp313-win_amd64.pyd +0 -0
  10. faster_eth_utils/crypto.py +3 -8
  11. faster_eth_utils/currency.cp313-win_amd64.pyd +0 -0
  12. faster_eth_utils/currency.py +11 -8
  13. faster_eth_utils/curried/__init__.py +62 -65
  14. faster_eth_utils/debug.cp313-win_amd64.pyd +0 -0
  15. faster_eth_utils/decorators.cp313-win_amd64.pyd +0 -0
  16. faster_eth_utils/decorators.py +65 -29
  17. faster_eth_utils/encoding.cp313-win_amd64.pyd +0 -0
  18. faster_eth_utils/encoding.py +1 -1
  19. faster_eth_utils/exceptions.cp313-win_amd64.pyd +0 -0
  20. faster_eth_utils/exceptions.py +8 -1
  21. faster_eth_utils/functional.cp313-win_amd64.pyd +0 -0
  22. faster_eth_utils/functional.py +10 -12
  23. faster_eth_utils/hexadecimal.cp313-win_amd64.pyd +0 -0
  24. faster_eth_utils/hexadecimal.py +8 -12
  25. faster_eth_utils/humanize.cp313-win_amd64.pyd +0 -0
  26. faster_eth_utils/humanize.py +16 -16
  27. faster_eth_utils/logging.py +51 -44
  28. faster_eth_utils/module_loading.cp313-win_amd64.pyd +0 -0
  29. faster_eth_utils/network.cp313-win_amd64.pyd +0 -0
  30. faster_eth_utils/network.py +2 -3
  31. faster_eth_utils/numeric.cp313-win_amd64.pyd +0 -0
  32. faster_eth_utils/pydantic.py +5 -7
  33. faster_eth_utils/toolz.cp313-win_amd64.pyd +0 -0
  34. faster_eth_utils/types.cp313-win_amd64.pyd +0 -0
  35. faster_eth_utils/types.py +7 -10
  36. faster_eth_utils/units.cp313-win_amd64.pyd +0 -0
  37. {faster_eth_utils-5.3.8.dist-info → faster_eth_utils-5.3.23.dist-info}/METADATA +39 -19
  38. faster_eth_utils-5.3.23.dist-info/RECORD +53 -0
  39. faster_eth_utils-5.3.23.dist-info/top_level.txt +3 -0
  40. faster_eth_utils__mypyc.cp313-win_amd64.pyd +0 -0
  41. 99c07adba6ff961eaf3e__mypyc.cp313-win_amd64.pyd +0 -0
  42. faster_eth_utils-5.3.8.dist-info/RECORD +0 -53
  43. faster_eth_utils-5.3.8.dist-info/top_level.txt +0 -3
  44. {faster_eth_utils-5.3.8.dist-info → faster_eth_utils-5.3.23.dist-info}/WHEEL +0 -0
  45. {faster_eth_utils-5.3.8.dist-info → faster_eth_utils-5.3.23.dist-info}/licenses/LICENSE +0 -0
Binary file
faster_eth_utils/abi.py CHANGED
@@ -1,20 +1,17 @@
1
- from collections import (
2
- abc,
3
- )
4
1
  import copy
5
2
  import itertools
6
3
  import re
7
- from typing import (
8
- Any,
9
- Dict,
4
+ from collections.abc import (
10
5
  Iterable,
11
- List,
12
- Literal,
13
6
  Mapping,
14
- Optional,
15
7
  Sequence,
16
- Tuple,
17
- Union,
8
+ )
9
+ from typing import (
10
+ Any,
11
+ Final,
12
+ Generic,
13
+ Literal,
14
+ TypeVar,
18
15
  cast,
19
16
  overload,
20
17
  )
@@ -30,6 +27,9 @@ from eth_typing import (
30
27
  ABIFunction,
31
28
  ABIReceive,
32
29
  )
30
+ from typing_extensions import (
31
+ Self,
32
+ )
33
33
 
34
34
  from faster_eth_utils.types import (
35
35
  is_list_like,
@@ -40,9 +40,37 @@ from .crypto import (
40
40
  )
41
41
 
42
42
 
43
+ T = TypeVar("T")
44
+
45
+ ABIType = Literal["function", "constructor", "fallback", "receive", "event", "error"]
46
+
47
+ _TUPLE_TYPE_STR_RE: Final = re.compile("^(tuple)((\\[([1-9]\\d*\\b)?])*)??$")
48
+
49
+ _chain: Final = itertools.chain
50
+
51
+
52
+ class _repeat(Generic[T]):
53
+
54
+ def __init__(self, value: T, times: int | None = None) -> None:
55
+ self._value: Final[T] = value
56
+ self._times: Final[int | None] = times
57
+ self._index = 0
58
+
59
+ def __iter__(self) -> Self:
60
+ return self
61
+
62
+ def __next__(self) -> T:
63
+ if self._times is None:
64
+ return self._value
65
+ if self._index >= self._times:
66
+ raise StopIteration
67
+ self._index += 1
68
+ return self._value
69
+
70
+
43
71
  def _align_abi_input(
44
72
  arg_abi: ABIComponent, normalized_arg: Any
45
- ) -> Union[Any, Tuple[Any, ...]]:
73
+ ) -> Any | tuple[Any, ...]:
46
74
  """
47
75
  Aligns the values of any mapping at any level of nesting in ``normalized_arg``
48
76
  according to the layout of the corresponding abi spec.
@@ -66,37 +94,38 @@ def _align_abi_input(
66
94
  new_abi = copy.copy(arg_abi)
67
95
  new_abi["type"] = tuple_prefix + "[]" * (num_dims - 1)
68
96
 
69
- sub_abis = itertools.repeat(new_abi)
97
+ sub_abis = _repeat(new_abi)
70
98
 
71
99
  aligned_arg: Any
72
- if isinstance(normalized_arg, abc.Mapping):
100
+ if isinstance(normalized_arg, Mapping):
73
101
  # normalized_arg is mapping. Align values according to abi order.
74
102
  aligned_arg = tuple(normalized_arg[abi["name"]] for abi in sub_abis)
75
103
  else:
76
104
  aligned_arg = normalized_arg
77
105
 
78
- if not is_list_like(aligned_arg):
79
- raise TypeError(
80
- f'Expected non-string sequence for "{arg_abi.get("type")}" '
81
- f"component type: got {aligned_arg}"
82
- )
106
+ # We can generate more optimized C code if we branch by arg type
107
+ if isinstance(aligned_arg, tuple):
108
+ # convert NamedTuple to regular tuple
109
+ return tuple(map(_align_abi_input, sub_abis, aligned_arg))
83
110
 
84
- # convert NamedTuple to regular tuple
85
- typing = tuple if isinstance(aligned_arg, tuple) else type(aligned_arg)
111
+ elif type(aligned_arg) is list:
112
+ return list(map(_align_abi_input, sub_abis, aligned_arg))
86
113
 
87
- return typing( # type: ignore [call-arg]
88
- _align_abi_input(sub_abi, sub_arg)
89
- for sub_abi, sub_arg in zip(sub_abis, aligned_arg)
114
+ elif is_list_like(aligned_arg):
115
+ return type(aligned_arg)(map(_align_abi_input, sub_abis, aligned_arg)) # type: ignore [call-arg]
116
+
117
+ raise TypeError(
118
+ f'Expected non-string sequence for "{arg_abi.get("type")}" '
119
+ f"component type: got {aligned_arg}"
90
120
  )
91
121
 
92
122
 
93
- def _get_tuple_type_str_and_dims(s: str) -> Optional[Tuple[str, Optional[str]]]:
123
+ def _get_tuple_type_str_and_dims(s: str) -> tuple[str, str | None] | None:
94
124
  """
95
125
  Takes a JSON ABI type string. For tuple type strings, returns the separated
96
126
  prefix and array dimension parts. For all other strings, returns ``None``.
97
127
  """
98
- tuple_type_str_re = "^(tuple)((\\[([1-9]\\d*\b)?])*)??$"
99
- match = re.compile(tuple_type_str_re).match(s)
128
+ match = _TUPLE_TYPE_STR_RE.match(s)
100
129
 
101
130
  return None if match is None else (match[1], match[2])
102
131
 
@@ -119,7 +148,7 @@ def _raise_if_fallback_or_receive_abi(abi_element: ABIElement) -> None:
119
148
  )
120
149
 
121
150
 
122
- def collapse_if_tuple(abi: Union[ABIComponent, Dict[str, Any], str]) -> str:
151
+ def collapse_if_tuple(abi: ABIComponent | dict[str, Any] | str) -> str:
123
152
  """
124
153
  Extract argument types from a function or event ABI parameter.
125
154
 
@@ -278,11 +307,21 @@ def filter_abi_by_name(abi_name: str, contract_abi: ABI) -> Sequence[ABIElement]
278
307
  ]
279
308
 
280
309
 
310
+ __ABI_TYPE_LITERALS: Final = {
311
+ Literal["function"]: "function",
312
+ Literal["constructor"]: "constructor",
313
+ Literal["fallback"]: "fallback",
314
+ Literal["receive"]: "receive",
315
+ Literal["event"]: "event",
316
+ Literal["error"]: "error",
317
+ }
318
+
319
+
281
320
  @overload
282
321
  def filter_abi_by_type(
283
322
  abi_type: Literal["function"],
284
323
  contract_abi: ABI,
285
- ) -> List[ABIFunction]:
324
+ ) -> list[ABIFunction]:
286
325
  pass
287
326
 
288
327
 
@@ -290,7 +329,7 @@ def filter_abi_by_type(
290
329
  def filter_abi_by_type(
291
330
  abi_type: Literal["constructor"],
292
331
  contract_abi: ABI,
293
- ) -> List[ABIConstructor]:
332
+ ) -> list[ABIConstructor]:
294
333
  pass
295
334
 
296
335
 
@@ -298,7 +337,7 @@ def filter_abi_by_type(
298
337
  def filter_abi_by_type(
299
338
  abi_type: Literal["fallback"],
300
339
  contract_abi: ABI,
301
- ) -> List[ABIFallback]:
340
+ ) -> list[ABIFallback]:
302
341
  pass
303
342
 
304
343
 
@@ -306,7 +345,7 @@ def filter_abi_by_type(
306
345
  def filter_abi_by_type(
307
346
  abi_type: Literal["receive"],
308
347
  contract_abi: ABI,
309
- ) -> List[ABIReceive]:
348
+ ) -> list[ABIReceive]:
310
349
  pass
311
350
 
312
351
 
@@ -314,7 +353,7 @@ def filter_abi_by_type(
314
353
  def filter_abi_by_type(
315
354
  abi_type: Literal["event"],
316
355
  contract_abi: ABI,
317
- ) -> List[ABIEvent]:
356
+ ) -> list[ABIEvent]:
318
357
  pass
319
358
 
320
359
 
@@ -322,18 +361,16 @@ def filter_abi_by_type(
322
361
  def filter_abi_by_type(
323
362
  abi_type: Literal["error"],
324
363
  contract_abi: ABI,
325
- ) -> List[ABIError]:
364
+ ) -> list[ABIError]:
326
365
  pass
327
366
 
328
367
 
329
368
  def filter_abi_by_type(
330
- abi_type: Literal[
331
- "function", "constructor", "fallback", "receive", "event", "error"
332
- ],
369
+ abi_type: ABIType,
333
370
  contract_abi: ABI,
334
- ) -> Union[
335
- List[ABIFunction], List[ABIConstructor], List[ABIFallback], List[ABIReceive], List[ABIEvent], List[ABIError]
336
- ]:
371
+ ) -> (
372
+ list[ABIFunction] | list[ABIConstructor] | list[ABIFallback] | list[ABIReceive] | list[ABIEvent] | list[ABIError]
373
+ ):
337
374
  """
338
375
  Return a list of each ``ABIElement`` that is of type ``abi_type``.
339
376
 
@@ -361,20 +398,12 @@ ABIEvent, ABIError]]`
361
398
  [{'type': 'function', 'name': 'myFunction', 'inputs': [], 'outputs': []}, \
362
399
  {'type': 'function', 'name': 'myFunction2', 'inputs': [], 'outputs': []}]
363
400
  """
364
- if abi_type == Literal["function"] or abi_type == "function":
365
- return [abi for abi in contract_abi if abi["type"] == "function"]
366
- elif abi_type == Literal["constructor"] or abi_type == "constructor":
367
- return [abi for abi in contract_abi if abi["type"] == "constructor"]
368
- elif abi_type == Literal["fallback"] or abi_type == "fallback":
369
- return [abi for abi in contract_abi if abi["type"] == "fallback"]
370
- elif abi_type == Literal["receive"] or abi_type == "receive":
371
- return [abi for abi in contract_abi if abi["type"] == "receive"]
372
- elif abi_type == Literal["event"] or abi_type == "event":
373
- return [abi for abi in contract_abi if abi["type"] == "event"]
374
- elif abi_type == Literal["error"] or abi_type == "error":
375
- return [abi for abi in contract_abi if abi["type"] == "error"]
376
- else:
401
+ if abi_type in ("function", "constructor", "fallback", "receive", "event", "error"):
402
+ return [abi for abi in contract_abi if abi["type"] == abi_type] # type: ignore [return-value]
403
+ abi_type_string: ABIType | None = __ABI_TYPE_LITERALS.get(abi_type) # type: ignore [call-overload]
404
+ if abi_type_string is None:
377
405
  raise ValueError(f"Unsupported ABI type: {abi_type}")
406
+ return [abi for abi in contract_abi if abi["type"] == abi_type_string] # type: ignore [return-value]
378
407
 
379
408
 
380
409
  def get_all_function_abis(contract_abi: ABI) -> Sequence[ABIFunction]:
@@ -428,7 +457,7 @@ def get_normalized_abi_inputs(
428
457
  abi_element: ABIElement,
429
458
  *args: Any,
430
459
  **kwargs: Any,
431
- ) -> Tuple[Any, ...]:
460
+ ) -> tuple[Any, ...]:
432
461
  r"""
433
462
  Flattens positional args (``args``) and keyword args (``kwargs``) into a Tuple and
434
463
  uses the ``abi_element`` for validation.
@@ -496,6 +525,7 @@ def get_normalized_abi_inputs(
496
525
 
497
526
  kwarg_names = set(kwargs.keys())
498
527
  sorted_arg_names = tuple(arg_abi["name"] for arg_abi in function_inputs)
528
+ arg_positions = {name: index for index, name in enumerate(sorted_arg_names)}
499
529
  args_as_kwargs = dict(zip(sorted_arg_names, args))
500
530
 
501
531
  # Check for duplicate args
@@ -521,22 +551,17 @@ def get_normalized_abi_inputs(
521
551
 
522
552
  # Sort args according to their position in the ABI and unzip them from their
523
553
  # names
524
- sorted_args = tuple(
525
- zip(
526
- *sorted(
527
- itertools.chain(kwargs.items(), args_as_kwargs.items()),
528
- key=lambda kv: sorted_arg_names.index(kv[0]),
529
- )
530
- )
554
+ sorted_items = sorted(
555
+ _chain(kwargs.items(), args_as_kwargs.items()),
556
+ key=lambda kv: arg_positions[kv[0]],
531
557
  )
532
-
533
- return tuple(sorted_args[1]) if sorted_args else ()
558
+ return tuple(val for _, val in sorted_items)
534
559
 
535
560
 
536
561
  def get_aligned_abi_inputs(
537
562
  abi_element: ABIElement,
538
- normalized_args: Union[Tuple[Any, ...], Mapping[Any, Any]],
539
- ) -> Tuple[Tuple[str, ...], Tuple[Any, ...]]:
563
+ normalized_args: tuple[Any, ...] | Mapping[Any, Any],
564
+ ) -> tuple[tuple[str, ...], tuple[Any, ...]]:
540
565
  """
541
566
  Returns a pair of nested Tuples containing a list of types and a list of input
542
567
  values sorted by the order specified by the ``abi``.
@@ -579,7 +604,7 @@ def get_aligned_abi_inputs(
579
604
  _raise_if_fallback_or_receive_abi(abi_element)
580
605
 
581
606
  abi_element_inputs = cast(Sequence[ABIComponent], abi_element.get("inputs", []))
582
- if isinstance(normalized_args, abc.Mapping):
607
+ if isinstance(normalized_args, Mapping):
583
608
  # `args` is mapping. Align values according to abi order.
584
609
  normalized_args = tuple(
585
610
  normalized_args[abi["name"]] for abi in abi_element_inputs
@@ -594,7 +619,7 @@ def get_aligned_abi_inputs(
594
619
  )
595
620
 
596
621
 
597
- def get_abi_input_names(abi_element: ABIElement) -> List[Optional[str]]:
622
+ def get_abi_input_names(abi_element: ABIElement) -> list[str | None]:
598
623
  """
599
624
  Return names for each input from the function or event ABI.
600
625
 
@@ -630,7 +655,7 @@ def get_abi_input_names(abi_element: ABIElement) -> List[Optional[str]]:
630
655
  ]
631
656
 
632
657
 
633
- def get_abi_input_types(abi_element: ABIElement) -> List[str]:
658
+ def get_abi_input_types(abi_element: ABIElement) -> list[str]:
634
659
  """
635
660
  Return types for each input from the function or event ABI.
636
661
 
@@ -666,7 +691,7 @@ def get_abi_input_types(abi_element: ABIElement) -> List[str]:
666
691
  ]
667
692
 
668
693
 
669
- def get_abi_output_names(abi_element: ABIElement) -> List[Optional[str]]:
694
+ def get_abi_output_names(abi_element: ABIElement) -> list[str | None]:
670
695
  """
671
696
  Return names for each output from the ABI element.
672
697
 
@@ -711,7 +736,7 @@ def get_abi_output_names(abi_element: ABIElement) -> List[Optional[str]]:
711
736
  ]
712
737
 
713
738
 
714
- def get_abi_output_types(abi_element: ABIElement) -> List[str]:
739
+ def get_abi_output_types(abi_element: ABIElement) -> list[str]:
715
740
  """
716
741
  Return types for each output from the function ABI.
717
742
 
@@ -2,7 +2,7 @@ import re
2
2
  from typing import (
3
3
  Any,
4
4
  Final,
5
- Union,
5
+ TypeGuard,
6
6
  )
7
7
 
8
8
  import cchecksum
@@ -13,21 +13,13 @@ from eth_typing import (
13
13
  HexAddress,
14
14
  HexStr,
15
15
  )
16
- from typing_extensions import (
17
- TypeGuard,
18
- )
19
16
 
20
17
  from .conversions import (
21
18
  hexstr_if_str,
22
19
  to_hex,
23
20
  )
24
- from .crypto import (
25
- keccak,
26
- )
27
21
  from .hexadecimal import (
28
- add_0x_prefix,
29
22
  decode_hex,
30
- encode_hex,
31
23
  remove_0x_prefix,
32
24
  )
33
25
  from .types import (
@@ -76,7 +68,7 @@ def is_address(value: Any) -> bool:
76
68
  return False
77
69
 
78
70
 
79
- def to_normalized_address(value: Union[AnyAddress, str, bytes]) -> HexAddress:
71
+ def to_normalized_address(value: AnyAddress | str | bytes) -> HexAddress:
80
72
  """
81
73
  Converts an address to its normalized hexadecimal representation.
82
74
  """
@@ -100,7 +92,7 @@ def is_normalized_address(value: Any) -> TypeGuard[HexAddress]:
100
92
  return is_address(value) and value == to_normalized_address(value)
101
93
 
102
94
 
103
- def to_canonical_address(address: Union[AnyAddress, str, bytes]) -> Address:
95
+ def to_canonical_address(address: AnyAddress | str | bytes) -> Address:
104
96
  """
105
97
  Convert a valid address to its canonical form (20-length bytes).
106
98
  """
@@ -115,15 +107,16 @@ def is_canonical_address(address: Any) -> TypeGuard[Address]:
115
107
  8
116
108
 
117
109
  def is_same_address(
118
- left: Union[AnyAddress, str, bytes], right: Union[AnyAddress, str, bytes]
110
+ left: AnyAddress | str | bytes, right: AnyAddress | str | bytes
119
111
  ) -> bool:
120
112
  """
121
113
  Checks if both addresses are same or not.
122
114
  """
123
115
  if not is_address(left) or not is_address(right):
124
116
  raise ValueError("Both values must be valid addresses")
125
- else:
126
- return to_normalized_address(left) == to_normalized_address(right)
117
+ if left == right:
118
+ return True
119
+ return to_normalized_address(left) == to_normalized_address(right)
127
120
 
128
121
 
129
122
  def is_checksum_address(value: Any) -> TypeGuard[ChecksumAddress]:
@@ -1,23 +1,18 @@
1
+ from collections.abc import Callable, Generator, Mapping, Sequence
1
2
  from typing import (
3
+ TYPE_CHECKING,
2
4
  Any,
3
- Callable,
4
- Dict,
5
- Generator,
6
- List,
7
- Sequence,
8
- Tuple,
5
+ TypeGuard,
9
6
  TypeVar,
10
- Union,
11
7
  cast,
8
+ overload,
12
9
  )
10
+
13
11
  import warnings
14
12
 
15
13
  from .decorators import (
16
14
  return_arg_type,
17
15
  )
18
- from .functional import (
19
- to_dict,
20
- )
21
16
  from .pydantic import (
22
17
  CamelModel,
23
18
  )
@@ -26,17 +21,23 @@ from .toolz import (
26
21
  curry,
27
22
  )
28
23
 
24
+ if TYPE_CHECKING:
25
+ from _typeshed import SupportsBool
26
+ # We have to sacrifice a little bit of specificity on dinosaur Python3.8
27
+
29
28
  TArg = TypeVar("TArg")
30
29
  TReturn = TypeVar("TReturn")
31
30
  TOther = TypeVar("TOther")
32
31
 
33
- Formatters = Callable[[List[Any]], List[Any]]
32
+ Formatters = Callable[[list[Any]], list[Any]]
34
33
 
35
34
 
36
35
  @return_arg_type(2)
37
36
  def apply_formatter_at_index(
38
- formatter: Callable[[TArg], TReturn], at_index: int, value: Sequence[Union[TArg, TOther]]
39
- ) -> Generator[Union[TOther, TReturn], None, None]:
37
+ formatter: Callable[[TArg], TReturn],
38
+ at_index: int,
39
+ value: Sequence[TArg | TOther],
40
+ ) -> Generator[TOther | TReturn, None, None]:
40
41
  try:
41
42
  item = value[at_index]
42
43
  except IndexError:
@@ -45,9 +46,9 @@ def apply_formatter_at_index(
45
46
  f"Need: {at_index + 1}"
46
47
  ) from None
47
48
 
48
- yield from value[:at_index]
49
+ yield from cast(Sequence[TOther], value[:at_index])
49
50
  yield formatter(cast(TArg, item))
50
- yield from value[at_index + 1 :]
51
+ yield from cast(Sequence[TOther], value[at_index + 1 :])
51
52
 
52
53
 
53
54
  def combine_argument_formatters(*formatters: Callable[..., Any]) -> Formatters:
@@ -63,7 +64,7 @@ def combine_argument_formatters(*formatters: Callable[..., Any]) -> Formatters:
63
64
  )
64
65
 
65
66
  _formatter_at_index = curry(apply_formatter_at_index)
66
- return compose( # type: ignore
67
+ return compose( # type: ignore [no-any-return]
67
68
  *(
68
69
  _formatter_at_index(formatter, index)
69
70
  for index, formatter in enumerate(formatters)
@@ -73,38 +74,55 @@ def combine_argument_formatters(*formatters: Callable[..., Any]) -> Formatters:
73
74
 
74
75
  @return_arg_type(1)
75
76
  def apply_formatters_to_sequence(
76
- formatters: List[Callable[[Any], TReturn]], sequence: Sequence[Any]
77
+ formatters: list[Callable[[Any], TReturn]], sequence: Sequence[Any]
77
78
  ) -> Generator[TReturn, None, None]:
78
- if len(formatters) == len(sequence):
79
+ num_formatters = len(formatters)
80
+ num_items = len(sequence)
81
+ if num_formatters == num_items:
79
82
  for formatter, item in zip(formatters, sequence):
80
83
  yield formatter(item)
81
- elif len(formatters) > len(sequence):
84
+ elif num_formatters > num_items:
82
85
  raise IndexError(
83
- f"Too many formatters for sequence: {len(formatters)} formatters for "
84
- f"{repr(sequence)}"
86
+ f"Too many formatters for sequence: {num_formatters} formatters for "
87
+ f"{sequence!r}"
85
88
  )
86
89
  else:
87
90
  raise IndexError(
88
- f"Too few formatters for sequence: {len(formatters)} formatters for "
89
- f"{repr(sequence)}"
91
+ f"Too few formatters for sequence: {num_formatters} formatters for "
92
+ f"{sequence!r}"
90
93
  )
91
94
 
92
95
 
96
+ @overload
97
+ def apply_formatter_if(
98
+ condition: Callable[[TArg], TypeGuard[TOther]],
99
+ formatter: Callable[[TOther], TReturn],
100
+ value: TArg,
101
+ ) -> TArg | TReturn: ...
102
+
103
+
104
+ @overload
93
105
  def apply_formatter_if(
94
106
  condition: Callable[[TArg], bool], formatter: Callable[[TArg], TReturn], value: TArg
95
- ) -> Union[TArg, TReturn]:
107
+ ) -> TArg | TReturn: ...
108
+
109
+
110
+ def apply_formatter_if( # type: ignore [misc]
111
+ condition: Callable[[TArg], TypeGuard[TOther]] | Callable[[TArg], bool],
112
+ formatter: Callable[[TOther], TReturn] | Callable[[TArg], TReturn],
113
+ value: TArg,
114
+ ) -> TArg | TReturn:
96
115
  if condition(value):
97
- return formatter(value)
116
+ return formatter(value) # type: ignore [arg-type]
98
117
  else:
99
118
  return value
100
119
 
101
120
 
102
- @to_dict
103
121
  def apply_formatters_to_dict(
104
- formatters: Dict[Any, Any],
105
- value: Union[Dict[Any, Any], CamelModel],
122
+ formatters: dict[Any, Any],
123
+ value: dict[Any, Any] | CamelModel,
106
124
  unaliased: bool = False,
107
- ) -> Generator[Tuple[Any, Any], None, None]:
125
+ ) -> dict[Any, Any]:
108
126
  """
109
127
  Apply formatters to a dictionary of values. If the value is a pydantic model,
110
128
  it will be serialized to a dictionary first, taking into account the
@@ -119,22 +137,21 @@ def apply_formatters_to_dict(
119
137
  if isinstance(value, CamelModel):
120
138
  value = value.model_dump(by_alias=not unaliased)
121
139
 
122
- for key, item in value.items():
123
- if key in formatters:
124
- try:
125
- yield key, formatters[key](item)
126
- except ValueError as exc:
127
- new_error_message = (
128
- f"Could not format invalid value {repr(item)} as field {repr(key)}"
129
- )
130
- raise ValueError(new_error_message) from exc
131
- except TypeError as exc:
132
- new_error_message = (
133
- f"Could not format invalid type {repr(item)} as field {repr(key)}"
134
- )
135
- raise TypeError(new_error_message) from exc
136
- else:
137
- yield key, item
140
+ def get_value(key: Any, val: Any) -> Any:
141
+ if key not in formatters:
142
+ return val
143
+ try:
144
+ return formatters[key](val)
145
+ except ValueError as exc:
146
+ raise ValueError(
147
+ f"Could not format invalid value {val!r} as field {key!r}"
148
+ ) from exc
149
+ except TypeError as exc:
150
+ raise TypeError(
151
+ f"Could not format invalid type {val!r} as field {key!r}"
152
+ ) from exc
153
+
154
+ return {key: get_value(key, val) for key, val in value.items()}
138
155
 
139
156
 
140
157
  @return_arg_type(1)
@@ -146,9 +163,11 @@ def apply_formatter_to_array(
146
163
 
147
164
 
148
165
  def apply_one_of_formatters(
149
- formatter_condition_pairs: Tuple[Tuple[Callable[[TArg], Any], Callable[[TArg], Any]], ...],
150
- value: Any,
151
- ) -> Any:
166
+ formatter_condition_pairs: tuple[
167
+ tuple[Callable[[TArg], "SupportsBool"], Callable[[TArg], TReturn]], ...
168
+ ],
169
+ value: TArg,
170
+ ) -> TReturn:
152
171
  for condition, formatter in formatter_condition_pairs:
153
172
  if condition(value):
154
173
  return formatter(value)
@@ -158,10 +177,9 @@ def apply_one_of_formatters(
158
177
  )
159
178
 
160
179
 
161
- @to_dict
162
180
  def apply_key_map(
163
- key_mappings: Dict[Any, Any], value: Dict[Any, Any]
164
- ) -> Generator[Tuple[Any, Any], None, None]:
181
+ key_mappings: dict[Any, Any], value: Mapping[Any, Any]
182
+ ) -> dict[Any, Any]:
165
183
  key_conflicts = (
166
184
  set(value.keys())
167
185
  .difference(key_mappings.keys())
@@ -172,8 +190,7 @@ def apply_key_map(
172
190
  f"Could not apply key map due to conflicting key(s): {key_conflicts}"
173
191
  )
174
192
 
175
- for key, item in value.items():
176
- if key in key_mappings:
177
- yield key_mappings[key], item
178
- else:
179
- yield key, item
193
+ def get_key(key: Any) -> Any:
194
+ return key_mappings[key] if key in key_mappings else key
195
+
196
+ return {get_key(key): item for key, item in value.items()}