dbos 1.8.0a5__tar.gz → 1.8.0a8__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 (109) hide show
  1. {dbos-1.8.0a5 → dbos-1.8.0a8}/PKG-INFO +1 -1
  2. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_dbos.py +0 -28
  3. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_error.py +5 -5
  4. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_sys_db.py +12 -7
  5. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/cli/cli.py +2 -2
  6. {dbos-1.8.0a5 → dbos-1.8.0a8}/pyproject.toml +1 -1
  7. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_admin_server.py +1 -1
  8. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_async.py +6 -2
  9. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_classdecorators.py +27 -0
  10. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_failures.py +7 -4
  11. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_queue.py +1 -1
  12. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_scheduler.py +3 -0
  13. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_singleton.py +0 -24
  14. {dbos-1.8.0a5 → dbos-1.8.0a8}/LICENSE +0 -0
  15. {dbos-1.8.0a5 → dbos-1.8.0a8}/README.md +0 -0
  16. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/__init__.py +0 -0
  17. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/__main__.py +0 -0
  18. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_admin_server.py +0 -0
  19. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_app_db.py +0 -0
  20. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_classproperty.py +0 -0
  21. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_client.py +0 -0
  22. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_conductor/conductor.py +0 -0
  23. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_conductor/protocol.py +0 -0
  24. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_context.py +0 -0
  25. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_core.py +0 -0
  26. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_croniter.py +0 -0
  27. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_dbos_config.py +0 -0
  28. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_debug.py +0 -0
  29. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_docker_pg_helper.py +0 -0
  30. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_event_loop.py +0 -0
  31. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_fastapi.py +0 -0
  32. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_flask.py +0 -0
  33. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_kafka.py +0 -0
  34. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_kafka_message.py +0 -0
  35. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_logger.py +0 -0
  36. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/env.py +0 -0
  37. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/script.py.mako +0 -0
  38. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  39. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  40. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  41. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  42. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/66478e1b95e5_consolidate_queues.py +0 -0
  43. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  44. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/933e86bdac6a_add_queue_priority.py +0 -0
  45. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  46. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  47. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  48. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/d994145b47b6_consolidate_inputs.py +0 -0
  49. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  50. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  51. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_outcome.py +0 -0
  52. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_queue.py +0 -0
  53. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_recovery.py +0 -0
  54. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_registrations.py +0 -0
  55. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_roles.py +0 -0
  56. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_scheduler.py +0 -0
  57. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_schemas/__init__.py +0 -0
  58. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_schemas/application_database.py +0 -0
  59. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_schemas/system_database.py +0 -0
  60. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_serialization.py +0 -0
  61. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/README.md +0 -0
  62. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  63. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  64. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  65. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  66. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  67. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  68. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  69. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  70. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  71. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_tracer.py +0 -0
  72. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_utils.py +0 -0
  73. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/_workflow_commands.py +0 -0
  74. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/cli/_github_init.py +0 -0
  75. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/cli/_template_init.py +0 -0
  76. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/dbos-config.schema.json +0 -0
  77. {dbos-1.8.0a5 → dbos-1.8.0a8}/dbos/py.typed +0 -0
  78. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/__init__.py +0 -0
  79. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/atexit_no_ctor.py +0 -0
  80. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/atexit_no_launch.py +0 -0
  81. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/classdefs.py +0 -0
  82. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/client_collateral.py +0 -0
  83. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/client_worker.py +0 -0
  84. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/conftest.py +0 -0
  85. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/dupname_classdefs1.py +0 -0
  86. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/dupname_classdefsa.py +0 -0
  87. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/more_classdefs.py +0 -0
  88. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/queuedworkflow.py +0 -0
  89. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_async_workflow_management.py +0 -0
  90. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_cli.py +0 -0
  91. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_client.py +0 -0
  92. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_concurrency.py +0 -0
  93. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_config.py +0 -0
  94. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_croniter.py +0 -0
  95. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_dbos.py +0 -0
  96. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_debug.py +0 -0
  97. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_docker_secrets.py +0 -0
  98. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_fastapi.py +0 -0
  99. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_fastapi_roles.py +0 -0
  100. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_flask.py +0 -0
  101. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_kafka.py +0 -0
  102. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_outcome.py +0 -0
  103. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_package.py +0 -0
  104. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_schema_migration.py +0 -0
  105. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_spans.py +0 -0
  106. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_sqlalchemy.py +0 -0
  107. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_workflow_introspection.py +0 -0
  108. {dbos-1.8.0a5 → dbos-1.8.0a8}/tests/test_workflow_management.py +0 -0
  109. {dbos-1.8.0a5 → dbos-1.8.0a8}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 1.8.0a5
