dbos-enterprise 0.1.0a2__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.
- dbos_enterprise-0.1.0a2/LICENSE +5 -0
- dbos_enterprise-0.1.0a2/PKG-INFO +34 -0
- dbos_enterprise-0.1.0a2/README.md +10 -0
- dbos_enterprise-0.1.0a2/dbos_enterprise/__init__.py +7 -0
- dbos_enterprise-0.1.0a2/dbos_enterprise/_scratch.py +22 -0
- dbos_enterprise-0.1.0a2/dbos_enterprise/_version.py +24 -0
- dbos_enterprise-0.1.0a2/dbos_enterprise/py.typed +0 -0
- dbos_enterprise-0.1.0a2/pyproject.toml +93 -0
- dbos_enterprise-0.1.0a2/tests/__init__.py +0 -0
- dbos_enterprise-0.1.0a2/tests/conftest.py +92 -0
- dbos_enterprise-0.1.0a2/tests/test_scratch.py +24 -0
- dbos_enterprise-0.1.0a2/tests/test_version.py +38 -0
- dbos_enterprise-0.1.0a2/version/__init__.py +45 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dbos-enterprise
|
|
3
|
+
Version: 0.1.0a2
|
|
4
|
+
Summary: Enterprise extension for DBOS Transact
|
|
5
|
+
Author-Email: "DBOS, Inc." <contact@dbos.dev>
|
|
6
|
+
License: DBOS Conductor Software License (https://www.dbos.dev/conductor-license)
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: Other/Proprietary License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: dbos<3.3,>=3.2.0a0
|
|
22
|
+
Requires-Dist: websockets>=14.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# DBOS Enterprise for Python
|
|
26
|
+
|
|
27
|
+
Closed-source extension of [DBOS Transact](https://github.com/dbos-inc/dbos-transact-py).
|
|
28
|
+
Requires a DBOS Conductor API key.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
pip install dbos[enterprise]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
See [DEVELOPING.md](./DEVELOPING.md) to work on this repo.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# DBOS Enterprise for Python
|
|
2
|
+
|
|
3
|
+
Closed-source extension of [DBOS Transact](https://github.com/dbos-inc/dbos-transact-py).
|
|
4
|
+
Requires a DBOS Conductor API key.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
pip install dbos[enterprise]
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
See [DEVELOPING.md](./DEVELOPING.md) to work on this repo.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Scratch code proving the package can reach core internals. Delete when real code lands."""
|
|
2
|
+
|
|
3
|
+
from typing import TypedDict
|
|
4
|
+
|
|
5
|
+
from dbos._utils import GlobalParams
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExecutorInfo(TypedDict):
|
|
9
|
+
app_name: str | None
|
|
10
|
+
app_version: str
|
|
11
|
+
executor_id: str
|
|
12
|
+
dbos_version: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def describe_executor() -> ExecutorInfo:
|
|
16
|
+
"""Report the identity core assigns this process at launch."""
|
|
17
|
+
return {
|
|
18
|
+
"app_name": GlobalParams.app_name,
|
|
19
|
+
"app_version": GlobalParams.app_version,
|
|
20
|
+
"executor_id": GlobalParams.executor_id,
|
|
21
|
+
"dbos_version": GlobalParams.dbos_version,
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
|
|
4
|
+
_MAJOR_MINOR = re.compile(r"^(\d+)\.(\d+)")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DBOSEnterpriseVersionError(Exception):
|
|
8
|
+
"""dbos and dbos-enterprise are installed at different minor versions."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _major_minor(package: str) -> tuple[int, int]:
|
|
12
|
+
match = _MAJOR_MINOR.match(version(package))
|
|
13
|
+
assert match is not None, f"unparseable version for {package}"
|
|
14
|
+
return int(match.group(1)), int(match.group(2))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check_core_version() -> None:
|
|
18
|
+
"""Raise unless dbos and dbos-enterprise share a major.minor version."""
|
|
19
|
+
core, enterprise = _major_minor("dbos"), _major_minor("dbos-enterprise")
|
|
20
|
+
if core != enterprise:
|
|
21
|
+
raise DBOSEnterpriseVersionError(
|
|
22
|
+
f"dbos {version('dbos')} and dbos-enterprise {version('dbos-enterprise')} "
|
|
23
|
+
"must share a minor version; upgrade both with `pip install -U dbos[enterprise]`."
|
|
24
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dbos-enterprise"
|
|
3
|
+
dynamic = []
|
|
4
|
+
description = "Enterprise extension for DBOS Transact"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "DBOS, Inc.", email = "contact@dbos.dev" },
|
|
7
|
+
]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"dbos>=3.2.0a0,<3.3",
|
|
10
|
+
"websockets>=14.0",
|
|
11
|
+
]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Programming Language :: Python",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"License :: Other/Proprietary License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
]
|
|
29
|
+
version = "0.1.0a2"
|
|
30
|
+
|
|
31
|
+
[project.license]
|
|
32
|
+
text = "DBOS Conductor Software License (https://www.dbos.dev/conductor-license)"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = [
|
|
36
|
+
"pdm-backend",
|
|
37
|
+
]
|
|
38
|
+
build-backend = "pdm.backend"
|
|
39
|
+
|
|
40
|
+
[tool.pdm]
|
|
41
|
+
distribution = true
|
|
42
|
+
|
|
43
|
+
[tool.pdm.build]
|
|
44
|
+
includes = [
|
|
45
|
+
"dbos_enterprise/",
|
|
46
|
+
]
|
|
47
|
+
source-includes = [
|
|
48
|
+
"tests/",
|
|
49
|
+
"version/",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[tool.pdm.version]
|
|
53
|
+
source = "scm"
|
|
54
|
+
version_format = "version:format_version"
|
|
55
|
+
|
|
56
|
+
[tool.black]
|
|
57
|
+
line-length = 88
|
|
58
|
+
|
|
59
|
+
[tool.isort]
|
|
60
|
+
profile = "black"
|
|
61
|
+
filter_files = true
|
|
62
|
+
atomic = true
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
strict = true
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
addopts = "-s"
|
|
69
|
+
log_cli_format = "%(asctime)s [%(levelname)8s] (%(name)s:%(filename)s:%(lineno)s) %(message)s"
|
|
70
|
+
log_cli_level = "INFO"
|
|
71
|
+
log_cli = true
|
|
72
|
+
timeout = 120
|
|
73
|
+
|
|
74
|
+
[dependency-groups]
|
|
75
|
+
dev = [
|
|
76
|
+
"pytest>=8.3.3",
|
|
77
|
+
"mypy>=1.15.0",
|
|
78
|
+
"pytest-mock>=3.14.0",
|
|
79
|
+
"black>=24.10.0",
|
|
80
|
+
"pre-commit>=4.0.1",
|
|
81
|
+
"isort>=5.13.2",
|
|
82
|
+
"GitPython>=3.1.43",
|
|
83
|
+
"pdm-backend>=2.4.2",
|
|
84
|
+
"pytest-asyncio>=0.25.0",
|
|
85
|
+
"pyright>=1.1.398",
|
|
86
|
+
"pytest-timeout>=2.3.1",
|
|
87
|
+
"psycopg[binary]>=3.1",
|
|
88
|
+
"types-PyYAML>=6.0.12.20240808",
|
|
89
|
+
"types-python-dateutil>=2.9.0.20260518",
|
|
90
|
+
]
|
|
91
|
+
core = [
|
|
92
|
+
"-e file:///${PROJECT_ROOT}/../dbos-transact-py",
|
|
93
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sqlite3
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Generator
|
|
5
|
+
from urllib.parse import quote
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
import sqlalchemy as sa
|
|
9
|
+
from dbos import DBOS, DBOSConfig
|
|
10
|
+
from dbos._utils import GlobalParams
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def using_sqlite() -> bool:
|
|
14
|
+
return os.environ.get("DBOS_DATABASE", None) == "SQLITE"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture()
|
|
18
|
+
def skip_with_sqlite() -> None:
|
|
19
|
+
if using_sqlite():
|
|
20
|
+
pytest.skip("Skipping test when testing SQLite")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def postgres_system_url() -> str:
|
|
24
|
+
password = quote(os.environ.get("PGPASSWORD", "dbos"), safe="")
|
|
25
|
+
return f"postgresql+psycopg://postgres:{password}@localhost:5432/dbosenterprise_test_dbos_sys"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture()
|
|
29
|
+
def sqlite_path(tmp_path: Path) -> Path:
|
|
30
|
+
# WAL mode, so a committing writer never stalls the notification poller.
|
|
31
|
+
db_path = tmp_path / "test.sqlite"
|
|
32
|
+
connection = sqlite3.connect(db_path)
|
|
33
|
+
try:
|
|
34
|
+
connection.execute("PRAGMA journal_mode=WAL")
|
|
35
|
+
finally:
|
|
36
|
+
connection.close()
|
|
37
|
+
return db_path
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.fixture()
|
|
41
|
+
def config(sqlite_path: Path) -> DBOSConfig:
|
|
42
|
+
return {
|
|
43
|
+
"name": "enterprise-test-app",
|
|
44
|
+
"system_database_url": (
|
|
45
|
+
f"sqlite:///{sqlite_path}" if using_sqlite() else postgres_system_url()
|
|
46
|
+
),
|
|
47
|
+
"enable_otlp": False,
|
|
48
|
+
"notification_listener_polling_interval_sec": 0.01,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def reset_global_params() -> None:
|
|
53
|
+
GlobalParams.app_version = os.environ.get("DBOS__APPVERSION", "")
|
|
54
|
+
GlobalParams.executor_id = os.environ.get("DBOS__VMID") or "local"
|
|
55
|
+
GlobalParams.app_name = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.fixture(autouse=True)
|
|
59
|
+
def clean_process_identity() -> Generator[None, Any, None]:
|
|
60
|
+
yield
|
|
61
|
+
reset_global_params()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _drop_system_database() -> None:
|
|
65
|
+
# Each test migrates from scratch; the suite is small enough for that.
|
|
66
|
+
DBOS.destroy(destroy_registry=True)
|
|
67
|
+
for var in ("DBOS__VMID", "DBOS__APPVERSION", "DBOS__APPID"):
|
|
68
|
+
os.environ.pop(var, None)
|
|
69
|
+
reset_global_params()
|
|
70
|
+
if using_sqlite():
|
|
71
|
+
return
|
|
72
|
+
url = sa.make_url(postgres_system_url())
|
|
73
|
+
engine = sa.create_engine(
|
|
74
|
+
url.set(database="postgres"), connect_args={"connect_timeout": 30}
|
|
75
|
+
)
|
|
76
|
+
try:
|
|
77
|
+
with engine.connect() as connection:
|
|
78
|
+
connection.execution_options(isolation_level="AUTOCOMMIT")
|
|
79
|
+
connection.execute(
|
|
80
|
+
sa.text(f"DROP DATABASE IF EXISTS {url.database} WITH (FORCE)")
|
|
81
|
+
)
|
|
82
|
+
finally:
|
|
83
|
+
engine.dispose()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.fixture()
|
|
87
|
+
def dbos(config: DBOSConfig) -> Generator[DBOS, Any, None]:
|
|
88
|
+
_drop_system_database()
|
|
89
|
+
dbos = DBOS(config=config)
|
|
90
|
+
DBOS.launch()
|
|
91
|
+
yield dbos
|
|
92
|
+
DBOS.destroy(destroy_registry=True)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from dbos import DBOS
|
|
2
|
+
|
|
3
|
+
from dbos_enterprise._scratch import describe_executor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_workflow_runs_against_core(dbos: DBOS) -> None:
|
|
7
|
+
@DBOS.step()
|
|
8
|
+
def greet_step(name: str) -> str:
|
|
9
|
+
return f"hello, {name}"
|
|
10
|
+
|
|
11
|
+
@DBOS.workflow()
|
|
12
|
+
def greet(name: str) -> str:
|
|
13
|
+
return greet_step(name)
|
|
14
|
+
|
|
15
|
+
assert greet("enterprise") == "hello, enterprise"
|
|
16
|
+
steps = DBOS.list_workflow_steps(DBOS.list_workflows()[0].workflow_id)
|
|
17
|
+
assert [s["function_name"] for s in steps] == [greet_step.__qualname__]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_describe_executor(dbos: DBOS) -> None:
|
|
21
|
+
info = describe_executor()
|
|
22
|
+
assert info["app_name"] == "enterprise-test-app"
|
|
23
|
+
assert info["executor_id"] == "local"
|
|
24
|
+
assert info["dbos_version"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from dbos_enterprise import (
|
|
4
|
+
DBOSEnterpriseVersionError,
|
|
5
|
+
__version__,
|
|
6
|
+
_version,
|
|
7
|
+
check_core_version,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _fake_version(versions: dict[str, str]) -> "type[object]":
|
|
12
|
+
def fake(package: str) -> str:
|
|
13
|
+
return versions[package]
|
|
14
|
+
|
|
15
|
+
return fake # type: ignore[return-value]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_matching_minor_passes(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
19
|
+
monkeypatch.setattr(
|
|
20
|
+
_version,
|
|
21
|
+
"version",
|
|
22
|
+
_fake_version({"dbos": "3.4.1", "dbos-enterprise": "3.4.0"}),
|
|
23
|
+
)
|
|
24
|
+
check_core_version()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_mismatched_minor_raises(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
28
|
+
monkeypatch.setattr(
|
|
29
|
+
_version,
|
|
30
|
+
"version",
|
|
31
|
+
_fake_version({"dbos": "3.5.0", "dbos-enterprise": "3.4.0"}),
|
|
32
|
+
)
|
|
33
|
+
with pytest.raises(DBOSEnterpriseVersionError, match="share a minor version"):
|
|
34
|
+
check_core_version()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_package_reports_a_version() -> None:
|
|
38
|
+
assert __version__
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from pdm.backend.hooks.version import SCMVersion
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def format_version(git_version: SCMVersion) -> str:
|
|
5
|
+
"""
|
|
6
|
+
Format version into string.
|
|
7
|
+
|
|
8
|
+
1. Release versions may only be published from release branches. Their version is a git tag.
|
|
9
|
+
2. Preview versions are published from main. They are PEP440 alpha releases whose version is the
|
|
10
|
+
next release version number followed by "a" followed by the number of commits since the last release.
|
|
11
|
+
If the last release was 1.2.3 and there have been ten commits since, the preview version is 1.2.3a10
|
|
12
|
+
3. Test versions are published from feature branches. They are PEP440 local versions tagged with a git hash.
|
|
13
|
+
"""
|
|
14
|
+
assert git_version.branch is not None
|
|
15
|
+
is_release = "release" in git_version.branch
|
|
16
|
+
is_preview = git_version.branch == "main"
|
|
17
|
+
|
|
18
|
+
next_version = guess_next_version(str(git_version.version))
|
|
19
|
+
|
|
20
|
+
if git_version.distance is None:
|
|
21
|
+
if is_release:
|
|
22
|
+
version = str(git_version.version)
|
|
23
|
+
elif is_preview:
|
|
24
|
+
version = f"{next_version}a0"
|
|
25
|
+
else:
|
|
26
|
+
version = f"{next_version}a0+{git_version.node}"
|
|
27
|
+
else:
|
|
28
|
+
if is_release:
|
|
29
|
+
raise Exception(
|
|
30
|
+
f"Release branches may only publish tagged releases. Distance: {git_version.distance}"
|
|
31
|
+
)
|
|
32
|
+
elif is_preview:
|
|
33
|
+
version = f"{next_version}a{git_version.distance}"
|
|
34
|
+
else:
|
|
35
|
+
version = f"{next_version}a{git_version.distance}+{git_version.node}"
|
|
36
|
+
|
|
37
|
+
return version
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def guess_next_version(version_number: str) -> str:
|
|
41
|
+
# A repo with no release tag yet reports "0.0"; pad it to three parts.
|
|
42
|
+
parts = [int(part) for part in version_number.split(".")]
|
|
43
|
+
major, minor, patch = (parts + [0, 0, 0])[:3]
|
|
44
|
+
minor += 1
|
|
45
|
+
return f"{major}.{minor}.{patch}"
|