dbos 0.11.0a3__py3-none-any.whl → 0.13.0a0__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.

Files changed (52) hide show
  1. dbos/__init__.py +7 -7
  2. dbos/{admin_sever.py → _admin_sever.py} +20 -11
  3. dbos/{application_database.py → _app_db.py} +4 -5
  4. dbos/{decorators.py → _classproperty.py} +3 -3
  5. dbos/{context.py → _context.py} +26 -26
  6. dbos/{core.py → _core.py} +121 -107
  7. dbos/{dbos.py → _dbos.py} +57 -59
  8. dbos/{dbos_config.py → _dbos_config.py} +9 -9
  9. dbos/{fastapi.py → _fastapi.py} +10 -11
  10. dbos/{flask.py → _flask.py} +6 -7
  11. dbos/{kafka.py → _kafka.py} +18 -18
  12. dbos/{logger.py → _logger.py} +13 -13
  13. dbos/{queue.py → _queue.py} +7 -7
  14. dbos/{recovery.py → _recovery.py} +8 -8
  15. dbos/{roles.py → _roles.py} +5 -5
  16. dbos/{scheduler/scheduler.py → _scheduler.py} +17 -7
  17. dbos/{utils.py → _serialization.py} +4 -4
  18. dbos/{system_database.py → _sys_db.py} +42 -37
  19. dbos/{tracer.py → _tracer.py} +2 -2
  20. dbos/cli.py +21 -21
  21. {dbos-0.11.0a3.dist-info → dbos-0.13.0a0.dist-info}/METADATA +2 -2
  22. dbos-0.13.0a0.dist-info/RECORD +54 -0
  23. {dbos-0.11.0a3.dist-info → dbos-0.13.0a0.dist-info}/WHEEL +1 -1
  24. dbos-0.11.0a3.dist-info/RECORD +0 -54
  25. /dbos/{scheduler/croniter.py → _croniter.py} +0 -0
  26. /dbos/{error.py → _error.py} +0 -0
  27. /dbos/{kafka_message.py → _kafka_message.py} +0 -0
  28. /dbos/{migrations → _migrations}/env.py +0 -0
  29. /dbos/{migrations → _migrations}/script.py.mako +0 -0
  30. /dbos/{migrations → _migrations}/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  31. /dbos/{migrations → _migrations}/versions/5c361fc04708_added_system_tables.py +0 -0
  32. /dbos/{migrations → _migrations}/versions/a3b18ad34abe_added_triggers.py +0 -0
  33. /dbos/{migrations → _migrations}/versions/d76646551a6b_job_queue_limiter.py +0 -0
  34. /dbos/{migrations → _migrations}/versions/d76646551a6c_workflow_queue.py +0 -0
  35. /dbos/{migrations → _migrations}/versions/eab0cc1d9a14_job_queue.py +0 -0
  36. /dbos/{registrations.py → _registrations.py} +0 -0
  37. /dbos/{request.py → _request.py} +0 -0
  38. /dbos/{schemas → _schemas}/__init__.py +0 -0
  39. /dbos/{schemas → _schemas}/application_database.py +0 -0
  40. /dbos/{schemas → _schemas}/system_database.py +0 -0
  41. /dbos/{templates → _templates}/hello/README.md +0 -0
  42. /dbos/{templates → _templates}/hello/__package/__init__.py +0 -0
  43. /dbos/{templates → _templates}/hello/__package/main.py +0 -0
  44. /dbos/{templates → _templates}/hello/__package/schema.py +0 -0
  45. /dbos/{templates → _templates}/hello/alembic.ini +0 -0
  46. /dbos/{templates → _templates}/hello/dbos-config.yaml.dbos +0 -0
  47. /dbos/{templates → _templates}/hello/migrations/env.py.dbos +0 -0
  48. /dbos/{templates → _templates}/hello/migrations/script.py.mako +0 -0
  49. /dbos/{templates → _templates}/hello/migrations/versions/2024_07_31_180642_init.py +0 -0
  50. /dbos/{templates → _templates}/hello/start_postgres_docker.py +0 -0
  51. {dbos-0.11.0a3.dist-info → dbos-0.13.0a0.dist-info}/entry_points.txt +0 -0
  52. {dbos-0.11.0a3.dist-info → dbos-0.13.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -6,15 +6,15 @@ from concurrent.futures import Future
6
6
  from functools import wraps
7
7
  from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, Tuple, TypeVar, cast
8
8
 
9
- from dbos.application_database import ApplicationDatabase, TransactionResultInternal
9
+ from ._app_db import ApplicationDatabase, TransactionResultInternal
10
10
 
11
11
  if sys.version_info < (3, 10):
12
12
  from typing_extensions import ParamSpec
13
13
  else:
14
14
  from typing import ParamSpec
15
15
 
16
- from dbos import utils
17
- from dbos.context import (
16
+ from . import _serialization
17
+ from ._context import (
18
18
  DBOSAssumeRole,
19
19
  DBOSContext,
20
20
  DBOSContextEnsure,
@@ -29,7 +29,7 @@ from dbos.context import (
29
29
  assert_current_dbos_context,
30
30
  get_local_dbos_context,
31
31
  )
32
- from dbos.error import (
32
+ from ._error import (
33
33
  DBOSException,
34
34
  DBOSMaxStepRetriesExceeded,
35
35
  DBOSNonExistentWorkflowError,
@@ -37,7 +37,7 @@ from dbos.error import (
37
37
  DBOSWorkflowConflictIDError,
38
38
  DBOSWorkflowFunctionNotFoundError,
39
39
  )
40
- from dbos.registrations import (
40
+ from ._registrations import (
41
41
  DEFAULT_MAX_RECOVERY_ATTEMPTS,
42
42
  get_config_name,
43
43
  get_dbos_class_name,
@@ -48,18 +48,24 @@ from dbos.registrations import (
48
48
  set_dbos_func_name,
49
49
  set_temp_workflow_type,
50
50
  )
51
- from dbos.roles import check_required_roles
52
- from dbos.system_database import (
51
+ from ._roles import check_required_roles
52
+ from ._serialization import WorkflowInputs
53
+ from ._sys_db import (
53
54
  GetEventWorkflowContext,
54
55
  OperationResultInternal,
55
56
  WorkflowStatusInternal,
56
57
  WorkflowStatusString,
57
58
  )
58
- from dbos.utils import WorkflowInputs
59
59
 
60
60
  if TYPE_CHECKING:
61
- from dbos.dbos import DBOS, Workflow, WorkflowHandle, WorkflowStatus, _DBOSRegistry
62
- from dbos.dbos import IsolationLevel
61
+ from ._dbos import (
62
+ DBOS,
63
+ Workflow,
64
+ WorkflowHandle,
65
+ WorkflowStatus,
66
+ DBOSRegistry,
67
+ IsolationLevel,
68
+ )
63
69
 
64
70
  from sqlalchemy.exc import DBAPIError
65
71
 
@@ -70,7 +76,7 @@ F = TypeVar("F", bound=Callable[..., Any])
70
76
  TEMP_SEND_WF_NAME = "<temp>.temp_send_workflow"
71
77
 
72
78
 
73
- class _WorkflowHandleFuture(Generic[R]):
79
+ class WorkflowHandleFuture(Generic[R]):
74
80
 
75
81
  def __init__(self, workflow_id: str, future: Future[R], dbos: "DBOS"):
76
82
  self.workflow_id = workflow_id
@@ -90,7 +96,7 @@ class _WorkflowHandleFuture(Generic[R]):
90
96
  return stat
91
97
 
92
98
 
93
- class _WorkflowHandlePolling(Generic[R]):
99
+ class WorkflowHandlePolling(Generic[R]):
94
100
 
95
101
  def __init__(self, workflow_id: str, dbos: "DBOS"):
96
102
  self.workflow_id = workflow_id
@@ -141,7 +147,9 @@ def _init_workflow(
141
147
  "app_id": ctx.app_id,
142
148
  "app_version": ctx.app_version,
143
149
  "executor_id": ctx.executor_id,
144
- "request": (utils.serialize(ctx.request) if ctx.request is not None else None),
150
+ "request": (
151
+ _serialization.serialize(ctx.request) if ctx.request is not None else None
152
+ ),
145
153
  "recovery_attempts": None,
146
154
  "authenticated_user": ctx.authenticated_user,
147
155
  "authenticated_roles": (
@@ -162,10 +170,10 @@ def _init_workflow(
162
170
  dbos._sys_db.update_workflow_status(
163
171
  status, False, ctx.in_recovery, max_recovery_attempts=max_recovery_attempts
164
172
  )
165
- dbos._sys_db.update_workflow_inputs(wfid, utils.serialize_args(inputs))
173
+ dbos._sys_db.update_workflow_inputs(wfid, _serialization.serialize_args(inputs))
166
174
  else:
167
175
  # Buffer the inputs for single-transaction workflows, but don't buffer the status
168
- dbos._sys_db.buffer_workflow_inputs(wfid, utils.serialize_args(inputs))
176
+ dbos._sys_db.buffer_workflow_inputs(wfid, _serialization.serialize_args(inputs))
169
177
 
170
178
  if queue is not None:
171
179
  dbos._sys_db.enqueue(wfid, queue)
@@ -183,7 +191,7 @@ def _execute_workflow(
183
191
  try:
184
192
  output = func(*args, **kwargs)
185
193
  status["status"] = "SUCCESS"
186
- status["output"] = utils.serialize(output)
194
+ status["output"] = _serialization.serialize(output)
187
195
  if status["queue_name"] is not None:
188
196
  queue = dbos._registry.queue_info_map[status["queue_name"]]
189
197
  dbos._sys_db.remove_from_queue(status["workflow_uuid"], queue)
@@ -198,7 +206,7 @@ def _execute_workflow(
198
206
  return output
199
207
  except Exception as error:
200
208
  status["status"] = "ERROR"
201
- status["error"] = utils.serialize_exception(error)
209
+ status["error"] = _serialization.serialize_exception(error)
202
210
  if status["queue_name"] is not None:
203
211
  queue = dbos._registry.queue_info_map[status["queue_name"]]
204
212
  dbos._sys_db.remove_from_queue(status["workflow_uuid"], queue)
@@ -231,7 +239,7 @@ def _execute_workflow_wthread(
231
239
  raise
232
240
 
233
241
 
234
- def _execute_workflow_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]":
242
+ def execute_workflow_by_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]":
235
243
  status = dbos._sys_db.get_workflow_status(workflow_id)
236
244
  if not status:
237
245
  raise DBOSRecoveryError(workflow_id, "Workflow status not found")
@@ -246,7 +254,9 @@ def _execute_workflow_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]
246
254
  with DBOSContextEnsure():
247
255
  ctx = assert_current_dbos_context()
248
256
  request = status["request"]
249
- ctx.request = utils.deserialize(request) if request is not None else None
257
+ ctx.request = (
258
+ _serialization.deserialize(request) if request is not None else None
259
+ )
250
260
  if status["config_name"] is not None:
251
261
  config_name = status["config_name"]
252
262
  class_name = status["class_name"]
@@ -257,7 +267,7 @@ def _execute_workflow_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]
257
267
  f"Cannot execute workflow because instance '{iname}' is not registered",
258
268
  )
259
269
  with SetWorkflowID(workflow_id):
260
- return _start_workflow(
270
+ return start_workflow(
261
271
  dbos,
262
272
  wf_func,
263
273
  status["queue_name"],
@@ -274,7 +284,7 @@ def _execute_workflow_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]
274
284
  f"Cannot execute workflow because class '{class_name}' is not registered",
275
285
  )
276
286
  with SetWorkflowID(workflow_id):
277
- return _start_workflow(
287
+ return start_workflow(
278
288
  dbos,
279
289
  wf_func,
280
290
  status["queue_name"],
@@ -285,7 +295,7 @@ def _execute_workflow_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]
285
295
  )
286
296
  else:
287
297
  with SetWorkflowID(workflow_id):
288
- return _start_workflow(
298
+ return start_workflow(
289
299
  dbos,
290
300
  wf_func,
291
301
  status["queue_name"],
@@ -295,69 +305,7 @@ def _execute_workflow_id(dbos: "DBOS", workflow_id: str) -> "WorkflowHandle[Any]
295
305
  )
296
306
 
297
307
 
298
- def _workflow_wrapper(
299
- dbosreg: "_DBOSRegistry",
300
- func: F,
301
- max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
302
- ) -> F:
303
- func.__orig_func = func # type: ignore
304
-
305
- fi = get_or_create_func_info(func)
306
- fi.max_recovery_attempts = max_recovery_attempts
307
-
308
- @wraps(func)
309
- def wrapper(*args: Any, **kwargs: Any) -> Any:
310
- if dbosreg.dbos is None:
311
- raise DBOSException(
312
- f"Function {func.__name__} invoked before DBOS initialized"
313
- )
314
- dbos = dbosreg.dbos
315
-
316
- rr: Optional[str] = check_required_roles(func, fi)
317
- attributes: TracedAttributes = {
318
- "name": func.__name__,
319
- "operationType": OperationType.WORKFLOW.value,
320
- }
321
- inputs: WorkflowInputs = {
322
- "args": args,
323
- "kwargs": kwargs,
324
- }
325
- ctx = get_local_dbos_context()
326
- enterWorkflowCtxMgr = (
327
- EnterDBOSChildWorkflow if ctx and ctx.is_workflow() else EnterDBOSWorkflow
328
- )
329
- with enterWorkflowCtxMgr(attributes), DBOSAssumeRole(rr):
330
- ctx = assert_current_dbos_context() # Now the child ctx
331
- status = _init_workflow(
332
- dbos,
333
- ctx,
334
- inputs=inputs,
335
- wf_name=get_dbos_func_name(func),
336
- class_name=get_dbos_class_name(fi, func, args),
337
- config_name=get_config_name(fi, func, args),
338
- temp_wf_type=get_temp_workflow_type(func),
339
- max_recovery_attempts=max_recovery_attempts,
340
- )
341
-
342
- dbos.logger.debug(
343
- f"Running workflow, id: {ctx.workflow_id}, name: {get_dbos_func_name(func)}"
344
- )
345
- return _execute_workflow(dbos, status, func, *args, **kwargs)
346
-
347
- wrapped_func = cast(F, wrapper)
348
- return wrapped_func
349
-
350
-
351
- def _workflow(reg: "_DBOSRegistry", max_recovery_attempts: int) -> Callable[[F], F]:
352
- def _workflow_decorator(func: F) -> F:
353
- wrapped_func = _workflow_wrapper(reg, func, max_recovery_attempts)
354
- reg.register_wf_function(func.__qualname__, wrapped_func)
355
- return wrapped_func
356
-
357
- return _workflow_decorator
358
-
359
-
360
- def _start_workflow(
308
+ def start_workflow(
361
309
  dbos: "DBOS",
362
310
  func: "Workflow[P, R]",
363
311
  queue_name: Optional[str],
@@ -420,7 +368,7 @@ def _start_workflow(
420
368
  )
421
369
 
422
370
  if not execute_workflow:
423
- return _WorkflowHandlePolling(new_wf_id, dbos)
371
+ return WorkflowHandlePolling(new_wf_id, dbos)
424
372
 
425
373
  if fself is not None:
426
374
  future = dbos._executor.submit(
@@ -443,11 +391,75 @@ def _start_workflow(
443
391
  *args,
444
392
  **kwargs,
445
393
  )
446
- return _WorkflowHandleFuture(new_wf_id, future, dbos)
394
+ return WorkflowHandleFuture(new_wf_id, future, dbos)
447
395
 
448
396
 
449
- def _transaction(
450
- dbosreg: "_DBOSRegistry", isolation_level: "IsolationLevel" = "SERIALIZABLE"
397
+ def workflow_wrapper(
398
+ dbosreg: "DBOSRegistry",
399
+ func: F,
400
+ max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS,
401
+ ) -> F:
402
+ func.__orig_func = func # type: ignore
403
+
404
+ fi = get_or_create_func_info(func)
405
+ fi.max_recovery_attempts = max_recovery_attempts
406
+
407
+ @wraps(func)
408
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
409
+ if dbosreg.dbos is None:
410
+ raise DBOSException(
411
+ f"Function {func.__name__} invoked before DBOS initialized"
412
+ )
413
+ dbos = dbosreg.dbos
414
+
415
+ rr: Optional[str] = check_required_roles(func, fi)
416
+ attributes: TracedAttributes = {
417
+ "name": func.__name__,
418
+ "operationType": OperationType.WORKFLOW.value,
419
+ }
420
+ inputs: WorkflowInputs = {
421
+ "args": args,
422
+ "kwargs": kwargs,
423
+ }
424
+ ctx = get_local_dbos_context()
425
+ enterWorkflowCtxMgr = (
426
+ EnterDBOSChildWorkflow if ctx and ctx.is_workflow() else EnterDBOSWorkflow
427
+ )
428
+ with enterWorkflowCtxMgr(attributes), DBOSAssumeRole(rr):
429
+ ctx = assert_current_dbos_context() # Now the child ctx
430
+ status = _init_workflow(
431
+ dbos,
432
+ ctx,
433
+ inputs=inputs,
434
+ wf_name=get_dbos_func_name(func),
435
+ class_name=get_dbos_class_name(fi, func, args),
436
+ config_name=get_config_name(fi, func, args),
437
+ temp_wf_type=get_temp_workflow_type(func),
438
+ max_recovery_attempts=max_recovery_attempts,
439
+ )
440
+
441
+ dbos.logger.debug(
442
+ f"Running workflow, id: {ctx.workflow_id}, name: {get_dbos_func_name(func)}"
443
+ )
444
+ return _execute_workflow(dbos, status, func, *args, **kwargs)
445
+
446
+ wrapped_func = cast(F, wrapper)
447
+ return wrapped_func
448
+
449
+
450
+ def decorate_workflow(
451
+ reg: "DBOSRegistry", max_recovery_attempts: int
452
+ ) -> Callable[[F], F]:
453
+ def _workflow_decorator(func: F) -> F:
454
+ wrapped_func = workflow_wrapper(reg, func, max_recovery_attempts)
455
+ reg.register_wf_function(func.__qualname__, wrapped_func)
456
+ return wrapped_func
457
+
458
+ return _workflow_decorator
459
+
460
+
461
+ def decorate_transaction(
462
+ dbosreg: "DBOSRegistry", isolation_level: "IsolationLevel" = "SERIALIZABLE"
451
463
  ) -> Callable[[F], F]:
452
464
  def decorator(func: F) -> F:
453
465
  def invoke_tx(*args: Any, **kwargs: Any) -> Any:
@@ -498,14 +510,14 @@ def _transaction(
498
510
  )
499
511
  if recorded_output["error"]:
500
512
  deserialized_error = (
501
- utils.deserialize_exception(
513
+ _serialization.deserialize_exception(
502
514
  recorded_output["error"]
503
515
  )
504
516
  )
505
517
  has_recorded_error = True
506
518
  raise deserialized_error
507
519
  elif recorded_output["output"]:
508
- return utils.deserialize(
520
+ return _serialization.deserialize(
509
521
  recorded_output["output"]
510
522
  )
511
523
  else:
@@ -518,7 +530,7 @@ def _transaction(
518
530
  )
519
531
 
520
532
  output = func(*args, **kwargs)
521
- txn_output["output"] = utils.serialize(output)
533
+ txn_output["output"] = _serialization.serialize(output)
522
534
  assert (
523
535
  ctx.sql_session is not None
524
536
  ), "Cannot find a database connection"
@@ -543,7 +555,9 @@ def _transaction(
543
555
  except Exception as error:
544
556
  # Don't record the error if it was already recorded
545
557
  if not has_recorded_error:
546
- txn_output["error"] = utils.serialize_exception(error)
558
+ txn_output["error"] = (
559
+ _serialization.serialize_exception(error)
560
+ )
547
561
  dbos._app_db.record_transaction_error(txn_output)
548
562
  raise
549
563
  return output
@@ -571,7 +585,7 @@ def _transaction(
571
585
  def temp_wf(*args: Any, **kwargs: Any) -> Any:
572
586
  return wrapper(*args, **kwargs)
573
587
 
574
- wrapped_wf = _workflow_wrapper(dbosreg, temp_wf)
588
+ wrapped_wf = workflow_wrapper(dbosreg, temp_wf)
575
589
  set_dbos_func_name(temp_wf, "<temp>." + func.__qualname__)
576
590
  set_temp_workflow_type(temp_wf, "transaction")
577
591
  dbosreg.register_wf_function(get_dbos_func_name(temp_wf), wrapped_wf)
@@ -582,8 +596,8 @@ def _transaction(
582
596
  return decorator
583
597
 
584
598
 
585
- def _step(
586
- dbosreg: "_DBOSRegistry",
599
+ def decorate_step(
600
+ dbosreg: "DBOSRegistry",
587
601
  *,
588
602
  retries_allowed: bool = False,
589
603
  interval_seconds: float = 1.0,
@@ -618,12 +632,12 @@ def _step(
618
632
  f"Replaying step, id: {ctx.function_id}, name: {attributes['name']}"
619
633
  )
620
634
  if recorded_output["error"] is not None:
621
- deserialized_error = utils.deserialize_exception(
635
+ deserialized_error = _serialization.deserialize_exception(
622
636
  recorded_output["error"]
623
637
  )
624
638
  raise deserialized_error
625
639
  elif recorded_output["output"] is not None:
626
- return utils.deserialize(recorded_output["output"])
640
+ return _serialization.deserialize(recorded_output["output"])
627
641
  else:
628
642
  raise Exception("Output and error are both None")
629
643
  else:
@@ -639,7 +653,7 @@ def _step(
639
653
  for attempt in range(1, local_max_attempts + 1):
640
654
  try:
641
655
  output = func(*args, **kwargs)
642
- step_output["output"] = utils.serialize(output)
656
+ step_output["output"] = _serialization.serialize(output)
643
657
  error = None
644
658
  break
645
659
  except Exception as err:
@@ -665,7 +679,9 @@ def _step(
665
679
  )
666
680
 
667
681
  step_output["error"] = (
668
- utils.serialize_exception(error) if error is not None else None
682
+ _serialization.serialize_exception(error)
683
+ if error is not None
684
+ else None
669
685
  )
670
686
  dbos._sys_db.record_operation_result(step_output)
671
687
 
@@ -698,7 +714,7 @@ def _step(
698
714
  def temp_wf(*args: Any, **kwargs: Any) -> Any:
699
715
  return wrapper(*args, **kwargs)
700
716
 
701
- wrapped_wf = _workflow_wrapper(dbosreg, temp_wf)
717
+ wrapped_wf = workflow_wrapper(dbosreg, temp_wf)
702
718
  set_dbos_func_name(temp_wf, "<temp>." + func.__qualname__)
703
719
  set_temp_workflow_type(temp_wf, "step")
704
720
  dbosreg.register_wf_function(get_dbos_func_name(temp_wf), wrapped_wf)
@@ -709,7 +725,7 @@ def _step(
709
725
  return decorator
710
726
 
711
727
 
712
- def _send(
728
+ def send(
713
729
  dbos: "DBOS", destination_id: str, message: Any, topic: Optional[str] = None
714
730
  ) -> None:
715
731
  def do_send(destination_id: str, message: Any, topic: Optional[str]) -> None:
@@ -735,9 +751,7 @@ def _send(
735
751
  wffn(destination_id, message, topic)
736
752
 
737
753
 
738
- def _recv(
739
- dbos: "DBOS", topic: Optional[str] = None, timeout_seconds: float = 60
740
- ) -> Any:
754
+ def recv(dbos: "DBOS", topic: Optional[str] = None, timeout_seconds: float = 60) -> Any:
741
755
  cur_ctx = get_local_dbos_context()
742
756
  if cur_ctx is not None:
743
757
  # Must call it within a workflow
@@ -760,7 +774,7 @@ def _recv(
760
774
  raise DBOSException("recv() must be called from within a workflow")
761
775
 
762
776
 
763
- def _set_event(dbos: "DBOS", key: str, value: Any) -> None:
777
+ def set_event(dbos: "DBOS", key: str, value: Any) -> None:
764
778
  cur_ctx = get_local_dbos_context()
765
779
  if cur_ctx is not None:
766
780
  # Must call it within a workflow
@@ -779,7 +793,7 @@ def _set_event(dbos: "DBOS", key: str, value: Any) -> None:
779
793
  raise DBOSException("set_event() must be called from within a workflow")
780
794
 
781
795
 
782
- def _get_event(
796
+ def get_event(
783
797
  dbos: "DBOS", workflow_id: str, key: str, timeout_seconds: float = 60
784
798
  ) -> Any:
785
799
  cur_ctx = get_local_dbos_context()