sonnet-graph 0.1.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.
- sonnet_graph/__init__.py +51 -0
- sonnet_graph/checks/__init__.py +11 -0
- sonnet_graph/checks/graphdb_health_check.py +35 -0
- sonnet_graph/checks/graphdb_initialization.py +28 -0
- sonnet_graph/checks/stages.py +13 -0
- sonnet_graph/context.py +151 -0
- sonnet_graph/dependencies.py +53 -0
- sonnet_graph/extension.py +72 -0
- sonnet_graph-0.1.0.dist-info/METADATA +75 -0
- sonnet_graph-0.1.0.dist-info/RECORD +12 -0
- sonnet_graph-0.1.0.dist-info/WHEEL +5 -0
- sonnet_graph-0.1.0.dist-info/top_level.txt +1 -0
sonnet_graph/__init__.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""sonnet-graph -- Graph database integration for sonnet-server applications.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
|
|
5
|
+
Extension (register in app.py):
|
|
6
|
+
GraphDBExtension -- pool lifecycle, readiness checks
|
|
7
|
+
|
|
8
|
+
Pool context:
|
|
9
|
+
borrow_graphdb -- context manager: acquire connection + set ContextVar
|
|
10
|
+
get_current_graphdb -- read ambient CypherGraphDB from ContextVar
|
|
11
|
+
init_graphdb_pool -- lazily create the pool singleton
|
|
12
|
+
dispose_graphdb_pool -- close and discard the pool
|
|
13
|
+
is_pool_initialized -- check if pool exists
|
|
14
|
+
graphdb_configured -- check if CGDB_* settings are present
|
|
15
|
+
|
|
16
|
+
FastAPI dependencies:
|
|
17
|
+
get_graphdb_session -- async generator for router-level ambient session
|
|
18
|
+
get_graphdb -- sync generator yielding CypherGraphDB explicitly
|
|
19
|
+
|
|
20
|
+
Readiness pipeline (auto-registered by GraphDBExtension):
|
|
21
|
+
GraphdbStage -- pipeline stage marker (order=40)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from sonnet_graph.checks.stages import GraphdbStage
|
|
25
|
+
from sonnet_graph.context import (
|
|
26
|
+
borrow_graphdb,
|
|
27
|
+
dispose_graphdb_pool,
|
|
28
|
+
get_current_graphdb,
|
|
29
|
+
graphdb_configured,
|
|
30
|
+
init_graphdb_pool,
|
|
31
|
+
is_pool_initialized,
|
|
32
|
+
)
|
|
33
|
+
from sonnet_graph.dependencies import get_graphdb, get_graphdb_session
|
|
34
|
+
from sonnet_graph.extension import GraphDBExtension
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
# Extension
|
|
38
|
+
"GraphDBExtension",
|
|
39
|
+
# Pool context
|
|
40
|
+
"borrow_graphdb",
|
|
41
|
+
"dispose_graphdb_pool",
|
|
42
|
+
"get_current_graphdb",
|
|
43
|
+
"graphdb_configured",
|
|
44
|
+
"init_graphdb_pool",
|
|
45
|
+
"is_pool_initialized",
|
|
46
|
+
# FastAPI dependencies
|
|
47
|
+
"get_graphdb",
|
|
48
|
+
"get_graphdb_session",
|
|
49
|
+
# Readiness pipeline
|
|
50
|
+
"GraphdbStage",
|
|
51
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Graph DB readiness checks for the sonnet-graph package.
|
|
2
|
+
|
|
3
|
+
Importing this package registers ``GraphdbStage`` in the global stage
|
|
4
|
+
registry (via the ``@readiness_stage`` decorator in ``stages.py``).
|
|
5
|
+
|
|
6
|
+
Check classes are registered lazily -- they are imported by
|
|
7
|
+
``GraphDBExtension.on_startup()`` so they only appear in the pipeline
|
|
8
|
+
when the extension is active.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from sonnet_graph.checks.stages import GraphdbStage # noqa: F401 -- registers stage
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""GraphDB connection health check for readiness pipeline."""
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from sonnet_graph.checks.stages import GraphdbStage
|
|
6
|
+
from sonnet_graph.context import borrow_graphdb
|
|
7
|
+
from sonnet_server.readiness_pipeline import ReadinessCheck, ReadinessCheckResult, readiness_check
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@readiness_check(stage=GraphdbStage, is_critical=True, run_once=False, order=1)
|
|
11
|
+
class GraphDBHealthCheck(ReadinessCheck):
|
|
12
|
+
"""Verifies a live connection to the graph database."""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
super().__init__("graphdb_health_check")
|
|
16
|
+
|
|
17
|
+
def _execute(self) -> ReadinessCheckResult:
|
|
18
|
+
logger.info("Checking GraphDB connection health")
|
|
19
|
+
try:
|
|
20
|
+
with borrow_graphdb() as cdb:
|
|
21
|
+
if not cdb.check_connection():
|
|
22
|
+
return self.failed(
|
|
23
|
+
"CypherGraphDB connection check failed",
|
|
24
|
+
{"graph": getattr(cdb, "graph_name", None)},
|
|
25
|
+
)
|
|
26
|
+
return self.success(
|
|
27
|
+
"CypherGraphDB connection is healthy",
|
|
28
|
+
{
|
|
29
|
+
"id": getattr(cdb, "id", None),
|
|
30
|
+
"graph": getattr(cdb, "graph_name", None),
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
except (OSError, RuntimeError, ConnectionError, TimeoutError) as e:
|
|
34
|
+
logger.error("GraphDB health check failed: {}", e)
|
|
35
|
+
return self.failed(f"GraphDB health check failed: {e}", {"error": str(e)})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""GraphDB pool initialization check for readiness pipeline."""
|
|
2
|
+
|
|
3
|
+
from loguru import logger
|
|
4
|
+
|
|
5
|
+
from sonnet_graph.checks.stages import GraphdbStage
|
|
6
|
+
from sonnet_graph.context import init_graphdb_pool, is_pool_initialized
|
|
7
|
+
from sonnet_server.readiness_pipeline import ReadinessCheck, ReadinessCheckResult, readiness_check
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@readiness_check(stage=GraphdbStage, is_critical=True, run_once=True, order=0)
|
|
11
|
+
class GraphDBInitializationCheck(ReadinessCheck):
|
|
12
|
+
"""Ensures the CypherGraphDB connection pool is initialized."""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
super().__init__("graphdb_initialization")
|
|
16
|
+
|
|
17
|
+
def _execute(self) -> ReadinessCheckResult:
|
|
18
|
+
logger.info("Checking GraphDB pool initialization")
|
|
19
|
+
try:
|
|
20
|
+
if not is_pool_initialized():
|
|
21
|
+
init_graphdb_pool()
|
|
22
|
+
return self.success("CypherGraphDB pool initialized", {"initialized": True})
|
|
23
|
+
except (OSError, RuntimeError, ConnectionError, TimeoutError) as e:
|
|
24
|
+
logger.error("CypherGraphDB initialization failed: {}", e)
|
|
25
|
+
return self.failed(
|
|
26
|
+
f"CypherGraphDB initialization failed: {e}",
|
|
27
|
+
{"error": str(e), "type": type(e).__name__, "initialized": False},
|
|
28
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Graph database readiness pipeline stage.
|
|
2
|
+
|
|
3
|
+
Registered at import time via the @readiness_stage decorator.
|
|
4
|
+
Order 40 places it after database (1), messaging (2), cache (3)
|
|
5
|
+
but before application-specific stages (50+).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sonnet_server.readiness_pipeline import readiness_stage
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@readiness_stage(order=40, description="Graph database checks", is_critical=False, fail_fast=True)
|
|
12
|
+
class GraphdbStage:
|
|
13
|
+
"""CypherGraphDB connection pool initialization and health."""
|
sonnet_graph/context.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Graph DB connection pool lifecycle and ambient session management.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- ``init_graphdb_pool()`` -- lazily initialise the connection pool
|
|
5
|
+
- ``dispose_graphdb_pool()`` -- close and discard the pool
|
|
6
|
+
- ``borrow_graphdb()`` -- context manager: acquire connection, set ContextVar
|
|
7
|
+
- ``get_current_graphdb()`` -- read the ambient CypherGraphDB from ContextVar
|
|
8
|
+
- ``is_pool_initialized()`` -- check if the pool has been created
|
|
9
|
+
- ``graphdb_configured()`` -- check if CGDB_* settings are present
|
|
10
|
+
|
|
11
|
+
Configuration comes from cypher-graphdb's own settings
|
|
12
|
+
(CGDB_BACKEND, CGDB_CINFO, CGDB_GRAPH) -- not duplicated here.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Iterator
|
|
18
|
+
from contextlib import contextmanager, suppress
|
|
19
|
+
from contextvars import ContextVar
|
|
20
|
+
|
|
21
|
+
from cypher_graphdb import CypherGraphDB
|
|
22
|
+
from cypher_graphdb.dbpool import CypherGraphDBPool
|
|
23
|
+
from cypher_graphdb.settings import Settings as CypherSettings
|
|
24
|
+
from cypher_graphdb.settings import get_settings as get_cypher_settings
|
|
25
|
+
from loguru import logger
|
|
26
|
+
|
|
27
|
+
_current_graphdb: ContextVar[CypherGraphDB | None] = ContextVar("_current_graphdb", default=None)
|
|
28
|
+
|
|
29
|
+
# Held as a module-level variable so dispose_graphdb_pool() can close it
|
|
30
|
+
# without risk of accidentally recreating it.
|
|
31
|
+
_pool_instance: CypherGraphDBPool | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def graphdb_configured(settings: CypherSettings | None = None) -> bool:
|
|
35
|
+
"""Return True if the required CGDB_* environment variables are set."""
|
|
36
|
+
s = settings or get_cypher_settings()
|
|
37
|
+
return bool(s.backend and s.cinfo and s.graph)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_current_graphdb() -> CypherGraphDB:
|
|
41
|
+
"""Return the ambient CypherGraphDB session.
|
|
42
|
+
|
|
43
|
+
Must be called within a ``borrow_graphdb()`` context.
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
RuntimeError: If no active session exists.
|
|
47
|
+
"""
|
|
48
|
+
cdb = _current_graphdb.get()
|
|
49
|
+
if cdb is None:
|
|
50
|
+
raise RuntimeError("No active CypherGraphDB session -- call within borrow_graphdb() context")
|
|
51
|
+
return cdb
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@contextmanager
|
|
55
|
+
def borrow_graphdb(block: bool = True, timeout: float = 10.0) -> Iterator[CypherGraphDB]:
|
|
56
|
+
"""Acquire a CypherGraphDB from the pool and set the ambient ContextVar.
|
|
57
|
+
|
|
58
|
+
The connection is returned to the pool when the context exits.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
block: Whether to block if no connections are available.
|
|
62
|
+
timeout: Maximum seconds to wait for a connection.
|
|
63
|
+
"""
|
|
64
|
+
pool = _get_or_create_pool()
|
|
65
|
+
with pool.acquire(block=block, timeout=timeout) as cdb:
|
|
66
|
+
token = _current_graphdb.set(cdb)
|
|
67
|
+
try:
|
|
68
|
+
yield cdb
|
|
69
|
+
finally:
|
|
70
|
+
_current_graphdb.reset(token)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def init_graphdb_pool(*, pool_size: int = 3) -> bool:
|
|
74
|
+
"""Initialize the graph DB connection pool.
|
|
75
|
+
|
|
76
|
+
Must be called before the first ``borrow_graphdb()`` to configure
|
|
77
|
+
pool_size. Safe to call multiple times -- idempotent once created.
|
|
78
|
+
|
|
79
|
+
Returns True if the pool was created, False if CGDB_* settings are missing.
|
|
80
|
+
"""
|
|
81
|
+
global _pool_instance
|
|
82
|
+
if not graphdb_configured():
|
|
83
|
+
return False
|
|
84
|
+
if _pool_instance is None:
|
|
85
|
+
_pool_instance = _create_pool(pool_size)
|
|
86
|
+
logger.info("GraphDB pool initialized")
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def dispose_graphdb_pool() -> None:
|
|
91
|
+
"""Shut down the pool and release all connections."""
|
|
92
|
+
global _pool_instance
|
|
93
|
+
if _pool_instance is not None:
|
|
94
|
+
with suppress(RuntimeError, ConnectionError, OSError):
|
|
95
|
+
_pool_instance.close()
|
|
96
|
+
_pool_instance = None
|
|
97
|
+
logger.info("GraphDB pool disposed")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def is_pool_initialized() -> bool:
|
|
101
|
+
"""Return True if the pool has been created."""
|
|
102
|
+
return _pool_instance is not None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# -- internal ----------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _get_or_create_pool() -> CypherGraphDBPool:
|
|
109
|
+
"""Return the existing pool or create one with default pool_size=3.
|
|
110
|
+
|
|
111
|
+
Called by ``borrow_graphdb()`` when the pool was not pre-initialized
|
|
112
|
+
via ``init_graphdb_pool()``. Falls back to pool_size=3.
|
|
113
|
+
"""
|
|
114
|
+
global _pool_instance
|
|
115
|
+
if _pool_instance is None:
|
|
116
|
+
_pool_instance = _create_pool(pool_size=3)
|
|
117
|
+
return _pool_instance
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _create_pool(pool_size: int) -> CypherGraphDBPool:
|
|
121
|
+
"""Create and return a new CypherGraphDBPool."""
|
|
122
|
+
settings = get_cypher_settings()
|
|
123
|
+
if not settings.backend:
|
|
124
|
+
raise RuntimeError("Graph backend not configured. Set CGDB_BACKEND.")
|
|
125
|
+
if not settings.cinfo:
|
|
126
|
+
raise RuntimeError("Graph connection not configured. Set CGDB_CINFO.")
|
|
127
|
+
if not settings.graph:
|
|
128
|
+
raise RuntimeError("Graph name not configured. Set CGDB_GRAPH.")
|
|
129
|
+
|
|
130
|
+
connect_params = {
|
|
131
|
+
"cinfo": settings.cinfo,
|
|
132
|
+
"graph_name": settings.graph,
|
|
133
|
+
"read_only": settings.read_only,
|
|
134
|
+
"create_graph": settings.create_graph,
|
|
135
|
+
}
|
|
136
|
+
# 0 means "no timeout" -- don't pass it (leave backend at its own default).
|
|
137
|
+
if settings.query_timeout_s:
|
|
138
|
+
connect_params["query_timeout_s"] = settings.query_timeout_s
|
|
139
|
+
pool = CypherGraphDBPool(
|
|
140
|
+
backend=settings.backend,
|
|
141
|
+
connect_params=connect_params,
|
|
142
|
+
pool_size=pool_size,
|
|
143
|
+
auto_connect=True,
|
|
144
|
+
)
|
|
145
|
+
logger.debug(
|
|
146
|
+
"GraphDB pool created: backend={}, graph={}, pool_size={}",
|
|
147
|
+
settings.backend,
|
|
148
|
+
settings.graph,
|
|
149
|
+
pool_size,
|
|
150
|
+
)
|
|
151
|
+
return pool
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""FastAPI dependencies for graph database access.
|
|
2
|
+
|
|
3
|
+
Provides two dependency patterns:
|
|
4
|
+
|
|
5
|
+
1. **Ambient session** (recommended) -- use ``graphdb_router()`` from
|
|
6
|
+
sonnet-server's ``api.dependencies`` module, or add
|
|
7
|
+
``get_graphdb_session`` as a router-level dependency. Services then
|
|
8
|
+
call ``get_current_graphdb()`` without parameters.
|
|
9
|
+
|
|
10
|
+
2. **Explicit parameter** -- use ``Depends(get_graphdb)`` on individual
|
|
11
|
+
endpoints to receive a ``CypherGraphDB`` instance directly.
|
|
12
|
+
|
|
13
|
+
Example::
|
|
14
|
+
|
|
15
|
+
from fastapi import APIRouter, Depends
|
|
16
|
+
from sonnet_graph.dependencies import get_graphdb_session
|
|
17
|
+
from sonnet_graph.context import get_current_graphdb
|
|
18
|
+
|
|
19
|
+
router = APIRouter(dependencies=[Depends(get_graphdb_session)])
|
|
20
|
+
|
|
21
|
+
@router.get("/nodes")
|
|
22
|
+
def list_nodes():
|
|
23
|
+
db = get_current_graphdb()
|
|
24
|
+
return db.execute("MATCH (n) RETURN n LIMIT 10")
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from collections.abc import AsyncIterator, Iterator
|
|
30
|
+
|
|
31
|
+
from cypher_graphdb import CypherGraphDB
|
|
32
|
+
|
|
33
|
+
from sonnet_graph.context import borrow_graphdb
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def get_graphdb_session() -> AsyncIterator[None]:
|
|
37
|
+
"""Router-level dependency establishing an ambient graphdb session.
|
|
38
|
+
|
|
39
|
+
Sets the ContextVar so services can call ``get_current_graphdb()``
|
|
40
|
+
without receiving CypherGraphDB as a parameter.
|
|
41
|
+
"""
|
|
42
|
+
with borrow_graphdb():
|
|
43
|
+
yield
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_graphdb() -> Iterator[CypherGraphDB]:
|
|
47
|
+
"""Endpoint-level dependency yielding a CypherGraphDB instance explicitly.
|
|
48
|
+
|
|
49
|
+
Prefer the ambient session pattern (``get_graphdb_session`` + ``get_current_graphdb``)
|
|
50
|
+
for new code.
|
|
51
|
+
"""
|
|
52
|
+
with borrow_graphdb() as db:
|
|
53
|
+
yield db
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""GraphDB infrastructure extension for sonnet-server applications.
|
|
2
|
+
|
|
3
|
+
Manages the CypherGraphDB connection pool lifecycle (startup/shutdown)
|
|
4
|
+
and optionally registers readiness checks.
|
|
5
|
+
|
|
6
|
+
Usage in app.py::
|
|
7
|
+
|
|
8
|
+
from sonnet_graph import GraphDBExtension
|
|
9
|
+
|
|
10
|
+
_extension_registry = create_extension_registry(
|
|
11
|
+
GraphDBExtension(), # required -- raises if CGDB_* missing
|
|
12
|
+
GraphDBExtension(optional=True), # optional -- skips if unconfigured
|
|
13
|
+
)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from loguru import logger
|
|
19
|
+
|
|
20
|
+
from sonnet_server.extensions import Extension
|
|
21
|
+
from sonnet_server.settings import Settings
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GraphDBExtension(Extension):
|
|
25
|
+
"""Infrastructure extension for the CypherGraphDB connection pool.
|
|
26
|
+
|
|
27
|
+
Starts unconditionally (no profile). Initialises the pool on startup
|
|
28
|
+
and disposes it on shutdown.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
optional: If True, silently skip when CGDB_* settings are absent.
|
|
32
|
+
If False (default), raise RuntimeError on missing settings.
|
|
33
|
+
pool_size: Maximum number of pooled connections (default: 3).
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, *, optional: bool = False, pool_size: int = 3) -> None:
|
|
37
|
+
self._optional = optional
|
|
38
|
+
self._pool_size = pool_size
|
|
39
|
+
self._active = False
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def name(self) -> str:
|
|
43
|
+
return "graphdb"
|
|
44
|
+
|
|
45
|
+
async def on_startup(self, settings: Settings) -> None:
|
|
46
|
+
from sonnet_graph.context import graphdb_configured, init_graphdb_pool
|
|
47
|
+
|
|
48
|
+
if not graphdb_configured():
|
|
49
|
+
if self._optional:
|
|
50
|
+
logger.info("GraphDBExtension: CGDB_* settings not configured, skipping (optional)")
|
|
51
|
+
return
|
|
52
|
+
raise RuntimeError(
|
|
53
|
+
"GraphDBExtension: graph database not configured. "
|
|
54
|
+
"Set CGDB_BACKEND, CGDB_CINFO, and CGDB_GRAPH environment variables."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
init_graphdb_pool(pool_size=self._pool_size)
|
|
58
|
+
self._active = True
|
|
59
|
+
|
|
60
|
+
# Import check modules to trigger @readiness_check decorator registration.
|
|
61
|
+
import sonnet_graph.checks.graphdb_health_check # noqa: F401
|
|
62
|
+
import sonnet_graph.checks.graphdb_initialization # noqa: F401
|
|
63
|
+
|
|
64
|
+
logger.info("GraphDBExtension: pool initialized, readiness checks registered")
|
|
65
|
+
|
|
66
|
+
async def on_shutdown(self) -> None:
|
|
67
|
+
if not self._active:
|
|
68
|
+
return
|
|
69
|
+
from sonnet_graph.context import dispose_graphdb_pool
|
|
70
|
+
|
|
71
|
+
dispose_graphdb_pool()
|
|
72
|
+
self._active = False
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sonnet-graph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Graph database integration (cypher-graphdb) for sonnet-server applications
|
|
5
|
+
Author-email: Wolfgang Miller <wolfgang.miller@petrarca-labs.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
11
|
+
Requires-Python: <4.0,>=3.14
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: sonnet-server>=0.1.11
|
|
14
|
+
Requires-Dist: cypher-graphdb>=0.2.7
|
|
15
|
+
Requires-Dist: loguru>=0.7.3
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
20
|
+
|
|
21
|
+
# sonnet-graph
|
|
22
|
+
|
|
23
|
+
Graph database integration (cypher-graphdb) for sonnet-server applications.
|
|
24
|
+
|
|
25
|
+
Provides reusable infrastructure for sonnet-server apps that need a
|
|
26
|
+
cypher-graphdb connection pool:
|
|
27
|
+
|
|
28
|
+
- **`GraphDBExtension`** -- sonnet-server extension managing pool lifecycle
|
|
29
|
+
(startup/shutdown), with optional mode for apps where graph is not required.
|
|
30
|
+
- **Pool context** -- `borrow_graphdb()` context manager and `get_current_graphdb()`
|
|
31
|
+
ambient session accessor via `ContextVar`.
|
|
32
|
+
- **Readiness checks** -- `GraphdbStage` with initialization and health checks
|
|
33
|
+
for the sonnet-server readiness pipeline.
|
|
34
|
+
- **FastAPI dependencies** -- `get_graphdb_session()` async generator for
|
|
35
|
+
router-level ambient sessions.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install sonnet-graph
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from sonnet_graph import GraphDBExtension, borrow_graphdb, get_current_graphdb
|
|
47
|
+
|
|
48
|
+
# In app.py -- register the extension
|
|
49
|
+
_extension_registry = create_extension_registry(
|
|
50
|
+
DatabaseExtension(),
|
|
51
|
+
GraphDBExtension(), # required (raises on missing CGDB_* settings)
|
|
52
|
+
GraphDBExtension(optional=True), # or optional (skips if unconfigured)
|
|
53
|
+
...
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# In service code -- borrow a connection
|
|
57
|
+
with borrow_graphdb() as db:
|
|
58
|
+
db.execute("MATCH (n) RETURN count(n)")
|
|
59
|
+
|
|
60
|
+
# In service code -- ambient session (within borrow_graphdb scope)
|
|
61
|
+
db = get_current_graphdb()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
Graph connection is configured via cypher-graphdb's own environment variables
|
|
67
|
+
(not duplicated in consumer settings):
|
|
68
|
+
|
|
69
|
+
| Variable | Description |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `CGDB_BACKEND` | Backend type: `age` or `memgraph` |
|
|
72
|
+
| `CGDB_CINFO` | Connection string / DSN |
|
|
73
|
+
| `CGDB_GRAPH` | Graph name |
|
|
74
|
+
| `CGDB_READ_ONLY` | Read-only mode (default: `false`) |
|
|
75
|
+
| `CGDB_CREATE_GRAPH_IF_NOT_EXISTS` | Auto-create graph, AGE only (default: `false`) |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
sonnet_graph/__init__.py,sha256=fFQOGmLtKebF3gcrw3NmAk_KvCWXNWWcjb5HmgmrIdE,1634
|
|
2
|
+
sonnet_graph/context.py,sha256=bpohxZZeU-Hw_crxty-GeFiC6uNXFkcKHOXe5d9F9lo,5263
|
|
3
|
+
sonnet_graph/dependencies.py,sha256=9Cbse8q9Wwcy06sHpgCgM_GBQG3hpsAwMTjoy5NuNVE,1639
|
|
4
|
+
sonnet_graph/extension.py,sha256=zxONLgP2Dg0h1RqtM0HWgzic9vL2ph6cRIk8dO21g90,2466
|
|
5
|
+
sonnet_graph/checks/__init__.py,sha256=YjS-WiUifxxmznb6hjclpT3uVoelUhgYwH8zLMngIQ4,449
|
|
6
|
+
sonnet_graph/checks/graphdb_health_check.py,sha256=4O1QPpOkEmvZtWzdea-4nEF1MeQwWyJUwSN6rueLyI4,1468
|
|
7
|
+
sonnet_graph/checks/graphdb_initialization.py,sha256=45-sX3cMBqgWfbwU0l1rPtirxjFD6_CA6NALhDReWs8,1224
|
|
8
|
+
sonnet_graph/checks/stages.py,sha256=pZbMcGKr-0vwSQQcK_F_pdPCD5Mo3y4aJC8rF_Gtl4I,471
|
|
9
|
+
sonnet_graph-0.1.0.dist-info/METADATA,sha256=nsyTdsVRsimmuw1bT1y0dfxqVfJxU3UWbnfPF_52AAM,2561
|
|
10
|
+
sonnet_graph-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
11
|
+
sonnet_graph-0.1.0.dist-info/top_level.txt,sha256=HCrJGvnLfVHPBz2E9t6WM-QHMQTDOeE1QjH1k-XZtiU,13
|
|
12
|
+
sonnet_graph-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sonnet_graph
|