haiway 0.26.0__py3-none-any.whl → 0.27.0__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.
- haiway/__init__.py +10 -0
- haiway/context/__init__.py +2 -0
- haiway/context/access.py +4 -0
- haiway/context/observability.py +11 -1
- haiway/helpers/concurrent.py +8 -9
- haiway/helpers/files.py +4 -4
- haiway/helpers/observability.py +8 -2
- haiway/opentelemetry/observability.py +60 -14
- haiway/utils/__init__.py +12 -0
- haiway/utils/collections.py +31 -31
- haiway/utils/formatting.py +126 -36
- haiway/utils/metadata.py +480 -0
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/METADATA +1 -1
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/RECORD +16 -15
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/WHEEL +0 -0
- {haiway-0.26.0.dist-info → haiway-0.27.0.dist-info}/licenses/LICENSE +0 -0
haiway/utils/formatting.py
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
-
from collections.abc import ItemsView, Mapping, Sequence
|
1
|
+
from collections.abc import ItemsView, Mapping, Sequence, Set
|
2
|
+
from datetime import datetime
|
2
3
|
from typing import Any
|
4
|
+
from uuid import UUID
|
3
5
|
|
4
6
|
from haiway.types.missing import MISSING
|
5
7
|
|
6
8
|
__all__ = ("format_str",)
|
7
9
|
|
8
10
|
|
9
|
-
def format_str( # noqa: PLR0911
|
11
|
+
def format_str( # noqa: PLR0911 PLR0912 C901
|
10
12
|
value: Any,
|
11
13
|
/,
|
14
|
+
*,
|
15
|
+
indent: int = 0,
|
12
16
|
) -> str:
|
13
17
|
"""
|
14
18
|
Format any Python value into a readable string representation.
|
@@ -37,108 +41,153 @@ def format_str( # noqa: PLR0911
|
|
37
41
|
- MISSING values are converted to empty strings
|
38
42
|
- Nested structures maintain proper indentation
|
39
43
|
"""
|
40
|
-
|
41
|
-
|
44
|
+
if value is None:
|
45
|
+
return "None"
|
46
|
+
|
47
|
+
elif isinstance(value, str):
|
42
48
|
if "\n" in value:
|
43
|
-
|
49
|
+
indent_str = " " * (indent + 2)
|
50
|
+
indented_value = value.replace("\n", f"\n{indent_str}")
|
51
|
+
return f'"""\n{indent_str}{indented_value}\n{" " * indent}"""'
|
44
52
|
|
45
53
|
else:
|
46
54
|
return f'"{value}"'
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
56
|
+
elif isinstance(value, int | float | complex):
|
57
|
+
return str(value)
|
58
|
+
|
59
|
+
elif isinstance(value, bool):
|
60
|
+
return str(value)
|
61
|
+
|
62
|
+
elif isinstance(value, set | frozenset | Set):
|
63
|
+
return _set_str(
|
64
|
+
value,
|
65
|
+
indent=indent,
|
66
|
+
)
|
51
67
|
|
52
|
-
# try unpack mapping
|
53
68
|
elif isinstance(value, Mapping):
|
54
|
-
return _mapping_str(
|
69
|
+
return _mapping_str(
|
70
|
+
value,
|
71
|
+
indent=indent,
|
72
|
+
)
|
55
73
|
|
56
|
-
# try unpack sequence
|
57
74
|
elif isinstance(value, Sequence):
|
58
|
-
return _sequence_str(
|
75
|
+
return _sequence_str(
|
76
|
+
value,
|
77
|
+
indent=indent,
|
78
|
+
)
|
59
79
|
|
60
80
|
elif value is MISSING:
|
61
81
|
return ""
|
62
82
|
|
83
|
+
elif isinstance(value, UUID):
|
84
|
+
return str(value)
|
85
|
+
|
86
|
+
elif isinstance(value, datetime):
|
87
|
+
return value.isoformat()
|
88
|
+
|
89
|
+
elif isinstance(value, bytes):
|
90
|
+
return repr(value)
|
91
|
+
|
63
92
|
else: # fallback to object
|
64
|
-
return _object_str(
|
93
|
+
return _object_str(
|
94
|
+
value,
|
95
|
+
indent=indent,
|
96
|
+
)
|
65
97
|
|
66
98
|
|
67
99
|
def _attribute_str(
|
68
100
|
*,
|
69
101
|
key: str,
|
70
102
|
value: str,
|
103
|
+
indent: int,
|
71
104
|
) -> str:
|
105
|
+
indent_str = " " * indent
|
72
106
|
if "\n" in value:
|
73
|
-
|
74
|
-
return f"┝ {key}:\n{
|
107
|
+
# Don't add extra indentation - value should already handle it
|
108
|
+
return f"{indent_str}┝ {key}:\n{value}"
|
75
109
|
|
76
110
|
else:
|
77
|
-
return f"┝ {key}: {value}"
|
111
|
+
return f"{indent_str}┝ {key}: {value}"
|
78
112
|
|
79
113
|
|
80
114
|
def _element_str(
|
81
115
|
*,
|
82
116
|
key: Any,
|
83
|
-
value:
|
117
|
+
value: str,
|
118
|
+
indent: int,
|
84
119
|
) -> str:
|
120
|
+
indent_str = " " * indent
|
85
121
|
if "\n" in value:
|
86
|
-
|
87
|
-
return f"[{key}]:\n{
|
122
|
+
# Don't add extra indentation - value should already handle it
|
123
|
+
return f"{indent_str}[{key}]:\n{value}"
|
88
124
|
|
89
125
|
else:
|
90
|
-
return f"[{key}]: {value}"
|
126
|
+
return f"{indent_str}[{key}]: {value}"
|
91
127
|
|
92
128
|
|
93
129
|
def _object_str(
|
94
130
|
other: object,
|
95
131
|
/,
|
132
|
+
*,
|
133
|
+
indent: int,
|
96
134
|
) -> str:
|
135
|
+
indent_str: str = " " * indent
|
97
136
|
if not hasattr(other, "__dict__"):
|
98
|
-
return
|
137
|
+
return f"{indent_str}{other}"
|
99
138
|
|
100
139
|
variables: ItemsView[str, Any] = vars(other).items()
|
101
|
-
|
102
|
-
parts: list[str] = [
|
140
|
+
header = f"{indent_str}┍━ {type(other).__name__}:"
|
141
|
+
parts: list[str] = [header]
|
103
142
|
for key, value in variables:
|
104
143
|
if key.startswith("_"):
|
105
144
|
continue # skip private and dunder
|
106
145
|
|
107
|
-
value_string: str = format_str(
|
146
|
+
value_string: str = format_str(
|
147
|
+
value,
|
148
|
+
indent=indent + 2,
|
149
|
+
)
|
108
150
|
|
109
151
|
if value_string:
|
110
152
|
parts.append(
|
111
153
|
_attribute_str(
|
112
154
|
key=key,
|
113
155
|
value=value_string,
|
156
|
+
indent=indent,
|
114
157
|
)
|
115
158
|
)
|
116
159
|
|
117
160
|
else:
|
118
161
|
continue # skip empty elements
|
119
162
|
|
120
|
-
|
121
|
-
return "\n".join(parts) + "\n┕━"
|
122
|
-
|
123
|
-
else:
|
124
|
-
return ""
|
163
|
+
return "\n".join(parts) + f"\n{indent_str}┕━"
|
125
164
|
|
126
165
|
|
127
166
|
def _mapping_str(
|
128
167
|
mapping: Mapping[Any, Any],
|
129
168
|
/,
|
169
|
+
*,
|
170
|
+
indent: int,
|
130
171
|
) -> str:
|
131
172
|
items: ItemsView[Any, Any] = mapping.items()
|
132
173
|
|
174
|
+
indent_str = " " * indent
|
133
175
|
parts: list[str] = []
|
134
176
|
for key, value in items:
|
135
|
-
value_string: str = format_str(
|
177
|
+
value_string: str = format_str(
|
178
|
+
value,
|
179
|
+
indent=indent + 2,
|
180
|
+
)
|
136
181
|
|
137
182
|
if value_string:
|
138
183
|
parts.append(
|
139
184
|
_element_str(
|
140
|
-
key=
|
185
|
+
key=format_str(
|
186
|
+
key,
|
187
|
+
indent=indent + 2,
|
188
|
+
),
|
141
189
|
value=value_string,
|
190
|
+
indent=indent + 2,
|
142
191
|
)
|
143
192
|
)
|
144
193
|
|
@@ -146,25 +195,64 @@ def _mapping_str(
|
|
146
195
|
continue # skip empty items
|
147
196
|
|
148
197
|
if parts:
|
149
|
-
|
198
|
+
open_brace = "{\n" if indent == 0 else f"{indent_str}{{\n"
|
199
|
+
close_brace = "\n}" if indent == 0 else f"\n{indent_str}}}"
|
200
|
+
return open_brace + "\n".join(parts) + close_brace
|
150
201
|
|
151
202
|
else:
|
152
|
-
return "{}"
|
203
|
+
return "{}" if indent == 0 else f"{indent_str}{{}}"
|
204
|
+
|
205
|
+
|
206
|
+
def _set_str(
|
207
|
+
set_value: Set[Any] | set[Any] | frozenset[Any],
|
208
|
+
/,
|
209
|
+
*,
|
210
|
+
indent: int,
|
211
|
+
) -> str:
|
212
|
+
indent_str: str = " " * indent
|
213
|
+
element_indent_str: str = " " * (indent + 2)
|
214
|
+
parts: list[str] = []
|
215
|
+
for element in set_value:
|
216
|
+
element_string: str = format_str(
|
217
|
+
element,
|
218
|
+
indent=indent + 2,
|
219
|
+
)
|
220
|
+
|
221
|
+
if element_string:
|
222
|
+
parts.append(f"{element_indent_str}{element_string}")
|
223
|
+
|
224
|
+
else:
|
225
|
+
continue # skip empty elements
|
226
|
+
|
227
|
+
if parts:
|
228
|
+
open_brace: str = f"{indent_str}{{\n"
|
229
|
+
close_brace: str = f"\n{indent_str}}}"
|
230
|
+
return open_brace + ",\n".join(parts) + close_brace
|
231
|
+
|
232
|
+
else:
|
233
|
+
return f"{indent_str}{{}}"
|
153
234
|
|
154
235
|
|
155
236
|
def _sequence_str(
|
156
237
|
sequence: Sequence[Any],
|
157
238
|
/,
|
239
|
+
*,
|
240
|
+
indent: int,
|
158
241
|
) -> str:
|
242
|
+
indent_str: str = " " * indent
|
159
243
|
parts: list[str] = []
|
160
244
|
for idx, element in enumerate(sequence):
|
161
|
-
element_string: str = format_str(
|
245
|
+
element_string: str = format_str(
|
246
|
+
element,
|
247
|
+
indent=indent + 2,
|
248
|
+
)
|
162
249
|
|
163
250
|
if element_string:
|
164
251
|
parts.append(
|
165
252
|
_element_str(
|
166
253
|
key=idx,
|
167
254
|
value=element_string,
|
255
|
+
indent=indent + 2,
|
168
256
|
)
|
169
257
|
)
|
170
258
|
|
@@ -172,7 +260,9 @@ def _sequence_str(
|
|
172
260
|
continue # skip empty elements
|
173
261
|
|
174
262
|
if parts:
|
175
|
-
|
263
|
+
open_bracket: str = f"{indent_str}[\n"
|
264
|
+
close_bracket: str = f"\n{indent_str}]"
|
265
|
+
return open_bracket + "\n".join(parts) + close_bracket
|
176
266
|
|
177
267
|
else:
|
178
|
-
return "[]"
|
268
|
+
return f"{indent_str}[]"
|