def add(self, items): """Add entry to global presubs list""" with _presub_lock: self.load_from_cloud() for entry in utils.ensure_listable(items): # add new entries, but only if they're unique if entry not in self.current: self.current.append(entry) self.save_to_cloud()
def remove(self, items): """Remove entry from global presubs list""" with _presub_lock: self.load_from_cloud() for entry in utils.ensure_listable(items): # remove all matching entries while entry in self.current: self.current.remove(entry) self.save_to_cloud()
def get_service_packages(self): """Get all service packages""" api = self._get_api(billing.DefaultApi) package_response = api.get_service_packages() packages = [] for state in PACKAGE_STATES: # iterate states in order items = getattr(package_response, state) or [] for item in ensure_listable(items): params = item.to_dict() params['state'] = state packages.append(ServicePackage(params)) return packages
def expand_dict_as_keys(d): """Expands a dictionary into a list of immutables with cartesian product :param d: dictionary (of strings or lists) :returns: cartesian product of list parts """ to_product = [] for key, values in sorted(d.items()): # if we sort the inputs here, itertools.product will keep a stable sort order for us later key_values = sorted([(key, v) for v in utils.ensure_listable(values) if v is not None]) if key_values: to_product.append(key_values) return list(itertools.product(*to_product))
def __init__(self, device_id=None, resource_path=None, first_value=FirstValue.on_value_update, **extra_filters): """Triggers when a resource's value changes .. warning:: This functionality is considered experimental; the interface may change in future releases :param device_id: device identifier, (or list thereof), optionally ending with wildcard * :param resource_path: resource path, (or list thereof), optionally ending with wildcard * :param first_value: mode for adjusting immediacy of subscribed values :param extra_filters: other local key-value filters """ super(ResourceValues, self).__init__() self.device_ids = [ str(d) for d in utils.ensure_listable(device_id) if d ] self.resource_paths = [ str(p) for p in utils.ensure_listable(resource_path) if p ] # TODO(endpoint_type) support needs the endpoint_type or identifier in presub api response # self.endpoint_type self._immediacy = first_value self._presubscription_registry = None # type: PreSubscriptionRegistry # for filtering inbound notifications (with wildcards) self._local_filters = {} # for configuring the router (which has no wildcard behaviour) self._route_seeds = { 'channel': ChannelIdentifiers.notifications, } # turn our parameters into a presubscriptions-compatible list (one device id per entry) # note: pluralised 'resource_paths' self._sdk_presub_params = [{ k: v for k, v in dict(device_id=d, resource_paths=self.resource_paths).items() if v } for d in self.device_ids or [None]] # api sends back different field names on the notification channel so remap them before # putting them in the routing table for api_key_name, values in dict(ep=self.device_ids, path=self.resource_paths).items(): has_wildcards = any(self._is_wildcard(v) for v in values if v) for to_validate in values: if to_validate is None: continue if not self._validate_wildcard(to_validate): raise ValueError( 'Wildcards can only be used at the end of values: "%s"' % (to_validate, )) # local filters recreate behaviour of server-side wildcard filter, but are not # routable (can't look them up in a dictionary) # routable filters are for explicit key-value pairs if has_wildcards: self._local_filters.setdefault(api_key_name, []).append(to_validate) else: self._route_seeds.setdefault(api_key_name, []).append(to_validate) self._route_keys = expand_dict_as_keys(self._route_seeds) self._optional_filters = {} self._optional_filters.update(extra_filters) self._optional_filter_keys = expand_dict_as_keys( self._optional_filters) self.add_filter_function(self._wildcard_filter)