dbos 0.16.0a2__py3-none-any.whl → 0.17.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
@@ -21,7 +21,7 @@ from typing import (
21
21
  overload,
22
22
  )
23
23
 
24
- from dbos._outcome import Immediate, Outcome, Pending
24
+ from dbos._outcome import Immediate, NoResult, Outcome, Pending
25
25
 
26
26
  from ._app_db import ApplicationDatabase, TransactionResultInternal
27
27
 
@@ -719,7 +719,7 @@ def decorate_step(
719
719
  finally:
720
720
  dbos._sys_db.record_operation_result(step_output)
721
721
 
722
- def check_existing_result() -> Optional[R]:
722
+ def check_existing_result() -> Union[NoResult, R]:
723
723
  ctx = assert_current_dbos_context()
724
724
  recorded_output = dbos._sys_db.check_operation_execution(
725
725
  ctx.workflow_id, ctx.function_id
@@ -743,7 +743,7 @@ def decorate_step(
743
743
  dbos.logger.debug(
744
744
  f"Running step, id: {ctx.function_id}, name: {attributes['name']}"
745
745
  )
746
- return None
746
+ return NoResult()
747
747
 
748
748
  stepOutcome = Outcome[R].make(functools.partial(func, *args, **kwargs))
749
749
  if retries_allowed:
dbos/_dbos.py CHANGED
@@ -45,7 +45,7 @@ from ._core import (
45
45
  start_workflow,
46
46
  workflow_wrapper,
47
47
  )
48
- from ._queue import Queue, _queue_thread
48
+ from ._queue import Queue, queue_thread
49
49
  from ._recovery import recover_pending_workflows, startup_recovery_thread
50
50
  from ._registrations import (
51
51
  DEFAULT_MAX_RECOVERY_ATTEMPTS,
@@ -283,6 +283,7 @@ class DBOS:
283
283
  self.flask: Optional["Flask"] = flask
284
284
  self._executor_field: Optional[ThreadPoolExecutor] = None
285
285
  self._background_threads: List[threading.Thread] = []
286
+ self._executor_id: str = os.environ.get("DBOS__VMID", "local")
286
287
 
287
288
  # If using FastAPI, set up middleware and lifecycle events
288
289
  if self.fastapi is not None:
@@ -383,7 +384,7 @@ class DBOS:
383
384
  evt = threading.Event()
384
385
  self.stop_events.append(evt)
385
386
  bg_queue_thread = threading.Thread(
386
- target=_queue_thread, args=(evt, self), daemon=True
387
+ target=queue_thread, args=(evt, self), daemon=True
387
388
  )
388
389
  bg_queue_thread.start()
389
390
  self._background_threads.append(bg_queue_thread)
dbos/_outcome.py CHANGED
@@ -8,6 +8,16 @@ T = TypeVar("T")
8
8
  R = TypeVar("R")
9
9
 
10
10
 
11
+ class NoResult:
12
+ _instance: Optional["NoResult"] = None
13
+ __slots__ = ()
14
+
15
+ def __new__(cls, *args: Any, **kwargs: Any) -> "NoResult":
16
+ if not cls._instance:
17
+ cls._instance = super(NoResult, cls).__new__(cls, *args, **kwargs)
18
+ return cls._instance
19
+
20
+
11
21
  # define Outcome protocol w/ common composition methods
12
22
  class Outcome(Protocol[T]):
13
23
 
@@ -28,7 +38,9 @@ class Outcome(Protocol[T]):
28
38
  exceeded_retries: Callable[[int], BaseException],
29
39
  ) -> "Outcome[T]": ...
30
40
 
31
- def intercept(self, interceptor: Callable[[], Optional[T]]) -> "Outcome[T]": ...
41
+ def intercept(
42
+ self, interceptor: Callable[[], Union[NoResult, T]]
43
+ ) -> "Outcome[T]": ...
32
44
 
33
45
  def __call__(self) -> Union[T, Coroutine[Any, Any, T]]: ...
34
46
 
@@ -58,11 +70,15 @@ class Immediate(Outcome[T]):
58
70
  return Immediate(lambda: before()(self._func))
59
71
 
60
72
  @staticmethod
61
- def _intercept(func: Callable[[], T], interceptor: Callable[[], Optional[T]]) -> T:
73
+ def _intercept(
74
+ func: Callable[[], T], interceptor: Callable[[], Union[NoResult, T]]
75
+ ) -> T:
62
76
  intercepted = interceptor()
63
- return intercepted if intercepted else func()
77
+ return intercepted if not isinstance(intercepted, NoResult) else func()
64
78
 
65
- def intercept(self, interceptor: Callable[[], Optional[T]]) -> "Immediate[T]":
79
+ def intercept(
80
+ self, interceptor: Callable[[], Union[NoResult, T]]
81
+ ) -> "Immediate[T]":
66
82
  return Immediate[T](lambda: Immediate._intercept(self._func, interceptor))
67
83
 
68
84
  @staticmethod
@@ -151,12 +167,12 @@ class Pending(Outcome[T]):
151
167
  @staticmethod
152
168
  async def _intercept(
153
169
  func: Callable[[], Coroutine[Any, Any, T]],
154
- interceptor: Callable[[], Optional[T]],
170
+ interceptor: Callable[[], Union[NoResult, T]],
155
171
  ) -> T:
156
172
  intercepted = await asyncio.to_thread(interceptor)
157
- return intercepted if intercepted else await func()
173
+ return intercepted if not isinstance(intercepted, NoResult) else await func()
158
174
 
159
- def intercept(self, interceptor: Callable[[], Optional[T]]) -> "Pending[T]":
175
+ def intercept(self, interceptor: Callable[[], Union[NoResult, T]]) -> "Pending[T]":
160
176
  return Pending[T](lambda: Pending._intercept(self._func, interceptor))
161
177
 
162
178
  @staticmethod
dbos/_queue.py CHANGED
@@ -51,13 +51,13 @@ class Queue:
51
51
  return start_workflow(dbos, func, self.name, False, *args, **kwargs)
52
52
 
53
53
 
54
- def _queue_thread(stop_event: threading.Event, dbos: "DBOS") -> None:
54
+ def queue_thread(stop_event: threading.Event, dbos: "DBOS") -> None:
55
55
  while not stop_event.is_set():
56
56
  if stop_event.wait(timeout=1):
57
57
  return
58
58
  for _, queue in dbos._registry.queue_info_map.items():
59
59
  try:
60
- wf_ids = dbos._sys_db.start_queued_workflows(queue)
60
+ wf_ids = dbos._sys_db.start_queued_workflows(queue, dbos._executor_id)
61
61
  for id in wf_ids:
62
62
  execute_workflow_by_id(dbos, id)
63
63
  except Exception:
dbos/_sys_db.py CHANGED
@@ -1104,7 +1104,7 @@ class SystemDatabase:
1104
1104
  .on_conflict_do_nothing()
1105
1105
  )
1106
1106
 
1107
- def start_queued_workflows(self, queue: "Queue") -> List[str]:
1107
+ def start_queued_workflows(self, queue: "Queue", executor_id: str) -> List[str]:
1108
1108
  start_time_ms = int(time.time() * 1000)
1109
1109
  if queue.limiter is not None:
1110
1110
  limiter_period_ms = int(queue.limiter["period"] * 1000)
@@ -1159,7 +1159,7 @@ class SystemDatabase:
1159
1159
  if len(ret_ids) + num_recent_queries >= queue.limiter["limit"]:
1160
1160
  break
1161
1161
 
1162
- # To start a function, first set its status to PENDING
1162
+ # To start a function, first set its status to PENDING and update its executor ID
1163
1163
  c.execute(
1164
1164
  SystemSchema.workflow_status.update()
1165
1165
  .where(SystemSchema.workflow_status.c.workflow_uuid == id)
@@ -1167,7 +1167,10 @@ class SystemDatabase:
1167
1167
  SystemSchema.workflow_status.c.status
1168
1168
  == WorkflowStatusString.ENQUEUED.value
1169
1169
  )
1170
- .values(status=WorkflowStatusString.PENDING.value)
1170
+ .values(
1171
+ status=WorkflowStatusString.PENDING.value,
1172
+ executor_id=executor_id,
1173
+ )
1171
1174
  )
1172
1175
 
1173
1176
  # Then give it a start time
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.16.0a2
3
+ Version: 0.17.0
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -1,15 +1,15 @@
1
- dbos-0.16.0a2.dist-info/METADATA,sha256=kH8V4rGJRqSp0StPXPo-Ftmg-O1ITgB_Vyjn638AABU,5022
2
- dbos-0.16.0a2.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- dbos-0.16.0a2.dist-info/entry_points.txt,sha256=z6GcVANQV7Uw_82H9Ob2axJX6V3imftyZsljdh-M1HU,54
4
- dbos-0.16.0a2.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.17.0.dist-info/METADATA,sha256=jFKo8TAjYuyNqaNqT6YpSBvRzFVuTW8uws5cODC_25Q,5020
2
+ dbos-0.17.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ dbos-0.17.0.dist-info/entry_points.txt,sha256=z6GcVANQV7Uw_82H9Ob2axJX6V3imftyZsljdh-M1HU,54
4
+ dbos-0.17.0.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
5
5
  dbos/__init__.py,sha256=CxRHBHEthPL4PZoLbZhp3rdm44-KkRTT2-7DkK9d4QQ,724
6
6
  dbos/_admin_server.py,sha256=DOgzVp9kmwiebQqmJB1LcrZnGTxSMbZiGXdenc1wZDg,3163
7
7
  dbos/_app_db.py,sha256=_tv2vmPjjiaikwgxH3mqxgJ4nUUcG2-0uMXKWCqVu1c,5509
8
8
  dbos/_classproperty.py,sha256=f0X-_BySzn3yFDRKB2JpCbLYQ9tLwt1XftfshvY7CBs,626
9
9
  dbos/_context.py,sha256=KV3fd3-Rv6EWrYDUdHARxltSlNZGNtQtNSqeQ-gkXE8,18049
10
- dbos/_core.py,sha256=F7ep-KA6c0tyrTxV7iwHjKRbxy9RyzCcxl-M8XsvVfE,33481
10
+ dbos/_core.py,sha256=NWJFQX5bECBvKlYH9pVmNJgmqFGYPnkHnOGjOlOQ3Ag,33504
11
11
  dbos/_croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
12
- dbos/_dbos.py,sha256=lBDofWMgK-vVgrU6xmBn1miVv1cf7IamGa9QvPn-BxM,34740
12
+ dbos/_dbos.py,sha256=riYx_dkYFzqeVDYpmcA5ABdAYQFhwyDi4AwxIihDNKA,34809
13
13
  dbos/_dbos_config.py,sha256=f37eccN3JpCA32kRdQ4UsERjhYGcdLWv-N21ijnDZmY,6406
14
14
  dbos/_error.py,sha256=UETk8CoZL-TO2Utn1-E7OSWelhShWmKM-fOlODMR9PE,3893
15
15
  dbos/_fastapi.py,sha256=iyefCZq-ZDKRUjN_rgYQmFmyvWf4gPrSlC6CLbfq4a8,3419
@@ -25,8 +25,8 @@ dbos/_migrations/versions/a3b18ad34abe_added_triggers.py,sha256=Rv0ZsZYZ_WdgGEUL
25
25
  dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py,sha256=8PyFi8rd6CN-mUro43wGhsg5wcQWKZPRHD6jw8R5pVc,986
26
26
  dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4hGBC02Ptng1715roTjY3xiyzZU4,729
27
27
  dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
28
- dbos/_outcome.py,sha256=4BxjNfVhjI-M_m8eCWimpvkO8m-knAEyoDscFYq6SQ4,6237
29
- dbos/_queue.py,sha256=hAXwrfBmtv6BGrlmFq-Ol6b_ED-HDaYqSSxumMJC6Xo,1938
28
+ dbos/_outcome.py,sha256=FDMgWVjZ06vm9xO-38H17mTqBImUYQxgKs_bDCSIAhE,6648
29
+ dbos/_queue.py,sha256=5NZ6RfKQd8LQD8EeUXgrwu86r0AadKEqPIMmL_1ORuw,1956
30
30
  dbos/_recovery.py,sha256=jbzGYxICA2drzyzlBSy2UiXhKV_16tBVacKQdTkqf-w,2008
31
31
  dbos/_registrations.py,sha256=mei6q6_3R5uei8i_Wo_TqGZs85s10shOekDX41sFYD0,6642
32
32
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
@@ -36,7 +36,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
37
37
  dbos/_schemas/system_database.py,sha256=7iw7eHJzEvkatHMOaHORoSvtfisF73wW5j8hRt_Ph14,5126
38
38
  dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
39
- dbos/_sys_db.py,sha256=NyakKa8lGMJ4WHydwukB8QubyGV9A6feK_bVepknZr4,48887
39
+ dbos/_sys_db.py,sha256=uZKeCnGc2MgvEd0ID3nReBBZj21HzClP56TFkXTvIZE,49028
40
40
  dbos/_templates/hello/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
41
41
  dbos/_templates/hello/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  dbos/_templates/hello/__package/main.py,sha256=eI0SS9Nwj-fldtiuSzIlIG6dC91GXXwdRsoHxv6S_WI,2719
@@ -52,4 +52,4 @@ dbos/cli.py,sha256=em1uAxrp5yyg53V7ZpmHFtqD6OJp2cMJkG9vGJPoFTA,10904
52
52
  dbos/dbos-config.schema.json,sha256=tS7x-bdFbFvpobcs3pIOhwun3yr_ndvTEYOn4BJjTzs,5889
53
53
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
54
54
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
55
- dbos-0.16.0a2.dist-info/RECORD,,
55
+ dbos-0.17.0.dist-info/RECORD,,
File without changes