dbos 0.22.0a1__py3-none-any.whl → 0.22.0a4__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
@@ -63,6 +63,7 @@ from ._registrations import (
63
63
  get_or_create_func_info,
64
64
  get_temp_workflow_type,
65
65
  set_dbos_func_name,
66
+ set_func_info,
66
67
  set_temp_workflow_type,
67
68
  )
68
69
  from ._roles import check_required_roles
@@ -286,6 +287,7 @@ def execute_workflow_by_id(
286
287
  ctx.request = (
287
288
  _serialization.deserialize(request) if request is not None else None
288
289
  )
290
+ # If this function belongs to a configured class, add that class instance as its first argument
289
291
  if status["config_name"] is not None:
290
292
  config_name = status["config_name"]
291
293
  class_name = status["class_name"]
@@ -295,28 +297,9 @@ def execute_workflow_by_id(
295
297
  workflow_id,
296
298
  f"Cannot execute workflow because instance '{iname}' is not registered",
297
299
  )
298
-
299
- if startNew:
300
- return start_workflow(
301
- dbos,
302
- wf_func,
303
- status["queue_name"],
304
- True,
305
- dbos._registry.instance_info_map[iname],
306
- *inputs["args"],
307
- **inputs["kwargs"],
308
- )
309
- else:
310
- with SetWorkflowID(workflow_id):
311
- return start_workflow(
312
- dbos,
313
- wf_func,
314
- status["queue_name"],
315
- True,
316
- dbos._registry.instance_info_map[iname],
317
- *inputs["args"],
318
- **inputs["kwargs"],
319
- )
300
+ class_instance = dbos._registry.instance_info_map[iname]
301
+ inputs["args"] = (class_instance,) + inputs["args"]
302
+ # If this function is a class method, add that class object as its first argument
320
303
  elif status["class_name"] is not None:
321
304
  class_name = status["class_name"]
322
305
  if class_name not in dbos._registry.class_info_map:
@@ -324,30 +307,20 @@ def execute_workflow_by_id(
324
307
  workflow_id,
325
308
  f"Cannot execute workflow because class '{class_name}' is not registered",
326
309
  )
310
+ class_object = dbos._registry.class_info_map[class_name]
311
+ inputs["args"] = (class_object,) + inputs["args"]
327
312
 
328
- if startNew:
329
- return start_workflow(
330
- dbos,
331
- wf_func,
332
- status["queue_name"],
333
- True,
334
- dbos._registry.class_info_map[class_name],
335
- *inputs["args"],
336
- **inputs["kwargs"],
337
- )
338
- else:
339
- with SetWorkflowID(workflow_id):
340
- return start_workflow(
341
- dbos,
342
- wf_func,
343
- status["queue_name"],
344
- True,
345
- dbos._registry.class_info_map[class_name],
346
- *inputs["args"],
347
- **inputs["kwargs"],
348
- )
313
+ if startNew:
314
+ return start_workflow(
315
+ dbos,
316
+ wf_func,
317
+ status["queue_name"],
318
+ True,
319
+ *inputs["args"],
320
+ **inputs["kwargs"],
321
+ )
349
322
  else:
350
- if startNew:
323
+ with SetWorkflowID(workflow_id):
351
324
  return start_workflow(
352
325
  dbos,
353
326
  wf_func,
@@ -356,16 +329,6 @@ def execute_workflow_by_id(
356
329
  *inputs["args"],
357
330
  **inputs["kwargs"],
358
331
  )
359
- else:
360
- with SetWorkflowID(workflow_id):
361
- return start_workflow(
362
- dbos,
363
- wf_func,
364
- status["queue_name"],
365
- True,
366
- *inputs["args"],
367
- **inputs["kwargs"],
368
- )
369
332
 
370
333
 
371
334
  @overload
@@ -398,9 +361,12 @@ def start_workflow(
398
361
  *args: P.args,
399
362
  **kwargs: P.kwargs,
400
363
  ) -> "WorkflowHandle[R]":
364
+ # If the function has a class, add the class object as its first argument
401
365
  fself: Optional[object] = None
402
366
  if hasattr(func, "__self__"):
403
367
  fself = func.__self__
368
+ if fself is not None:
369
+ args = (fself,) + args # type: ignore
404
370
 
405
371
  fi = get_func_info(func)
406
372
  if fi is None:
@@ -436,17 +402,13 @@ def start_workflow(
436
402
  new_wf_ctx.id_assigned_for_next_workflow = new_wf_ctx.assign_workflow_id()
437
403
  new_wf_id = new_wf_ctx.id_assigned_for_next_workflow
438
404
 
439
- gin_args: Tuple[Any, ...] = args
440
- if fself is not None:
441
- gin_args = (fself,)
442
-
443
405
  status = _init_workflow(
444
406
  dbos,
445
407
  new_wf_ctx,
446
408
  inputs=inputs,
447
409
  wf_name=get_dbos_func_name(func),
448
- class_name=get_dbos_class_name(fi, func, gin_args),
449
- config_name=get_config_name(fi, func, gin_args),
410
+ class_name=get_dbos_class_name(fi, func, args),
411
+ config_name=get_config_name(fi, func, args),
450
412
  temp_wf_type=get_temp_workflow_type(func),
451
413
  queue=queue_name,
452
414
  max_recovery_attempts=fi.max_recovery_attempts,
@@ -464,27 +426,15 @@ def start_workflow(
464
426
  )
465
427
  return WorkflowHandlePolling(new_wf_id, dbos)
466
428
 
467
- if fself is not None:
468
- future = dbos._executor.submit(
469
- cast(Callable[..., R], _execute_workflow_wthread),
470
- dbos,
471
- status,
472
- func,
473
- new_wf_ctx,
474
- fself,
475
- *args,
476
- **kwargs,
477
- )
478
- else:
479
- future = dbos._executor.submit(
480
- cast(Callable[..., R], _execute_workflow_wthread),
481
- dbos,
482
- status,
483
- func,
484
- new_wf_ctx,
485
- *args,
486
- **kwargs,
487
- )
429
+ future = dbos._executor.submit(
430
+ cast(Callable[..., R], _execute_workflow_wthread),
431
+ dbos,
432
+ status,
433
+ func,
434
+ new_wf_ctx,
435
+ *args,
436
+ **kwargs,
437
+ )
488
438
  return WorkflowHandleFuture(new_wf_id, future, dbos)
489
439
 
490
440
 
@@ -516,6 +466,8 @@ def workflow_wrapper(
516
466
 
517
467
  @wraps(func)
518
468
  def wrapper(*args: Any, **kwargs: Any) -> R:
469
+ fi = get_func_info(func)
470
+ assert fi is not None
519
471
  if dbosreg.dbos is None:
520
472
  raise DBOSException(
521
473
  f"Function {func.__name__} invoked before DBOS initialized"
@@ -726,6 +678,8 @@ def decorate_transaction(
726
678
  set_temp_workflow_type(temp_wf, "transaction")
727
679
  dbosreg.register_wf_function(get_dbos_func_name(temp_wf), wrapped_wf)
728
680
  wrapper.__orig_func = temp_wf # type: ignore
681
+ set_func_info(wrapped_wf, get_or_create_func_info(func))
682
+ set_func_info(temp_wf, get_or_create_func_info(func))
729
683
 
730
684
  return cast(F, wrapper)
731
685
 
@@ -875,6 +829,8 @@ def decorate_step(
875
829
  set_temp_workflow_type(temp_wf, "step")
876
830
  dbosreg.register_wf_function(get_dbos_func_name(temp_wf), wrapped_wf)
877
831
  wrapper.__orig_func = temp_wf # type: ignore
832
+ set_func_info(wrapped_wf, get_or_create_func_info(func))
833
+ set_func_info(temp_wf, get_or_create_func_info(func))
878
834
 
879
835
  return cast(Callable[P, R], wrapper)
880
836
 
dbos/_dbos.py CHANGED
@@ -997,6 +997,10 @@ def _dbos_exit_hook() -> None:
997
997
  )
998
998
  return
999
999
  if not _dbos_global_instance._launched:
1000
+ if _dbos_global_instance.fastapi is not None:
1001
+ # FastAPI lifespan middleware will call launch/destroy, so we can ignore this.
1002
+ # This is likely to happen during fastapi dev runs, where the reloader loads the module multiple times.
1003
+ return
1000
1004
  print("DBOS exiting; DBOS exists but launch() was not called")
1001
1005
  dbos_logger.warning("DBOS exiting; DBOS exists but launch() was not called")
1002
1006
  return
dbos/_queue.py CHANGED
@@ -76,7 +76,9 @@ def queue_thread(stop_event: threading.Event, dbos: "DBOS") -> None:
76
76
  execute_workflow_by_id(dbos, id)
77
77
  except OperationalError as e:
78
78
  # Ignore serialization error
79
- if not isinstance(e.orig, errors.SerializationFailure):
79
+ if not isinstance(
80
+ e.orig, (errors.SerializationFailure, errors.LockNotAvailable)
81
+ ):
80
82
  dbos.logger.warning(
81
83
  f"Exception encountered in queue thread: {traceback.format_exc()}"
82
84
  )
dbos/_registrations.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import inspect
2
+ from dataclasses import dataclass
2
3
  from enum import Enum
3
4
  from types import FunctionType
4
5
  from typing import Any, Callable, List, Literal, Optional, Tuple, Type, cast
@@ -31,9 +32,9 @@ def set_temp_workflow_type(f: Any, name: TempWorkflowType) -> None:
31
32
  setattr(f, "dbos_temp_workflow_type", name)
32
33
 
33
34
 
35
+ @dataclass
34
36
  class DBOSClassInfo:
35
- def __init__(self) -> None:
36
- self.def_required_roles: Optional[List[str]] = None
37
+ def_required_roles: Optional[List[str]] = None
37
38
 
38
39
 
39
40
  class DBOSFuncType(Enum):
@@ -44,12 +45,12 @@ class DBOSFuncType(Enum):
44
45
  Instance = 4
45
46
 
46
47
 
48
+ @dataclass
47
49
  class DBOSFuncInfo:
48
- def __init__(self) -> None:
49
- self.class_info: Optional[DBOSClassInfo] = None
50
- self.func_type: DBOSFuncType = DBOSFuncType.Unknown
51
- self.required_roles: Optional[List[str]] = None
52
- self.max_recovery_attempts = DEFAULT_MAX_RECOVERY_ATTEMPTS
50
+ class_info: Optional[DBOSClassInfo] = None
51
+ func_type: DBOSFuncType = DBOSFuncType.Unknown
52
+ required_roles: Optional[List[str]] = None
53
+ max_recovery_attempts: int = DEFAULT_MAX_RECOVERY_ATTEMPTS
53
54
 
54
55
 
55
56
  def get_or_create_class_info(cls: Type[Any]) -> DBOSClassInfo:
@@ -110,6 +111,10 @@ def get_or_create_func_info(func: Callable[..., Any]) -> DBOSFuncInfo:
110
111
  return fi
111
112
 
112
113
 
114
+ def set_func_info(func: Callable[..., Any], fi: DBOSFuncInfo) -> None:
115
+ setattr(func, "dbos_func_decorator_info", fi)
116
+
117
+
113
118
  def get_class_info(cls: Type[Any]) -> Optional[DBOSClassInfo]:
114
119
  if hasattr(cls, "dbos_class_decorator_info"):
115
120
  ci: DBOSClassInfo = getattr(cls, "dbos_class_decorator_info")
dbos/_sys_db.py CHANGED
@@ -189,6 +189,10 @@ class SystemDatabase:
189
189
  host=config["database"]["hostname"],
190
190
  port=config["database"]["port"],
191
191
  database="postgres",
192
+ # fills the "application_name" column in pg_stat_activity
193
+ query={
194
+ "application_name": f"dbos_transact_{os.environ.get('DBOS__VMID', 'local')}_{os.environ.get('DBOS__APPVERSION', '')}"
195
+ },
192
196
  )
193
197
  engine = sa.create_engine(postgres_db_url)
194
198
  with engine.connect() as conn:
@@ -207,6 +211,10 @@ class SystemDatabase:
207
211
  host=config["database"]["hostname"],
208
212
  port=config["database"]["port"],
209
213
  database=sysdb_name,
214
+ # fills the "application_name" column in pg_stat_activity
215
+ query={
216
+ "application_name": f"dbos_transact_{os.environ.get('DBOS__VMID', 'local')}_{os.environ.get('DBOS__APPVERSION', '')}"
217
+ },
210
218
  )
211
219
 
212
220
  # Create a connection pool for the system database
@@ -1307,6 +1315,55 @@ class SystemDatabase:
1307
1315
  # Dequeue functions eligible for this worker and ordered by the time at which they were enqueued.
1308
1316
  # If there is a global or local concurrency limit N, select only the N oldest enqueued
1309
1317
  # functions, else select all of them.
1318
+
1319
+ # First lets figure out how many tasks the worker can dequeue
1320
+ running_tasks_query = (
1321
+ sa.select(
1322
+ SystemSchema.workflow_queue.c.executor_id,
1323
+ sa.func.count().label("task_count"),
1324
+ )
1325
+ .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1326
+ .where(
1327
+ SystemSchema.workflow_queue.c.executor_id.isnot(
1328
+ None
1329
+ ) # Task is dequeued
1330
+ )
1331
+ .where(
1332
+ SystemSchema.workflow_queue.c.completed_at_epoch_ms.is_(
1333
+ None
1334
+ ) # Task is not completed
1335
+ )
1336
+ .group_by(SystemSchema.workflow_queue.c.executor_id)
1337
+ )
1338
+ running_tasks_result = c.execute(running_tasks_query).fetchall()
1339
+ running_tasks_result_dict = {row[0]: row[1] for row in running_tasks_result}
1340
+ running_tasks_for_this_worker = running_tasks_result_dict.get(
1341
+ executor_id, 0
1342
+ ) # Get count for current executor
1343
+
1344
+ max_tasks = float("inf")
1345
+ if queue.worker_concurrency is not None:
1346
+ # Worker local concurrency limit should always be >= running_tasks_for_this_worker
1347
+ # This should never happen but a check + warning doesn't hurt
1348
+ if running_tasks_for_this_worker > queue.worker_concurrency:
1349
+ dbos_logger.warning(
1350
+ f"Number of tasks on this worker ({running_tasks_for_this_worker}) exceeds the worker concurrency limit ({queue.worker_concurrency})"
1351
+ )
1352
+ max_tasks = max(
1353
+ 0, queue.worker_concurrency - running_tasks_for_this_worker
1354
+ )
1355
+ if queue.concurrency is not None:
1356
+ total_running_tasks = sum(running_tasks_result_dict.values())
1357
+ # Queue global concurrency limit should always be >= running_tasks_count
1358
+ # This should never happen but a check + warning doesn't hurt
1359
+ if total_running_tasks > queue.concurrency:
1360
+ dbos_logger.warning(
1361
+ f"Total running tasks ({total_running_tasks}) exceeds the global concurrency limit ({queue.concurrency})"
1362
+ )
1363
+ available_tasks = max(0, queue.concurrency - total_running_tasks)
1364
+ max_tasks = min(max_tasks, available_tasks)
1365
+
1366
+ # Lookup tasks
1310
1367
  query = (
1311
1368
  sa.select(
1312
1369
  SystemSchema.workflow_queue.c.workflow_uuid,
@@ -1315,29 +1372,25 @@ class SystemDatabase:
1315
1372
  )
1316
1373
  .where(SystemSchema.workflow_queue.c.queue_name == queue.name)
1317
1374
  .where(SystemSchema.workflow_queue.c.completed_at_epoch_ms == None)
1318
- .where(
1319
- # Only select functions that have not been started yet or have been started by this worker
1320
- or_(
1321
- SystemSchema.workflow_queue.c.executor_id == None,
1322
- SystemSchema.workflow_queue.c.executor_id == executor_id,
1323
- )
1324
- )
1375
+ .where(SystemSchema.workflow_queue.c.executor_id == None)
1325
1376
  .order_by(SystemSchema.workflow_queue.c.created_at_epoch_ms.asc())
1377
+ .with_for_update(nowait=True) # Error out early
1326
1378
  )
1327
- # Set a dequeue limit if necessary
1328
- if queue.worker_concurrency is not None:
1329
- query = query.limit(queue.worker_concurrency)
1330
- elif queue.concurrency is not None:
1331
- query = query.limit(queue.concurrency)
1379
+ # Apply limit only if max_tasks is finite
1380
+ if max_tasks != float("inf"):
1381
+ query = query.limit(int(max_tasks))
1332
1382
 
1333
1383
  rows = c.execute(query).fetchall()
1334
1384
 
1335
- # Now, get the workflow IDs of functions that have not yet been started
1336
- dequeued_ids: List[str] = [row[0] for row in rows if row[1] is None]
1385
+ # Get the workflow IDs
1386
+ dequeued_ids: List[str] = [row[0] for row in rows]
1387
+ if len(dequeued_ids) > 0:
1388
+ dbos_logger.debug(
1389
+ f"[{queue.name}] dequeueing {len(dequeued_ids)} task(s)"
1390
+ )
1337
1391
  ret_ids: list[str] = []
1338
- dbos_logger.debug(f"[{queue.name}] dequeueing {len(dequeued_ids)} task(s)")
1339
- for id in dequeued_ids:
1340
1392
 
1393
+ for id in dequeued_ids:
1341
1394
  # If we have a limiter, stop starting functions when the number
1342
1395
  # of functions started this period exceeds the limit.
1343
1396
  if queue.limiter is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.22.0a1
3
+ Version: 0.22.0a4
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-0.22.0a1.dist-info/METADATA,sha256=DBJiSN2oOdnkb8P0DBogQjH4Wjh79UQ9gI6bDVdbbP8,5309
2
- dbos-0.22.0a1.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
- dbos-0.22.0a1.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
- dbos-0.22.0a1.dist-info/licenses/LICENSE,sha256=VGZit_a5-kdw9WT6fY5jxAWVwGQzgLFyPWrcVVUhVNU,1067
1
+ dbos-0.22.0a4.dist-info/METADATA,sha256=3hz0YdIdTpfM3YyJqBeoKy5ADFI91UA3j-MW4lNhBfs,5309
2
+ dbos-0.22.0a4.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ dbos-0.22.0a4.dist-info/entry_points.txt,sha256=_QOQ3tVfEjtjBlr1jS4sHqHya9lI2aIEIWkz8dqYp14,58
4
+ dbos-0.22.0a4.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=PJgneZG9-64TapZrPeJtt73puAswRImCE5uce2k2PKU,4750
7
7
  dbos/_app_db.py,sha256=_tv2vmPjjiaikwgxH3mqxgJ4nUUcG2-0uMXKWCqVu1c,5509
@@ -10,10 +10,10 @@ dbos/_cloudutils/authentication.py,sha256=V0fCWQN9stCkhbuuxgPTGpvuQcDqfU3KAxPAh0
10
10
  dbos/_cloudutils/cloudutils.py,sha256=5e3CW1deSW-dI5G3QN0XbiVsBhyqT8wu7fuV2f8wtGU,7688
11
11
  dbos/_cloudutils/databases.py,sha256=x4187Djsyoa-QaG3Kog8JT2_GERsnqa93LIVanmVUmg,8393
12
12
  dbos/_context.py,sha256=FHB_fpE4fQt4fIJvAmMMsbY4xHwH77gsW01cFsRZjsE,17779
13
- dbos/_core.py,sha256=nGiXyYgV8H5TRRZG0e8HCd5IZimufYQLmKNr7nBbwbo,36564
13
+ dbos/_core.py,sha256=PvII-lcUgii0WqaDb9-56gfQ_69VoWthKAkKexRebuk,35461
14
14
  dbos/_croniter.py,sha256=hbhgfsHBqclUS8VeLnJ9PSE9Z54z6mi4nnrr1aUXn0k,47561
15
15
  dbos/_db_wizard.py,sha256=xgKLna0_6Xi50F3o8msRosXba8NScHlpJR5ICVCkHDQ,7534
16
- dbos/_dbos.py,sha256=wAjdlUgDSIC_Q8D_GZYDoiKaxjtr6KNHeq6DDuUh9do,36340
16
+ dbos/_dbos.py,sha256=klpuF1Wj05_qsWiKnLSWfei-16-K-g4BQ5R0MMfTuNE,36620
17
17
  dbos/_dbos_config.py,sha256=DfiqVVxNqnafkocSzLqBp1Ig5vCviDTDK_GO3zTtQqI,8298
18
18
  dbos/_error.py,sha256=vtaSsG0QW6cRlwfZ4zzZWy_IHCZlomwSlrDyGWuyn8c,4337
19
19
  dbos/_fastapi.py,sha256=ke03vqsSYDnO6XeOtOVFXj0-f-v1MGsOxa9McaROvNc,3616
@@ -31,9 +31,9 @@ dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py,sha256=8PyFi8rd6CN-m
31
31
  dbos/_migrations/versions/d76646551a6c_workflow_queue.py,sha256=G942nophZ2uC2vc4hGBC02Ptng1715roTjY3xiyzZU4,729
32
32
  dbos/_migrations/versions/eab0cc1d9a14_job_queue.py,sha256=uvhFOtqbBreCePhAxZfIT0qCAI7BiZTou9wt6QnbY7c,1412
33
33
  dbos/_outcome.py,sha256=FDMgWVjZ06vm9xO-38H17mTqBImUYQxgKs_bDCSIAhE,6648
34
- dbos/_queue.py,sha256=o_aczwualJTMoXb0XXL-Y5QH77OEukWzuerogbWi2ho,2779
34
+ dbos/_queue.py,sha256=eZiapBcyn70-viW0y9fo7u09V6_VF5ACNGJxD-U_dNM,2844
35
35
  dbos/_recovery.py,sha256=rek9rm2CaENbbl_vu3To-BdXop7tMEyGvtoNiJLVxjQ,2772
36
- dbos/_registrations.py,sha256=mei6q6_3R5uei8i_Wo_TqGZs85s10shOekDX41sFYD0,6642
36
+ dbos/_registrations.py,sha256=_zy6k944Ll8QwqU12Kr3OP23ukVtm8axPNN1TS_kJRc,6717
37
37
  dbos/_request.py,sha256=cX1B3Atlh160phgS35gF1VEEV4pD126c9F3BDgBmxZU,929
38
38
  dbos/_roles.py,sha256=iOsgmIAf1XVzxs3gYWdGRe1B880YfOw5fpU7Jwx8_A8,2271
39
39
  dbos/_scheduler.py,sha256=0I3e8Y-OIBG3wiUCIskShd-Sk_eUFCFyRB5u4L7IHXI,1940
@@ -41,7 +41,7 @@ dbos/_schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  dbos/_schemas/application_database.py,sha256=KeyoPrF7hy_ODXV7QNike_VFSD74QBRfQ76D7QyE9HI,966
42
42
  dbos/_schemas/system_database.py,sha256=rwp4EvCSaXcUoMaRczZCvETCxGp72k3-hvLyGUDkih0,5163
43
43
  dbos/_serialization.py,sha256=YCYv0qKAwAZ1djZisBC7khvKqG-5OcIv9t9EC5PFIog,1743
44
- dbos/_sys_db.py,sha256=B8I-MUOHcXalrtOewomKuxbyRvYdItxQhHsfJ1IjunA,59719
44
+ dbos/_sys_db.py,sha256=pH4vfPAAFVqu0KAfUFSUDKp5T-4OrwaLWJylMrzDFL4,62421
45
45
  dbos/_templates/dbos-db-starter/README.md,sha256=GhxhBj42wjTt1fWEtwNriHbJuKb66Vzu89G4pxNHw2g,930
46
46
  dbos/_templates/dbos-db-starter/__package/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  dbos/_templates/dbos-db-starter/__package/main.py,sha256=eI0SS9Nwj-fldtiuSzIlIG6dC91GXXwdRsoHxv6S_WI,2719
@@ -60,4 +60,4 @@ dbos/cli/cli.py,sha256=_tXw2IQrWW7fV_h51f_R99vEBSi6aMLz-vCOxKaENiQ,14155
60
60
  dbos/dbos-config.schema.json,sha256=X5TpXNcARGceX0zQs0fVgtZW_Xj9uBbY5afPt9Rz9yk,5741
61
61
  dbos/py.typed,sha256=QfzXT1Ktfk3Rj84akygc7_42z0lRpCq0Ilh8OXI6Zas,44
62
62
  version/__init__.py,sha256=L4sNxecRuqdtSFdpUGX3TtBi9KL3k7YsZVIvv-fv9-A,1678
63
- dbos-0.22.0a1.dist-info/RECORD,,
63
+ dbos-0.22.0a4.dist-info/RECORD,,