Issues (1445)

pincer/cog.py (1 issue)

1
# Copyright Pincer 2021-Present
0 ignored issues
show
Missing module docstring
Loading history...
2
# Full MIT License can be found in `LICENSE` at the project root.
3
4
from __future__ import annotations
5
from asyncio import ensure_future
6
7
from importlib import reload, import_module
8
from inspect import isclass
9
from types import ModuleType
10
from typing import TYPE_CHECKING, List
11
12
from .commands.chat_command_handler import ChatCommandHandler
13
from .commands.interactable import Interactable
14
from .exceptions import CogAlreadyExists
15
16
if TYPE_CHECKING:
17
    from typing import Type
18
    from .client import Client
19
20
21
class CogManager:
22
    """
23
    A class that can load and unload cogs
24
    """
25
26
    def load_cog(self, cog: Type[Cog]):
27
        """Load a cog from a string path, setup method in COG may
28
        optionally have a first argument which will contain the client!
29
30
        :Example usage:
31
32
        run.py
33
34
        .. code-block:: python3
35
36
             from pincer import Client
37
             from cogs.say import SayCommand
38
39
             class MyClient(Client):
40
                 def __init__(self, *args, **kwargs):
41
                     self.load_cog(SayCommand)
42
                     super().__init__(*args, **kwargs)
43
44
        cogs/say.py
45
46
        .. code-block:: python3
47
48
             from pincer import command
49
50
             class SayCommand(Cog):
51
                 @command()
52
                 async def say(self, message: str) -> str:
53
                     return message
54
55
        Parameters
56
        ----------
57
        cog : Type[:class:`~pincer.cog.Cog`]
58
            The cog to load.
59
        """
60
        if cog in ChatCommandHandler.managers:
61
            raise CogAlreadyExists(
62
                f"Cog `{cog}` is trying to be loaded but already exists."
63
            )
64
65
        cog_manager = cog(self)
66
67
        ChatCommandHandler.managers.append(cog_manager)
68
69
    def load_cogs(self, *cogs: Type[Cog]):
70
        """
71
        Loads a list of cogs
72
73
        Parameters
74
        ----------
75
        \\*cogs : Type[:class:`~pincer.cog.Cog`]
76
            A list of cogs to load.
77
        """
78
        for cog in cogs:
79
            self.load_cog(cog)
80
81
    def load_module(self, module: ModuleType):
82
        """Loads the cogs from a module recursively.
83
84
        Parameters
85
        ----------
86
        module : :class:`~types.ModuleType`
87
            The module to load.
88
        """
89
        for item in vars(module).values():
90
            if isinstance(item, ModuleType):
91
                self.load_module(item)
92
            elif item is not Cog and isclass(item) and issubclass(item, Cog):
93
                self.load_cog(item)
94
95
    def reload_cogs(self):
96
        """Reloads all of the loaded cogs"""
97
98
        modules = []
99
100
        for cog in self.cogs:
101
            cog.unassign()
102
103
            mod = import_module(type(cog).__module__)
104
            if mod not in modules:
105
                modules.append(mod)
106
107
        for mod in modules:
108
            reload(mod)
109
110
        for cog in self.cogs:
111
            for mod in modules:
112
                cog = getattr(mod, type(cog).__name__, None)
113
                if cog:
114
                    self.load_cog(cog)
115
116
        ChatCommandHandler.has_been_initialized = False
117
        ensure_future(ChatCommandHandler(self).initialize())
118
119
    @property
120
    def cogs(self) -> List[Cog]:
121
        """Get a dictionary of all loaded cogs.
122
123
        The key/value pair is import path/cog class.
124
125
        Returns
126
        -------
127
        List[:class:`~pincer.cog.Cog`]
128
            The list of cogs
129
        """
130
        return [
131
            manager
132
            for manager in ChatCommandHandler.managers
133
            if isinstance(manager, Cog)
134
        ]
135
136
137
class Cog(Interactable):
138
    """A cog object
139
    This is an object that can register commands and message components that isn't a
140
    client. It also can be loaded and unloaded at runtime so commands can be changed
141
    without restarting the bot.
142
    """
143
144
    def __init__(self, client: Client) -> None:
145
        self.client = client
146
147
        super().__init__()
148
149
    @classmethod
150
    def name(cls) -> str:
151
        """
152
        Returns a unique name for this cog.
153
154
        Returns
155
        -------
156
        str
157
            A unique name for this cog.
158
        """
159
        return f"{cls.__module__}.{cls.__name__}"
160