[commands] Walk through MRO for Cog derived classes.

This should support cog subclasses in a relatively consistent way in
terms of expectations. Hopefully nothing is broken.

Fixes #1950
This commit is contained in:
Rapptz 2019-03-17 22:03:44 -04:00
parent cb9ae7bd76
commit f43690bde8

View File

@ -90,30 +90,44 @@ class CogMeta(type):
attrs['__cog_name__'] = kwargs.pop('name', name) attrs['__cog_name__'] = kwargs.pop('name', name)
attrs['__cog_settings__'] = command_attrs = kwargs.pop('command_attrs', {}) attrs['__cog_settings__'] = command_attrs = kwargs.pop('command_attrs', {})
commands = [] commands = {}
listeners = [] listeners = {}
for elem, value in attrs.items(): new_cls = super().__new__(cls, name, bases, attrs, **kwargs)
is_static_method = isinstance(value, staticmethod) for base in reversed(new_cls.__mro__):
if is_static_method: for elem, value in base.__dict__.items():
value = value.__func__ if elem in commands:
if isinstance(value, _BaseCommand): del commands[elem]
if elem in listeners:
del listeners[elem]
is_static_method = isinstance(value, staticmethod)
if is_static_method: if is_static_method:
raise TypeError('Command in method {0!r} must not be staticmethod.'.format(elem)) value = value.__func__
if isinstance(value, _BaseCommand):
if is_static_method:
raise TypeError('Command in method {0}.{1!r} must not be staticmethod.'.format(base, elem))
commands.append(value) commands[elem] = value
elif inspect.iscoroutinefunction(value): elif inspect.iscoroutinefunction(value):
try: try:
is_listener = getattr(value, '__cog_listener__') is_listener = getattr(value, '__cog_listener__')
except AttributeError: except AttributeError:
continue continue
else: else:
for listener_name in value.__cog_listener_names__: listeners[elem] = value
listeners.append((listener_name, value.__name__))
attrs['__cog_commands__'] = commands # this will be copied in Cog.__new__ new_cls.__cog_commands__ = list(commands.values()) # this will be copied in Cog.__new__
attrs['__cog_listeners__'] = tuple(listeners)
return super().__new__(cls, name, bases, attrs, **kwargs) listeners_as_list = []
for listener in listeners.values():
for listener_name in listener.__cog_listener_names__:
# I use __name__ instead of just storing the value so I can inject
# the self attribute when the time comes to add them to the bot
listeners_as_list.append((listener_name, listener.__name__))
new_cls.__cog_listeners__ = listeners_as_list
return new_cls
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args) super().__init__(*args)