temparse 0.0.1__tar.gz → 0.0.3__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.
- {temparse-0.0.1 → temparse-0.0.3}/PKG-INFO +22 -17
- {temparse-0.0.1 → temparse-0.0.3}/README.md +21 -16
- {temparse-0.0.1 → temparse-0.0.3}/pyproject.toml +1 -1
- temparse-0.0.3/src/temparse/__init__.py +167 -0
- temparse-0.0.1/src/temparse/__init__.py +0 -110
- {temparse-0.0.1 → temparse-0.0.3}/src/temparse/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: temparse
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: temparse is template + parse
|
|
5
5
|
Author: jiang-zhexin
|
|
6
6
|
Author-email: jiang-zhexin <162887873+jiang-zhexin@users.noreply.github.com>
|
|
@@ -52,18 +52,23 @@ The return type is `tuple[str, int]`.
|
|
|
52
52
|
|
|
53
53
|
## Supported Types
|
|
54
54
|
|
|
55
|
-
| Interpolation | Example
|
|
56
|
-
| --------------------------------------- |
|
|
57
|
-
| `{str}` | `"hello"`
|
|
58
|
-
| `{int}` | `"42"`
|
|
59
|
-
| `{int}` | `"0xff"`
|
|
60
|
-
| `{int:16}` | `"ff"`
|
|
61
|
-
| `{float}` | `"3.14"`
|
|
62
|
-
| `{complex}` | `"-1.23+4.5j"`
|
|
63
|
-
| `{
|
|
64
|
-
| `{
|
|
65
|
-
| `{
|
|
66
|
-
| `{datetime.
|
|
55
|
+
| Interpolation | Example | Output |
|
|
56
|
+
| --------------------------------------- | --------------------- | ------------ |
|
|
57
|
+
| `{str}` | `"hello"` | `"hello"` |
|
|
58
|
+
| `{int}` | `"42"` | `42` |
|
|
59
|
+
| `{int}` | `"0xff"` | `255` |
|
|
60
|
+
| `{int:16}` | `"ff"` | `255` |
|
|
61
|
+
| `{float}` | `"3.14"` | `3.14` |
|
|
62
|
+
| `{complex}` | `"-1.23+4.5j"` | `-1.23+4.5j` |
|
|
63
|
+
| `{list}` | `'[1,2,3]'` | `[1, 2, 3]` |
|
|
64
|
+
| `{dict}` | `'{"a":1}'` | `{"a": 1}` |
|
|
65
|
+
| `{json}` | `'{"a":1}'` | `{"a": 1}` |
|
|
66
|
+
| `{datetime.datetime:%d/%m/%y %H:%M:%S}` | `"31/01/22 23:59:59"` | `datetime` |
|
|
67
|
+
| `{datetime.date:%Y-%m-%d}` | `"2024-03-15"` | `date` |
|
|
68
|
+
| `{datetime.time:%H:%M:%S}` | `"13:23:27"` | `time` |
|
|
69
|
+
|
|
70
|
+
> [!IMPORTANT]
|
|
71
|
+
> list and dict are just aliases for json.
|
|
67
72
|
|
|
68
73
|
## Custom Conversions
|
|
69
74
|
|
|
@@ -74,12 +79,12 @@ from temparse import Conversion, parse
|
|
|
74
79
|
|
|
75
80
|
|
|
76
81
|
@Conversion
|
|
77
|
-
def
|
|
78
|
-
return s.
|
|
82
|
+
def percent(s: str) -> float:
|
|
83
|
+
return float(s.rstrip("%")) / 100
|
|
79
84
|
|
|
80
85
|
|
|
81
|
-
(result,) = parse[
|
|
82
|
-
assert result ==
|
|
86
|
+
(result,) = parse[float](t"x = {percent}", "x = 30%")
|
|
87
|
+
assert result == 0.3
|
|
83
88
|
```
|
|
84
89
|
|
|
85
90
|
When your converter needs the format spec as well, use `FormatConversion`:
|
|
@@ -36,18 +36,23 @@ The return type is `tuple[str, int]`.
|
|
|
36
36
|
|
|
37
37
|
## Supported Types
|
|
38
38
|
|
|
39
|
-
| Interpolation | Example
|
|
40
|
-
| --------------------------------------- |
|
|
41
|
-
| `{str}` | `"hello"`
|
|
42
|
-
| `{int}` | `"42"`
|
|
43
|
-
| `{int}` | `"0xff"`
|
|
44
|
-
| `{int:16}` | `"ff"`
|
|
45
|
-
| `{float}` | `"3.14"`
|
|
46
|
-
| `{complex}` | `"-1.23+4.5j"`
|
|
47
|
-
| `{
|
|
48
|
-
| `{
|
|
49
|
-
| `{
|
|
50
|
-
| `{datetime.
|
|
39
|
+
| Interpolation | Example | Output |
|
|
40
|
+
| --------------------------------------- | --------------------- | ------------ |
|
|
41
|
+
| `{str}` | `"hello"` | `"hello"` |
|
|
42
|
+
| `{int}` | `"42"` | `42` |
|
|
43
|
+
| `{int}` | `"0xff"` | `255` |
|
|
44
|
+
| `{int:16}` | `"ff"` | `255` |
|
|
45
|
+
| `{float}` | `"3.14"` | `3.14` |
|
|
46
|
+
| `{complex}` | `"-1.23+4.5j"` | `-1.23+4.5j` |
|
|
47
|
+
| `{list}` | `'[1,2,3]'` | `[1, 2, 3]` |
|
|
48
|
+
| `{dict}` | `'{"a":1}'` | `{"a": 1}` |
|
|
49
|
+
| `{json}` | `'{"a":1}'` | `{"a": 1}` |
|
|
50
|
+
| `{datetime.datetime:%d/%m/%y %H:%M:%S}` | `"31/01/22 23:59:59"` | `datetime` |
|
|
51
|
+
| `{datetime.date:%Y-%m-%d}` | `"2024-03-15"` | `date` |
|
|
52
|
+
| `{datetime.time:%H:%M:%S}` | `"13:23:27"` | `time` |
|
|
53
|
+
|
|
54
|
+
> [!IMPORTANT]
|
|
55
|
+
> list and dict are just aliases for json.
|
|
51
56
|
|
|
52
57
|
## Custom Conversions
|
|
53
58
|
|
|
@@ -58,12 +63,12 @@ from temparse import Conversion, parse
|
|
|
58
63
|
|
|
59
64
|
|
|
60
65
|
@Conversion
|
|
61
|
-
def
|
|
62
|
-
return s.
|
|
66
|
+
def percent(s: str) -> float:
|
|
67
|
+
return float(s.rstrip("%")) / 100
|
|
63
68
|
|
|
64
69
|
|
|
65
|
-
(result,) = parse[
|
|
66
|
-
assert result ==
|
|
70
|
+
(result,) = parse[float](t"x = {percent}", "x = 30%")
|
|
71
|
+
assert result == 0.3
|
|
67
72
|
```
|
|
68
73
|
|
|
69
74
|
When your converter needs the format spec as well, use `FormatConversion`:
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import functools
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from string.templatelib import Interpolation, Template
|
|
8
|
+
from typing import Any, get_origin, is_typeddict
|
|
9
|
+
|
|
10
|
+
__all__ = ["Conversion", "FormatConversion", "Parser", "parse"]
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Parser[*T]:
|
|
16
|
+
"""
|
|
17
|
+
A compiled template parser that can be reused to parse multiple strings.
|
|
18
|
+
|
|
19
|
+
example:
|
|
20
|
+
```python
|
|
21
|
+
parser = Parser[str, int, float](t"{str} + {int} = {float}")
|
|
22
|
+
|
|
23
|
+
parser.parse("foo + 3 = 3.14")
|
|
24
|
+
```
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, template: Template, flags: re.RegexFlag = re.NOFLAG) -> None:
|
|
28
|
+
self._template = template
|
|
29
|
+
self._flags = flags
|
|
30
|
+
self._expression = "".join(
|
|
31
|
+
re.escape(item) if isinstance(item, str) else r"(.+?)" for item in template
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
log.debug(f"match expression: {self._expression!r}")
|
|
35
|
+
|
|
36
|
+
@functools.cached_property
|
|
37
|
+
def _match_re(self) -> re.Pattern[str]:
|
|
38
|
+
return re.compile(self._expression, self._flags)
|
|
39
|
+
|
|
40
|
+
def parse(self, s: str) -> tuple[*T]:
|
|
41
|
+
m = self._match_re.fullmatch(s)
|
|
42
|
+
if m is None:
|
|
43
|
+
raise ValueError(f"Invalid format: {s!r}")
|
|
44
|
+
|
|
45
|
+
return tuple(
|
|
46
|
+
_convert(text, interpolation)
|
|
47
|
+
for text, interpolation in zip(m.groups(), self._template.interpolations)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class parse[*T]:
|
|
52
|
+
"""
|
|
53
|
+
Parse string by template.
|
|
54
|
+
|
|
55
|
+
example:
|
|
56
|
+
```python
|
|
57
|
+
city, year = parse[str, int](
|
|
58
|
+
t"I live in {str}, since {int}", "I live in Tokyo, since 2010"
|
|
59
|
+
)
|
|
60
|
+
assert city == "Tokyo"
|
|
61
|
+
assert year == 2010
|
|
62
|
+
```
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __new__(cls, t: Template, s: str, flags: re.RegexFlag = re.NOFLAG) -> tuple[*T]:
|
|
66
|
+
"""
|
|
67
|
+
Used to replace this function until [PEP 718](https://peps.python.org/pep-0718/) is accepted.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
def parse[*T](t: Template, s: str, flags: re.RegexFlag = re.NOFLAG) -> tuple[*T]:
|
|
71
|
+
return Parser(t).parse(s)
|
|
72
|
+
```
|
|
73
|
+
"""
|
|
74
|
+
return Parser(t, flags).parse(s)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Conversion[T]:
|
|
78
|
+
"""
|
|
79
|
+
Convert the string to your object.
|
|
80
|
+
|
|
81
|
+
example:
|
|
82
|
+
```python
|
|
83
|
+
@Conversion
|
|
84
|
+
def percent(s: str) -> float:
|
|
85
|
+
return float(s.rstrip("%")) / 100
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
(result,) = parse[float](t"x = {percent}", "x = 30%")
|
|
89
|
+
assert result == 0.3
|
|
90
|
+
```
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
__name__: str
|
|
94
|
+
__qualname__: str
|
|
95
|
+
|
|
96
|
+
def __init__(self, func: Callable[[str], T]) -> None:
|
|
97
|
+
self._convert = func
|
|
98
|
+
functools.update_wrapper(self, func)
|
|
99
|
+
|
|
100
|
+
def __call__(self, s: str) -> T:
|
|
101
|
+
return self._convert(s)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class FormatConversion[T]:
|
|
105
|
+
"""
|
|
106
|
+
Convert the string to your object with format.
|
|
107
|
+
|
|
108
|
+
example:
|
|
109
|
+
```python
|
|
110
|
+
@FormatConversion
|
|
111
|
+
def between(s: str, spec: str) -> str:
|
|
112
|
+
lo, hi = spec.split(",")
|
|
113
|
+
return s[int(lo) : int(hi)]
|
|
114
|
+
|
|
115
|
+
(result,) = parse[str](t"{between:2,5}", "abcdefg")
|
|
116
|
+
assert result == "cde"
|
|
117
|
+
```
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
__name__: str
|
|
121
|
+
__qualname__: str
|
|
122
|
+
|
|
123
|
+
def __init__(self, func: Callable[[str, str], T]) -> None:
|
|
124
|
+
self._convert = func
|
|
125
|
+
functools.update_wrapper(self, func)
|
|
126
|
+
|
|
127
|
+
def __call__(self, s: str, format: str) -> T:
|
|
128
|
+
return self._convert(s, format)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _convert(s: str, i: Interpolation[Any]) -> Any:
|
|
132
|
+
origin = get_origin(i.value) or i.value
|
|
133
|
+
match origin:
|
|
134
|
+
case Conversion():
|
|
135
|
+
return i.value(s)
|
|
136
|
+
|
|
137
|
+
case FormatConversion():
|
|
138
|
+
return i.value(s, i.format_spec)
|
|
139
|
+
|
|
140
|
+
case datetime.datetime:
|
|
141
|
+
return datetime.datetime.strptime(s, i.format_spec).astimezone(datetime.UTC)
|
|
142
|
+
|
|
143
|
+
case datetime.date:
|
|
144
|
+
return datetime.date.strptime(s, i.format_spec)
|
|
145
|
+
|
|
146
|
+
case datetime.time:
|
|
147
|
+
return datetime.time.strptime(s, i.format_spec)
|
|
148
|
+
|
|
149
|
+
case _ if origin is int:
|
|
150
|
+
return int(s, base=int(i.format_spec) if i.format_spec else 0)
|
|
151
|
+
|
|
152
|
+
case _ if origin is float:
|
|
153
|
+
return float(s)
|
|
154
|
+
|
|
155
|
+
case _ if origin is complex:
|
|
156
|
+
return complex(s)
|
|
157
|
+
|
|
158
|
+
case _ if origin is str:
|
|
159
|
+
return s
|
|
160
|
+
|
|
161
|
+
case _ if (
|
|
162
|
+
origin is list or origin is dict or origin is json or is_typeddict(origin)
|
|
163
|
+
):
|
|
164
|
+
return json.loads(s)
|
|
165
|
+
|
|
166
|
+
case _:
|
|
167
|
+
raise TypeError(f"unkown object: {i.value}")
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import functools
|
|
3
|
-
import json
|
|
4
|
-
import logging
|
|
5
|
-
import re
|
|
6
|
-
from collections.abc import Callable
|
|
7
|
-
from string.templatelib import Interpolation, Template
|
|
8
|
-
from typing import Any
|
|
9
|
-
|
|
10
|
-
__all__ = ["Conversion", "FormatConversion", "Parser", "parse"]
|
|
11
|
-
|
|
12
|
-
log = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Parser[*T]:
|
|
16
|
-
def __init__(self, template: Template):
|
|
17
|
-
self.template = template
|
|
18
|
-
self.expression = f"^{
|
|
19
|
-
''.join(
|
|
20
|
-
re.escape(item) if isinstance(item, str) else r'(.+?)'
|
|
21
|
-
for item in template
|
|
22
|
-
)
|
|
23
|
-
}$"
|
|
24
|
-
log.debug(f"match expression: {self.expression!r}")
|
|
25
|
-
|
|
26
|
-
self._match_re = re.compile(self.expression)
|
|
27
|
-
|
|
28
|
-
def parse(self, s: str) -> tuple[*T]:
|
|
29
|
-
m = self._match_re.match(s)
|
|
30
|
-
if m is None:
|
|
31
|
-
raise ValueError(f"Invalid format: {s!r}")
|
|
32
|
-
|
|
33
|
-
return tuple(
|
|
34
|
-
_convert(text, interpolation)
|
|
35
|
-
for text, interpolation in zip(m.groups(), self.template.interpolations)
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class parse[*T]:
|
|
40
|
-
"""
|
|
41
|
-
Used to replace this function until [PEP 718](https://peps.python.org/pep-0718/) is accepted.
|
|
42
|
-
|
|
43
|
-
```python
|
|
44
|
-
def parse[*T](t: Template, s: str) -> tuple[*T]:
|
|
45
|
-
return Parser(t).parse(s)
|
|
46
|
-
```
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
def __new__(cls, t: Template, s: str) -> tuple[*T]:
|
|
50
|
-
return Parser(t).parse(s)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class Conversion[T]:
|
|
54
|
-
__name__: str
|
|
55
|
-
__qualname__: str
|
|
56
|
-
|
|
57
|
-
def __init__(self, func: Callable[[str], T]) -> None:
|
|
58
|
-
self._convert = func
|
|
59
|
-
functools.update_wrapper(self, func)
|
|
60
|
-
|
|
61
|
-
def __call__(self, s: str) -> T:
|
|
62
|
-
return self._convert(s)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class FormatConversion[T]:
|
|
66
|
-
__name__: str
|
|
67
|
-
__qualname__: str
|
|
68
|
-
|
|
69
|
-
def __init__(self, func: Callable[[str, str], T]) -> None:
|
|
70
|
-
self._convert = func
|
|
71
|
-
functools.update_wrapper(self, func)
|
|
72
|
-
|
|
73
|
-
def __call__(self, s: str, format: str) -> T:
|
|
74
|
-
return self._convert(s, format)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def _convert(s: str, i: Interpolation[Any]) -> Any:
|
|
78
|
-
match i.value:
|
|
79
|
-
case Conversion():
|
|
80
|
-
return i.value(s)
|
|
81
|
-
|
|
82
|
-
case FormatConversion():
|
|
83
|
-
return i.value(s, i.format_spec)
|
|
84
|
-
|
|
85
|
-
case datetime.datetime:
|
|
86
|
-
return datetime.datetime.strptime(s, i.format_spec).astimezone(datetime.UTC)
|
|
87
|
-
|
|
88
|
-
case datetime.date:
|
|
89
|
-
return datetime.date.strptime(s, i.format_spec)
|
|
90
|
-
|
|
91
|
-
case datetime.time:
|
|
92
|
-
return datetime.time.strptime(s, i.format_spec)
|
|
93
|
-
|
|
94
|
-
case _ if i.value is int:
|
|
95
|
-
return int(s, base=int(i.format_spec) if i.format_spec else 0)
|
|
96
|
-
|
|
97
|
-
case _ if i.value is float:
|
|
98
|
-
return float(s)
|
|
99
|
-
|
|
100
|
-
case _ if i.value is complex:
|
|
101
|
-
return complex(s)
|
|
102
|
-
|
|
103
|
-
case _ if i.value is str:
|
|
104
|
-
return s
|
|
105
|
-
|
|
106
|
-
case _ if i.value is json:
|
|
107
|
-
return json.loads(s)
|
|
108
|
-
|
|
109
|
-
case _:
|
|
110
|
-
raise TypeError(f"unkown object: {i.value}")
|
|
File without changes
|