Skip to content

Commit

Permalink
Use lifespan function instead of event
Browse files Browse the repository at this point in the history
  • Loading branch information
mekanix committed Feb 8, 2024
1 parent 851f1f1 commit 8871aa4
Show file tree
Hide file tree
Showing 129 changed files with 1,596 additions and 3,470 deletions.
26 changes: 25 additions & 1 deletion .github/workflows/test-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
POSTGRES_DB: testsuite
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 --name postgres

steps:
- name: Checkout
Expand Down Expand Up @@ -71,6 +71,30 @@ jobs:
DATABASE_URL: "mysql://username:[email protected]:3306/testsuite"
run: bash scripts/test.sh

- name: Install postgresql-client
run: |
sudo apt-get update
sudo apt-get install --yes postgresql-client
- name: Connect to PostgreSQL with CLI
run: env PGPASSWORD=password psql -h localhost -U username -c 'SELECT VERSION();' testsuite

- name: Show max connections
run: env PGPASSWORD=password psql -h localhost -U username -c 'SHOW max_connections;' testsuite

- name: Alter max connections
run: |
docker exec -i postgres bash << EOF
sed -i -e 's/max_connections = 100/max_connections = 1000/' /var/lib/postgresql/data/postgresql.conf
sed -i -e 's/shared_buffers = 128MB/shared_buffers = 512MB/' /var/lib/postgresql/data/postgresql.conf
EOF
docker restart --time 0 postgres
sleep 5
- name: Show max connections
run: env PGPASSWORD=password psql -h localhost -U username -c 'SHOW max_connections;' testsuite

- name: Run postgres
env:
DATABASE_URL: "postgresql://username:password@localhost:5432/testsuite"
Expand Down
24 changes: 5 additions & 19 deletions benchmarks/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,20 @@
import string
import time

import databases
import nest_asyncio
import ormar
import pytest
import pytest_asyncio
import sqlalchemy
from tests.settings import DATABASE_URL

nest_asyncio.apply()
from tests.settings import create_config
from tests.lifespan import init_tests


database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
base_ormar_config = create_config()
nest_asyncio.apply()
pytestmark = pytest.mark.asyncio


base_ormar_config = ormar.OrmarConfig(
metadata=metadata,
database=database,
)


class Author(ormar.Model):
ormar_config = base_ormar_config.copy(tablename="authors")

Expand Down Expand Up @@ -57,13 +49,7 @@ class Book(ormar.Model):
year: int = ormar.Integer(nullable=True)


@pytest.fixture(autouse=True, scope="function") # TODO: fix this to be module
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.drop_all(engine)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config, scope="function")


@pytest_asyncio.fixture
Expand Down
45 changes: 23 additions & 22 deletions docs/fastapi/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Here you can find a very simple sample application code.

### Imports and initialization

First take care of the imports and initialization
Define startup and shutdown procedures using FastAPI lifespan and use is in the
application.
```python
from typing import List, Optional

Expand All @@ -36,29 +37,29 @@ from fastapi import FastAPI

import ormar

app = FastAPI()
metadata = sqlalchemy.MetaData()
database = databases.Database("sqlite:///test.db")
app.state.database = database
```
from contextlib import asynccontextmanager
from fastapi import FastAPI

### Database connection

Next define startup and shutdown events (or use middleware)
- note that this is `databases` specific setting not the ormar one
```python
@app.on_event("startup")
async def startup() -> None:
database_ = app.state.database
if not database_.is_connected:
await database_.connect()


@app.on_event("shutdown")
async def shutdown() -> None:
database_ = app.state.database
if database_.is_connected:
await database_.disconnect()
@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
if not config.database.is_connected:
await config.database.connect()
config.metadata.drop_all(config.engine)
config.metadata.create_all(config.engine)

yield

if config.database.is_connected:
config.metadata.drop_all(config.engine)
await config.database.disconnect()


base_ormar_config = ormar.OrmarConfig(
metadata=sqlalchemy.MetaData(),
database=databases.Database("sqlite:///test.db"),
)
app = FastAPI(lifespan=lifespan(base_ormar_config))
```

!!!info
Expand Down
32 changes: 6 additions & 26 deletions docs_src/fastapi/docs001.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,25 @@
from typing import List, Optional

import databases
import ormar
import sqlalchemy
from fastapi import FastAPI

DATABASE_URL = "sqlite:///test.db"
from tests.settings import create_config
from tests.lifespan import lifespan

ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)

app = FastAPI()
metadata = sqlalchemy.MetaData()
database = databases.Database("sqlite:///test.db")
app.state.database = database


@app.on_event("startup")
async def startup() -> None:
database_ = app.state.database
if not database_.is_connected:
await database_.connect()


@app.on_event("shutdown")
async def shutdown() -> None:
database_ = app.state.database
if database_.is_connected:
await database_.disconnect()
base_ormar_config = create_config()
app = FastAPI(lifespan=lifespan(base_ormar_config))


class Category(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="categories")
ormar_config = base_ormar_config.copy(tablename="categories")

id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)


class Item(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="items")
ormar_config = base_ormar_config.copy(tablename="items")

