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 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.__name__
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(self, cls: Type[T]) -> Type[T]:
192
- ci = get_or_create_class_info(cls)
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__.__name__
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(cls) -> Callable[[Type[T]], Type[T]]:
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
- return _get_or_create_dbos_registry().create_class_info
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 current context. This is a unique identifier of the current step within the workflow."""
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.is_within_workflow()
1068
- ), "step_id is only available within a DBOS workflow."
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 postgres://postgres:{pool_config['password']}@{pool_config['host']}:{pool_config['port']}"
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
- class DBOSWorkflowConflictIDError(DBOSException):
45
- """Exception raised when a workflow database record already exists."""
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(cls: Type[Any]) -> DBOSClassInfo:
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
- ci = DBOSClassInfo()
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 str(first_arg.__class__.__name__)
190
+ return _class_fqn(first_arg.__class__)
176
191
  if fi.func_type == DBOSFuncType.Class:
177
192
  first_arg = args[0]
178
- return str(first_arg.__name__)
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.is_within_workflow():
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.is_within_workflow():
1592
+ if ctx and ctx.is_workflow():
1591
1593
  self.record_operation_result(
1592
1594
  {
1593
1595
  "workflow_uuid": ctx.workflow_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.26.0a10
3
+ Version: 0.26.0a13
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
- dbos-0.26.0a10.dist-info/METADATA,sha256=jPlzlBHfDRKYXanW4twnWxivVJqJ_Z05yrFCduovDVw,5554
2
- dbos-0.26.0a10.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
3
- dbos-0.26.0a10.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.26.0a10.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
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=TOLi95Aca50huyOAWl9H5fii4nMYaGwN-zQ8GlLWdOg,45569
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=9OGbuavRA_cwE-uPiLZJSdpbQu-6PPgl9clQZB2zT_U,5852
20
- dbos/_error.py,sha256=HtdV6Qy7qRyGD57wxLwE7YT0WdYtlx5ZLEe_Kv_gC-U,5953
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=_zy6k944Ll8QwqU12Kr3OP23ukVtm8axPNN1TS_kJRc,6717
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=BqXZ0l4X4Y4cFKDyaa8ZirWCnRlof9A12yp-XflGnb0,68229
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.0a10.dist-info/RECORD,,
67
+ dbos-0.26.0a13.dist-info/RECORD,,