haiway 0.8.0__tar.gz → 0.8.1__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.
Files changed (54) hide show
  1. {haiway-0.8.0/src/haiway.egg-info → haiway-0.8.1}/PKG-INFO +1 -1
  2. {haiway-0.8.0 → haiway-0.8.1}/pyproject.toml +1 -1
  3. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/access.py +5 -0
  4. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/metrics.py +59 -45
  5. {haiway-0.8.0 → haiway-0.8.1/src/haiway.egg-info}/PKG-INFO +1 -1
  6. {haiway-0.8.0 → haiway-0.8.1}/tests/test_context.py +0 -1
  7. {haiway-0.8.0 → haiway-0.8.1}/LICENSE +0 -0
  8. {haiway-0.8.0 → haiway-0.8.1}/README.md +0 -0
  9. {haiway-0.8.0 → haiway-0.8.1}/setup.cfg +0 -0
  10. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/__init__.py +0 -0
  11. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/__init__.py +0 -0
  12. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/disposables.py +0 -0
  13. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/identifier.py +0 -0
  14. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/logging.py +0 -0
  15. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/metrics.py +0 -0
  16. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/state.py +0 -0
  17. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/tasks.py +0 -0
  18. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/types.py +0 -0
  19. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/__init__.py +0 -0
  20. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/asynchrony.py +0 -0
  21. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/caching.py +0 -0
  22. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/retries.py +0 -0
  23. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/throttling.py +0 -0
  24. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/timeouted.py +0 -0
  25. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/tracing.py +0 -0
  26. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/py.typed +0 -0
  27. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/__init__.py +0 -0
  28. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/attributes.py +0 -0
  29. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/path.py +0 -0
  30. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/requirement.py +0 -0
  31. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/structure.py +0 -0
  32. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/validation.py +0 -0
  33. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/types/__init__.py +0 -0
  34. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/types/frozen.py +0 -0
  35. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/types/missing.py +0 -0
  36. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/__init__.py +0 -0
  37. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/always.py +0 -0
  38. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/env.py +0 -0
  39. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/immutable.py +0 -0
  40. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/logs.py +0 -0
  41. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/mimic.py +0 -0
  42. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/noop.py +0 -0
  43. {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/queue.py +0 -0
  44. {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/SOURCES.txt +0 -0
  45. {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/dependency_links.txt +0 -0
  46. {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/requires.txt +0 -0
  47. {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/top_level.txt +0 -0
  48. {haiway-0.8.0 → haiway-0.8.1}/tests/test_async_queue.py +0 -0
  49. {haiway-0.8.0 → haiway-0.8.1}/tests/test_attribute_path.py +0 -0
  50. {haiway-0.8.0 → haiway-0.8.1}/tests/test_auto_retry.py +0 -0
  51. {haiway-0.8.0 → haiway-0.8.1}/tests/test_cache.py +0 -0
  52. {haiway-0.8.0 → haiway-0.8.1}/tests/test_state.py +0 -0
  53. {haiway-0.8.0 → haiway-0.8.1}/tests/test_streaming.py +0 -0
  54. {haiway-0.8.0 → haiway-0.8.1}/tests/test_timeout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
6
6
  License: MIT License
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "haiway"
7
7
  description = "Framework for dependency injection and state management within structured concurrency model."
8
- version = "0.8.0"
8
+ version = "0.8.1"
9
9
  readme = "README.md"
10
10
  maintainers = [
11
11
  { name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
@@ -46,6 +46,11 @@ class ScopeContext:
46
46
  self._state: tuple[State, ...] = state
47
47
  self._disposables: Disposables | None = disposables
48
48
  # pre-building metrics context to ensure nested context registering
49
+ if __debug__:
50
+ if self._identifier.is_root and metrics is None:
51
+ from haiway.helpers import MetricsLogger
52
+
53
+ metrics = MetricsLogger.handler()
49
54
  self._metrics_context: MetricsContext = MetricsContext.scope(
50
55
  self._identifier,
51
56
  metrics=metrics,
@@ -1,16 +1,19 @@
1
1
  from collections.abc import Sequence
2
2
  from itertools import chain
3
3
  from time import monotonic
4
- from typing import Any, Self, cast, final
4
+ from typing import Any, Final, Self, cast, final
5
5
 
6
6
  from haiway.context import MetricsHandler, ScopeIdentifier, ctx
7
7
  from haiway.state import State
8
8
  from haiway.types import MISSING, Missing
9
+ from haiway.utils import getenv_bool
9
10
 
10
11
  __all_ = [
11
12
  "MetricsLogger",
12
13
  ]
13
14
 
15
+ DEBUG_LOGGING: Final[bool] = getenv_bool("DEBUG_LOGGING", __debug__)
16
+
14
17
 
15
18
  class MetricsScopeStore:
16
19
  def __init__(
@@ -59,11 +62,11 @@ class MetricsLogger:
59
62
  def handler(
60
63
  cls,
61
64
  items_limit: int | None = None,
62
- item_character_limit: int | None = None,
65
+ redact_content: bool = False,
63
66
  ) -> MetricsHandler:
64
67
  logger_handler: Self = cls(
65
68
  items_limit=items_limit,
66
- item_character_limit=item_character_limit,
69
+ redact_content=redact_content,
67
70
  )
68
71
  return MetricsHandler(
69
72
  record=logger_handler.record,
@@ -73,11 +76,11 @@ class MetricsLogger:
73
76
 
74
77
  def __init__(
75
78
  self,
76
- items_limit: int | None = None,
77
- item_character_limit: int | None = None,
79
+ items_limit: int | None,
80
+ redact_content: bool,
78
81
  ) -> None:
79
82
  self.items_limit: int | None = items_limit
80
- self.item_character_limit: int | None = item_character_limit
83
+ self.redact_content: bool = redact_content
81
84
  self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
82
85
 
83
86
  def record(
@@ -93,13 +96,13 @@ class MetricsLogger:
93
96
  metrics[type(metric)] = current.__add__(metric) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
94
97
 
95
98
  metrics[type(metric)] = metric
96
- if __debug__:
99
+ if DEBUG_LOGGING:
97
100
  if log := _state_log(
98
101
  metric,
99
102
  list_items_limit=self.items_limit,
100
- item_character_limit=self.item_character_limit,
103
+ redact_content=self.redact_content,
101
104
  ):
102
- ctx.log_info(f"Recorded:\n {type(metric).__qualname__}:{log}")
105
+ ctx.log_info(f"Recorded metric:\n {type(metric).__qualname__}:{log}\n⌊")
103
106
 
104
107
  def enter_scope[Metric: State](
105
108
  self,
@@ -107,7 +110,17 @@ class MetricsLogger:
107
110
  /,
108
111
  ) -> None:
109
112
  assert scope not in self.scopes # nosec: B101
110
- self.scopes[scope] = MetricsScopeStore(scope)
113
+ scope_metrics = MetricsScopeStore(scope)
114
+ self.scopes[scope] = scope_metrics
115
+ if not scope.is_root: # root scopes have no actual parent
116
+ for key in self.scopes.keys():
117
+ if key.scope_id == scope.parent_id:
118
+ self.scopes[key].nested.append(scope_metrics)
119
+ return
120
+
121
+ ctx.log_error(
122
+ "Attempting to enter nested scope metrics without entering its parent first"
123
+ )
111
124
 
112
125
  def exit_scope[Metric: State](
113
126
  self,
@@ -117,22 +130,24 @@ class MetricsLogger:
117
130
  assert scope in self.scopes # nosec: B101
118
131
  self.scopes[scope].exited = monotonic()
119
132
 
120
- if __debug__:
133
+ if DEBUG_LOGGING:
121
134
  if scope.is_root and self.scopes[scope].finished:
122
135
  if log := _tree_log(
123
136
  self.scopes[scope],
124
137
  list_items_limit=self.items_limit,
125
- item_character_limit=self.item_character_limit,
138
+ redact_content=self.redact_content,
126
139
  ):
127
- ctx.log_info(log)
140
+ ctx.log_info(f"Metrics summary:\n{log}")
128
141
 
129
142
 
130
143
  def _tree_log(
131
144
  metrics: MetricsScopeStore,
132
145
  list_items_limit: int | None,
133
- item_character_limit: int | None,
146
+ redact_content: bool,
134
147
  ) -> str:
135
- log: str = f"@{metrics.identifier}({metrics.time:.2f}s):"
148
+ log: str = (
149
+ f"⎡ @{metrics.identifier.label} [{metrics.identifier.scope_id}]({metrics.time:.2f}s):"
150
+ )
136
151
 
137
152
  for metric in metrics.merged():
138
153
  metric_log: str = ""
@@ -140,52 +155,52 @@ def _tree_log(
140
155
  if value_log := _value_log(
141
156
  value,
142
157
  list_items_limit=list_items_limit,
143
- item_character_limit=item_character_limit,
158
+ redact_content=redact_content,
144
159
  ):
145
- metric_log += f"\n| + {key}: {value_log}"
160
+ metric_log += f"\n {key}: {value_log}"
146
161
 
147
162
  else:
148
- continue # skip missing values
163
+ continue # skip empty values
149
164
 
150
165
  if not metric_log:
151
166
  continue # skip empty logs
152
167
 
153
- log += f"\n• {type(metric).__qualname__}:{metric_log}"
168
+ log += f"\n•{type(metric).__qualname__}:{metric_log.replace("\n", "\n| ")}\n⌊"
154
169
 
155
170
  for nested in metrics.nested:
156
171
  nested_log: str = _tree_log(
157
172
  nested,
158
173
  list_items_limit=list_items_limit,
159
- item_character_limit=item_character_limit,
160
- ).replace("\n", "\n| ")
174
+ redact_content=redact_content,
175
+ )
161
176
 
162
- log += f"\n{nested_log}"
177
+ log += f"\n\n{nested_log}"
163
178
 
164
- return log.strip()
179
+ return log.strip().replace("\n", "\n| ") + "\n⌊"
165
180
 
166
181
 
167
182
  def _state_log(
168
183
  value: State,
169
184
  /,
170
185
  list_items_limit: int | None,
171
- item_character_limit: int | None,
186
+ redact_content: bool,
172
187
  ) -> str | None:
173
188
  state_log: str = ""
174
189
  for key, element in vars(value).items():
175
190
  element_log: str | None = _value_log(
176
191
  element,
177
192
  list_items_limit=list_items_limit,
178
- item_character_limit=item_character_limit,
193
+ redact_content=redact_content,
179
194
  )
180
195
 
181
196
  if element_log:
182
- state_log += f"\n| + {key}: {element_log}"
197
+ state_log += f"\n {key}: {element_log}"
183
198
 
184
199
  else:
185
200
  continue # skip empty logs
186
201
 
187
202
  if state_log:
188
- return state_log.replace("\n", "\n| ")
203
+ return state_log
189
204
 
190
205
  else:
191
206
  return None # skip empty logs
@@ -195,17 +210,17 @@ def _dict_log(
195
210
  value: dict[Any, Any],
196
211
  /,
197
212
  list_items_limit: int | None,
198
- item_character_limit: int | None,
213
+ redact_content: bool,
199
214
  ) -> str | None:
200
215
  dict_log: str = ""
201
216
  for key, element in value.items():
202
217
  element_log: str | None = _value_log(
203
218
  element,
204
219
  list_items_limit=list_items_limit,
205
- item_character_limit=item_character_limit,
220
+ redact_content=redact_content,
206
221
  )
207
222
  if element_log:
208
- dict_log += f"\n| + {key}: {element_log}"
223
+ dict_log += f"\n[{key}]: {element_log}"
209
224
 
210
225
  else:
211
226
  continue # skip empty logs
@@ -221,7 +236,7 @@ def _list_log(
221
236
  value: list[Any],
222
237
  /,
223
238
  list_items_limit: int | None,
224
- item_character_limit: int | None,
239
+ redact_content: bool,
225
240
  ) -> str | None:
226
241
  list_log: str = ""
227
242
  enumerated: list[tuple[int, Any]] = list(enumerate(value))
@@ -236,10 +251,10 @@ def _list_log(
236
251
  element_log: str | None = _value_log(
237
252
  element,
238
253
  list_items_limit=list_items_limit,
239
- item_character_limit=item_character_limit,
254
+ redact_content=redact_content,
240
255
  )
241
256
  if element_log:
242
- list_log += f"\n| [{idx}] {element_log}"
257
+ list_log += f"\n[{idx}] {element_log}"
243
258
 
244
259
  else:
245
260
  continue # skip empty logs
@@ -254,34 +269,33 @@ def _list_log(
254
269
  def _raw_value_log(
255
270
  value: Any,
256
271
  /,
257
- item_character_limit: int | None,
272
+ redact_content: bool,
258
273
  ) -> str | None:
259
274
  if value is MISSING:
260
275
  return None # skip missing
261
276
 
262
- value_log = str(value)
263
- if not value_log:
264
- return None # skip empty logs
277
+ if redact_content:
278
+ return "[redacted]"
265
279
 
266
- if (item_character_limit := item_character_limit) and len(value_log) > item_character_limit:
267
- return value_log.replace("\n", " ")[:item_character_limit] + "..."
280
+ elif isinstance(value, str):
281
+ return f'"{value}"'.replace("\n", "\n| ")
268
282
 
269
283
  else:
270
- return value_log.replace("\n", "\n| ")
284
+ return str(value).strip().replace("\n", "\n| ")
271
285
 
272
286
 
273
287
  def _value_log(
274
288
  value: Any,
275
289
  /,
276
290
  list_items_limit: int | None,
277
- item_character_limit: int | None,
291
+ redact_content: bool,
278
292
  ) -> str | None:
279
293
  # try unpack dicts
280
294
  if isinstance(value, dict):
281
295
  return _dict_log(
282
296
  cast(dict[Any, Any], value),
283
297
  list_items_limit=list_items_limit,
284
- item_character_limit=item_character_limit,
298
+ redact_content=redact_content,
285
299
  )
286
300
 
287
301
  # try unpack lists
@@ -289,7 +303,7 @@ def _value_log(
289
303
  return _list_log(
290
304
  cast(list[Any], value),
291
305
  list_items_limit=list_items_limit,
292
- item_character_limit=item_character_limit,
306
+ redact_content=redact_content,
293
307
  )
294
308
 
295
309
  # try unpack state
@@ -297,11 +311,11 @@ def _value_log(
297
311
  return _state_log(
298
312
  value,
299
313
  list_items_limit=list_items_limit,
300
- item_character_limit=item_character_limit,
314
+ redact_content=redact_content,
301
315
  )
302
316
 
303
317
  else:
304
318
  return _raw_value_log(
305
319
  value,
306
- item_character_limit=item_character_limit,
320
+ redact_content=redact_content,
307
321
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: haiway
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Framework for dependency injection and state management within structured concurrency model.
5
5
  Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
6
6
  License: MIT License
@@ -1,4 +1,3 @@
1
-
2
1
  from pytest import mark, raises
3
2
 
4
3
  from haiway import MissingContext, State, ctx
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes