From 60b472af9db4a3c4ea769b0d0b6a30cdc44de17a Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Wed, 31 Mar 2021 18:09:16 +0700 Subject: [PATCH 1/8] WIP: Add set_injector, get_injector --- CHANGELOG.md | 7 ++ tests/core/test_injection.py | 10 +++ tests/test_get_instance.py | 37 ----------- tests/winter_sqlalchemy/test_sqla_crud.py | 71 +++++++++++++++++---- winter/__version__.py | 2 +- winter/core/__init__.py | 2 + winter/core/injection.py | 15 +++++ winter/web/__init__.py | 2 - winter/web/configurer.py | 5 +- winter/web/controller.py | 19 ------ winter_django/view.py | 5 +- winter_sqlalchemy/repository.py | 78 ++++++++++++----------- 12 files changed, 142 insertions(+), 111 deletions(-) create mode 100644 tests/core/test_injection.py delete mode 100644 tests/test_get_instance.py create mode 100644 winter/core/injection.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1da5c3ba..da726dbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [9.0.0] - 2021-03-31 + +### New features +- CRUDRepository now supports custom implementation extensions +### Changed +- set_factory changed to set_injector + ## [8.2.0] - 2021-01-27 ### New features diff --git a/tests/core/test_injection.py b/tests/core/test_injection.py new file mode 100644 index 00000000..b005155e --- /dev/null +++ b/tests/core/test_injection.py @@ -0,0 +1,10 @@ +from injector import Injector + +from winter.core import get_injector +from winter.core import set_injector + + +def test_set_get_injector(): + injector = Injector() + set_injector(injector) + assert get_injector() is injector diff --git a/tests/test_get_instance.py b/tests/test_get_instance.py deleted file mode 100644 index 3e789152..00000000 --- a/tests/test_get_instance.py +++ /dev/null @@ -1,37 +0,0 @@ -from winter.web import get_instance -from winter.web import set_factory - - -class App: - pass - - -class Controller: - def __init__(self, app: App = None): - self.app = app - - -def test_default_get_instance(): - # Act - controller = get_instance(Controller) - - # Assert - assert controller.app is None - - -def test_get_instance(): - app = App() - - def factory(cls): - return cls(app) - - set_factory(factory) - try: - # Act - controller = get_instance(Controller) - - # Assert - assert isinstance(controller, Controller) - assert controller.app is app - finally: - set_factory(None) diff --git a/tests/winter_sqlalchemy/test_sqla_crud.py b/tests/winter_sqlalchemy/test_sqla_crud.py index a76ac6f6..dd2d7c31 100644 --- a/tests/winter_sqlalchemy/test_sqla_crud.py +++ b/tests/winter_sqlalchemy/test_sqla_crud.py @@ -1,15 +1,22 @@ +from abc import ABCMeta +from abc import abstractmethod from typing import Optional import pytest +import sqlalchemy as sa from dataclasses import dataclass +from injector import Injector +from injector import inject from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import MetaData from sqlalchemy import String from sqlalchemy import Table from sqlalchemy import create_engine +from sqlalchemy.engine import Engine from sqlalchemy.orm import mapper +from winter.core import set_injector from winter.data import CRUDRepository from winter.data.exceptions import NotFoundException from winter_ddd import AggregateRoot @@ -25,6 +32,7 @@ class MarkedAsDoneDomainEvent(DomainEvent): class MyEntity(AggregateRoot): def __init__(self, id_: int, name: Optional[str], lastname: Optional[str]): + super().__init__() self.id = id_ self.name = name self.lastname = lastname @@ -49,13 +57,37 @@ class Fixture: def __init__(self): self._engine = create_engine('sqlite://') - @sqla_crud class MyRepository(CRUDRepository[MyEntity, int]): - def find_one_by_name_and_lastname(self, name: str, lastname: str) -> MyEntity: + @abstractmethod + def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[MyEntity]: pass + class MyRepositoryImpl(MyRepository, metaclass=ABCMeta): + @inject + def __init__(self, engine: Engine): + self._engine = engine + + def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[MyEntity]: + query = sa.select([ + my_entity_table.c.id, + ]).where( + sa.and_( + my_entity_table.c.name == name, + my_entity_table.c.lastname == lastname, + ), + ) + with self._engine.connect() as connection: + rows = connection.execute(query) + rows = list(rows) + assert len(rows) <= 1 + if not rows: + return None + entity_id = rows[0][0] + return self.get_by_id(entity_id) + metadata.create_all(bind=self._engine) - repository = MyRepository(engine=self._engine) + set_injector(Injector()) + repository = sqla_crud(MyRepository)(engine=self._engine) self.repository = repository class DomainEventHandlers: @@ -244,11 +276,28 @@ def test_save_many(): assert set(entities) == {(1, 'done'), (2, 'started'), (3, 'done'), (100, 'handled'), (300, 'handled')} -# def test_find_one_by_name_and_lastname(): -# fixture = Fixture() -# fixture.execute("INSERT INTO my_entities (id, name, lastname) VALUES (1, 'name', NULL), (2, 'name', 'lastname');") -# -# # Act -# entity = fixture.repository.find_one_by_name_and_lastname('name', 'lastname') -# -# assert entity.id == 2 +@pytest.mark.parametrize( + 'name, lastname, entity_id', [ + ('name', 'something', None), + ('name', 'lastname', 2), + ], +) +def test_find_one_by_name_and_lastname(name, lastname, entity_id): + fixture = Fixture() + fixture.execute("INSERT INTO my_entities (id, name, lastname) VALUES (1, 'name', NULL), (2, 'name', 'lastname');") + + # Act + entity = fixture.repository.find_one_by_name_and_lastname(name, lastname) + + if entity_id is None: + assert entity is None + else: + assert entity.id == entity_id + + +def test_raises_if_injector_is_not_configured(): + class MyRepository(CRUDRepository[MyEntity, int]): + pass + + with pytest.raises(RuntimeError, match='Injector is not configured'): + sqla_crud(MyRepository) diff --git a/winter/__version__.py b/winter/__version__.py index e93ed637..33de8d16 100644 --- a/winter/__version__.py +++ b/winter/__version__.py @@ -1 +1 @@ -__version__ = '8.3.0' +__version__ = '9.0.0' diff --git a/winter/core/__init__.py b/winter/core/__init__.py index 7d534795..a5be88b0 100644 --- a/winter/core/__init__.py +++ b/winter/core/__init__.py @@ -9,4 +9,6 @@ from .component_method import component_method from .component_method_argument import ArgumentDoesNotHaveDefault from .component_method_argument import ComponentMethodArgument +from .injection import get_injector +from .injection import set_injector from .utils import cached_property diff --git a/winter/core/injection.py b/winter/core/injection.py new file mode 100644 index 00000000..ac19b1e9 --- /dev/null +++ b/winter/core/injection.py @@ -0,0 +1,15 @@ +from typing import Optional + +from injector import Injector + +_injector: Optional[Injector] = None + + +def set_injector(injector: Injector) -> None: + global _injector + _injector = injector + + +def get_injector() -> Injector: + global _injector + return _injector diff --git a/winter/web/__init__.py b/winter/web/__init__.py index 6e5cc0a2..16464188 100644 --- a/winter/web/__init__.py +++ b/winter/web/__init__.py @@ -4,8 +4,6 @@ from .configurer import Configurer from .controller import controller from .controller import get_component -from .controller import get_instance -from .controller import set_factory from .exceptions import ExceptionHandler from .exceptions import exception_handlers_registry from .exceptions import problem diff --git a/winter/web/configurer.py b/winter/web/configurer.py index c8e247a0..c3f6818c 100644 --- a/winter/web/configurer.py +++ b/winter/web/configurer.py @@ -1,6 +1,6 @@ import abc -from .controller import get_instance +from winter.core import get_injector from .interceptor import InterceptorRegistry from .interceptor import interceptor_registry @@ -12,6 +12,7 @@ def add_interceptors(self, registry: InterceptorRegistry): def run_configurers(): + injector = get_injector() for configurer_class in Configurer.__subclasses__(): - configurer = get_instance(configurer_class) + configurer = injector.get(configurer_class) configurer.add_interceptors(interceptor_registry) diff --git a/winter/web/controller.py b/winter/web/controller.py index efb8b074..c0043c07 100644 --- a/winter/web/controller.py +++ b/winter/web/controller.py @@ -1,31 +1,12 @@ -from typing import Callable -from typing import NewType -from typing import Optional from typing import Type -from typing import TypeVar from winter.core import Component -Factory = NewType('Factory', Callable[[Type], object]) -_factory: Optional[Factory] = None -T = TypeVar('T') - def controller(controller_class: Type) -> Type: Component.register(controller_class) return controller_class -def set_factory(controller_factory: Factory) -> None: - global _factory - _factory = controller_factory - - -def get_instance(class_: Type[T]) -> T: - if _factory is None: - return class_() - return _factory(class_) - - def get_component(class_: Type) -> Component: return Component.get_by_cls(class_) diff --git a/winter_django/view.py b/winter_django/view.py index 156246c0..3449b70a 100644 --- a/winter_django/view.py +++ b/winter_django/view.py @@ -13,9 +13,9 @@ from rest_framework.request import Request from winter.core import ComponentMethod +from winter.core import get_injector from winter.web import ResponseEntity from winter.web import get_component -from winter.web import get_instance from winter.web import response_headers_serializer from winter.web.argument_resolver import arguments_resolver from winter.web.auth import is_authentication_needed @@ -70,10 +70,11 @@ class WinterView(rest_framework.views.APIView): def _create_dispatch_function(controller_class, route: Route): component = get_component(controller_class) + injector = get_injector() @wraps(route.method.func) def dispatch(winter_view, request: Request, **path_variables): - controller = get_instance(component.component_cls) + controller = injector.get(component.component_cls) return _call_controller_method(controller, route, request) return dispatch diff --git a/winter_sqlalchemy/repository.py b/winter_sqlalchemy/repository.py index 9d7bf325..7622d98e 100644 --- a/winter_sqlalchemy/repository.py +++ b/winter_sqlalchemy/repository.py @@ -14,6 +14,7 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.orm.exc import UnmappedClassError +from winter.core import get_injector from winter.data import CRUDRepository from winter.data.exceptions import NotFoundException from winter_ddd import AggregateRoot @@ -25,6 +26,11 @@ def sqla_crud(repository_cls): + injector = get_injector() + + if not injector: + raise RuntimeError('Injector is not configured') + if not issubclass(repository_cls, CRUDRepository): raise TypeError('Repository must be inherited from CRUDRepository before annotating with sqla_crud') @@ -43,7 +49,7 @@ def sqla_crud(repository_cls): entity_table = mapper.tables[0] - class RepositoryImpl(repository_cls): + class DefaultCRUDRepositoryImpl: """ SQLAlchemy implementation for CRUDRepository This repository implementation is not thread-safe. @@ -52,51 +58,51 @@ class RepositoryException(Exception): pass def count(self) -> int: - return self._engine.execute(select([func.count()]).select_from(entity_table)).scalar() + return self.__engine.execute(select([func.count()]).select_from(entity_table)).scalar() def delete(self, entity: T): try: - session = self._sessions[entity] + session = self.__sessions[entity] except KeyError: raise self.RepositoryException('Entity must be fetched with repository before being deleted') pk = inspect(entity).identity - del self._identity_map[pk] + del self.__identity_map[pk] session.delete(entity) session.commit() session.close() - del self._sessions[entity] + del self.__sessions[entity] def delete_many(self, entities: Iterable[T]): for entity in entities: self.delete(entity) def delete_all(self): - self._engine.execute(entity_table.delete()) - self._identity_map = {} - for session in self._sessions.values(): + self.__engine.execute(entity_table.delete()) + self.__identity_map = {} + for session in self.__sessions.values(): session.close() - self._sessions = {} + self.__sessions = {} def delete_by_id(self, id_: K): if not isinstance(id_, tuple): id_ = (id_,) - if id_ in self._identity_map: - entity = self._identity_map[id_] + if id_ in self.__identity_map: + entity = self.__identity_map[id_] self.delete(entity) else: expressions = (column == value for column, value in zip(entity_table.primary_key.columns, id_)) - self._engine.execute(entity_table.delete().where(and_(*expressions))) + self.__engine.execute(entity_table.delete().where(and_(*expressions))) def exists_by_id(self, id_: K) -> bool: if not isinstance(id_, tuple): id_ = (id_,) - if id_ in self._identity_map: + if id_ in self.__identity_map: return True expressions = (column == value for column, value in zip(entity_table.primary_key.columns, id_)) - return self._engine.execute(select([exists().where(and_(*expressions))])).scalar() + return self.__engine.execute(select([exists().where(and_(*expressions))])).scalar() def find_all(self) -> Iterable[T]: - ids = self._engine.execute(select(entity_table.primary_key.columns)) + ids = self.__engine.execute(select(entity_table.primary_key.columns)) ids = next(zip(*ids)) return self.find_all_by_id(ids) @@ -108,16 +114,16 @@ def find_all_by_id(self, ids: Iterable[K]) -> Iterable[T]: def find_by_id(self, id_: K) -> Optional[T]: if not isinstance(id_, tuple): id_ = (id_,) - if id_ in self._identity_map: - return self._identity_map[id_] + if id_ in self.__identity_map: + return self.__identity_map[id_] - session = self._session_factory() + session = self.__session_factory() instance = session.query(entity_cls).get(id_) if instance is None: return None - self._identity_map[id_] = instance - self._sessions[instance] = session + self.__identity_map[id_] = instance + self.__sessions[instance] = session return instance def get_by_id(self, id_: K) -> T: @@ -139,12 +145,12 @@ def save_many(self, entities: Iterable[T]) -> Iterable[T]: return entities def _save(self, entity: T): - if entity not in self._sessions: - self._sessions[entity] = self._session_factory() - self._sessions[entity].add(entity) - self._sessions[entity].flush() + if entity not in self.__sessions: + self.__sessions[entity] = self.__session_factory() + self.__sessions[entity].add(entity) + self.__sessions[entity].flush() pk = inspect(entity).identity - self._identity_map[pk] = entity + self.__identity_map[pk] = entity def _process_domain_events(self, aggregates: Iterable[AggregateRoot]): domain_events: List[DomainEvent] = [] @@ -155,17 +161,15 @@ def _process_domain_events(self, aggregates: Iterable[AggregateRoot]): @inject def __init__(self, engine: Engine): - self._engine = engine - self._session_factory = sessionmaker(bind=self._engine) - self._identity_map = {} - self._sessions = {} - - # def func1(self, name, lastname): - # return self._session_factory().query(entity_cls).filter( - # getattr(entity_cls, 'name') == name, - # getattr(entity_cls, 'lastname') == lastname, - # ).one_or_none() - # - # RepositoryImpl.find_one_by_name_and_lastname = func1 + self.__engine = engine + self.__session_factory = sessionmaker(bind=self.__engine) + self.__identity_map = {} + self.__sessions = {} + + class RepositoryImpl(DefaultCRUDRepositoryImpl): # , repository_impl): + @inject + def __init__(self): + injector.call_with_injection(DefaultCRUDRepositoryImpl.__init__, self) + # injector.call_with_injection(repository_impl.__init__, self) return RepositoryImpl From 68085c23dfff4462063666bda5d62f612ed199c2 Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 16:22:55 +0700 Subject: [PATCH 2/8] Custom CRUD methods --- CHANGELOG.md | 1 + tests/conftest.py | 18 +++ tests/core/test_injection.py | 10 +- tests/winter_sqlalchemy/test_injector.py | 63 --------- tests/winter_sqlalchemy/test_sqla_crud.py | 164 ++++++++++------------ winter/core/utils/typing.py | 4 +- winter_ddd/domain_event_dispatcher.py | 8 +- winter_sqlalchemy/repository.py | 9 +- 8 files changed, 109 insertions(+), 168 deletions(-) delete mode 100644 tests/winter_sqlalchemy/test_injector.py diff --git a/CHANGELOG.md b/CHANGELOG.md index da726dbd..65cf7ae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - CRUDRepository now supports custom implementation extensions ### Changed - set_factory changed to set_injector +- DomainEventDispatcher.set_handler_factory is deleted ## [8.2.0] - 2021-01-27 diff --git a/tests/conftest.py b/tests/conftest.py index 8a83d097..aaf65f43 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,14 @@ from pathlib import Path import django +from injector import CallableProvider +from injector import Injector +from injector import Module +from injector import singleton +from sqlalchemy import create_engine +from sqlalchemy.engine import Engine +from winter.core import set_injector from .entities import Guest @@ -28,4 +35,15 @@ def pytest_configure(): 'tests', ), ) + injector = Injector([Configuration]) + set_injector(injector) django.setup() + + +class Configuration(Module): + def configure(self, binder): + binder.bind(Engine, to=CallableProvider(make_engine), scope=singleton) + + +def make_engine(): + return create_engine('sqlite://') diff --git a/tests/core/test_injection.py b/tests/core/test_injection.py index b005155e..3f22d28e 100644 --- a/tests/core/test_injection.py +++ b/tests/core/test_injection.py @@ -1,10 +1,6 @@ -from injector import Injector - from winter.core import get_injector -from winter.core import set_injector -def test_set_get_injector(): - injector = Injector() - set_injector(injector) - assert get_injector() is injector +def test_get_injector(): + injector = get_injector() + assert injector is not None diff --git a/tests/winter_sqlalchemy/test_injector.py b/tests/winter_sqlalchemy/test_injector.py deleted file mode 100644 index cbde0dc6..00000000 --- a/tests/winter_sqlalchemy/test_injector.py +++ /dev/null @@ -1,63 +0,0 @@ -from typing import Optional - -from injector import ClassProvider -from injector import Injector -from injector import Module -from injector import singleton -from sqlalchemy import Column -from sqlalchemy import Integer -from sqlalchemy import MetaData -from sqlalchemy import String -from sqlalchemy import Table -from sqlalchemy import create_engine -from sqlalchemy.engine import Engine -from sqlalchemy.orm import mapper - -from winter.data import CRUDRepository -from winter_ddd import AggregateRoot -from winter_sqlalchemy import sqla_crud - - -class Fixture: - def __init__(self): - class MyEntity(AggregateRoot): - def __init__(self, id_: int, name: Optional[str]): - self.id = id_ - self.name = name - - engine = self._engine = create_engine('sqlite://') - metadata = MetaData() - my_entity_table = Table( - 'my_entities', - metadata, - Column('id', Integer, primary_key=True), - Column('name', String), - ) - mapper(MyEntity, my_entity_table) - - class MyRepository(CRUDRepository[MyEntity, int]): - pass - - metadata.create_all(bind=self._engine) - - class Configuration(Module): - def configure(self, binder): - binder.bind(Engine, engine) - binder.bind(MyRepository, to=ClassProvider(sqla_crud(MyRepository)), scope=singleton) - - configuration = Configuration() - injector = Injector([configuration]) - self.repository = injector.get(MyRepository) - - def execute(self, sql): - return self._engine.execute(sql) - - -def test_count(): - fixture = Fixture() - fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') - - # Act - count = fixture.repository.count() - - assert count == 3 diff --git a/tests/winter_sqlalchemy/test_sqla_crud.py b/tests/winter_sqlalchemy/test_sqla_crud.py index dd2d7c31..31076c24 100644 --- a/tests/winter_sqlalchemy/test_sqla_crud.py +++ b/tests/winter_sqlalchemy/test_sqla_crud.py @@ -5,18 +5,17 @@ import pytest import sqlalchemy as sa from dataclasses import dataclass -from injector import Injector +from injector import ClassProvider from injector import inject from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import MetaData from sqlalchemy import String from sqlalchemy import Table -from sqlalchemy import create_engine from sqlalchemy.engine import Engine from sqlalchemy.orm import mapper -from winter.core import set_injector +from winter.core import get_injector from winter.data import CRUDRepository from winter.data.exceptions import NotFoundException from winter_ddd import AggregateRoot @@ -53,60 +52,74 @@ def mark_as_done(self): mapper(MyEntity, my_entity_table) -class Fixture: - def __init__(self): - self._engine = create_engine('sqlite://') - - class MyRepository(CRUDRepository[MyEntity, int]): - @abstractmethod - def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[MyEntity]: - pass - - class MyRepositoryImpl(MyRepository, metaclass=ABCMeta): - @inject - def __init__(self, engine: Engine): - self._engine = engine - - def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[MyEntity]: - query = sa.select([ - my_entity_table.c.id, - ]).where( - sa.and_( - my_entity_table.c.name == name, - my_entity_table.c.lastname == lastname, - ), - ) - with self._engine.connect() as connection: - rows = connection.execute(query) - rows = list(rows) - assert len(rows) <= 1 - if not rows: - return None - entity_id = rows[0][0] - return self.get_by_id(entity_id) +class MyRepository(CRUDRepository[MyEntity, int]): + @abstractmethod + def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[MyEntity]: + pass + + +class MyRepositoryImpl(MyRepository, metaclass=ABCMeta): + @inject + def __init__(self, engine: Engine): + self._engine = engine + + def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[MyEntity]: + query = sa.select([ + my_entity_table.c.id, + ]).where( + sa.and_(my_entity_table.c.name == name, my_entity_table.c.lastname == lastname), + ) + with self._engine.connect() as connection: + rows = connection.execute(query) + rows = list(rows) + assert len(rows) <= 1 + if not rows: + return None + entity_id = rows[0][0] + return self.get_by_id(entity_id) + + +class DomainEventHandlers: + @inject + def __init__(self, repository: MyRepository): + self._repository = repository + + @domain_event_handler + def on_marked_as_done(self, event: MarkedAsDoneDomainEvent): + entity = event.entity + new_entity = MyEntity( + id_=entity.id * 100, + name='handled', + lastname='', + ) + self._repository.save(new_entity) + + +@pytest.fixture() +def fixture(): + return get_injector().get(Fixture) + +@pytest.fixture() +def engine(): + return get_injector().get(Engine) + + +class Fixture: + @inject + def __init__(self, engine: Engine): + injector = get_injector() + injector.binder.bind(MyRepository, to=ClassProvider(sqla_crud(MyRepository))) + self._engine = engine + self.repository = injector.get(MyRepository) + metadata.drop_all(bind=self._engine) metadata.create_all(bind=self._engine) - set_injector(Injector()) - repository = sqla_crud(MyRepository)(engine=self._engine) - self.repository = repository - - class DomainEventHandlers: - @domain_event_handler - def on_marked_as_done(self, event: MarkedAsDoneDomainEvent): - entity = event.entity - new_entity = MyEntity( - id_=entity.id * 100, - name='handled', - lastname='', - ) - repository.save(new_entity) def execute(self, sql): return self._engine.execute(sql) -def test_count(): - fixture = Fixture() +def test_count(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') # Act @@ -115,8 +128,7 @@ def test_count(): assert count == 3 -def test_delete(): - fixture = Fixture() +def test_delete(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2);') entity = fixture.repository.find_by_id(1) @@ -128,8 +140,7 @@ def test_delete(): assert fixture.repository.find_by_id(1) is None -def test_delete_many(): - fixture = Fixture() +def test_delete_many(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') entity_1 = fixture.repository.find_by_id(1) entity_3 = fixture.repository.find_by_id(3) @@ -143,8 +154,7 @@ def test_delete_many(): assert fixture.repository.find_by_id(3) is None -def test_delete_all(): - fixture = Fixture() +def test_delete_all(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') # Act @@ -154,8 +164,7 @@ def test_delete_all(): assert count == 0 -def test_delete_by_id(): - fixture = Fixture() +def test_delete_by_id(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2);') # Act @@ -165,8 +174,7 @@ def test_delete_by_id(): assert list(result) == [(2,)] -def test_exists_by_id(): - fixture = Fixture() +def test_exists_by_id(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2);') # Act @@ -175,8 +183,7 @@ def test_exists_by_id(): assert exists is True -def test_not_exists_by_id(): - fixture = Fixture() +def test_not_exists_by_id(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2);') # Act @@ -185,8 +192,7 @@ def test_not_exists_by_id(): assert exists is False -def test_find_all(): - fixture = Fixture() +def test_find_all(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') # Act @@ -197,8 +203,7 @@ def test_find_all(): assert set(ids) == {1, 2, 3} -def test_find_all_by_id(): - fixture = Fixture() +def test_find_all_by_id(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') # Act @@ -209,8 +214,7 @@ def test_find_all_by_id(): assert set(ids) == {1, 3} -def test_find_by_id(): - fixture = Fixture() +def test_find_by_id(fixture): fixture.execute('INSERT INTO my_entities (id) VALUES (1), (2), (3);') # Act @@ -219,16 +223,13 @@ def test_find_by_id(): assert entity.id == 2 -def test_get_by_id(): - fixture = Fixture() - +def test_get_by_id(fixture): with pytest.raises(NotFoundException, match='MyEntity with ID=2 not found'): # Act fixture.repository.get_by_id(2) -def test_save_new(): - fixture = Fixture() +def test_save_new(fixture): entity = MyEntity(id_=1, name='name', lastname='lastname') # Act @@ -241,8 +242,7 @@ def test_save_new(): assert entities[0].lastname == 'lastname' -def test_save(): - fixture = Fixture() +def test_save(fixture): fixture.execute("INSERT INTO my_entities (id, name) VALUES (1, 'started'), (2, 'started'), (3, 'started');") entity = fixture.repository.find_by_id(2) entity.mark_as_done() @@ -257,8 +257,7 @@ def test_save(): assert set(entities) == {(1, 'started'), (2, 'done'), (3, 'started'), (200, 'handled')} -def test_save_many(): - fixture = Fixture() +def test_save_many(fixture): fixture.execute("INSERT INTO my_entities (id, name) VALUES (1, 'started'), (2, 'started'), (3, 'started');") entities = fixture.repository.find_all_by_id([1, 3]) for entity in entities: @@ -282,8 +281,7 @@ def test_save_many(): ('name', 'lastname', 2), ], ) -def test_find_one_by_name_and_lastname(name, lastname, entity_id): - fixture = Fixture() +def test_find_one_by_name_and_lastname(fixture, name, lastname, entity_id): fixture.execute("INSERT INTO my_entities (id, name, lastname) VALUES (1, 'name', NULL), (2, 'name', 'lastname');") # Act @@ -293,11 +291,3 @@ def test_find_one_by_name_and_lastname(name, lastname, entity_id): assert entity is None else: assert entity.id == entity_id - - -def test_raises_if_injector_is_not_configured(): - class MyRepository(CRUDRepository[MyEntity, int]): - pass - - with pytest.raises(RuntimeError, match='Injector is not configured'): - sqla_crud(MyRepository) diff --git a/winter/core/utils/typing.py b/winter/core/utils/typing.py index d50551b1..559ccd27 100644 --- a/winter/core/utils/typing.py +++ b/winter/core/utils/typing.py @@ -62,6 +62,4 @@ def get_generic_args(type_): if sys.version_info >= (3, 8): from typing import get_args return get_args(type_) - if sys.version_info >= (3, 7): - return type_.__args__ - return type_.__args__ + return getattr(type_, '__args__', ()) diff --git a/winter_ddd/domain_event_dispatcher.py b/winter_ddd/domain_event_dispatcher.py index 6536eb78..b979995d 100644 --- a/winter_ddd/domain_event_dispatcher.py +++ b/winter_ddd/domain_event_dispatcher.py @@ -6,6 +6,7 @@ from typing import Type from typing import TypeVar +from winter.core import get_injector from .domain_event import DomainEvent from .domain_event_subscription import DomainEventSubscription @@ -19,10 +20,7 @@ class DomainEventDispatcher: def __init__(self): self._subscriptions: Dict[EventFilter, List[DomainEventSubscription]] = {} self._event_type_to_event_filters_map: Dict[Type[DomainEvent], List[EventFilter]] = {} - self._handler_factory = lambda cls: cls() - - def set_handler_factory(self, handler_factory: HandlerFactory): - self._handler_factory = handler_factory + self._injector = get_injector() def add_subscription(self, subscription: DomainEventSubscription): self._subscriptions.setdefault(subscription.event_filter, []).append(subscription) @@ -42,7 +40,7 @@ def dispatch(self, events: Iterable[DomainEvent]): for event_filter, events in filtered_events.items(): for subscription in self._subscriptions.get(event_filter, []): - handler_instance = self._handler_factory(subscription.handler_class) + handler_instance = self._injector.get(subscription.handler_class) if subscription.collection: subscription.handler_method(handler_instance, events) else: diff --git a/winter_sqlalchemy/repository.py b/winter_sqlalchemy/repository.py index 7622d98e..94e9469b 100644 --- a/winter_sqlalchemy/repository.py +++ b/winter_sqlalchemy/repository.py @@ -103,7 +103,7 @@ def exists_by_id(self, id_: K) -> bool: def find_all(self) -> Iterable[T]: ids = self.__engine.execute(select(entity_table.primary_key.columns)) - ids = next(zip(*ids)) + ids = next(zip(*ids), []) return self.find_all_by_id(ids) def find_all_by_id(self, ids: Iterable[K]) -> Iterable[T]: @@ -166,10 +166,13 @@ def __init__(self, engine: Engine): self.__identity_map = {} self.__sessions = {} - class RepositoryImpl(DefaultCRUDRepositoryImpl): # , repository_impl): + repository_subclasses = repository_cls.__subclasses__() + + class RepositoryImpl(DefaultCRUDRepositoryImpl, *repository_subclasses): @inject def __init__(self): injector.call_with_injection(DefaultCRUDRepositoryImpl.__init__, self) - # injector.call_with_injection(repository_impl.__init__, self) + for subclass in repository_subclasses: + injector.call_with_injection(subclass.__init__, self) return RepositoryImpl From 7badd0ef9571f1b26358ede670861f4884bc3915 Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 16:31:46 +0700 Subject: [PATCH 3/8] Fix bug --- tests/winter_sqlalchemy/test_sqla_crud.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/winter_sqlalchemy/test_sqla_crud.py b/tests/winter_sqlalchemy/test_sqla_crud.py index 31076c24..8e2bed79 100644 --- a/tests/winter_sqlalchemy/test_sqla_crud.py +++ b/tests/winter_sqlalchemy/test_sqla_crud.py @@ -1,4 +1,3 @@ -from abc import ABCMeta from abc import abstractmethod from typing import Optional @@ -58,7 +57,7 @@ def find_one_by_name_and_lastname(self, name: str, lastname: str) -> Optional[My pass -class MyRepositoryImpl(MyRepository, metaclass=ABCMeta): +class MyRepositoryImpl(MyRepository): @inject def __init__(self, engine: Engine): self._engine = engine From 9245abf37e0d183bcea64faec3e980483f8a44f5 Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 16:40:05 +0700 Subject: [PATCH 4/8] Fix coverage --- tests/controllers/controller_with_exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/controllers/controller_with_exceptions.py b/tests/controllers/controller_with_exceptions.py index dbebcb2b..330bb261 100644 --- a/tests/controllers/controller_with_exceptions.py +++ b/tests/controllers/controller_with_exceptions.py @@ -36,8 +36,8 @@ def handle(self, exception: CustomException) -> int: class WithUnknownArgumentExceptionHandler(winter.web.ExceptionHandler): @winter.response_status(400) - def handle(self, exception: CustomException, unknown_argument: int) -> str: - return 'Bad Request' + def handle(self, exception: WithUnknownArgumentException, unknown_argument: int) -> str: + pass winter.web.exception_handlers_registry.add_handler(CustomException, CustomExceptionHandler) From 210e5dc59831f0a2d39c0166ca6a5b036cbddc4d Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 17:17:56 +0700 Subject: [PATCH 5/8] Fix coverage --- tests/controllers/__init__.py | 1 - .../controller_with_media_types_routing.py | 11 ------- .../controller_with_path_parameters.py | 4 +-- .../controller_with_problem_exceptions.py | 4 +-- .../controller_with_response_headers.py | 4 +-- tests/controllers/simple_controller.py | 2 +- tests/core/test_annotations.py | 20 +++++++------ tests/core/test_component.py | 6 ++-- tests/core/test_component_method.py | 2 +- tests/core/test_component_method_argument.py | 8 ++--- tests/routing/test_path_parameters.py | 30 +++++++++++-------- tests/urls.py | 1 - 12 files changed, 43 insertions(+), 50 deletions(-) delete mode 100644 tests/controllers/controller_with_media_types_routing.py diff --git a/tests/controllers/__init__.py b/tests/controllers/__init__.py index eccdd48f..f114f1d3 100644 --- a/tests/controllers/__init__.py +++ b/tests/controllers/__init__.py @@ -1,7 +1,6 @@ from .controller_with_exceptions import ControllerWithExceptions from .controller_with_request_data import ControllerWithRequestData from .controller_with_limits import ControllerWithLimits -from .controller_with_media_types_routing import ControllerWithMediaTypesRouting from .controller_with_path_parameters import ControllerWithPathParameters from .controller_with_problem_exceptions import ControllerWithProblemExceptions from .controller_with_query_parameters import ControllerWithQueryParameters diff --git a/tests/controllers/controller_with_media_types_routing.py b/tests/controllers/controller_with_media_types_routing.py deleted file mode 100644 index 3b3010f3..00000000 --- a/tests/controllers/controller_with_media_types_routing.py +++ /dev/null @@ -1,11 +0,0 @@ -import winter -from winter.web import MediaType - - -@winter.controller -@winter.route('controller_with_media_types_routing/') -class ControllerWithMediaTypesRouting: - - @winter.route_get('xml/', produces=(MediaType.APPLICATION_XML, )) - def get_xml(self) -> str: - return 'Hello, sir!' diff --git a/tests/controllers/controller_with_path_parameters.py b/tests/controllers/controller_with_path_parameters.py index 1d7dd329..a920793c 100644 --- a/tests/controllers/controller_with_path_parameters.py +++ b/tests/controllers/controller_with_path_parameters.py @@ -18,7 +18,7 @@ def _missing_(cls, value): # This is need because of needing of instancing from try: value = int(value) except ValueError: - super()._missing_(cls, value) + super()._missing_(cls, value) # pragma: no cover else: return cls(value) @@ -37,4 +37,4 @@ def test( param5: OneTwoEnumWithInt, param6: str, ) -> str: - return 'Hello, sir!' + pass diff --git a/tests/controllers/controller_with_problem_exceptions.py b/tests/controllers/controller_with_problem_exceptions.py index 7b110703..234626e9 100644 --- a/tests/controllers/controller_with_problem_exceptions.py +++ b/tests/controllers/controller_with_problem_exceptions.py @@ -38,8 +38,8 @@ class AllFieldConstProblemExistsException(Exception): class ProblemExistsExceptionHandler(winter.web.ExceptionHandler): @winter.response_status(HTTPStatus.FORBIDDEN) - def handle(self, exception: ProblemExistsException, **kwargs) -> Dict: - return {'message': str(exception)} + def handle(self, exception: ProblemExistsException, **kwargs) -> Dict: # pragma: no cover + pass @dataclasses.dataclass diff --git a/tests/controllers/controller_with_response_headers.py b/tests/controllers/controller_with_response_headers.py index ea76d633..7cc9f39f 100644 --- a/tests/controllers/controller_with_response_headers.py +++ b/tests/controllers/controller_with_response_headers.py @@ -50,5 +50,5 @@ def two_headers(self, header1: ResponseHeader[str], header2: ResponseHeader[str] return 'OK' @winter.route_get('header-without-annotation/') - def header_without_annotation(self, header: ResponseHeader[str]) -> str: - return 'OK' + def header_without_annotation(self, header: ResponseHeader[str]) -> str: # pragma: no cover + pass diff --git a/tests/controllers/simple_controller.py b/tests/controllers/simple_controller.py index 883434ac..0c3e8c5d 100644 --- a/tests/controllers/simple_controller.py +++ b/tests/controllers/simple_controller.py @@ -68,5 +68,5 @@ def put(self): pass @winter.response_status(HTTPStatus.OK) - def no_route(self): + def no_route(self): # pragma: no cover pass diff --git a/tests/core/test_annotations.py b/tests/core/test_annotations.py index 8ef76d68..6848737d 100644 --- a/tests/core/test_annotations.py +++ b/tests/core/test_annotations.py @@ -45,7 +45,7 @@ def test_on_method_by_decorator(decorator): class SimpleComponent: @decorator('test') - def method(self): + def method(self): # pragma: no cover pass assert SimpleComponent.method.annotations.get(SimpleAnnotation) == [SimpleAnnotation('test')] @@ -82,11 +82,13 @@ def test_get_one(): assert annotation == SimpleAnnotation('first') -@pytest.mark.parametrize(('decorator_factory', 'error_message_template'), ( - (annotate, 'Need function or class. Got: {instance}'), - (annotate_class, 'Need class. Got: {instance}'), - (annotate_method, 'Need function. Got: {instance}'), -)) +@pytest.mark.parametrize( + ('decorator_factory', 'error_message_template'), ( + (annotate, 'Need function or class. Got: {instance}'), + (annotate_class, 'Need class. Got: {instance}'), + (annotate_method, 'Need function. Got: {instance}'), + ), +) def test_annotate_with_instance(decorator_factory, error_message_template): instance = object() @@ -103,7 +105,7 @@ def test_empty_annotation(): class Controller: @annotate(None) - def simple_method(self): + def simple_method(self): # pragma: no cover return None component = Component.get_by_cls(Controller) @@ -119,7 +121,7 @@ def test_try_twice_annotation_with_single(): with pytest.raises(AlreadyAnnotated) as exception: @annotate(SimpleAnnotation('test'), single=True) @annotate(SimpleAnnotation('test'), single=True) - def simple_method(): + def simple_method(): # pragma: no cover return None assert exception.value.annotation == SimpleAnnotation('test') @@ -130,7 +132,7 @@ def test_try_twice_annotation_with_unique(): with pytest.raises(AlreadyAnnotated) as exception: @annotate(SimpleAnnotation('test'), unique=True) @annotate(SimpleAnnotation('test'), unique=True) - def simple_method(): + def simple_method(): # pragma: no cover return None assert exception.value.annotation == SimpleAnnotation('test') diff --git a/tests/core/test_component.py b/tests/core/test_component.py index 30c4a701..b32d889f 100644 --- a/tests/core/test_component.py +++ b/tests/core/test_component.py @@ -51,8 +51,8 @@ def test_method_state(): class SimpleComponent: @simple_annotation('/url/') - def simple_method(self): - return 123 + def simple_method(self): # pragma: no cover + pass assert SimpleComponent.simple_method.annotations.get(SimpleAnnotation) == [SimpleAnnotation('/url/')] @@ -63,7 +63,7 @@ class SimpleComponent: @simple_annotation('first') @simple_annotation('second') - def simple_method(self): + def simple_method(self): # pragma: no cover return None expected_annotations = [SimpleAnnotation('second'), SimpleAnnotation('first')] diff --git a/tests/core/test_component_method.py b/tests/core/test_component_method.py index e93ea8b7..ece14029 100644 --- a/tests/core/test_component_method.py +++ b/tests/core/test_component_method.py @@ -35,7 +35,7 @@ def method(self, number: argument_type): def test_component_method(): - def test(): + def test(): # pragma: no cover return None method = ComponentMethod(test) diff --git a/tests/core/test_component_method_argument.py b/tests/core/test_component_method_argument.py index a7926939..3fd8db25 100644 --- a/tests/core/test_component_method_argument.py +++ b/tests/core/test_component_method_argument.py @@ -5,8 +5,8 @@ def test_parameter(): - def test(number: int) -> int: - return number + def test(number: int) -> int: # pragma: no cover + pass method = ComponentMethod(test) argument = method.get_argument('number') @@ -17,8 +17,8 @@ def test(number: int) -> int: def test_default(): - def test(number: int) -> int: - return number + def test(number: int) -> int: # pragma: no cover + pass method = ComponentMethod(test) argument = method.get_argument('number') diff --git a/tests/routing/test_path_parameters.py b/tests/routing/test_path_parameters.py index 25f792e2..b0f30d77 100644 --- a/tests/routing/test_path_parameters.py +++ b/tests/routing/test_path_parameters.py @@ -7,21 +7,23 @@ from tests.controllers.controller_with_path_parameters import ControllerWithPathParameters from tests.controllers.controller_with_path_parameters import OneTwoEnum from tests.controllers.controller_with_path_parameters import OneTwoEnumWithInt +from winter.core import Component from winter.web.argument_resolver import ArgumentNotSupported from winter.web.controller import get_component -from winter.core import Component from winter.web.path_parameters_argument_resolver import PathParametersArgumentResolver uuid_ = uuid.uuid4() -@pytest.mark.parametrize('path, arg_name, expected_value', [ - (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param1', '123'), - (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param2', 456), - (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param3', OneTwoEnum.ONE), - (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param4', uuid_), - (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param5', OneTwoEnumWithInt.TWO), -]) +@pytest.mark.parametrize( + 'path, arg_name, expected_value', [ + (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param1', '123'), + (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param2', 456), + (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param3', OneTwoEnum.ONE), + (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param4', uuid_), + (f'/controller_with_path_parameters/123/456/one/{uuid_}/2/', 'param5', OneTwoEnumWithInt.TWO), + ], +) def test_resolve_path_parameter(path, arg_name, expected_value): component = Component.get_by_cls(ControllerWithPathParameters) argument = component.get_method('test').get_argument(arg_name) @@ -36,11 +38,13 @@ def test_resolve_path_parameter(path, arg_name, expected_value): assert result == expected_value -@pytest.mark.parametrize('controller_class, method_name, arg_name, expected_value', [ - (ControllerWithPathParameters, 'test', 'param1', True), - (ControllerWithPathParameters, 'test', 'param2', True), - (ControllerWithPathParameters, 'test', 'param6', False), -]) +@pytest.mark.parametrize( + 'controller_class, method_name, arg_name, expected_value', [ + (ControllerWithPathParameters, 'test', 'param1', True), + (ControllerWithPathParameters, 'test', 'param2', True), + (ControllerWithPathParameters, 'test', 'param6', False), + ], +) def test_is_supported_path_parameter(controller_class, method_name, arg_name, expected_value): component = get_component(controller_class) argument = component.get_method(method_name).get_argument(arg_name) diff --git a/tests/urls.py b/tests/urls.py index 9dc329a9..d179e540 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -8,7 +8,6 @@ *winter_django.create_django_urls(controllers.SimpleController), *winter_django.create_django_urls(controllers.ControllerWithExceptions), *winter_django.create_django_urls(controllers.ControllerWithProblemExceptions), - *winter_django.create_django_urls(controllers.ControllerWithMediaTypesRouting), *winter_django.create_django_urls(drf.ControllerWithOutputTemplate), *winter_django.create_django_urls(controllers.ControllerWithPathParameters), *winter_django.create_django_urls(controllers.ControllerWithQueryParameters), From 9b8138127d91b05d4a2444fd2e9f1d7a4d6dff4a Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 17:37:33 +0700 Subject: [PATCH 6/8] Fix coverage --- tests/controllers/controller_with_exceptions.py | 2 +- tests/controllers/controller_with_path_parameters.py | 6 +++--- tests/winter_sqlalchemy/test_sqla_crud.py | 7 +------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/controllers/controller_with_exceptions.py b/tests/controllers/controller_with_exceptions.py index 330bb261..c2e20e10 100644 --- a/tests/controllers/controller_with_exceptions.py +++ b/tests/controllers/controller_with_exceptions.py @@ -36,7 +36,7 @@ def handle(self, exception: CustomException) -> int: class WithUnknownArgumentExceptionHandler(winter.web.ExceptionHandler): @winter.response_status(400) - def handle(self, exception: WithUnknownArgumentException, unknown_argument: int) -> str: + def handle(self, exception: WithUnknownArgumentException, unknown_argument: int) -> str: # pragma: no cover pass diff --git a/tests/controllers/controller_with_path_parameters.py b/tests/controllers/controller_with_path_parameters.py index a920793c..075cbf77 100644 --- a/tests/controllers/controller_with_path_parameters.py +++ b/tests/controllers/controller_with_path_parameters.py @@ -17,8 +17,8 @@ class OneTwoEnumWithInt(enum.Enum): def _missing_(cls, value): # This is need because of needing of instancing from string try: value = int(value) - except ValueError: - super()._missing_(cls, value) # pragma: no cover + except ValueError: # pragma: no cover + super()._missing_(cls, value) else: return cls(value) @@ -36,5 +36,5 @@ def test( param4: uuid.UUID, param5: OneTwoEnumWithInt, param6: str, - ) -> str: + ) -> str: # pragma: no cover pass diff --git a/tests/winter_sqlalchemy/test_sqla_crud.py b/tests/winter_sqlalchemy/test_sqla_crud.py index 8e2bed79..ba18af95 100644 --- a/tests/winter_sqlalchemy/test_sqla_crud.py +++ b/tests/winter_sqlalchemy/test_sqla_crud.py @@ -99,11 +99,6 @@ def fixture(): return get_injector().get(Fixture) -@pytest.fixture() -def engine(): - return get_injector().get(Engine) - - class Fixture: @inject def __init__(self, engine: Engine): @@ -232,7 +227,7 @@ def test_save_new(fixture): entity = MyEntity(id_=1, name='name', lastname='lastname') # Act - entity = fixture.repository.save(entity) + fixture.repository.save(entity) entities = list(fixture.repository.find_all()) assert len(entities) == 1 From e3286bb8580cf66753057b2580af457dd5a84eb5 Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 17:42:45 +0700 Subject: [PATCH 7/8] Fix coverage --- winter/web/configurer.py | 7 ++++--- winter/web/interceptor.py | 7 ++++--- winter_sqlalchemy/repository.py | 3 --- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/winter/web/configurer.py b/winter/web/configurer.py index c3f6818c..f00d2e64 100644 --- a/winter/web/configurer.py +++ b/winter/web/configurer.py @@ -1,12 +1,13 @@ -import abc +from abc import ABC +from abc import abstractmethod from winter.core import get_injector from .interceptor import InterceptorRegistry from .interceptor import interceptor_registry -class Configurer(abc.ABC): - @abc.abstractmethod +class Configurer(ABC): + @abstractmethod def add_interceptors(self, registry: InterceptorRegistry): pass diff --git a/winter/web/interceptor.py b/winter/web/interceptor.py index bc77222d..aa448624 100644 --- a/winter/web/interceptor.py +++ b/winter/web/interceptor.py @@ -1,8 +1,9 @@ -import abc +from abc import ABC +from abc import abstractmethod -class Interceptor(abc.ABC): - @abc.abstractmethod +class Interceptor(ABC): + @abstractmethod def pre_handle(self, **kwargs): pass diff --git a/winter_sqlalchemy/repository.py b/winter_sqlalchemy/repository.py index 94e9469b..fb200e03 100644 --- a/winter_sqlalchemy/repository.py +++ b/winter_sqlalchemy/repository.py @@ -28,9 +28,6 @@ def sqla_crud(repository_cls): injector = get_injector() - if not injector: - raise RuntimeError('Injector is not configured') - if not issubclass(repository_cls, CRUDRepository): raise TypeError('Repository must be inherited from CRUDRepository before annotating with sqla_crud') From 933c31ea6a5d493fe10b56b488d15d6b268ee753 Mon Sep 17 00:00:00 2001 From: "Pavel V. Pristupa" Date: Mon, 5 Apr 2021 17:46:13 +0700 Subject: [PATCH 8/8] Fix coverage --- winter_sqlalchemy/repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winter_sqlalchemy/repository.py b/winter_sqlalchemy/repository.py index fb200e03..0dab67f9 100644 --- a/winter_sqlalchemy/repository.py +++ b/winter_sqlalchemy/repository.py @@ -83,7 +83,7 @@ def delete_all(self): def delete_by_id(self, id_: K): if not isinstance(id_, tuple): id_ = (id_,) - if id_ in self.__identity_map: + if id_ in self.__identity_map: # pragma: no cover entity = self.__identity_map[id_] self.delete(entity) else: