rebulk 4.2.2__tar.gz → 4.3.0__tar.gz
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.
- {rebulk-4.2.2 → rebulk-4.3.0}/PKG-INFO +16 -2
- {rebulk-4.2.2 → rebulk-4.3.0}/README.md +15 -1
- {rebulk-4.2.2 → rebulk-4.3.0}/pyproject.toml +1 -1
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/__version__.py +1 -1
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/builder.py +4 -3
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/chain.py +9 -7
- rebulk-4.3.0/rebulk/key.py +58 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/match.py +27 -4
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_key.py +18 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_match.py +5 -1
- rebulk-4.2.2/rebulk/key.py +0 -45
- {rebulk-4.2.2 → rebulk-4.3.0}/LICENSE +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/__init__.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/debug.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/formatters.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/introspector.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/loose.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/pattern.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/processors.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/py.typed +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/rebulk.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/remodule.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/rules.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/__init__.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/default_rules_module.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/rebulk_rules_module.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/rules_module.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_chain.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_debug.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_introspector.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_loose.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_model.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_pattern.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_processors.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_rebulk.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_rules.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_toposort.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_utils.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/test/test_validators.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/toposort.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/utils.py +0 -0
- {rebulk-4.2.2 → rebulk-4.3.0}/rebulk/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rebulk
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Rebulk - Define simple search patterns in bulk to perform advanced matching on any string.
|
|
5
5
|
Keywords: re,regexp,regular expression,search,pattern,string,match
|
|
6
6
|
Author: Rémi Alvergnat
|
|
@@ -533,7 +533,10 @@ It has the following additional methods and properties on it.
|
|
|
533
533
|
`Match.value` as value.
|
|
534
534
|
|
|
535
535
|
if `enforce_list` is `True`, all values will be wrapped to a list,
|
|
536
|
-
even if a single value is found.
|
|
536
|
+
even if a single value is found. This form has a predictable, typed
|
|
537
|
+
shape (`MatchesDict[list]`), unlike the default which is a scalar *or*
|
|
538
|
+
a list per name. For fully typed access, prefer the typed retrieval
|
|
539
|
+
API above (`Key` and `Matches.to(...)`).
|
|
537
540
|
|
|
538
541
|
If `details` is True, `Match.value` objects are replaced with
|
|
539
542
|
complete `Match` object.
|
|
@@ -567,6 +570,17 @@ returns `T | None`, and `matches.all(key)` returns `list[T]`.
|
|
|
567
570
|
|
|
568
571
|
```
|
|
569
572
|
|
|
573
|
+
For values that aren't built straight from a string, pass an explicit
|
|
574
|
+
`formatter` (a `(str) -> T` converter); the key stays fully typed as `T`.
|
|
575
|
+
|
|
576
|
+
```python
|
|
577
|
+
>>> from datetime import date
|
|
578
|
+
>>> released = Key("released", date, formatter=date.fromisoformat)
|
|
579
|
+
>>> Rebulk().regex(r'\d{4}-\d{2}-\d{2}', key=released).matches("on 2008-01-02")[released]
|
|
580
|
+
datetime.date(2008, 1, 2)
|
|
581
|
+
|
|
582
|
+
```
|
|
583
|
+
|
|
570
584
|
You can also project the matches onto a typed dataclass with `to`. Each field
|
|
571
585
|
is filled from matches sharing its name: a `list[...]` field collects all
|
|
572
586
|
values, any other field takes the first, and unmatched fields fall back to
|
|
@@ -508,7 +508,10 @@ It has the following additional methods and properties on it.
|
|
|
508
508
|
`Match.value` as value.
|
|
509
509
|
|
|
510
510
|
if `enforce_list` is `True`, all values will be wrapped to a list,
|
|
511
|
-
even if a single value is found.
|
|
511
|
+
even if a single value is found. This form has a predictable, typed
|
|
512
|
+
shape (`MatchesDict[list]`), unlike the default which is a scalar *or*
|
|
513
|
+
a list per name. For fully typed access, prefer the typed retrieval
|
|
514
|
+
API above (`Key` and `Matches.to(...)`).
|
|
512
515
|
|
|
513
516
|
If `details` is True, `Match.value` objects are replaced with
|
|
514
517
|
complete `Match` object.
|
|
@@ -542,6 +545,17 @@ returns `T | None`, and `matches.all(key)` returns `list[T]`.
|
|
|
542
545
|
|
|
543
546
|
```
|
|
544
547
|
|
|
548
|
+
For values that aren't built straight from a string, pass an explicit
|
|
549
|
+
`formatter` (a `(str) -> T` converter); the key stays fully typed as `T`.
|
|
550
|
+
|
|
551
|
+
```python
|
|
552
|
+
>>> from datetime import date
|
|
553
|
+
>>> released = Key("released", date, formatter=date.fromisoformat)
|
|
554
|
+
>>> Rebulk().regex(r'\d{4}-\d{2}-\d{2}', key=released).matches("on 2008-01-02")[released]
|
|
555
|
+
datetime.date(2008, 1, 2)
|
|
556
|
+
|
|
557
|
+
```
|
|
558
|
+
|
|
545
559
|
You can also project the matches onto a typed dataclass with `to`. Each field
|
|
546
560
|
is filled from matches sharing its name: a `list[...]` field collects all
|
|
547
561
|
values, any other field takes the first, and unmatched fields fall back to
|
|
@@ -27,12 +27,13 @@ log = getLogger(__name__).log
|
|
|
27
27
|
|
|
28
28
|
def _apply_key(key: Key[Any] | None, kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
29
29
|
"""
|
|
30
|
-
Inject a typed :class:`~rebulk.key.Key`'s name and
|
|
31
|
-
formatter) into pattern kwargs, without overriding
|
|
30
|
+
Inject a typed :class:`~rebulk.key.Key`'s name and converter (its explicit
|
|
31
|
+
``formatter`` or its ``value_type``) into pattern kwargs, without overriding
|
|
32
|
+
explicit values.
|
|
32
33
|
"""
|
|
33
34
|
if key is not None:
|
|
34
35
|
kwargs.setdefault("name", key.name)
|
|
35
|
-
kwargs.setdefault("formatter", key.
|
|
36
|
+
kwargs.setdefault("formatter", key.converter)
|
|
36
37
|
return kwargs
|
|
37
38
|
|
|
38
39
|
|
|
@@ -324,7 +324,7 @@ class ChainPart(BasePattern):
|
|
|
324
324
|
if max_match_index + 1 < self.repeater_start:
|
|
325
325
|
raise _InvalidChainException
|
|
326
326
|
|
|
327
|
-
def chain(self) ->
|
|
327
|
+
def chain(self) -> Chain:
|
|
328
328
|
"""
|
|
329
329
|
Add patterns chain, using configuration from this chain
|
|
330
330
|
|
|
@@ -354,7 +354,7 @@ class ChainPart(BasePattern):
|
|
|
354
354
|
"""
|
|
355
355
|
return self._hidden
|
|
356
356
|
|
|
357
|
-
def regex(self, *pattern: Any, **kwargs: Any) ->
|
|
357
|
+
def regex(self, *pattern: Any, **kwargs: Any) -> ChainPart:
|
|
358
358
|
"""
|
|
359
359
|
Add re pattern
|
|
360
360
|
|
|
@@ -365,9 +365,11 @@ class ChainPart(BasePattern):
|
|
|
365
365
|
:return:
|
|
366
366
|
:rtype:
|
|
367
367
|
"""
|
|
368
|
-
|
|
368
|
+
# The builder's regex/string/functional are typed `-> Self` (Chain), but
|
|
369
|
+
# Chain.pattern() yields a ChainPart, which is what is actually returned.
|
|
370
|
+
return cast("ChainPart", self._chain.regex(*pattern, **kwargs))
|
|
369
371
|
|
|
370
|
-
def functional(self, *pattern: Any, **kwargs: Any) ->
|
|
372
|
+
def functional(self, *pattern: Any, **kwargs: Any) -> ChainPart:
|
|
371
373
|
"""
|
|
372
374
|
Add functional pattern
|
|
373
375
|
|
|
@@ -378,9 +380,9 @@ class ChainPart(BasePattern):
|
|
|
378
380
|
:return:
|
|
379
381
|
:rtype:
|
|
380
382
|
"""
|
|
381
|
-
return self._chain.functional(*pattern, **kwargs)
|
|
383
|
+
return cast("ChainPart", self._chain.functional(*pattern, **kwargs))
|
|
382
384
|
|
|
383
|
-
def string(self, *pattern: Any, **kwargs: Any) ->
|
|
385
|
+
def string(self, *pattern: Any, **kwargs: Any) -> ChainPart:
|
|
384
386
|
"""
|
|
385
387
|
Add string pattern
|
|
386
388
|
|
|
@@ -391,7 +393,7 @@ class ChainPart(BasePattern):
|
|
|
391
393
|
:return:
|
|
392
394
|
:rtype:
|
|
393
395
|
"""
|
|
394
|
-
return self._chain.string(*pattern, **kwargs)
|
|
396
|
+
return cast("ChainPart", self._chain.string(*pattern, **kwargs))
|
|
395
397
|
|
|
396
398
|
def close(self) -> Any:
|
|
397
399
|
"""
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Typed keys binding a match name to its value type for type-safe retrieval.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, is_dataclass
|
|
9
|
+
from typing import TYPE_CHECKING, Generic, TypeVar, is_typeddict
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class Key(Generic[T]):
|
|
19
|
+
r"""
|
|
20
|
+
A typed handle for a named match.
|
|
21
|
+
|
|
22
|
+
``name`` is the match name to declare and look up; ``value_type`` is the type
|
|
23
|
+
of the match value (e.g. ``int``, ``str``, ``float``). ``formatter``, if
|
|
24
|
+
given, is the ``(str) -> T`` converter applied to the matched substring; when
|
|
25
|
+
omitted it defaults to ``value_type`` itself (so ``Key("year", int)`` casts
|
|
26
|
+
with ``int``). Use ``formatter`` for values not constructible straight from a
|
|
27
|
+
string, e.g. ``Key("released", date, formatter=date.fromisoformat)``.
|
|
28
|
+
|
|
29
|
+
Passing a key to a builder method (``key=...``) wires up both the match
|
|
30
|
+
``name`` and the formatter, so that ``matches[key]`` and ``matches.all(key)``
|
|
31
|
+
return precise types instead of ``Any``.
|
|
32
|
+
|
|
33
|
+
``value_type`` must be a scalar type (it/the formatter is applied to a single
|
|
34
|
+
matched substring). Structured types (``dataclass`` / ``TypedDict``) cannot
|
|
35
|
+
be built from one string and are rejected here; assemble them from several
|
|
36
|
+
named matches with :meth:`Matches.to` instead.
|
|
37
|
+
|
|
38
|
+
>>> from rebulk import Rebulk, Key
|
|
39
|
+
>>> year = Key("year", int)
|
|
40
|
+
>>> Rebulk().regex(r"\d{4}", key=year).matches("born in 1984")[year]
|
|
41
|
+
1984
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
name: str
|
|
45
|
+
value_type: type[T]
|
|
46
|
+
formatter: Callable[[str], T] | None = None
|
|
47
|
+
|
|
48
|
+
def __post_init__(self) -> None:
|
|
49
|
+
if is_dataclass(self.value_type) or is_typeddict(self.value_type):
|
|
50
|
+
raise TypeError(
|
|
51
|
+
f"Key value_type must be a scalar type, not {self.value_type!r}; "
|
|
52
|
+
"build structured types from several matches with Matches.to(...) instead"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def converter(self) -> Callable[[str], T]:
|
|
57
|
+
"""The ``(str) -> T`` converter: the explicit ``formatter`` or ``value_type``."""
|
|
58
|
+
return self.formatter if self.formatter is not None else self.value_type
|
|
@@ -10,7 +10,18 @@ import dataclasses
|
|
|
10
10
|
import itertools
|
|
11
11
|
from collections import OrderedDict, defaultdict
|
|
12
12
|
from collections.abc import Callable, Iterable, KeysView, MutableSequence
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import (
|
|
14
|
+
TYPE_CHECKING,
|
|
15
|
+
Any,
|
|
16
|
+
Literal,
|
|
17
|
+
TypeVar,
|
|
18
|
+
cast,
|
|
19
|
+
get_args,
|
|
20
|
+
get_origin,
|
|
21
|
+
get_type_hints,
|
|
22
|
+
is_typeddict,
|
|
23
|
+
overload,
|
|
24
|
+
)
|
|
14
25
|
|
|
15
26
|
from .debug import defined_at
|
|
16
27
|
from .key import Key
|
|
@@ -22,11 +33,17 @@ if TYPE_CHECKING:
|
|
|
22
33
|
|
|
23
34
|
T = TypeVar("T")
|
|
24
35
|
M = TypeVar("M")
|
|
36
|
+
_V = TypeVar("_V")
|
|
25
37
|
|
|
26
38
|
|
|
27
|
-
class MatchesDict(OrderedDict
|
|
39
|
+
class MatchesDict(OrderedDict[str | None, _V]):
|
|
28
40
|
"""
|
|
29
41
|
A custom dict with matches property.
|
|
42
|
+
|
|
43
|
+
Generic over the value type: ``to_dict(enforce_list=True)`` returns a
|
|
44
|
+
``MatchesDict[list[Any]]`` (every name maps to a list), the other modes a
|
|
45
|
+
``MatchesDict[Any]`` (a name maps to a single value or a list, depending on
|
|
46
|
+
the matches).
|
|
30
47
|
"""
|
|
31
48
|
|
|
32
49
|
def __init__(self) -> None:
|
|
@@ -714,7 +731,13 @@ class _BaseMatches(MutableSequence): # type: ignore[type-arg]
|
|
|
714
731
|
"""
|
|
715
732
|
return self._tag_dict.keys()
|
|
716
733
|
|
|
717
|
-
|
|
734
|
+
@overload
|
|
735
|
+
def to_dict(
|
|
736
|
+
self, details: bool = ..., first_value: bool = ..., *, enforce_list: Literal[True]
|
|
737
|
+
) -> MatchesDict[list[Any]]: ...
|
|
738
|
+
@overload
|
|
739
|
+
def to_dict(self, details: bool = ..., first_value: bool = ..., enforce_list: bool = ...) -> MatchesDict[Any]: ...
|
|
740
|
+
def to_dict(self, details: bool = False, first_value: bool = False, enforce_list: bool = False) -> MatchesDict[Any]:
|
|
718
741
|
"""
|
|
719
742
|
Converts matches to a dict object.
|
|
720
743
|
:param details if True, values will be complete Match object, else it will be only string Match.value property
|
|
@@ -728,7 +751,7 @@ class _BaseMatches(MutableSequence): # type: ignore[type-arg]
|
|
|
728
751
|
:return:
|
|
729
752
|
:rtype: dict
|
|
730
753
|
"""
|
|
731
|
-
ret = MatchesDict()
|
|
754
|
+
ret: MatchesDict[Any] = MatchesDict()
|
|
732
755
|
for match in sorted(self):
|
|
733
756
|
value = match if details else match.value
|
|
734
757
|
ret.matches[match.name].append(match)
|
|
@@ -6,6 +6,7 @@ Tests for typed Key retrieval (POC for type-safe value access).
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
+
from datetime import date
|
|
9
10
|
from typing import TYPE_CHECKING, TypedDict
|
|
10
11
|
|
|
11
12
|
import pytest
|
|
@@ -31,6 +32,20 @@ def test_key_scalar_retrieval() -> None:
|
|
|
31
32
|
assert matches[title] == "Big Buck Bunny"
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
def test_key_converter_defaults_to_value_type() -> None:
|
|
36
|
+
assert Key("year", int).converter is int
|
|
37
|
+
fmt = date.fromisoformat
|
|
38
|
+
assert Key("released", date, formatter=fmt).converter is fmt
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_key_with_explicit_formatter() -> None:
|
|
42
|
+
released = Key("released", date, formatter=date.fromisoformat)
|
|
43
|
+
matches = Rebulk().regex(r"\d{4}-\d{2}-\d{2}", key=released).matches("on 2008-01-02 ...")
|
|
44
|
+
|
|
45
|
+
assert matches[released] == date(2008, 1, 2)
|
|
46
|
+
assert matches.all(released) == [date(2008, 1, 2)]
|
|
47
|
+
|
|
48
|
+
|
|
34
49
|
def test_key_rejects_structured_value_type() -> None:
|
|
35
50
|
@dataclass
|
|
36
51
|
class Movie:
|
|
@@ -74,5 +89,8 @@ if TYPE_CHECKING:
|
|
|
74
89
|
assert_type(matches[year], "int | None")
|
|
75
90
|
assert_type(matches.all(year), list[int])
|
|
76
91
|
assert_type(matches[title], "str | None")
|
|
92
|
+
# A formatter-based key keeps the precise value type.
|
|
93
|
+
released = Key("released", date, formatter=date.fromisoformat)
|
|
94
|
+
assert_type(matches[released], "date | None")
|
|
77
95
|
# Existing integer / slice access stays intact.
|
|
78
96
|
assert_type(matches[0], Match)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
@@ -632,3 +632,7 @@ if TYPE_CHECKING:
|
|
|
632
632
|
assert_type(matches.named("title", index=0), "Match | None")
|
|
633
633
|
assert_type(matches.named("title", 0), "Match | None")
|
|
634
634
|
assert_type(matches.named("title", lambda m: True), "list[Match]")
|
|
635
|
+
|
|
636
|
+
def _to_dict_reveal_types(matches: Matches) -> None:
|
|
637
|
+
# enforce_list=True -> every value is a list (predictable, typable)
|
|
638
|
+
assert_type(matches.to_dict(enforce_list=True)["x"], list[Any])
|
rebulk-4.2.2/rebulk/key.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
"""
|
|
3
|
-
Typed keys binding a match name to its value type for type-safe retrieval.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from __future__ import annotations
|
|
7
|
-
|
|
8
|
-
from dataclasses import dataclass, is_dataclass
|
|
9
|
-
from typing import Generic, TypeVar, is_typeddict
|
|
10
|
-
|
|
11
|
-
T = TypeVar("T")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass(frozen=True)
|
|
15
|
-
class Key(Generic[T]):
|
|
16
|
-
r"""
|
|
17
|
-
A typed handle for a named match.
|
|
18
|
-
|
|
19
|
-
``name`` is the match name to declare and look up; ``value_type`` is the type
|
|
20
|
-
of the match value and the ``(str) -> T`` converter applied to produce it
|
|
21
|
-
(e.g. ``int``, ``str``, ``float``). Passing a key to a builder method
|
|
22
|
-
(``key=...``) wires up both the match ``name`` and ``formatter``, so that
|
|
23
|
-
``matches[key]`` and ``matches.all(key)`` return precise types instead of
|
|
24
|
-
``Any``.
|
|
25
|
-
|
|
26
|
-
``value_type`` must be a scalar ``(str) -> T`` converter: it is applied to a
|
|
27
|
-
single matched substring. Structured types (``dataclass`` / ``TypedDict``)
|
|
28
|
-
cannot be built from one string and are rejected here; assemble them from
|
|
29
|
-
several named matches with :meth:`Matches.to` instead.
|
|
30
|
-
|
|
31
|
-
>>> from rebulk import Rebulk, Key
|
|
32
|
-
>>> year = Key("year", int)
|
|
33
|
-
>>> Rebulk().regex(r"\d{4}", key=year).matches("born in 1984")[year]
|
|
34
|
-
1984
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
name: str
|
|
38
|
-
value_type: type[T]
|
|
39
|
-
|
|
40
|
-
def __post_init__(self) -> None:
|
|
41
|
-
if is_dataclass(self.value_type) or is_typeddict(self.value_type):
|
|
42
|
-
raise TypeError(
|
|
43
|
-
f"Key value_type must be a scalar (str) -> T converter, not {self.value_type!r}; "
|
|
44
|
-
"build structured types from several matches with Matches.to(...) instead"
|
|
45
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|