dbos 0.20.0a7__tar.gz → 0.20.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 (89) hide show
  1. {dbos-0.20.0a7 → dbos-0.20.0a9}/PKG-INFO +1 -1
  2. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_dbos.py +17 -1
  3. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_sys_db.py +43 -0
  4. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/cli/cli.py +4 -49
  5. {dbos-0.20.0a7 → dbos-0.20.0a9}/pyproject.toml +1 -1
  6. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_package.py +33 -1
  7. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_schema_migration.py +40 -0
  8. {dbos-0.20.0a7 → dbos-0.20.0a9}/LICENSE +0 -0
  9. {dbos-0.20.0a7 → dbos-0.20.0a9}/README.md +0 -0
  10. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/__init__.py +0 -0
  11. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_admin_server.py +0 -0
  12. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_app_db.py +0 -0
  13. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_classproperty.py +0 -0
  14. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_cloudutils/authentication.py +0 -0
  15. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_cloudutils/cloudutils.py +0 -0
  16. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_cloudutils/databases.py +0 -0
  17. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_context.py +0 -0
  18. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_core.py +0 -0
  19. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_croniter.py +0 -0
  20. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_db_wizard.py +0 -0
  21. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_dbos_config.py +0 -0
  22. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_error.py +0 -0
  23. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_fastapi.py +0 -0
  24. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_flask.py +0 -0
  25. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_kafka.py +0 -0
  26. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_kafka_message.py +0 -0
  27. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_logger.py +0 -0
  28. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/env.py +0 -0
  29. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/script.py.mako +0 -0
  30. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  31. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  32. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  33. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  34. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  35. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  36. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  37. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_outcome.py +0 -0
  38. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_queue.py +0 -0
  39. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_recovery.py +0 -0
  40. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_registrations.py +0 -0
  41. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_request.py +0 -0
  42. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_roles.py +0 -0
  43. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_scheduler.py +0 -0
  44. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_schemas/__init__.py +0 -0
  45. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_schemas/application_database.py +0 -0
  46. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_schemas/system_database.py +0 -0
  47. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_serialization.py +0 -0
  48. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/README.md +0 -0
  49. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  50. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/__package/main.py +0 -0
  51. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  52. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  53. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  54. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  55. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  56. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  57. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  58. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_tracer.py +0 -0
  59. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/_workflow_commands.py +0 -0
  60. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/cli/_github_init.py +0 -0
  61. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/cli/_template_init.py +0 -0
  62. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/dbos-config.schema.json +0 -0
  63. {dbos-0.20.0a7 → dbos-0.20.0a9}/dbos/py.typed +0 -0
  64. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/__init__.py +0 -0
  65. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/atexit_no_ctor.py +0 -0
  66. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/atexit_no_launch.py +0 -0
  67. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/classdefs.py +0 -0
  68. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/conftest.py +0 -0
  69. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/more_classdefs.py +0 -0
  70. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/queuedworkflow.py +0 -0
  71. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_admin_server.py +0 -0
  72. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_async.py +0 -0
  73. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_classdecorators.py +0 -0
  74. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_concurrency.py +0 -0
  75. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_config.py +0 -0
  76. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_croniter.py +0 -0
  77. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_dbos.py +0 -0
  78. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_failures.py +0 -0
  79. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_fastapi.py +0 -0
  80. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_fastapi_roles.py +0 -0
  81. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_flask.py +0 -0
  82. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_kafka.py +0 -0
  83. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_outcome.py +0 -0
  84. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_queue.py +0 -0
  85. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_scheduler.py +0 -0
  86. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_singleton.py +0 -0
  87. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_spans.py +0 -0
  88. {dbos-0.20.0a7 → dbos-0.20.0a9}/tests/test_workflow_cmds.py +0 -0
  89. {dbos-0.20.0a7 → dbos-0.20.0a9}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.20.0a7
3
+ Version: 0.20.0a9
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -56,7 +56,7 @@ from ._registrations import (
56
56
  )
57
57
  from ._roles import default_required_roles, required_roles
58
58
  from ._scheduler import ScheduledWorkflow, scheduled
59
- from ._sys_db import WorkflowStatusString
59
+ from ._sys_db import WorkflowStatusString, reset_system_database
60
60
  from ._tracer import dbos_tracer
61
61
 
62
62
  if TYPE_CHECKING:
@@ -409,6 +409,22 @@ class DBOS:
409
409
  dbos_logger.error(f"DBOS failed to launch: {traceback.format_exc()}")
410
410
  raise
411
411
 
412
+ @classmethod
413
+ def reset_system_database(cls) -> None:
414
+ """
415
+ Destroy the DBOS system database. Useful for resetting the state of DBOS between tests.
416
+ This is a destructive operation and should only be used in a test environment.
417
+ More information on testing DBOS apps: https://docs.dbos.dev/python/tutorials/testing
418
+ """
419
+ if _dbos_global_instance is not None:
420
+ _dbos_global_instance._reset_system_database()
421
+
422
+ def _reset_system_database(self) -> None:
423
+ assert (
424
+ not self._launched
425
+ ), "The system database cannot be reset after DBOS is launched. Resetting the system database is a destructive operation that should only be used in a test environment."
426
+ reset_system_database(self.config)
427
+
412
428
  def _destroy(self) -> None:
413
429
  self._initialized = False
414
430
  for event in self.stop_events:
@@ -1265,3 +1265,46 @@ class SystemDatabase:
1265
1265
  .where(SystemSchema.workflow_queue.c.workflow_uuid == workflow_id)
1266
1266
  .values(completed_at_epoch_ms=int(time.time() * 1000))
1267
1267
  )
1268
+
1269
+
1270
+ def reset_system_database(config: ConfigFile) -> None:
1271
+ sysdb_name = (
1272
+ config["database"]["sys_db_name"]
1273
+ if "sys_db_name" in config["database"] and config["database"]["sys_db_name"]
1274
+ else config["database"]["app_db_name"] + SystemSchema.sysdb_suffix
1275
+ )
1276
+ postgres_db_url = sa.URL.create(
1277
+ "postgresql+psycopg",
1278
+ username=config["database"]["username"],
1279
+ password=config["database"]["password"],
1280
+ host=config["database"]["hostname"],
1281
+ port=config["database"]["port"],
1282
+ database="postgres",
1283
+ )
1284
+ try:
1285
+ # Connect to postgres default database
1286
+ engine = sa.create_engine(postgres_db_url)
1287
+
1288
+ with engine.connect() as conn:
1289
+ # Set autocommit required for database dropping
1290
+ conn.execution_options(isolation_level="AUTOCOMMIT")
1291
+
1292
+ # Terminate existing connections
1293
+ conn.execute(
1294
+ sa.text(
1295
+ """
1296
+ SELECT pg_terminate_backend(pg_stat_activity.pid)
1297
+ FROM pg_stat_activity
1298
+ WHERE pg_stat_activity.datname = :db_name
1299
+ AND pid <> pg_backend_pid()
1300
+ """
1301
+ ),
1302
+ {"db_name": sysdb_name},
1303
+ )
1304
+
1305
+ # Drop the database
1306
+ conn.execute(sa.text(f"DROP DATABASE IF EXISTS {sysdb_name}"))
1307
+
1308
+ except sa.exc.SQLAlchemyError as e:
1309
+ dbos_logger.error(f"Error resetting system database: {str(e)}")
1310
+ raise e
@@ -18,8 +18,7 @@ from typing_extensions import Annotated
18
18
  from .. import load_config
19
19
  from .._app_db import ApplicationDatabase
20
20
  from .._dbos_config import _is_valid_app_name
21
- from .._schemas.system_database import SystemSchema
22
- from .._sys_db import SystemDatabase
21
+ from .._sys_db import SystemDatabase, reset_system_database
23
22
  from .._workflow_commands import _cancel_workflow, _get_workflow, _list_workflows
24
23
  from ..cli._github_init import create_template_from_github
25
24
  from ._template_init import copy_template, get_project_name, get_templates_directory
@@ -101,7 +100,7 @@ def init(
101
100
  ) -> None:
102
101
  try:
103
102
 
104
- git_templates = ["dbos-app-starter", "dbos-cron-starter"]
103
+ git_templates = ["dbos-toolbox", "dbos-app-starter", "dbos-cron-starter"]
105
104
  templates_dir = get_templates_directory()
106
105
  templates = git_templates + [
107
106
  x.name for x in os.scandir(templates_dir) if x.is_dir()
@@ -224,56 +223,12 @@ def reset(
224
223
  typer.echo("Operation cancelled.")
225
224
  raise typer.Exit()
226
225
  config = load_config()
227
- sysdb_name = (
228
- config["database"]["sys_db_name"]
229
- if "sys_db_name" in config["database"] and config["database"]["sys_db_name"]
230
- else config["database"]["app_db_name"] + SystemSchema.sysdb_suffix
231
- )
232
- postgres_db_url = sa.URL.create(
233
- "postgresql+psycopg",
234
- username=config["database"]["username"],
235
- password=config["database"]["password"],
236
- host=config["database"]["hostname"],
237
- port=config["database"]["port"],
238
- database="postgres",
239
- )
240
226
  try:
241
- # Connect to postgres default database
242
- engine = sa.create_engine(postgres_db_url)
243
-
244
- with engine.connect() as conn:
245
- # Set autocommit required for database dropping
246
- conn.execution_options(isolation_level="AUTOCOMMIT")
247
-
248
- # Terminate existing connections
249
- conn.execute(
250
- sa.text(
251
- """
252
- SELECT pg_terminate_backend(pg_stat_activity.pid)
253
- FROM pg_stat_activity
254
- WHERE pg_stat_activity.datname = :db_name
255
- AND pid <> pg_backend_pid()
256
- """
257
- ),
258
- {"db_name": sysdb_name},
259
- )
260
-
261
- # Drop the database
262
- conn.execute(sa.text(f"DROP DATABASE IF EXISTS {sysdb_name}"))
263
-
227
+ reset_system_database(config)
264
228
  except sa.exc.SQLAlchemyError as e:
265
- typer.echo(f"Error dropping database: {str(e)}")
229
+ typer.echo(f"Error resetting system database: {str(e)}")
266
230
  return
267
231
 
268
- sys_db = None
269
- try:
270
- sys_db = SystemDatabase(config)
271
- except Exception as e:
272
- typer.echo(f"DBOS system schema migration failed: {e}")
273
- finally:
274
- if sys_db:
275
- sys_db.destroy()
276
-
277
232
 
278
233
  @workflow.command(help="List workflows for your application")
279
234
  def list(
@@ -27,7 +27,7 @@ dependencies = [
27
27
  ]
28
28
  requires-python = ">=3.9"
29
29
  readme = "README.md"
30
- version = "0.20.0a7"
30
+ version = "0.20.0a9"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -50,7 +50,7 @@ def test_package(build_wheel: str, postgres_db_engine: sa.Engine) -> None:
50
50
 
51
51
  # initalize the app with dbos scaffolding
52
52
  subprocess.check_call(
53
- ["dbos", "init", template_name, "--template", "dbos-db-starter"],
53
+ ["dbos", "init", template_name, "--template", template_name],
54
54
  cwd=temp_path,
55
55
  env=venv,
56
56
  )
@@ -115,3 +115,35 @@ def test_init_config() -> None:
115
115
  actual_yaml = yaml.safe_load(f)
116
116
 
117
117
  assert actual_yaml == expected_yaml
118
+
119
+
120
+ def test_reset(postgres_db_engine: sa.Engine) -> None:
121
+ app_name = "reset-app"
122
+ sysdb_name = "reset_app_dbos_sys"
123
+ with tempfile.TemporaryDirectory() as temp_path:
124
+ subprocess.check_call(
125
+ ["dbos", "init", app_name, "--template", "dbos-db-starter"],
126
+ cwd=temp_path,
127
+ )
128
+
129
+ # Create a system database and verify it exists
130
+ subprocess.check_call(["dbos", "migrate"], cwd=temp_path)
131
+ with postgres_db_engine.connect() as c:
132
+ c.execution_options(isolation_level="AUTOCOMMIT")
133
+ result = c.execute(
134
+ sa.text(
135
+ f"SELECT COUNT(*) FROM pg_database WHERE datname = '{sysdb_name}'"
136
+ )
137
+ ).scalar()
138
+ assert result == 1
139
+
140
+ # Call reset and verify it's destroyed
141
+ subprocess.check_call(["dbos", "reset", "-y"], cwd=temp_path)
142
+ with postgres_db_engine.connect() as c:
143
+ c.execution_options(isolation_level="AUTOCOMMIT")
144
+ result = c.execute(
145
+ sa.text(
146
+ f"SELECT COUNT(*) FROM pg_database WHERE datname = '{sysdb_name}'"
147
+ )
148
+ ).scalar()
149
+ assert result == 0
@@ -102,3 +102,43 @@ def rollback_system_db(sysdb_url: str) -> None:
102
102
  )
103
103
  alembic_cfg.set_main_option("sqlalchemy.url", escaped_conn_string)
104
104
  command.downgrade(alembic_cfg, "base") # Rollback all migrations
105
+
106
+
107
+ def test_reset(config: ConfigFile, postgres_db_engine: sa.Engine) -> None:
108
+ DBOS.destroy()
109
+ dbos = DBOS(config=config)
110
+ DBOS.launch()
111
+
112
+ # Make sure the system database exists
113
+ with dbos._sys_db.engine.connect() as c:
114
+ sql = SystemSchema.workflow_status.select()
115
+ result = c.execute(sql)
116
+ assert result.fetchall() == []
117
+
118
+ DBOS.destroy()
119
+ dbos = DBOS(config=config)
120
+ DBOS.reset_system_database()
121
+
122
+ sysdb_name = (
123
+ config["database"]["sys_db_name"]
124
+ if "sys_db_name" in config["database"] and config["database"]["sys_db_name"]
125
+ else config["database"]["app_db_name"] + SystemSchema.sysdb_suffix
126
+ )
127
+ with postgres_db_engine.connect() as c:
128
+ c.execution_options(isolation_level="AUTOCOMMIT")
129
+ count: int = c.execute(
130
+ sa.text(f"SELECT COUNT(*) FROM pg_database WHERE datname = '{sysdb_name}'")
131
+ ).scalar_one()
132
+ assert count == 0
133
+
134
+ DBOS.launch()
135
+
136
+ # Make sure the system database is recreated
137
+ with dbos._sys_db.engine.connect() as c:
138
+ sql = SystemSchema.workflow_status.select()
139
+ result = c.execute(sql)
140
+ assert result.fetchall() == []
141
+
142
+ # Verify that resetting after launch throws
143
+ with pytest.raises(AssertionError):
144
+ DBOS.reset_system_database()
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