| 
					
				 | 
			
			
				@@ -22,19 +22,24 @@ import sys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import threading 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from datetime import datetime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from heapq import heappush, heappop 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from decimal import Decimal 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from heapq import heapify, heappush, heappop 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from itertools import islice 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from operator import itemgetter 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from time import time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from weakref import ref 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from kombu.clocks import timetuple 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from kombu.utils import kwdict 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from kombu.utils import cached_property, kwdict 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from celery import states 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from celery.datastructures import AttributeDict 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from celery.five import items, values 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from celery.five import class_property, items, values 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from celery.utils import deprecated 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from celery.utils.functional import LRUCache 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from celery.utils.log import get_logger 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+PYPY = hasattr(sys, 'pypy_version_info') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # The window (in percentage) is added to the workers heartbeat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # frequency.  If the time between updates exceeds this window, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 # then the worker is considered to be offline. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -55,15 +60,25 @@ logger = get_logger(__name__) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 warn = logger.warning 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 R_STATE = '<State: events={0.event_count} tasks={0.task_count}>' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-R_WORKER = '<Worker: {0.hostname} ({0.status_string})' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-R_TASK = '<Task: {0.name}({0.uuid}) {0.state}>' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+R_WORKER = '<Worker: {0.hostname} ({0.status_string} clock:{0.clock})' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+R_TASK = '<Task: {0.name}({0.uuid}) {0.state} clock:{0.clock}>' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 __all__ = ['Worker', 'Task', 'State', 'heartbeat_expires'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 def heartbeat_expires(timestamp, freq=60, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                      expire_window=HEARTBEAT_EXPIRE_WINDOW): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    return timestamp + freq * (expire_window / 1e2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      expire_window=HEARTBEAT_EXPIRE_WINDOW, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      Decimal=Decimal, float=float, isinstance=isinstance): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # some json implementations returns decimal.Decimal objects, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    # which are not compatible with float. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    freq = float(freq) if isinstance(freq, Decimal) else freq 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if isinstance(timestamp, Decimal): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        timestamp = float(timestamp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return timestamp + (freq * (expire_window / 1e2)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def _depickle_task(cls, fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return cls(**(fields if CAN_KWDICT else kwdict(fields))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 def with_unique_field(attr): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -89,45 +104,71 @@ def with_unique_field(attr): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @with_unique_field('hostname') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-class Worker(AttributeDict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Worker(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Worker State.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     heartbeat_max = 4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     expire_window = HEARTBEAT_EXPIRE_WINDOW 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    pid = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    _defaults = {'hostname': None, 'pid': None, 'freq': 60} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def __init__(self, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        dict.__init__(self, self._defaults, **fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.heartbeats = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def on_online(self, timestamp=None, local_received=None, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`worker-online` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(**kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update_heartbeat(local_received, timestamp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def on_offline(self, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`worker-offline` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(**kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.heartbeats = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    _fields = ('hostname', 'pid', 'freq', 'heartbeats', 'clock', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               'active', 'processed', 'loadavg', 'sw_ident', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               'sw_ver', 'sw_sys') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if not PYPY: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        __slots__ = _fields + ('event', '__dict__', '__weakref__') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, hostname=None, pid=None, freq=60, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 heartbeats=None, clock=0, active=None, processed=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 loadavg=None, sw_ident=None, sw_ver=None, sw_sys=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.hostname = hostname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.pid = pid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.freq = freq 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.heartbeats = [] if heartbeats is None else heartbeats 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.clock = clock or 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.active = active 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.processed = processed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.loadavg = loadavg 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.sw_ident = sw_ident 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.sw_ver = sw_ver 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.sw_sys = sw_sys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event = self._create_event_handler() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def on_heartbeat(self, timestamp=None, local_received=None, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`worker-heartbeat` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(**kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update_heartbeat(local_received, timestamp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def update_heartbeat(self, received, timestamp): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if not received or not timestamp: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        drift = abs(int(received) - int(timestamp)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if drift > HEARTBEAT_DRIFT_MAX: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            warn(DRIFT_WARNING, self.hostname, drift, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 datetime.fromtimestamp(received), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 datetime.fromtimestamp(timestamp)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        heartbeats, hbmax = self.heartbeats, self.heartbeat_max 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if not heartbeats or (received and received > heartbeats[-1]): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            heappush(heartbeats, received) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if len(heartbeats) > hbmax: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                heartbeats[:] = heartbeats[hbmax:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __reduce__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self.__class__, (self.hostname, self.pid, self.freq, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                self.heartbeats, self.clock, self.active, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                self.processed, self.loadavg, self.sw_ident, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                self.sw_ver, self.sw_sys) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _create_event_handler(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        _set = object.__setattr__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        heartbeats = self.heartbeats 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        hbmax = self.heartbeat_max 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        def event(type_, timestamp=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  local_received=None, fields=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  max_drift=HEARTBEAT_DRIFT_MAX, items=items, abs=abs, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  heappush=heappush, heappop=heappop, int=int, len=len): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            fields = fields or {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for k, v in items(fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                _set(self, k, v) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if type_ == 'offline': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                heartbeats[:] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if not local_received or not timestamp: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                drift = abs(int(local_received) - int(timestamp)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if drift > HEARTBEAT_DRIFT_MAX: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    warn(DRIFT_WARNING, self.hostname, drift, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         datetime.fromtimestamp(local_received), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         datetime.fromtimestamp(timestamp)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if not heartbeats or ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        local_received and local_received > heartbeats[-1]): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    heappush(heartbeats, local_received) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if len(heartbeats) > hbmax: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        heappop(heartbeats) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return event 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def update(self, f, **kw): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for k, v in items(dict(f, **kw) if kw else f): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            setattr(self, k, v) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def __repr__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return R_WORKER.format(self) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -142,17 +183,53 @@ class Worker(AttributeDict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                  self.freq, self.expire_window) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def alive(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return bool(self.heartbeats and time() < self.heartbeat_expires) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def alive(self, nowfun=time): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return bool(self.heartbeats and nowfun() < self.heartbeat_expires) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def id(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return '{0.hostname}.{0.pid}'.format(self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def update_heartbeat(self, received, timestamp): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event(None, timestamp, received) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def on_online(self, timestamp=None, local_received=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('online', timestamp, local_received, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def on_offline(self, timestamp=None, local_received=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('offline', timestamp, local_received, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def on_heartbeat(self, timestamp=None, local_received=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('heartbeat', timestamp, local_received, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @class_property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _defaults(cls): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Deprecated, to be removed in 3.3""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source = cls() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return dict((k, getattr(source, k)) for k in cls._fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 @with_unique_field('uuid') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-class Task(AttributeDict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Task(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Task State.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    name = received = sent = started = succeeded = failed = retried = \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        revoked = args = kwargs = eta = expires = retries = worker = result = \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        exception = timestamp = runtime = traceback = exchange = \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        routing_key = client = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    state = states.PENDING 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    clock = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    _fields = ('uuid', 'name', 'state', 'received', 'sent', 'started', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               'succeeded', 'failed', 'retried', 'revoked', 'args', 'kwargs', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               'eta', 'expires', 'retries', 'worker', 'result', 'exception', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               'timestamp', 'runtime', 'traceback', 'exchange', 'routing_key', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               'clock', 'client') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if not PYPY: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        __slots__ = ('__dict__', '__weakref__') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #: How to merge out of order events. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #: Disorder is detected by logical ordering (e.g. :event:`task-received` 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -166,116 +243,153 @@ class Task(AttributeDict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                      'retries', 'eta', 'expires')} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #: meth:`info` displays these fields by default. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    _info_fields = ('args', 'kwargs', 'retries', 'result', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    'eta', 'runtime', 'expires', 'exception', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    'exchange', 'routing_key') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    #: Default values. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    _defaults = dict(uuid=None, name=None, state=states.PENDING, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     received=False, sent=False, started=False, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     succeeded=False, failed=False, retried=False, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     revoked=False, args=None, kwargs=None, eta=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     expires=None, retries=None, worker=None, result=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     exception=None, timestamp=None, runtime=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     traceback=None, exchange=None, routing_key=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     clock=0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def __init__(self, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        dict.__init__(self, self._defaults, **fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def update(self, state, timestamp, fields, _state=states.state): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Update state from new event. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        :param state: State from event. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        :param timestamp: Timestamp from event. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        :param fields: Event data. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    _info_fields = ('args', 'kwargs', 'retries', 'result', 'eta', 'runtime', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    'expires', 'exception', 'exchange', 'routing_key') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, uuid=None, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.uuid = uuid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if kwargs: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for k, v in items(kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                setattr(self, k, v) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def event(self, type_, timestamp=None, local_received=None, fields=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              precedence=states.precedence, items=items, dict=dict, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              PENDING=states.PENDING, RECEIVED=states.RECEIVED, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              STARTED=states.STARTED, FAILURE=states.FAILURE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              RETRY=states.RETRY, SUCCESS=states.SUCCESS, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              REVOKED=states.REVOKED): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        fields = fields or {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if type_ == 'sent': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.sent = PENDING, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif type_ == 'received': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.received = RECEIVED, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif type_ == 'started': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.started = STARTED, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif type_ == 'failed': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.failed = FAILURE, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif type_ == 'retried': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.retried = RETRY, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif type_ == 'succeeded': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.succeeded = SUCCESS, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        elif type_ == 'revoked': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state, self.revoked = REVOKED, timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            state = type_.upper() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        time_received = fields.get('local_received') or 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if self.worker and time_received: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.worker.update_heartbeat(time_received, timestamp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if state != states.RETRY and self.state != states.RETRY and \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                _state(state) < _state(self.state): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # note that precedence here is reversed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # see implementation in celery.states.state.__lt__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if state != RETRY and self.state != RETRY and \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                precedence(state) > precedence(self.state): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             # this state logically happens-before the current state, so merge. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.merge(state, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            keep = self.merge_rules.get(state) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if keep is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                fields = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    (k, v) for k, v in items(fields) if k in keep 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for key, value in items(fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                setattr(self, key, value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.state = state 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.timestamp = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            super(Task, self).update(fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for key, value in items(fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                setattr(self, key, value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def merge(self, state, timestamp, fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Merge with out of order event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        keep = self.merge_rules.get(state) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if keep is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            fields = dict((key, fields.get(key)) for key in keep) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            super(Task, self).update(fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def info(self, fields=None, extra=[]): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Information about this task suitable for on-screen display.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        fields = self._info_fields if fields is None else fields 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        def _keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for key in list(fields) + list(extra): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                value = getattr(self, key, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if value is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    yield key, value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return dict(_keys()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __repr__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return R_TASK.format(self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def as_dict(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        get = object.__getattribute__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (k, get(self, k)) for k in self._fields 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __reduce__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return _depickle_task, (self.__class__, self.as_dict()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def origin(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self.client if self.worker is None else self.worker.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def ready(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self.state in states.READY_STATES 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_sent(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-sent` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.sent = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.PENDING, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('sent', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_received(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-received` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.received = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.RECEIVED, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('received', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_started(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-started` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.started = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.STARTED, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('started', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_failed(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-failed` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.failed = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.FAILURE, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('failed', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_retried(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-retried` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.retried = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.RETRY, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('retried', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_succeeded(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-succeeded` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.succeeded = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.SUCCESS, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('succeeded', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_revoked(self, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Callback for the :event:`task-revoked` event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.revoked = timestamp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(states.REVOKED, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event('revoked', timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def on_unknown_event(self, shortype, timestamp=None, **fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.update(shortype.upper(), timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.event(shortype, timestamp, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def info(self, fields=None, extra=[]): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Information about this task suitable for on-screen display.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        fields = self._info_fields if fields is None else fields 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def update(self, state, timestamp, fields, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               _state=states.state, RETRY=states.RETRY): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self.event(state, timestamp, None, fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        def _keys(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            for key in list(fields) + list(extra): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                value = getattr(self, key, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if value is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    yield key, value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return dict(_keys()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def __repr__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return R_TASK.format(self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @deprecated(3.2, 3.3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def merge(self, state, timestamp, fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        keep = self.merge_rules.get(state) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if keep is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            fields = dict((k, v) for k, v in items(fields) if k in keep) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for key, value in items(fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            setattr(self, key, value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def ready(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return self.state in states.READY_STATES 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @class_property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _defaults(cls): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Deprecated, to be removed in 3.3.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source = cls() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return dict((k, getattr(source, k)) for k in source._fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Records clusters state.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Worker = Worker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Task = Task 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     event_count = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     task_count = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    heap_multiplier = 4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def __init__(self, callback=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                  workers=None, tasks=None, taskheap=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                 max_workers_in_memory=5000, max_tasks_in_memory=10000): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 max_workers_in_memory=5000, max_tasks_in_memory=10000, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 on_node_join=None, on_node_leave=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.event_callback = callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.workers = (LRUCache(max_workers_in_memory) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if workers is None else workers) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -284,10 +398,16 @@ class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._taskheap = [] if taskheap is None else taskheap 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.max_workers_in_memory = max_workers_in_memory 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.max_tasks_in_memory = max_tasks_in_memory 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.on_node_join = on_node_join 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.on_node_leave = on_node_leave 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self._mutex = threading.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.handlers = {'task': self.task_event, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                         'worker': self.worker_event} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self._get_handler = self.handlers.__getitem__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.handlers = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._seen_types = set() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.rebuild_taskheap() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @cached_property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _event(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._create_dispatcher() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def freeze_while(self, fun, *args, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         clear_after = kwargs.pop('clear_after', False) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -330,11 +450,12 @@ class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             worker = self.workers[hostname] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            worker.update(kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if kwargs: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                worker.update(kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return worker, False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            worker = self.workers[hostname] = Worker( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                hostname=hostname, **kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            worker = self.workers[hostname] = self.Worker( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                hostname, **kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return worker, True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def get_or_create_task(self, uuid): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -342,61 +463,112 @@ class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return self.tasks[uuid], False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            task = self.tasks[uuid] = Task(uuid=uuid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            task = self.tasks[uuid] = self.Task(uuid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return task, True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def worker_event(self, type, fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Process worker event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            hostname = fields['hostname'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            worker, created = self.get_or_create_worker(hostname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            handler = getattr(worker, 'on_' + type, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if handler: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                handler(**(fields if CAN_KWDICT else kwdict(fields))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return worker, created 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def task_event(self, type, fields, timetuple=timetuple): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Process task event.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        uuid = fields['uuid'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        hostname = fields['hostname'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        worker, _ = self.get_or_create_worker(hostname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        task, created = self.get_or_create_task(uuid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        task.worker = worker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        maxtasks = self.max_tasks_in_memory * 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        taskheap = self._taskheap 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        timestamp = fields.get('timestamp') or 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        clock = 0 if type == 'sent' else fields.get('clock') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        heappush(taskheap, timetuple(clock, timestamp, worker.id, task)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if len(taskheap) > maxtasks: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            heappop(taskheap) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        handler = getattr(task, 'on_' + type, None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if type == 'received': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.task_count += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if handler: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            handler(**fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            task.on_unknown_event(type, **fields) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return created 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def event(self, event): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         with self._mutex: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return self._dispatch_event(event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def _dispatch_event(self, event, kwdict=kwdict): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.event_count += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        event = kwdict(event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        group, _, subject = event['type'].partition('-') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self._get_handler(group)(subject, event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if self.event_callback: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.event_callback(self, event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return self._event(event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def task_event(self, type_, fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Deprecated, use :meth:`event`.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._event(dict(fields, type='-'.join(['task', type_])))[0] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def worker_event(self, type_, fields): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Deprecated, use :meth:`event`.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._event(dict(fields, type='-'.join(['worker', type_])))[0] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def _create_dispatcher(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        get_handler = self.handlers.__getitem__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        event_callback = self.event_callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        wfields = itemgetter('hostname', 'timestamp', 'local_received') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tfields = itemgetter('uuid', 'hostname', 'timestamp', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             'local_received', 'clock') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        taskheap = self._taskheap 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Removing events from task heap is an O(n) operation, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # so easier to just account for the common number of events 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # for each task (PENDING->RECEIVED->STARTED->final) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        #: an O(n) operation 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        max_events_in_heap = self.max_tasks_in_memory * self.heap_multiplier 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        add_type = self._seen_types.add 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        on_node_join, on_node_leave = self.on_node_join, self.on_node_leave 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tasks, Task = self.tasks, self.Task 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        workers, Worker = self.workers, self.Worker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # avoid updating LRU entry at getitem 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        get_worker, get_task = workers.data.__getitem__, tasks.data.__getitem__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        def _event(event, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   timetuple=timetuple, KeyError=KeyError, created=True): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.event_count += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if event_callback: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                event_callback(self, event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            group, _, subject = event['type'].partition('-') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                handler = get_handler(group) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return handler(subject, event), subject 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if group == 'worker': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    hostname, timestamp, local_received = wfields(event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        worker, created = get_worker(hostname), False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if subject == 'offline': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            worker, created = None, False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            worker = workers[hostname] = Worker(hostname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if worker: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        worker.event(subject, timestamp, local_received, event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if on_node_join and (created or subject == 'online'): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        on_node_join(worker) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if on_node_leave and subject == 'offline': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        on_node_leave(worker) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return (worker, created), subject 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif group == 'task': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (uuid, hostname, timestamp, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 local_received, clock) = tfields(event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                # task-sent event is sent by client, not worker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                is_client_event = subject == 'sent' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    task, created = get_task(uuid), False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    task = tasks[uuid] = Task(uuid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if is_client_event: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    task.client = hostname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        worker, created = get_worker(hostname), False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    except KeyError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        worker = workers[hostname] = Worker(hostname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    task.worker = worker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if worker is not None and local_received: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        worker.event(None, local_received, timestamp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                origin = hostname if is_client_event else worker.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                heappush(taskheap, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         timetuple(clock, timestamp, origin, ref(task))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if len(taskheap) > max_events_in_heap: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    heappop(taskheap) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if subject == 'received': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.task_count += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                task.event(subject, timestamp, local_received, event) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                task_name = task.name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if task_name is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    add_type(task_name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return (task, created), subject 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return _event 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def rebuild_taskheap(self, timetuple=timetuple, heapify=heapify): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        heap = self._taskheap[:] = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            timetuple(t.clock, t.timestamp, t.origin, ref(t)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for t in values(self.tasks) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        heapify(heap) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def itertasks(self, limit=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for index, row in enumerate(items(self.tasks)): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -409,10 +581,12 @@ class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         in ``(uuid, Task)`` tuples.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         seen = set() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for evtup in islice(reversed(self._taskheap), 0, limit): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            uuid = evtup[3].uuid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if uuid not in seen: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                yield uuid, evtup[3] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                seen.add(uuid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            task = evtup[3]() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if task is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                uuid = task.uuid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if uuid not in seen: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    yield uuid, task 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    seen.add(uuid) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     tasks_by_timestamp = tasks_by_time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def tasks_by_type(self, name, limit=None): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -439,8 +613,7 @@ class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def task_types(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Return a list of all seen task types.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return list(sorted(set(task.name for task in values(self.tasks) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                               if task.name is not None))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return sorted(self._seen_types) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def alive_workers(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Return a list of (seemingly) alive workers.""" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -451,6 +624,7 @@ class State(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def __reduce__(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return self.__class__, ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.event_callback, self.workers, self.tasks, self._taskheap, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.event_callback, self.workers, self.tasks, None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.max_workers_in_memory, self.max_tasks_in_memory, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.on_node_join, self.on_node_leave, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ) 
			 |