Skip to content

Commit 77a6dc6

Browse files
committed
Fix a bug in Cog, where events were persisting across multiple cogs.
1 parent 75512f9 commit 77a6dc6

File tree

1 file changed

+31
-30
lines changed

1 file changed

+31
-30
lines changed

twitchio/ext/commands/meta.py

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,41 @@
2626
from functools import partial
2727

2828
from .core import *
29-
from .errors import *
3029

3130

3231
__all__ = ("Cog",)
3332

3433

35-
class Cog:
34+
class CogEvent:
35+
def __init__(self, *, name: str, func):
36+
self.name = name
37+
self.func = func
38+
39+
40+
class CogMeta(type):
41+
def __new__(mcs, *args, **kwargs):
42+
name, bases, attrs = args
43+
attrs["__cogname__"] = kwargs.pop("name", name)
44+
45+
self = super().__new__(mcs, name, bases, attrs, **kwargs)
46+
self._events = {}
47+
self._commands = {}
48+
49+
for name, mem in inspect.getmembers(self):
50+
if isinstance(mem, (CogEvent, Command)):
51+
if name.startswith(("cog_", "bot_")): # Invalid method prefixes
52+
raise RuntimeError(f'The event or command "{name}" starts with an invalid prefix (cog_ or bot_).')
53+
54+
if isinstance(mem, CogEvent):
55+
try:
56+
self._events[mem.name].append(mem.func)
57+
except KeyError:
58+
self._events[mem.name] = [mem.func]
59+
60+
return self
61+
62+
63+
class Cog(metaclass=CogMeta):
3664
"""Class used for creating a TwitchIO Cog.
3765
3866
Cogs help organise code and provide powerful features for creating bots.
@@ -74,18 +102,6 @@ def prepare(bot: commands.Bot):
74102
bot.add_cog(MyCog(bot))
75103
"""
76104

77-
__valid_slots__ = ("cog_unload", "cog_check", "cog_error", "cog_command_error")
78-
79-
def __init_subclass__(cls, *args, **kwargs):
80-
cls.__cogname__ = kwargs.get("name", cls.__name__)
81-
82-
for name, mem in inspect.getmembers(cls):
83-
if name.startswith(("cog", "bot")) and name not in cls.__valid_slots__: # Invalid method prefixes
84-
raise InvalidCogMethod(f'The method "{name}" starts with an invalid prefix (cog or bot).')
85-
86-
cls._events = getattr(cls, "_events", {})
87-
cls._commands = {}
88-
89105
def _load_methods(self, bot):
90106
for name, method in inspect.getmembers(self):
91107
if isinstance(method, Command):
@@ -102,12 +118,6 @@ def _load_methods(self, bot):
102118
for callback in callbacks:
103119

104120
callback = partial(callback, self)
105-
106-
if event in self._events:
107-
self._events[event].append(callback)
108-
else:
109-
self._events[event] = [callback]
110-
111121
bot.add_event(callback=callback, name=event)
112122

113123
def _unload_methods(self, bot):
@@ -147,20 +157,11 @@ async def event_message(self, message: twitchio.Message):
147157
async def bot_is_ready(self):
148158
print('Bot is ready!')
149159
"""
150-
if not hasattr(cls, "_events"):
151-
cls._events = {}
152160

153161
def decorator(func):
154162
event_name = event or func.__name__
155-
if event_name in cls.__valid_slots__:
156-
raise ValueError(f"{event_name} cannot be a decorated event.")
157-
158-
if event_name in cls._events:
159-
cls._events[event_name].append(func)
160-
else:
161-
cls._events[event_name] = [func]
162163

163-
return func
164+
return CogEvent(name=event_name, func=func)
164165

165166
return decorator
166167

0 commit comments

Comments
 (0)