trodo-python 2.4.1__tar.gz → 2.6.0__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.
- {trodo_python-2.4.1 → trodo_python-2.6.0}/PKG-INFO +1 -1
- {trodo_python-2.4.1 → trodo_python-2.6.0}/pyproject.toml +1 -1
- trodo_python-2.6.0/tests/test_anon_distinct_id.py +100 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/processor.py +5 -5
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/wrap_agent.py +81 -21
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo_python.egg-info/PKG-INFO +1 -1
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo_python.egg-info/SOURCES.txt +1 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/README.md +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/setup.cfg +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_auto_instrument_fixes.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_cross_process_session.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_end_run.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_processor_methods.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_register_otel.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_start_run.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/tests/test_wrap_agent_unchanged.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/api/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/api/async_client.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/api/endpoints.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/api/http_client.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/auto/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/auto/auto_event_manager.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/client.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/managers/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/managers/group_manager.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/managers/people_manager.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/auto_instrument.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/context.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/helpers.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/register.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/otel/transport.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/queue/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/queue/batch_flusher.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/queue/event_queue.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/session/__init__.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/session/server_session.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/session/session_manager.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/types.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo/user_context.py +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo_python.egg-info/dependency_links.txt +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo_python.egg-info/requires.txt +0 -0
- {trodo_python-2.4.1 → trodo_python-2.6.0}/trodo_python.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Anonymous distinct_id minting on agent surfaces.
|
|
2
|
+
|
|
3
|
+
When the caller doesn't pass ``distinct_id``, the SDK mints an
|
|
4
|
+
``anon_<ts>_python_<uuid>_<rand>`` id so:
|
|
5
|
+
|
|
6
|
+
* the agent_runs row always lands with a non-null distinct_id,
|
|
7
|
+
* the RunHandle exposes the same id so callers can bind
|
|
8
|
+
``trodo.feedback(distinct_id=...)`` later,
|
|
9
|
+
* older backends without identity-resolution still attribute the row.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
from trodo.otel.wrap_agent import _mint_anon_distinct_id, start_run, wrap_agent
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ANON_RE = re.compile(r"^anon_\d+_python_")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_mint_anon_distinct_id_shape():
|
|
22
|
+
a = _mint_anon_distinct_id()
|
|
23
|
+
assert ANON_RE.match(a)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_mint_anon_distinct_id_unique_under_load():
|
|
27
|
+
ids = {_mint_anon_distinct_id() for _ in range(1000)}
|
|
28
|
+
assert len(ids) == 1000
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_wrap_agent_mints_anon_when_distinct_id_omitted(processor, http):
|
|
32
|
+
observed = {}
|
|
33
|
+
with wrap_agent(
|
|
34
|
+
processor=processor,
|
|
35
|
+
team_site_id="site-x",
|
|
36
|
+
agent_name="chat",
|
|
37
|
+
) as run:
|
|
38
|
+
observed["distinct_id"] = run.distinct_id
|
|
39
|
+
run.set_output("done")
|
|
40
|
+
|
|
41
|
+
assert ANON_RE.match(observed["distinct_id"])
|
|
42
|
+
assert len(http.run_ingest) == 1
|
|
43
|
+
assert http.run_ingest[0]["run"]["distinct_id"] == observed["distinct_id"]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_wrap_agent_respects_explicit_distinct_id(processor, http):
|
|
47
|
+
with wrap_agent(
|
|
48
|
+
processor=processor,
|
|
49
|
+
team_site_id="site-x",
|
|
50
|
+
agent_name="chat",
|
|
51
|
+
distinct_id="user-42",
|
|
52
|
+
) as run:
|
|
53
|
+
assert run.distinct_id == "user-42"
|
|
54
|
+
|
|
55
|
+
assert http.run_ingest[0]["run"]["distinct_id"] == "user-42"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_wrap_agent_mints_different_anon_ids_across_calls(processor, http):
|
|
59
|
+
seen = []
|
|
60
|
+
for _ in range(3):
|
|
61
|
+
with wrap_agent(
|
|
62
|
+
processor=processor,
|
|
63
|
+
team_site_id="site-x",
|
|
64
|
+
agent_name="chat",
|
|
65
|
+
) as run:
|
|
66
|
+
seen.append(run.distinct_id)
|
|
67
|
+
assert len(set(seen)) == 3
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_wrap_agent_still_mints_anon_on_error(processor, http):
|
|
71
|
+
import pytest
|
|
72
|
+
with pytest.raises(ValueError):
|
|
73
|
+
with wrap_agent(
|
|
74
|
+
processor=processor,
|
|
75
|
+
team_site_id="site-x",
|
|
76
|
+
agent_name="chat",
|
|
77
|
+
) as _run:
|
|
78
|
+
raise ValueError("boom")
|
|
79
|
+
|
|
80
|
+
assert len(http.run_ingest) == 1
|
|
81
|
+
payload = http.run_ingest[0]["run"]
|
|
82
|
+
assert payload["status"] == "error"
|
|
83
|
+
assert ANON_RE.match(payload["distinct_id"])
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_start_run_mints_anon_when_distinct_id_omitted(processor, http):
|
|
87
|
+
start_run(processor=processor, agent_name="external_session")
|
|
88
|
+
assert len(http.run_start) == 1
|
|
89
|
+
run = http.run_start[0]["run"]
|
|
90
|
+
assert ANON_RE.match(run["distinct_id"])
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_start_run_respects_explicit_distinct_id(processor, http):
|
|
94
|
+
start_run(
|
|
95
|
+
processor=processor,
|
|
96
|
+
agent_name="external_session",
|
|
97
|
+
distinct_id="user-7",
|
|
98
|
+
)
|
|
99
|
+
run = http.run_start[0]["run"]
|
|
100
|
+
assert run["distinct_id"] == "user-7"
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import threading
|
|
11
11
|
from dataclasses import dataclass, asdict
|
|
12
|
-
from typing import Any, Dict, List, Optional
|
|
12
|
+
from typing import Any, Dict, List, Optional, Union
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
@@ -20,8 +20,8 @@ class TrodoRun:
|
|
|
20
20
|
conversation_id: Optional[str] = None
|
|
21
21
|
parent_run_id: Optional[str] = None
|
|
22
22
|
status: str = "ok" # 'running' | 'ok' | 'error'
|
|
23
|
-
input: Optional[str] = None
|
|
24
|
-
output: Optional[str] = None
|
|
23
|
+
input: Optional[Union[str, Dict[str, Any]]] = None
|
|
24
|
+
output: Optional[Union[str, Dict[str, Any]]] = None
|
|
25
25
|
started_at: Optional[str] = None
|
|
26
26
|
ended_at: Optional[str] = None
|
|
27
27
|
duration_ms: Optional[int] = None
|
|
@@ -50,8 +50,8 @@ class TrodoSpan:
|
|
|
50
50
|
started_at: Optional[str] = None
|
|
51
51
|
ended_at: Optional[str] = None
|
|
52
52
|
duration_ms: Optional[int] = None
|
|
53
|
-
input: Optional[str] = None
|
|
54
|
-
output: Optional[str] = None
|
|
53
|
+
input: Optional[Union[str, Dict[str, Any]]] = None
|
|
54
|
+
output: Optional[Union[str, Dict[str, Any]]] = None
|
|
55
55
|
error_type: Optional[str] = None
|
|
56
56
|
error_message: Optional[str] = None
|
|
57
57
|
model: Optional[str] = None
|
|
@@ -28,7 +28,7 @@ import json
|
|
|
28
28
|
import time
|
|
29
29
|
import uuid
|
|
30
30
|
from datetime import datetime, timezone
|
|
31
|
-
from typing import Any, Callable, Dict, Optional
|
|
31
|
+
from typing import Any, Callable, Dict, Optional, Union
|
|
32
32
|
|
|
33
33
|
from .context import ActiveSpanContext, get_active_context, run_with_context
|
|
34
34
|
from .processor import TrodoSpanProcessor, TrodoRun, TrodoSpan
|
|
@@ -62,7 +62,11 @@ def _now_iso() -> str:
|
|
|
62
62
|
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
_MAX_VALUE_LEN = 1_000_000 # 1 MB
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _truncate(value: Any, max_len: int = _MAX_VALUE_LEN) -> Optional[str]:
|
|
69
|
+
"""Coerce to string and truncate. Use for string-only contexts (OTel attrs, error summaries)."""
|
|
66
70
|
if value is None:
|
|
67
71
|
return None
|
|
68
72
|
if isinstance(value, str):
|
|
@@ -75,6 +79,26 @@ def _truncate(value: Any, max_len: int = 64_000) -> Optional[str]:
|
|
|
75
79
|
return s[:max_len] if len(s) > max_len else s
|
|
76
80
|
|
|
77
81
|
|
|
82
|
+
def _prepare_value(value: Any, max_len: int = _MAX_VALUE_LEN) -> Optional[Union[str, Dict[str, Any]]]:
|
|
83
|
+
"""Prepare a value for storage in the JSONB input/output column.
|
|
84
|
+
|
|
85
|
+
Dicts/lists pass through as-is (stored as JSONB objects/arrays).
|
|
86
|
+
Strings are truncated at max_len.
|
|
87
|
+
Everything else is JSON-serialised then truncated.
|
|
88
|
+
"""
|
|
89
|
+
if value is None:
|
|
90
|
+
return None
|
|
91
|
+
if isinstance(value, dict):
|
|
92
|
+
return value
|
|
93
|
+
if isinstance(value, str):
|
|
94
|
+
return value[:max_len] if len(value) > max_len else value
|
|
95
|
+
try:
|
|
96
|
+
s = json.dumps(value, default=str)
|
|
97
|
+
except Exception:
|
|
98
|
+
s = str(value)
|
|
99
|
+
return s[:max_len] if len(s) > max_len else s
|
|
100
|
+
|
|
101
|
+
|
|
78
102
|
def current_run_id() -> Optional[str]:
|
|
79
103
|
"""Return the run_id of the currently active agent run, if any."""
|
|
80
104
|
ctx = get_active_context()
|
|
@@ -114,21 +138,46 @@ def _aggregate(spans: list[TrodoSpan]) -> Dict[str, Any]:
|
|
|
114
138
|
}
|
|
115
139
|
|
|
116
140
|
|
|
141
|
+
def _mint_anon_distinct_id() -> str:
|
|
142
|
+
"""Mint a server-side anonymous distinct_id for an agent run.
|
|
143
|
+
|
|
144
|
+
Mirrors UserIdentity.generateAnonymousDistinctId on the backend
|
|
145
|
+
(``anon_<ts>_<scope>_<uuid>_<rand>``) so server-SDK anon runs and
|
|
146
|
+
browser-SDK anon sessions share one prefix the dashboard already
|
|
147
|
+
filters on.
|
|
148
|
+
|
|
149
|
+
Minted client-side (not just server-side) so:
|
|
150
|
+
* the RunHandle / OTLP span attribute carries the id immediately and
|
|
151
|
+
downstream ``trodo.feedback(distinct_id=...)`` calls have something
|
|
152
|
+
to bind to,
|
|
153
|
+
* users running against an older backend still get a real attributable
|
|
154
|
+
distinct_id on every ``agent_runs`` row, instead of NULL.
|
|
155
|
+
"""
|
|
156
|
+
import random
|
|
157
|
+
import string
|
|
158
|
+
ts = int(time.time() * 1000)
|
|
159
|
+
rand = "".join(random.choices(string.ascii_lowercase + string.digits, k=16))
|
|
160
|
+
return f"anon_{ts}_python_{uuid.uuid4()}_{rand}"
|
|
161
|
+
|
|
162
|
+
|
|
117
163
|
class RunHandle:
|
|
118
164
|
"""Handle returned by wrap_agent for setting input/output and getting run_id."""
|
|
119
165
|
|
|
120
|
-
def __init__(self, run_id: str, agent_name: str) -> None:
|
|
166
|
+
def __init__(self, run_id: str, agent_name: str, distinct_id: str) -> None:
|
|
121
167
|
self.run_id = run_id
|
|
122
168
|
self.agent_name = agent_name
|
|
123
|
-
|
|
124
|
-
|
|
169
|
+
# Always populated — wrap_agent mints anon if caller didn't pass one
|
|
170
|
+
# so downstream ``trodo.feedback(distinct_id=...)`` always has a target.
|
|
171
|
+
self.distinct_id = distinct_id
|
|
172
|
+
self.input: Optional[Union[str, Dict[str, Any]]] = None
|
|
173
|
+
self.output: Optional[Union[str, Dict[str, Any]]] = None
|
|
125
174
|
self.metadata: Dict[str, Any] = {}
|
|
126
175
|
|
|
127
176
|
def set_input(self, value: Any) -> None:
|
|
128
|
-
self.input =
|
|
177
|
+
self.input = _prepare_value(value)
|
|
129
178
|
|
|
130
179
|
def set_output(self, value: Any) -> None:
|
|
131
|
-
self.output =
|
|
180
|
+
self.output = _prepare_value(value)
|
|
132
181
|
|
|
133
182
|
def set_metadata(self, **kwargs: Any) -> None:
|
|
134
183
|
self.metadata.update(kwargs)
|
|
@@ -140,8 +189,8 @@ class SpanHandle:
|
|
|
140
189
|
def __init__(self, span_id: str, name: str) -> None:
|
|
141
190
|
self.span_id = span_id
|
|
142
191
|
self.name = name
|
|
143
|
-
self.input: Optional[str] = None
|
|
144
|
-
self.output: Optional[str] = None
|
|
192
|
+
self.input: Optional[Union[str, Dict[str, Any]]] = None
|
|
193
|
+
self.output: Optional[Union[str, Dict[str, Any]]] = None
|
|
145
194
|
self.attributes: Dict[str, Any] = {}
|
|
146
195
|
self.model: Optional[str] = None
|
|
147
196
|
self.provider: Optional[str] = None
|
|
@@ -152,10 +201,10 @@ class SpanHandle:
|
|
|
152
201
|
self.tool_name: Optional[str] = None
|
|
153
202
|
|
|
154
203
|
def set_input(self, value: Any) -> None:
|
|
155
|
-
self.input =
|
|
204
|
+
self.input = _prepare_value(value)
|
|
156
205
|
|
|
157
206
|
def set_output(self, value: Any) -> None:
|
|
158
|
-
self.output =
|
|
207
|
+
self.output = _prepare_value(value)
|
|
159
208
|
|
|
160
209
|
def set_attribute(self, key: str, value: Any) -> None:
|
|
161
210
|
self.attributes[key] = value
|
|
@@ -209,14 +258,20 @@ def start_run(
|
|
|
209
258
|
to add spans — they flush incrementally via ``append_spans``.
|
|
210
259
|
"""
|
|
211
260
|
rid = run_id or str(uuid.uuid4())
|
|
261
|
+
# Mint anon when missing so the run row is attributable. The minted id
|
|
262
|
+
# is stamped onto the TrodoRun payload but is not surfaced to the caller —
|
|
263
|
+
# start_run's signature returns just run_id for backward compat. Callers
|
|
264
|
+
# who need the distinct_id should pass their own (or use wrap_agent,
|
|
265
|
+
# whose RunHandle exposes it via handle.distinct_id).
|
|
266
|
+
effective_distinct_id = distinct_id or _mint_anon_distinct_id()
|
|
212
267
|
run = TrodoRun(
|
|
213
268
|
run_id=rid,
|
|
214
269
|
agent_name=agent_name,
|
|
215
|
-
distinct_id=
|
|
270
|
+
distinct_id=effective_distinct_id,
|
|
216
271
|
conversation_id=conversation_id,
|
|
217
272
|
parent_run_id=parent_run_id,
|
|
218
273
|
status="running",
|
|
219
|
-
input=
|
|
274
|
+
input=_prepare_value(input),
|
|
220
275
|
started_at=_now_iso(),
|
|
221
276
|
metadata=metadata,
|
|
222
277
|
)
|
|
@@ -248,7 +303,7 @@ def end_run(
|
|
|
248
303
|
**agg,
|
|
249
304
|
}
|
|
250
305
|
if output is not None:
|
|
251
|
-
payload["output"] =
|
|
306
|
+
payload["output"] = _prepare_value(output)
|
|
252
307
|
if error_summary is not None:
|
|
253
308
|
payload["error_summary"] = _truncate(error_summary, 4_000)
|
|
254
309
|
if metadata is not None:
|
|
@@ -278,7 +333,11 @@ class wrap_agent:
|
|
|
278
333
|
self._processor = processor
|
|
279
334
|
self._team_site_id = team_site_id
|
|
280
335
|
self._agent_name = agent_name
|
|
281
|
-
|
|
336
|
+
# Mint anon when caller didn't pass one. From this point on every
|
|
337
|
+
# internal path uses self._distinct_id, never the raw constructor
|
|
338
|
+
# argument, so the run row, OTLP span attribute, and RunHandle all
|
|
339
|
+
# agree on a single non-null value.
|
|
340
|
+
self._distinct_id = distinct_id or _mint_anon_distinct_id()
|
|
282
341
|
self._conversation_id = conversation_id
|
|
283
342
|
self._parent_run_id = parent_run_id
|
|
284
343
|
self._metadata = metadata
|
|
@@ -312,7 +371,7 @@ class wrap_agent:
|
|
|
312
371
|
self._started_iso = _now_iso()
|
|
313
372
|
self._started_ms = time.time() * 1000.0
|
|
314
373
|
|
|
315
|
-
self.handle = RunHandle(run_id, self._agent_name)
|
|
374
|
+
self.handle = RunHandle(run_id, self._agent_name, self._distinct_id)
|
|
316
375
|
ctx = ActiveSpanContext(
|
|
317
376
|
run_id=run_id,
|
|
318
377
|
span_id=root_span_id,
|
|
@@ -387,8 +446,9 @@ class wrap_agent:
|
|
|
387
446
|
run_id = _hex_to_uuid(trace_id_hex)
|
|
388
447
|
|
|
389
448
|
otel_span.set_attribute("trodo.agent_name", self._agent_name)
|
|
390
|
-
|
|
391
|
-
|
|
449
|
+
# self._distinct_id is always set (anon-minted in __init__ when caller
|
|
450
|
+
# didn't pass one), so the attribute always lands on the OTLP span.
|
|
451
|
+
otel_span.set_attribute("trodo.distinct_id", str(self._distinct_id))
|
|
392
452
|
if self._conversation_id:
|
|
393
453
|
otel_span.set_attribute("trodo.conversation_id", str(self._conversation_id))
|
|
394
454
|
if self._parent_run_id:
|
|
@@ -398,7 +458,7 @@ class wrap_agent:
|
|
|
398
458
|
otel_span.set_attribute(f"trodo.metadata.{k}", _serialize_attr(v))
|
|
399
459
|
|
|
400
460
|
self._otel_span = otel_span
|
|
401
|
-
self.handle = RunHandle(run_id, self._agent_name)
|
|
461
|
+
self.handle = RunHandle(run_id, self._agent_name, self._distinct_id)
|
|
402
462
|
return self.handle
|
|
403
463
|
|
|
404
464
|
def _exit_otel(self, exc_type, exc, tb) -> None:
|
|
@@ -457,7 +517,7 @@ class join_run:
|
|
|
457
517
|
self._parent_span_id = parent_span_id
|
|
458
518
|
self._name = name
|
|
459
519
|
self._kind = kind
|
|
460
|
-
self._input =
|
|
520
|
+
self._input = _prepare_value(input) if input is not None else None
|
|
461
521
|
self._attributes = attributes
|
|
462
522
|
self._ctx_mgr: Optional[run_with_context] = None
|
|
463
523
|
self._started_ms: float = 0.0
|
|
@@ -544,7 +604,7 @@ class span:
|
|
|
544
604
|
) -> None:
|
|
545
605
|
self._name = name
|
|
546
606
|
self._kind = kind
|
|
547
|
-
self._input =
|
|
607
|
+
self._input = _prepare_value(input) if input is not None else None
|
|
548
608
|
self._attributes = attributes
|
|
549
609
|
self._ctx_mgr: Optional[run_with_context] = None
|
|
550
610
|
self._started_ms: float = 0.0
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|