dbos 0.27.0a8__tar.gz → 0.27.0a9__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.

Potentially problematic release.


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

Files changed (105) hide show
  1. {dbos-0.27.0a8 → dbos-0.27.0a9}/PKG-INFO +1 -1
  2. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_client.py +30 -9
  3. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_dbos.py +8 -1
  4. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_sys_db.py +17 -6
  5. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_workflow_commands.py +15 -2
  6. {dbos-0.27.0a8 → dbos-0.27.0a9}/pyproject.toml +1 -1
  7. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_client.py +8 -3
  8. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_workflow_management.py +66 -25
  9. {dbos-0.27.0a8 → dbos-0.27.0a9}/LICENSE +0 -0
  10. {dbos-0.27.0a8 → dbos-0.27.0a9}/README.md +0 -0
  11. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/__init__.py +0 -0
  12. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/__main__.py +0 -0
  13. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_admin_server.py +0 -0
  14. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_app_db.py +0 -0
  15. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_classproperty.py +0 -0
  16. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_conductor/conductor.py +0 -0
  17. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_conductor/protocol.py +0 -0
  18. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_context.py +0 -0
  19. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_core.py +0 -0
  20. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_croniter.py +0 -0
  21. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_dbos_config.py +0 -0
  22. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_debug.py +0 -0
  23. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_docker_pg_helper.py +0 -0
  24. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_error.py +0 -0
  25. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_event_loop.py +0 -0
  26. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_fastapi.py +0 -0
  27. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_flask.py +0 -0
  28. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_kafka.py +0 -0
  29. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_kafka_message.py +0 -0
  30. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_logger.py +0 -0
  31. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/env.py +0 -0
  32. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/script.py.mako +0 -0
  33. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  34. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  35. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  36. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  37. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  38. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  39. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  40. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  41. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  42. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  43. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_outcome.py +0 -0
  44. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_queue.py +0 -0
  45. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_recovery.py +0 -0
  46. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_registrations.py +0 -0
  47. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_request.py +0 -0
  48. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_roles.py +0 -0
  49. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_scheduler.py +0 -0
  50. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_schemas/__init__.py +0 -0
  51. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_schemas/application_database.py +0 -0
  52. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_schemas/system_database.py +0 -0
  53. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_serialization.py +0 -0
  54. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/README.md +0 -0
  55. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  56. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
  57. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  58. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  59. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  60. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  61. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  62. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  63. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  64. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_tracer.py +0 -0
  65. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/_utils.py +0 -0
  66. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/cli/_github_init.py +0 -0
  67. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/cli/_template_init.py +0 -0
  68. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/cli/cli.py +0 -0
  69. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/dbos-config.schema.json +0 -0
  70. {dbos-0.27.0a8 → dbos-0.27.0a9}/dbos/py.typed +0 -0
  71. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/__init__.py +0 -0
  72. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/atexit_no_ctor.py +0 -0
  73. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/atexit_no_launch.py +0 -0
  74. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/classdefs.py +0 -0
  75. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/client_collateral.py +0 -0
  76. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/client_worker.py +0 -0
  77. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/conftest.py +0 -0
  78. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/dupname_classdefs1.py +0 -0
  79. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/dupname_classdefsa.py +0 -0
  80. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/more_classdefs.py +0 -0
  81. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/queuedworkflow.py +0 -0
  82. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_admin_server.py +0 -0
  83. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_async.py +0 -0
  84. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_classdecorators.py +0 -0
  85. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_concurrency.py +0 -0
  86. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_config.py +0 -0
  87. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_croniter.py +0 -0
  88. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_dbos.py +0 -0
  89. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_debug.py +0 -0
  90. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_docker_secrets.py +0 -0
  91. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_failures.py +0 -0
  92. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_fastapi.py +0 -0
  93. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_fastapi_roles.py +0 -0
  94. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_flask.py +0 -0
  95. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_kafka.py +0 -0
  96. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_outcome.py +0 -0
  97. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_package.py +0 -0
  98. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_queue.py +0 -0
  99. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_scheduler.py +0 -0
  100. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_schema_migration.py +0 -0
  101. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_singleton.py +0 -0
  102. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_spans.py +0 -0
  103. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_sqlalchemy.py +0 -0
  104. {dbos-0.27.0a8 → dbos-0.27.0a9}/tests/test_workflow_introspection.py +0 -0
  105. {dbos-0.27.0a8 → dbos-0.27.0a9}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.27.0a8
