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.
@@ -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
+
@@ -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
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,12 @@
1
+ import abc
2
+
3
+
4
+ class Migrations(abc.ABC):
5
+ @abc.abstractmethod
6
+ async def head(self) -> str | None:
7
+ pass
8
+
9
+
10
+ class DefaultMigrations(Migrations):
11
+ async def head(self) -> str | None:
12
+ return None
@@ -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