3
+ Version: 1.8.0a8
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1376,31 +1376,3 @@ class DBOSConfiguredInstance:
1376
1376
  def __init__(self, config_name: str) -> None:
1377
1377
  self.config_name = config_name
1378
1378
  DBOS.register_instance(self)
1379
-
1380
-
1381
- # Apps that import DBOS probably don't exit. If they do, let's see if
1382
- # it looks like startup was abandoned or a call was forgotten...
1383
- def _dbos_exit_hook() -> None:
1384
- if _dbos_global_registry is None:
1385
- # Probably used as or for a support module
1386
- return
1387
- if _dbos_global_instance is None:
1388
- print("DBOS exiting; functions were registered but DBOS() was not called")
1389
- dbos_logger.warning(
1390
- "DBOS exiting; functions were registered but DBOS() was not called"
1391
- )
1392
- return
1393
- if not _dbos_global_instance._launched:
1394
- if _dbos_global_instance.fastapi is not None:
1395
- # FastAPI lifespan middleware will call launch/destroy, so we can ignore this.
1396
- # This is likely to happen during fastapi dev runs, where the reloader loads the module multiple times.
1397
- return
1398
- print("DBOS exiting; DBOS exists but launch() was not called")
1399
- dbos_logger.warning("DBOS exiting; DBOS exists but launch() was not called")
1400
- return
1401
- # If we get here, we're exiting normally
1402
- _dbos_global_instance.destroy()
1403
-
1404
-
1405
- # Register the exit hook
1406
- atexit.register(_dbos_exit_hook)
@@ -55,7 +55,7 @@ class DBOSErrorCode(Enum):
55
55
  InitializationError = 3
56
56
  WorkflowFunctionNotFound = 4
57
57
  NonExistentWorkflowError = 5
58
- DeadLetterQueueError = 6
58
+ MaxRecoveryAttemptsExceeded = 6
59
59
  MaxStepRetriesExceeded = 7
60
60
  NotAuthorized = 8
61
61
  ConflictingWorkflowError = 9
@@ -121,13 +121,13 @@ class DBOSNonExistentWorkflowError(DBOSException):
121
121
  )
122
122
 
123
123
 
124
- class DBOSDeadLetterQueueError(DBOSException):
125
- """Exception raised when a workflow database record does not exist for a given ID."""
124
+ class MaxRecoveryAttemptsExceededError(DBOSException):
125
+ """Exception raised when a workflow exceeds its max recovery attempts."""
126
126
 
127
127
  def __init__(self, wf_id: str, max_retries: int):
128
128
  super().__init__(
129
- f"Workflow {wf_id} has been moved to the dead-letter queue after exceeding the maximum of {max_retries} retries",
130
- dbos_error_code=DBOSErrorCode.DeadLetterQueueError.value,
129
+ f"Workflow {wf_id} has exceeded its maximum of {max_retries} execution or recovery attempts. Further attempts to execute or recover it will fail. See documentation for details: https://docs.dbos.dev/python/reference/decorators",
130
+ dbos_error_code=DBOSErrorCode.MaxRecoveryAttemptsExceeded.value,
131
131
  )
132
132
 
133
133
 
@@ -37,12 +37,12 @@ from ._context import get_local_dbos_context
37
37
  from ._error import (
38
38
  DBOSAwaitedWorkflowCancelledError,
39
39
  DBOSConflictingWorkflowError,
40
- DBOSDeadLetterQueueError,
41
40
  DBOSNonExistentWorkflowError,
42
41
  DBOSQueueDeduplicatedError,
43
42
  DBOSUnexpectedStepError,
44
43
  DBOSWorkflowCancelledError,
45
44
  DBOSWorkflowConflictIDError,
45
+ MaxRecoveryAttemptsExceededError,
46
46
  )
