Skip to content
This repository was archived by the owner on Aug 28, 2019. It is now read-only.

Commit d5211fb

Browse files
committed
[tasks] Create different Loop objects for different instances
Fixes Rapptz#2294
1 parent 11211d4 commit d5211fb

File tree

1 file changed

+44
-2
lines changed

1 file changed

+44
-2
lines changed

discord/ext/tasks/__init__.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,21 @@ def change_interval(self, *, seconds=0, minutes=0, hours=0):
405405
self.hours = hours
406406
self.minutes = minutes
407407

408+
class _LoopFactory:
409+
def __init__(self, func, **kwargs):
410+
self.func = func
411+
self.name = func.__name__
412+
self.kwargs = kwargs
413+
414+
def __get__(self, obj, objtype):
415+
if obj is None:
416+
return self
417+
418+
loop = Loop(self.func, **self.kwargs)
419+
loop._injected = obj
420+
setattr(obj, self.name, loop)
421+
return loop
422+
408423
def loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None):
409424
"""A decorator that schedules a task in the background for you with
410425
optional reconnect logic. The decorator returns a :class:`Loop`.
@@ -436,6 +451,33 @@ def loop(*, seconds=0, minutes=0, hours=0, count=None, reconnect=True, loop=None
436451
The function was not a coroutine.
437452
"""
438453
def decorator(func):
439-
return Loop(func, seconds=seconds, minutes=minutes, hours=hours,
440-
count=count, reconnect=reconnect, loop=loop)
454+
defined_within_class = False
455+
frames = inspect.stack()
456+
# Essentially, to detect whether we're using this decorator a class
457+
# context we're walking the stack to see whether it's top level or
458+
# within a class level. This code is pretty finicky and hacky but
459+
# it's better than the alternative that requires maintaining a list
460+
# of IDs. This code does however break if someone assigns a loop
461+
# decorator using different ways, such as a dynamically created
462+
# class or calling the decorator directly. However such uses should
463+
# be niche and thus don't really impede functionality for 99.99% of users
464+
for frame in frames[1:]:
465+
if frame[3] == '<module>':
466+
break
467+
if '__module__' in frame[0].f_code.co_names:
468+
defined_within_class = True
469+
break
470+
471+
kwargs = {
472+
'seconds': seconds,
473+
'minutes': minutes,
474+
'hours': hours,
475+
'count': count,
476+
'reconnect': reconnect,
477+
'loop': loop
478+
}
479+
480+
if defined_within_class:
481+
return _LoopFactory(func, **kwargs)
482+
return Loop(func, **kwargs)
441483
return decorator

0 commit comments

Comments
 (0)