Skip to content

Commit

Permalink
Merge branch 'release/0.15.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
ri-gilfanov committed Jun 20, 2021
2 parents d5ecfc2 + 1b050b9 commit 9779da8
Show file tree
Hide file tree
Showing 15 changed files with 89 additions and 79 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ install:
- pip install coveralls
- poetry install -E sqlite
script:
- mypy
- pytest --cov=aiohttp_sqlalchemy
after_success:
- coveralls
34 changes: 13 additions & 21 deletions aiohttp_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from typing import cast, TYPE_CHECKING
import warnings

from aiohttp_sqlalchemy.constants import SA_DEFAULT_KEY
from aiohttp_sqlalchemy.constants import DEFAULT_KEY, SA_DEFAULT_KEY
from aiohttp_sqlalchemy.decorators import sa_decorator
from aiohttp_sqlalchemy.exceptions import DuplicateAppKeyError, \
DuplicateRequestKeyError
Expand All @@ -15,23 +14,24 @@

if TYPE_CHECKING:
from aiohttp.web import Application
from typing import Any, Callable, Iterable, Union, Tuple
from aiohttp_sqlalchemy.typedefs import TBinding, TBindings, TBindTo, \
TSessionFactory

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


__version__ = '0.15.3'
__version__ = '0.15.4'

__all__ = ['bind', 'DuplicateAppKeyError', 'DuplicateRequestKeyError',
'init_db', 'SAAbstractView', 'SABaseView', 'SA_DEFAULT_KEY',
'sa_bind', 'sa_decorator', 'sa_middleware', 'sa_init_db',
'sa_session', 'SAView', 'setup',]
__all__ = [
'bind', 'DuplicateAppKeyError', 'DuplicateRequestKeyError', 'init_db',
'SAAbstractView', 'SABaseView', 'SA_DEFAULT_KEY', 'sa_decorator',
'sa_middleware', 'sa_session', 'SAView', 'setup',
# synonyms
'DEFAULT_KEY', 'sa_bind', 'sa_init_db',
]


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

