From dfb4f4960c00d902419a3e3e91a04959e99fa90e Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sun, 21 Dec 2025 09:22:38 +0300 Subject: [PATCH] rename to db-retry --- AGENTS.md | 6 +++--- Dockerfile | 6 ++++-- Justfile | 14 ++++++++------ README.md | 30 +++++++++++++++-------------- db_retry/__init__.py | 13 +++++++++++++ {db_try => db_retry}/connections.py | 0 {db_try => db_retry}/dsn.py | 0 {db_try => db_retry}/py.typed | 0 {db_try => db_retry}/retry.py | 4 ++-- db_retry/settings.py | 5 +++++ {db_try => db_retry}/transaction.py | 0 db_try/__init__.py | 13 ------------- db_try/settings.py | 5 ----- docker-compose.yml | 3 ++- pyproject.toml | 7 ++++--- tests/test_connection_factory.py | 2 +- tests/test_dsn.py | 2 +- tests/test_retry.py | 2 +- tests/test_transaction.py | 2 +- 19 files changed, 61 insertions(+), 53 deletions(-) create mode 100644 db_retry/__init__.py rename {db_try => db_retry}/connections.py (100%) rename {db_try => db_retry}/dsn.py (100%) rename {db_try => db_retry}/py.typed (100%) rename {db_try => db_retry}/retry.py (91%) create mode 100644 db_retry/settings.py rename {db_try => db_retry}/transaction.py (100%) delete mode 100644 db_try/__init__.py delete mode 100644 db_try/settings.py diff --git a/AGENTS.md b/AGENTS.md index 1cab754..f3de4a5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Project Overview -This is a Python library called `db-try` that provides PostgreSQL and SQLAlchemy utilities, specifically focusing on: +This is a Python library called `db-retry` that provides PostgreSQL and SQLAlchemy utilities, specifically focusing on: 1. **Retry decorators** for handling database connection issues and serialization errors 2. **Connection factory builders** for managing PostgreSQL connections with multiple hosts @@ -25,7 +25,7 @@ The library is built with modern Python practices (3.13+) and uses type hints ex ## Project Structure ``` -db_try/ +db_retry/ ├── __init__.py # Exports all public APIs ├── connections.py # Connection factory builders ├── dsn.py # DSN parsing and manipulation utilities @@ -130,4 +130,4 @@ The library can be configured using environment variables: The retry behavior is defined in `retry.py` and uses the tenacity library. Modify the `_retry_handler` function to change which exceptions trigger retries. ### Working with Connections -Connection handling is in `connections.py`. The `build_connection_factory` function handles connecting to PostgreSQL with support for multiple hosts and fallback mechanisms. \ No newline at end of file +Connection handling is in `connections.py`. The `build_connection_factory` function handles connecting to PostgreSQL with support for multiple hosts and fallback mechanisms. diff --git a/Dockerfile b/Dockerfile index 87fbb82..4d195b9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,10 @@ RUN apt update \ COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv RUN useradd --no-create-home --gid root runner -ENV UV_PYTHON_PREFERENCE=only-system -ENV UV_NO_CACHE=true +ENV UV_PROJECT_ENVIRONMENT=/code/.venv \ + UV_NO_MANAGED_PYTHON=1 \ + UV_NO_CACHE=true \ + UV_LINK_MODE=copy WORKDIR /code diff --git a/Justfile b/Justfile index f6d85ab..b9c0246 100644 --- a/Justfile +++ b/Justfile @@ -17,14 +17,16 @@ install: uv sync --all-extras --all-groups --frozen lint: - uv run --frozen ruff format - uv run --frozen ruff check --fix - uv run --frozen mypy . + uv run eof-fixer . + uv run ruff format + uv run ruff check --fix + uv run mypy . lint-ci: - uv run --frozen ruff format --check - uv run --frozen ruff check --no-fix - uv run --frozen mypy . + uv run eof-fixer . --check + uv run ruff format --check + uv run ruff check --no-fix + uv run mypy . publish: rm -rf dist diff --git a/README.md b/README.md index d1da288..cc0300b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# db-try +# db-retry A Python library providing robust retry mechanisms, connection utilities, and transaction helpers for PostgreSQL and SQLAlchemy applications. @@ -14,13 +14,13 @@ A Python library providing robust retry mechanisms, connection utilities, and tr ### Using uv ```bash -uv add db-try +uv add db-retry ``` ### Using pip ```bash -pip install db-try +pip install db-retry ``` ## ORM-Based Usage Examples @@ -34,15 +34,17 @@ import asyncio import sqlalchemy as sa from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column -from db_try import postgres_retry +from db_retry import postgres_retry + class User(DeclarativeBase): __tablename__ = "users" - + id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] = mapped_column(sa.String()) email: Mapped[str] = mapped_column(sa.String(), index=True) + # Apply retry logic to ORM operations @postgres_retry async def get_user_by_email(session: AsyncSession, email: str) -> User: @@ -59,6 +61,7 @@ async def main(): if user: print(f"Found user: {user.name}") + asyncio.run(main()) ``` @@ -70,8 +73,7 @@ Set up resilient database connections with multiple fallback hosts: import sqlalchemy as sa from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column -from db_try import build_connection_factory, build_db_dsn - +from db_retry import build_connection_factory, build_db_dsn # Configure multiple database hosts for high availability multi_host_dsn = ( @@ -91,7 +93,7 @@ dsn = build_db_dsn( # Create connection factory with timeout connection_factory = build_connection_factory( - url=dsn, + url=dsn, timeout=5.0 # 5 second connection timeout ) @@ -109,7 +111,7 @@ import datetime import typing from schemas import AnalyticsEventCreate, AnalyticsEvent -from db_try import Transaction, postgres_retry +from db_retry import Transaction, postgres_retry from your_service_name.database.tables import EventsTable from your_service_name.producers.analytics_service_events_producer import AnalyticsEventsProducer @@ -125,8 +127,8 @@ class CreateEventUseCase: @postgres_retry async def __call__( - self, - event_create_data: AnalyticsEventCreate, + self, + event_create_data: AnalyticsEventCreate, ) -> AnalyticsEvent: async with self.transaction: model: typing.Final = EventsTable( @@ -147,12 +149,12 @@ Use serializable isolation level to prevent race conditions with ORM: ```python from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine -from db_try import Transaction +from db_retry import Transaction async def main(): engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/mydb") - + async with AsyncSession(engine) as session: strict_transaction = Transaction( session=session, @@ -167,7 +169,7 @@ The library can be configured using environment variables: | Variable | Description | Default | |-------------------------|--------------------------------------------------|---------| -| `DB_TRY_RETRIES_NUMBER` | Number of retry attempts for database operations | 3 | +| `DB_RETRY_RETRIES_NUMBER` | Number of retry attempts for database operations | 3 | Example: ```bash diff --git a/db_retry/__init__.py b/db_retry/__init__.py new file mode 100644 index 0000000..28e10de --- /dev/null +++ b/db_retry/__init__.py @@ -0,0 +1,13 @@ +from db_retry.connections import build_connection_factory +from db_retry.dsn import build_db_dsn, is_dsn_multihost +from db_retry.retry import postgres_retry +from db_retry.transaction import Transaction + + +__all__ = [ + "Transaction", + "build_connection_factory", + "build_db_dsn", + "is_dsn_multihost", + "postgres_retry", +] diff --git a/db_try/connections.py b/db_retry/connections.py similarity index 100% rename from db_try/connections.py rename to db_retry/connections.py diff --git a/db_try/dsn.py b/db_retry/dsn.py similarity index 100% rename from db_try/dsn.py rename to db_retry/dsn.py diff --git a/db_try/py.typed b/db_retry/py.typed similarity index 100% rename from db_try/py.typed rename to db_retry/py.typed diff --git a/db_try/retry.py b/db_retry/retry.py similarity index 91% rename from db_try/retry.py rename to db_retry/retry.py index a1b3671..c1e6fb0 100644 --- a/db_try/retry.py +++ b/db_retry/retry.py @@ -6,7 +6,7 @@ import tenacity from sqlalchemy.exc import DBAPIError -from db_try import settings +from db_retry import settings logger = logging.getLogger(__name__) @@ -29,7 +29,7 @@ def postgres_retry[**P, T]( func: typing.Callable[P, typing.Coroutine[None, None, T]], ) -> typing.Callable[P, typing.Coroutine[None, None, T]]: @tenacity.retry( - stop=tenacity.stop_after_attempt(settings.DB_TRY_RETRIES_NUMBER), + stop=tenacity.stop_after_attempt(settings.DB_RETRY_RETRIES_NUMBER), wait=tenacity.wait_exponential_jitter(), retry=tenacity.retry_if_exception(_retry_handler), reraise=True, diff --git a/db_retry/settings.py b/db_retry/settings.py new file mode 100644 index 0000000..c04d63e --- /dev/null +++ b/db_retry/settings.py @@ -0,0 +1,5 @@ +import os +import typing + + +DB_RETRY_RETRIES_NUMBER: typing.Final = int(os.getenv("DB_RETRY_RETRIES_NUMBER", "3")) diff --git a/db_try/transaction.py b/db_retry/transaction.py similarity index 100% rename from db_try/transaction.py rename to db_retry/transaction.py diff --git a/db_try/__init__.py b/db_try/__init__.py deleted file mode 100644 index 8be4859..0000000 --- a/db_try/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from db_try.connections import build_connection_factory -from db_try.dsn import build_db_dsn, is_dsn_multihost -from db_try.retry import postgres_retry -from db_try.transaction import Transaction - - -__all__ = [ - "Transaction", - "build_connection_factory", - "build_db_dsn", - "is_dsn_multihost", - "postgres_retry", -] diff --git a/db_try/settings.py b/db_try/settings.py deleted file mode 100644 index fb8ea7e..0000000 --- a/db_try/settings.py +++ /dev/null @@ -1,5 +0,0 @@ -import os -import typing - - -DB_TRY_RETRIES_NUMBER: typing.Final = int(os.getenv("DB_TRY_RETRIES_NUMBER", "3")) diff --git a/docker-compose.yml b/docker-compose.yml index f027435..fc01f31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,8 @@ services: dockerfile: ./Dockerfile restart: always volumes: - - .:/srv/www/ + - .:/code + - /code/.venv depends_on: db: condition: service_healthy diff --git a/pyproject.toml b/pyproject.toml index 565e2fa..2697bd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "db-try" +name = "db-retry" description = "PostgreSQL and SQLAlchemy Tools" authors = [ { name = "community-of-python" }, @@ -35,9 +35,10 @@ dev = [ "pytest-asyncio", ] lint = [ - "asyncpg-stubs", "ruff", "mypy", + "eof-fixer", + "asyncpg-stubs", ] [build-system] @@ -45,7 +46,7 @@ requires = ["uv_build"] build-backend = "uv_build" [tool.uv.build-backend] -module-name = "db_try" +module-name = "db_retry" module-root = "" [tool.mypy] diff --git a/tests/test_connection_factory.py b/tests/test_connection_factory.py index 5ffc287..0886f7a 100644 --- a/tests/test_connection_factory.py +++ b/tests/test_connection_factory.py @@ -7,7 +7,7 @@ import sqlalchemy from sqlalchemy.ext import asyncio as sa_async -from db_try.connections import build_connection_factory +from db_retry.connections import build_connection_factory async def test_connection_factory_success() -> None: diff --git a/tests/test_dsn.py b/tests/test_dsn.py index 9cae475..e74cb2c 100644 --- a/tests/test_dsn.py +++ b/tests/test_dsn.py @@ -1,6 +1,6 @@ import typing -from db_try import build_db_dsn, is_dsn_multihost +from db_retry import build_db_dsn, is_dsn_multihost def test_build_db_dsn() -> None: diff --git a/tests/test_retry.py b/tests/test_retry.py index cc92eca..44500ee 100644 --- a/tests/test_retry.py +++ b/tests/test_retry.py @@ -3,7 +3,7 @@ from sqlalchemy.exc import DBAPIError from sqlalchemy.ext import asyncio as sa_async -from db_try.retry import postgres_retry +from db_retry.retry import postgres_retry @pytest.mark.parametrize( diff --git a/tests/test_transaction.py b/tests/test_transaction.py index 52aa376..5d6068d 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -4,7 +4,7 @@ import pytest from sqlalchemy.ext import asyncio as sa_async -from db_try import Transaction +from db_retry import Transaction @pytest.fixture