db.Session: Add

Add database session API to db. This is a breaking change, because
from this commit on, a session object has to be passed to every
query.

This commit also removes any reference to Cmds / App objects. An
instantiated database object can be worked with outside of an App.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2025-03-10 18:44:39 +01:00
commit 495cebd769
5 changed files with 56 additions and 31 deletions

View file

@ -3,21 +3,27 @@
from typing import Any from typing import Any
import abc import abc
from contextlib import contextmanager
from jwutils.Config import Config from jwutils.Config import Config
from jwutils.db.schema.Schema import Schema from jwutils.db.schema.Schema import Schema
from jwutils import Cmds from jwutils import Cmds
from .Session import Session
from ..log import *
class DataBase(abc.ABC): class DataBase(abc.ABC):
def __init__(self, schema: Schema, conf: Config, app: Any) -> None: def __init__(self, schema: Schema, conf: Config) -> None:
self.__conf = conf self.__conf = conf
self.__app = app
self.__schema = schema self.__schema = schema
conf.dump(NOTICE, "Initializing database with configuration")
@property @abc.abstractmethod
def app(self) -> Cmds: def _create_session(self):
return self.__app pass
def _delete_session(self, session: Session):
del session
@property @property
def schema(self): def schema(self):
@ -26,3 +32,11 @@ class DataBase(abc.ABC):
@property @property
def conf(self): def conf(self):
return self.__conf return self.__conf
@contextmanager
def session(self):
ret = self._create_session()
try:
yield ret
finally:
self._delete_session(ret)

View file

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
import abc
class Session(abc.ABC): # export
def __init__(self, db):
self.__db = db
@property
def db(self):
return self.__db

View file

@ -24,8 +24,8 @@ class Queries(abc.ABC): # export
# -- implement API # -- implement API
def _run(self, *args, **kwargs) -> QueryResult: def _run(self, session, *args, **kwargs) -> QueryResult:
return self.__func(*args, **kwargs) return self.__func(session, *args, **kwargs)
def _register(self): def _register(self):
raise Exception('Can\'t call register on this object') raise Exception('Can\'t call register on this object')
@ -57,10 +57,6 @@ class Queries(abc.ABC): # export
def db(self) -> DataBase: def db(self) -> DataBase:
return self.__db return self.__db
@property
def app(self) -> Cmds:
return self.__db.app
def load(self, modules: list[str], cls=QueryBase): def load(self, modules: list[str], cls=QueryBase):
for path in modules: for path in modules:
slog(INFO, f'Loading modules from {path}') slog(INFO, f'Loading modules from {path}')

View file

@ -8,6 +8,7 @@ from jwutils.log import *
from jwutils.misc import load_classes from jwutils.misc import load_classes
from jwutils.Cmds import Cmds from jwutils.Cmds import Cmds
from jwutils.db.DataBase import DataBase from jwutils.db.DataBase import DataBase
from jwutils.db.Session import Session
from jwutils.db.query.QueryResult import QueryResult from jwutils.db.query.QueryResult import QueryResult
#from jwutils.db.query.Queries import Queries #from jwutils.db.query.Queries import Queries
@ -19,7 +20,7 @@ class Query(abc.ABC): # export
# -- pure virtuals # -- pure virtuals
@abc.abstractmethod @abc.abstractmethod
def _run(self, *args, **kwargs) -> QueryResult: def _run(self, session: Session, *args, **kwargs) -> QueryResult:
raise Exception('Called pure virtual _run()') raise Exception('Called pure virtual _run()')
@abc.abstractmethod @abc.abstractmethod
@ -40,8 +41,8 @@ class Query(abc.ABC): # export
def _add(self, query_name: str, location: str, func: Any): def _add(self, query_name: str, location: str, func: Any):
return self.__parent.add(self, query_name, location, func) return self.__parent.add(self, query_name, location, func)
def run(self, *args, **kwargs) -> QueryResult: def run(self, session: Session, *args, **kwargs) -> QueryResult:
return self._run(*args, **kwargs) return self._run(session, *args, **kwargs)
@property @property
def parent(self): def parent(self):
@ -55,10 +56,6 @@ class Query(abc.ABC): # export
def schema(self): def schema(self):
return self.__parent.db.schema return self.__parent.db.schema
@property
def app(self) -> Cmds:
return self.__parent.app
@property @property
def column_names(self) -> list[str]: def column_names(self) -> list[str]:
return self._column_names() return self._column_names()

View file

@ -8,27 +8,33 @@ from enum import Enum, auto
from jwutils.log import * from jwutils.log import *
from jwutils.Cmds import Cmds from jwutils.Cmds import Cmds
from jwutils.db.DataBase import DataBase from jwutils.db.DataBase import DataBase
from jwutils.db.Session import Session
class ResType(Enum): # export class ResType(Enum): # export
Statement = auto() Statement = auto()
Scalars = auto() Scalars = auto()
One = auto() One = auto()
First = auto() First = auto()
Pages = auto() Pages = auto()
class QueryResult(abc.ABC): # export class QueryResult(abc.ABC): # export
def __init__(self, query: Any) -> None: def __init__(self, session: Session, query: Any) -> None:
self.__query = query self.__query = query
self.__session = session
@property
def app(self) -> Cmds:
return self.__query.app
@property @property
def db(self) -> DataBase: def db(self) -> DataBase:
return self.__query.db return self.__query.db
@property
def query(self) -> DataBase:
return self.__query
@property
def session(self) -> DataBase:
return self.__session
@property @property
def schema(self): def schema(self):
return self.__query.db.schema return self.__query.db.schema
@ -36,8 +42,8 @@ class QueryResult(abc.ABC): # export
def rows(self) -> list[Any]: def rows(self) -> list[Any]:
return self._cast(ResType.Scalars) return self._cast(ResType.Scalars)
def pages(self, per_page=20) -> Any: def pages(self, per_page=20, page=1) -> Any:
return self._cast(ResType.Pages, per_page=per_page) return self._cast(ResType.Pages, per_page=per_page, page=page)
def one(self) -> Any: def one(self) -> Any:
return self._cast(ResType.One) return self._cast(ResType.One)
@ -52,5 +58,5 @@ class QueryResult(abc.ABC): # export
# -- pure virtuals # -- pure virtuals
@abc.abstractmethod @abc.abstractmethod
def _cast(self, res_type: ResType, **kwargs) -> Union[Any|list[Any]]: def _cast(self, session: Session, res_type: ResType, **kwargs) -> Union[Any|list[Any]]:
pass pass