mermaid-trace 0.3.1__py3-none-any.whl → 0.4.1__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.
@@ -1,50 +1,68 @@
1
+ """
2
+ Log Context Management Module
3
+
4
+ This module provides a thread-safe, async-friendly context management system
5
+ for tracking execution context across the application. It uses Python's ContextVar
6
+ mechanism to ensure proper context propagation in both synchronous and asynchronous
7
+ environments.
8
+ """
9
+
1
10
  from contextvars import ContextVar, Token
2
11
  from contextlib import asynccontextmanager, contextmanager
3
12
  from typing import Any, AsyncIterator, Dict, Iterator
4
13
  import uuid
5
14
 
15
+
6
16
  class LogContext:
7
17
  """
8
18
  Manages global context information for logging (e.g., request_id, user_id, current_participant).
9
-
10
- This class utilizes `contextvars.ContextVar` to ensure thread-safety and
11
- correct context propagation in asynchronous (asyncio) environments.
12
- Unlike `threading.local()`, `ContextVar` works natively with Python's async/await
13
- event loop, ensuring that context is preserved across `await` points but isolated
19
+
20
+ This class utilizes `contextvars.ContextVar` to ensure thread-safety and
21
+ correct context propagation in asynchronous (asyncio) environments. Unlike
22
+ `threading.local()`, `ContextVar` works natively with Python's async/await
23
+ event loop, ensuring that context is preserved across `await` points but isolated
14
24
  between different concurrent tasks.
15
25
  """
16
-
17
- # ContextVar is the key mechanism here.
18
- # It stores a dictionary unique to the current execution context (Task/Thread).
19
- # "log_context" is the name of the variable, useful for debugging.
20
- # The default value is implicitly an empty state if not set (handled in _get_store).
26
+
27
+ # ContextVar stores a dictionary unique to the current execution context (Task/Thread)
28
+ # The name "log_context" is used for debugging purposes
21
29
  _context_store: ContextVar[Dict[str, Any]] = ContextVar("log_context")
22
30
 
23
31
  @classmethod
24
32
  def _get_store(cls) -> Dict[str, Any]:
25
33
  """
26
34
  Retrieves the current context dictionary.
27
-
28
- If the context variable has not been set in the current context,
29
- it returns a fresh empty dictionary. This prevents LookupError
30
- and ensures there's always a valid dictionary to work with.
35
+
36
+ If the context variable has not been set in the current context,
37
+ it creates a fresh empty dictionary, sets it to the contextvar,
38
+ and returns it. This prevents LookupError and ensures there's
39
+ always a valid dictionary to work with.
40
+
41
+ Returns:
42
+ Dict[str, Any]: Current context dictionary for the execution flow
31
43
  """
32
44
  try:
33
45
  return cls._context_store.get()
34
46
  except LookupError:
35
- return {}
47
+ empty_dict: Dict[str, Any] = {}
48
+ cls._context_store.set(empty_dict)
49
+ return empty_dict
36
50
 
37
51
  @classmethod
38
52
  def set(cls, key: str, value: Any) -> None:
39
53
  """
40
54
  Sets a specific key-value pair in the current context.
41
-
42
- Important: ContextVars are immutable collections. To modify the context,
55
+
56
+ Important: ContextVars are immutable collections. To modify the context,
43
57
  we must:
44
- 1. Retrieve the current dictionary.
45
- 2. Create a shallow copy (to avoid affecting parent contexts if we were reusing the object).
46
- 3. Update the copy.
47
- 4. Re-set the ContextVar with the new dictionary.
58
+ 1. Retrieve the current dictionary using _get_store()
59
+ 2. Create a shallow copy to avoid affecting parent contexts
60
+ 3. Update the copy with the new key-value pair
61
+ 4. Re-set the ContextVar with the new dictionary
62
+
63
+ Args:
64
+ key (str): Name of the context variable to set
65
+ value (Any): Value to associate with the key
48
66
  """
49
67
  ctx = cls._get_store().copy()
50
68
  ctx[key] = value
@@ -54,9 +72,12 @@ class LogContext:
54
72
  def update(cls, data: Dict[str, Any]) -> None:
55
73
  """
56
74
  Updates multiple keys in the current context at once.
57
-
58
- This follows the same Copy-Update-Set pattern as `set()` to maintain
59
- context isolation.
75
+
76
+ This follows the same Copy-Update-Set pattern as `set()` to maintain
77
+ context isolation between different execution flows.
78
+
79
+ Args:
80
+ data (Dict[str, Any]): Dictionary of key-value pairs to update in context
60
81
  """
61
82
  if not data:
62
83
  return
@@ -68,6 +89,13 @@ class LogContext:
68
89
  def get(cls, key: str, default: Any = None) -> Any:
69
90
  """
70
91
  Retrieves a value from the current context safely.
92
+
93
+ Args:
94
+ key (str): Name of the context variable to retrieve
95
+ default (Any, optional): Default value if key doesn't exist. Defaults to None.
96
+
97
+ Returns:
98
+ Any: Value associated with the key, or default if key not found
71
99
  """
72
100
  return cls._get_store().get(key, default)
73
101
 
@@ -75,6 +103,9 @@ class LogContext:
75
103
  def get_all(cls) -> Dict[str, Any]:
76
104
  """
77
105
  Returns a copy of the entire context dictionary.
106
+
107
+ Returns:
108
+ Dict[str, Any]: Complete copy of the current context
78
109
  """
