Guest User

after2.py

a guest
Jul 23rd, 2019
148
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.75 KB | None | 0 0
  1. # Based off of https://github.com/getninjas/celery-executor/
  2. #
  3. # Apache Software License 2.0
  4. #
  5. # Copyright (c) 2018, Alan Justino da Silva
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the "License");
  8. # you may not use this file except in compliance with the License.
  9. # You may obtain a copy of the License at
  10. #
  11. # http://www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an "AS IS" BASIS,
  15. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18.  
  19.                                       CANCELLED_AND_NOTIFIED)
  20. from threading import Lock, Thread
  21. import time
  22.  
  23. from terra.logger import getLogger
  24. logger = getLogger(__name__)
  25.  
  26.  
  27. class CeleryExecutorFuture(Future):
  28.   def __init__(self, asyncresult):
  29.     self._ar = asyncresult
  30.     super().__init__()
  31.  
  32.   def __del__(self):
  33.     self._ar.forget()
  34.     del self._ar
  35.  
  36.   def cancel(self):
  37.     """Cancel the future if possible.
  38.    Returns True if the future was cancelled, False otherwise. A future
  39.    cannot be cancelled if it is running or has already completed.
  40.    """
  41.     with self._condition:
  42.       if self._state in [RUNNING, FINISHED, CANCELLED, CANCELLED_AND_NOTIFIED]:
  43.         return super().cancel()
  44.  
  45.       # Not running and not canceled. May be possible to cancel!
  46.       self._ar.ready()  # Triggers an update check
  47.       if self._ar.state != 'REVOKED':
  48.         self._ar.revoke()
  49.         self._ar.ready()
  50.  
  51.       # Celery task should be REVOKED now. Otherwise may be not possible
  52.       # revoke it.
  53.       if self._ar.state == 'REVOKED':
  54.         result = super().cancel()
  55.         if not result:  # pragma: no cover
  56.           logger.error('Please open an issue on Github: Upstream '
  57.                        'implementation changed?')
  58.       else:
  59.         # Is not running nor revoked nor finished :/
  60.         # The revoke() had not produced effect: Task is probable not on a
  61.         # worker, then not revoke-able.
  62.         # Setting as RUNNING to inhibit super() from cancelling the Future,
  63.         # then putting back.
  64.         initial_state = self._state
  65.         self._state = RUNNING
  66.         result = super().cancel()
  67.         if result:  # pragma: no cover
  68.           logger.error('Please open an issue on Github: Upstream '
  69.                        'implementation changed?')
  70.         self._state = initial_state
  71.  
  72.       return result
  73.  
  74.  
  75. class CeleryExecutor(Executor):
  76.   def __init__(self, predelay=None, postdelay=None, applyasync_kwargs=None,
  77.                retry_kwargs=None, retry_queue='', update_delay=0.1):
  78.     """
  79.    Executor implementation using celery tasks.
  80.  
  81.    Args:
  82.        predelay: Will trigger before the `.apply_async` internal call
  83.        postdelay: Will trigger before the `.apply_async` internal call
  84.        applyasync_kwargs: Options passed to the `.apply_async()` call
  85.        retry_kwargs: Options passed to the `.retry()` call on errors
  86.        retry_queue: Sugar to set an alternative queue specially for errors
  87.        update_delay: Delay time between checks for Future state changes
  88.    """
  89.     # Options about calling the Task
  90.     self._predelay = predelay
  91.     self._postdelay = postdelay
  92.     self._applyasync_kwargs = applyasync_kwargs or {}
  93.     self._retry_kwargs = retry_kwargs or {}
  94.     if retry_queue:
  95.       self._retry_kwargs['queue'] = retry_queue
  96.       self._retry_kwargs.setdefault('max_retries', 1)
  97.     self._retry_kwargs.setdefault('max_retries', 0)
  98.  
  99.     # Options about managing this Executor flow
  100.     self._update_delay = update_delay
  101.     self._shutdown = False
  102.     self._shutdown_lock = Lock()
  103.     self._futures = set()
  104.     self._monitor_started = False
  105.     self._monitor_stopping = False
  106.     self._monitor = Thread(target=self._update_futures)
  107.     self._monitor.setDaemon(True)
  108.  
  109.   def _update_futures(self):
  110.     while True:
  111.       time.sleep(self._update_delay)  # Not-so-busy loop
  112.       if self._monitor_stopping:
  113.         return
  114.  
  115.       for fut in tuple(self._futures):
  116.         if fut._state in (FINISHED, CANCELLED_AND_NOTIFIED):
  117.           # This Future is set and done. Nothing else to do.
  118.           self._futures.remove(fut)
  119.           continue
  120.  
  121.         ar = fut._ar
  122.         ar.ready()  # Just trigger the AsyncResult state update check
  123.  
  124.         if ar.state == 'REVOKED':
  125.           logger.debug1('Celery task "%s" canceled.', ar.id)
  126.           if not fut.cancelled():
  127.           logger.debug1('Celery task "%s" canceled.', ar.id)
  128.               logger.error('Future was not running but failed to be cancelled')
  129.             fut.set_running_or_notify_cancel()
  130.           # Future is CANCELLED -> CANCELLED_AND_NOTIFIED
  131.  
  132.         elif ar.state in ('RUNNING', 'RETRY'):
  133.           logger.debug1('Celery task "%s" running.', ar.id)
  134.           if not fut.running():
  135.           logger.debug1('Celery task "%s" running.', ar.id)
  136.           # Future is RUNNING
  137.  
  138.         elif ar.state == 'SUCCESS':
  139.           logger.debug1('Celery task "%s" resolved.', ar.id)
  140.           fut.set_result(ar.get(disable_sync_subtasks=False))
  141.           logger.debug1('Celery task "%s" resolved.', ar.id)
  142.  
  143.         elif ar.state == 'FAILURE':
  144.           logger.debug1('Celery task "%s" resolved with error.', ar.id)
  145.           fut.set_exception(ar.result)
  146.           logger.debug1('Celery task "%s" resolved with error.', ar.id)
  147.  
  148.         # else:  # ar.state in [RECEIVED, STARTED, REJECTED, RETRY]
  149.         #     pass
  150.  
  151.   def submit(self, fn, *args, **kwargs):
  152.     with self._shutdown_lock:
  153.       if self._shutdown:
  154.         raise RuntimeError('cannot schedule new futures after shutdown')
  155.  
  156.       if not self._monitor_started:
  157.         self._monitor.start()
  158.         self._monitor_started = True
  159.  
  160.       # metadata = {
  161.       #     'retry_kwargs': self._retry_kwargs.copy()
  162.       # }
  163.  
  164.       if self._predelay:
  165.         self._predelay(fn, *args, **kwargs)
  166.       # asyncresult = _celery_call.apply_async((fn, metadata) + args, kwargs,
  167.       #                                        **self._applyasync_kwargs)
  168.       asyncresult = fn.apply_async(args, kwargs)
  169.  
  170.       if self._postdelay:
  171.         self._postdelay(asyncresult)
  172.  
  173.       future = CeleryExecutorFuture(asyncresult)
  174.       self._futures.add(future)
  175.       return future
  176.  
  177.   def shutdown(self, wait=True):
  178.     with self._shutdown_lock:
  179.       self._shutdown = True
  180.       for fut in self._futures:
  181.         fut.cancel()
  182.  
  183.     if wait:
  184.       for _ in as_completed(self._futures):
  185.         pass
  186.  
  187.       self._monitor_stopping = True
  188.       try:
  189.         self._monitor.join()
  190.       except RuntimeError:  # pragma: no cover
  191.         # Thread never started. Cannot join
  192.       except RuntimeError:  # pragma: no cover
Advertisement
Add Comment
Please, Sign In to add comment