dbos 0.26.0a10__py3-none-any.whl → 0.26.0a13__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/_dbos.py +20 -10
- dbos/_docker_pg_helper.py +1 -1
- dbos/_error.py +51 -18
- dbos/_registrations.py +19 -4
- dbos/_sys_db.py +4 -2
- {dbos-0.26.0a10.dist-info → dbos-0.26.0a13.dist-info}/METADATA +1 -1
- {dbos-0.26.0a10.dist-info → dbos-0.26.0a13.dist-info}/RECORD +10 -10
- {dbos-0.26.0a10.dist-info → dbos-0.26.0a13.dist-info}/WHEEL +0 -0
- {dbos-0.26.0a10.dist-info → dbos-0.26.0a13.dist-info}/entry_points.txt +0 -0
- {dbos-0.26.0a10.dist-info → dbos-0.26.0a13.dist-info}/licenses/LICENSE +0 -0
dbos/_dbos.py
CHANGED
@@ -62,6 +62,7 @@ from ._recovery import recover_pending_workflows, startup_recovery_thread
|
|
62
62
|
from ._registrations import (
|
63
63
|
DEFAULT_MAX_RECOVERY_ATTEMPTS,
|
64
64
|
DBOSClassInfo,
|
65
|
+
_class_fqn,
|
65
66
|
get_or_create_class_info,
|
66
67
|
set_dbos_func_name,
|
67
68
|
set_temp_workflow_type,
|
@@ -181,15 +182,17 @@ class DBOSRegistry:
|
|
181
182
|
self.workflow_info_map[name] = wrapped_func
|
182
183
|
|
183
184
|
def register_class(self, cls: type, ci: DBOSClassInfo) -> None:
|
184
|
-
class_name = cls
|
185
|
+
class_name = _class_fqn(cls)
|
185
186
|
if class_name in self.class_info_map:
|
186
187
|
if self.class_info_map[class_name] is not cls:
|
187
188
|
raise Exception(f"Duplicate type registration for class '{class_name}'")
|
188
189
|
else:
|
189
190
|
self.class_info_map[class_name] = cls
|
190
191
|
|
191
|
-
def create_class_info(
|
192
|
-
|
192
|
+
def create_class_info(
|
193
|
+
self, cls: Type[T], class_name: Optional[str] = None
|
194
|
+
) -> Type[T]:
|
195
|
+
ci = get_or_create_class_info(cls, class_name)
|
193
196
|
self.register_class(cls, ci)
|
194
197
|
return cls
|
195
198
|
|
@@ -204,7 +207,7 @@ class DBOSRegistry:
|
|
204
207
|
|
205
208
|
def register_instance(self, inst: object) -> None:
|
206
209
|
config_name = getattr(inst, "config_name")
|
207
|
-
class_name = inst.__class__
|
210
|
+
class_name = _class_fqn(inst.__class__)
|
208
211
|
fn = f"{class_name}/{config_name}"
|
209
212
|
if fn in self.instance_info_map:
|
210
213
|
if self.instance_info_map[fn] is not inst:
|
@@ -641,15 +644,22 @@ class DBOS:
|
|
641
644
|
)
|
642
645
|
|
643
646
|
@classmethod
|
644
|
-
def dbos_class(
|
647
|
+
def dbos_class(
|
648
|
+
cls, class_name: Optional[str] = None
|
649
|
+
) -> Callable[[Type[T]], Type[T]]:
|
645
650
|
"""
|
646
651
|
Decorate a class that contains DBOS member functions.
|
647
652
|
|
648
653
|
All DBOS classes must be decorated, as this associates the class with
|
649
|
-
its member functions.
|
654
|
+
its member functions. Class names must be globally unique. By default, the class name is class.__qualname__ but you can optionally provide a class name that is different from the default name.
|
650
655
|
"""
|
651
656
|
|
652
|
-
|
657
|
+
def register_class(cls: Type[T]) -> Type[T]:
|
658
|
+
# Register the class with the DBOS registry
|
659
|
+
_get_or_create_dbos_registry().create_class_info(cls, class_name)
|
660
|
+
return cls
|
661
|
+
|
662
|
+
return register_class
|
653
663
|
|
654
664
|
@classmethod
|
655
665
|
def default_required_roles(cls, roles: List[str]) -> Callable[[Type[T]], Type[T]]:
|
@@ -1061,11 +1071,11 @@ class DBOS:
|
|
1061
1071
|
|
1062
1072
|
@classproperty
|
1063
1073
|
def step_id(cls) -> int:
|
1064
|
-
"""Return the step ID for the
|
1074
|
+
"""Return the step ID for the currently executing step. This is a unique identifier of the current step within the workflow."""
|
1065
1075
|
ctx = assert_current_dbos_context()
|
1066
1076
|
assert (
|
1067
|
-
ctx.
|
1068
|
-
), "step_id is only available within a DBOS
|
1077
|
+
ctx.is_step() or ctx.is_transaction()
|
1078
|
+
), "step_id is only available within a DBOS step."
|
1069
1079
|
return ctx.function_id
|
1070
1080
|
|
1071
1081
|
@classproperty
|
dbos/_docker_pg_helper.py
CHANGED
@@ -46,7 +46,7 @@ def start_docker_pg() -> None:
|
|
46
46
|
if has_docker:
|
47
47
|
start_docker_postgres(pool_config)
|
48
48
|
logging.info(
|
49
|
-
f"Postgres available at
|
49
|
+
f"Postgres available at postgresql://postgres:{pool_config['password']}@{pool_config['host']}:{pool_config['port']}"
|
50
50
|
)
|
51
51
|
else:
|
52
52
|
logging.warning("Docker not detected locally")
|
dbos/_error.py
CHANGED
@@ -26,6 +26,29 @@ class DBOSException(Exception):
|
|
26
26
|
return f"DBOS Error: {self.message}"
|
27
27
|
|
28
28
|
|
29
|
+
class DBOSBaseException(BaseException):
|
30
|
+
"""
|
31
|
+
This class is for DBOS exceptions that should not be caught by user code.
|
32
|
+
It inherits from BaseException instead of Exception so it cannot be caught
|
33
|
+
except by code specifically trying to catch it.
|
34
|
+
|
35
|
+
Attributes:
|
36
|
+
message(str): The error message string
|
37
|
+
dbos_error_code(DBOSErrorCode): The error code, from the `DBOSErrorCode` enum
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(self, message: str, dbos_error_code: Optional[int] = None):
|
41
|
+
self.message = message
|
42
|
+
self.dbos_error_code = dbos_error_code
|
43
|
+
self.status_code: Optional[int] = None
|
44
|
+
super().__init__(self.message)
|
45
|
+
|
46
|
+
def __str__(self) -> str:
|
47
|
+
if self.dbos_error_code:
|
48
|
+
return f"DBOS Error {self.dbos_error_code}: {self.message}"
|
49
|
+
return f"DBOS Error: {self.message}"
|
50
|
+
|
51
|
+
|
29
52
|
class DBOSErrorCode(Enum):
|
30
53
|
ConflictingIDError = 1
|
31
54
|
RecoveryError = 2
|
@@ -41,14 +64,9 @@ class DBOSErrorCode(Enum):
|
|
41
64
|
ConflictingRegistrationError = 25
|
42
65
|
|
43
66
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def __init__(self, workflow_id: str):
|
48
|
-
super().__init__(
|
49
|
-
f"Conflicting workflow ID {workflow_id}",
|
50
|
-
dbos_error_code=DBOSErrorCode.ConflictingIDError.value,
|
51
|
-
)
|
67
|
+
#######################################
|
68
|
+
## Exception
|
69
|
+
#######################################
|
52
70
|
|
53
71
|
|
54
72
|
class DBOSConflictingWorkflowError(DBOSException):
|
@@ -138,16 +156,6 @@ class DBOSMaxStepRetriesExceeded(DBOSException):
|
|
138
156
|
return (self.__class__, (self.step_name, self.max_retries))
|
139
157
|
|
140
158
|
|
141
|
-
class DBOSWorkflowCancelledError(DBOSException):
|
142
|
-
"""Exception raised when the workflow has already been cancelled."""
|
143
|
-
|
144
|
-
def __init__(self, msg: str) -> None:
|
145
|
-
super().__init__(
|
146
|
-
msg,
|
147
|
-
dbos_error_code=DBOSErrorCode.WorkflowCancelled.value,
|
148
|
-
)
|
149
|
-
|
150
|
-
|
151
159
|
class DBOSConflictingRegistrationError(DBOSException):
|
152
160
|
"""Exception raised when conflicting decorators are applied to the same function."""
|
153
161
|
|
@@ -168,3 +176,28 @@ class DBOSUnexpectedStepError(DBOSException):
|
|
168
176
|
f"During execution of workflow {workflow_id} step {step_id}, function {recorded_name} was recorded when {expected_name} was expected. Check that your workflow is deterministic.",
|
169
177
|
dbos_error_code=DBOSErrorCode.UnexpectedStep.value,
|
170
178
|
)
|
179
|
+
|
180
|
+
|
181
|
+
#######################################
|
182
|
+
## BaseException
|
183
|
+
#######################################
|
184
|
+
|
185
|
+
|
186
|
+
class DBOSWorkflowCancelledError(DBOSBaseException):
|
187
|
+
"""BaseException raised when the workflow has already been cancelled."""
|
188
|
+
|
189
|
+
def __init__(self, msg: str) -> None:
|
190
|
+
super().__init__(
|
191
|
+
msg,
|
192
|
+
dbos_error_code=DBOSErrorCode.WorkflowCancelled.value,
|
193
|
+
)
|
194
|
+
|
195
|
+
|
196
|
+
class DBOSWorkflowConflictIDError(DBOSBaseException):
|
197
|
+
"""BaseException raised when a workflow database record already exists."""
|
198
|
+
|
199
|
+
def __init__(self, workflow_id: str):
|
200
|
+
super().__init__(
|
201
|
+
f"Conflicting workflow ID {workflow_id}",
|
202
|
+
dbos_error_code=DBOSErrorCode.ConflictingIDError.value,
|
203
|
+
)
|
dbos/_registrations.py
CHANGED
@@ -34,6 +34,7 @@ def set_temp_workflow_type(f: Any, name: TempWorkflowType) -> None:
|
|
34
34
|
|
35
35
|
@dataclass
|
36
36
|
class DBOSClassInfo:
|
37
|
+
registered_name: str
|
37
38
|
def_required_roles: Optional[List[str]] = None
|
38
39
|
|
39
40
|
|
@@ -53,11 +54,17 @@ class DBOSFuncInfo:
|
|
53
54
|
max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS
|
54
55
|
|
55
56
|
|
56
|
-
def get_or_create_class_info(
|
57
|
+
def get_or_create_class_info(
|
58
|
+
cls: Type[Any], provided_name: Optional[str] = None
|
59
|
+
) -> DBOSClassInfo:
|
57
60
|
if hasattr(cls, "dbos_class_decorator_info"):
|
58
61
|
ci: DBOSClassInfo = getattr(cls, "dbos_class_decorator_info")
|
59
62
|
return ci
|
60
|
-
|
63
|
+
class_name = _class_fqn(cls)
|
64
|
+
# Use the provided name instead of the class name if it is not None
|
65
|
+
if provided_name is not None:
|
66
|
+
class_name = provided_name
|
67
|
+
ci = DBOSClassInfo(registered_name=class_name)
|
61
68
|
setattr(cls, "dbos_class_decorator_info", ci)
|
62
69
|
|
63
70
|
# Tell all DBOS functions about this
|
@@ -166,16 +173,24 @@ def get_config_name(
|
|
166
173
|
return None
|
167
174
|
|
168
175
|
|
176
|
+
def _class_fqn(cls: type) -> str:
|
177
|
+
"""Returns the registered name of the given class. If the class name was not overridden at registration time, it returns the qualified name of the class."""
|
178
|
+
ci = get_class_info(cls)
|
179
|
+
if ci is not None:
|
180
|
+
return ci.registered_name
|
181
|
+
return cls.__qualname__
|
182
|
+
|
183
|
+
|
169
184
|
def get_dbos_class_name(
|
170
185
|
fi: Optional[DBOSFuncInfo], func: Callable[..., Any], args: Tuple[Any, ...]
|
171
186
|
) -> Optional[str]:
|
172
187
|
if fi and fi.func_type != DBOSFuncType.Unknown and len(args) > 0:
|
173
188
|
if fi.func_type == DBOSFuncType.Instance:
|
174
189
|
first_arg = args[0]
|
175
|
-
return
|
190
|
+
return _class_fqn(first_arg.__class__)
|
176
191
|
if fi.func_type == DBOSFuncType.Class:
|
177
192
|
first_arg = args[0]
|
178
|
-
return
|
193
|
+
return _class_fqn(first_arg)
|
179
194
|
return None
|
180
195
|
|
181
196
|
# Check for improperly-registered functions
|
dbos/_sys_db.py
CHANGED
@@ -1569,7 +1569,9 @@ class SystemDatabase:
|
|
1569
1569
|
|
1570
1570
|
def call_function_as_step(self, fn: Callable[[], T], function_name: str) -> T:
|
1571
1571
|
ctx = get_local_dbos_context()
|
1572
|
-
if ctx and ctx.
|
1572
|
+
if ctx and ctx.is_transaction():
|
1573
|
+
raise Exception(f"Invalid call to `{function_name}` inside a transaction")
|
1574
|
+
if ctx and ctx.is_workflow():
|
1573
1575
|
ctx.function_id += 1
|
1574
1576
|
res = self.check_operation_execution(
|
1575
1577
|
ctx.workflow_id, ctx.function_id, function_name
|
@@ -1587,7 +1589,7 @@ class SystemDatabase:
|
|
1587
1589
|
f"Recorded output and error are both None for {function_name}"
|
1588
1590
|
)
|
1589
1591
|
result = fn()
|
1590
|
-
if ctx and ctx.
|
1592
|
+
if ctx and ctx.is_workflow():
|
1591
1593
|
self.record_operation_result(
|
1592
1594
|
{
|
1593
1595
|
"workflow_uuid": ctx.workflow_id,
|
@@ -1,7 +1,7 @@
|
|
1
|
-
dbos-0.26.
|
2
|
-
dbos-0.26.
|
3
|
-
dbos-0.26.
|
4
|
-
dbos-0.26.
|
1
|
+
dbos-0.26.0a13.dist-info/METADATA,sha256=lq7ULNY71KRf0nnpFc3weG23tltMD5ULJ03jX-KxMXU,5554
|
2
|
+
dbos-0.26.0a13.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
|
3
|
+
dbos-0.26.0a13.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
|
4
|
+
dbos-0.26.0a13.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
|
5
5
|
dbos/__init__.py,sha256=3NQfGlBiiUSM_v88STdVP3rNZvGkUL_9WbSotKb8Voo,873
|
6
6
|
dbos/__main__.py,sha256=G7Exn-MhGrVJVDbgNlpzhfh8WMX_72t3_oJaFT9Lmt8,653
|
7
7
|
dbos/_admin_server.py,sha256=vxPG_YJ6lYrkfPCSp42FiATVLBOij7Fm52Yngg5Z_tE,7027
|
@@ -13,11 +13,11 @@ dbos/_conductor/protocol.py,sha256=xN7pmooyF1pqbH1b6WhllU5718P7zSb_b0KCwA6bzcs,6
|
|
13
13
|
dbos/_context.py,sha256=I8sLkdKTTkZEz7wG-MjynaQB6XEF2bLXuwNksiauP7w,19430
|
14
14
|
dbos/_core.py,sha256=tjBGVbSgOn59lR29gcYi5f6fcKNKQM5EP1QXrQGUkXA,45426
|
15
15
|
dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
|
16
|
-
dbos/_dbos.py,sha256=
|
16
|
+
dbos/_dbos.py,sha256=QMsU6ccOdTqhojtimiQzoa2eSUfHetsm7gbMlffIt90,46066
|
17
17
|
dbos/_dbos_config.py,sha256=m05IFjM0jSwZBsnFMF_4qP2JkjVFc0gqyM2tnotXq20,20636
|
18
18
|
dbos/_debug.py,sha256=MNlQVZ6TscGCRQeEEL0VE8Uignvr6dPeDDDefS3xgIE,1823
|
19
|
-
dbos/_docker_pg_helper.py,sha256=
|
20
|
-
dbos/_error.py,sha256=
|
19
|
+
dbos/_docker_pg_helper.py,sha256=NmcgqmR5rQA_4igfeqh8ugNT2z3YmoOvuep_MEtxTiY,5854
|
20
|
+
dbos/_error.py,sha256=9ITvFsN_Udpx0xXtYQHXXXb6PjPr3TmMondGmprV-L0,7003
|
21
21
|
dbos/_fastapi.py,sha256=PhaKftbApHnjtYEOw0EYna_3K0cmz__J9of7mRJWzu4,3704
|
22
22
|
dbos/_flask.py,sha256=DZKUZR5-xOzPI7tYZ53r2PvvHVoAb8SYwLzMVFsVfjI,2608
|
23
23
|
dbos/_kafka.py,sha256=pz0xZ9F3X9Ky1k-VSbeF3tfPhP3UPr3lUUhUfE41__U,4198
|
@@ -36,7 +36,7 @@ dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py,sha256
|
|
36
36
|
dbos/_outcome.py,sha256=EXxBg4jXCVJsByDQ1VOCIedmbeq_03S6d-p1vqQrLFU,6810
|
37
37
|
dbos/_queue.py,sha256=l0g_CXJbxEmftCA9yhy-cyaR_sddfQSCfm-5XgIWzqU,3397
|
38
38
|
dbos/_recovery.py,sha256=98Py7icfytyIELJ54gIsdvmURBvTb0HmWaxEAuYL0dc,2546
|
39
|
-
dbos/_registrations.py,sha256=
|
39
|
+
dbos/_registrations.py,sha256=ZDC5lghy_1ZMdMGsSBrXSyS96DH3baA4nyCkFdUmIlc,7292
|
40
40
|
dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
|
41
41
|
dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
|
42
42
|
dbos/_scheduler.py,sha256=SR1oRZRcVzYsj-JauV2LA8JtwTkt8mru7qf6H1AzQ1U,2027
|
@@ -44,7 +44,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
44
|
dbos/_schemas/application_database.py,sha256=SypAS9l9EsaBHFn9FR8jmnqt01M74d9AF1AMa4m2hhI,1040
|
45
45
|
dbos/_schemas/system_database.py,sha256=W9eSpL7SZzQkxcEZ4W07BOcwkkDr35b9oCjUOgfHWek,5336
|
46
46
|
dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
|
47
|
-
dbos/_sys_db.py,sha256=
|
47
|
+
dbos/_sys_db.py,sha256=vQzr6qACjtmjVS151OJ45BlhOeB1tBBZemyEzOsy5nc,68343
|
48
48
|
dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
|
49
49
|
dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
dbos/_templates/dbos-db-starter/__package/main.py,sha256=nJMN3ZD2lmwg4Dcgmiwqc-tQGuCJuJal2Xl85iA277U,2453
|
@@ -64,4 +64,4 @@ dbos/cli/cli.py,sha256=Lb_RYmXoT5KH0xDbwaYpROE4c-svZ0eCq2Kxg7cAxTw,16537
|
|
64
64
|
dbos/dbos-config.schema.json,sha256=i7jcxXqByKq0Jzv3nAUavONtj03vTwj6vWP4ylmBr8o,5694
|
65
65
|
dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
|
66
66
|
version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
|
67
|
-
dbos-0.26.
|
67
|
+
dbos-0.26.0a13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|