if isinstance(bind_to, str):
Expand Down Expand Up @@ -62,7 +62,7 @@ def bind(bind_to: 'TBindTo', key: str = SA_DEFAULT_KEY, *,
sa_bind = bind # sa_bind is synonym for bind


def setup(app: 'Application', bindings: 'Iterable[TSABinding]') -> None:
def setup(app: 'Application', bindings: 'TBindings') -> None:
""" Setup function for binding SQLAlchemy engines. """
for factory, key, middleware in bindings:
if key in app:
Expand All @@ -72,11 +72,3 @@ def setup(app: 'Application', bindings: 'Iterable[TSABinding]') -> None:

if middleware:
app.middlewares.append(sa_middleware(key))


def __getattr__(name: str) -> 'Any':
if name == 'DEFAULT_KEY':
msg = "'DEFAULT_KEY' has been deprecated, use 'SA_DEFAULT_KEY'"
warnings.warn(msg, UserWarning, stacklevel=2)
return SA_DEFAULT_KEY
raise AttributeError(f"module {__name__} has no attribute {name}")
15 changes: 1 addition & 14 deletions aiohttp_sqlalchemy/constants.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
from typing import TYPE_CHECKING
import warnings

if TYPE_CHECKING:
from typing import Any


SA_DEFAULT_KEY = 'sa_main'


def __getattr__(name: str) -> 'Any':
if name == 'DEFAULT_KEY':
msg = "'DEFAULT_KEY' has been deprecated, use 'SA_DEFAULT_KEY'"
warnings.warn(msg, UserWarning, stacklevel=2)
return SA_DEFAULT_KEY
raise AttributeError(f"module {__name__} has no attribute {name}")
DEFAULT_KEY = SA_DEFAULT_KEY # synonym for SA_DEFAULT_KEY
12 changes: 6 additions & 6 deletions aiohttp_sqlalchemy/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
from aiohttp_sqlalchemy.exceptions import DuplicateRequestKeyError

if TYPE_CHECKING:
from aiohttp.web import Request, StreamResponse
from typing import Any, Awaitable, Callable, Union, Type
from aiohttp.web import StreamResponse
from typing import Any, Callable, Union, Type
from aiohttp_sqlalchemy.typedefs import THandler

TDecorated = Callable[[Request], Awaitable[StreamResponse]]
TResult = Callable[..., Union[Type[AbstractView], TDecorated]]
THandlerWrapper = Callable[..., Union[Type[AbstractView], THandler]]


def sa_decorator(key: str = SA_DEFAULT_KEY) -> 'TResult':
def sa_decorator(key: str = SA_DEFAULT_KEY) -> 'THandlerWrapper':
""" SQLAlchemy asynchronous handler decorator. """
def wrapper(handler: 'TDecorated') -> 'TDecorated':
def wrapper(handler: 'THandler') -> 'THandler':
@wraps(handler)
async def wrapped(*args: 'Any', **kwargs: 'Any') -> 'StreamResponse':
request = args[0].request \
Expand Down
4 changes: 1 addition & 3 deletions aiohttp_sqlalchemy/middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

if TYPE_CHECKING:
from aiohttp.web import Request, StreamResponse
from typing import Awaitable, Callable

THandler = Callable[..., Awaitable[StreamResponse]]
from aiohttp_sqlalchemy.typedefs import THandler


def sa_middleware(key: str = SA_DEFAULT_KEY) -> 'THandler':
Expand Down
11 changes: 11 additions & 0 deletions aiohttp_sqlalchemy/typedefs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from aiohttp.web import StreamResponse
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from typing import Awaitable, Callable, Iterable, Tuple, Union


TSessionFactory = Callable[..., AsyncSession]
THandler = Callable[..., Awaitable[StreamResponse]]

TBindTo = Union[str, AsyncEngine, TSessionFactory]
TBinding = Tuple[TSessionFactory, str, bool]
TBindings = Iterable[TBinding]
1 change: 0 additions & 1 deletion aiohttp_sqlalchemy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from aiohttp.web import Application, Request
from sqlalchemy import MetaData
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Any


async def init_db(
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.15.3'
release = '0.15.4'


# -- General configuration ---------------------------------------------------
Expand Down
22 changes: 14 additions & 8 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,19 +228,25 @@ Decorating handlers
Change log
----------
Version 0.15.4
^^^^^^^^^^^^^^
Changed
"""""""
* Changed ``DEFAULT_KEY`` from deprecated to synonym.

Version 0.15
^^^^^^^^^^^^
Add
"""
* Add synonym ``bind`` for ``sa_bind``.
* Add synonym ``init_db`` for ``sa_init_db``.
Added
"""""
* Added synonym ``bind`` for ``sa_bind``.
* Added synonym ``init_db`` for ``sa_init_db``.

Version 0.14
^^^^^^^^^^^^
Add
"""
* Add utility ``sa_init_db(app, metadata, key = SA_DEFAULT_KEY)``.
* Add constant ``SA_DEFAULT_KEY`` instead ``DEFAULT_KEY``.
Added
"""""
* Added utility ``sa_init_db(app, metadata, key = SA_DEFAULT_KEY)``.
* Added constant ``SA_DEFAULT_KEY`` instead ``DEFAULT_KEY``.

Deprecated
""""""""""
Expand Down
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 14 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
[tool.poetry]
name = "aiohttp-sqlalchemy"
version = "0.15.3"
version = "0.15.4"
description = "SQLAlchemy 1.4 / 2.0 support for aiohttp."
authors = [
"Ruslan Ilyasovich Gilfanov <ri.gilfanov@yandex.ru>",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Framework :: AsyncIO",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Database",
"Topic :: Database :: Front-Ends",
"Topic :: Internet",
"Topic :: Internet :: WWW/HTTP",
]
Expand All @@ -21,7 +24,6 @@ license = "BSD-3-Clause"
readme = "README.rst"

documentation = "https://aiohttp-sqlalchemy.readthedocs.io/"
homepage = "https://github.com/ri-gilfanov/aiohttp-sqlalchemy"
repository = "https://github.com/ri-gilfanov/aiohttp-sqlalchemy"

[tool.poetry.dependencies]
Expand All @@ -32,23 +34,27 @@ aiomysql = { version = ">=0.0.21", optional = true }
asyncpg = { version = "^0.23.0", optional = true }
aiosqlite = { version = "^0.17.0", optional = true }

[tool.poetry.extras]
mysql = ["aiomysql"]
postgresql = ["asyncpg"]
sqlite = ["aiosqlite"]

[tool.poetry.dev-dependencies]
aiosqlite = "^0.17.0"
mypy = ">=0.902"
pytest = ">=6.2.4"
pytest-cov = ">=2.12.1"
Sphinx = ">=4.0.2"
sphinx_rtd_theme = ">=0.5.2"

[tool.poetry.extras]
mysql = ["aiomysql"]
postgres = ["asyncpg"]
sqlite = ["aiosqlite"]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.coverage.report]
exclude_lines = [
"if TYPE_CHECKING",
"if TYPE_CHECKING:",
]
omit = [
"aiohttp_sqlalchemy/typedefs.py",
]
22 changes: 14 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,32 @@
import pytest
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
import typing
from typing import TYPE_CHECKING

import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import SA_DEFAULT_KEY, sa_bind, sa_middleware

if TYPE_CHECKING:
from aiohttp.web import Request, Response
from aiohttp_sqlalchemy.typedefs import TSessionFactory
from sqlalchemy.ext.asyncio import AsyncEngine


pytest_plugins = 'aiohttp.pytest_plugin'


@pytest.fixture
def orm_async_engine():
def orm_async_engine() -> 'AsyncEngine':
return create_async_engine('sqlite+aiosqlite:///')


@pytest.fixture
def orm_session_factory(orm_async_engine):
def orm_session_factory(orm_async_engine: 'AsyncEngine') -> 'TSessionFactory':
return orm.sessionmaker(orm_async_engine, AsyncSession)


@pytest.fixture
def orm_session(orm_session_factory):
def orm_session(orm_session_factory) -> AsyncSession:
return orm_session_factory()


Expand All @@ -34,25 +39,26 @@ def sa_main_middleware():


@pytest.fixture
def middlewared_app(orm_session_factory):
def middlewared_app(orm_session_factory: 'TSessionFactory'):
app = web.Application()
aiohttp_sqlalchemy.setup(app, [sa_bind(orm_session_factory)])
return app


@pytest.fixture
def mocked_request(middlewared_app):
return make_mocked_request(METH_GET, '/', app=middlewared_app)


async def function_handler(request):
async def function_handler(request: 'Request') -> 'Response':
return web.json_response({})


class ClassHandler:
async def get(request):
async def get(self, request: 'Request') -> 'Response':
return web.json_response({})


class ClassBasedView(web.View):
async def get(self):
async def get(self) -> 'Response':
return web.json_response({})
5 changes: 5 additions & 0 deletions tests/test_aiohttp_sqlalchemy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from aiohttp_sqlalchemy import __version__


def test_version():
assert __version__ == '0.15.4'
8 changes: 3 additions & 5 deletions tests/test_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
from aiohttp_sqlalchemy import constants


def test_default_key():
assert aiohttp_sqlalchemy.DEFAULT_KEY == 'sa_main'
assert constants.DEFAULT_KEY == 'sa_main'


def test_sa_default_key():
assert aiohttp_sqlalchemy.SA_DEFAULT_KEY == 'sa_main'
assert aiohttp_sqlalchemy.DEFAULT_KEY == 'sa_main'
assert constants.SA_DEFAULT_KEY == 'sa_main'
assert constants.DEFAULT_KEY == 'sa_main'
3 changes: 2 additions & 1 deletion tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ async def test_decorated_class_based_view(mocked_request):

async def test_decorated_class_handler(mocked_request):
assert mocked_request.get(SA_DEFAULT_KEY) is None
await sa_decorator()(ClassHandler.get)(mocked_request)
class_handler = ClassHandler()
await sa_decorator()(class_handler.get)(mocked_request)
assert isinstance(mocked_request.get(SA_DEFAULT_KEY), AsyncSession)


Expand Down

0 comments on commit 9779da8

Please sign in to comment.