dbos 1.8.0a5__py3-none-any.whl → 1.9.0__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.

Potentially problematic release.


This version of dbos might be problematic. Click here for more details.

dbos/_core.py CHANGED
@@ -49,6 +49,7 @@ from ._context import (
49
49
  get_local_dbos_context,
50
50
  )
51
51
  from ._error import (
52
+ DBOSAwaitedWorkflowCancelledError,
52
53
  DBOSException,
53
54
  DBOSMaxStepRetriesExceeded,
54
55
  DBOSNonExistentWorkflowError,
@@ -370,7 +371,7 @@ def _get_wf_invoke_func(
370
371
  r: R = dbos._sys_db.await_workflow_result(status["workflow_uuid"])
371
372
  return r
372
373
  except DBOSWorkflowCancelledError as error:
373
- raise
374
+ raise DBOSAwaitedWorkflowCancelledError(status["workflow_uuid"])
374
375
  except Exception as error:
375
376
  if not dbos.debug_mode:
376
377
  dbos._sys_db.update_workflow_outcome(
dbos/_dbos.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import atexit
5
4
  import hashlib
6
5
  import inspect
7
6
  import os
@@ -1219,39 +1218,40 @@ class DBOS:
1219
1218
  return rv
1220
1219
 
1221
1220
  @classproperty
1222
- def workflow_id(cls) -> str:
1223
- """Return the workflow ID for the current context, which must be executing a workflow function."""
1224
- ctx = assert_current_dbos_context()
1225
- assert (
1226
- ctx.is_within_workflow()
1227
- ), "workflow_id is only available within a DBOS operation."
1228
- return ctx.workflow_id
1221
+ def workflow_id(cls) -> Optional[str]:
1222
+ """Return the ID of the currently executing workflow. If a workflow is not executing, return None."""
1223
+ ctx = get_local_dbos_context()
1224
+ if ctx and ctx.is_within_workflow():
1225
+ return ctx.workflow_id
1226
+ else:
1227
+ return None
1229
1228
 
1230
1229
  @classproperty
1231
- def step_id(cls) -> int:
1232
- """Return the step ID for the currently executing step. This is a unique identifier of the current step within the workflow."""
1233
- ctx = assert_current_dbos_context()
1234
- assert (
1235
- ctx.is_step() or ctx.is_transaction()
1236
- ), "step_id is only available within a DBOS step."
1237
- return ctx.function_id
1230
+ def step_id(cls) -> Optional[int]:
1231
+ """Return the step ID for the currently executing step. This is a unique identifier of the current step within the workflow. If a step is not currently executing, return None."""
1232
+ ctx = get_local_dbos_context()
1233
+ if ctx and (ctx.is_step() or ctx.is_transaction()):
1234
+ return ctx.function_id
1235
+ else:
1236
+ return None
1238
1237
 
1239
1238
  @classproperty
1240
- def step_status(cls) -> StepStatus:
1241
- """Return the status of the currently executing step."""
1242
- ctx = assert_current_dbos_context()
1243
- assert ctx.is_step(), "step_status is only available within a DBOS step."
1244
- assert ctx.step_status is not None
1245
- return ctx.step_status
1239
+ def step_status(cls) -> Optional[StepStatus]:
1240
+ """Return the status of the currently executing step. If a step is not currently executing, return None."""
1241
+ ctx = get_local_dbos_context()
1242
+ if ctx and ctx.is_step():
1243
+ return ctx.step_status
1244
+ else:
1245
+ return None
1246
1246
 
1247
1247
  @classproperty
1248
1248
  def parent_workflow_id(cls) -> str:
1249
1249
  """
1250
- Return the workflow ID for the parent workflow.
1251
-
1252
- `parent_workflow_id` must be accessed from within a workflow function.
1250
+ This method is deprecated and should not be used.
1253
1251
  """
1254
-
1252
+ dbos_logger.warning(
1253
+ "DBOS.parent_workflow_id is deprecated and should not be used"
1254
+ )
1255
1255
  ctx = assert_current_dbos_context()
1256
1256
  assert (
1257
1257
  ctx.is_within_workflow()
@@ -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)
dbos/_error.py CHANGED
@@ -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
 
dbos/_sys_db.py CHANGED
@@ -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
 
dbos/cli/cli.py CHANGED
@@ -10,7 +10,7 @@ from typing import Any, Optional
10
10
  import jsonpickle # type: ignore
11
11
  import sqlalchemy as sa
12
12
  import typer
13
- from rich import print
13
+ from rich import print as richprint
14
14
  from rich.prompt import IntPrompt
15
15
  from typing_extensions import Annotated, List
16
16
 
@@ -196,7 +196,7 @@ def init(
196
196
  path.join(templates_dir, template), project_name, config_mode=config
197
197
  )
198
198
  except Exception as e:
199
- print(f"[red]{e}[/red]")
199
+ richprint(f"[red]{e}[/red]")
200
200
 
201
201
 
202
202
  def _resolve_project_name_and_template(
@@ -217,9 +217,9 @@ def _resolve_project_name_and_template(
217
217
  if template not in templates:
218
218
  raise Exception(f"Template {template} not found in {templates_dir}")
219
219
  else:
220
- print("\n[bold]Available templates:[/bold]")
220
+ richprint("\n[bold]Available templates:[/bold]")
221
221
  for idx, template_name in enumerate(templates, 1):
222
- print(f" {idx}. {template_name}")
222
+ richprint(f" {idx}. {template_name}")
223
223
  while True:
224
224
  try:
225
225
  choice = IntPrompt.ask(
@@ -231,13 +231,13 @@ def _resolve_project_name_and_template(
231
231
  template = templates[choice - 1]
232
232
  break
233
233
  else:
234
- print(
234
+ richprint(
235
235
  "[red]Invalid selection. Please choose a number from the list.[/red]"
236
236
  )
237
237
  except (KeyboardInterrupt, EOFError):
238
238
  raise typer.Abort()
239
239
  except ValueError:
240
- print("[red]Please enter a valid number.[/red]")
240
+ richprint("[red]Please enter a valid number.[/red]")
241
241
 
242
242
  if template in git_templates:
243
243
  if project_name is None:
@@ -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[
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 1.8.0a5
3
+ Version: 1.9.0
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-1.8.0a5.dist-info/METADATA,sha256=4VFTFK4ah0MNauAMusSRH9aW0CnLjveeNNLwsC5cxgs,13267
2
- dbos-1.8.0a5.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- dbos-1.8.0a5.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-1.8.0a5.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-1.9.0.dist-info/METADATA,sha256=wk4ToDPZ7FqnLG5bFfQAoQE8ZVWMooq7PyDKskE8EVA,13265
2
+ dbos-1.9.0.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
+ dbos-1.9.0.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-1.9.0.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=NssPCubaBxdiKarOWa-wViz1hdJSkmBGcpLX_gQ4NeA,891
6
6
  dbos/__main__.py,sha256=G7Exn-MhGrVJVDbgNlpzhfh8WMX_72t3_oJaFT9Lmt8,653
7
7
  dbos/_admin_server.py,sha256=e8ELhcDWqR3_PNobnNgUvLGh5lzZq0yFSF6dvtzoQRI,16267
@@ -11,13 +11,13 @@ dbos/_client.py,sha256=6ReC_VF1JhscxtMjmjwJH_X7HV0L9Zoj_OiYhs4el40,15517
11
11
  dbos/_conductor/conductor.py,sha256=3E_hL3c9g9yWqKZkvI6KA0-ZzPMPRo06TOzT1esMiek,24114
12
12
  dbos/_conductor/protocol.py,sha256=q3rgLxINFtWFigdOONc-4gX4vn66UmMlJQD6Kj8LnL4,7420
13
13
  dbos/_context.py,sha256=0vFtLAk3WF5BQYIYNFImDRBppKO2CTKOSy51zQC-Cu8,25723
14
- dbos/_core.py,sha256=kRY2PXVryfpwjbOCmgzPA_-qNsFmRMLi-CxYCnyp1V8,49495
14
+ dbos/_core.py,sha256=TA-UOSO_BhvM6L6j4__dwesK7x5Y93dk6mV1xx0WZBY,49593
15
15
  dbos/_croniter.py,sha256=XHAyUyibs_59sJQfSNWkP7rqQY6_XrlfuuCxk4jYqek,47559
16
- dbos/_dbos.py,sha256=bVKff1pUa8LeUEi53FFWMIC008qxM_RLLtfdMmz4vFA,52035
16
+ dbos/_dbos.py,sha256=LqE8ej317diZ5JjrXhndLwDr40E1Aw3SK1YPxC8-t3k,50902
17
17
  dbos/_dbos_config.py,sha256=TWIbGCWl_8o3l0Y2IzrL1q9mTYl_vVeZDjYJMDXCPVU,21676
18
18
  dbos/_debug.py,sha256=99j2SChWmCPAlZoDmjsJGe77tpU2LEa8E2TtLAnnh7o,1831
19
19
  dbos/_docker_pg_helper.py,sha256=tLJXWqZ4S-ExcaPnxg_i6cVxL6ZxrYlZjaGsklY-s2I,6115
20
- dbos/_error.py,sha256=MAHBjo2MLoaHBtRL1pOPNKqAM3IdNPPV11n7-meSF54,8580
20
+ dbos/_error.py,sha256=2_2ve3qN0BLhFWMmd3aD9At54tIMCeh8TqHtVcEEVQo,8705
21
21
  dbos/_event_loop.py,sha256=cvaFN9-II3MsHEOq8QoICc_8qSKrjikMlLfuhC3Y8Dk,2923
22
22
  dbos/_fastapi.py,sha256=T7YlVY77ASqyTqq0aAPclZ9YzlXdGTT0lEYSwSgt1EE,3151
23
23
  dbos/_flask.py,sha256=Npnakt-a3W5OykONFRkDRnumaDhTQmA0NPdUCGRYKXE,1652
@@ -49,7 +49,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  dbos/_schemas/application_database.py,sha256=SypAS9l9EsaBHFn9FR8jmnqt01M74d9AF1AMa4m2hhI,1040
50
50
  dbos/_schemas/system_database.py,sha256=rbFKggONdvvbb45InvGz0TM6a7c-Ux9dcaL-h_7Z7pU,4438
51
51
  dbos/_serialization.py,sha256=bWuwhXSQcGmiazvhJHA5gwhrRWxtmFmcCFQSDJnqqkU,3666
52
- dbos/_sys_db.py,sha256=XcDXqr9kQo_EcprawANLxUkn1MLXNV8iPpTOsA5Wgx0,81200
52
+ dbos/_sys_db.py,sha256=0GAWwxxelTxukYHQIwLr9JOlwy0vENkxbyGcfnv8_Ko,81321
53
53
  dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
54
54
  dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  dbos/_templates/dbos-db-starter/__package/main.py.dbos,sha256=aQnBPSSQpkB8ERfhf7gB7P9tsU6OPKhZscfeh0yiaD8,2702
@@ -65,8 +65,8 @@ dbos/_utils.py,sha256=uywq1QrjMwy17btjxW4bES49povlQwYwYbvKwMT6C2U,1575
65
65
  dbos/_workflow_commands.py,sha256=EmmAaQfRWeOZm_WPTznuU-O3he3jiSzzT9VpYrhxugE,4835
66
66
  dbos/cli/_github_init.py,sha256=Y_bDF9gfO2jB1id4FV5h1oIxEJRWyqVjhb7bNEa5nQ0,3224
67
67
  dbos/cli/_template_init.py,sha256=7JBcpMqP1r2mfCnvWatu33z8ctEGHJarlZYKgB83cXE,2972
68
- dbos/cli/cli.py,sha256=Nez2c5Z61jBUcvgglSiy7FZoXhxR_EaVfQWDyLLi7IQ,22094
68
+ dbos/cli/cli.py,sha256=oU2uvRF90eAydQ9FoQcgi8N_Cojzz8NLn1WE-vrA1p0,22155
69
69
  dbos/dbos-config.schema.json,sha256=CjaspeYmOkx6Ip_pcxtmfXJTn_YGdSx_0pcPBF7KZmo,6060
70
70
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
71
71
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
72
- dbos-1.8.0a5.dist-info/RECORD,,
72
+ dbos-1.9.0.dist-info/RECORD,,
File without changes