#NEWLINE# def serial_task( timeout , lock_key = '' , **celery_args ) : #NEWLINE# #INDENT# "\n    Decorator ensures that there's only one running task with given task_name.\n    Decorated tasks are bound tasks, meaning their first argument is always their Task instance\n    :param timeout: time after which lock is released.\n    :param lock_key: allows to define different lock for respective parameters of task.\n    :param celery_args: argument passed to celery's shared_task decorator.\n    " #NEWLINE# def wrapper( func ) : #NEWLINE# #INDENT# @ shared_task ( bind = True , ** celery_args ) #NEWLINE# @ wraps ( func ) #NEWLINE# def wrapped_func( self , * args , **kwargs ) : #NEWLINE# #INDENT# lock_name = 'serial_task.{}[{}]' . format ( self . name , lock_key . format ( * args , ** kwargs ) ) #NEWLINE# if ( not cache . add ( lock_name , True , timeout = timeout ) ) : #NEWLINE# #INDENT# raise RuntimeError ( "Can't execute task '{}' because the previously called task is still running." . format ( lock_name ) ) #NEWLINE# #UNINDENT# try : #NEWLINE# #INDENT# return func ( self , * args , ** kwargs ) #NEWLINE# #UNINDENT# finally : #NEWLINE# #INDENT# cache . delete ( lock_name ) #NEWLINE# #UNINDENT# #UNINDENT# return wrapped_func #NEWLINE# #UNINDENT# return lock_key