dbos 0.26.0a11__tar.gz → 0.26.0a13__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.
Files changed (102) hide show
  1. {dbos-0.26.0a11 → dbos-0.26.0a13}/PKG-INFO +1 -1
  2. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_dbos.py +17 -7
  3. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_docker_pg_helper.py +1 -1
  4. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_error.py +51 -18
  5. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_registrations.py +19 -4
  6. {dbos-0.26.0a11 → dbos-0.26.0a13}/pyproject.toml +1 -1
  7. dbos-0.26.0a13/tests/dupname_classdefs1.py +9 -0
  8. dbos-0.26.0a13/tests/dupname_classdefsa.py +9 -0
  9. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_classdecorators.py +37 -16
  10. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_singleton.py +1 -1
  11. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_workflow_management.py +5 -1
  12. {dbos-0.26.0a11 → dbos-0.26.0a13}/LICENSE +0 -0
  13. {dbos-0.26.0a11 → dbos-0.26.0a13}/README.md +0 -0
  14. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/__init__.py +0 -0
  15. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/__main__.py +0 -0
  16. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_admin_server.py +0 -0
  17. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_app_db.py +0 -0
  18. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_classproperty.py +0 -0
  19. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_client.py +0 -0
  20. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_conductor/conductor.py +0 -0
  21. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_conductor/protocol.py +0 -0
  22. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_context.py +0 -0
  23. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_core.py +0 -0
  24. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_croniter.py +0 -0
  25. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_dbos_config.py +0 -0
  26. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_debug.py +0 -0
  27. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_fastapi.py +0 -0
  28. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_flask.py +0 -0
  29. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_kafka.py +0 -0
  30. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_kafka_message.py +0 -0
  31. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_logger.py +0 -0
  32. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/env.py +0 -0
  33. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/script.py.mako +0 -0
  34. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  35. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  36. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  37. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  38. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  39. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  40. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  41. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  42. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_outcome.py +0 -0
  43. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_queue.py +0 -0
  44. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_recovery.py +0 -0
  45. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_request.py +0 -0
  46. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_roles.py +0 -0
  47. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_scheduler.py +0 -0
  48. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_schemas/__init__.py +0 -0
  49. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_schemas/application_database.py +0 -0
  50. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_schemas/system_database.py +0 -0
  51. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_serialization.py +0 -0
  52. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_sys_db.py +0 -0
  53. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/README.md +0 -0
  54. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  55. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
  56. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  57. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  58. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  59. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  60. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  61. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  62. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  63. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_tracer.py +0 -0
  64. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_utils.py +0 -0
  65. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/_workflow_commands.py +0 -0
  66. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/cli/_github_init.py +0 -0
  67. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/cli/_template_init.py +0 -0
  68. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/cli/cli.py +0 -0
  69. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/dbos-config.schema.json +0 -0
  70. {dbos-0.26.0a11 → dbos-0.26.0a13}/dbos/py.typed +0 -0
  71. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/__init__.py +0 -0
  72. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/atexit_no_ctor.py +0 -0
  73. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/atexit_no_launch.py +0 -0
  74. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/classdefs.py +0 -0
  75. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/client_collateral.py +0 -0
  76. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/client_worker.py +0 -0
  77. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/conftest.py +0 -0
  78. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/more_classdefs.py +0 -0
  79. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/queuedworkflow.py +0 -0
  80. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_admin_server.py +0 -0
  81. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_async.py +0 -0
  82. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_client.py +0 -0
  83. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_concurrency.py +0 -0
  84. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_config.py +0 -0
  85. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_croniter.py +0 -0
  86. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_dbos.py +0 -0
  87. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_debug.py +0 -0
  88. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_docker_secrets.py +0 -0
  89. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_failures.py +0 -0
  90. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_fastapi.py +0 -0
  91. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_fastapi_roles.py +0 -0
  92. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_flask.py +0 -0
  93. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_kafka.py +0 -0
  94. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_outcome.py +0 -0
  95. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_package.py +0 -0
  96. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_queue.py +0 -0
  97. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_scheduler.py +0 -0
  98. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_schema_migration.py +0 -0
  99. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_spans.py +0 -0
  100. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_sqlalchemy.py +0 -0
  101. {dbos-0.26.0a11 → dbos-0.26.0a13}/tests/test_workflow_introspection.py +0 -0
  102. {dbos-0.26.0a11 → dbos-0.26.0a13}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.26.0a11
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
@@ -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]]:
@@ -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")
@@ -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
+ )
@@ -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
@@ -28,7 +28,7 @@ dependencies = [
28
28
  ]
29
29
  requires-python = ">=3.9"
30
30
  readme = "README.md"
31
- version = "0.26.0a11"
31
+ version = "0.26.0a13"
32
32
 
