Skip to content

Commit

Permalink
Merge branch 'release/0.9.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ri-gilfanov committed Jun 8, 2021
2 parents 1a460d0 + eb7b771 commit c888470
Show file tree
Hide file tree
Showing 24 changed files with 296 additions and 147 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dist
*.egg-info

# Checkers
.coverage
.mypy_cache


Expand Down
4 changes: 1 addition & 3 deletions .mypy.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
[mypy]
files = aiohttp_sqlalchemy, tests

[mypy-sqlalchemy.*]
ignore_missing_imports = True
plugins = sqlalchemy.ext.mypy.plugin
59 changes: 30 additions & 29 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
==================
aiohttp-sqlalchemy
==================

SQLAlchemy 1.4 / 2.0 support for aiohttp.

Library forward a ``sqlalchemy.ext.asyncio.AsyncSession`` object as
Expand All @@ -22,49 +21,51 @@ Installation

Simple example
--------------
Install aiosqlite for work with sqlite3: ::
Install ``aiosqlite`` for work with sqlite3: ::

pip install aiosqlite

Copy and paste this code in a file and run:

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_bind, sa_middleware
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_bind
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
class MyModel(Base):
__tablename__ = 'my_table'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
class MyModel(Base):
__tablename__ = 'my_table'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
async def main(request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def main(request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async with request['sa_main'].begin():
request['sa_main'].add_all([MyModel()])
result = await request['sa_main'].execute(sa.select(MyModel))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
async with request['sa_main'].begin():
request['sa_main'].add_all([MyModel()])
result = await request['sa_main'].execute(sa.select(MyModel))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
app = web.Application()
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
app.add_routes([web.get('/', main)])
app.add_routes([web.get('/', main)])
web.run_app(app)
if __name__ == '__main__':
web.run_app(app)
18 changes: 3 additions & 15 deletions aiohttp_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession
from typing import TYPE_CHECKING
import warnings

from aiohttp_sqlalchemy.constants import DEFAULT_KEY
from aiohttp_sqlalchemy.decorators import sa_decorator
Expand All @@ -12,13 +10,13 @@

if TYPE_CHECKING:
from aiohttp.web import Application
from typing import Callable, Iterable, Union, Tuple
from typing import Callable, Iterable, Tuple

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


__version__ = '0.8.0'
__version__ = '0.9.0'

__all__ = ['DuplicateAppKeyError', 'DuplicateRequestKeyError', 'SABaseView',
'sa_bind', 'sa_decorator', 'sa_middleware', 'SAView', 'setup',]
Expand All @@ -27,16 +25,6 @@
def sa_bind(factory: 'TSessionFactory', key: str = DEFAULT_KEY, *,
middleware: bool = True) -> 'TSABinding':
""" Session factory wrapper for binding in setup function. """

if isinstance(factory, AsyncEngine):
msg = (
"`AsyncEngine` type is deprecated in `sa_bind()` signature. "
"Use `sessionmaker(engine, AsyncSession)` or custom session "
"factory returning `AsyncSession` instance."
)
warnings.warn(msg, stacklevel=2)
factory = sessionmaker(factory, AsyncSession)

return factory, key, middleware


Expand Down
13 changes: 4 additions & 9 deletions aiohttp_sqlalchemy/decorators.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from aiohttp.abc import AbstractView
from asyncio import iscoroutinefunction
from functools import wraps
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from sqlalchemy.orm import sessionmaker
from typing import TYPE_CHECKING

from aiohttp_sqlalchemy.constants import DEFAULT_KEY
Expand All @@ -17,12 +14,10 @@ def sa_decorator(key: str = DEFAULT_KEY):
def wrapper(handler):
@wraps(handler)
async def wrapped(*args, **kwargs) -> 'StreamResponse':
if isinstance(args[0], AbstractView):
request = args[0].request
elif iscoroutinefunction(handler):
request = args[0]
else:
raise TypeError('Unsupported handler type')
print(*args)
request = args[0].request \
if isinstance(args[0], AbstractView) \
else args[-1]

if key in request:
raise DuplicateRequestKeyError(key)
Expand Down
2 changes: 0 additions & 2 deletions aiohttp_sqlalchemy/middlewares.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from aiohttp.web import middleware
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from sqlalchemy.orm import sessionmaker
from typing import TYPE_CHECKING

from aiohttp_sqlalchemy.constants import DEFAULT_KEY
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.8.0'
release = '0.9.0'


# -- General configuration ---------------------------------------------------
Expand Down
71 changes: 43 additions & 28 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,44 +40,46 @@ Copy and paste this code in a file and run:

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_bind, sa_middleware
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_bind
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
class MyModel(Base):
__tablename__ = 'my_table'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
class MyModel(Base):
__tablename__ = 'my_table'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
async def main(request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def main(request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async with request['sa_main'].begin():
request['sa_main'].add_all([MyModel()])
result = await request['sa_main'].execute(sa.select(MyModel))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
async with request['sa_main'].begin():
request['sa_main'].add_all([MyModel()])
result = await request['sa_main'].execute(sa.select(MyModel))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
app = web.Application()
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
app.add_routes([web.get('/', main)])
app.add_routes([web.get('/', main)])
web.run_app(app)
if __name__ == '__main__':
web.run_app(app)
SQLAlchemy and Asyncio
Expand Down Expand Up @@ -175,6 +177,19 @@ Nested apps
Change log
----------
Version 0.9.0
^^^^^^^^^^^^^
Added
"""""
Added support of handlers in class for a ``sa_decorator(key)``. For example
``app.add_routes([web.get('/', MyClass().my_get_method)])``.

Removed
"""""""
Removed support of ``AsyncEngine`` type in ``sa_bind()`` signature. Use
``sessionmaker(engine, AsyncSession)`` or custom session factory returning
``AsyncSession`` instance.

Version 0.8.0
^^^^^^^^^^^^^
Changed
Expand All @@ -184,7 +199,7 @@ Rename first argument from ``arg`` to ``factory`` in ``sa_bind()`` signature.
Deprecated
""""""""""
``AsyncEngine`` type is deprecated in ``sa_bind()`` signature. Use
``sessionmaker(engine, AsyncSession)`` or custom session factory returning
``sessionmaker(engine, AsyncSession)`` or custom session factory returning
``AsyncSession`` instance.

Version 0.7.0
Expand Down
4 changes: 3 additions & 1 deletion examples/hybrid_approach.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ async def main(request):
])

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

if __name__ == '__main__':
web.run_app(app)
4 changes: 3 additions & 1 deletion examples/multiple_decorated_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ async def main(request):
])

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

if __name__ == '__main__':
web.run_app(app)
4 changes: 3 additions & 1 deletion examples/multiple_middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ async def main(request):
])

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

if __name__ == '__main__':
web.run_app(app)
4 changes: 3 additions & 1 deletion examples/nested_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ async def main(request):
subapp.add_routes([web.get('', main)])

app.add_subapp(prefix='/subapp', subapp=subapp)
web.run_app(app)

if __name__ == '__main__':
web.run_app(app)
8 changes: 5 additions & 3 deletions examples/single_decorated_cbv.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ async def get(self):
await conn.run_sync(Base.metadata.create_all)

async with self.sa_session().begin():
self.request['sa_main'].add_all([Request()])
result = await self.request['sa_main'].execute(sa.select(Request))
self.sa_session().add_all([Request()])
result = await self.sa_session().execute(sa.select(Request))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)

Expand All @@ -37,4 +37,6 @@ async def get(self):
aiohttp_sqlalchemy.setup(app, [sa_bind(Session, middleware=False)])

app.add_routes([web.view('/', Main)])
web.run_app(app)

if __name__ == '__main__':
web.run_app(app)
43 changes: 43 additions & 0 deletions examples/single_decorated_class_method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_decorator, sa_bind, SAView
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine


metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)


class Request(Base):
__tablename__ = 'requests'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)


class Main:
@sa_decorator()
async def get(self, request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)

async with request['sa_main'].begin():
request['sa_main'].add_all([Request()])
result = await request['sa_main'].execute(sa.select(Request))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)


app = web.Application()

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

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

if __name__ == '__main__':
web.run_app(app)
4 changes: 3 additions & 1 deletion examples/single_decorated_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ async def main(request):
aiohttp_sqlalchemy.setup(app, [sa_bind(Session, middleware=False)])

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

if __name__ == '__main__':
web.run_app(app)

0 comments on commit c888470

Please sign in to comment.