<commit_before># This file is part of curious.
#
# curious is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# curious is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with curious.  If not, see <http://www.gnu.org/licenses/>.

"""
Special helpers for events.

.. currentmodule: curious.core.events
"""
import functools
import inspect
import logging
import typing

import multio
from async_generator import asynccontextmanager
from multidict import MultiDict

from curious.core import client as md_client
from curious.util import remove_from_multidict, safe_generator

logger = logging.getLogger("curious.events")


class ListenerExit(Exception):
    """
    Raised when a temporary listener is to be exited.

    .. code-block:: python3

        def listener(ctx, message):
            if message.author.id == message.guild.owner_id:
                raise ListenerExit

    """


class _WithWaitFor:
    """
    Helper class for managing a wait_for.
    """

    def __init__(self, manager, task_group, event_name: str, predicate):
        self.manager = manager
        self.task_group = task_group
        self._task = None

        self._evt = event_name
        self._pred = predicate

    async def __aenter__(self):
        # spawn the wait_for handler
        partial = functools.partial(self.manager.wait_for, self._evt, self._pred)
        self._task = await self.task_group.spawn(partial)

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            await self._task.cancel()

        try:
            await self._task.join()
        except multio.asynclib.Cancelled:
            pass

        return False


@asynccontextmanager
@safe_generator
async def _wait_for_manager(manager, name: str, predicate):
    """
    Helper class for managing a wait_for.
    """
    async with multio.asynclib.task_manager() as tg:
        try:
            partial = functools.partial(manager.wait_for, name, predicate)
            await multio.asynclib.spawn(partial)
            yield
        finally:
            await multio.asynclib.cancel_task_group(tg)


class EventManager(object):
    """
    A manager for events.

    This deals with firing of events and temporary listeners.
    """

    def __init__(self):
        #: The task manager used to spawn events.
        self.task_manager = None

        #: A list of event hooks.
        self.event_hooks = set()

        #: A MultiDict of event listeners.
        self.event_listeners = MultiDict()

        #: A MultiDict of temporary listeners.
        self.temporary_listeners = MultiDict()

    # add or removal functions
    # Events<commit_msg>Fix `wait_for_manager`.<commit_after># This file is part of curious.
#
# curious is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# curious is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with curious.  If not, see <http://www.gnu.org/licenses/>.

"""
Special helpers for events.

.. currentmodule: curious.core.events
"""
import functools
import inspect
import logging
import typing

import multio
from async_generator import asynccontextmanager
from multidict import MultiDict

from curious.core import client as md_client
from curious.util import remove_from_multidict, safe_generator

logger = logging.getLogger("curious.events")


class ListenerExit(Exception):
    """
    Raised when a temporary listener is to be exited.

    .. code-block:: python3

        def listener(ctx, message):
            if message.author.id == message.guild.owner_id:
                raise ListenerExit

    """


class _WithWaitFor:
    """
    Helper class for managing a wait_for.
    """

    def __init__(self, manager, task_group, event_name: str, predicate):
        self.manager = manager
        self.task_group = task_group
        self._task = None

        self._evt = event_name
        self._pred = predicate

    async def __aenter__(self):
        # spawn the wait_for handler
        partial = functools.partial(self.manager.wait_for, self._evt, self._pred)
        self._task = await self.task_group.spawn(partial)

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            await self._task.cancel()

        try:
            await self._task.join()
        except multio.asynclib.Cancelled:
            pass

        return False


@asynccontextmanager
@safe_generator
async def _wait_for_manager(manager, name: str, predicate):
    """
    Helper class for managing a wait_for.
    """
    async with multio.asynclib.task_manager() as tg:
        try:
            partial = functools.partial(manager.wait_for, name, predicate)
            await multio.asynclib.spawn(tg, partial)
            yield
        finally:
            await multio.asynclib.cancel_task_group(tg)


class EventManager(object):
    """
    A manager for events.

    This deals with firing of events and temporary listeners.
    """

    def __init__(self):
        #: The task manager used to spawn events.
        self.task_manager = None

        #: A list of event hooks.
        self.event_hooks = set()

        #: A MultiDict of event listeners.
        self.event_listeners = MultiDict()

        #: A MultiDict of temporary listeners.
        self.temporary_listeners = MultiDict()

    # add or removal functions
    # Events
{'fraction_non_alphanumeric': 0.07568438003220612, 'fraction_numerical': 0.0006441223832528181, 'mean_word_length': 2.7460796139927623, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4784242', 'n_tokens_mistral': 1740, 'n_tokens_neox': 1697, 'n_words': 607}