33
33
  [project.license]
34
34
  text = "MIT"
@@ -0,0 +1,9 @@
1
+ from dbos import DBOS, DBOSConfiguredInstance
2
+
3
+
4
+ @DBOS.dbos_class(class_name="AnotherDBOSTestRegDup")
5
+ class DBOSTestRegDup(DBOSConfiguredInstance):
6
+ """DBOSTestRegDup duplicates the name of a class defined dupname_classdefsa.py"""
7
+
8
+ def __init__(self, instance_name: str) -> None:
9
+ super().__init__(instance_name)
@@ -0,0 +1,9 @@
1
+ from dbos import DBOS, DBOSConfiguredInstance
2
+
3
+
4
+ @DBOS.dbos_class()
5
+ class DBOSTestRegDup(DBOSConfiguredInstance):
6
+ """DBOSTestRegDup duplicates the name of a class defined dupname_classdefs1.py"""
7
+
8
+ def __init__(self, instance_name: str) -> None:
9
+ super().__init__(instance_name)
@@ -316,11 +316,11 @@ def test_simple_workflow_inst(dbos: DBOS) -> None:
316
316
  stati = dbos.get_workflow_status(wfh.get_workflow_id())
317
317
  assert stati
318
318
  assert stati.config_name == "bob"
319
- assert stati.class_name == "DBOSTestClassInst"
319
+ assert stati.class_name and "DBOSTestClassInst" in stati.class_name
320
320
  stat = wfh.get_status()
321
321
  assert stat
322
322
  assert stat.config_name == "bob"
323
- assert stat.class_name == "DBOSTestClassInst"
323
+ assert stat.class_name and "DBOSTestClassInst" in stat.class_name
324
324
 
325
325
  assert wfh.get_result() == "bob1bob"
326
326
  assert inst.txn_counter == 2
@@ -376,17 +376,38 @@ def test_duplicate_reg(dbos: DBOS) -> None:
376
376
  def __init__(self) -> None:
377
377
  super().__init__("bob")
378
378
 