3
+ Version: 0.27.0a9
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -243,11 +243,13 @@ class DBOSClient:
243
243
  async def cancel_workflow_async(self, workflow_id: str) -> None:
244
244
  await asyncio.to_thread(self.cancel_workflow, workflow_id)
245
245
 
246
- def resume_workflow(self, workflow_id: str) -> None:
246
+ def resume_workflow(self, workflow_id: str) -> WorkflowHandle[Any]:
247
247
  self._sys_db.resume_workflow(workflow_id)
248
+ return WorkflowHandleClientPolling[Any](workflow_id, self._sys_db)
248
249
 
249
- async def resume_workflow_async(self, workflow_id: str) -> None:
250
+ async def resume_workflow_async(self, workflow_id: str) -> WorkflowHandleAsync[Any]:
250
251
  await asyncio.to_thread(self.resume_workflow, workflow_id)
252
+ return WorkflowHandleClientAsyncPolling[Any](workflow_id, self._sys_db)
251
253
 
252
254
  def list_workflows(
253
255
  self,
@@ -361,16 +363,35 @@ class DBOSClient:
361
363
  async def list_workflow_steps_async(self, workflow_id: str) -> List[StepInfo]:
362
364
  return await asyncio.to_thread(self.list_workflow_steps, workflow_id)
363
365
 
364
- def fork_workflow(self, workflow_id: str, start_step: int) -> WorkflowHandle[R]:
366
+ def fork_workflow(
367
+ self,
368
+ workflow_id: str,
369
+ start_step: int,
370
+ *,
371
+ application_version: Optional[str] = None,
372
+ ) -> WorkflowHandle[Any]:
365
373
  forked_workflow_id = fork_workflow(
366
- self._sys_db, self._app_db, workflow_id, start_step
374
+ self._sys_db,
375
+ self._app_db,
376
+ workflow_id,
377
+ start_step,
378
+ application_version=application_version,
367
379
  )
368
- return WorkflowHandleClientPolling[R](forked_workflow_id, self._sys_db)
380
+ return WorkflowHandleClientPolling[Any](forked_workflow_id, self._sys_db)
369
381
 
370
382
  async def fork_workflow_async(
371
- self, workflow_id: str, start_step: int
372
- ) -> WorkflowHandleAsync[R]:
383
+ self,
384
+ workflow_id: str,
385
+ start_step: int,
386
+ *,
387
+ application_version: Optional[str] = None,
388
+ ) -> WorkflowHandleAsync[Any]:
373
389
  forked_workflow_id = await asyncio.to_thread(
374
- fork_workflow, self._sys_db, self._app_db, workflow_id, start_step
390
+ fork_workflow,
391
+ self._sys_db,
392
+ self._app_db,
393
+ workflow_id,
394
+ start_step,
395
+ application_version=application_version,
375
396
  )
376
- return WorkflowHandleClientAsyncPolling[R](forked_workflow_id, self._sys_db)
397
+ return WorkflowHandleClientAsyncPolling[Any](forked_workflow_id, self._sys_db)
@@ -978,7 +978,13 @@ class DBOS:
978
978
  return cls.fork_workflow(workflow_id, 1)
979
979
 
980
980
  @classmethod
981
- def fork_workflow(cls, workflow_id: str, start_step: int) -> WorkflowHandle[Any]:
981
+ def fork_workflow(
982
+ cls,
983
+ workflow_id: str,
984
+ start_step: int,
985
+ *,
986
+ application_version: Optional[str] = None,
987
+ ) -> WorkflowHandle[Any]:
982
988
  """Restart a workflow with a new workflow ID from a specific step"""
983
989
 
984
990
  def fn() -> str:
@@ -988,6 +994,7 @@ class DBOS:
988
994
  _get_dbos_instance()._app_db,
