apppy-db 0.1.0__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.
- apppy_db-0.1.0/.gitignore +28 -0
- apppy_db-0.1.0/PKG-INFO +15 -0
- apppy_db-0.1.0/README.md +0 -0
- apppy_db-0.1.0/db.mk +23 -0
- apppy_db-0.1.0/pyproject.toml +29 -0
- apppy_db-0.1.0/src/apppy/db/__init__.py +0 -0
- apppy_db-0.1.0/src/apppy/db/migrations.py +12 -0
- apppy_db-0.1.0/src/apppy/db/postgres.py +73 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
__generated__/
|
|
2
|
+
dist/
|
|
3
|
+
*.egg-info
|
|
4
|
+
.env
|
|
5
|
+
.env.*
|
|
6
|
+
*.env
|
|
7
|
+
!.env.ci
|
|
8
|
+
.file_store/
|
|
9
|
+
*.pid
|
|
10
|
+
.python-version
|
|
11
|
+
*.secrets
|
|
12
|
+
.secrets
|
|
13
|
+
*.tar.gz
|
|
14
|
+
*.test_output/
|
|
15
|
+
.test_output/
|
|
16
|
+
uv.lock
|
|
17
|
+
*.whl
|
|
18
|
+
|
|
19
|
+
# System files
|
|
20
|
+
__pycache__
|
|
21
|
+
.DS_Store
|
|
22
|
+
|
|
23
|
+
# Editor files
|
|
24
|
+
*.sublime-project
|
|
25
|
+
*.sublime-workspace
|
|
26
|
+
.vscode/*
|
|
27
|
+
!.vscode/settings.json
|
|
28
|
+
|
apppy_db-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apppy-db
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python database definitions for server development
|
|
5
|
+
Project-URL: Homepage, https://github.com/spals/apppy
|
|
6
|
+
Author: Tim Kral
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: apppy-env>=0.1.0
|
|
12
|
+
Requires-Dist: apppy-logger>=0.1.0
|
|
13
|
+
Requires-Dist: fastapi-lifespan-manager==0.1.4
|
|
14
|
+
Requires-Dist: psycopg-pool==3.2.2
|
|
15
|
+
Requires-Dist: psycopg[binary]==3.1.19
|
apppy_db-0.1.0/README.md
ADDED
|
File without changes
|
apppy_db-0.1.0/db.mk
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
ifndef APPPY_DB_MK_INCLUDED
|
|
2
|
+
APPPY_DB_MK_INCLUDED := 1
|
|
3
|
+
DB_PKG_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
|
4
|
+
|
|
5
|
+
.PHONY: db db-dev db/build db/clean db/install db/install-dev
|
|
6
|
+
|
|
7
|
+
db: db/clean db/install
|
|
8
|
+
|
|
9
|
+
db-dev: db/clean db/install-dev
|
|
10
|
+
|
|
11
|
+
db/build:
|
|
12
|
+
cd $(DB_PKG_DIR) && uvx --from build pyproject-build
|
|
13
|
+
|
|
14
|
+
db/clean:
|
|
15
|
+
cd $(DB_PKG_DIR) && rm -rf dist/ *.egg-info .venv
|
|
16
|
+
|
|
17
|
+
db/install: db/build
|
|
18
|
+
cd $(DB_PKG_DIR) && uv pip install dist/*.whl
|
|
19
|
+
|
|
20
|
+
db/install-dev:
|
|
21
|
+
cd $(DB_PKG_DIR) && uv pip install -e .
|
|
22
|
+
|
|
23
|
+
endif # APPPY_DB_MK_INCLUDED
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "apppy-db"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python database definitions for server development"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{ name = "Tim Kral" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"apppy-env>=0.1.0",
|
|
19
|
+
"apppy-logger>=0.1.0",
|
|
20
|
+
"fastapi-lifespan-manager==0.1.4",
|
|
21
|
+
"psycopg[binary]==3.1.19",
|
|
22
|
+
"psycopg-pool==3.2.2"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/spals/apppy"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/apppy"]
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import psycopg
|
|
2
|
+
from fastapi_lifespan_manager import LifespanManager
|
|
3
|
+
from psycopg.rows import DictRow, dict_row
|
|
4
|
+
from psycopg_pool.pool_async import AsyncConnectionPool as DBConnAsyncPool
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from apppy.env import EnvSettings
|
|
8
|
+
from apppy.logger import WithLogger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PostgresClientSettings(EnvSettings):
|
|
12
|
+
db_conn: str = Field(alias="APP_POSTGRES_DB_CONN")
|
|
13
|
+
db_host: str = Field(alias="APP_POSTGRES_DB_HOST")
|
|
14
|
+
db_password: str = Field(alias="APP_POSTGRES_DB_PASSWORD", exclude=True)
|
|
15
|
+
|
|
16
|
+
db_pool_min_size: int = Field(alias="APP_POSTGRES_DB_POOL_MIN_SIZE", default=4)
|
|
17
|
+
db_pool_max_size: int | None = Field(alias="APP_POSTGRES_DB_POOL_MAX_SIZE", default=None)
|
|
18
|
+
db_pool_timeout: float = Field(alias="APP_POSTGRES_DB_POOL_TIMEOUT", default=30)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PostgresClient(WithLogger):
|
|
22
|
+
def __init__(self, settings: PostgresClientSettings, lifespan: LifespanManager) -> None:
|
|
23
|
+
self._settings = settings
|
|
24
|
+
|
|
25
|
+
self._conninfo: str = (
|
|
26
|
+
f"host={settings.db_host} password={settings.db_password} {settings.db_conn}"
|
|
27
|
+
)
|
|
28
|
+
self._db_pool_async: DBConnAsyncPool | None = None
|
|
29
|
+
lifespan.add(self.__open_db_pool_async)
|
|
30
|
+
|
|
31
|
+
async def __open_db_pool_async(self):
|
|
32
|
+
self._logger.info("Opening Postgres psycopg_pool_async")
|
|
33
|
+
if not self._db_pool_async or self._db_pool_async.closed:
|
|
34
|
+
self._db_pool_async = DBConnAsyncPool(
|
|
35
|
+
conninfo=self._conninfo,
|
|
36
|
+
open=False,
|
|
37
|
+
min_size=self._settings.db_pool_min_size,
|
|
38
|
+
max_size=self._settings.db_pool_max_size,
|
|
39
|
+
timeout=self._settings.db_pool_timeout,
|
|
40
|
+
)
|
|
41
|
+
self._logger.info(
|
|
42
|
+
"Opened Postgres psycopg_pool_async",
|
|
43
|
+
extra={
|
|
44
|
+
"min_size": self._settings.db_pool_min_size,
|
|
45
|
+
"max_size": self._settings.db_pool_max_size,
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
await self._db_pool_async.open(wait=True)
|
|
50
|
+
yield {"db_pool_async": self._db_pool_async}
|
|
51
|
+
|
|
52
|
+
self._logger.info("Closing Postgres psycopg_pool_async")
|
|
53
|
+
try:
|
|
54
|
+
await self._db_pool_async.close()
|
|
55
|
+
except Exception:
|
|
56
|
+
self._logger.exception("Error while closing Postgres psycopg_pool_async")
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def db_pool_async(self) -> DBConnAsyncPool:
|
|
60
|
+
if self._db_pool_async is None:
|
|
61
|
+
raise Exception("Postgres db_pool_async is uninitialized")
|
|
62
|
+
|
|
63
|
+
return self._db_pool_async
|
|
64
|
+
|
|
65
|
+
async def db_query_async(self, query: str, params: dict | None = None) -> list[DictRow]:
|
|
66
|
+
async with (
|
|
67
|
+
self.db_pool_async.connection() as db_conn,
|
|
68
|
+
psycopg.AsyncClientCursor(db_conn, row_factory=dict_row) as db_cursor_async,
|
|
69
|
+
):
|
|
70
|
+
await db_cursor_async.execute(query=query, params=params)
|
|
71
|
+
result_set = await db_cursor_async.fetchall()
|
|
72
|
+
|
|
73
|
+
return result_set
|