dbos 1.5.0a10__tar.gz → 1.6.0a3__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 (108) hide show
  1. {dbos-1.5.0a10 → dbos-1.6.0a3}/PKG-INFO +1 -1
  2. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_client.py +7 -7
  3. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_context.py +12 -1
  4. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_core.py +10 -2
  5. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_dbos.py +3 -2
  6. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_sys_db.py +10 -7
  7. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_workflow_commands.py +5 -5
  8. {dbos-1.5.0a10 → dbos-1.6.0a3}/pyproject.toml +1 -1
  9. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_queue.py +41 -0
  10. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_workflow_introspection.py +4 -0
  11. {dbos-1.5.0a10 → dbos-1.6.0a3}/LICENSE +0 -0
  12. {dbos-1.5.0a10 → dbos-1.6.0a3}/README.md +0 -0
  13. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/__init__.py +0 -0
  14. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/__main__.py +0 -0
  15. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_admin_server.py +0 -0
  16. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_app_db.py +0 -0
  17. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_classproperty.py +0 -0
  18. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_conductor/conductor.py +0 -0
  19. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_conductor/protocol.py +0 -0
  20. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_croniter.py +0 -0
  21. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_dbos_config.py +0 -0
  22. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_debug.py +0 -0
  23. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_docker_pg_helper.py +0 -0
  24. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_error.py +0 -0
  25. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_event_loop.py +0 -0
  26. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_fastapi.py +0 -0
  27. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_flask.py +0 -0
  28. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_kafka.py +0 -0
  29. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_kafka_message.py +0 -0
  30. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_logger.py +0 -0
  31. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/env.py +0 -0
  32. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/script.py.mako +0 -0
  33. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  34. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  35. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  36. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  37. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/66478e1b95e5_consolidate_queues.py +0 -0
  38. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  39. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/933e86bdac6a_add_queue_priority.py +0 -0
  40. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  41. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  42. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  43. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/d994145b47b6_consolidate_inputs.py +0 -0
  44. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  45. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  46. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_outcome.py +0 -0
  47. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_queue.py +0 -0
  48. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_recovery.py +0 -0
  49. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_registrations.py +0 -0
  50. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_roles.py +0 -0
  51. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_scheduler.py +0 -0
  52. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_schemas/__init__.py +0 -0
  53. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_schemas/application_database.py +0 -0
  54. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_schemas/system_database.py +0 -0
  55. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_serialization.py +0 -0
  56. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/README.md +0 -0
  57. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  58. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  59. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  60. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  61. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  62. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  63. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  64. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  65. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  66. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_tracer.py +0 -0
  67. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/_utils.py +0 -0
  68. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/cli/_github_init.py +0 -0
  69. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/cli/_template_init.py +0 -0
  70. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/cli/cli.py +0 -0
  71. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/dbos-config.schema.json +0 -0
  72. {dbos-1.5.0a10 → dbos-1.6.0a3}/dbos/py.typed +0 -0
  73. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/__init__.py +0 -0
  74. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/atexit_no_ctor.py +0 -0
  75. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/atexit_no_launch.py +0 -0
  76. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/classdefs.py +0 -0
  77. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/client_collateral.py +0 -0
  78. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/client_worker.py +0 -0
  79. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/conftest.py +0 -0
  80. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/dupname_classdefs1.py +0 -0
  81. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/dupname_classdefsa.py +0 -0
  82. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/more_classdefs.py +0 -0
  83. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/queuedworkflow.py +0 -0
  84. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_admin_server.py +0 -0
  85. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_async.py +0 -0
  86. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_classdecorators.py +0 -0
  87. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_cli.py +0 -0
  88. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_client.py +0 -0
  89. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_concurrency.py +0 -0
  90. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_config.py +0 -0
  91. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_croniter.py +0 -0
  92. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_dbos.py +0 -0
  93. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_debug.py +0 -0
  94. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_docker_secrets.py +0 -0
  95. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_failures.py +0 -0
  96. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_fastapi.py +0 -0
  97. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_fastapi_roles.py +0 -0
  98. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_flask.py +0 -0
  99. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_kafka.py +0 -0
  100. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_outcome.py +0 -0
  101. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_package.py +0 -0
  102. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_scheduler.py +0 -0
  103. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_schema_migration.py +0 -0
  104. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_singleton.py +0 -0
  105. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_spans.py +0 -0
  106. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_sqlalchemy.py +0 -0
  107. {dbos-1.5.0a10 → dbos-1.6.0a3}/tests/test_workflow_management.py +0 -0
  108. {dbos-1.5.0a10 → dbos-1.6.0a3}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 1.5.0a10
