dbos 0.26.0a19__py3-none-any.whl → 0.26.0a21__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/__init__.py CHANGED
@@ -5,8 +5,7 @@ from ._dbos import DBOS, DBOSConfiguredInstance, WorkflowHandle
5
5
  from ._dbos_config import ConfigFile, DBOSConfig, get_dbos_database_url, load_config
6
6
  from ._kafka_message import KafkaMessage
7
7
  from ._queue import Queue
8
- from ._sys_db import GetWorkflowsInput, WorkflowStatusString
9
- from ._workflow_commands import WorkflowStatus
8
+ from ._sys_db import GetWorkflowsInput, WorkflowStatus, WorkflowStatusString
10
9
 
11
10
  __all__ = [
12
11
  "ConfigFile",
dbos/_client.py CHANGED
@@ -19,11 +19,11 @@ from dbos._serialization import WorkflowInputs
19
19
  from dbos._sys_db import (
20
20
  StepInfo,
21
21
  SystemDatabase,
22
+ WorkflowStatus,
22
23
  WorkflowStatusInternal,
23
24
  WorkflowStatusString,
24
25
  )
25
26
  from dbos._workflow_commands import (
26
- WorkflowStatus,
27
27
  fork_workflow,
28
28
  get_workflow,
29
29
  list_queued_workflows,
@@ -54,7 +54,7 @@ class WorkflowHandleClientPolling(Generic[R]):
54
54
  res: R = self._sys_db.await_workflow_result(self.workflow_id)
55
55
  return res
56
56
 
57
- def get_status(self) -> "WorkflowStatus":
57
+ def get_status(self) -> WorkflowStatus:
58
58
  status = get_workflow(self._sys_db, self.workflow_id, True)
59
59
  if status is None:
60
60
  raise DBOSNonExistentWorkflowError(self.workflow_id)
@@ -76,7 +76,7 @@ class WorkflowHandleClientAsyncPolling(Generic[R]):
76
76
  )
77
77
  return res
78
78
 
79
- async def get_status(self) -> "WorkflowStatus":
79
+ async def get_status(self) -> WorkflowStatus:
80
80
  status = await asyncio.to_thread(
81
81
  get_workflow, self._sys_db, self.workflow_id, True
82
82
  )
@@ -134,7 +134,9 @@ class DBOSClient:
134
134
  "kwargs": kwargs,
135
135
  }
136
136
 
137
- self._sys_db.init_workflow(status, _serialization.serialize_args(inputs))
137
+ self._sys_db.init_workflow(
138
+ status, _serialization.serialize_args(inputs), max_recovery_attempts=None
139
+ )
138
140
  return workflow_id
139
141
 
140
142
  def enqueue(
@@ -190,7 +192,9 @@ class DBOSClient:
190
192
  "app_version": None,
191
193
  }
192
194
  with self._sys_db.engine.begin() as conn:
193
- self._sys_db.insert_workflow_status(status, conn)
195
+ self._sys_db.insert_workflow_status(
196
+ status, conn, max_recovery_attempts=None
197
+ )
194
198
  self._sys_db.send(status["workflow_uuid"], 0, destination_id, message, topic)
195
199
 
196
200
  async def send_async(
@@ -3,8 +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
7
- from dbos._workflow_commands import WorkflowStatus
6
+ from dbos._sys_db import StepInfo, WorkflowStatus
8
7
 
9
8
 
10
9
  class MessageType(str, Enum):
dbos/_core.py CHANGED
@@ -75,6 +75,7 @@ from ._serialization import WorkflowInputs
75
75
  from ._sys_db import (
76
76
  GetEventWorkflowContext,
77
77
  OperationResultInternal,
78
+ WorkflowStatus,
78
79
  WorkflowStatusInternal,
79
80
  WorkflowStatusString,
80
81
  )
@@ -87,7 +88,6 @@ if TYPE_CHECKING:
87
88
  DBOSRegistry,
88
89
  IsolationLevel,
89
90
  )
90
- from ._workflow_commands import WorkflowStatus
91
91
 
92
92
  from sqlalchemy.exc import DBAPIError, InvalidRequestError
93
93
 
@@ -119,7 +119,7 @@ class WorkflowHandleFuture(Generic[R]):
119
119
  self.dbos._sys_db.record_get_result(self.workflow_id, serialized_r, None)
120
120
  return r
121
121
 
122
- def get_status(self) -> "WorkflowStatus":
122
+ def get_status(self) -> WorkflowStatus:
123
123
  stat = self.dbos.get_workflow_status(self.workflow_id)
124
124
  if stat is None:
125
125
  raise DBOSNonExistentWorkflowError(self.workflow_id)