79
110
  return cls._get_store().copy()
80
111
 
@@ -83,19 +114,25 @@ class LogContext:
83
114
  def scope(cls, data: Dict[str, Any]) -> Iterator[None]:
84
115
  """
85
116
  Synchronous context manager for temporary context updates.
86
-
117
+
87
118
  Usage:
88
119
  with LogContext.scope({"user_id": 123}):
89
120
  # user_id is 123 here
90
121
  some_function()
91
122
  # user_id reverts to previous value (or disappears) here
92
-
123
+
93
124
  Mechanism:
94
- 1. Copies current context and updates it with new data.
95
- 2. Sets the ContextVar to this new state, receiving a `Token`.
96
- 3. Yields control to the block.
97
- 4. Finally, uses the `Token` to reset the ContextVar to its exact state
98
- before the block entered.
125
+ 1. Copies current context and updates it with new data
126
+ 2. Sets the ContextVar to this new state, receiving a `Token`
127
+ 3. Yields control to the block
128
+ 4. Finally, uses the `Token` to reset the ContextVar to its exact state
129
+ before the block entered
130
+
131
+ Args:
132
+ data (Dict[str, Any]): Dictionary of context values to set within the scope
133
+
134
+ Yields:
135
+ None: Control to the block using this context manager
99
136
  """
100
137
  current_ctx = cls._get_store().copy()
101
138
  current_ctx.update(data)
@@ -103,7 +140,7 @@ class LogContext:
103
140
  try:
104
141
  yield
105
142
  finally:
106
- # Crucial: Reset restores the context to what it was before .set()
143
+ # Reset restores context to state before .set() was called
107
144
  cls._context_store.reset(token)
108
145
 
109
146
  @classmethod
@@ -111,14 +148,20 @@ class LogContext:
111
148
  async def ascope(cls, data: Dict[str, Any]) -> AsyncIterator[None]:
112
149
  """
113
150
  Async context manager for temporary context updates in coroutines.
114
-
151
+
115
152
  Usage:
116
153
  async with LogContext.ascope({"request_id": "abc"}):
117
154
  await some_async_function()
118
-
155
+
119
156
  This is functionally identical to `scope` but designed for `async with` blocks.
120
157
  It ensures that even if the code inside `yield` suspends execution (await),
121
158
  the context remains valid for that task.
159
+
160
+ Args:
161
+ data (Dict[str, Any]): Dictionary of context values to set within the scope
162
+
163
+ Yields:
164
+ None: Control to the async block using this context manager
122
165
  """
123
166
  current_ctx = cls._get_store().copy()
124
167
  current_ctx.update(data)
@@ -136,6 +179,12 @@ class LogContext:
136
179
  """
137
180
  Replaces the entire context with the provided data.
138
181
  Returns a Token that can be used to manually reset the context later.
182
+
183
+ Args:
184
+ data (Dict[str, Any]): New context dictionary to replace the current one
185
+
186
+ Returns:
187
+ Token[Dict[str, Any]]: Token for resetting context to previous state
139
188
  """
140
189
  return cls._context_store.set(data.copy())
141
190
 
@@ -143,21 +192,30 @@ class LogContext:
143
192
  def reset(cls, token: Token[Dict[str, Any]]) -> None:
144
193
  """
145
194
  Manually resets the context using a Token obtained from `set` or `set_all`.
195
+
196
+ Args:
197
+ token (Token[Dict[str, Any]]): Token returned by set_all() method
146
198
  """
147
199
  cls._context_store.reset(token)
148
200
 
149
201
  @classmethod
150
202
  def current_participant(cls) -> str:
151
203
  """
152
- Helper to get the 'participant' field, representing the current active object/module.
204
+ Helper method to get the 'participant' field, representing the current active object/module.
153
205
  Defaults to 'Unknown' if not set.
206
+
207
+ Returns:
208
+ str: Name of the current participant in the trace flow
154
209
  """
155
210
  return str(cls.get("participant", "Unknown"))
156
211
 
157
212
  @classmethod
158
213
  def set_participant(cls, name: str) -> None:
159
214
  """
160
- Helper to set the 'participant' field.
215
+ Helper method to set the 'participant' field.
216
+
217
+ Args:
218
+ name (str): Name of the participant to set
161
219
  """
162
220
  cls.set("participant", name)
163
221
 
@@ -165,13 +223,16 @@ class LogContext:
165
223
  def current_trace_id(cls) -> str:
166
224
  """
167
225
  Retrieves the current trace ID for correlating events in a single flow.
168
-
226
+
169
227
  Lazy Initialization Logic:
170
- If no trace_id exists in the current context, it generates a new UUIDv4
228
+ If no trace_id exists in the current context, it generates a new UUIDv4
171
229
  and sets it immediately. This ensures that:
172
- 1. A trace ID is always available when asked for.
173
- 2. Once generated, the same ID persists for the duration of the context
174
- (unless manually changed), linking all subsequent logs together.
230
+ 1. A trace ID is always available when asked for
231
+ 2. Once generated, the same ID persists for the duration of the context
232
+ (unless manually changed), linking all subsequent logs together
233
+
234
+ Returns:
235
+ str: Unique trace ID for the current execution flow
175
236
  """
176
237
  tid = cls.get("trace_id")
177
238
  if not tid: