Passed
Pull Request — main (#389)
by
unknown
02:06
created

pincer.cog.CogManager.load_module()   B

Complexity

Conditions 6

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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