@@ -146,7 +146,7 @@ class WorkflowHandlePolling(Generic[R]):
146
146
  self.dbos._sys_db.record_get_result(self.workflow_id, serialized_r, None)
147
147
  return r
148
148
 
149
- def get_status(self) -> "WorkflowStatus":
149
+ def get_status(self) -> WorkflowStatus:
150
150
  stat = self.dbos.get_workflow_status(self.workflow_id)
151
151
  if stat is None:
152
152
  raise DBOSNonExistentWorkflowError(self.workflow_id)
@@ -181,7 +181,7 @@ class WorkflowHandleAsyncTask(Generic[R]):
181
181
  )
182
182
  return r
183
183
 
184
- async def get_status(self) -> "WorkflowStatus":
184
+ async def get_status(self) -> WorkflowStatus:
185
185
  stat = await asyncio.to_thread(self.dbos.get_workflow_status, self.workflow_id)
186
186
  if stat is None:
187
187
  raise DBOSNonExistentWorkflowError(self.workflow_id)
@@ -217,7 +217,7 @@ class WorkflowHandleAsyncPolling(Generic[R]):
217
217
  )
218
218
  return r
219
219
 
220
- async def get_status(self) -> "WorkflowStatus":
220
+ async def get_status(self) -> WorkflowStatus:
221
221
  stat = await asyncio.to_thread(self.dbos.get_workflow_status, self.workflow_id)
222
222
  if stat is None:
223
223
  raise DBOSNonExistentWorkflowError(self.workflow_id)
@@ -232,8 +232,8 @@ def _init_workflow(
232
232
  class_name: Optional[str],
233
233
  config_name: Optional[str],
234
234
  temp_wf_type: Optional[str],
235
- queue: Optional[str] = None,
236
- max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
235
+ queue: Optional[str],
236
+ max_recovery_attempts: Optional[int],
237
237
  ) -> WorkflowStatusInternal:
