Skip to content

Commit

Permalink
Merge branch 'release/0.12.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ri-gilfanov committed Jun 14, 2021
2 parents bde55f6 + 20ae249 commit ce1bd0f
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 27 deletions.
5 changes: 1 addition & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ Copy and paste this code in a file and run:
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
metadata = sa.MetaData()
Expand Down Expand Up @@ -85,9 +84,7 @@ Copy and paste this code in a file and run:
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
aiohttp_sqlalchemy.setup(app, [sa_bind('sqlite+aiosqlite:///')])
app.add_routes([web.get('/', main)])
Expand Down
36 changes: 29 additions & 7 deletions aiohttp_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from typing import TYPE_CHECKING
from sqlalchemy.engine import Engine
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from typing import cast, TYPE_CHECKING

from aiohttp_sqlalchemy.constants import DEFAULT_KEY
from aiohttp_sqlalchemy.decorators import sa_decorator
Expand All @@ -11,27 +14,46 @@

if TYPE_CHECKING:
from aiohttp.web import Application
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Callable, Iterable, Tuple
from typing import Callable, Iterable, Union, Tuple

TSessionFactory = Callable[..., AsyncSession]
TBindTo = Union[str, Callable[..., AsyncSession]]
TSABinding = Tuple[TSessionFactory, str, bool]


__version__ = '0.11.2'
__version__ = '0.12.0'

__all__ = ['DuplicateAppKeyError', 'DuplicateRequestKeyError',
'SAAbstractView', 'SABaseView', 'sa_bind', 'sa_decorator',
'sa_middleware', 'sa_session', 'SAView', 'setup',]


def sa_bind(factory: 'TSessionFactory', key: str = DEFAULT_KEY, *,
def sa_bind(bind_to: 'TBindTo', key: str = DEFAULT_KEY, *,
middleware: bool = True) -> 'TSABinding':
""" Session factory wrapper for binding in setup function. """
return factory, key, middleware

if isinstance(bind_to, str):
bind_to = cast(AsyncEngine, create_async_engine(bind_to))

def setup(app: 'Application', bindings: 'Iterable[TSABinding]'):
if isinstance(bind_to, AsyncEngine):
bind_to = cast('TSessionFactory', sessionmaker(bind_to, AsyncSession))

if isinstance(bind_to, Engine):
msg = 'Synchronous engine is unsupported argument for `sa_bind()`.'
raise ValueError(msg)

if not callable(bind_to):
msg = 'Session factory must be callable.'
raise ValueError(msg)

if not isinstance(bind_to(), AsyncSession):
msg = 'Session factory must returning `AsyncSession` instance.'
raise ValueError(msg)

return bind_to, key, middleware


def setup(app: 'Application', bindings: 'Iterable[TSABinding]') -> None:
""" Setup function for binding SQLAlchemy engines. """
for factory, key, middleware in bindings:
if key in app:
Expand Down
8 changes: 5 additions & 3 deletions aiohttp_sqlalchemy/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Any
from typing import Any, Optional


class SAAbstractView(AbstractView, metaclass=ABCMeta):
Expand All @@ -18,8 +18,10 @@ class SAAbstractView(AbstractView, metaclass=ABCMeta):
Suitable for a specific usage with multiple models.
"""
def sa_session(self, key: str = DEFAULT_KEY) -> 'AsyncSession':
return self.request[key]
sa_session_key: 'str' = DEFAULT_KEY

def sa_session(self, key: 'Optional[str]' = None) -> 'AsyncSession':
return self.request[key or self.sa_session_key]


class SAOneModelMixin(SAAbstractView, metaclass=ABCMeta):
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Ruslan Ilyasovich Gilfanov'

# The full version, including alpha/beta/rc tags
release = '0.11.2'
release = '0.12.0'


# -- General configuration ---------------------------------------------------
Expand Down
20 changes: 16 additions & 4 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ Copy and paste this code in a file and run:
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
metadata = sa.MetaData()
Expand Down Expand Up @@ -96,9 +95,7 @@ Copy and paste this code in a file and run:
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
aiohttp_sqlalchemy.setup(app, [sa_bind('sqlite+aiosqlite:///')])
app.add_routes([web.get('/', main)])
Expand All @@ -116,6 +113,8 @@ Binding multiple session factories
----------------------------------
.. code-block:: python
from aiohttp_sqlalchemy import sa_bind
main_engine = create_async_engine('postgresql+asyncpg://user:password@host/database')
second_engine = create_async_engine('mysql+aiomysql://user:password@host/database')
third_engine = create_async_engine('sqlite+aiosqlite:///')
Expand Down Expand Up @@ -206,6 +205,19 @@ Nested apps
Change log
----------
Version 0.12
^^^^^^^^^^^^
Added
"""""
* Added ``sa_session_key`` attribute in ``SAAbstractView`` class.
* Added support url and ``AssyncEngine`` instance as first argument
in ``sa_bind()``.

Changed
"""""""
* Rename first argument from ``factory`` to ``bind_to`` in ``sa_bind()``.
signature.

Version 0.11
^^^^^^^^^^^^
Added
Expand Down
5 changes: 1 addition & 4 deletions examples/single_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from typing import TYPE_CHECKING

import aiohttp_sqlalchemy
Expand Down Expand Up @@ -38,9 +37,7 @@ async def main(request):

app = web.Application()

engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
aiohttp_sqlalchemy.setup(app, [sa_bind('sqlite+aiosqlite:///')])

app.add_routes([web.get('/', main)])

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aiohttp-sqlalchemy"
version = "0.11.2"
version = "0.12.0"
description = "SQLAlchemy 1.4 / 2.0 support for aiohttp."
authors = [
"Ruslan Ilyasovich Gilfanov <ri.gilfanov@yandex.ru>",
Expand Down
10 changes: 7 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@


@pytest.fixture
def orm_session_factory():
engine = create_async_engine('sqlite+aiosqlite:///')
return orm.sessionmaker(engine, AsyncSession)
def orm_async_engine():
return create_async_engine('sqlite+aiosqlite:///')


@pytest.fixture
def orm_session_factory(orm_async_engine):
return orm.sessionmaker(orm_async_engine, AsyncSession)


@pytest.fixture
Expand Down
49 changes: 49 additions & 0 deletions tests/test_sa_bind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import sessionmaker
import pytest

from aiohttp_sqlalchemy import sa_bind


def test_sa_bind_1():
binding = sa_bind('sqlite+aiosqlite:///')
session_factory = binding[0]
session = session_factory()

assert isinstance(session, AsyncSession)


def test_bind_to_async_engine(orm_async_engine):
binding = sa_bind(orm_async_engine)
session_factory = binding[0]
session = session_factory()

assert isinstance(session, AsyncSession)


def test_bind_to_sync_engine():
engine = create_engine('sqlite+aiosqlite:///')
with pytest.raises(ValueError):
sa_bind(engine)


def test_sa_bind_with_ready_session(orm_async_engine):
session = AsyncSession(orm_async_engine)
with pytest.raises(ValueError):
sa_bind(session)


def test_sa_bind_with_sync_session(orm_async_engine):
session_factory = sessionmaker(orm_async_engine)
with pytest.raises(ValueError):
sa_bind(session_factory)


def test_sa_bind_5(orm_async_engine):
session_factory = sessionmaker(orm_async_engine, AsyncSession)
binding = sa_bind(session_factory)
session_factory = binding[0]
session = session_factory()

assert isinstance(session, AsyncSession)

0 comments on commit ce1bd0f

Please sign in to comment.