47
47
  from ._logger import dbos_logger
48
48
  from ._schemas.system_database import SystemSchema
@@ -57,20 +57,25 @@ class WorkflowStatusString(Enum):
57
57
  PENDING = "PENDING"
58
58
  SUCCESS = "SUCCESS"
59
59
  ERROR = "ERROR"
60
- RETRIES_EXCEEDED = "RETRIES_EXCEEDED"
60
+ MAX_RECOVERY_ATTEMPTS_EXCEEDED = "MAX_RECOVERY_ATTEMPTS_EXCEEDED"
61
61
  CANCELLED = "CANCELLED"
62
62
  ENQUEUED = "ENQUEUED"
63
63
 
64
64
 
65
65
  WorkflowStatuses = Literal[
66
- "PENDING", "SUCCESS", "ERROR", "RETRIES_EXCEEDED", "CANCELLED", "ENQUEUED"
66
+ "PENDING",
67
+ "SUCCESS",
68
+ "ERROR",
69
+ "MAX_RECOVERY_ATTEMPTS_EXCEEDED",
70
+ "CANCELLED",
71
+ "ENQUEUED",
67
72
  ]
68
73
 
69
74
 
70
75
  class WorkflowStatus:
71
76
  # The workflow ID
72
77
  workflow_id: str
73
- # The workflow status. Must be one of ENQUEUED, PENDING, SUCCESS, ERROR, CANCELLED, or RETRIES_EXCEEDED
78
+ # The workflow status. Must be one of ENQUEUED, PENDING, SUCCESS, ERROR, CANCELLED, or MAX_RECOVERY_ATTEMPTS_EXCEEDED
74
79
  status: str
75
80
  # The name of the workflow function
76
81
  name: str
@@ -515,7 +520,7 @@ class SystemDatabase:
515
520
  raise DBOSConflictingWorkflowError(status["workflow_uuid"], err_msg)
516
521
 
517
522
  # Every time we start executing a workflow (and thus attempt to insert its status), we increment `recovery_attempts` by 1.
518
- # When this number becomes equal to `maxRetries + 1`, we mark the workflow as `RETRIES_EXCEEDED`.
523
+ # When this number becomes equal to `maxRetries + 1`, we mark the workflow as `MAX_RECOVERY_ATTEMPTS_EXCEEDED`.
519
524
  if (
520
525
  (wf_status != "SUCCESS" and wf_status != "ERROR")
521
526
  and max_recovery_attempts is not None
@@ -532,7 +537,7 @@ class SystemDatabase:
532
537
  == WorkflowStatusString.PENDING.value
533
538
  )