id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
Expand Down
21 changes: 9 additions & 12 deletions examples/fastapi_quick_start.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from contextlib import asynccontextmanager
from typing import List, Optional

import databases
Expand All @@ -13,25 +14,21 @@
)


app = FastAPI()
metadata = sqlalchemy.MetaData()
database = databases.Database("sqlite:///test.db")
app.state.database = database


@app.on_event("startup")
async def startup() -> None:
@asynccontextmanager
async def lifespan(app: FastAPI):
database_ = app.state.database
if not database_.is_connected:
await database_.connect()


@app.on_event("shutdown")
async def shutdown() -> None:
yield
database_ = app.state.database
if database_.is_connected:
await database_.disconnect()

app = FastAPI(lifespan=lifespan)
metadata = sqlalchemy.MetaData()
database = databases.Database("sqlite:///test.db")
app.state.database = database


class Category(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="categories")
Expand Down
4 changes: 4 additions & 0 deletions ormar/models/ormar_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __init__(
self,
metadata: Optional[sqlalchemy.MetaData] = None,
database: Optional[databases.Database] = None,
engine: Optional[sqlalchemy.engine.Engine] = None,
tablename: Optional[str] = None,
order_by: Optional[List[str]] = None,
abstract: bool = False,
Expand All @@ -39,6 +40,7 @@ def __init__(
self.pkname = None # type: ignore
self.metadata = metadata
self.database = database # type: ignore
self.engine = engine # type: ignore
self.tablename = tablename # type: ignore
self.orders_by = order_by or []
self.columns: List[sqlalchemy.Column] = []
Expand All @@ -60,6 +62,7 @@ def copy(
self,
metadata: Optional[sqlalchemy.MetaData] = None,
database: Optional[databases.Database] = None,
engine: Optional[sqlalchemy.engine.Engine] = None,
tablename: Optional[str] = None,
order_by: Optional[List[str]] = None,
abstract: Optional[bool] = None,
Expand All @@ -71,6 +74,7 @@ def copy(
return OrmarConfig(
metadata=metadata or self.metadata,
database=database or self.database,
engine=engine or self.engine,
tablename=tablename,
order_by=order_by,
abstract=abstract or self.abstract,
Expand Down
33 changes: 33 additions & 0 deletions tests/lifespan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest
import sqlalchemy

from contextlib import asynccontextmanager
from fastapi import FastAPI
from typing import AsyncIterator


def lifespan(config):
@asynccontextmanager
async def do_lifespan(_: FastAPI) -> AsyncIterator[None]:
if not config.database.is_connected:
await config.database.connect()

yield

if config.database.is_connected:
await config.database.disconnect()

return do_lifespan


def init_tests(config, scope="module"):
@pytest.fixture(autouse=True, scope=scope)
def create_database():
config.engine = sqlalchemy.create_engine(config.database.url._url)
config.metadata.create_all(config.engine)

yield

config.metadata.drop_all(config.engine)

return create_database
14 changes: 14 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import os

import databases
import ormar
import sqlalchemy

DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///test.db")
database_url = databases.DatabaseURL(DATABASE_URL)
if database_url.scheme == "postgresql+aiopg": # pragma no cover
DATABASE_URL = str(database_url.replace(driver=None))
print("USED DB:", DATABASE_URL)


def create_config(**args):
database_ = databases.Database(DATABASE_URL, **args)
metadata_ = sqlalchemy.MetaData()
engine_ = sqlalchemy.create_engine(DATABASE_URL)

return ormar.OrmarConfig(
metadata=metadata_,
database=database_,
engine=engine_,
)
30 changes: 9 additions & 21 deletions tests/test_deferred/test_forward_cross_refs.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
# type: ignore
from typing import ForwardRef, List, Optional

import databases
import ormar
import pytest
import sqlalchemy as sa
from sqlalchemy import create_engine

from tests.settings import DATABASE_URL
from tests.settings import create_config
from tests.lifespan import init_tests

metadata = sa.MetaData()
db = databases.Database(DATABASE_URL)
engine = create_engine(DATABASE_URL)

TeacherRef = ForwardRef("Teacher")
base_ormar_config = create_config()


base_ormar_config = ormar.OrmarConfig(
metadata=metadata,
database=db,
)
TeacherRef = ForwardRef("Teacher")


class Student(ormar.Model):
Expand Down Expand Up @@ -76,17 +68,13 @@ class City(ormar.Model):
Country.update_forward_refs()


@pytest.fixture(autouse=True, scope="module")
def create_test_database():
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)


@pytest.mark.asyncio
async def test_double_relations():
async with db:
async with db.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
t1 = await Teacher.objects.create(name="Mr. Jones")
t2 = await Teacher.objects.create(name="Ms. Smith")
t3 = await Teacher.objects.create(name="Mr. Quibble")
Expand Down Expand Up @@ -143,8 +131,8 @@ async def test_double_relations():

@pytest.mark.asyncio
async def test_auto_through_model():
async with db:
async with db.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
england = await Country(name="England").save()
france = await Country(name="France").save()
london = await City(name="London", country=england).save()
Expand Down
Loading

0 comments on commit 8871aa4

Please sign in to comment.