989
995
  workflow_id,
990
996
  start_step,
997
+ application_version=application_version,
991
998
  )
992
999
 
993
1000
  new_id = _get_dbos_instance()._sys_db.call_function_as_step(
@@ -544,15 +544,17 @@ class SystemDatabase:
544
544
  # Execute with snapshot isolation in case of concurrent calls on the same workflow
545
545
  c.execute(sa.text("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"))
546
546
  # Check the status of the workflow. If it is complete, do nothing.
547
- row = c.execute(
547
+ status_row = c.execute(
548
548
  sa.select(
549
549
  SystemSchema.workflow_status.c.status,
550
550
  ).where(SystemSchema.workflow_status.c.workflow_uuid == workflow_id)
551
551
  ).fetchone()
552
+ if status_row is None:
553
+ return
554
+ status = status_row[0]
552
555
  if (
553
- row is None
554
- or row[0] == WorkflowStatusString.SUCCESS.value
555
- or row[0] == WorkflowStatusString.ERROR.value
556
+ status == WorkflowStatusString.SUCCESS.value
557
+ or status == WorkflowStatusString.ERROR.value
556
558
  ):
557
559
  return
558
560
  # Remove the workflow from the queues table so resume can safely be called on an ENQUEUED workflow
@@ -592,7 +594,12 @@ class SystemDatabase:
592
594
  return max_function_id
593
595
 
594
596
  def fork_workflow(
595
- self, original_workflow_id: str, forked_workflow_id: str, start_step: int = 1
597
+ self,
598
+ original_workflow_id: str,
599
+ forked_workflow_id: str,
600
+ start_step: int,
601
+ *,
602
+ application_version: Optional[str],
596
603
  ) -> str:
597
604
 
598
605
  status = self.get_workflow_status(original_workflow_id)
@@ -612,7 +619,11 @@ class SystemDatabase:
612
619
  name=status["name"],
613
620
  class_name=status["class_name"],
614
621
  config_name=status["config_name"],
615
- application_version=status["app_version"],
622
+ application_version=(
623
+ application_version
624
+ if application_version is not None
625
+ else status["app_version"]
626
+ ),
616
627
  application_id=status["app_id"],
617
628
  request=status["request"],
618
629
  authenticated_user=status["authenticated_user"],
@@ -1,6 +1,7 @@
1
1
  import uuid
2
2
  from typing import List, Optional
3
3
 
4
+ from dbos._context import get_local_dbos_context
4
5
  from dbos._error import DBOSException
5
6
 
6
7
  from ._app_db import ApplicationDatabase
@@ -103,6 +104,8 @@ def fork_workflow(
103
104
  app_db: ApplicationDatabase,
104
105
  workflow_id: str,
105
106
  start_step: int,
107
+ *,
108
+ application_version: Optional[str],
106
109
  ) -> str:
107
110
  def get_max_function_id(workflow_uuid: str) -> int:
108
111
  max_transactions = app_db.get_max_function_id(workflow_uuid) or 0
@@ -114,7 +117,17 @@ def fork_workflow(
114
117
  raise DBOSException(
115
118
  f"Cannot fork workflow {workflow_id} from step {start_step}. The workflow has {max_function_id} steps."
116
119
  )
117
- forked_workflow_id = str(uuid.uuid4())
120
+ ctx = get_local_dbos_context()
121
+ if ctx is not None and len(ctx.id_assigned_for_next_workflow) > 0:
122
+ forked_workflow_id = ctx.id_assigned_for_next_workflow
123
+ ctx.id_assigned_for_next_workflow = ""
124
+ else:
125
+ forked_workflow_id = str(uuid.uuid4())
118
126
  app_db.clone_workflow_transactions(workflow_id, forked_workflow_id, start_step)
119
- sys_db.fork_workflow(workflow_id, forked_workflow_id, start_step)
127
+ sys_db.fork_workflow(
128
+ workflow_id,
129
+ forked_workflow_id,
130
+ start_step,
131
+ application_version=application_version,
132
+ )
120
133
  return forked_workflow_id
@@ -28,7 +28,7 @@ dependencies = [
28
28
  ]
29
29
  requires-python = ">=3.9"
30
30
  readme = "README.md"
31
- version = "0.27.0a8"
31
+ version = "0.27.0a9"
32
32
 
33
33
  [project.license]
34
34
  text = "MIT"
@@ -439,12 +439,17 @@ def test_client_fork(dbos: DBOS, client: DBOSClient) -> None:
439
439
  assert handle.get_result() == input * 2
440
440
  assert len(client.list_workflow_steps(handle.workflow_id)) == 2
441
441
 
442
- forked_handle: WorkflowHandle[int] = client.fork_workflow(handle.workflow_id, 1)
443
- assert forked_handle.workflow_id != handle.workflow_id
442
+ fork_id = str(uuid.uuid4())
443
+ with SetWorkflowID(fork_id):
444
+ forked_handle: WorkflowHandle[int] = client.fork_workflow(handle.workflow_id, 1)
445
+ assert forked_handle.workflow_id == fork_id
444
446
  assert forked_handle.get_result() == input * 2
445
447
 
446
448
  forked_handle = client.fork_workflow(handle.workflow_id, 2)
447
- assert forked_handle.workflow_id != handle.workflow_id
449
+ assert (
450
+ forked_handle.workflow_id != handle.workflow_id
451
+ and forked_handle.workflow_id != fork_id
452
+ )
448
453
  assert forked_handle.get_result() == input * 2
449
454
 
450
455
  assert len(client.list_workflows()) == 3
@@ -8,7 +8,7 @@ import pytest
8
8
  from dbos import DBOS, Queue, SetWorkflowID
9
9
  from dbos._dbos import DBOSConfiguredInstance
10
10
  from dbos._error import DBOSException, DBOSWorkflowCancelledError
11
- from dbos._utils import INTERNAL_QUEUE_NAME
11
+ from dbos._utils import INTERNAL_QUEUE_NAME, GlobalParams
12
12
  from tests.conftest import queue_entries_are_cleaned_up
13
13
 
14
14
 
@@ -54,6 +54,7 @@ def test_cancel_resume(dbos: DBOS) -> None:
54
54
 
55
55
  # Resume the workflow. Verify it completes successfully.
56
56
  handle = DBOS.resume_workflow(wfid)
57
+ assert handle.get_status().app_version == GlobalParams.app_version
57
58
  assert handle.get_result() == input
58
59
  assert steps_completed == 2
59
60
 
@@ -218,7 +219,7 @@ def test_restart(dbos: DBOS) -> None:
218
219
  assert queue_entries_are_cleaned_up(dbos)
219
220
 
220
221
 
221
- def test_restart_fromsteps_stepsonly(
222
+ def test_fork_steps(
222
223
  dbos: DBOS,
223
224
  ) -> None:
224
225
 
@@ -229,47 +230,45 @@ def test_restart_fromsteps_stepsonly(
229
230
  stepFiveCount = 0
230
231
 
231
232
  @DBOS.workflow()
232
- def simple_workflow() -> None:
233
- stepOne()
234
- stepTwo()
235
- stepThree()
236
- stepFour()
237
- stepFive()
238
- return
233
+ def simple_workflow(x: int) -> int:
234
+ return stepOne(x) + stepTwo(x) + stepThree(x) + stepFour(x) + stepFive(x)
239
235
 
240
236
  @DBOS.step()
241
- def stepOne() -> None:
237
+ def stepOne(x: int) -> int:
242
238
  nonlocal stepOneCount
243
239
  stepOneCount += 1
244
- return
240
+ return x + 1
245
241
 
246
242
  @DBOS.step()
247
- def stepTwo() -> None:
243
+ def stepTwo(x: int) -> int:
248
244
  nonlocal stepTwoCount
249
245
  stepTwoCount += 1
250
- return
246
+ return x + 2
251
247
 
252
248
  @DBOS.step()
253
- def stepThree() -> None:
249
+ def stepThree(x: int) -> int:
254
250
  nonlocal stepThreeCount
255
251
  stepThreeCount += 1
256
- return
252
+ return x + 3
257
253
 
258
254
  @DBOS.step()
259
- def stepFour() -> None:
255
+ def stepFour(x: int) -> int:
260
256
  nonlocal stepFourCount
261
257
  stepFourCount += 1
262
- return
258
+ return x + 4
263
259
 
264
260
  @DBOS.step()
265
- def stepFive() -> None:
261
+ def stepFive(x: int) -> int:
266
262
  nonlocal stepFiveCount
267
263
  stepFiveCount += 1
268
- return
264
+ return x + 5
265
+
266
+ input = 1
267
+ output = 5 * input + 15
269
268
 
270
269
  wfid = str(uuid.uuid4())
271
270
  with SetWorkflowID(wfid):
272
- simple_workflow()
271
+ assert simple_workflow(input) == output
273
272
 
274
273
  assert stepOneCount == 1
275
274
  assert stepTwoCount == 1
@@ -277,9 +276,12 @@ def test_restart_fromsteps_stepsonly(
277
276
  assert stepFourCount == 1
278
277
  assert stepFiveCount == 1
279
278
 
280
- forked_handle = DBOS.fork_workflow(wfid, 3)
281
- assert forked_handle.workflow_id != wfid
282
- forked_handle.get_result()
279
+ fork_id = str(uuid.uuid4())
280
+ with SetWorkflowID(fork_id):
281
+ forked_handle = DBOS.fork_workflow(wfid, 3)
282
+ assert forked_handle.workflow_id == fork_id
283
+ assert forked_handle.get_status().app_version == GlobalParams.app_version
284
+ assert forked_handle.get_result() == output
283
285
 
284
286
  assert stepOneCount == 1
285
287
  assert stepTwoCount == 1
@@ -289,7 +291,7 @@ def test_restart_fromsteps_stepsonly(
289
291
 
290
292
  forked_handle = DBOS.fork_workflow(wfid, 5)
291
293
  assert forked_handle.workflow_id != wfid
292
- forked_handle.get_result()
294
+ assert forked_handle.get_result() == output
293
295
 
294
296
  assert stepOneCount == 1
295
297
  assert stepTwoCount == 1
@@ -299,7 +301,7 @@ def test_restart_fromsteps_stepsonly(
299
301
 
300
302
  forked_handle = DBOS.fork_workflow(wfid, 1)
301
303
  assert forked_handle.workflow_id != wfid
302
- forked_handle.get_result()
304
+ assert forked_handle.get_result() == output
303
305
 
304
306
  assert stepOneCount == 2
305
307
  assert stepTwoCount == 2
@@ -589,3 +591,42 @@ def test_restart_fromsteps_childwf(
589
591
  assert stepOneCount == 2
590
592
  assert childwfCount == 3
591
593
  assert stepThreeCount == 4
594
+
595
+
596
+ def test_fork_version(
597
+ dbos: DBOS,
598
+ ) -> None:
599
+
600
+ stepOneCount = 0
601
+ stepTwoCount = 0
602
+
603
+ @DBOS.workflow()
604
+ def simple_workflow(x: int) -> int:
605
+ return stepOne(x) + stepTwo(x)
606
+
607
+ @DBOS.step()
608
+ def stepOne(x: int) -> int:
609
+ nonlocal stepOneCount
610
+ stepOneCount += 1
611
+ return x + 1
612
+
613
+ @DBOS.step()
614
+ def stepTwo(x: int) -> int:
615
+ nonlocal stepTwoCount
616
+ stepTwoCount += 1
617
+ return x + 2
618
+
619
+ input = 1
620
+ output = 2 * input + 3
621
+
622
+ workflow_id = str(uuid.uuid4())
623
+ with SetWorkflowID(workflow_id):
624
+ assert simple_workflow(input) == output
625
+
626
+ # Fork the workflow with a different version. Verify it is set to that version.
627
+ new_version = "my_new_version"
628
+ handle = DBOS.fork_workflow(workflow_id, 2, application_version=new_version)
629
+ assert handle.get_status().app_version == new_version
630
+ # Set the global version to this new version, verify the workflow completes
631
+ GlobalParams.app_version = new_version
632
+ assert handle.get_result() == output
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