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

pincer.cog.reload_cog()   C

Complexity

Conditions 10

Size

Total Lines 42
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 42
rs 5.9999
c 0
b 0
f 0
cc 10
nop 2

How to fix   Complexity   

Complexity

Complex classes like pincer.cog.reload_cog() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 types import ModuleType
9
from typing import TYPE_CHECKING
10
11
from . import client as _client
12
from .commands.chat_command_handler import ChatCommandHandler
13
from .commands.interactable import Interactable
14
from .exceptions import CogAlreadyExists, CogNotFound
15
16
if TYPE_CHECKING:
17
    from typing import Type
18
    from .client import Client
19
20
21
def get_cog_name(cog: Type[Cog]) -> str:
22
    """Gets the path to import a cog. This is used as a unique identifier"""
23
    try:
24
        return f"{cog.__module__}.{cog.__name__}"
25
    except AttributeError:
26
        return f"{cog.__module__}.{cog.__class__.__name__}"
27
28
29
def load_cog(client: Client, cog: Type[Cog]):
30
    """Loads a cog
31
32
    Parameters
33
    ----------
34
    client : :class:`~pincer.client.Client`
35
        The client the cog should hold a reference to.
36
    cog : Type[:class:`~pincer.cog.Cog`]
37
        The cog to load.
38
    """
39
    if cog in ChatCommandHandler.managers:
40
        raise CogAlreadyExists(
41
            f"Cog `{cog}` is trying to be loaded but already exists."
42
        )
43
44
    cog_manager = cog(client)
45
46
    ChatCommandHandler.managers.append(cog_manager)
47
48
49
def load_module(client: Client, module: ModuleType):
50
    """Loads the cogs from a module recursively.
51
52
    Parameters
53
    ----------
54
    client : :class:`~pincer.client.Client`
55
        The client the cog should hold a reference to.
56
    module : :class:`~types.ModuleType`
57
        The module to load.
58
    """
59
    for item in module.__dict__.values():
60
        if isinstance(item, ModuleType):
61
            load_module(client, item)
62
        elif Cog in getattr(item, "__bases__", []):
63
            load_cog(client, item)
64
65
66
def reload_cog(client: Client, cog: Type[Cog]):
67
    """Reloads a cog.
68
69
    Parameters
70
    ----------
71
    client : :class:`~pincer.client.Client`
72
        The client the cog should hold a reference to.
73
    cog : Type[:class:`~pincer.cog.Cog`]
74
        The cog to load.
75
    """
76
77
    # Remove application commands registered to this cog
78
    for item in ChatCommandHandler.managers:
79
        if get_cog_name(item) == get_cog_name(cog):
80
            old_cog = item
81
            break
82
    else:
83
        raise CogNotFound(f"Cog `{cog}` could not be found!")
84
85
    to_pop = []
86
87
    for key, command in ChatCommandHandler.register.items():
88
        if not command:
89
            continue
90
        if command.manager == old_cog:
91
            to_pop.append(key)
92
93
    for pop in to_pop:
94
        ChatCommandHandler.register.pop(pop)
95
96
    ChatCommandHandler.managers.remove(old_cog)
97
98
    # Remove events registered to this cog
99
    for event in type(old_cog).__dict__.values():
100
        if isinstance(event, _client.PartialEvent):
101
            _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...
102
103
    mod = reload(import_module(cog.__module__))
104
    new_cog = getattr(mod, cog.__name__)
105
    client.load_cog(new_cog)
106
    ChatCommandHandler.has_been_initialized = False
107
    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...
108
109
110
class Cog(Interactable):
111
    """A cog object
112
    This is an object that can register commands and message components that isn't a
113
    client. It also can be loaded and unloaded in runtime so commands can be changed
114
    without restarting the bot.
115
    """
116
117
    def __init__(self, client: Client) -> None:
118
        self.client = client
119
120
        super().__init__()
121