haiway 0.3.0__py3-none-any.whl → 0.3.2__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 +13 -1
- haiway/context/metrics.py +23 -8
- haiway/helpers/__init__.py +6 -2
- haiway/helpers/{asynchronous.py → asynchrony.py} +12 -28
- haiway/helpers/tracing.py +136 -0
- haiway/state/validation.py +25 -1
- haiway/types/missing.py +7 -4
- {haiway-0.3.0.dist-info → haiway-0.3.2.dist-info}/METADATA +1 -1
- {haiway-0.3.0.dist-info → haiway-0.3.2.dist-info}/RECORD +13 -12
- {haiway-0.3.0.dist-info → haiway-0.3.2.dist-info}/WHEEL +1 -1
- /haiway/helpers/{cached.py → caching.py} +0 -0
- {haiway-0.3.0.dist-info → haiway-0.3.2.dist-info}/LICENSE +0 -0
- {haiway-0.3.0.dist-info → haiway-0.3.2.dist-info}/top_level.txt +0 -0
haiway/__init__.py
CHANGED
@@ -6,7 +6,16 @@ from haiway.context import (
|
|
6
6
|
ScopeMetrics,
|
7
7
|
ctx,
|
8
8
|
)
|
9
|
-
from haiway.helpers import
|
9
|
+
from haiway.helpers import (
|
10
|
+
ArgumentsTrace,
|
11
|
+
ResultTrace,
|
12
|
+
asynchronous,
|
13
|
+
cache,
|
14
|
+
retry,
|
15
|
+
throttle,
|
16
|
+
timeout,
|
17
|
+
traced,
|
18
|
+
)
|
10
19
|
from haiway.state import State
|
11
20
|
from haiway.types import (
|
12
21
|
MISSING,
|
@@ -34,6 +43,7 @@ from haiway.utils import (
|
|
34
43
|
|
35
44
|
__all__ = [
|
36
45
|
"always",
|
46
|
+
"ArgumentsTrace",
|
37
47
|
"async_always",
|
38
48
|
"async_noop",
|
39
49
|
"asynchronous",
|
@@ -57,11 +67,13 @@ __all__ = [
|
|
57
67
|
"MissingState",
|
58
68
|
"noop",
|
59
69
|
"not_missing",
|
70
|
+
"ResultTrace",
|
60
71
|
"retry",
|
61
72
|
"ScopeMetrics",
|
62
73
|
"setup_logging",
|
63
74
|
"State",
|
64
75
|
"throttle",
|
65
76
|
"timeout",
|
77
|
+
"traced",
|
66
78
|
"when_missing",
|
67
79
|
]
|
haiway/context/metrics.py
CHANGED
@@ -10,6 +10,7 @@ from typing import Any, Self, cast, final, overload
|
|
10
10
|
from uuid import uuid4
|
11
11
|
|
12
12
|
from haiway.state import State
|
13
|
+
from haiway.types import MISSING, Missing, not_missing
|
13
14
|
from haiway.utils import freeze
|
14
15
|
|
15
16
|
__all__ = [
|
@@ -28,7 +29,13 @@ class ScopeMetrics:
|
|
28
29
|
logger: Logger | None,
|
29
30
|
) -> None:
|
30
31
|
self.trace_id: str = trace_id or uuid4().hex
|
31
|
-
self.
|
32
|
+
self.identifier: str = uuid4().hex
|
33
|
+
self.label: str = scope
|
34
|
+
self._logger_prefix: str = (
|
35
|
+
f"[{self.trace_id}] [{scope}] [{self.identifier}]"
|
36
|
+
if scope
|
37
|
+
else f"[{self.trace_id}] [{self.identifier}]"
|
38
|
+
)
|
32
39
|
self._logger: Logger = logger or getLogger(name=scope)
|
33
40
|
self._metrics: dict[type[State], State] = {}
|
34
41
|
self._nested: list[ScopeMetrics] = []
|
@@ -41,21 +48,29 @@ class ScopeMetrics:
|
|
41
48
|
self._complete() # ensure completion on deinit
|
42
49
|
|
43
50
|
def __str__(self) -> str:
|
44
|
-
return self.
|
51
|
+
return f"{self.label}[{self.identifier}]@[{self.trace_id}]"
|
45
52
|
|
46
53
|
def metrics(
|
47
54
|
self,
|
48
55
|
*,
|
49
|
-
merge: Callable[[State, State], State]
|
56
|
+
merge: Callable[[State | Missing, State], State | Missing] | None = None,
|
50
57
|
) -> list[State]:
|
58
|
+
if not merge:
|
59
|
+
return list(self._metrics.values())
|
60
|
+
|
51
61
|
metrics: dict[type[State], State] = copy(self._metrics)
|
52
62
|
for metric in chain.from_iterable(nested.metrics(merge=merge) for nested in self._nested):
|
53
63
|
metric_type: type[State] = type(metric)
|
54
|
-
|
55
|
-
metrics
|
64
|
+
merged: State | Missing = merge(
|
65
|
+
metrics.get( # current
|
66
|
+
metric_type,
|
67
|
+
MISSING,
|
68
|
+
),
|
69
|
+
metric, # received
|
70
|
+
)
|
56
71
|
|
57
|
-
|
58
|
-
metrics[metric_type] =
|
72
|
+
if not_missing(merged):
|
73
|
+
metrics[metric_type] = merged
|
59
74
|
|
60
75
|
return list(metrics.values())
|
61
76
|
|
@@ -145,7 +160,7 @@ class ScopeMetrics:
|
|
145
160
|
) -> None:
|
146
161
|
self._logger.log(
|
147
162
|
level,
|
148
|
-
f"
|
163
|
+
f"{self._logger_prefix} {message}",
|
149
164
|
*args,
|
150
165
|
exc_info=exception,
|
151
166
|
)
|
haiway/helpers/__init__.py
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
from haiway.helpers.
|
2
|
-
from haiway.helpers.
|
1
|
+
from haiway.helpers.asynchrony import asynchronous
|
2
|
+
from haiway.helpers.caching import cache
|
3
3
|
from haiway.helpers.retries import retry
|
4
4
|
from haiway.helpers.throttling import throttle
|
5
5
|
from haiway.helpers.timeouted import timeout
|
6
|
+
from haiway.helpers.tracing import ArgumentsTrace, ResultTrace, traced
|
6
7
|
|
7
8
|
__all__ = [
|
9
|
+
"ArgumentsTrace",
|
8
10
|
"asynchronous",
|
9
11
|
"cache",
|
12
|
+
"ResultTrace",
|
10
13
|
"retry",
|
11
14
|
"throttle",
|
12
15
|
"timeout",
|
16
|
+
"traced",
|
13
17
|
]
|
@@ -3,9 +3,9 @@ from collections.abc import Callable, Coroutine
|
|
3
3
|
from concurrent.futures import Executor
|
4
4
|
from contextvars import Context, copy_context
|
5
5
|
from functools import partial
|
6
|
-
from typing import Any,
|
6
|
+
from typing import Any, cast, overload
|
7
7
|
|
8
|
-
from haiway.types.missing import MISSING, Missing
|
8
|
+
from haiway.types.missing import MISSING, Missing
|
9
9
|
|
10
10
|
__all__ = [
|
11
11
|
"asynchronous",
|
@@ -25,7 +25,7 @@ def asynchronous[**Args, Result]() -> (
|
|
25
25
|
def asynchronous[**Args, Result](
|
26
26
|
*,
|
27
27
|
loop: AbstractEventLoop | None = None,
|
28
|
-
executor: Executor
|
28
|
+
executor: Executor,
|
29
29
|
) -> Callable[
|
30
30
|
[Callable[Args, Result]],
|
31
31
|
Callable[Args, Coroutine[None, None, Result]],
|
@@ -43,7 +43,7 @@ def asynchronous[**Args, Result](
|
|
43
43
|
function: Callable[Args, Result] | None = None,
|
44
44
|
/,
|
45
45
|
loop: AbstractEventLoop | None = None,
|
46
|
-
executor: Executor |
|
46
|
+
executor: Executor | Missing = MISSING,
|
47
47
|
) -> (
|
48
48
|
Callable[
|
49
49
|
[Callable[Args, Result]],
|
@@ -63,10 +63,9 @@ def asynchronous[**Args, Result](
|
|
63
63
|
loop: AbstractEventLoop | None
|
64
64
|
loop used to call the function. When None was provided the loop currently running while \
|
65
65
|
executing the function will be used. Default is None.
|
66
|
-
executor: Executor |
|
67
|
-
executor used to run the function.
|
68
|
-
|
69
|
-
(function will by just wrapped as an async function without any executor)
|
66
|
+
executor: Executor | Missing
|
67
|
+
executor used to run the function. When not provided (Missing) default loop executor\
|
68
|
+
will be used.
|
70
69
|
|
71
70
|
Returns
|
72
71
|
-------
|
@@ -79,26 +78,11 @@ def asynchronous[**Args, Result](
|
|
79
78
|
) -> Callable[Args, Coroutine[None, None, Result]]:
|
80
79
|
assert not iscoroutinefunction(wrapped), "Cannot wrap async function in executor" # nosec: B101
|
81
80
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
)
|
88
|
-
|
89
|
-
else:
|
90
|
-
|
91
|
-
async def wrapper(
|
92
|
-
*args: Args.args,
|
93
|
-
**kwargs: Args.kwargs,
|
94
|
-
) -> Result:
|
95
|
-
return wrapped(
|
96
|
-
*args,
|
97
|
-
**kwargs,
|
98
|
-
)
|
99
|
-
|
100
|
-
_mimic_async(wrapped, within=wrapper)
|
101
|
-
return wrapper
|
81
|
+
return _ExecutorWrapper(
|
82
|
+
wrapped,
|
83
|
+
loop=loop,
|
84
|
+
executor=cast(Executor | None, None if executor is MISSING else executor),
|
85
|
+
)
|
102
86
|
|
103
87
|
if function := function:
|
104
88
|
return wrap(wrapped=function)
|
@@ -0,0 +1,136 @@
|
|
1
|
+
from asyncio import iscoroutinefunction
|
2
|
+
from collections.abc import Callable, Coroutine
|
3
|
+
from typing import Any, Self, cast
|
4
|
+
|
5
|
+
from haiway.context import ctx
|
6
|
+
from haiway.state import State
|
7
|
+
from haiway.types import MISSING, Missing
|
8
|
+
from haiway.utils import mimic_function
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"traced",
|
12
|
+
"ArgumentsTrace",
|
13
|
+
"ResultTrace",
|
14
|
+
]
|
15
|
+
|
16
|
+
|
17
|
+
class ArgumentsTrace(State):
|
18
|
+
if __debug__:
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def of(cls, *args: Any, **kwargs: Any) -> Self:
|
22
|
+
return cls(
|
23
|
+
args=args if args else MISSING,
|
24
|
+
kwargs=kwargs if kwargs else MISSING,
|
25
|
+
)
|
26
|
+
|
27
|
+
else: # remove tracing for non debug runs to prevent accidental secret leaks
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def of(cls, *args: Any, **kwargs: Any) -> Self:
|
31
|
+
return cls(
|
32
|
+
args=MISSING,
|
33
|
+
kwargs=MISSING,
|
34
|
+
)
|
35
|
+
|
36
|
+
args: tuple[Any, ...] | Missing
|
37
|
+
kwargs: dict[str, Any] | Missing
|
38
|
+
|
39
|
+
|
40
|
+
class ResultTrace(State):
|
41
|
+
if __debug__:
|
42
|
+
|
43
|
+
@classmethod
|
44
|
+
def of(
|
45
|
+
cls,
|
46
|
+
value: Any,
|
47
|
+
/,
|
48
|
+
) -> Self:
|
49
|
+
return cls(result=value)
|
50
|
+
|
51
|
+
else: # remove tracing for non debug runs to prevent accidental secret leaks
|
52
|
+
|
53
|
+
@classmethod
|
54
|
+
def of(
|
55
|
+
cls,
|
56
|
+
value: Any,
|
57
|
+
/,
|
58
|
+
) -> Self:
|
59
|
+
return cls(result=MISSING)
|
60
|
+
|
61
|
+
result: Any | Missing
|
62
|
+
|
63
|
+
|
64
|
+
def traced[**Args, Result](
|
65
|
+
function: Callable[Args, Result],
|
66
|
+
/,
|
67
|
+
) -> Callable[Args, Result]:
|
68
|
+
if __debug__:
|
69
|
+
if iscoroutinefunction(function):
|
70
|
+
return cast(
|
71
|
+
Callable[Args, Result],
|
72
|
+
_traced_async(
|
73
|
+
function,
|
74
|
+
label=function.__name__,
|
75
|
+
),
|
76
|
+
)
|
77
|
+
else:
|
78
|
+
return _traced_sync(
|
79
|
+
function,
|
80
|
+
label=function.__name__,
|
81
|
+
)
|
82
|
+
|
83
|
+
else: # do not trace on non debug runs
|
84
|
+
return function
|
85
|
+
|
86
|
+
|
87
|
+
def _traced_sync[**Args, Result](
|
88
|
+
function: Callable[Args, Result],
|
89
|
+
/,
|
90
|
+
label: str,
|
91
|
+
) -> Callable[Args, Result]:
|
92
|
+
def traced(
|
93
|
+
*args: Args.args,
|
94
|
+
**kwargs: Args.kwargs,
|
95
|
+
) -> Result:
|
96
|
+
with ctx.scope(label):
|
97
|
+
ctx.record(ArgumentsTrace.of(*args, **kwargs))
|
98
|
+
try:
|
99
|
+
result: Result = function(*args, **kwargs)
|
100
|
+
ctx.record(ResultTrace.of(result))
|
101
|
+
return result
|
102
|
+
|
103
|
+
except BaseException as exc:
|
104
|
+
ctx.record(ResultTrace.of(exc))
|
105
|
+
raise exc
|
106
|
+
|
107
|
+
return mimic_function(
|
108
|
+
function,
|
109
|
+
within=traced,
|
110
|
+
)
|
111
|
+
|
112
|
+
|
113
|
+
def _traced_async[**Args, Result](
|
114
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
115
|
+
/,
|
116
|
+
label: str,
|
117
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
118
|
+
async def traced(
|
119
|
+
*args: Args.args,
|
120
|
+
**kwargs: Args.kwargs,
|
121
|
+
) -> Result:
|
122
|
+
with ctx.scope(label):
|
123
|
+
ctx.record(ArgumentsTrace.of(*args, **kwargs))
|
124
|
+
try:
|
125
|
+
result: Result = await function(*args, **kwargs)
|
126
|
+
ctx.record(ResultTrace.of(result))
|
127
|
+
return result
|
128
|
+
|
129
|
+
except BaseException as exc:
|
130
|
+
ctx.record(ResultTrace.of(exc))
|
131
|
+
raise exc
|
132
|
+
|
133
|
+
return mimic_function(
|
134
|
+
function,
|
135
|
+
within=traced,
|
136
|
+
)
|
haiway/state/validation.py
CHANGED
@@ -11,7 +11,7 @@ __all__ = [
|
|
11
11
|
]
|
12
12
|
|
13
13
|
|
14
|
-
def attribute_type_validator(
|
14
|
+
def attribute_type_validator( # noqa: PLR0911
|
15
15
|
annotation: AttributeAnnotation,
|
16
16
|
/,
|
17
17
|
) -> Callable[[Any], Any]:
|
@@ -31,6 +31,10 @@ def attribute_type_validator(
|
|
31
31
|
case typing.Any:
|
32
32
|
return _any_validator
|
33
33
|
|
34
|
+
# typed dicts fail on type checks
|
35
|
+
case typed_dict if typing.is_typeddict(typed_dict):
|
36
|
+
return _prepare_typed_dict_validator(typed_dict)
|
37
|
+
|
34
38
|
case type() as other_type:
|
35
39
|
return _prepare_type_validator(other_type)
|
36
40
|
|
@@ -123,3 +127,23 @@ def _prepare_type_validator(
|
|
123
127
|
)
|
124
128
|
|
125
129
|
return type_validator
|
130
|
+
|
131
|
+
|
132
|
+
def _prepare_typed_dict_validator(
|
133
|
+
validated_type: type[Any],
|
134
|
+
/,
|
135
|
+
) -> Callable[[Any], Any]:
|
136
|
+
def typed_dict_validator(
|
137
|
+
value: Any,
|
138
|
+
) -> Any:
|
139
|
+
match value:
|
140
|
+
case value if isinstance(value, dict):
|
141
|
+
# for typed dicts check only if that is a dict
|
142
|
+
return value # pyright: ignore[reportUnknownVariableType]
|
143
|
+
|
144
|
+
case _:
|
145
|
+
raise TypeError(
|
146
|
+
f"Type '{type(value)}' is not matching expected type '{validated_type}'"
|
147
|
+
)
|
148
|
+
|
149
|
+
return typed_dict_validator
|
haiway/types/missing.py
CHANGED
@@ -27,6 +27,9 @@ class Missing(metaclass=MissingType):
|
|
27
27
|
Type representing absence of a value. Use MISSING constant for its value.
|
28
28
|
"""
|
29
29
|
|
30
|
+
__slots__ = ()
|
31
|
+
__match_args__ = ()
|
32
|
+
|
30
33
|
def __bool__(self) -> bool:
|
31
34
|
return False
|
32
35
|
|
@@ -42,24 +45,24 @@ class Missing(metaclass=MissingType):
|
|
42
45
|
def __repr__(self) -> str:
|
43
46
|
return "MISSING"
|
44
47
|
|
45
|
-
def
|
48
|
+
def __getattr__(
|
46
49
|
self,
|
47
50
|
name: str,
|
48
51
|
) -> Any:
|
49
|
-
raise
|
52
|
+
raise AttributeError("Missing has no attributes")
|
50
53
|
|
51
54
|
def __setattr__(
|
52
55
|
self,
|
53
56
|
__name: str,
|
54
57
|
__value: Any,
|
55
58
|
) -> None:
|
56
|
-
raise
|
59
|
+
raise AttributeError("Missing can't be modified")
|
57
60
|
|
58
61
|
def __delattr__(
|
59
62
|
self,
|
60
63
|
__name: str,
|
61
64
|
) -> None:
|
62
|
-
raise
|
65
|
+
raise AttributeError("Missing can't be modified")
|
63
66
|
|
64
67
|
|
65
68
|
MISSING: Final[Missing] = Missing()
|
@@ -1,25 +1,26 @@
|
|
1
|
-
haiway/__init__.py,sha256=
|
1
|
+
haiway/__init__.py,sha256=hLc3-FDmNQEV4r-RLOiGjWtYSk7krU8vRMBaZyRU08g,1267
|
2
2
|
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
haiway/context/__init__.py,sha256=21Y3zvRo1bHASZD6B_FNkU28k1-g88RdmUyqxvYXJxg,336
|
4
4
|
haiway/context/access.py,sha256=E9aIC1RUupKk2LXik5qb13oHpW2s_tNQC0UxGcO9xr0,12761
|
5
5
|
haiway/context/disposables.py,sha256=VQX9jVo1pjqkmOYzWpsbYyF45y0XtpjorIIaeMBCwTU,1771
|
6
|
-
haiway/context/metrics.py,sha256=
|
6
|
+
haiway/context/metrics.py,sha256=TGZzNrmGPolAwVb1mBEF10mXRyVyBq-MHTucAokNuhY,9105
|
7
7
|
haiway/context/state.py,sha256=GxGwPQTK8FdSprBd83lQbA9veubp0o93_1Yk3gb7HMc,3000
|
8
8
|
haiway/context/tasks.py,sha256=xXtXIUwXOra0EePTdkcEbMOmpWwFcO3hCRfR_IfvAHk,1978
|
9
9
|
haiway/context/types.py,sha256=VvJA7wAPZ3ISpgyThVguioYUXqhHf0XkPfRd0M1ERiQ,142
|
10
|
-
haiway/helpers/__init__.py,sha256=
|
11
|
-
haiway/helpers/
|
12
|
-
haiway/helpers/
|
10
|
+
haiway/helpers/__init__.py,sha256=YYEORuo3xyce5_kGjfQVyaQHyp1dBh3ZlrZavR0hQzk,443
|
11
|
+
haiway/helpers/asynchrony.py,sha256=FPqmFFRDtAn8migwYHFKViKHypNHZW3cJCrh9y03AwI,5526
|
12
|
+
haiway/helpers/caching.py,sha256=Ok_WE5Whe7XqnIuLZo4rNNBFeWap-aUWX799s4b1JAQ,9536
|
13
13
|
haiway/helpers/retries.py,sha256=gIkyUlqJLDYaxIZd3qzeqGFY9y5Gp8dgZLlZ6hs8hoc,7538
|
14
14
|
haiway/helpers/throttling.py,sha256=zo0OwFq64si5KUwhd58cFHLmGAmYwRbFRJMbv9suhPs,3844
|
15
15
|
haiway/helpers/timeouted.py,sha256=1xU09hQnFdj6p48BwZl5xUvtIr3zC0ZUXehkdrduCjs,3074
|
16
|
+
haiway/helpers/tracing.py,sha256=yiK8MdDyX_fmpK9Zu5-IiZae5E8ReKQtRBBenXIPVqQ,3326
|
16
17
|
haiway/state/__init__.py,sha256=dh7l_ZImy0uHHDGD-fzMhQFmz_ej8WU8WEE2OmIoyVM,204
|
17
18
|
haiway/state/attributes.py,sha256=kkIYNlvWCM1NkgiCbE6gZDwgBVOk_TkmqWv67MmU0to,13399
|
18
19
|
haiway/state/structure.py,sha256=G-Ln72hoQtE0FmKHeZdNmXf_FA3f5-e5AGbmJ2yMNb4,7003
|
19
|
-
haiway/state/validation.py,sha256=
|
20
|
+
haiway/state/validation.py,sha256=Z6kp_KjTnnP9eVWsLmzKkEQLZkhFCOSphjdbr6VxLFQ,3628
|
20
21
|
haiway/types/__init__.py,sha256=cAJQzDgFi8AKRqpzY3HWrutaPR69tnJqeJK_mQVtYUk,252
|
21
22
|
haiway/types/frozen.py,sha256=CZhFCXnWAKEhuWSfILxA8smfdpMd5Ku694ycfLh98R8,76
|
22
|
-
haiway/types/missing.py,sha256=
|
23
|
+
haiway/types/missing.py,sha256=JiXo5xdi7H-PbIJr0fuK5wpOuQZhjrDYUkMlfIFcsaE,1705
|
23
24
|
haiway/utils/__init__.py,sha256=UA9h8YDvYI5rYujvsIS9t5Q-SWYImmk30uhR_42flqs,608
|
24
25
|
haiway/utils/always.py,sha256=2abp8Lm9rQkrfS3rm1Iqhb-IcWyVfH1BULab3KMxgOw,1234
|
25
26
|
haiway/utils/env.py,sha256=lKPOBZWyRD_gQariDGBjVLYTm0740nytPCSQpK2oRyE,3136
|
@@ -28,8 +29,8 @@ haiway/utils/logs.py,sha256=oDsc1ZdqKDjlTlctLbDcp9iX98Acr-1tdw-Pyg3DElo,1577
|
|
28
29
|
haiway/utils/mimic.py,sha256=BkVjTVP2TxxC8GChPGyDV6UXVwJmiRiSWeOYZNZFHxs,1828
|
29
30
|
haiway/utils/noop.py,sha256=qgbZlOKWY6_23Zs43OLukK2HagIQKRyR04zrFVm5rWI,344
|
30
31
|
haiway/utils/queue.py,sha256=WGW8kSusIwRYHsYRIKD2CaqhhC1pUtVgtNHFDXDtYrw,2443
|
31
|
-
haiway-0.3.
|
32
|
-
haiway-0.3.
|
33
|
-
haiway-0.3.
|
34
|
-
haiway-0.3.
|
35
|
-
haiway-0.3.
|
32
|
+
haiway-0.3.2.dist-info/LICENSE,sha256=GehQEW_I1pkmxkkj3NEa7rCTQKYBn7vTPabpDYJlRuo,1063
|
33
|
+
haiway-0.3.2.dist-info/METADATA,sha256=Ze8RmuhIj2Ck3SVea8h3VbaTSf43_Y57oISLZKytNOA,3872
|
34
|
+
haiway-0.3.2.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
35
|
+
haiway-0.3.2.dist-info/top_level.txt,sha256=_LdXVLzUzgkvAGQnQJj5kQfoFhpPW6EF4Kj9NapniLg,7
|
36
|
+
haiway-0.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|