@@ -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+
408423def 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