1import logging 2import threading 3 4from acts.event import event_bus 5from acts.event.event_subscription import EventSubscription 6from acts.event.subscription_handle import InstanceSubscriptionHandle 7from acts.event.subscription_handle import SubscriptionHandle 8from acts.event.subscription_handle import StaticSubscriptionHandle 9 10 11class SubscriptionBundle(object): 12 """A class for maintaining a set of EventSubscriptions in the event bus. 13 14 Attributes: 15 subscriptions: A dictionary of {EventSubscription: RegistrationID} 16 """ 17 18 def __init__(self): 19 self.subscriptions = {} 20 self._subscription_lock = threading.Lock() 21 self._registered = False 22 23 @property 24 def registered(self): 25 """True if this SubscriptionBundle has been registered.""" 26 return self._registered 27 28 def add(self, event_type, func, event_filter=None, 29 order=0): 30 """Adds a new Subscription to this SubscriptionBundle. 31 32 If this SubscriptionBundle is registered, the added Subscription will 33 also be registered. 34 35 Returns: 36 the EventSubscription object created. 37 """ 38 subscription = EventSubscription(event_type, func, 39 event_filter=event_filter, 40 order=order) 41 return self.add_subscription(subscription) 42 43 def add_subscription(self, subscription): 44 """Adds an existing Subscription to the subscription bundle. 45 46 If this SubscriptionBundle is registered, the added subscription will 47 also be registered. 48 49 Returns: 50 the subscription object. 51 """ 52 registration_id = None 53 with self._subscription_lock: 54 if self.registered: 55 registration_id = event_bus.register_subscription(subscription) 56 57 self.subscriptions[subscription] = registration_id 58 return subscription 59 60 def remove_subscription(self, subscription): 61 """Removes a subscription from the SubscriptionBundle. 62 63 If the SubscriptionBundle is registered, removing the subscription will 64 also unregister it. 65 """ 66 if subscription not in self.subscriptions.keys(): 67 return False 68 with self._subscription_lock: 69 if self.registered: 70 event_bus.unregister(self.subscriptions[subscription]) 71 del self.subscriptions[subscription] 72 return True 73 74 def register(self): 75 """Registers all subscriptions found within this object.""" 76 if self.registered: 77 return 78 with self._subscription_lock: 79 self._registered = True 80 for subscription, registration_id in self.subscriptions.items(): 81 if registration_id is not None: 82 logging.warning('Registered subscription found in ' 83 'unregistered SubscriptionBundle: %s, %s' % 84 (subscription, registration_id)) 85 self.subscriptions[subscription] = ( 86 event_bus.register_subscription(subscription)) 87 88 def unregister(self): 89 """Unregisters all subscriptions managed by this SubscriptionBundle.""" 90 if not self.registered: 91 return 92 with self._subscription_lock: 93 self._registered = False 94 for subscription, registration_id in self.subscriptions.items(): 95 if registration_id is None: 96 logging.warning('Unregistered subscription found in ' 97 'registered SubscriptionBundle: %s, %s' % 98 (subscription, registration_id)) 99 event_bus.unregister(subscription) 100 self.subscriptions[subscription] = None 101 102 103def create_from_static(obj): 104 """Generates a SubscriptionBundle from @subscribe_static functions on obj. 105 106 Args: 107 obj: The object that contains @subscribe_static functions. Can either 108 be a module or a class. 109 110 Returns: 111 An unregistered SubscriptionBundle. 112 """ 113 return _create_from_object(obj, obj, StaticSubscriptionHandle) 114 115 116def create_from_instance(instance): 117 """Generates a SubscriptionBundle from an instance's @subscribe functions. 118 119 Args: 120 instance: The instance object that contains @subscribe functions. 121 122 Returns: 123 An unregistered SubscriptionBundle. 124 """ 125 return _create_from_object(instance, instance.__class__, 126 InstanceSubscriptionHandle) 127 128 129def _create_from_object(obj, obj_to_search, subscription_handle_type): 130 """Generates a SubscriptionBundle from an object's SubscriptionHandles. 131 132 Note that instance variables do not have the class's functions as direct 133 attributes. The attributes are resolved from the type of the object. Here, 134 we need to search through the instance's class to find the correct types, 135 and subscribe the instance-specific subscriptions. 136 137 Args: 138 obj: The object that contains SubscriptionHandles. 139 obj_to_search: The class to search for SubscriptionHandles from. 140 subscription_handle_type: The type of the SubscriptionHandles to 141 capture. 142 143 Returns: 144 An unregistered SubscriptionBundle. 145 """ 146 bundle = SubscriptionBundle() 147 for attr_name, attr_value in obj_to_search.__dict__.items(): 148 if isinstance(attr_value, subscription_handle_type): 149 bundle.add_subscription(getattr(obj, attr_name).subscription) 150 if isinstance(attr_value, staticmethod): 151 if isinstance(getattr(obj, attr_name), subscription_handle_type): 152 bundle.add_subscription(getattr(obj, attr_name).subscription) 153 return bundle 154