3
+ Version: 1.6.0a3
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
1
  import asyncio
2
2
  import sys
3
3
  import uuid
4
- from typing import Any, Generic, List, Optional, TypedDict, TypeVar
4
+ from typing import Any, Generic, List, Optional, TypedDict, TypeVar, Union
5
5
 
6
6
  from dbos._app_db import ApplicationDatabase
7
7
  from dbos._context import MaxPriority, MinPriority
@@ -128,7 +128,6 @@ class DBOSClient:
128
128
  workflow_name = options["workflow_name"]
129
129
  queue_name = options["queue_name"]
130
130
 
131
- app_version = options.get("app_version")
132
131
  max_recovery_attempts = options.get("max_recovery_attempts")
133
132
  if max_recovery_attempts is None:
134
133
  max_recovery_attempts = DEFAULT_MAX_RECOVERY_ATTEMPTS
@@ -139,6 +138,7 @@ class DBOSClient:
139
138
  enqueue_options_internal: EnqueueOptionsInternal = {
140
139
  "deduplication_id": options.get("deduplication_id"),
141
140
  "priority": options.get("priority"),
141
+ "app_version": options.get("app_version"),
142
142
  }
143
143
 
144
144
  inputs: WorkflowInputs = {
@@ -152,7 +152,7 @@ class DBOSClient:
152
152
  "name": workflow_name,
153
153
  "class_name": None,
154
154
  "queue_name": queue_name,
155
- "app_version": app_version,
155
+ "app_version": enqueue_options_internal["app_version"],
156
156
  "config_name": None,
157
157
  "authenticated_user": None,
158
158
  "assumed_role": None,
@@ -284,7 +284,7 @@ class DBOSClient:
284
284
  self,
285
285
  *,
286
286
  workflow_ids: Optional[List[str]] = None,
287
- status: Optional[str] = None,
287
+ status: Optional[Union[str, List[str]]] = None,
288
288
  start_time: Optional[str] = None,
289
289
  end_time: Optional[str] = None,
290
290
  name: Optional[str] = None,
@@ -314,7 +314,7 @@ class DBOSClient:
314
314
  self,
315
315
  *,
316
316
  workflow_ids: Optional[List[str]] = None,
317
- status: Optional[str] = None,
317
+ status: Optional[Union[str, List[str]]] = None,
318
318
  start_time: Optional[str] = None,
319
319
  end_time: Optional[str] = None,
320
320
  name: Optional[str] = None,
@@ -344,7 +344,7 @@ class DBOSClient:
344
344
  self,
345
345
  *,
346
346
  queue_name: Optional[str] = None,
347
- status: Optional[str] = None,
347
+ status: Optional[Union[str, List[str]]] = None,
348
348
  start_time: Optional[str] = None,
349
349
  end_time: Optional[str] = None,
350
350
  name: Optional[str] = None,
@@ -368,7 +368,7 @@ class DBOSClient:
368
368
  self,
369
369
  *,
370
370
  queue_name: Optional[str] = None,
371
- status: Optional[str] = None,
371
+ status: Optional[Union[str, List[str]]] = None,
372
372
  start_time: Optional[str] = None,
373
373
  end_time: Optional[str] = None,
374
374
  name: Optional[str] = None,
@@ -93,6 +93,8 @@ class DBOSContext:
93
93
  self.assumed_role: Optional[str] = None
94
94
  self.step_status: Optional[StepStatus] = None
95
95
 
96
+ self.app_version: Optional[str] = None
97
+
96
98
  # A user-specified workflow timeout. Takes priority over a propagated deadline.
97
99
  self.workflow_timeout_ms: Optional[int] = None
98
100
  # A propagated workflow deadline.
@@ -435,7 +437,11 @@ class SetEnqueueOptions:
435
437
  """
436
438
 
437
439
  def __init__(
438
- self, *, deduplication_id: Optional[str] = None, priority: Optional[int] = None
440
+ self,
441
+ *,
442
+ deduplication_id: Optional[str] = None,
443
+ priority: Optional[int] = None,
444
+ app_version: Optional[str] = None,
439
445
  ) -> None:
440
446
  self.created_ctx = False
441
447
  self.deduplication_id: Optional[str] = deduplication_id
@@ -446,6 +452,8 @@ class SetEnqueueOptions:
446
452
  )
447
453
  self.priority: Optional[int] = priority
448
454
  self.saved_priority: Optional[int] = None
455
+ self.app_version: Optional[str] = app_version
456
+ self.saved_app_version: Optional[str] = None
449
457
 
450
458
  def __enter__(self) -> SetEnqueueOptions:
451
459
  # Code to create a basic context
@@ -458,6 +466,8 @@ class SetEnqueueOptions:
458
466
  ctx.deduplication_id = self.deduplication_id
459
467
  self.saved_priority = ctx.priority
460
468
  ctx.priority = self.priority
469
+ self.saved_app_version = ctx.app_version
470
+ ctx.app_version = self.app_version
461
471
  return self
462
472
 
463
473
  def __exit__(
@@ -469,6 +479,7 @@ class SetEnqueueOptions:
469
479
  curr_ctx = assert_current_dbos_context()
470
480
  curr_ctx.deduplication_id = self.saved_deduplication_id
471
481
  curr_ctx.priority = self.saved_priority
482
+ curr_ctx.app_version = self.saved_app_version
472
483
  # Code to clean up the basic context if we created it
473
484
  if self.created_ctx:
474
485
  _clear_local_dbos_context()
@@ -270,7 +270,12 @@ def _init_workflow(
270
270
  "output": None,
271
271
  "error": None,
272
272
  "app_id": ctx.app_id,
273
- "app_version": GlobalParams.app_version,
273
+ "app_version": (
274
+ enqueue_options["app_version"]
275
+ if enqueue_options is not None
276
+ and enqueue_options["app_version"] is not None
277
+ else GlobalParams.app_version
278
+ ),
274
279
  "executor_id": ctx.executor_id,
275
280
  "recovery_attempts": None,
276
281
  "authenticated_user": ctx.authenticated_user,
@@ -445,7 +450,8 @@ def execute_workflow_by_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[An
445
450
  wf_func = dbos._registry.workflow_info_map.get(status["name"], None)
446
451
  if not wf_func:
447
452
  raise DBOSWorkflowFunctionNotFoundError(
448
- workflow_id, "Workflow function not found"
453
+ workflow_id,
454
+ f"Cannot execute workflow because {status['name']} is not a registered workflow function",
449
455
  )
450
456
  with DBOSContextEnsure():
451
457
  # If this function belongs to a configured class, add that class instance as its first argument
@@ -546,6 +552,7 @@ def start_workflow(
546
552
  enqueue_options = EnqueueOptionsInternal(
547
553
  deduplication_id=local_ctx.deduplication_id if local_ctx is not None else None,
548
554
  priority=local_ctx.priority if local_ctx is not None else None,
555
+ app_version=local_ctx.app_version if local_ctx is not None else None,
549
556
  )
550
557
  new_wf_id, new_wf_ctx = _get_new_wf()
551
558
 
@@ -637,6 +644,7 @@ async def start_workflow_async(
637
644
  enqueue_options = EnqueueOptionsInternal(
638
645
  deduplication_id=local_ctx.deduplication_id if local_ctx is not None else None,
639
646
  priority=local_ctx.priority if local_ctx is not None else None,
647
+ app_version=local_ctx.app_version if local_ctx is not None else None,
640
648
  )
641
649
  new_wf_id, new_wf_ctx = _get_new_wf()
642
650
 
@@ -24,6 +24,7 @@ from typing import (
24
24
  Tuple,
25
25
  Type,
26
26
  TypeVar,
27
+ Union,
27
28
  )
28
29
 
29
30
  from opentelemetry.trace import Span
@@ -998,7 +999,7 @@ class DBOS:
998
999
  cls,
999
1000
  *,
1000
1001
  workflow_ids: Optional[List[str]] = None,
1001
- status: Optional[str] = None,
1002
+ status: Optional[Union[str, List[str]]] = None,
1002
1003
  start_time: Optional[str] = None,
1003
1004
  end_time: Optional[str] = None,
1004
1005
  name: Optional[str] = None,
@@ -1034,7 +1035,7 @@ class DBOS:
1034
1035
  cls,
1035
1036
  *,
1036
1037
  queue_name: Optional[str] = None,
1037
- status: Optional[str] = None,
1038
+ status: Optional[Union[str, List[str]]] = None,
1038
1039
  start_time: Optional[str] = None,
1039
1040
  end_time: Optional[str] = None,
1040
1041
  name: Optional[str] = None,
@@ -149,6 +149,8 @@ class EnqueueOptionsInternal(TypedDict):
149
149
  deduplication_id: Optional[str]
150
150
  # Priority of the workflow on the queue, starting from 1 ~ 2,147,483,647. Default 0 (highest priority).
151
151
  priority: Optional[int]
152
+ # On what version the workflow is enqueued. Current version if not specified.
153
+ app_version: Optional[str]
152
154
 
153
155
 
154
156
  class RecordedResult(TypedDict):
@@ -185,7 +187,9 @@ class GetWorkflowsInput:
185
187
  self.authenticated_user: Optional[str] = None # The user who ran the workflow.
186
188
  self.start_time: Optional[str] = None # Timestamp in ISO 8601 format
187
189
  self.end_time: Optional[str] = None # Timestamp in ISO 8601 format
188
- self.status: Optional[str] = None
190
+ self.status: Optional[List[str]] = (
191
+ None # Get workflows with one of these statuses
192
+ )
189
193
  self.application_version: Optional[str] = (
190
194
  None # The application version that ran this workflow. = None
191
195
  )
@@ -205,7 +209,7 @@ class GetWorkflowsInput:
205
209
 
206
210
  class GetQueuedWorkflowsInput(TypedDict):
207
211
  queue_name: Optional[str] # Get workflows belonging to this queue
208
- status: Optional[str] # Get workflows with this status
212
+ status: Optional[list[str]] # Get workflows with one of these statuses
209
213
  start_time: Optional[str] # Timestamp in ISO 8601 format
210
214
  end_time: Optional[str] # Timestamp in ISO 8601 format
211
215
  limit: Optional[int] # Return up to this many workflows IDs.
@@ -832,7 +836,7 @@ class SystemDatabase:
832
836
  <= datetime.datetime.fromisoformat(input.end_time).timestamp() * 1000
833
837
  )
834
838
  if input.status:
835
- query = query.where(SystemSchema.workflow_status.c.status == input.status)
839
+ query = query.where(SystemSchema.workflow_status.c.status.in_(input.status))
836
840
  if input.application_version:
837
841
  query = query.where(
838
842
  SystemSchema.workflow_status.c.application_version
@@ -938,10 +942,9 @@ class SystemDatabase:
938
942
  SystemSchema.workflow_status.c.queue_name == input["queue_name"]
939
943
  )
940
944
 
941
- if input.get("status"):
942
- query = query.where(
943
- SystemSchema.workflow_status.c.status == input["status"]
944
- )
945
+ status = input.get("status", None)
946
+ if status:
947
+ query = query.where(SystemSchema.workflow_status.c.status.in_(status))
945
948
  if "start_time" in input and input["start_time"] is not None:
946
949
  query = query.where(
947
950
  SystemSchema.workflow_status.c.created_at
@@ -1,7 +1,7 @@
1
1
  import time
2
2
  import uuid
3
3
  from datetime import datetime
4
- from typing import TYPE_CHECKING, List, Optional
4
+ from typing import TYPE_CHECKING, List, Optional, Union
5
5
 
6
6
  from dbos._context import get_local_dbos_context
7
7
 
@@ -23,7 +23,7 @@ def list_workflows(
23
23
  sys_db: SystemDatabase,
24
24
  *,
25
25
  workflow_ids: Optional[List[str]] = None,
26
- status: Optional[str] = None,
26
+ status: Optional[Union[str, List[str]]] = None,
27
27
  start_time: Optional[str] = None,
28
28
  end_time: Optional[str] = None,
29
29
  name: Optional[str] = None,
@@ -39,7 +39,7 @@ def list_workflows(
39
39
  input.authenticated_user = user
40
40
  input.start_time = start_time
41
41
  input.end_time = end_time
42
- input.status = status
42
+ input.status = status if status is None or isinstance(status, list) else [status]
43
43
  input.application_version = app_version
44
44
  input.limit = limit
45
45
  input.name = name
@@ -56,7 +56,7 @@ def list_queued_workflows(
56
56
  sys_db: SystemDatabase,
57
57
  *,
58
58
  queue_name: Optional[str] = None,
59
- status: Optional[str] = None,
59
+ status: Optional[Union[str, List[str]]] = None,
60
60
  start_time: Optional[str] = None,
61
61
  end_time: Optional[str] = None,
62
62
  name: Optional[str] = None,
@@ -68,7 +68,7 @@ def list_queued_workflows(
68
68
  "queue_name": queue_name,
69
69
  "start_time": start_time,
70
70
  "end_time": end_time,
71
- "status": status,
71
+ "status": status if status is None or isinstance(status, list) else [status],
72
72
  "limit": limit,
73
73
  "name": name,
74
74
  "offset": offset,
@@ -27,7 +27,7 @@ dependencies = [
27
27
  ]
28
28
  requires-python = ">=3.9"
29
29
  readme = "README.md"
30
- version = "1.5.0a10"
30
+ version = "1.6.0a3"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -1515,3 +1515,44 @@ def test_complex_type(dbos: DBOS) -> None:
1515
1515
  event.set()
1516
1516
  check(handle.get_result())
1517
1517
  check(recovery_handle.get_result())
1518
+
1519
+
1520
+ def test_enqueue_version(dbos: DBOS) -> None:
1521
+
1522
+ @DBOS.workflow()
1523
+ def workflow(x: int) -> int:
1524
+ return x
1525
+
1526
+ queue = Queue("queue")
1527
+ input = 5
1528
+
1529
+ # Enqueue the app on a different version, verify it has that version
1530
+ future_version = str(uuid.uuid4())
1531
+ with SetEnqueueOptions(app_version=future_version):
1532
+ handle = queue.enqueue(workflow, input)
1533
+ assert handle.get_status().app_version == future_version
1534
+
1535
+ # Change the global version, verify it works
1536
+ GlobalParams.app_version = future_version
1537
+ assert handle.get_result() == input
1538
+
1539
+
1540
+ @pytest.mark.asyncio
1541
+ async def test_enqueue_version_async(dbos: DBOS) -> None:
1542
+
1543
+ @DBOS.workflow()
1544
+ async def workflow(x: int) -> int:
1545
+ return x
1546
+
1547
+ queue = Queue("queue")
1548
+ input = 5
1549
+
1550
+ # Enqueue the app on a different version, verify it has that version
1551
+ future_version = str(uuid.uuid4())
1552
+ with SetEnqueueOptions(app_version=future_version):
1553
+ handle = await queue.enqueue_async(workflow, input)
1554
+ assert (await handle.get_status()).app_version == future_version
1555
+
1556
+ # Change the global version, verify it works
1557
+ GlobalParams.app_version = future_version
1558
+ assert await handle.get_result() == input
@@ -49,6 +49,8 @@ def test_list_workflow(dbos: DBOS) -> None:
49
49
  assert len(outputs) == 0
50
50
  outputs = DBOS.list_workflows(status="SUCCESS")
51
51
  assert len(outputs) == 1
52
+ outputs = DBOS.list_workflows(status=["SUCCESS", "PENDING"])
53
+ assert len(outputs) == 1
52
54
 
53
55
  # Test searching by workflow name
54
56
  outputs = DBOS.list_workflows(name="no")
@@ -258,6 +260,8 @@ def test_queued_workflows(dbos: DBOS) -> None:
258
260
  assert len(workflows) == queued_steps
259
261
  workflows = DBOS.list_queued_workflows(status=WorkflowStatusString.ENQUEUED.value)
260
262
  assert len(workflows) == 0
263
+ workflows = DBOS.list_queued_workflows(status=["ENQUEUED", "PENDING"])
264
+ assert len(workflows) == queued_steps
261
265
  workflows = DBOS.list_queued_workflows(queue_name=queue.name)
262
266
  assert len(workflows) == queued_steps
263
267
  workflows = DBOS.list_queued_workflows(queue_name="no")
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