# Path: rep0st/framework/data/database.py
# B = TypeVar('B')
# class DatabaseModule(Module):
#   def __init__(self, url: URL, base: B):
#   def configure(self, binder: Binder):
#   def provide_database_engine(self) -> DatabaseEngine:
#   def provide_database_session_factory(
#       self, database_engine: DatabaseEngine) -> DatabaseSessionFactory:
#   def provide_database_session(
#       self, database_session_factory: DatabaseSessionFactory) -> Session:
# 
# Path: rep0st/framework/decorator.py
# class DecoratorProcessor(ABC):
# 
#   @abstractmethod
#   def process(self, bindings: List[Any]) -> None:
#     pass
# 
#   def methods_by_decorated_name(
#       self, bindings: List[Any],
#       search_name: str) -> Iterator[Tuple[Type, T, Callable[[Any], Any]]]:
#     for binding in bindings:
#       interface = binding
#       for name, fun in inspect.getmembers(interface):
#         data = getattr(fun, '__' + search_name + '__', None)
#         if data is not None:
#           yield (interface, data, fun)
# 
#   @staticmethod
#   def wrap_function(instance: Any, fun: Callable,
#                     injector: Injector) -> Callable:
#     fun = types.MethodType(fun, instance)
# 
#     @functools.wraps(fun)
#     def wrapper(*args: Any, **kwargs: Any) -> Any:
#       return injector.call_with_injection(
#           callable=fun, args=args, kwargs=kwargs)
# 
#     return wrapper  # type: ignore


import functools
import logging
from threading import local
from typing import Any, Callable, List
from injector import Binder, Module, ProviderOf, inject, multiprovider, singleton
from sqlalchemy.orm import Session
from rep0st.framework.data.database import DatabaseSessionFactory
from rep0st.framework.decorator import DecoratorProcessor
@singleton
class TransactionalProcessor(DecoratorProcessor):

  @inject
  def __init__(
      self, database_session_provider: ProviderOf[Session],
      database_session_factory_provider: ProviderOf[DatabaseSessionFactory]
  ) -> None:
    super().__init__()
    self.database_session_provider = database_session_provider
    self.database_session_factory_provider = database_session_factory_provider

  def process(self, bindings: List[Any]) -> None:
    for interface, transactional_configuration, fun in self.methods_by_decorated_name(
        bindings, 'transactional'):
      full_fun_name = f'{interface.__module__}.{interface.__class__.__name__}.{fun.__name__}'
      log.debug(f'Discovered method {full_fun_name} for transactional')
      transactional_configuration.name = full_fun_name
      transactional_configuration.database_session_provider = self.database_session_provider
      transactional_configuration.database_session_factory_provider = self.database_session_factory_provider


class TransactionalModule(Module):

  def configure(self, binder: Binder) -> None:
    binder.bind(TransactionalProcessor)

  @multiprovider
  def provide_decorator_processor(
      self, transactional_processor: TransactionalProcessor

# Path: rep0st/framework/data/database.py
# B = TypeVar('B')
# class DatabaseModule(Module):
#   def __init__(self, url: URL, base: B):
#   def configure(self, binder: Binder):
#   def provide_database_engine(self) -> DatabaseEngine:
#   def provide_database_session_factory(
#       self, database_engine: DatabaseEngine) -> DatabaseSessionFactory:
#   def provide_database_session(
#       self, database_session_factory: DatabaseSessionFactory) -> Session:
# 
# Path: rep0st/framework/decorator.py
# class DecoratorProcessor(ABC):
# 
#   @abstractmethod
#   def process(self, bindings: List[Any]) -> None:
#     pass
# 
#   def methods_by_decorated_name(
#       self, bindings: List[Any],
#       search_name: str) -> Iterator[Tuple[Type, T, Callable[[Any], Any]]]:
#     for binding in bindings:
#       interface = binding
#       for name, fun in inspect.getmembers(interface):
#         data = getattr(fun, '__' + search_name + '__', None)
#         if data is not None:
#           yield (interface, data, fun)
# 
#   @staticmethod
#   def wrap_function(instance: Any, fun: Callable,
#                     injector: Injector) -> Callable:
#     fun = types.MethodType(fun, instance)
# 
#     @functools.wraps(fun)
#     def wrapper(*args: Any, **kwargs: Any) -> Any:
#       return injector.call_with_injection(
#           callable=fun, args=args, kwargs=kwargs)
# 
#     return wrapper  # type: ignore


# Path: rep0st/framework/data/transaction.py
import functools
import logging
from threading import local
from typing import Any, Callable, List
from injector import Binder, Module, ProviderOf, inject, multiprovider, singleton
from sqlalchemy.orm import Session
from rep0st.framework.data.database import DatabaseSessionFactory
from rep0st.framework.decorator import DecoratorProcessor

@singleton
class TransactionalProcessor(DecoratorProcessor):

  @inject
  def __init__(
      self, database_session_provider: ProviderOf[Session],
      database_session_factory_provider: ProviderOf[DatabaseSessionFactory]
  ) -> None:
    super().__init__()
    self.database_session_provider = database_session_provider
    self.database_session_factory_provider = database_session_factory_provider

  def process(self, bindings: List[Any]) -> None:
    for interface, transactional_configuration, fun in self.methods_by_decorated_name(
        bindings, 'transactional'):
      full_fun_name = f'{interface.__module__}.{interface.__class__.__name__}.{fun.__name__}'
      log.debug(f'Discovered method {full_fun_name} for transactional')
      transactional_configuration.name = full_fun_name
      transactional_configuration.database_session_provider = self.database_session_provider
      transactional_configuration.database_session_factory_provider = self.database_session_factory_provider


class TransactionalModule(Module):

  def configure(self, binder: Binder) -> None:
    binder.bind(TransactionalProcessor)

  @multiprovider
  def provide_decorator_processor(
      self, transactional_processor: TransactionalProcessor

  ) -> List[DecoratorProcessor]: