haiway 0.10.14__py3-none-any.whl → 0.10.16__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 +111 -0
- haiway/context/__init__.py +27 -0
- haiway/context/access.py +615 -0
- haiway/context/disposables.py +78 -0
- haiway/context/identifier.py +92 -0
- haiway/context/logging.py +176 -0
- haiway/context/metrics.py +165 -0
- haiway/context/state.py +113 -0
- haiway/context/tasks.py +64 -0
- haiway/context/types.py +12 -0
- haiway/helpers/__init__.py +21 -0
- haiway/helpers/asynchrony.py +225 -0
- haiway/helpers/caching.py +326 -0
- haiway/helpers/metrics.py +459 -0
- haiway/helpers/retries.py +223 -0
- haiway/helpers/throttling.py +133 -0
- haiway/helpers/timeouted.py +112 -0
- haiway/helpers/tracing.py +137 -0
- haiway/py.typed +0 -0
- haiway/state/__init__.py +12 -0
- haiway/state/attributes.py +747 -0
- haiway/state/path.py +524 -0
- haiway/state/requirement.py +229 -0
- haiway/state/structure.py +414 -0
- haiway/state/validation.py +468 -0
- haiway/types/__init__.py +14 -0
- haiway/types/default.py +108 -0
- haiway/types/frozen.py +5 -0
- haiway/types/missing.py +95 -0
- haiway/utils/__init__.py +28 -0
- haiway/utils/always.py +61 -0
- haiway/utils/collections.py +185 -0
- haiway/utils/env.py +230 -0
- haiway/utils/freezing.py +28 -0
- haiway/utils/logs.py +57 -0
- haiway/utils/mimic.py +77 -0
- haiway/utils/noop.py +24 -0
- haiway/utils/queue.py +82 -0
- {haiway-0.10.14.dist-info → haiway-0.10.16.dist-info}/METADATA +1 -1
- haiway-0.10.16.dist-info/RECORD +42 -0
- haiway-0.10.14.dist-info/RECORD +0 -4
- {haiway-0.10.14.dist-info → haiway-0.10.16.dist-info}/WHEEL +0 -0
- {haiway-0.10.14.dist-info → haiway-0.10.16.dist-info}/licenses/LICENSE +0 -0
haiway/utils/always.py
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
from collections.abc import Callable, Coroutine
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
__all__ = [
|
5
|
+
"always",
|
6
|
+
"async_always",
|
7
|
+
]
|
8
|
+
|
9
|
+
|
10
|
+
def always[Value](
|
11
|
+
value: Value,
|
12
|
+
/,
|
13
|
+
) -> Callable[..., Value]:
|
14
|
+
"""
|
15
|
+
Factory method creating functions returning always the same value.
|
16
|
+
|
17
|
+
Parameters
|
18
|
+
----------
|
19
|
+
value: Value
|
20
|
+
value to be always returned from prepared function
|
21
|
+
|
22
|
+
Returns
|
23
|
+
-------
|
24
|
+
Callable[..., Value]
|
25
|
+
function ignoring arguments and always returning the provided value.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def always_value(
|
29
|
+
*args: Any,
|
30
|
+
**kwargs: Any,
|
31
|
+
) -> Value:
|
32
|
+
return value
|
33
|
+
|
34
|
+
return always_value
|
35
|
+
|
36
|
+
|
37
|
+
def async_always[Value](
|
38
|
+
value: Value,
|
39
|
+
/,
|
40
|
+
) -> Callable[..., Coroutine[None, None, Value]]:
|
41
|
+
"""
|
42
|
+
Factory method creating async functions returning always the same value.
|
43
|
+
|
44
|
+
Parameters
|
45
|
+
----------
|
46
|
+
value: Value
|
47
|
+
value to be always returned from prepared function
|
48
|
+
|
49
|
+
Returns
|
50
|
+
-------
|
51
|
+
Callable[..., Coroutine[None, None, Value]]
|
52
|
+
async function ignoring arguments and always returning the provided value.
|
53
|
+
"""
|
54
|
+
|
55
|
+
async def always_value(
|
56
|
+
*args: Any,
|
57
|
+
**kwargs: Any,
|
58
|
+
) -> Value:
|
59
|
+
return value
|
60
|
+
|
61
|
+
return always_value
|
@@ -0,0 +1,185 @@
|
|
1
|
+
from collections.abc import Mapping, Sequence, Set
|
2
|
+
from typing import overload
|
3
|
+
|
4
|
+
__all__ = [
|
5
|
+
"as_dict",
|
6
|
+
"as_list",
|
7
|
+
"as_set",
|
8
|
+
"as_tuple",
|
9
|
+
]
|
10
|
+
|
11
|
+
|
12
|
+
@overload
|
13
|
+
def as_list[T](
|
14
|
+
collection: Sequence[T],
|
15
|
+
/,
|
16
|
+
) -> list[T]: ...
|
17
|
+
|
18
|
+
|
19
|
+
@overload
|
20
|
+
def as_list[T](
|
21
|
+
collection: Sequence[T] | None,
|
22
|
+
/,
|
23
|
+
) -> list[T] | None: ...
|
24
|
+
|
25
|
+
|
26
|
+
def as_list[T](
|
27
|
+
collection: Sequence[T] | None,
|
28
|
+
/,
|
29
|
+
) -> list[T] | None:
|
30
|
+
"""
|
31
|
+
Converts any given Sequence into a list.
|
32
|
+
|
33
|
+
Parameters
|
34
|
+
----------
|
35
|
+
collection : Sequence[T] | None
|
36
|
+
The input collection to be converted.
|
37
|
+
|
38
|
+
Returns
|
39
|
+
-------
|
40
|
+
list[T] | None
|
41
|
+
A new list containing all elements of the input collection,\
|
42
|
+
or the original list if it was already one.
|
43
|
+
None if no value was provided.
|
44
|
+
"""
|
45
|
+
|
46
|
+
if collection is None:
|
47
|
+
return None
|
48
|
+
|
49
|
+
if isinstance(collection, list):
|
50
|
+
return collection
|
51
|
+
|
52
|
+
else:
|
53
|
+
return list(collection)
|
54
|
+
|
55
|
+
|
56
|
+
@overload
|
57
|
+
def as_tuple[T](
|
58
|
+
collection: Sequence[T],
|
59
|
+
/,
|
60
|
+
) -> tuple[T, ...]: ...
|
61
|
+
|
62
|
+
|
63
|
+
@overload
|
64
|
+
def as_tuple[T](
|
65
|
+
collection: Sequence[T] | None,
|
66
|
+
/,
|
67
|
+
) -> tuple[T, ...] | None: ...
|
68
|
+
|
69
|
+
|
70
|
+
def as_tuple[T](
|
71
|
+
collection: Sequence[T] | None,
|
72
|
+
/,
|
73
|
+
) -> tuple[T, ...] | None:
|
74
|
+
"""
|
75
|
+
Converts any given Sequence into a tuple.
|
76
|
+
|
77
|
+
Parameters
|
78
|
+
----------
|
79
|
+
collection : Sequence[T] | None
|
80
|
+
The input collection to be converted.
|
81
|
+
|
82
|
+
Returns
|
83
|
+
-------
|
84
|
+
tuple[T] | None
|
85
|
+
A new tuple containing all elements of the input collection,\
|
86
|
+
or the original tuple if it was already one.
|
87
|
+
None if no value was provided.
|
88
|
+
"""
|
89
|
+
|
90
|
+
if collection is None:
|
91
|
+
return None
|
92
|
+
|
93
|
+
if isinstance(collection, tuple):
|
94
|
+
return collection
|
95
|
+
|
96
|
+
else:
|
97
|
+
return tuple(collection)
|
98
|
+
|
99
|
+
|
100
|
+
@overload
|
101
|
+
def as_set[T](
|
102
|
+
collection: Set[T],
|
103
|
+
/,
|
104
|
+
) -> set[T]: ...
|
105
|
+
|
106
|
+
|
107
|
+
@overload
|
108
|
+
def as_set[T](
|
109
|
+
collection: Set[T] | None,
|
110
|
+
/,
|
111
|
+
) -> set[T] | None: ...
|
112
|
+
|
113
|
+
|
114
|
+
def as_set[T](
|
115
|
+
collection: Set[T] | None,
|
116
|
+
/,
|
117
|
+
) -> set[T] | None:
|
118
|
+
"""
|
119
|
+
Converts any given Set into a set.
|
120
|
+
|
121
|
+
Parameters
|
122
|
+
----------
|
123
|
+
collection : Set[T]
|
124
|
+
The input collection to be converted.
|
125
|
+
|
126
|
+
Returns
|
127
|
+
-------
|
128
|
+
set[T]
|
129
|
+
A new set containing all elements of the input collection,\
|
130
|
+
or the original set if it was already one.
|
131
|
+
None if no value was provided.
|
132
|
+
"""
|
133
|
+
|
134
|
+
if collection is None:
|
135
|
+
return None
|
136
|
+
|
137
|
+
if isinstance(collection, set):
|
138
|
+
return collection
|
139
|
+
|
140
|
+
else:
|
141
|
+
return set(collection)
|
142
|
+
|
143
|
+
|
144
|
+
@overload
|
145
|
+
def as_dict[K, V](
|
146
|
+
collection: Mapping[K, V],
|
147
|
+
/,
|
148
|
+
) -> dict[K, V]: ...
|
149
|
+
|
150
|
+
|
151
|
+
@overload
|
152
|
+
def as_dict[K, V](
|
153
|
+
collection: Mapping[K, V] | None,
|
154
|
+
/,
|
155
|
+
) -> dict[K, V] | None: ...
|
156
|
+
|
157
|
+
|
158
|
+
def as_dict[K, V](
|
159
|
+
collection: Mapping[K, V] | None,
|
160
|
+
/,
|
161
|
+
) -> dict[K, V] | None:
|
162
|
+
"""
|
163
|
+
Converts any given Mapping into a dict.
|
164
|
+
|
165
|
+
Parameters
|
166
|
+
----------
|
167
|
+
collection : Mapping[K, V]
|
168
|
+
The input collection to be converted.
|
169
|
+
|
170
|
+
Returns
|
171
|
+
-------
|
172
|
+
dict[K, V]
|
173
|
+
A new dict containing all elements of the input collection,\
|
174
|
+
or the original dict if it was already one.
|
175
|
+
None if no value was provided.
|
176
|
+
"""
|
177
|
+
|
178
|
+
if collection is None:
|
179
|
+
return None
|
180
|
+
|
181
|
+
if isinstance(collection, dict):
|
182
|
+
return collection
|
183
|
+
|
184
|
+
else:
|
185
|
+
return dict(collection)
|
haiway/utils/env.py
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
from os import environ, getenv
|
2
|
+
from typing import Literal, overload
|
3
|
+
|
4
|
+
__all__ = [
|
5
|
+
"getenv_bool",
|
6
|
+
"getenv_float",
|
7
|
+
"getenv_int",
|
8
|
+
"getenv_str",
|
9
|
+
"load_env",
|
10
|
+
]
|
11
|
+
|
12
|
+
|
13
|
+
@overload
|
14
|
+
def getenv_bool(
|
15
|
+
key: str,
|
16
|
+
/,
|
17
|
+
) -> bool | None: ...
|
18
|
+
|
19
|
+
|
20
|
+
@overload
|
21
|
+
def getenv_bool(
|
22
|
+
key: str,
|
23
|
+
/,
|
24
|
+
default: bool,
|
25
|
+
) -> bool: ...
|
26
|
+
|
27
|
+
|
28
|
+
@overload
|
29
|
+
def getenv_bool(
|
30
|
+
key: str,
|
31
|
+
/,
|
32
|
+
*,
|
33
|
+
required: Literal[True],
|
34
|
+
) -> bool: ...
|
35
|
+
|
36
|
+
|
37
|
+
def getenv_bool(
|
38
|
+
key: str,
|
39
|
+
/,
|
40
|
+
default: bool | None = None,
|
41
|
+
*,
|
42
|
+
required: bool = False,
|
43
|
+
) -> bool | None:
|
44
|
+
if value := getenv(key=key):
|
45
|
+
return value.lower() in ("true", "1", "t")
|
46
|
+
|
47
|
+
elif required and default is None:
|
48
|
+
raise ValueError(f"Required environment value `{key}` is missing!")
|
49
|
+
|
50
|
+
else:
|
51
|
+
return default
|
52
|
+
|
53
|
+
|
54
|
+
@overload
|
55
|
+
def getenv_int(
|
56
|
+
key: str,
|
57
|
+
/,
|
58
|
+
) -> int | None: ...
|
59
|
+
|
60
|
+
|
61
|
+
@overload
|
62
|
+
def getenv_int(
|
63
|
+
key: str,
|
64
|
+
/,
|
65
|
+
default: int,
|
66
|
+
) -> int: ...
|
67
|
+
|
68
|
+
|
69
|
+
@overload
|
70
|
+
def getenv_int(
|
71
|
+
key: str,
|
72
|
+
/,
|
73
|
+
*,
|
74
|
+
required: Literal[True],
|
75
|
+
) -> int: ...
|
76
|
+
|
77
|
+
|
78
|
+
def getenv_int(
|
79
|
+
key: str,
|
80
|
+
/,
|
81
|
+
default: int | None = None,
|
82
|
+
*,
|
83
|
+
required: bool = False,
|
84
|
+
) -> int | None:
|
85
|
+
if value := getenv(key=key):
|
86
|
+
try:
|
87
|
+
return int(value)
|
88
|
+
|
89
|
+
except Exception as exc:
|
90
|
+
raise ValueError(f"Environment value `{key}` is not a valid int!") from exc
|
91
|
+
|
92
|
+
elif required and default is None:
|
93
|
+
raise ValueError(f"Required environment value `{key}` is missing!")
|
94
|
+
|
95
|
+
else:
|
96
|
+
return default
|
97
|
+
|
98
|
+
|
99
|
+
@overload
|
100
|
+
def getenv_float(
|
101
|
+
key: str,
|
102
|
+
/,
|
103
|
+
) -> float | None: ...
|
104
|
+
|
105
|
+
|
106
|
+
@overload
|
107
|
+
def getenv_float(
|
108
|
+
key: str,
|
109
|
+
/,
|
110
|
+
default: float,
|
111
|
+
) -> float: ...
|
112
|
+
|
113
|
+
|
114
|
+
@overload
|
115
|
+
def getenv_float(
|
116
|
+
key: str,
|
117
|
+
/,
|
118
|
+
*,
|
119
|
+
required: Literal[True],
|
120
|
+
) -> float: ...
|
121
|
+
|
122
|
+
|
123
|
+
def getenv_float(
|
124
|
+
key: str,
|
125
|
+
/,
|
126
|
+
default: float | None = None,
|
127
|
+
*,
|
128
|
+
required: bool = False,
|
129
|
+
) -> float | None:
|
130
|
+
if value := getenv(key=key):
|
131
|
+
try:
|
132
|
+
return float(value)
|
133
|
+
|
134
|
+
except Exception as exc:
|
135
|
+
raise ValueError(f"Environment value `{key}` is not a valid float!") from exc
|
136
|
+
|
137
|
+
elif required and default is None:
|
138
|
+
raise ValueError(f"Required environment value `{key}` is missing!")
|
139
|
+
|
140
|
+
else:
|
141
|
+
return default
|
142
|
+
|
143
|
+
|
144
|
+
@overload
|
145
|
+
def getenv_str(
|
146
|
+
key: str,
|
147
|
+
/,
|
148
|
+
) -> str | None: ...
|
149
|
+
|
150
|
+
|
151
|
+
@overload
|
152
|
+
def getenv_str(
|
153
|
+
key: str,
|
154
|
+
/,
|
155
|
+
default: str,
|
156
|
+
) -> str: ...
|
157
|
+
|
158
|
+
|
159
|
+
@overload
|
160
|
+
def getenv_str(
|
161
|
+
key: str,
|
162
|
+
/,
|
163
|
+
*,
|
164
|
+
required: Literal[True],
|
165
|
+
) -> str: ...
|
166
|
+
|
167
|
+
|
168
|
+
def getenv_str(
|
169
|
+
key: str,
|
170
|
+
/,
|
171
|
+
default: str | None = None,
|
172
|
+
*,
|
173
|
+
required: bool = False,
|
174
|
+
) -> str | None:
|
175
|
+
if value := getenv(key=key):
|
176
|
+
return value
|
177
|
+
|
178
|
+
elif required and default is None:
|
179
|
+
raise ValueError(f"Required environment value `{key}` is missing!")
|
180
|
+
|
181
|
+
else:
|
182
|
+
return default
|
183
|
+
|
184
|
+
|
185
|
+
def load_env(
|
186
|
+
path: str | None = None,
|
187
|
+
override: bool = True,
|
188
|
+
) -> None:
|
189
|
+
"""\
|
190
|
+
Minimalist implementation of environment variables file loader. \
|
191
|
+
When the file is not available configuration won't be loaded.
|
192
|
+
Allows only subset of formatting:
|
193
|
+
- lines starting with '#' are ignored
|
194
|
+
- other comments are not allowed
|
195
|
+
- each element is in a new line
|
196
|
+
- each element must be a `key=value` pair without whitespaces or additional characters
|
197
|
+
- keys without values are ignored
|
198
|
+
|
199
|
+
Parameters
|
200
|
+
----------
|
201
|
+
path: str
|
202
|
+
custom path to load environment variables, default is '.env'
|
203
|
+
override: bool
|
204
|
+
override existing variables on conflict if True, otherwise keep existing
|
205
|
+
"""
|
206
|
+
|
207
|
+
try:
|
208
|
+
with open(file=path or ".env") as file:
|
209
|
+
for line in file.readlines():
|
210
|
+
if line.startswith("#"):
|
211
|
+
continue # ignore commented
|
212
|
+
|
213
|
+
idx: int # find where key ends
|
214
|
+
for element in enumerate(line):
|
215
|
+
if element[1] == "=":
|
216
|
+
idx: int = element[0]
|
217
|
+
break
|
218
|
+
else: # ignore keys without assignment
|
219
|
+
continue
|
220
|
+
|
221
|
+
if idx >= len(line):
|
222
|
+
continue # ignore keys without values
|
223
|
+
|
224
|
+
key: str = line[0:idx]
|
225
|
+
value: str = line[idx + 1 :].strip()
|
226
|
+
if value and (override or key not in environ):
|
227
|
+
environ[key] = value
|
228
|
+
|
229
|
+
except FileNotFoundError:
|
230
|
+
pass # ignore loading if no .env available
|
haiway/utils/freezing.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
__all__ = [
|
4
|
+
"freeze",
|
5
|
+
]
|
6
|
+
|
7
|
+
|
8
|
+
def freeze(
|
9
|
+
instance: object,
|
10
|
+
/,
|
11
|
+
) -> None:
|
12
|
+
"""
|
13
|
+
Freeze object instance by replacing __delattr__ and __setattr__ to raising Exceptions.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def frozen_set(
|
17
|
+
__name: str,
|
18
|
+
__value: Any,
|
19
|
+
) -> None:
|
20
|
+
raise RuntimeError(f"{instance.__class__.__qualname__} is frozen and can't be modified")
|
21
|
+
|
22
|
+
def frozen_del(
|
23
|
+
__name: str,
|
24
|
+
) -> None:
|
25
|
+
raise RuntimeError(f"{instance.__class__.__qualname__} is frozen and can't be modified")
|
26
|
+
|
27
|
+
instance.__delattr__ = frozen_del
|
28
|
+
instance.__setattr__ = frozen_set
|
haiway/utils/logs.py
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
from logging.config import dictConfig
|
2
|
+
|
3
|
+
from haiway.utils.env import getenv_bool
|
4
|
+
|
5
|
+
__all__ = [
|
6
|
+
"setup_logging",
|
7
|
+
]
|
8
|
+
|
9
|
+
|
10
|
+
def setup_logging(
|
11
|
+
*loggers: str,
|
12
|
+
debug: bool = getenv_bool("DEBUG_LOGGING", __debug__),
|
13
|
+
) -> None:
|
14
|
+
"""\
|
15
|
+
Setup logging configuration and prepare specified loggers.
|
16
|
+
|
17
|
+
Parameters
|
18
|
+
----------
|
19
|
+
*loggers: str
|
20
|
+
names of additional loggers to configure.
|
21
|
+
|
22
|
+
NOTE: this function should be run only once on application start
|
23
|
+
"""
|
24
|
+
|
25
|
+
dictConfig(
|
26
|
+
config={
|
27
|
+
"version": 1,
|
28
|
+
"disable_existing_loggers": True,
|
29
|
+
"formatters": {
|
30
|
+
"standard": {
|
31
|
+
"format": "%(asctime)s [%(levelname)-4s] [%(name)s] %(message)s",
|
32
|
+
"datefmt": "%d/%b/%Y:%H:%M:%S +0000",
|
33
|
+
},
|
34
|
+
},
|
35
|
+
"handlers": {
|
36
|
+
"console": {
|
37
|
+
"level": "DEBUG" if debug else "INFO",
|
38
|
+
"formatter": "standard",
|
39
|
+
"class": "logging.StreamHandler",
|
40
|
+
"stream": "ext://sys.stdout",
|
41
|
+
},
|
42
|
+
},
|
43
|
+
"loggers": {
|
44
|
+
name: {
|
45
|
+
"handlers": ["console"],
|
46
|
+
"level": "DEBUG" if debug else "INFO",
|
47
|
+
"propagate": False,
|
48
|
+
}
|
49
|
+
for name in loggers
|
50
|
+
},
|
51
|
+
"root": { # root logger
|
52
|
+
"handlers": ["console"],
|
53
|
+
"level": "DEBUG" if debug else "INFO",
|
54
|
+
"propagate": False,
|
55
|
+
},
|
56
|
+
},
|
57
|
+
)
|
haiway/utils/mimic.py
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import Any, cast, overload
|
3
|
+
|
4
|
+
__all__ = [
|
5
|
+
"mimic_function",
|
6
|
+
]
|
7
|
+
|
8
|
+
|
9
|
+
@overload
|
10
|
+
def mimic_function[**Args, Result](
|
11
|
+
function: Callable[Args, Result],
|
12
|
+
/,
|
13
|
+
within: Callable[..., Any],
|
14
|
+
) -> Callable[Args, Result]: ...
|
15
|
+
|
16
|
+
|
17
|
+
@overload
|
18
|
+
def mimic_function[**Args, Result](
|
19
|
+
function: Callable[Args, Result],
|
20
|
+
/,
|
21
|
+
) -> Callable[[Callable[..., Any]], Callable[Args, Result]]: ...
|
22
|
+
|
23
|
+
|
24
|
+
def mimic_function[**Args, Result](
|
25
|
+
function: Callable[Args, Result],
|
26
|
+
/,
|
27
|
+
within: Callable[..., Result] | None = None,
|
28
|
+
) -> Callable[[Callable[..., Result]], Callable[Args, Result]] | Callable[Args, Result]:
|
29
|
+
def mimic(
|
30
|
+
target: Callable[..., Result],
|
31
|
+
) -> Callable[Args, Result]:
|
32
|
+
# mimic function attributes if able
|
33
|
+
for attribute in (
|
34
|
+
"__module__",
|
35
|
+
"__name__",
|
36
|
+
"__qualname__",
|
37
|
+
"__doc__",
|
38
|
+
"__annotations__",
|
39
|
+
"__type_params__",
|
40
|
+
"__defaults__",
|
41
|
+
"__kwdefaults__",
|
42
|
+
"__globals__",
|
43
|
+
):
|
44
|
+
try:
|
45
|
+
setattr(
|
46
|
+
target,
|
47
|
+
attribute,
|
48
|
+
getattr(
|
49
|
+
function,
|
50
|
+
attribute,
|
51
|
+
),
|
52
|
+
)
|
53
|
+
|
54
|
+
except AttributeError:
|
55
|
+
pass
|
56
|
+
try:
|
57
|
+
target.__dict__.update(function.__dict__)
|
58
|
+
|
59
|
+
except AttributeError:
|
60
|
+
pass
|
61
|
+
|
62
|
+
setattr( # noqa: B010 - mimic functools.wraps behavior for correct signature checks
|
63
|
+
target,
|
64
|
+
"__wrapped__",
|
65
|
+
function,
|
66
|
+
)
|
67
|
+
|
68
|
+
return cast(
|
69
|
+
Callable[Args, Result],
|
70
|
+
target,
|
71
|
+
)
|
72
|
+
|
73
|
+
if target := within:
|
74
|
+
return mimic(target)
|
75
|
+
|
76
|
+
else:
|
77
|
+
return mimic
|
haiway/utils/noop.py
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
__all__ = [
|
4
|
+
"async_noop",
|
5
|
+
"noop",
|
6
|
+
]
|
7
|
+
|
8
|
+
|
9
|
+
def noop(
|
10
|
+
*args: Any,
|
11
|
+
**kwargs: Any,
|
12
|
+
) -> None:
|
13
|
+
"""
|
14
|
+
Placeholder function doing nothing (no operation).
|
15
|
+
"""
|
16
|
+
|
17
|
+
|
18
|
+
async def async_noop(
|
19
|
+
*args: Any,
|
20
|
+
**kwargs: Any,
|
21
|
+
) -> None:
|
22
|
+
"""
|
23
|
+
Placeholder async function doing nothing (no operation).
|
24
|
+
"""
|