# 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 logging
import signal
import types
from collections import namedtuple
from typing import Any, Callable, Dict, Iterator, List, Tuple, Type
from injector import Binder, Injector, Module, inject, multiprovider, singleton
from rep0st.framework.decorator import DecoratorProcessor


@singleton
class SignalHandler:
  handlers: Dict[int, List[Callable[[], None]]] = {}

  def register_handler(self, signum: int, fun: Callable[[], None]):
    if not signum in self.handlers:
      self.handlers[signum] = []
      signal.signal(signum, self._signal_handler)
    self.handlers[signum].append(fun)

  def _signal_handler(self, signum, frame):
    log.info("Signal %s(%d) detected. Calling handlers.",
             signal.Signals(signum).name, signum)
    if signum in self.handlers:
      for fun in self.handlers[signum]:
        fun()


@singleton
class OnShutdownProcessor(DecoratorProcessor):
  injector: Injector
  methods: List[Callable[[], None]] = []

  @inject
  def __init__(self, injector: Injector, signal_handler: SignalHandler) -> None:
    super().__init__()
    signal_handler.register_handler(signal.SIGTERM, self.handle_shutdown)
    signal_handler.register_handler(signal.SIGINT, self.handle_shutdown)

# 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/signal_handler.py
import logging
import signal
import types
from collections import namedtuple
from typing import Any, Callable, Dict, Iterator, List, Tuple, Type
from injector import Binder, Injector, Module, inject, multiprovider, singleton
from rep0st.framework.decorator import DecoratorProcessor



@singleton
class SignalHandler:
  handlers: Dict[int, List[Callable[[], None]]] = {}

  def register_handler(self, signum: int, fun: Callable[[], None]):
    if not signum in self.handlers:
      self.handlers[signum] = []
      signal.signal(signum, self._signal_handler)
    self.handlers[signum].append(fun)

  def _signal_handler(self, signum, frame):
    log.info("Signal %s(%d) detected. Calling handlers.",
             signal.Signals(signum).name, signum)
    if signum in self.handlers:
      for fun in self.handlers[signum]:
        fun()


@singleton
class OnShutdownProcessor(DecoratorProcessor):
  injector: Injector
  methods: List[Callable[[], None]] = []

  @inject
  def __init__(self, injector: Injector, signal_handler: SignalHandler) -> None:
    super().__init__()
    signal_handler.register_handler(signal.SIGTERM, self.handle_shutdown)
    signal_handler.register_handler(signal.SIGINT, self.handle_shutdown)

    signal_handler.register_handler(signal.SIGQUIT, self.handle_shutdown)