238
238
  wfid = (
239
239
  ctx.workflow_id
@@ -653,7 +653,7 @@ else:
653
653
  def workflow_wrapper(
654
654
  dbosreg: "DBOSRegistry",
655
655
  func: Callable[P, R],
656
- max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
656
+ max_recovery_attempts: Optional[int] = DEFAULT_MAX_RECOVERY_ATTEMPTS,
657
657
  ) -> Callable[P, R]:
658
658
  func.__orig_func = func # type: ignore
659
659
 
@@ -718,6 +718,7 @@ def workflow_wrapper(
718
718
  class_name=get_dbos_class_name(fi, func, args),
719
719
  config_name=get_config_name(fi, func, args),
720
720
  temp_wf_type=get_temp_workflow_type(func),
721
+ queue=None,
721
722
  max_recovery_attempts=max_recovery_attempts,
722
723
  )
723
724
 
@@ -765,7 +766,7 @@ def workflow_wrapper(
765
766
 
766
767
 
767
768
  def decorate_workflow(
768
- reg: "DBOSRegistry", max_recovery_attempts: int
769
+ reg: "DBOSRegistry", max_recovery_attempts: Optional[int]
769
770
  ) -> Callable[[Callable[P, R]], Callable[P, R]]:
770
771
  def _workflow_decorator(func: Callable[P, R]) -> Callable[P, R]:
771
772
  wrapped_func = workflow_wrapper(reg, func, max_recovery_attempts)
dbos/_dbos.py CHANGED
@@ -31,9 +31,9 @@ from typing import (
31
31
  from opentelemetry.trace import Span
32
32
 
33
33
  from dbos._conductor.conductor import ConductorWebsocket
34
+ from dbos._sys_db import WorkflowStatus
34
35
  from dbos._utils import INTERNAL_QUEUE_NAME, GlobalParams
35
36
  from dbos._workflow_commands import (
36
- WorkflowStatus,
37
37
  fork_workflow,
38
38
  list_queued_workflows,
39
39
  list_workflows,
@@ -68,7 +68,7 @@ from ._registrations import (
68
68
  )
69
69
  from ._roles import default_required_roles, required_roles
70
70
  from ._scheduler import ScheduledWorkflow, scheduled
71
- from ._sys_db import StepInfo, reset_system_database
71
+ from ._sys_db import StepInfo, WorkflowStatus, reset_system_database
72
72
  from ._tracer import dbos_tracer
73
73
 
74
74
  if TYPE_CHECKING:
@@ -114,7 +114,7 @@ from ._error import (
114
114
  from ._event_loop import BackgroundEventLoop
115
115
  from ._logger import add_otlp_to_all_loggers, config_logger, dbos_logger, init_logger
116
116
  from ._sys_db import SystemDatabase
117
- from ._workflow_commands import WorkflowStatus, get_workflow, list_workflow_steps
117
+ from ._workflow_commands import get_workflow, list_workflow_steps
118
118
 
119
119
  # Most DBOS functions are just any callable F, so decorators / wrappers work on F
120
120
  # There are cases where the parameters P and return value R should be separate
@@ -600,7 +600,7 @@ class DBOS:
600
600
  # Decorators for DBOS functionality
601
601
  @classmethod
602
602
  def workflow(
603
- cls, *, max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS
603
+ cls, *, max_recovery_attempts: Optional[int] = DEFAULT_MAX_RECOVERY_ATTEMPTS
604
604
  ) -> Callable[[Callable[P, R]], Callable[P, R]]:
605
605
  """Decorate a function for use as a DBOS workflow."""
606
606
  return decorate_workflow(_get_or_create_dbos_registry(), max_recovery_attempts)
dbos/_registrations.py CHANGED
@@ -51,7 +51,7 @@ class DBOSFuncInfo:
51
51
  class_info: Optional[DBOSClassInfo] = None
52
52
  func_type: DBOSFuncType = DBOSFuncType.Unknown
53
53
  required_roles: Optional[List[str]] = None
54
- max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS
54
+ max_recovery_attempts: Optional[int] = DEFAULT_MAX_RECOVERY_ATTEMPTS
55
55
 
56
56
 
57
57
  def get_or_create_class_info(
dbos/_sys_db.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import datetime
2
+ import json
2
3
  import logging
3
4
  import os
4
5
  import re
@@ -64,6 +65,50 @@ WorkflowStatuses = Literal[
64
65
  ]
65
66
 
66
67
 
68
+ class WorkflowStatus:
69
+ # The workflow ID
70
+ workflow_id: str
71
+ # The workflow status. Must be one of ENQUEUED, PENDING, SUCCESS, ERROR, CANCELLED, or RETRIES_EXCEEDED
72
+ status: str
73
+ # The name of the workflow function
74
+ name: str
75
+ # The name of the workflow's class, if any
76
+ class_name: Optional[str]
77
+ # The name with which the workflow's class instance was configured, if any
78
+ config_name: Optional[str]
79
+ # The user who ran the workflow, if specified
80
+ authenticated_user: Optional[str]
81
+ # The role with which the workflow ran, if specified
82
+ assumed_role: Optional[str]
83
+ # All roles which the authenticated user could assume
84
+ authenticated_roles: Optional[list[str]]
85
+ # The deserialized workflow input object
86
+ input: Optional[_serialization.WorkflowInputs]
87
+ # The workflow's output, if any
88
+ output: Optional[Any] = None
89
+ # The error the workflow threw, if any
90
+ error: Optional[Exception] = None
91
+ # Workflow start time, as a Unix epoch timestamp in ms
92
+ created_at: Optional[int]
93
+ # Last time the workflow status was updated, as a Unix epoch timestamp in ms
94
+ updated_at: Optional[int]
95
+ # If this workflow was enqueued, on which queue
96
+ queue_name: Optional[str]
97
+ # The executor to most recently executed this workflow
98
+ executor_id: Optional[str]
99
+ # The application version on which this workflow was started
100
+ app_version: Optional[str]
101
+
102
+ # INTERNAL FIELDS
103
+
104
+ # The ID of the application executing this workflow
105
+ app_id: Optional[str]
106
+ # The number of times this workflow's execution has been attempted
107
+ recovery_attempts: Optional[int]
108
+ # The HTTP request that triggered the workflow, if known
109
+ request: Optional[str]
110
+
111
+
67
112
  class WorkflowStatusInternal(TypedDict):
68
113
  workflow_uuid: str
69
114
  status: WorkflowStatuses
@@ -148,11 +193,6 @@ class GetQueuedWorkflowsInput(TypedDict):
148
193
  sort_desc: Optional[bool] # Sort by created_at in DESC or ASC order
149
194
 
150
195
 
151
- class GetWorkflowsOutput:
152
- def __init__(self, workflow_uuids: List[str]):
153
- self.workflow_uuids = workflow_uuids
154
-
155
-
156
196
  class GetPendingWorkflowsOutput:
157
197
  def __init__(self, *, workflow_uuid: str, queue_name: Optional[str] = None):
158
198
  self.workflow_uuid: str = workflow_uuid
@@ -287,7 +327,7 @@ class SystemDatabase:
287
327
  status: WorkflowStatusInternal,
288
328
  conn: sa.Connection,
289
329
  *,
290
- max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
330
+ max_recovery_attempts: Optional[int],
291
331
  ) -> WorkflowStatuses:
292
332
  if self._debug_mode:
293
333
  raise Exception("called insert_workflow_status in debug mode")
@@ -354,7 +394,11 @@ class SystemDatabase:
354
394
 
355
395
  # Every time we start executing a workflow (and thus attempt to insert its status), we increment `recovery_attempts` by 1.
356
396
  # When this number becomes equal to `maxRetries + 1`, we mark the workflow as `RETRIES_EXCEEDED`.
357
- if recovery_attempts > max_recovery_attempts + 1:
397
+ if (
398
+ (wf_status != "SUCCESS" and wf_status != "ERROR")
399
+ and max_recovery_attempts is not None
400
+ and recovery_attempts > max_recovery_attempts + 1
401
+ ):
358
402
  delete_cmd = sa.delete(SystemSchema.workflow_queue).where(
359
403
  SystemSchema.workflow_queue.c.workflow_uuid
360
404
  == status["workflow_uuid"]
@@ -702,8 +746,37 @@ class SystemDatabase:
702
746
  )
703
747
  return inputs
704
748
 
705
- def get_workflows(self, input: GetWorkflowsInput) -> GetWorkflowsOutput:
706
- query = sa.select(SystemSchema.workflow_status.c.workflow_uuid)
749
+ def get_workflows(
750
+ self, input: GetWorkflowsInput, get_request: bool = False
751
+ ) -> List[WorkflowStatus]:
752
+ """
753
+ Retrieve a list of workflows result and inputs based on the input criteria. The result is a list of external-facing workflow status objects.
754
+ """
755
+ query = sa.select(
756
+ SystemSchema.workflow_status.c.workflow_uuid,
757
+ SystemSchema.workflow_status.c.status,
758
+ SystemSchema.workflow_status.c.name,
759
+ SystemSchema.workflow_status.c.request,
760
+ SystemSchema.workflow_status.c.recovery_attempts,
761
+ SystemSchema.workflow_status.c.config_name,
762
+ SystemSchema.workflow_status.c.class_name,
763
+ SystemSchema.workflow_status.c.authenticated_user,
764
+ SystemSchema.workflow_status.c.authenticated_roles,
765
+ SystemSchema.workflow_status.c.assumed_role,
766
+ SystemSchema.workflow_status.c.queue_name,
767
+ SystemSchema.workflow_status.c.executor_id,
768
+ SystemSchema.workflow_status.c.created_at,
769
+ SystemSchema.workflow_status.c.updated_at,
770
+ SystemSchema.workflow_status.c.application_version,
771
+ SystemSchema.workflow_status.c.application_id,
772
+ SystemSchema.workflow_inputs.c.inputs,
773
+ SystemSchema.workflow_status.c.output,
774
+ SystemSchema.workflow_status.c.error,
775
+ ).join(
776
+ SystemSchema.workflow_inputs,
777
+ SystemSchema.workflow_status.c.workflow_uuid
778
+ == SystemSchema.workflow_inputs.c.workflow_uuid,
779
+ )
707
780
  if input.sort_desc:
708
781
  query = query.order_by(SystemSchema.workflow_status.c.created_at.desc())
709
782
  else:
@@ -749,18 +822,76 @@ class SystemDatabase:
749
822
 
750
823
  with self.engine.begin() as c:
751
824
  rows = c.execute(query)
752
- workflow_ids = [row[0] for row in rows]
753
825
 
754
- return GetWorkflowsOutput(workflow_ids)
826
+ infos: List[WorkflowStatus] = []
827
+ for row in rows:
828
+ info = WorkflowStatus()
829
+ info.workflow_id = row[0]
830
+ info.status = row[1]
831
+ info.name = row[2]
832
+ info.request = row[3] if get_request else None
833
+ info.recovery_attempts = row[4]
834
+ info.config_name = row[5]
835
+ info.class_name = row[6]
836
+ info.authenticated_user = row[7]
837
+ info.authenticated_roles = (
838
+ json.loads(row[8]) if row[8] is not None else None
839
+ )
840
+ info.assumed_role = row[9]
841
+ info.queue_name = row[10]
842
+ info.executor_id = row[11]
843
+ info.created_at = row[12]
844
+ info.updated_at = row[13]
845
+ info.app_version = row[14]
846
+ info.app_id = row[15]
847
+
848
+ inputs = _serialization.deserialize_args(row[16])
849
+ if inputs is not None:
850
+ info.input = inputs
851
+ if info.status == WorkflowStatusString.SUCCESS.value:
852
+ info.output = _serialization.deserialize(row[17])
853
+ elif info.status == WorkflowStatusString.ERROR.value:
854
+ info.error = _serialization.deserialize_exception(row[18])
855
+
856
+ infos.append(info)
857
+ return infos
755
858
 
756
859
  def get_queued_workflows(
757
- self, input: GetQueuedWorkflowsInput
758
- ) -> GetWorkflowsOutput:
759
-
760
- query = sa.select(SystemSchema.workflow_queue.c.workflow_uuid).join(
761
- SystemSchema.workflow_status,
762
- SystemSchema.workflow_queue.c.workflow_uuid
763
- == SystemSchema.workflow_status.c.workflow_uuid,
860
+ self, input: GetQueuedWorkflowsInput, get_request: bool = False
861
+ ) -> List[WorkflowStatus]:
862
+ """
863
+ Retrieve a list of queued workflows result and inputs based on the input criteria. The result is a list of external-facing workflow status objects.
864
+ """
865
+ query = sa.select(
866
+ SystemSchema.workflow_status.c.workflow_uuid,
867
+ SystemSchema.workflow_status.c.status,
868
+ SystemSchema.workflow_status.c.name,
869
+ SystemSchema.workflow_status.c.request,
870
+ SystemSchema.workflow_status.c.recovery_attempts,
871
+ SystemSchema.workflow_status.c.config_name,
872
+ SystemSchema.workflow_status.c.class_name,
873
+ SystemSchema.workflow_status.c.authenticated_user,
874
+ SystemSchema.workflow_status.c.authenticated_roles,
875
+ SystemSchema.workflow_status.c.assumed_role,
876
+ SystemSchema.workflow_status.c.queue_name,
877
+ SystemSchema.workflow_status.c.executor_id,
878
+ SystemSchema.workflow_status.c.created_at,
879
+ SystemSchema.workflow_status.c.updated_at,
880
+ SystemSchema.workflow_status.c.application_version,
881
+ SystemSchema.workflow_status.c.application_id,
882
+ SystemSchema.workflow_inputs.c.inputs,
883
+ SystemSchema.workflow_status.c.output,
884
+ SystemSchema.workflow_status.c.error,
885
+ ).select_from(
886
+ SystemSchema.workflow_queue.join(
887
+ SystemSchema.workflow_status,
888
+ SystemSchema.workflow_queue.c.workflow_uuid
889
+ == SystemSchema.workflow_status.c.workflow_uuid,
890
+ ).join(
891
+ SystemSchema.workflow_inputs,
892
+ SystemSchema.workflow_queue.c.workflow_uuid
893
+ == SystemSchema.workflow_inputs.c.workflow_uuid,
894
+ )
764
895
  )
765
896
  if input["sort_desc"]:
766
897
  query = query.order_by(SystemSchema.workflow_status.c.created_at.desc())
@@ -797,9 +928,40 @@ class SystemDatabase:
797
928
 
798
929
  with self.engine.begin() as c:
799
930
  rows = c.execute(query)
800
- workflow_uuids = [row[0] for row in rows]
801
931
 
802
- return GetWorkflowsOutput(workflow_uuids)
932
+ infos: List[WorkflowStatus] = []
933
+ for row in rows:
934
+ info = WorkflowStatus()
935
+ info.workflow_id = row[0]
936
+ info.status = row[1]
937
+ info.name = row[2]
938
+ info.request = row[3] if get_request else None
939
+ info.recovery_attempts = row[4]
940
+ info.config_name = row[5]
941
+ info.class_name = row[6]
942
+ info.authenticated_user = row[7]
943
+ info.authenticated_roles = (
944
+ json.loads(row[8]) if row[8] is not None else None
945
+ )
946
+ info.assumed_role = row[9]
947
+ info.queue_name = row[10]
948
+ info.executor_id = row[11]
949
+ info.created_at = row[12]
950
+ info.updated_at = row[13]
951
+ info.app_version = row[14]
952
+ info.app_id = row[15]
953
+
954
+ inputs = _serialization.deserialize_args(row[16])
955
+ if inputs is not None:
956
+ info.input = inputs
957
+ if info.status == WorkflowStatusString.SUCCESS.value:
958
+ info.output = _serialization.deserialize(row[17])
959
+ elif info.status == WorkflowStatusString.ERROR.value:
960
+ info.error = _serialization.deserialize_exception(row[18])
961
+
962
+ infos.append(info)
963
+
964
+ return infos
803
965
 
804
966
  def get_pending_workflows(
805
967
  self, executor_id: str, app_version: str
@@ -1658,7 +1820,7 @@ class SystemDatabase:
1658
1820
  status: WorkflowStatusInternal,
1659
1821
  inputs: str,
1660
1822
  *,
1661
- max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
1823
+ max_recovery_attempts: Optional[int],
1662
1824
  ) -> WorkflowStatuses:
1663
1825
  """
1664
1826
  Synchronously record the status and inputs for workflows in a single transaction
@@ -1,64 +1,18 @@
1
- import json
2
1
  import uuid
3
- from typing import Any, List, Optional
2
+ from typing import List, Optional
4
3
 
5
4
  from dbos._error import DBOSException
6
5
 
7
- from . import _serialization
8
6
  from ._app_db import ApplicationDatabase
9
7
  from ._sys_db import (
10
8
  GetQueuedWorkflowsInput,
11
9
  GetWorkflowsInput,
12
- GetWorkflowsOutput,
13
10
  StepInfo,
14
11
  SystemDatabase,
12
+ WorkflowStatus,
15
13
  )
16
14
 
17
15
 
18
- class WorkflowStatus:
19
- # The workflow ID
20
- workflow_id: str
21
- # The workflow status. Must be one of ENQUEUED, PENDING, SUCCESS, ERROR, CANCELLED, or RETRIES_EXCEEDED
22
- status: str
23
- # The name of the workflow function
24
- name: str
25
- # The name of the workflow's class, if any
26
- class_name: Optional[str]
27
- # The name with which the workflow's class instance was configured, if any
28
- config_name: Optional[str]
29
- # The user who ran the workflow, if specified
30
- authenticated_user: Optional[str]
31
- # The role with which the workflow ran, if specified
32
- assumed_role: Optional[str]
33
- # All roles which the authenticated user could assume
34
- authenticated_roles: Optional[list[str]]
35
- # The deserialized workflow input object
36
- input: Optional[_serialization.WorkflowInputs]
37
- # The workflow's output, if any
38
- output: Optional[Any] = None
39
- # The error the workflow threw, if any
40
- error: Optional[Exception] = None
41
- # Workflow start time, as a Unix epoch timestamp in ms
42
- created_at: Optional[int]
43
- # Last time the workflow status was updated, as a Unix epoch timestamp in ms
44
- updated_at: Optional[int]
45
- # If this workflow was enqueued, on which queue
46
- queue_name: Optional[str]
47
- # The executor to most recently executed this workflow
48
- executor_id: Optional[str]
49
- # The application version on which this workflow was started
50
- app_version: Optional[str]
51
-
52
- # INTERNAL FIELDS
53
-
54
- # The ID of the application executing this workflow
55
- app_id: Optional[str]
56
- # The number of times this workflow's execution has been attempted
57
- recovery_attempts: Optional[int]
58
- # The HTTP request that triggered the workflow, if known
59
- request: Optional[str]
60
-
61
-
62
16
  def list_workflows(
63
17
  sys_db: SystemDatabase,
64
18
  *,
@@ -88,12 +42,8 @@ def list_workflows(
88
42
  input.sort_desc = sort_desc
89
43
  input.workflow_id_prefix = workflow_id_prefix
90
44
 
91
- output: GetWorkflowsOutput = sys_db.get_workflows(input)
92
- infos: List[WorkflowStatus] = []
93
- for workflow_id in output.workflow_uuids:
94
- info = get_workflow(sys_db, workflow_id, request) # Call the method for each ID
95
- if info is not None:
96
- infos.append(info)
45
+ infos: List[WorkflowStatus] = sys_db.get_workflows(input, request)
46
+
97
47
  return infos
98
48
 
99
49
 
@@ -120,63 +70,22 @@ def list_queued_workflows(
120
70
  "offset": offset,
121
71
  "sort_desc": sort_desc,
122
72
  }
123
- output: GetWorkflowsOutput = sys_db.get_queued_workflows(input)
124
- infos: List[WorkflowStatus] = []
125
- for workflow_id in output.workflow_uuids:
126
- info = get_workflow(sys_db, workflow_id, request) # Call the method for each ID
127
- if info is not None:
128
- infos.append(info)
73
+
74
+ infos: List[WorkflowStatus] = sys_db.get_queued_workflows(input, request)
129
75
  return infos
130
76
 
131
77
 
132
78
  def get_workflow(
133
79
  sys_db: SystemDatabase, workflow_id: str, get_request: bool
134
80
  ) -> Optional[WorkflowStatus]:
81
+ input = GetWorkflowsInput()
82
+ input.workflow_ids = [workflow_id]
135
83
 
136
- internal_status = sys_db.get_workflow_status(workflow_id)
137
- if internal_status is None:
84
+ infos: List[WorkflowStatus] = sys_db.get_workflows(input, get_request)
85
+ if not infos:
138
86
  return None
139
87
 
140
- info = WorkflowStatus()
141
-
142
- info.workflow_id = workflow_id
143
- info.status = internal_status["status"]
144
- info.name = internal_status["name"]
145
- info.class_name = internal_status["class_name"]
146
- info.config_name = internal_status["config_name"]
147
- info.authenticated_user = internal_status["authenticated_user"]
148
- info.assumed_role = internal_status["assumed_role"]
149
- info.authenticated_roles = (
150
- json.loads(internal_status["authenticated_roles"])
151
- if internal_status["authenticated_roles"] is not None
152
- else None
153
- )
154
- info.request = internal_status["request"]
155
- info.created_at = internal_status["created_at"]
156
- info.updated_at = internal_status["updated_at"]
157
- info.queue_name = internal_status["queue_name"]
158
- info.executor_id = internal_status["executor_id"]
159
- info.app_version = internal_status["app_version"]
160
- info.app_id = internal_status["app_id"]
161
- info.recovery_attempts = internal_status["recovery_attempts"]
162
-
163
- input_data = sys_db.get_workflow_inputs(workflow_id)
164
- if input_data is not None:
165
- info.input = input_data
166
-
167
- if internal_status.get("status") == "SUCCESS":
168
- result = sys_db.await_workflow_result(workflow_id)
169
- info.output = result
170
- elif internal_status.get("status") == "ERROR":
171
- try:
172
- sys_db.await_workflow_result(workflow_id)
173
- except Exception as e:
174
- info.error = e
175
-
176
- if not get_request:
177
- info.request = None
178
-
179
- return info
88
+ return infos[0]
180
89
 
181
90
 
182
91
  def list_workflow_steps(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.26.0a19
3
+ Version: 0.26.0a21
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,19 +1,19 @@
1
- dbos-0.26.0a19.dist-info/METADATA,sha256=YWeav0kTgNnVV03IK_iG7vuKR04nKeOnIM4M1cmCELw,5554
2
- dbos-0.26.0a19.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- dbos-0.26.0a19.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.26.0a19.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
- dbos/__init__.py,sha256=3NQfGlBiiUSM_v88STdVP3rNZvGkUL_9WbSotKb8Voo,873
1
+ dbos-0.26.0a21.dist-info/METADATA,sha256=6JPLTUn5uCaKpHI_sEis2zJWOrNqsRUIQ2_4h7ZzWfw,5554
2
+ dbos-0.26.0a21.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
+ dbos-0.26.0a21.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.26.0a21.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
+ dbos/__init__.py,sha256=VoGS7H9GVtNAnD2S4zseIEioS1dNIJXRovQ4oHlg8og,842
6
6
  dbos/__main__.py,sha256=G7Exn-MhGrVJVDbgNlpzhfh8WMX_72t3_oJaFT9Lmt8,653
7
7
  dbos/_admin_server.py,sha256=RrbABfR1D3p9c_QLrCSrgFuYce6FKi0fjMRIYLjO_Y8,9038
8
8
  dbos/_app_db.py,sha256=obNlgC9IZ20y8tqQeA1q4TjceG3jBFalxz70ieDOWCA,11332
9
9
  dbos/_classproperty.py,sha256=f0X-_BySzn3yFDRKB2JpCbLYQ9tLwt1XftfshvY7CBs,626
10
- dbos/_client.py,sha256=PtOZv_4TCd7I0y9kw_0a93Lf_cUkytdDjCdrrHnyTS4,12020
10
+ dbos/_client.py,sha256=S3tejQ7xAJ9wjo1PhQ0P3UYuloDOdZqXsQwE4YjAQ8s,12124
11
11
  dbos/_conductor/conductor.py,sha256=HYzVL29IMMrs2Mnms_7cHJynCnmmEN5SDQOMjzn3UoU,16840
12
- dbos/_conductor/protocol.py,sha256=xN7pmooyF1pqbH1b6WhllU5718P7zSb_b0KCwA6bzcs,6716
12
+ dbos/_conductor/protocol.py,sha256=zEKIuOQdIaSduNqfZKpo8PSD9_1oNpKIPnBNCu3RUyE,6681
13
13
  dbos/_context.py,sha256=I8sLkdKTTkZEz7wG-MjynaQB6XEF2bLXuwNksiauP7w,19430
14
- dbos/_core.py,sha256=uxDIJui4WS_2V1k2np0Ifue_IRzLTyq-c52bgZSQYn4,45118
14
+ dbos/_core.py,sha256=SecObOKLjNinNAXDcYVMVUURHcoaPe0js-axLMMNwqY,45098
15
15
  dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
16
- dbos/_dbos.py,sha256=Waz_5d9PkDjxD9LUe-nDf4gn5ds2kO0ZyJFQd8Tkz9w,47155
16
+ dbos/_dbos.py,sha256=bbio_FjBfU__Zk1BFegfS16IrPPejFxOKm5rUg5nW1o,47185
17
17
  dbos/_dbos_config.py,sha256=m05IFjM0jSwZBsnFMF_4qP2JkjVFc0gqyM2tnotXq20,20636
18
18
  dbos/_debug.py,sha256=MNlQVZ6TscGCRQeEEL0VE8Uignvr6dPeDDDefS3xgIE,1823
19
19
  dbos/_docker_pg_helper.py,sha256=NmcgqmR5rQA_4igfeqh8ugNT2z3YmoOvuep_MEtxTiY,5854
@@ -37,7 +37,7 @@ dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py,sha256
37
37
  dbos/_outcome.py,sha256=EXxBg4jXCVJsByDQ1VOCIedmbeq_03S6d-p1vqQrLFU,6810
38
38
  dbos/_queue.py,sha256=l0g_CXJbxEmftCA9yhy-cyaR_sddfQSCfm-5XgIWzqU,3397
39
39
  dbos/_recovery.py,sha256=98Py7icfytyIELJ54gIsdvmURBvTb0HmWaxEAuYL0dc,2546
40
- dbos/_registrations.py,sha256=ZDC5lghy_1ZMdMGsSBrXSyS96DH3baA4nyCkFdUmIlc,7292
40
+ dbos/_registrations.py,sha256=EZzG3ZfYmWA2bHX2hpnSIQ3PTi3-cXsvbcmXjyOusMk,7302
41
41
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
42
42
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
43
43
  dbos/_scheduler.py,sha256=SR1oRZRcVzYsj-JauV2LA8JtwTkt8mru7qf6H1AzQ1U,2027
@@ -45,7 +45,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  dbos/_schemas/application_database.py,sha256=SypAS9l9EsaBHFn9FR8jmnqt01M74d9AF1AMa4m2hhI,1040
46
46
  dbos/_schemas/system_database.py,sha256=W9eSpL7SZzQkxcEZ4W07BOcwkkDr35b9oCjUOgfHWek,5336
47
47
  dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
48
- dbos/_sys_db.py,sha256=kfNR9R7rQ6MTqBuPt4OI5nZElIJNXlGuUjG_ypGKHWI,71195
48
+ dbos/_sys_db.py,sha256=M3BVJVhG0YXkLhw5axSrKjBN1AOS3KmvgWEYn2l94pw,78203
49
49
  dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
50
50
  dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  dbos/_templates/dbos-db-starter/__package/main.py,sha256=nJMN3ZD2lmwg4Dcgmiwqc-tQGuCJuJal2Xl85iA277U,2453
@@ -58,11 +58,11 @@ dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py,sh
58
58
  dbos/_templates/dbos-db-starter/start_postgres_docker.py,sha256=lQVLlYO5YkhGPEgPqwGc7Y8uDKse9HsWv5fynJEFJHM,1681
59
59
  dbos/_tracer.py,sha256=dFDSFlta-rfA3-ahIRLYwnnoAOmlavdxAGllqwFgnCA,2440
60
60
  dbos/_utils.py,sha256=nFRUHzVjXG5AusF85AlYHikj63Tzi-kQm992ihsrAxA,201
61
- dbos/_workflow_commands.py,sha256=BzvWGOQ-4fbHlAoFI5Hdwk1PimUUgBn1kISLnSMt0To,7189
61
+ dbos/_workflow_commands.py,sha256=7wyxTfIyh2IVIqlkaTr8CMBq8yxWP3Hhddyv1YJY8zE,3576
62
62
  dbos/cli/_github_init.py,sha256=Y_bDF9gfO2jB1id4FV5h1oIxEJRWyqVjhb7bNEa5nQ0,3224
63
63
  dbos/cli/_template_init.py,sha256=-WW3kbq0W_Tq4WbMqb1UGJG3xvJb3woEY5VspG95Srk,2857
64
64
  dbos/cli/cli.py,sha256=1qCTs__A9LOEfU44XZ6TufwmRwe68ZEwbWEPli3vnVM,17873
65
65
  dbos/dbos-config.schema.json,sha256=i7jcxXqByKq0Jzv3nAUavONtj03vTwj6vWP4ylmBr8o,5694
66
66
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
67
67
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
68
- dbos-0.26.0a19.dist-info/RECORD,,
68
+ dbos-0.26.0a21.dist-info/RECORD,,