534
539
  .values(
535
- status=WorkflowStatusString.RETRIES_EXCEEDED.value,
540
+ status=WorkflowStatusString.MAX_RECOVERY_ATTEMPTS_EXCEEDED.value,
536
541
  deduplication_id=None,
537
542
  started_at_epoch_ms=None,
538
543
  queue_name=None,
@@ -541,7 +546,7 @@ class SystemDatabase:
541
546
  conn.execute(dlq_cmd)
542
547
  # Need to commit here because we're throwing an exception
543
548
  conn.commit()
544
- raise DBOSDeadLetterQueueError(
549
+ raise MaxRecoveryAttemptsExceededError(
545
550
  status["workflow_uuid"], max_recovery_attempts
546
551
  )
547
552
 
@@ -450,7 +450,7 @@ def list(
450
450
  typer.Option(
451
451
  "--status",
452
452
  "-S",
453
- help="Retrieve workflows with this status (PENDING, SUCCESS, ERROR, RETRIES_EXCEEDED, ENQUEUED, or CANCELLED)",
453
+ help="Retrieve workflows with this status (PENDING, SUCCESS, ERROR, ENQUEUED, CANCELLED, or MAX_RECOVERY_ATTEMPTS_EXCEEDED)",
454
454
  ),
455
455
  ] = None,
456
456
  appversion: Annotated[
@@ -657,7 +657,7 @@ def list_queue(
657
657
  typer.Option(
658
658
  "--status",
659
659
  "-S",
660
- help="Retrieve functions with this status (PENDING, SUCCESS, ERROR, RETRIES_EXCEEDED, ENQUEUED, or CANCELLED)",
660
+ help="Retrieve functions with this status (PENDING, SUCCESS, ERROR, ENQUEUED, CANCELLED, or MAX_RECOVERY_ATTEMPTS_EXCEEDED)",
661
661
  ),
662
662
  ] = None,
663
663
  queue_name: Annotated[
@@ -27,7 +27,7 @@ dependencies = [
27
27
  ]
28
28
  requires-python = ">=3.9"
29
29
  readme = "README.md"
30
- version = "1.8.0a5"
30
+ version = "1.8.0a8"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -104,7 +104,7 @@ def test_deactivate(dbos: DBOS, config: DBOSConfig) -> None:
104
104
  assert event.is_set()
105
105
  # Verify the scheduled workflow does not run anymore
106
106
  time.sleep(5)
107
- assert wf_counter <= val + 1
107
+ assert wf_counter <= val + 2
108
108
  # Enqueue a workflow, verify it still runs
109
109
  assert queue.enqueue(regular_workflow).get_result() == 5
110
110
 
@@ -32,12 +32,12 @@ async def test_async_workflow(dbos: DBOS) -> None:
32
32
  nonlocal wf_counter
33
33
  wf_counter += 1
34
34
  res1 = test_transaction(var1)
35
- res2 = test_step(var2)
35
+ res2 = await test_step(var2)
36
36
  DBOS.logger.info("I'm test_workflow")
37
37
  return res1 + res2
38
38
 
39
39
  @DBOS.step()
40
- def test_step(var: str) -> str:
40
+ async def test_step(var: str) -> str:
41
41
  nonlocal step_counter
42
42
  step_counter += 1
43
43
  DBOS.logger.info("I'm test_step")
@@ -73,6 +73,10 @@ async def test_async_workflow(dbos: DBOS) -> None:
73
73
  sync_handle = DBOS.start_workflow(test_workflow, "alice", "bob")
74
74
  assert sync_handle.get_result() == "alicetxn31bobstep3" # type: ignore
75
75
 
76
+ # Test DBOS.start_workflow_async on steps
77
+ handle = await DBOS.start_workflow_async(test_step, "alice")
78
+ assert (await handle.get_result()) == "alicestep4"
79
+
76
80
 
77
81
  @pytest.mark.asyncio
78
82
  async def test_async_step(dbos: DBOS) -> None:
@@ -10,6 +10,7 @@ from dbos import DBOS, DBOSConfiguredInstance, Queue, SetWorkflowID
10
10
 
11
11
  # Private API used because this is a test
12
12
  from dbos._context import DBOSContextEnsure, assert_current_dbos_context
13
+ from dbos._dbos_config import DBOSConfig
13
14
  from tests.conftest import queue_entries_are_cleaned_up
14
15
 
15
16
 
@@ -884,3 +885,29 @@ def test_mixed_methods(dbos: DBOS) -> None:
884
885
  status = handle.get_status()
885
886
  assert status.class_name == None
886
887
  assert status.config_name == None
888
+
889
+
890
+ def test_class_step_without_dbos(dbos: DBOS, config: DBOSConfig) -> None:
891
+ DBOS.destroy(destroy_registry=True)
892
+
893
+ @DBOS.dbos_class()
894
+ class TestClass(DBOSConfiguredInstance):
895
+ def __init__(self, x: int) -> None:
896
+ self.x = x
897
+ super().__init__("test")
898
+
899
+ @DBOS.step()
900
+ def step(self, x: int) -> int:
901
+ return self.x + x
902
+
903
+ input = 5
904
+ inst = TestClass(input)
905
+ assert inst.step(input) == input + input
906
+
907
+ DBOS(config=config)
908
+
909
+ assert inst.step(input) == input + input
910
+
911
+ DBOS.launch()
912
+
913
+ assert inst.step(input) == input + input
@@ -10,11 +10,11 @@ from sqlalchemy.exc import InvalidRequestError, OperationalError
10
10
  from dbos import DBOS, Queue, SetWorkflowID
11
11
  from dbos._error import (
12
12
  DBOSAwaitedWorkflowCancelledError,
13
- DBOSDeadLetterQueueError,
14
13
  DBOSMaxStepRetriesExceeded,
15
14
  DBOSNotAuthorizedError,
16
15
  DBOSQueueDeduplicatedError,
17
16
  DBOSUnexpectedStepError,
17
+ MaxRecoveryAttemptsExceededError,
18
18
  )
19
19
  from dbos._registrations import DEFAULT_MAX_RECOVERY_ATTEMPTS
20
20
  from dbos._serialization import (
@@ -179,12 +179,15 @@ def test_dead_letter_queue(dbos: DBOS) -> None:
179
179
  # and puts the workflow in the DLQ status.
180
180
  with pytest.raises(Exception) as exc_info:
181
181
  DBOS._recover_pending_workflows()
182
- assert exc_info.errisinstance(DBOSDeadLetterQueueError)
183
- assert handle.get_status().status == WorkflowStatusString.RETRIES_EXCEEDED.value
182
+ assert exc_info.errisinstance(MaxRecoveryAttemptsExceededError)
183
+ assert (
184
+ handle.get_status().status
185
+ == WorkflowStatusString.MAX_RECOVERY_ATTEMPTS_EXCEEDED.value
186
+ )
184
187
  with pytest.raises(Exception) as exc_info:
185
188
  with SetWorkflowID(wfid):
186
189
  dead_letter_workflow()
187
- assert exc_info.errisinstance(DBOSDeadLetterQueueError)
190
+ assert exc_info.errisinstance(MaxRecoveryAttemptsExceededError)
188
191
 
189
192
  # Resume the workflow. Verify it can recover again without error.
190
193
  resumed_handle = dbos.resume_workflow(wfid)
@@ -1041,7 +1041,7 @@ def test_dlq_enqueued_workflows(dbos: DBOS) -> None:
1041
1041
  time.sleep(2)
1042
1042
  assert (
1043
1043
  blocked_handle.get_status().status
1044
- == WorkflowStatusString.RETRIES_EXCEEDED.value
1044
+ == WorkflowStatusString.MAX_RECOVERY_ATTEMPTS_EXCEEDED.value
1045
1045
  )
1046
1046
  with dbos._sys_db.engine.begin() as c:
1047
1047
  query = sa.select(SystemSchema.workflow_status.c.recovery_attempts).where(
@@ -219,6 +219,9 @@ def test_scheduler_oaoo(dbos: DBOS) -> None:
219
219
  for evt in dbos.poller_stop_events:
220
220
  evt.set()
221
221
 
222
+ # Wait for workflows to finish
223
+ time.sleep(2)
224
+
222
225
  dbos._sys_db.update_workflow_outcome(workflow_id, "PENDING")
223
226
 
224
227
  workflow_handles = DBOS._recover_pending_workflows()
@@ -129,27 +129,3 @@ def test_dbos_singleton_negative(cleanup_test_databases: None) -> None:
129
129
  assert "launch" in str(exc_info.value)
130
130
 
131
131
  DBOS.destroy()
132
-
133
-
134
- def test_dbos_atexit_no_dbos(cleanup_test_databases: None) -> None:
135
- # Run the .py as a separate process
136
- result = subprocess.run(
137
- [sys.executable, path.join("tests", "atexit_no_ctor.py")],
138
- capture_output=True,
139
- text=True,
140
- )
141
-
142
- # Assert that the output contains the warning message
143
- assert "DBOS exiting; functions were registered" in result.stdout
144
-
145
-
146
- def test_dbos_atexit_no_launch(cleanup_test_databases: None) -> None:
147
- # Run the .py as a separate process
148
- result = subprocess.run(
149
- [sys.executable, path.join("tests", "atexit_no_launch.py")],
150
- capture_output=True,
151
- text=True,
152
- )
153
-
154
- # Assert that the output contains the warning message
155
- assert "DBOS exists but launch() was not called" in result.stdout
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
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