379
- assert "Duplicate type registration for class 'DBOSTestRegDup'" == str(
380
- exc_info.value
379
+ assert (
380
+ "Duplicate type registration for class 'test_duplicate_reg.<locals>.DBOSTestRegDup'"
381
+ == str(exc_info.value)
381
382
  )
382
383
 
383
- # Dupliocate instance registration
384
+ # Duplicate instance registration
384
385
  inst = DBOSTestRegDup()
385
386
  with pytest.raises(Exception) as exc_info:
386
387
  inst = DBOSTestRegDup()
387
388
 
388
389
  assert (
389
- "Duplicate instance registration for class 'DBOSTestRegDup' instance 'bob'"
390
+ "Duplicate instance registration for class 'test_duplicate_reg.<locals>.DBOSTestRegDup' instance 'bob'"
391
+ == str(exc_info.value)
392
+ )
393
+
394
+ # There should be no collision when the duplicate class names are in
395
+ # different modules if they're specified with different names.
396
+ from tests import dupname_classdefs1, dupname_classdefsa
397
+
398
+ # Two instances of the same class may be registered if they have different
399
+ # instance_name.
400
+ # Duplicate instance registration error still occurs with identical class
401
+ # name and instance_name.
402
+ alice = dupname_classdefs1.DBOSTestRegDup("alice")
403
+ bob = dupname_classdefsa.DBOSTestRegDup("bob")
404
+ bob2 = dupname_classdefs1.DBOSTestRegDup("bob")
405
+
406
+ with pytest.raises(Exception) as exc_info:
407
+ bob2 = dupname_classdefs1.DBOSTestRegDup("bob")
408
+
409
+ assert (
410
+ "Duplicate instance registration for class 'AnotherDBOSTestRegDup' instance 'bob'"
390
411
  == str(exc_info.value)
391
412
  )
392
413
 
@@ -451,7 +472,7 @@ def test_inst_recovery(dbos: DBOS) -> None:
451
472
  assert last_inst is None
452
473
 
453
474
  status = DBOS.retrieve_workflow(wfid).get_status()
454
- assert status.class_name == "TestClass"
475
+ assert status.class_name and "TestClass" in status.class_name
455
476
  assert status.config_name == "test_class"
456
477
 
457
478
 
@@ -479,7 +500,7 @@ def test_inst_async_recovery(dbos: DBOS) -> None:
479
500
  orig_handle = DBOS.start_workflow(inst.workflow, input)
480
501
 
481
502
  status = orig_handle.get_status()
482
- assert status.class_name == "TestClass"
503
+ assert status.class_name and "TestClass" in status.class_name
483
504
  assert status.config_name == "test_class"
484
505
 
485
506
  recovery_handle = DBOS._execute_workflow_id(wfid)
@@ -513,7 +534,7 @@ def test_inst_async_step_recovery(dbos: DBOS) -> None:
513
534
  orig_handle = DBOS.start_workflow(inst.step, input)
514
535
 
515
536
  status = orig_handle.get_status()
516
- assert status.class_name == "TestClass"
537
+ assert status.class_name and "TestClass" in status.class_name
517
538
  assert status.config_name == "test_class"
518
539
 
519
540
  recovery_handle = DBOS._execute_workflow_id(wfid)
@@ -558,7 +579,7 @@ def test_step_recovery(dbos: DBOS) -> None:
558
579
  thread_event.wait()
559
580
 
560
581
  status = DBOS.retrieve_workflow(wfid).get_status()
561
- assert status.class_name == "TestClass"
582
+ assert status.class_name and "TestClass" in status.class_name
562
583
  assert status.config_name == "test_class"
563
584
 
564
585
  recovery_handle = DBOS._execute_workflow_id(wfid)
@@ -626,7 +647,7 @@ def test_class_queue_recovery(dbos: DBOS) -> None:
626
647
  result = [i * multiplier for i in range(5)]
627
648
  for h in recovery_handles:
628
649
  status = h.get_status()
629
- assert status.class_name == "TestClass"
650
+ assert status.class_name and "TestClass" in status.class_name
630
651
  assert status.config_name == "test_class"
631
652
  if h.get_workflow_id() == wfid:
632
653
  assert h.get_result() == result
@@ -770,7 +791,7 @@ def test_class_classmethod_queue_recovery(dbos: DBOS) -> None:
770
791
  for h in recovery_handles:
771
792
  status = h.get_status()
772
793
  # Class name is recorded for class methods
773
- assert status.class_name == "TestClass"
794
+ assert status.class_name and "TestClass" in status.class_name
774
795
  assert status.config_name == None
775
796
  if h.get_workflow_id() == wfid:
776
797
  assert h.get_result() == result
@@ -808,13 +829,13 @@ def test_inst_txn(dbos: DBOS) -> None:
808
829
  with SetWorkflowID(wfid):
809
830
  assert inst.transaction(input) == input * multiplier
810
831
  status = DBOS.retrieve_workflow(wfid).get_status()
811
- assert status.class_name == "TestClass"
832
+ assert status.class_name and "TestClass" in status.class_name
812
833
  assert status.config_name == "test_class"
813
834
 
814
835
  handle = DBOS.start_workflow(inst.transaction, input)
815
836
  assert handle.get_result() == input * multiplier
816
837
  status = handle.get_status()
817
- assert status.class_name == "TestClass"
838
+ assert status.class_name and "TestClass" in status.class_name
818
839
  assert status.config_name == "test_class"
819
840
 
820
841
 
@@ -848,13 +869,13 @@ def test_mixed_methods(dbos: DBOS) -> None:
848
869
  handle = DBOS.start_workflow(inst.instance_workflow, input)
849
870
  assert handle.get_result() == input * multiplier
850
871
  status = handle.get_status()
851
- assert status.class_name == "TestClass"
872
+ assert status.class_name and "TestClass" in status.class_name
852
873
  assert status.config_name == "test_class"
853
874
 
854
875
  handle = DBOS.start_workflow(inst.classmethod_workflow, input)
855
876
  assert handle.get_result() == input
856
877
  status = handle.get_status()
857
- assert status.class_name == "TestClass"
878
+ assert status.class_name and "TestClass" in status.class_name
858
879
  assert status.config_name == None
859
880
 
860
881
  handle = DBOS.start_workflow(inst.staticmethod_workflow, input)
@@ -48,7 +48,7 @@ def test_dbos_singleton(cleanup_test_databases: None) -> None:
48
48
  stati = DBOS.get_workflow_status(wh.get_workflow_id())
49
49
  assert stati
50
50
  assert stati.config_name == "myconfig"
51
- assert stati.class_name == "DBOSTestClass"
51
+ assert stati.class_name and "DBOSTestClass" in stati.class_name
52
52
  wfhr: WorkflowHandle[str] = DBOS.retrieve_workflow(wh.get_workflow_id())
53
53
  assert wfhr.workflow_id == wh.get_workflow_id()
54
54
 
@@ -33,7 +33,11 @@ def test_cancel_resume(dbos: DBOS) -> None:
33
33
  step_one()
34
34
  main_thread_event.set()
35
35
  workflow_event.wait()
36
- step_two()
36
+ # A handler like this should not catch DBOSWorkflowCancelledError
37
+ try:
38
+ step_two()
39
+ except Exception:
40
+ raise
37
41
  return x
38
42
 
39
43
  # Start the workflow and cancel it.
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes