dbos 0.25.0a8__py3-none-any.whl → 0.25.0a12__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.
- dbos/_conductor/conductor.py +32 -1
- dbos/_conductor/protocol.py +34 -0
- dbos/_core.py +86 -26
- dbos/_dbos.py +5 -14
- dbos/_queue.py +4 -4
- dbos/_sys_db.py +42 -5
- {dbos-0.25.0a8.dist-info → dbos-0.25.0a12.dist-info}/METADATA +2 -1
- {dbos-0.25.0a8.dist-info → dbos-0.25.0a12.dist-info}/RECORD +11 -11
- {dbos-0.25.0a8.dist-info → dbos-0.25.0a12.dist-info}/WHEEL +0 -0
- {dbos-0.25.0a8.dist-info → dbos-0.25.0a12.dist-info}/entry_points.txt +0 -0
- {dbos-0.25.0a8.dist-info → dbos-0.25.0a12.dist-info}/licenses/LICENSE +0 -0
dbos/_conductor/conductor.py
CHANGED
@@ -9,7 +9,12 @@ from websockets.sync.client import connect
|
|
9
9
|
from websockets.sync.connection import Connection
|
10
10
|
|
11
11
|
from dbos._utils import GlobalParams
|
12
|
-
from dbos._workflow_commands import
|
12
|
+
from dbos._workflow_commands import (
|
13
|
+
get_workflow,
|
14
|
+
list_queued_workflows,
|
15
|
+
list_workflow_steps,
|
16
|
+
list_workflows,
|
17
|
+
)
|
13
18
|
|
14
19
|
from . import protocol as p
|
15
20
|
|
@@ -243,6 +248,32 @@ class ConductorWebsocket(threading.Thread):
|
|
243
248
|
)
|
244
249
|
)
|
245
250
|
websocket.send(exist_pending_workflows_response.to_json())
|
251
|
+
elif msg_type == p.MessageType.LIST_STEPS:
|
252
|
+
list_steps_message = p.ListStepsRequest.from_json(message)
|
253
|
+
step_info = None
|
254
|
+
try:
|
255
|
+
step_info = list_workflow_steps(
|
256
|
+
self.dbos._sys_db,
|
257
|
+
list_steps_message.workflow_id,
|
258
|
+
)
|
259
|
+
except Exception as e:
|
260
|
+
error_message = f"Exception encountered when getting workflow {list_steps_message.workflow_id}: {traceback.format_exc()}"
|
261
|
+
self.dbos.logger.error(error_message)
|
262
|
+
|
263
|
+
list_steps_response = p.ListStepsResponse(
|
264
|
+
type=p.MessageType.LIST_STEPS,
|
265
|
+
request_id=base_message.request_id,
|
266
|
+
output=(
|
267
|
+
[
|
268
|
+
p.WorkflowSteps.from_step_info(i)
|
269
|
+
for i in step_info
|
270
|
+
]
|
271
|
+
if step_info is not None
|
272
|
+
else None
|
273
|
+
),
|
274
|
+
error_message=error_message,
|
275
|
+
)
|
276
|
+
websocket.send(list_steps_response.to_json())
|
246
277
|
else:
|
247
278
|
self.dbos.logger.warning(
|
248
279
|
f"Unexpected message type: {msg_type}"
|
dbos/_conductor/protocol.py
CHANGED
@@ -3,6 +3,7 @@ from dataclasses import asdict, dataclass
|
|
3
3
|
from enum import Enum
|
4
4
|
from typing import List, Optional, Type, TypedDict, TypeVar
|
5
5
|
|
6
|
+
from dbos._sys_db import StepInfo
|
6
7
|
from dbos._workflow_commands import WorkflowStatus
|
7
8
|
|
8
9
|
|
@@ -16,6 +17,7 @@ class MessageType(str, Enum):
|
|
16
17
|
RESTART = "restart"
|
17
18
|
GET_WORKFLOW = "get_workflow"
|
18
19
|
EXIST_PENDING_WORKFLOWS = "exist_pending_workflows"
|
20
|
+
LIST_STEPS = "list_steps"
|
19
21
|
|
20
22
|
|
21
23
|
T = TypeVar("T", bound="BaseMessage")
|
@@ -176,6 +178,27 @@ class WorkflowsOutput:
|
|
176
178
|
)
|
177
179
|
|
178
180
|
|
181
|
+
@dataclass
|
182
|
+
class WorkflowSteps:
|
183
|
+
function_id: int
|
184
|
+
function_name: str
|
185
|
+
output: Optional[str]
|
186
|
+
error: Optional[str]
|
187
|
+
child_workflow_id: Optional[str]
|
188
|
+
|
189
|
+
@classmethod
|
190
|
+
def from_step_info(cls, info: StepInfo) -> "WorkflowSteps":
|
191
|
+
output_str = str(info["output"]) if info["output"] is not None else None
|
192
|
+
error_str = str(info["error"]) if info["error"] is not None else None
|
193
|
+
return cls(
|
194
|
+
function_id=info["function_id"],
|
195
|
+
function_name=info["function_name"],
|
196
|
+
output=output_str,
|
197
|
+
error=error_str,
|
198
|
+
child_workflow_id=info["child_workflow_id"],
|
199
|
+
)
|
200
|
+
|
201
|
+
|
179
202
|
@dataclass
|
180
203
|
class ListWorkflowsRequest(BaseMessage):
|
181
204
|
body: ListWorkflowsBody
|
@@ -230,3 +253,14 @@ class ExistPendingWorkflowsRequest(BaseMessage):
|
|
230
253
|
class ExistPendingWorkflowsResponse(BaseMessage):
|
231
254
|
exist: bool
|
232
255
|
error_message: Optional[str] = None
|
256
|
+
|
257
|
+
|
258
|
+
@dataclass
|
259
|
+
class ListStepsRequest(BaseMessage):
|
260
|
+
workflow_id: str
|
261
|
+
|
262
|
+
|
263
|
+
@dataclass
|
264
|
+
class ListStepsResponse(BaseMessage):
|
265
|
+
output: Optional[List[WorkflowSteps]]
|
266
|
+
error_message: Optional[str] = None
|
dbos/_core.py
CHANGED
@@ -81,7 +81,6 @@ from ._sys_db import (
|
|
81
81
|
if TYPE_CHECKING:
|
82
82
|
from ._dbos import (
|
83
83
|
DBOS,
|
84
|
-
Workflow,
|
85
84
|
WorkflowHandle,
|
86
85
|
WorkflowHandleAsync,
|
87
86
|
DBOSRegistry,
|
@@ -109,7 +108,15 @@ class WorkflowHandleFuture(Generic[R]):
|
|
109
108
|
return self.workflow_id
|
110
109
|
|
111
110
|
def get_result(self) -> R:
|
112
|
-
|
111
|
+
try:
|
112
|
+
r = self.future.result()
|
113
|
+
except Exception as e:
|
114
|
+
serialized_e = _serialization.serialize_exception(e)
|
115
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, None, serialized_e)
|
116
|
+
raise
|
117
|
+
serialized_r = _serialization.serialize(r)
|
118
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, serialized_r, None)
|
119
|
+
return r
|
113
120
|
|
114
121
|
def get_status(self) -> "WorkflowStatus":
|
115
122
|
stat = self.dbos.get_workflow_status(self.workflow_id)
|
@@ -128,8 +135,15 @@ class WorkflowHandlePolling(Generic[R]):
|
|
128
135
|
return self.workflow_id
|
129
136
|
|
130
137
|
def get_result(self) -> R:
|
131
|
-
|
132
|
-
|
138
|
+
try:
|
139
|
+
r: R = self.dbos._sys_db.await_workflow_result(self.workflow_id)
|
140
|
+
except Exception as e:
|
141
|
+
serialized_e = _serialization.serialize_exception(e)
|
142
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, None, serialized_e)
|
143
|
+
raise
|
144
|
+
serialized_r = _serialization.serialize(r)
|
145
|
+
self.dbos._sys_db.record_get_result(self.workflow_id, serialized_r, None)
|
146
|
+
return r
|
133
147
|
|
134
148
|
def get_status(self) -> "WorkflowStatus":
|
135
149
|
stat = self.dbos.get_workflow_status(self.workflow_id)
|
@@ -149,7 +163,22 @@ class WorkflowHandleAsyncTask(Generic[R]):
|
|
149
163
|
return self.workflow_id
|
150
164
|
|
151
165
|
async def get_result(self) -> R:
|
152
|
-
|
166
|
+
try:
|
167
|
+
r = await self.task
|
168
|
+
except Exception as e:
|
169
|
+
serialized_e = _serialization.serialize_exception(e)
|
170
|
+
await asyncio.to_thread(
|
171
|
+
self.dbos._sys_db.record_get_result,
|
172
|
+
self.workflow_id,
|
173
|
+
None,
|
174
|
+
serialized_e,
|
175
|
+
)
|
176
|
+
raise
|
177
|
+
serialized_r = _serialization.serialize(r)
|
178
|
+
await asyncio.to_thread(
|
179
|
+
self.dbos._sys_db.record_get_result, self.workflow_id, serialized_r, None
|
180
|
+
)
|
181
|
+
return r
|
153
182
|
|
154
183
|
async def get_status(self) -> "WorkflowStatus":
|
155
184
|
stat = await asyncio.to_thread(self.dbos.get_workflow_status, self.workflow_id)
|
@@ -168,10 +197,24 @@ class WorkflowHandleAsyncPolling(Generic[R]):
|
|
168
197
|
return self.workflow_id
|
169
198
|
|
170
199
|
async def get_result(self) -> R:
|
171
|
-
|
172
|
-
|
200
|
+
try:
|
201
|
+
r: R = await asyncio.to_thread(
|
202
|
+
self.dbos._sys_db.await_workflow_result, self.workflow_id
|
203
|
+
)
|
204
|
+
except Exception as e:
|
205
|
+
serialized_e = _serialization.serialize_exception(e)
|
206
|
+
await asyncio.to_thread(
|
207
|
+
self.dbos._sys_db.record_get_result,
|
208
|
+
self.workflow_id,
|
209
|
+
None,
|
210
|
+
serialized_e,
|
211
|
+
)
|
212
|
+
raise
|
213
|
+
serialized_r = _serialization.serialize(r)
|
214
|
+
await asyncio.to_thread(
|
215
|
+
self.dbos._sys_db.record_get_result, self.workflow_id, serialized_r, None
|
173
216
|
)
|
174
|
-
return
|
217
|
+
return r
|
175
218
|
|
176
219
|
async def get_status(self) -> "WorkflowStatus":
|
177
220
|
stat = await asyncio.to_thread(self.dbos.get_workflow_status, self.workflow_id)
|
@@ -279,13 +322,9 @@ def _get_wf_invoke_func(
|
|
279
322
|
dbos._sys_db.buffer_workflow_status(status)
|
280
323
|
return output
|
281
324
|
except DBOSWorkflowConflictIDError:
|
282
|
-
#
|
283
|
-
|
284
|
-
|
285
|
-
status["workflow_uuid"], existing_workflow=False
|
286
|
-
)
|
287
|
-
output = wf_handle.get_result()
|
288
|
-
return output
|
325
|
+
# Await the workflow result
|
326
|
+
r: R = dbos._sys_db.await_workflow_result(status["workflow_uuid"])
|
327
|
+
return r
|
289
328
|
except DBOSWorkflowCancelledError as error:
|
290
329
|
raise
|
291
330
|
except Exception as error:
|
@@ -304,7 +343,7 @@ def _get_wf_invoke_func(
|
|
304
343
|
def _execute_workflow_wthread(
|
305
344
|
dbos: "DBOS",
|
306
345
|
status: WorkflowStatusInternal,
|
307
|
-
func: "
|
346
|
+
func: "Callable[P, R]",
|
308
347
|
ctx: DBOSContext,
|
309
348
|
*args: Any,
|
310
349
|
**kwargs: Any,
|
@@ -335,7 +374,7 @@ def _execute_workflow_wthread(
|
|
335
374
|
async def _execute_workflow_async(
|
336
375
|
dbos: "DBOS",
|
337
376
|
status: WorkflowStatusInternal,
|
338
|
-
func: "
|
377
|
+
func: "Callable[P, Coroutine[Any, Any, R]]",
|
339
378
|
ctx: DBOSContext,
|
340
379
|
*args: Any,
|
341
380
|
**kwargs: Any,
|
@@ -449,7 +488,7 @@ def _get_new_wf() -> tuple[str, DBOSContext]:
|
|
449
488
|
|
450
489
|
def start_workflow(
|
451
490
|
dbos: "DBOS",
|
452
|
-
func: "
|
491
|
+
func: "Callable[P, Union[R, Coroutine[Any, Any, R]]]",
|
453
492
|
queue_name: Optional[str],
|
454
493
|
execute_workflow: bool,
|
455
494
|
*args: P.args,
|
@@ -505,7 +544,7 @@ def start_workflow(
|
|
505
544
|
ctx.parent_workflow_id,
|
506
545
|
new_child_workflow_id,
|
507
546
|
ctx.parent_workflow_fid,
|
508
|
-
func
|
547
|
+
get_dbos_func_name(func),
|
509
548
|
)
|
510
549
|
|
511
550
|
if not execute_workflow or (
|
@@ -531,7 +570,7 @@ def start_workflow(
|
|
531
570
|
|
532
571
|
async def start_workflow_async(
|
533
572
|
dbos: "DBOS",
|
534
|
-
func: "
|
573
|
+
func: "Callable[P, Coroutine[Any, Any, R]]",
|
535
574
|
queue_name: Optional[str],
|
536
575
|
execute_workflow: bool,
|
537
576
|
*args: P.args,
|
@@ -590,7 +629,7 @@ async def start_workflow_async(
|
|
590
629
|
ctx.parent_workflow_id,
|
591
630
|
new_child_workflow_id,
|
592
631
|
ctx.parent_workflow_fid,
|
593
|
-
func
|
632
|
+
get_dbos_func_name(func),
|
594
633
|
)
|
595
634
|
|
596
635
|
wf_status = status["status"]
|
@@ -632,8 +671,6 @@ def workflow_wrapper(
|
|
632
671
|
) -> Callable[P, R]:
|
633
672
|
func.__orig_func = func # type: ignore
|
634
673
|
|
635
|
-
funcName = func.__name__
|
636
|
-
|
637
674
|
fi = get_or_create_func_info(func)
|
638
675
|
fi.max_recovery_attempts = max_recovery_attempts
|
639
676
|
|
@@ -663,17 +700,22 @@ def workflow_wrapper(
|
|
663
700
|
|
664
701
|
wfOutcome = Outcome[R].make(functools.partial(func, *args, **kwargs))
|
665
702
|
|
703
|
+
workflow_id = None
|
704
|
+
|
666
705
|
def init_wf() -> Callable[[Callable[[], R]], R]:
|
667
706
|
|
668
707
|
def recorded_result(
|
669
708
|
c_wfid: str, dbos: "DBOS"
|
670
709
|
) -> Callable[[Callable[[], R]], R]:
|
671
710
|
def recorded_result_inner(func: Callable[[], R]) -> R:
|
672
|
-
|
711
|
+
r: R = dbos._sys_db.await_workflow_result(c_wfid)
|
712
|
+
return r
|
673
713
|
|
674
714
|
return recorded_result_inner
|
675
715
|
|
676
716
|
ctx = assert_current_dbos_context() # Now the child ctx
|
717
|
+
nonlocal workflow_id
|
718
|
+
workflow_id = ctx.workflow_id
|
677
719
|
|
678
720
|
if ctx.has_parent():
|
679
721
|
child_workflow_id = dbos._sys_db.check_child_workflow(
|
@@ -703,15 +745,33 @@ def workflow_wrapper(
|
|
703
745
|
ctx.parent_workflow_id,
|
704
746
|
ctx.workflow_id,
|
705
747
|
ctx.parent_workflow_fid,
|
706
|
-
|
748
|
+
get_dbos_func_name(func),
|
707
749
|
)
|
708
750
|
|
709
751
|
return _get_wf_invoke_func(dbos, status)
|
710
752
|
|
753
|
+
def record_get_result(func: Callable[[], R]) -> R:
|
754
|
+
"""
|
755
|
+
If a child workflow is invoked synchronously, this records the implicit "getResult" where the
|
756
|
+
parent retrieves the child's output. It executes in the CALLER'S context, not the workflow's.
|
757
|
+
"""
|
758
|
+
try:
|
759
|
+
r = func()
|
760
|
+
except Exception as e:
|
761
|
+
serialized_e = _serialization.serialize_exception(e)
|
762
|
+
assert workflow_id is not None
|
763
|
+
dbos._sys_db.record_get_result(workflow_id, None, serialized_e)
|
764
|
+
raise
|
765
|
+
serialized_r = _serialization.serialize(r)
|
766
|
+
assert workflow_id is not None
|
767
|
+
dbos._sys_db.record_get_result(workflow_id, serialized_r, None)
|
768
|
+
return r
|
769
|
+
|
711
770
|
outcome = (
|
712
771
|
wfOutcome.wrap(init_wf)
|
713
772
|
.also(DBOSAssumeRole(rr))
|
714
773
|
.also(enterWorkflowCtxMgr(attributes))
|
774
|
+
.then(record_get_result)
|
715
775
|
)
|
716
776
|
return outcome() # type: ignore
|
717
777
|
|
@@ -914,7 +974,7 @@ def decorate_step(
|
|
914
974
|
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
915
975
|
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
916
976
|
|
917
|
-
stepName = func.
|
977
|
+
stepName = func.__qualname__
|
918
978
|
|
919
979
|
def invoke_step(*args: Any, **kwargs: Any) -> Any:
|
920
980
|
if dbosreg.dbos is None:
|
dbos/_dbos.py
CHANGED
@@ -126,17 +126,6 @@ R = TypeVar("R", covariant=True) # A generic type for workflow return values
|
|
126
126
|
|
127
127
|
T = TypeVar("T")
|
128
128
|
|
129
|
-
|
130
|
-
class DBOSCallProtocol(Protocol[P, R]):
|
131
|
-
__name__: str
|
132
|
-
__qualname__: str
|
133
|
-
|
134
|
-
def __call__(*args: P.args, **kwargs: P.kwargs) -> R: ...
|
135
|
-
|
136
|
-
|
137
|
-
Workflow: TypeAlias = DBOSCallProtocol[P, R]
|
138
|
-
|
139
|
-
|
140
129
|
IsolationLevel = Literal[
|
141
130
|
"SERIALIZABLE",
|
142
131
|
"REPEATABLE READ",
|
@@ -169,7 +158,7 @@ RegisteredJob = Tuple[
|
|
169
158
|
|
170
159
|
class DBOSRegistry:
|
171
160
|
def __init__(self) -> None:
|
172
|
-
self.workflow_info_map: dict[str,
|
161
|
+
self.workflow_info_map: dict[str, Callable[..., Any]] = {}
|
173
162
|
self.function_type_map: dict[str, str] = {}
|
174
163
|
self.class_info_map: dict[str, type] = {}
|
175
164
|
self.instance_info_map: dict[str, object] = {}
|
@@ -559,6 +548,8 @@ class DBOS:
|
|
559
548
|
"""
|
560
549
|
if _dbos_global_instance is not None:
|
561
550
|
_dbos_global_instance._reset_system_database()
|
551
|
+
else:
|
552
|
+
dbos_logger.warning("reset_system_database has no effect because global DBOS object does not exist")
|
562
553
|
|
563
554
|
def _reset_system_database(self) -> None:
|
564
555
|
assert (
|
@@ -713,7 +704,7 @@ class DBOS:
|
|
713
704
|
@classmethod
|
714
705
|
def start_workflow(
|
715
706
|
cls,
|
716
|
-
func:
|
707
|
+
func: Callable[P, R],
|
717
708
|
*args: P.args,
|
718
709
|
**kwargs: P.kwargs,
|
719
710
|
) -> WorkflowHandle[R]:
|
@@ -723,7 +714,7 @@ class DBOS:
|
|
723
714
|
@classmethod
|
724
715
|
async def start_workflow_async(
|
725
716
|
cls,
|
726
|
-
func:
|
717
|
+
func: Callable[P, Coroutine[Any, Any, R]],
|
727
718
|
*args: P.args,
|
728
719
|
**kwargs: P.kwargs,
|
729
720
|
) -> WorkflowHandleAsync[R]:
|
dbos/_queue.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import threading
|
2
2
|
import traceback
|
3
|
-
from typing import TYPE_CHECKING, Any, Coroutine, Optional, TypedDict
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypedDict
|
4
4
|
|
5
5
|
from psycopg import errors
|
6
6
|
from sqlalchemy.exc import OperationalError
|
@@ -10,7 +10,7 @@ from dbos._utils import GlobalParams
|
|
10
10
|
from ._core import P, R, execute_workflow_by_id, start_workflow, start_workflow_async
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
|
-
from ._dbos import DBOS,
|
13
|
+
from ._dbos import DBOS, WorkflowHandle, WorkflowHandleAsync
|
14
14
|
|
15
15
|
|
16
16
|
class QueueRateLimit(TypedDict):
|
@@ -59,7 +59,7 @@ class Queue:
|
|
59
59
|
registry.queue_info_map[self.name] = self
|
60
60
|
|
61
61
|
def enqueue(
|
62
|
-
self, func: "
|
62
|
+
self, func: "Callable[P, R]", *args: P.args, **kwargs: P.kwargs
|
63
63
|
) -> "WorkflowHandle[R]":
|
64
64
|
from ._dbos import _get_dbos_instance
|
65
65
|
|
@@ -68,7 +68,7 @@ class Queue:
|
|
68
68
|
|
69
69
|
async def enqueue_async(
|
70
70
|
self,
|
71
|
-
func: "
|
71
|
+
func: "Callable[P, Coroutine[Any, Any, R]]",
|
72
72
|
*args: P.args,
|
73
73
|
**kwargs: P.kwargs,
|
74
74
|
) -> "WorkflowHandleAsync[R]":
|
dbos/_sys_db.py
CHANGED
@@ -33,7 +33,6 @@ from ._dbos_config import ConfigFile
|
|
33
33
|
from ._error import (
|
34
34
|
DBOSConflictingWorkflowError,
|
35
35
|
DBOSDeadLetterQueueError,
|
36
|
-
DBOSException,
|
37
36
|
DBOSNonExistentWorkflowError,
|
38
37
|
DBOSWorkflowConflictIDError,
|
39
38
|
)
|
@@ -154,10 +153,15 @@ class GetPendingWorkflowsOutput:
|
|
154
153
|
|
155
154
|
|
156
155
|
class StepInfo(TypedDict):
|
156
|
+
# The unique ID of the step in the workflow
|
157
157
|
function_id: int
|
158
|
+
# The (fully qualified) name of the step
|
158
159
|
function_name: str
|
159
|
-
output
|
160
|
-
|
160
|
+
# The step's output, if any
|
161
|
+
output: Optional[Any]
|
162
|
+
# The error the step threw, if any
|
163
|
+
error: Optional[Exception]
|
164
|
+
# If the step starts or retrieves the result of a workflow, its ID
|
161
165
|
child_workflow_id: Optional[str]
|
162
166
|
|
163
167
|
|
@@ -771,8 +775,16 @@ class SystemDatabase:
|
|
771
775
|
StepInfo(
|
772
776
|
function_id=row[0],
|
773
777
|
function_name=row[1],
|
774
|
-
output=
|
775
|
-
|
778
|
+
output=(
|
779
|
+
_serialization.deserialize(row[2])
|
780
|
+
if row[2] is not None
|
781
|
+
else row[2]
|
782
|
+
),
|
783
|
+
error=(
|
784
|
+
_serialization.deserialize_exception(row[3])
|
785
|
+
if row[3] is not None
|
786
|
+
else row[3]
|
787
|
+
),
|
776
788
|
child_workflow_id=row[4],
|
777
789
|
)
|
778
790
|
for row in rows
|
@@ -804,6 +816,31 @@ class SystemDatabase:
|
|
804
816
|
raise DBOSWorkflowConflictIDError(result["workflow_uuid"])
|
805
817
|
raise
|
806
818
|
|
819
|
+
def record_get_result(
|
820
|
+
self, result_workflow_id: str, output: Optional[str], error: Optional[str]
|
821
|
+
) -> None:
|
822
|
+
ctx = get_local_dbos_context()
|
823
|
+
# Only record get_result called in workflow functions
|
824
|
+
if ctx is None or not ctx.is_workflow():
|
825
|
+
return
|
826
|
+
ctx.function_id += 1 # Record the get_result as a step
|
827
|
+
# Because there's no corresponding check, we do nothing on conflict
|
828
|
+
# and do not raise a DBOSWorkflowConflictIDError
|
829
|
+
sql = (
|
830
|
+
pg.insert(SystemSchema.operation_outputs)
|
831
|
+
.values(
|
832
|
+
workflow_uuid=ctx.workflow_id,
|
833
|
+
function_id=ctx.function_id,
|
834
|
+
function_name="DBOS.getResult",
|
835
|
+
output=output,
|
836
|
+
error=error,
|
837
|
+
child_workflow_id=result_workflow_id,
|
838
|
+
)
|
839
|
+
.on_conflict_do_nothing()
|
840
|
+
)
|
841
|
+
with self.engine.begin() as c:
|
842
|
+
c.execute(sql)
|
843
|
+
|
807
844
|
def record_child_workflow(
|
808
845
|
self,
|
809
846
|
parentUUID: str,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dbos
|
3
|
-
Version: 0.25.
|
3
|
+
Version: 0.25.0a12
|
4
4
|
Summary: Ultra-lightweight durable execution in Python
|
5
5
|
Author-Email: "DBOS, Inc." <contact@dbos.dev>
|
6
6
|
License: MIT
|
@@ -23,6 +23,7 @@ Requires-Dist: cryptography>=43.0.3
|
|
23
23
|
Requires-Dist: rich>=13.9.4
|
24
24
|
Requires-Dist: pyjwt>=2.10.1
|
25
25
|
Requires-Dist: websockets>=15.0
|
26
|
+
Requires-Dist: pyright>=1.1.398
|
26
27
|
Description-Content-Type: text/markdown
|
27
28
|
|
28
29
|
|
@@ -1,7 +1,7 @@
|
|
1
|
-
dbos-0.25.
|
2
|
-
dbos-0.25.
|
3
|
-
dbos-0.25.
|
4
|
-
dbos-0.25.
|
1
|
+
dbos-0.25.0a12.dist-info/METADATA,sha256=_7a_sxE2zkGk8RlELQfYhjJ9az9uFS4FUNHA2haL3No,5554
|
2
|
+
dbos-0.25.0a12.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
+
dbos-0.25.0a12.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
|
4
|
+
dbos-0.25.0a12.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
|
5
5
|
dbos/__init__.py,sha256=2Ur8QyNElSVn7CeL9Ovek2Zsye8A_ZCyjb9djF-N4A4,785
|
6
6
|
dbos/__main__.py,sha256=G7Exn-MhGrVJVDbgNlpzhfh8WMX_72t3_oJaFT9Lmt8,653
|
7
7
|
dbos/_admin_server.py,sha256=7kguOf9jEt4vg9LO-QJdh4jYddp6Uqtrt14gh7mKA2Y,6387
|
@@ -10,13 +10,13 @@ dbos/_classproperty.py,sha256=f0X-_BySzn3yFDRKB2JpCbLYQ9tLwt1XftfshvY7CBs,626
|
|
10
10
|
dbos/_cloudutils/authentication.py,sha256=V0fCWQN9stCkhbuuxgPTGpvuQcDqfU3KAxPAh01vKW4,5007
|
11
11
|
dbos/_cloudutils/cloudutils.py,sha256=YC7jGsIopT0KveLsqbRpQk2KlRBk-nIRC_UCgep4f3o,7797
|
12
12
|
dbos/_cloudutils/databases.py,sha256=_shqaqSvhY4n2ScgQ8IP5PDZvzvcx3YBKV8fj-cxhSY,8543
|
13
|
-
dbos/_conductor/conductor.py,sha256=
|
14
|
-
dbos/_conductor/protocol.py,sha256=
|
13
|
+
dbos/_conductor/conductor.py,sha256=7elKINsgl4s1Tg5DwrU-K7xQ5vQvmDAIfAvUgfwpGN0,16784
|
14
|
+
dbos/_conductor/protocol.py,sha256=xN7pmooyF1pqbH1b6WhllU5718P7zSb_b0KCwA6bzcs,6716
|
15
15
|
dbos/_context.py,sha256=3He4w46OTFbR7h8U1MLcdaU10wNyIPBSRqzLkdggv7U,19368
|
16
|
-
dbos/_core.py,sha256=
|
16
|
+
dbos/_core.py,sha256=llBq1lW2spbRDa0ICgVdtgAxVwmGjYNtdXoOVut6xKQ,45732
|
17
17
|
dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
|
18
18
|
dbos/_db_wizard.py,sha256=VnMa6OL87Lc-XPDD1RnXp8NjsJE8YgiQLj3wtWAXp-8,8252
|
19
|
-
dbos/_dbos.py,sha256=
|
19
|
+
dbos/_dbos.py,sha256=YXQjNLR9SgOr2Y9R3tMBA_DkXBJPECzYRTaz-i9GNWA,45458
|
20
20
|
dbos/_dbos_config.py,sha256=7Qm3FARP3lTKZS0gSxDHLbpaDCT30GzfyERxfCde4bc,21566
|
21
21
|
dbos/_debug.py,sha256=mmgvLkqlrljMBBow9wk01PPur9kUf2rI_11dTJXY4gw,1822
|
22
22
|
dbos/_error.py,sha256=B6Y9XLS1f6yrawxB2uAEYFMxFwk9BHhdxPNddKco-Fw,5399
|
@@ -36,7 +36,7 @@ dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4
|
|
36
36
|
dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
|
37
37
|
dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py,sha256=rwfMdqL6LYI4RwtxmB-OJbRKP_wQi-OA8fsmxley-OM,1042
|
38
38
|
dbos/_outcome.py,sha256=EXxBg4jXCVJsByDQ1VOCIedmbeq_03S6d-p1vqQrLFU,6810
|
39
|
-
dbos/_queue.py,sha256=
|
39
|
+
dbos/_queue.py,sha256=yYwKCjxCSFjtD63vpnRQmb835BZAe9UATgWjMW6dcnY,3341
|
40
40
|
dbos/_recovery.py,sha256=4KyZb0XJEUGH7ekYT1kpx38i6y5vygPeH75Ta7RZjYo,2596
|
41
41
|
dbos/_registrations.py,sha256=_zy6k944Ll8QwqU12Kr3OP23ukVtm8axPNN1TS_kJRc,6717
|
42
42
|
dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
|
@@ -46,7 +46,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
46
|
dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
|
47
47
|
dbos/_schemas/system_database.py,sha256=W9eSpL7SZzQkxcEZ4W07BOcwkkDr35b9oCjUOgfHWek,5336
|
48
48
|
dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
|
49
|
-
dbos/_sys_db.py,sha256=
|
49
|
+
dbos/_sys_db.py,sha256=k9fMfg6daAQB012uaPwgkiBhA0wPON8PIwY5Ki9_7Io,68208
|
50
50
|
dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
|
51
51
|
dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
dbos/_templates/dbos-db-starter/__package/main.py,sha256=nJMN3ZD2lmwg4Dcgmiwqc-tQGuCJuJal2Xl85iA277U,2453
|
@@ -66,4 +66,4 @@ dbos/cli/cli.py,sha256=ut47q-R6A423je0zvBTEgwdxENagaKKoyIvyTeACFIU,15977
|
|
66
66
|
dbos/dbos-config.schema.json,sha256=HtF_njVTGHLdzBGZ4OrGQz3qbPPT0Go-iwd1PgFVTNg,5847
|
67
67
|
dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
|
68
68
|
version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
|
69
|
-
dbos-0.25.
|
69
|
+
dbos-0.25.0a12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|