Skip to content

Commit

Permalink
Merge pull request #273 from lsst-sqre/tickets/DM-45281
Browse files Browse the repository at this point in the history
DM-45281: Allow create_database_engine to take a Url
  • Loading branch information
rra authored Jul 18, 2024
2 parents ef060c5 + dfb2c0d commit 6b7b99a
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 2 deletions.
3 changes: 3 additions & 0 deletions changelog.d/20240717_220712_rra_DM_45281_queue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### New features

- `safir.database.create_database_engine` now accepts the database URL as a Pydantic `Url` as well as a `str`.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies = [
"gidgethub<6",
"httpx>=0.20.0,<1",
"pydantic>2,<3",
"pydantic-core",
"starlette<1",
# 23.3.0 excluded due to https://github.com/hynek/structlog/issues/584
"structlog>=21.2.0,!=23.3.0",
Expand Down
9 changes: 7 additions & 2 deletions src/safir/database/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from urllib.parse import quote, urlparse

from pydantic import SecretStr
from pydantic_core import Url
from sqlalchemy.exc import OperationalError
from sqlalchemy.ext.asyncio import (
AsyncEngine,
Expand All @@ -22,7 +23,9 @@
]


def _build_database_url(url: str, password: str | SecretStr | None) -> str:
def _build_database_url(
url: str | Url, password: str | SecretStr | None
) -> str:
"""Build the authenticated URL for the database.
The database scheme is forced to ``postgresql+asyncpg`` if it is
Expand All @@ -46,6 +49,8 @@ def _build_database_url(url: str, password: str | SecretStr | None) -> str:
Raised if a password was provided but the connection URL has no
username.
"""
if not isinstance(url, str):
url = str(url)
parsed_url = urlparse(url)
if parsed_url.scheme == "postgresql":
parsed_url = parsed_url._replace(scheme="postgresql+asyncpg")
Expand All @@ -67,7 +72,7 @@ def _build_database_url(url: str, password: str | SecretStr | None) -> str:


def create_database_engine(
url: str,
url: str | Url,
password: str | SecretStr | None,
*,
isolation_level: str | None = None,
Expand Down
7 changes: 7 additions & 0 deletions tests/database_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pytest
import structlog
from pydantic import BaseModel, SecretStr
from pydantic_core import Url
from sqlalchemy import Column, MetaData, String, Table
from sqlalchemy.exc import OperationalError, ProgrammingError
from sqlalchemy.future import select
Expand Down Expand Up @@ -83,6 +84,12 @@ def test_build_database_url(database_url: str) -> None:
)
assert url == "postgresql+asyncpg://foo:[email protected]:5433/foo"

pydantic_url = Url.build(
scheme="postgresql", username="user", host="localhost", path="foo"
)
url = _build_database_url(pydantic_url, "password")
assert url == "postgresql+asyncpg://user:password@localhost/foo"

# Test that the username and password are quoted properly.
url = _build_database_url(
"postgresql://foo%[email protected]:4444/foo",
Expand Down

0 comments on commit 6b7b99a

Please sign in to comment.