Passed
Pull Request — main (#389)
by
unknown
01:50
created

pincer.cog.CogManager.reload_cog()   B

Complexity

Conditions 6

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 34
rs 8.5666
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
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 . import client as _client
13
from .commands.chat_command_handler import ChatCommandHandler
14
from .commands.interactable import Interactable
15
from .exceptions import CogAlreadyExists, CogNotFound
16
17
if TYPE_CHECKING:
18
    from typing import Type
19
    from .client import Client
20
21
22
class CogManager:
23
    """
24
    A class that can load and unload cogs
25
    """
26
27
    def load_cog(self, cog: Type[Cog]):
28
        """Load a cog from a string path, setup method in COG may
29
        optionally have a first argument which will contain the client!
30
31
        :Example usage:
32
33
        run.py
34
35
        .. code-block:: python3
36
37
             from pincer import Client
38
             from cogs.say import SayCommand
39
40
             class MyClient(Client):
41
                 def __init__(self, *args, **kwargs):
42
                     self.load_cog(SayCommand)
43
                     super().__init__(*args, **kwargs)
44
45
        cogs/say.py
46
47
        .. code-block:: python3
48
49
             from pincer import command
50
51
             class SayCommand(Cog):
52
                 @command()
53
                 async def say(self, message: str) -> str:
54
                     return message
55
56
        Parameters
57
        ----------
58
        cog : Type[:class:`~pincer.cog.Cog`]
59
            The cog to load.
60
        """
61
        if cog in ChatCommandHandler.managers:
62
            raise CogAlreadyExists(
63
                f"Cog `{cog}` is trying to be loaded but already exists."
64
            )
65
66
        cog_manager = cog(self)
67
68
        ChatCommandHandler.managers.append(cog_manager)
69
70
    def load_cogs(self, *cogs: Type[Cog]):
71
        """
72
        Loads a list of cogs
73
74
        Parameters
75
        ----------
76
        \\*cogs : Type[:class:`~pincer.cog.Cog`]
77
            A list of cogs to load.
78
        """
79
        for cog in cogs:
80
            self.load_cog(cog)
81
82
    def load_module(self, module: ModuleType):
83
        """Loads the cogs from a module recursively.
84
85
        Parameters
86
        ----------
87
        module : :class:`~types.ModuleType`
88
            The module to load.
89
        """
90
        for item in module.__dict__.values():
91
            if isinstance(item, ModuleType):
92
                self.load_module(item)
93
            elif isclass(item) and issubclass(item, Cog):
94
                self.load_cog(item)
95
96
    def reload_cog(self, cog: Type[Cog]):
97
        """Reloads a cog.
98
99
        Parameters
100
        ----------
101
        cog : Type[:class:`~pincer.cog.Cog`]
102
            The cog to load.
103
        """
104
105
        # Remove application commands registered to this cog
106
        for item in self.cogs:
107
            if item.name() == cog.name():
108
                old_cog = item
109
                break
110
        else:
111
            raise CogNotFound(f"Cog `{cog}` could not be found!")
112
113
        ChatCommandHandler.register = {
114
            key: command for key, command in ChatCommandHandler.register.items()
115
            if command and command.manager != old_cog
116
        }
117
118
        ChatCommandHandler.managers.remove(old_cog)
119
120
        # Remove events registered to this cog
121
        for event in vars(type(old_cog)).values():
122
            if isinstance(event, _client.PartialEvent):
123
                _client._events.pop(event.func.__name__)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _events was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
124
125
        mod = reload(import_module(cog.__module__))
126
        new_cog = getattr(mod, cog.__name__)
127
        self.load_cog(new_cog)
128
        ChatCommandHandler.has_been_initialized = False
129
        ensure_future(ChatCommandHandler().initialize())
0 ignored issues
show
Bug introduced by
It seems like a value for argument client is missing in the constructor call.
Loading history...
130
131
    @property
132
    def cogs(self) -> List[Cog]:
133
        """Get a dictionary of all loaded cogs.
134
135
        The key/value pair is import path/cog class.
136
137
        Returns
138
        -------
139
        List[:class:`~pincer.cog.Cog`]
140
            The list of cogs
141
        """
142
        return [
143
            manager for manager in ChatCommandHandler.managers
144
            if isinstance(manager, Cog)
145
        ]
146
147
148
class Cog(Interactable):
149
    """A cog object
150
    This is an object that can register commands and message components that isn't a
151
    client. It also can be loaded and unloaded at runtime so commands can be changed
152
    without restarting the bot.
153
    """
154
155
    def __init__(self, client: Client) -> None:
156
        self.client = client
157
158
        super().__init__()
159
160
    @classmethod
161
    def name(cls) -> str:
162
        """
163
        Returns a unique name for this cog.
164
165
        Returns
166
        -------
167
        str
168
            A unique name for this cog.
169
        """
170
        return f"{cls.__module__}.{cls.__name__}"
171