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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: temparse
3
- Version: 0.0.1
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 | 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
- | `{json}` | `'[1,2,3]'`, `'{"a":1}'` | `[1,2,3]`, `{"a":1}` |
64
- | `{datetime.datetime:%d/%m/%y %H:%M:%S}` | `"31/01/22 23:59:59"` | `datetime` |
65
- | `{datetime.date:%Y-%m-%d}` | `"2024-03-15"` | `date` |
66
- | `{datetime.time:%H:%M:%S}` | `"13:23:27"` | `time` |
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 upper(s: str) -> str:
78
- return s.upper()
82
+ def percent(s: str) -> float:
83
+ return float(s.rstrip("%")) / 100
79
84
 
80
85
 
81
- (result,) = parse[str](t"{upper}", "hello")
82
- assert result == "HELLO"
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 | 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
- | `{json}` | `'[1,2,3]'`, `'{"a":1}'` | `[1,2,3]`, `{"a":1}` |
48
- | `{datetime.datetime:%d/%m/%y %H:%M:%S}` | `"31/01/22 23:59:59"` | `datetime` |
49
- | `{datetime.date:%Y-%m-%d}` | `"2024-03-15"` | `date` |
50
- | `{datetime.time:%H:%M:%S}` | `"13:23:27"` | `time` |
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 upper(s: str) -> str:
62
- return s.upper()
66
+ def percent(s: str) -> float:
67
+ return float(s.rstrip("%")) / 100
63
68
 
64
69
 
65
- (result,) = parse[str](t"{upper}", "hello")
66
- assert result == "HELLO"
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`:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "temparse"
3
- version = "0.0.1"
3
+ version = "0.0.3"
4
4
  license = "MIT"
5
5
  requires-python = ">=3.14"
6
6
  readme = "README.md"
@@ -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