# Path: stoq/data_classes.py
# class Error:
#     def __init__(
#         self,
#         error: str,
#         plugin_name: Optional[str] = None,
#         payload_id: Optional[str] = None,
#     ) -> None:
#         """
# 
#         Object for errors collected from plugins
# 
#         :param error: Error message to add to results
#         :param plugin_name: The name of the plugin producing the error
#         :param payload_id: The ``payload_id`` of the ``Payload`` that the error occurred on
# 
#         >>> from stoq import Error, Payload
#         >>> errors: List[Error] = []
#         >>> payload = Payload(b'test bytes')
#         >>> err = Error(
#         ...     error='This is our error message',
#         ...     plugin_name='test_plugin', 
#         ...     payload_id=payload.results.payload_id
#         ... )
#         >>> errors.append(err)
# 
#         """
#         self.error = error
#         self.plugin_name = plugin_name
#         self.payload_id = payload_id
# 
#     def __str__(self) -> str:
#         return helpers.dumps(self)
# 
#     def __repr__(self):
#         return repr(self.__dict__)
# 
# Path: stoq/exceptions.py
# class StoqException(Exception):
#     pass
# 
# class StoqPluginNotFound(Exception):
#     pass
# 
# Path: stoq/plugins/base.py
# class BasePlugin(ABC):
#     def __init__(self, config: StoqConfigParser) -> None:
#         self.config = config
#         self.plugin_name = config.get('Core', 'Name', fallback=self.__class__.__name__)
#         self.__author__ = config.get('Documentation', 'Author', fallback='')
#         self.__version__ = config.get('Documentation', 'Version', fallback='')
#         self.__website__ = config.get('Documentation', 'Website', fallback='')
#         self.__description__ = config.get('Documentation', 'Description', fallback='')
#         self.log = logging.getLogger(f'stoq.{self.plugin_name}')
# 
# Path: stoq/plugins/archiver.py
# class ArchiverPlugin(BasePlugin):
#     async def archive(
#         self, payload: Payload, request: Request
#     ) -> Optional[ArchiverResponse]:
#         """
#         Archive payload
# 
#         :param payload: Payload object to archive
#         :param request: Originating Request object
# 
#         :return: ArchiverResponse object. Results are used to retrieve payload.
# 
#         >>> import asyncio
#         >>> from stoq import Stoq, Payload
#         >>> payload = Payload(b'this is going to be saved')
#         >>> s = Stoq()
#         >>> loop = asyncio.get_event_loop()
#         >>> archiver = s.load_plugin('filedir')
#         >>> loop.run_until_complete(archiver.archive(payload))
#         ... {'path': '/tmp/bad.exe'}
# 
#         """
#         pass
# 
#     async def get(self, task: ArchiverResponse) -> Optional[Payload]:
#         """
#         Retrieve payload for processing
# 
#         :param task: Task to be processed to load payload. Must contain `ArchiverResponse`
#                      results from `ArchiverPlugin.archive()`
# 
#         :return: Payload object for scanning
# 
#         >>> import asyncio
#         >>> from stoq import Stoq, ArchiverResponse
#         >>> s = Stoq()
#         >>> loop = asyncio.get_event_loop()
#         >>> archiver = s.load_plugin('filedir')
#         >>> task = ArchiverResponse(results={'path': '/tmp/bad.exe'})
#         >>> payload = loop.run_until_complete(archiver.get(task))
# 
#         """
#         pass
# 
# Path: stoq/plugins/connector.py
# class ConnectorPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def save(self, response: StoqResponse) -> None:
#         pass
# 
# Path: stoq/plugins/provider.py
# class ProviderPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def ingest(self, queue: Queue) -> None:
#         pass
# 
# Path: stoq/plugins/worker.py
# class WorkerPlugin(BasePlugin, ABC):
#     def __init__(self, config: StoqConfigParser) -> None:
#         super().__init__(config)
#         self.required_workers = config.getset(
#             'options', 'required_workers', fallback=set()
#         )
# 
#     @abstractmethod
#     async def scan(
#         self, payload: Payload, request: Request
#     ) -> Optional[WorkerResponse]:
#         pass
# 
# Path: stoq/plugins/dispatcher.py
# class DispatcherPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def get_dispatches(
#         self, payload: Payload, request: Request
#     ) -> Optional[DispatcherResponse]:
#         pass
# 
# Path: stoq/plugins/decorator.py
# class DecoratorPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def decorate(self, response: StoqResponse) -> Optional[DecoratorResponse]:
#         pass


import os
import inspect
import logging
import configparser
import importlib.util
import stoq.helpers as helpers
        import ast
from pkg_resources import parse_version
from typing import Dict, List, Optional, Tuple, Any, Union
from stoq.data_classes import Error
from .exceptions import StoqException, StoqPluginNotFound
from stoq.plugins import (
    ArchiverPlugin,
    BasePlugin,
    ProviderPlugin,
    WorkerPlugin,
    ConnectorPlugin,
    DispatcherPlugin,
    DecoratorPlugin,
)
            from stoq import __version__
            # Placing this import at the top of this file causes a circular
            # import chain that causes stoq to crash on initialization

            if parse_version(__version__) < parse_version(min_stoq_version):
                self.log.warning(
                    f'Plugin {plugin_name} not compatible with this version of '
                    'stoQ. Unpredictable results may occur!'
                )
        spec = importlib.util.spec_from_file_location(
            plugin_config.get('Core', 'Module'), module_path
        )
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)  # type: ignore
        plugin_classes = inspect.getmembers(
            module,
            predicate=lambda mem: inspect.isclass(mem)
            and issubclass(mem, BasePlugin)
            and mem
            not in [
                ArchiverPlugin,
                BasePlugin,
                ProviderPlugin,
                WorkerPlugin,
                ConnectorPlugin,
                DispatcherPlugin,
                DecoratorPlugin,
            ]
            and not inspect.isabstract(mem),
        )
        if len(plugin_classes) == 0:

# Path: stoq/data_classes.py
# class Error:
#     def __init__(
#         self,
#         error: str,
#         plugin_name: Optional[str] = None,
#         payload_id: Optional[str] = None,
#     ) -> None:
#         """
# 
#         Object for errors collected from plugins
# 
#         :param error: Error message to add to results
#         :param plugin_name: The name of the plugin producing the error
#         :param payload_id: The ``payload_id`` of the ``Payload`` that the error occurred on
# 
#         >>> from stoq import Error, Payload
#         >>> errors: List[Error] = []
#         >>> payload = Payload(b'test bytes')
#         >>> err = Error(
#         ...     error='This is our error message',
#         ...     plugin_name='test_plugin', 
#         ...     payload_id=payload.results.payload_id
#         ... )
#         >>> errors.append(err)
# 
#         """
#         self.error = error
#         self.plugin_name = plugin_name
#         self.payload_id = payload_id
# 
#     def __str__(self) -> str:
#         return helpers.dumps(self)
# 
#     def __repr__(self):
#         return repr(self.__dict__)
# 
# Path: stoq/exceptions.py
# class StoqException(Exception):
#     pass
# 
# class StoqPluginNotFound(Exception):
#     pass
# 
# Path: stoq/plugins/base.py
# class BasePlugin(ABC):
#     def __init__(self, config: StoqConfigParser) -> None:
#         self.config = config
#         self.plugin_name = config.get('Core', 'Name', fallback=self.__class__.__name__)
#         self.__author__ = config.get('Documentation', 'Author', fallback='')
#         self.__version__ = config.get('Documentation', 'Version', fallback='')
#         self.__website__ = config.get('Documentation', 'Website', fallback='')
#         self.__description__ = config.get('Documentation', 'Description', fallback='')
#         self.log = logging.getLogger(f'stoq.{self.plugin_name}')
# 
# Path: stoq/plugins/archiver.py
# class ArchiverPlugin(BasePlugin):
#     async def archive(
#         self, payload: Payload, request: Request
#     ) -> Optional[ArchiverResponse]:
#         """
#         Archive payload
# 
#         :param payload: Payload object to archive
#         :param request: Originating Request object
# 
#         :return: ArchiverResponse object. Results are used to retrieve payload.
# 
#         >>> import asyncio
#         >>> from stoq import Stoq, Payload
#         >>> payload = Payload(b'this is going to be saved')
#         >>> s = Stoq()
#         >>> loop = asyncio.get_event_loop()
#         >>> archiver = s.load_plugin('filedir')
#         >>> loop.run_until_complete(archiver.archive(payload))
#         ... {'path': '/tmp/bad.exe'}
# 
#         """
#         pass
# 
#     async def get(self, task: ArchiverResponse) -> Optional[Payload]:
#         """
#         Retrieve payload for processing
# 
#         :param task: Task to be processed to load payload. Must contain `ArchiverResponse`
#                      results from `ArchiverPlugin.archive()`
# 
#         :return: Payload object for scanning
# 
#         >>> import asyncio
#         >>> from stoq import Stoq, ArchiverResponse
#         >>> s = Stoq()
#         >>> loop = asyncio.get_event_loop()
#         >>> archiver = s.load_plugin('filedir')
#         >>> task = ArchiverResponse(results={'path': '/tmp/bad.exe'})
#         >>> payload = loop.run_until_complete(archiver.get(task))
# 
#         """
#         pass
# 
# Path: stoq/plugins/connector.py
# class ConnectorPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def save(self, response: StoqResponse) -> None:
#         pass
# 
# Path: stoq/plugins/provider.py
# class ProviderPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def ingest(self, queue: Queue) -> None:
#         pass
# 
# Path: stoq/plugins/worker.py
# class WorkerPlugin(BasePlugin, ABC):
#     def __init__(self, config: StoqConfigParser) -> None:
#         super().__init__(config)
#         self.required_workers = config.getset(
#             'options', 'required_workers', fallback=set()
#         )
# 
#     @abstractmethod
#     async def scan(
#         self, payload: Payload, request: Request
#     ) -> Optional[WorkerResponse]:
#         pass
# 
# Path: stoq/plugins/dispatcher.py
# class DispatcherPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def get_dispatches(
#         self, payload: Payload, request: Request
#     ) -> Optional[DispatcherResponse]:
#         pass
# 
# Path: stoq/plugins/decorator.py
# class DecoratorPlugin(BasePlugin, ABC):
#     @abstractmethod
#     async def decorate(self, response: StoqResponse) -> Optional[DecoratorResponse]:
#         pass


# Path: stoq/plugin_manager.py
import os
import inspect
import logging
import configparser
import importlib.util
import stoq.helpers as helpers
        import ast
from pkg_resources import parse_version
from typing import Dict, List, Optional, Tuple, Any, Union
from stoq.data_classes import Error
from .exceptions import StoqException, StoqPluginNotFound
from stoq.plugins import (
    ArchiverPlugin,
    BasePlugin,
    ProviderPlugin,
    WorkerPlugin,
    ConnectorPlugin,
    DispatcherPlugin,
    DecoratorPlugin,
)
            from stoq import __version__

            # Placing this import at the top of this file causes a circular
            # import chain that causes stoq to crash on initialization

            if parse_version(__version__) < parse_version(min_stoq_version):
                self.log.warning(
                    f'Plugin {plugin_name} not compatible with this version of '
                    'stoQ. Unpredictable results may occur!'
                )
        spec = importlib.util.spec_from_file_location(
            plugin_config.get('Core', 'Module'), module_path
        )
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)  # type: ignore
        plugin_classes = inspect.getmembers(
            module,
            predicate=lambda mem: inspect.isclass(mem)
            and issubclass(mem, BasePlugin)
            and mem
            not in [
                ArchiverPlugin,
                BasePlugin,
                ProviderPlugin,
                WorkerPlugin,
                ConnectorPlugin,
                DispatcherPlugin,
                DecoratorPlugin,
            ]
            and not inspect.isabstract(mem),
        )
        if len(plugin_classes) == 0:

            raise StoqException(