dbos 1.14.0a8__tar.gz → 1.15.0a1__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.
- {dbos-1.14.0a8 → dbos-1.15.0a1}/PKG-INFO +8 -16
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_client.py +13 -14
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_context.py +12 -6
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_core.py +2 -7
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_dbos.py +5 -13
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_dbos_config.py +17 -29
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_debouncer.py +13 -24
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_debug.py +0 -8
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_docker_pg_helper.py +93 -51
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_fastapi.py +5 -1
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_logger.py +18 -21
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_migration.py +4 -41
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_serialization.py +19 -30
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_sys_db_postgres.py +2 -9
- dbos-1.15.0a1/dbos/_templates/dbos-db-starter/migrations/create_table.py.dbos +34 -0
- dbos-1.15.0a1/dbos/_tracer.py +99 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/cli/_github_init.py +22 -16
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/cli/_template_init.py +5 -16
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/cli/cli.py +20 -28
- {dbos-1.14.0a8 → dbos-1.15.0a1}/pyproject.toml +13 -16
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/conftest.py +1 -0
- dbos-1.15.0a1/tests/script_without_fastapi.py +25 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_async.py +0 -85
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_config.py +2 -19
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_dbos.py +8 -32
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_debouncer.py +55 -47
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_debug.py +0 -7
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_docker_secrets.py +0 -20
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_failures.py +5 -4
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_fastapi_roles.py +1 -125
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_package.py +13 -1
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_schema_migration.py +1 -55
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_spans.py +1 -1
- dbos-1.14.0a8/dbos/_alembic_migrations/env.py +0 -62
- dbos-1.14.0a8/dbos/_alembic_migrations/script.py.mako +0 -26
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/01ce9f07bd10_streaming.py +0 -42
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -34
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -45
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/471b60d64126_dbos_migrations.py +0 -35
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -35
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/5c361fc04708_added_system_tables.py +0 -193
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/66478e1b95e5_consolidate_queues.py +0 -71
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -44
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/933e86bdac6a_add_queue_priority.py +0 -35
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/a3b18ad34abe_added_triggers.py +0 -72
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -43
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/d76646551a6c_workflow_queue.py +0 -28
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/d994145b47b6_consolidate_inputs.py +0 -30
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/eab0cc1d9a14_job_queue.py +0 -56
- dbos-1.14.0a8/dbos/_alembic_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -46
- dbos-1.14.0a8/dbos/_templates/dbos-db-starter/alembic.ini +0 -116
- dbos-1.14.0a8/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -85
- dbos-1.14.0a8/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -26
- dbos-1.14.0a8/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -35
- dbos-1.14.0a8/dbos/_tracer.py +0 -88
- {dbos-1.14.0a8 → dbos-1.15.0a1}/LICENSE +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/README.md +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/__init__.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/__main__.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_admin_server.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_app_db.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_classproperty.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_conductor/conductor.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_conductor/protocol.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_croniter.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_error.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_event_loop.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_flask.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_kafka.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_kafka_message.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_outcome.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_queue.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_recovery.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_registrations.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_roles.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_scheduler.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_schemas/__init__.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_schemas/application_database.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_schemas/system_database.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_sys_db.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_sys_db_sqlite.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_templates/dbos-db-starter/README.md +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_utils.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/_workflow_commands.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/cli/migration.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/dbos-config.schema.json +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/dbos/py.typed +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/__init__.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/atexit_no_ctor.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/atexit_no_launch.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/classdefs.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/client_collateral.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/client_worker.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/dupname_classdefs1.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/dupname_classdefsa.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/more_classdefs.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/queuedworkflow.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_admin_server.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_async_workflow_management.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_classdecorators.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_cli.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_client.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_concurrency.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_croniter.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_fastapi.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_flask.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_kafka.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_outcome.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_queue.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_scheduler.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_singleton.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_sqlalchemy.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_streaming.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_workflow_introspection.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/tests/test_workflow_management.py +0 -0
- {dbos-1.14.0a8 → dbos-1.15.0a1}/version/__init__.py +0 -0
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dbos
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.0a1
|
|
4
4
|
Summary: Ultra-lightweight durable execution in Python
|
|
5
5
|
Author-Email: "DBOS, Inc." <contact@dbos.dev>
|
|
6
6
|
License: MIT
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
8
|
Requires-Dist: pyyaml>=6.0.2
|
|
9
|
-
Requires-Dist: jsonschema>=4.23.0
|
|
10
|
-
Requires-Dist: alembic>=1.13.3
|
|
11
|
-
Requires-Dist: typing-extensions>=4.12.2; python_version < "3.10"
|
|
12
|
-
Requires-Dist: typer>=0.12.5
|
|
13
|
-
Requires-Dist: jsonpickle>=3.3.0
|
|
14
|
-
Requires-Dist: opentelemetry-api>=1.27.0
|
|
15
|
-
Requires-Dist: opentelemetry-sdk>=1.27.0
|
|
16
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27.0
|
|
17
9
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
18
|
-
Requires-Dist: fastapi[standard]>=0.115.2
|
|
19
|
-
Requires-Dist: tomlkit>=0.13.2
|
|
20
10
|
Requires-Dist: psycopg[binary]>=3.1
|
|
21
|
-
Requires-Dist: docker>=7.1.0
|
|
22
|
-
Requires-Dist: cryptography>=43.0.3
|
|
23
|
-
Requires-Dist: rich>=13.9.4
|
|
24
|
-
Requires-Dist: pyjwt>=2.10.1
|
|
25
11
|
Requires-Dist: websockets>=14.0
|
|
12
|
+
Requires-Dist: typer-slim>=0.17.4
|
|
13
|
+
Requires-Dist: sqlalchemy>=2.0.43
|
|
14
|
+
Provides-Extra: otel
|
|
15
|
+
Requires-Dist: opentelemetry-api>=1.37.0; extra == "otel"
|
|
16
|
+
Requires-Dist: opentelemetry-sdk>=1.37.0; extra == "otel"
|
|
17
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.37.0; extra == "otel"
|
|
26
18
|
Description-Content-Type: text/markdown
|
|
27
19
|
|
|
28
20
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import sys
|
|
3
2
|
import time
|
|
4
3
|
import uuid
|
|
5
4
|
from typing import (
|
|
@@ -15,17 +14,11 @@ from typing import (
|
|
|
15
14
|
Union,
|
|
16
15
|
)
|
|
17
16
|
|
|
17
|
+
from dbos import _serialization
|
|
18
18
|
from dbos._app_db import ApplicationDatabase
|
|
19
19
|
from dbos._context import MaxPriority, MinPriority
|
|
20
20
|
from dbos._sys_db import SystemDatabase
|
|
21
21
|
|
|
22
|
-
if sys.version_info < (3, 11):
|
|
23
|
-
from typing_extensions import NotRequired
|
|
24
|
-
else:
|
|
25
|
-
from typing import NotRequired
|
|
26
|
-
|
|
27
|
-
from dbos import _serialization
|
|
28
|
-
|
|
29
22
|
if TYPE_CHECKING:
|
|
30
23
|
from dbos._dbos import WorkflowHandle, WorkflowHandleAsync
|
|
31
24
|
|
|
@@ -58,14 +51,20 @@ from dbos._workflow_commands import (
|
|
|
58
51
|
R = TypeVar("R", covariant=True) # A generic type for workflow return values
|
|
59
52
|
|
|
60
53
|
|
|
61
|
-
|
|
54
|
+
# Required EnqueueOptions fields
|
|
55
|
+
class _EnqueueOptionsRequired(TypedDict):
|
|
62
56
|
workflow_name: str
|
|
63
57
|
queue_name: str
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Optional EnqueueOptions fields
|
|
61
|
+
class EnqueueOptions(_EnqueueOptionsRequired, total=False):
|
|
62
|
+
workflow_id: str
|
|
63
|
+
app_version: str
|
|
64
|
+
workflow_timeout: float
|
|
65
|
+
deduplication_id: str
|
|
66
|
+
priority: int
|
|
67
|
+
max_recovery_attempts: int
|
|
69
68
|
|
|
70
69
|
|
|
71
70
|
def validate_enqueue_options(options: EnqueueOptions) -> None:
|
|
@@ -8,9 +8,11 @@ from contextvars import ContextVar
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from enum import Enum
|
|
10
10
|
from types import TracebackType
|
|
11
|
-
from typing import List, Literal, Optional, Type, TypedDict
|
|
11
|
+
from typing import TYPE_CHECKING, List, Literal, Optional, Type, TypedDict
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from opentelemetry.trace import Span
|
|
12
15
|
|
|
13
|
-
from opentelemetry.trace import Span, Status, StatusCode, use_span
|
|
14
16
|
from sqlalchemy.orm import Session
|
|
15
17
|
|
|
16
18
|
from dbos._utils import GlobalParams
|
|
@@ -78,8 +80,8 @@ class ContextSpan:
|
|
|
78
80
|
context_manager: The context manager that is used to manage the span's lifecycle.
|
|
79
81
|
"""
|
|
80
82
|
|
|
81
|
-
span: Span
|
|
82
|
-
context_manager: AbstractContextManager[Span]
|
|
83
|
+
span: "Span"
|
|
84
|
+
context_manager: "AbstractContextManager[Span]"
|
|
83
85
|
|
|
84
86
|
|
|
85
87
|
class DBOSContext:
|
|
@@ -217,19 +219,21 @@ class DBOSContext:
|
|
|
217
219
|
|
|
218
220
|
""" Return the current DBOS span if any. It must be a span created by DBOS."""
|
|
219
221
|
|
|
220
|
-
def get_current_dbos_span(self) -> Optional[Span]:
|
|
222
|
+
def get_current_dbos_span(self) -> "Optional[Span]":
|
|
221
223
|
if len(self.context_spans) > 0:
|
|
222
224
|
return self.context_spans[-1].span
|
|
223
225
|
return None
|
|
224
226
|
|
|
225
227
|
""" Return the current active span if any. It might not be a DBOS span."""
|
|
226
228
|
|
|
227
|
-
def get_current_active_span(self) -> Optional[Span]:
|
|
229
|
+
def get_current_active_span(self) -> "Optional[Span]":
|
|
228
230
|
return dbos_tracer.get_current_span()
|
|
229
231
|
|
|
230
232
|
def _start_span(self, attributes: TracedAttributes) -> None:
|
|
231
233
|
if dbos_tracer.disable_otlp:
|
|
232
234
|
return
|
|
235
|
+
from opentelemetry.trace import use_span
|
|
236
|
+
|
|
233
237
|
attributes["operationUUID"] = (
|
|
234
238
|
self.workflow_id if len(self.workflow_id) > 0 else None
|
|
235
239
|
)
|
|
@@ -257,6 +261,8 @@ class DBOSContext:
|
|
|
257
261
|
def _end_span(self, exc_value: Optional[BaseException]) -> None:
|
|
258
262
|
if dbos_tracer.disable_otlp:
|
|
259
263
|
return
|
|
264
|
+
from opentelemetry.trace import Status, StatusCode
|
|
265
|
+
|
|
260
266
|
context_span = self.context_spans.pop()
|
|
261
267
|
if exc_value is None:
|
|
262
268
|
context_span.span.set_status(Status(StatusCode.OK))
|
|
@@ -14,6 +14,7 @@ from typing import (
|
|
|
14
14
|
Coroutine,
|
|
15
15
|
Generic,
|
|
16
16
|
Optional,
|
|
17
|
+
ParamSpec,
|
|
17
18
|
TypeVar,
|
|
18
19
|
Union,
|
|
19
20
|
cast,
|
|
@@ -22,14 +23,8 @@ from typing import (
|
|
|
22
23
|
from dbos._outcome import Immediate, NoResult, Outcome, Pending
|
|
23
24
|
from dbos._utils import GlobalParams, retriable_postgres_exception
|
|
24
25
|
|
|
25
|
-
from ._app_db import ApplicationDatabase, TransactionResultInternal
|
|
26
|
-
|
|
27
|
-
if sys.version_info < (3, 10):
|
|
28
|
-
from typing_extensions import ParamSpec
|
|
29
|
-
else:
|
|
30
|
-
from typing import ParamSpec
|
|
31
|
-
|
|
32
26
|
from . import _serialization
|
|
27
|
+
from ._app_db import ApplicationDatabase, TransactionResultInternal
|
|
33
28
|
from ._context import (
|
|
34
29
|
DBOSAssumeRole,
|
|
35
30
|
DBOSContext,
|
|
@@ -28,9 +28,6 @@ from typing import (
|
|
|
28
28
|
Union,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
from opentelemetry.trace import Span
|
|
32
|
-
from rich import print
|
|
33
|
-
|
|
34
31
|
from dbos._conductor.conductor import ConductorWebsocket
|
|
35
32
|
from dbos._debouncer import debouncer_workflow
|
|
36
33
|
from dbos._sys_db import SystemDatabase, WorkflowStatus
|
|
@@ -53,7 +50,6 @@ from ._core import (
|
|
|
53
50
|
set_event,
|
|
54
51
|
start_workflow,
|
|
55
52
|
start_workflow_async,
|
|
56
|
-
workflow_wrapper,
|
|
57
53
|
)
|
|
58
54
|
from ._queue import Queue, queue_thread
|
|
59
55
|
from ._recovery import recover_pending_workflows, startup_recovery_thread
|
|
@@ -62,8 +58,6 @@ from ._registrations import (
|
|
|
62
58
|
DBOSClassInfo,
|
|
63
59
|
_class_fqn,
|
|
64
60
|
get_or_create_class_info,
|
|
65
|
-
set_dbos_func_name,
|
|
66
|
-
set_temp_workflow_type,
|
|
67
61
|
)
|
|
68
62
|
from ._roles import default_required_roles, required_roles
|
|
69
63
|
from ._scheduler import ScheduledWorkflow, scheduled
|
|
@@ -80,13 +74,11 @@ if TYPE_CHECKING:
|
|
|
80
74
|
from fastapi import FastAPI
|
|
81
75
|
from ._kafka import _KafkaConsumerWorkflow
|
|
82
76
|
from flask import Flask
|
|
77
|
+
from opentelemetry.trace import Span
|
|
83
78
|
|
|
84
|
-
from
|
|
79
|
+
from typing import ParamSpec
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
from typing_extensions import ParamSpec
|
|
88
|
-
else:
|
|
89
|
-
from typing import ParamSpec
|
|
81
|
+
from sqlalchemy.orm import Session
|
|
90
82
|
|
|
91
83
|
from ._admin_server import AdminServer
|
|
92
84
|
from ._app_db import ApplicationDatabase
|
|
@@ -558,7 +550,7 @@ class DBOS:
|
|
|
558
550
|
f"https://console.dbos.dev/self-host?appname={app_name}"
|
|
559
551
|
)
|
|
560
552
|
print(
|
|
561
|
-
f"
|
|
553
|
+
f"To view and manage workflows, connect to DBOS Conductor at:{conductor_registration_url}"
|
|
562
554
|
)
|
|
563
555
|
|
|
564
556
|
# Flush handlers and add OTLP to all loggers if enabled
|
|
@@ -1297,7 +1289,7 @@ class DBOS:
|
|
|
1297
1289
|
return ctx.parent_workflow_id
|
|
1298
1290
|
|
|
1299
1291
|
@classproperty
|
|
1300
|
-
def span(cls) -> Span:
|
|
1292
|
+
def span(cls) -> "Span":
|
|
1301
1293
|
"""Return the tracing `Span` associated with the current context."""
|
|
1302
1294
|
ctx = assert_current_dbos_context()
|
|
1303
1295
|
span = ctx.get_current_active_span()
|
|
@@ -5,8 +5,6 @@ from importlib import resources
|
|
|
5
5
|
from typing import Any, Dict, List, Optional, TypedDict, cast
|
|
6
6
|
|
|
7
7
|
import yaml
|
|
8
|
-
from jsonschema import ValidationError, validate
|
|
9
|
-
from rich import print
|
|
10
8
|
from sqlalchemy import make_url
|
|
11
9
|
|
|
12
10
|
from ._error import DBOSInitializationError
|
|
@@ -36,7 +34,7 @@ class DBOSConfig(TypedDict, total=False):
|
|
|
36
34
|
otlp_attributes (dict[str, str]): A set of custom attributes to apply OTLP-exported logs and traces
|
|
37
35
|
application_version (str): Application version
|
|
38
36
|
executor_id (str): Executor ID, used to identify the application instance in distributed environments
|
|
39
|
-
|
|
37
|
+
enable_otlp (bool): If True, enable built-in DBOS OTLP tracing and logging.
|
|
40
38
|
"""
|
|
41
39
|
|
|
42
40
|
name: str
|
|
@@ -54,7 +52,7 @@ class DBOSConfig(TypedDict, total=False):
|
|
|
54
52
|
otlp_attributes: Optional[dict[str, str]]
|
|
55
53
|
application_version: Optional[str]
|
|
56
54
|
executor_id: Optional[str]
|
|
57
|
-
|
|
55
|
+
enable_otlp: Optional[bool]
|
|
58
56
|
|
|
59
57
|
|
|
60
58
|
class RuntimeConfig(TypedDict, total=False):
|
|
@@ -97,7 +95,7 @@ class TelemetryConfig(TypedDict, total=False):
|
|
|
97
95
|
logs: Optional[LoggerConfig]
|
|
98
96
|
OTLPExporter: Optional[OTLPExporterConfig]
|
|
99
97
|
otlp_attributes: Optional[dict[str, str]]
|
|
100
|
-
disable_otlp:
|
|
98
|
+
disable_otlp: bool
|
|
101
99
|
|
|
102
100
|
|
|
103
101
|
class ConfigFile(TypedDict, total=False):
|
|
@@ -165,10 +163,12 @@ def translate_dbos_config_to_config_file(config: DBOSConfig) -> ConfigFile:
|
|
|
165
163
|
]
|
|
166
164
|
|
|
167
165
|
# Telemetry config
|
|
166
|
+
enable_otlp = config.get("enable_otlp", None)
|
|
167
|
+
disable_otlp = True if enable_otlp is None else not enable_otlp
|
|
168
168
|
telemetry: TelemetryConfig = {
|
|
169
169
|
"OTLPExporter": {"tracesEndpoint": [], "logsEndpoint": []},
|
|
170
170
|
"otlp_attributes": config.get("otlp_attributes", {}),
|
|
171
|
-
"disable_otlp":
|
|
171
|
+
"disable_otlp": disable_otlp,
|
|
172
172
|
}
|
|
173
173
|
# For mypy
|
|
174
174
|
assert telemetry["OTLPExporter"] is not None
|
|
@@ -265,17 +265,6 @@ def load_config(
|
|
|
265
265
|
)
|
|
266
266
|
data = cast(Dict[str, Any], data)
|
|
267
267
|
|
|
268
|
-
# Load the JSON schema relative to the package root
|
|
269
|
-
schema_file = resources.files("dbos").joinpath("dbos-config.schema.json")
|
|
270
|
-
with schema_file.open("r") as f:
|
|
271
|
-
schema = json.load(f)
|
|
272
|
-
|
|
273
|
-
# Validate the data against the schema
|
|
274
|
-
try:
|
|
275
|
-
validate(instance=data, schema=schema)
|
|
276
|
-
except ValidationError as e:
|
|
277
|
-
raise DBOSInitializationError(f"Validation error: {e}")
|
|
278
|
-
|
|
279
268
|
# Special case: convert logsEndpoint and tracesEndpoint from strings to lists of strings, if present
|
|
280
269
|
if "telemetry" in data and "OTLPExporter" in data["telemetry"]:
|
|
281
270
|
if "logsEndpoint" in data["telemetry"]["OTLPExporter"]:
|
|
@@ -441,17 +430,13 @@ def process_config(
|
|
|
441
430
|
printable_sys_db_url = make_url(data["system_database_url"]).render_as_string(
|
|
442
431
|
hide_password=True
|
|
443
432
|
)
|
|
444
|
-
print(
|
|
445
|
-
f"[bold blue]DBOS system database URL: {printable_sys_db_url}[/bold blue]"
|
|
446
|
-
)
|
|
433
|
+
print(f"DBOS system database URL: {printable_sys_db_url}")
|
|
447
434
|
if data["database_url"].startswith("sqlite"):
|
|
448
435
|
print(
|
|
449
|
-
f"
|
|
436
|
+
f"Using SQLite as a system database. The SQLite system database is for development and testing. PostgreSQL is recommended for production use."
|
|
450
437
|
)
|
|
451
438
|
else:
|
|
452
|
-
print(
|
|
453
|
-
f"[bold blue]Database engine parameters: {data['database']['db_engine_kwargs']}[/bold blue]"
|
|
454
|
-
)
|
|
439
|
+
print(f"Database engine parameters: {data['database']['db_engine_kwargs']}")
|
|
455
440
|
|
|
456
441
|
# Return data as ConfigFile type
|
|
457
442
|
return data
|
|
@@ -563,12 +548,15 @@ def overwrite_config(provided_config: ConfigFile) -> ConfigFile:
|
|
|
563
548
|
if "telemetry" not in provided_config or provided_config["telemetry"] is None:
|
|
564
549
|
provided_config["telemetry"] = {
|
|
565
550
|
"OTLPExporter": {"tracesEndpoint": [], "logsEndpoint": []},
|
|
551
|
+
"disable_otlp": False,
|
|
566
552
|
}
|
|
567
|
-
|
|
568
|
-
provided_config["telemetry"]["
|
|
569
|
-
|
|
570
|
-
"
|
|
571
|
-
|
|
553
|
+
else:
|
|
554
|
+
provided_config["telemetry"]["disable_otlp"] = False
|
|
555
|
+
if "OTLPExporter" not in provided_config["telemetry"]:
|
|
556
|
+
provided_config["telemetry"]["OTLPExporter"] = {
|
|
557
|
+
"tracesEndpoint": [],
|
|
558
|
+
"logsEndpoint": [],
|
|
559
|
+
}
|
|
572
560
|
|
|
573
561
|
# This is a super messy from a typing perspective.
|
|
574
562
|
# Some of ConfigFile keys are optional -- but in practice they'll always be present in hosted environments
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import math
|
|
3
|
-
import sys
|
|
4
3
|
import time
|
|
5
4
|
import types
|
|
6
5
|
import uuid
|
|
@@ -12,17 +11,12 @@ from typing import (
|
|
|
12
11
|
Dict,
|
|
13
12
|
Generic,
|
|
14
13
|
Optional,
|
|
14
|
+
ParamSpec,
|
|
15
15
|
Tuple,
|
|
16
16
|
TypedDict,
|
|
17
17
|
TypeVar,
|
|
18
|
-
Union,
|
|
19
18
|
)
|
|
20
19
|
|
|
21
|
-
if sys.version_info < (3, 10):
|
|
22
|
-
from typing_extensions import ParamSpec
|
|
23
|
-
else:
|
|
24
|
-
from typing import ParamSpec
|
|
25
|
-
|
|
26
20
|
from dbos._client import (
|
|
27
21
|
DBOSClient,
|
|
28
22
|
EnqueueOptions,
|
|
@@ -147,7 +141,6 @@ class Debouncer(Generic[P, R]):
|
|
|
147
141
|
self,
|
|
148
142
|
workflow_name: str,
|
|
149
143
|
*,
|
|
150
|
-
debounce_key: str,
|
|
151
144
|
debounce_timeout_sec: Optional[float] = None,
|
|
152
145
|
queue: Optional[Queue] = None,
|
|
153
146
|
):
|
|
@@ -157,13 +150,11 @@ class Debouncer(Generic[P, R]):
|
|
|
157
150
|
"queue_name": queue.name if queue else None,
|
|
158
151
|
"workflow_name": workflow_name,
|
|
159
152
|
}
|
|
160
|
-
self.debounce_key = debounce_key
|
|
161
153
|
|
|
162
154
|
@staticmethod
|
|
163
155
|
def create(
|
|
164
156
|
workflow: Callable[P, R],
|
|
165
157
|
*,
|
|
166
|
-
debounce_key: str,
|
|
167
158
|
debounce_timeout_sec: Optional[float] = None,
|
|
168
159
|
queue: Optional[Queue] = None,
|
|
169
160
|
) -> "Debouncer[P, R]":
|
|
@@ -172,7 +163,6 @@ class Debouncer(Generic[P, R]):
|
|
|
172
163
|
raise TypeError("Only workflow functions may be debounced, not methods")
|
|
173
164
|
return Debouncer[P, R](
|
|
174
165
|
get_dbos_func_name(workflow),
|
|
175
|
-
debounce_key=debounce_key,
|
|
176
166
|
debounce_timeout_sec=debounce_timeout_sec,
|
|
177
167
|
queue=queue,
|
|
178
168
|
)
|
|
@@ -181,7 +171,6 @@ class Debouncer(Generic[P, R]):
|
|
|
181
171
|
def create_async(
|
|
182
172
|
workflow: Callable[P, Coroutine[Any, Any, R]],
|
|
183
173
|
*,
|
|
184
|
-
debounce_key: str,
|
|
185
174
|
debounce_timeout_sec: Optional[float] = None,
|
|
186
175
|
queue: Optional[Queue] = None,
|
|
187
176
|
) -> "Debouncer[P, R]":
|
|
@@ -190,13 +179,16 @@ class Debouncer(Generic[P, R]):
|
|
|
190
179
|
raise TypeError("Only workflow functions may be debounced, not methods")
|
|
191
180
|
return Debouncer[P, R](
|
|
192
181
|
get_dbos_func_name(workflow),
|
|
193
|
-
debounce_key=debounce_key,
|
|
194
182
|
debounce_timeout_sec=debounce_timeout_sec,
|
|
195
183
|
queue=queue,
|
|
196
184
|
)
|
|
197
185
|
|
|
198
186
|
def debounce(
|
|
199
|
-
self,
|
|
187
|
+
self,
|
|
188
|
+
debounce_key: str,
|
|
189
|
+
debounce_period_sec: float,
|
|
190
|
+
*args: P.args,
|
|
191
|
+
**kwargs: P.kwargs,
|
|
200
192
|
) -> "WorkflowHandle[R]":
|
|
201
193
|
from dbos._dbos import DBOS, _get_dbos_instance
|
|
202
194
|
|
|
@@ -232,9 +224,7 @@ class Debouncer(Generic[P, R]):
|
|
|
232
224
|
while True:
|
|
233
225
|
try:
|
|
234
226
|
# Attempt to enqueue a debouncer for this workflow.
|
|
235
|
-
deduplication_id =
|
|
236
|
-
f"{self.options['workflow_name']}-{self.debounce_key}"
|
|
237
|
-
)
|
|
227
|
+
deduplication_id = f"{self.options['workflow_name']}-{debounce_key}"
|
|
238
228
|
with SetEnqueueOptions(deduplication_id=deduplication_id):
|
|
239
229
|
with SetWorkflowTimeout(None):
|
|
240
230
|
internal_queue.enqueue(
|
|
@@ -284,6 +274,7 @@ class Debouncer(Generic[P, R]):
|
|
|
284
274
|
|
|
285
275
|
async def debounce_async(
|
|
286
276
|
self,
|
|
277
|
+
debounce_key: str,
|
|
287
278
|
debounce_period_sec: float,
|
|
288
279
|
*args: P.args,
|
|
289
280
|
**kwargs: P.kwargs,
|
|
@@ -292,7 +283,7 @@ class Debouncer(Generic[P, R]):
|
|
|
292
283
|
|
|
293
284
|
dbos = _get_dbos_instance()
|
|
294
285
|
handle = await asyncio.to_thread(
|
|
295
|
-
self.debounce, debounce_period_sec, *args, **kwargs
|
|
286
|
+
self.debounce, debounce_key, debounce_period_sec, *args, **kwargs
|
|
296
287
|
)
|
|
297
288
|
return WorkflowHandleAsyncPolling(handle.workflow_id, dbos)
|
|
298
289
|
|
|
@@ -304,7 +295,6 @@ class DebouncerClient:
|
|
|
304
295
|
client: DBOSClient,
|
|
305
296
|
workflow_options: EnqueueOptions,
|
|
306
297
|
*,
|
|
307
|
-
debounce_key: str,
|
|
308
298
|
debounce_timeout_sec: Optional[float] = None,
|
|
309
299
|
queue: Optional[Queue] = None,
|
|
310
300
|
):
|
|
@@ -314,11 +304,10 @@ class DebouncerClient:
|
|
|
314
304
|
"queue_name": queue.name if queue else None,
|
|
315
305
|
"workflow_name": workflow_options["workflow_name"],
|
|
316
306
|
}
|
|
317
|
-
self.debounce_key = debounce_key
|
|
318
307
|
self.client = client
|
|
319
308
|
|
|
320
309
|
def debounce(
|
|
321
|
-
self, debounce_period_sec: float, *args: Any, **kwargs: Any
|
|
310
|
+
self, debounce_key: str, debounce_period_sec: float, *args: Any, **kwargs: Any
|
|
322
311
|
) -> "WorkflowHandle[R]":
|
|
323
312
|
|
|
324
313
|
ctxOptions: ContextOptions = {
|
|
@@ -337,7 +326,7 @@ class DebouncerClient:
|
|
|
337
326
|
try:
|
|
338
327
|
# Attempt to enqueue a debouncer for this workflow.
|
|
339
328
|
deduplication_id = (
|
|
340
|
-
f"{self.debouncer_options['workflow_name']}-{
|
|
329
|
+
f"{self.debouncer_options['workflow_name']}-{debounce_key}"
|
|
341
330
|
)
|
|
342
331
|
debouncer_options: EnqueueOptions = {
|
|
343
332
|
"workflow_name": DEBOUNCER_WORKFLOW_NAME,
|
|
@@ -390,10 +379,10 @@ class DebouncerClient:
|
|
|
390
379
|
)
|
|
391
380
|
|
|
392
381
|
async def debounce_async(
|
|
393
|
-
self, debounce_period_sec: float, *args: Any, **kwargs: Any
|
|
382
|
+
self, deboucne_key: str, debounce_period_sec: float, *args: Any, **kwargs: Any
|
|
394
383
|
) -> "WorkflowHandleAsync[R]":
|
|
395
384
|
handle: "WorkflowHandle[R]" = await asyncio.to_thread(
|
|
396
|
-
self.debounce, debounce_period_sec, *args, **kwargs
|
|
385
|
+
self.debounce, deboucne_key, debounce_period_sec, *args, **kwargs
|
|
397
386
|
)
|
|
398
387
|
return WorkflowHandleClientAsyncPolling[R](
|
|
399
388
|
handle.workflow_id, self.client._sys_db
|
|
@@ -4,8 +4,6 @@ import sys
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Union
|
|
6
6
|
|
|
7
|
-
from fastapi_cli.discover import get_module_data_from_path
|
|
8
|
-
|
|
9
7
|
from dbos import DBOS
|
|
10
8
|
|
|
11
9
|
|
|
@@ -34,12 +32,6 @@ def debug_workflow(workflow_id: str, entrypoint: Union[str, PythonModule]) -> No
|
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
def parse_start_command(command: str) -> Union[str, PythonModule]:
|
|
37
|
-
match = re.match(r"fastapi\s+run\s+(\.?[\w/]+\.py)", command)
|
|
38
|
-
if match:
|
|
39
|
-
# Mirror the logic in fastapi's run command by converting the path argument to a module
|
|
40
|
-
mod_data = get_module_data_from_path(Path(match.group(1)))
|
|
41
|
-
sys.path.insert(0, str(mod_data.extra_sys_path))
|
|
42
|
-
return PythonModule(mod_data.module_import_str)
|
|
43
35
|
match = re.match(r"python3?\s+(\.?[\w/]+\.py)", command)
|
|
44
36
|
if match:
|
|
45
37
|
return match.group(1)
|