def __init__(self, ordered=False): # flag indicating whether observers should be maintained in a SortedSet # or a regular set (unordered) self._ordered = ordered # map: URI => ExactUriObservation self._observations_exact = {} # map: URI => PrefixUriObservation self._observations_prefix = StringTrie() # map: URI => WildcardUriObservation self._observations_wildcard = WildcardTrieMatcher() # map: observation ID => UriObservation self._observation_id_to_observation = {}
def __init__(self, ordered=False): # flag indicating whether observers should be maintained in a SortedSet # or a regular set (unordered) self._ordered = ordered # map: URI => ExactUriObservation self._observations_exact = {} # map: URI => PrefixUriObservation self._observations_prefix = StringTrie() # map: URI => WildcardUriObservation if True: # use a Trie-based implementation (supposed to be faster, but # otherwise compatible to the naive implementation below) self._observations_wildcard = WildcardTrieMatcher() else: self._observations_wildcard = WildcardMatcher() # map: observation ID => UriObservation self._observation_id_to_observation = {}
class UriObservationMap(object): """ Represents the current set of observations maintained by a broker/dealer. To test: trial crossbar.router.test.test_subscription """ def __init__(self, ordered=False): # flag indicating whether observers should be maintained in a SortedSet # or a regular set (unordered) self._ordered = ordered # map: URI => ExactUriObservation self._observations_exact = {} # map: URI => PrefixUriObservation self._observations_prefix = StringTrie() # map: URI => WildcardUriObservation self._observations_wildcard = WildcardTrieMatcher() # map: observation ID => UriObservation self._observation_id_to_observation = {} def add_observer(self, observer, uri, match=u"exact", extra=None): """ Adds a observer to the observation set and returns the respective observation. :param observer: The observer to add (this can be any opaque object). :type observer: obj :param uri: The URI (or URI pattern) to add the observer to add to. :type uri: unicode :param match: The matching policy for observing, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``. :type match: unicode :returns: A tuple ``(observation, was_already_observed, was_first_observer)``. Here, ``observation`` is an instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``. :rtype: tuple """ if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format( type(uri).__name__)) if match == u"exact": # if the exact-matching URI isn't in our map, create a new observation # if uri not in self._observations_exact: self._observations_exact[uri] = ExactUriObservation( uri, ordered=self._ordered, extra=extra) is_first_observer = True else: is_first_observer = False # get the observation # observation = self._observations_exact[uri] elif match == u"prefix": # if the prefix-matching URI isn't in our map, create a new observation # if uri not in self._observations_prefix: self._observations_prefix[uri] = PrefixUriObservation( uri, ordered=self._ordered, extra=extra) is_first_observer = True else: is_first_observer = False # get the observation # observation = self._observations_prefix[uri] elif match == u"wildcard": # if the wildcard-matching URI isn't in our map, create a new observation # if uri not in self._observations_wildcard: self._observations_wildcard[uri] = WildcardUriObservation( uri, ordered=self._ordered, extra=extra) is_first_observer = True else: is_first_observer = False # get the observation # observation = self._observations_wildcard[uri] else: raise Exception("invalid match strategy '{}'".format(match)) # note observation in observation ID map # if is_first_observer: self._observation_id_to_observation[observation.id] = observation # add observer if not already in observation # if observer not in observation.observers: observation.observers.add(observer) was_already_observed = False else: was_already_observed = True return observation, was_already_observed, is_first_observer def get_observation(self, uri, match=u"exact"): """ Get a observation (if any) for given URI and match policy. :param uri: The URI (or URI pattern) to get the observation for. :type uri: unicode :param match: The matching policy for observation to retrieve, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``. :type match: unicode :returns: The observation (instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``) or ``None``. :rtype: obj or None """ if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format( type(uri).__name__)) if match == u"exact": return self._observations_exact.get(uri, None) elif match == u"prefix": return self._observations_prefix.get(uri, None) elif match == u"wildcard": return self._observations_wildcard.get(uri, None) else: raise Exception("invalid match strategy '{}'".format(match)) def match_observations(self, uri): """ Returns the observations matching the given URI. This is the core method called by a broker to actually dispatch events. :param uri: The URI to match. :type uri: unicode :returns: A list of observations matching the URI. This is a list of instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``. :rtype: list """ observations = [] if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format( type(uri).__name__)) if uri in self._observations_exact: observations.append(self._observations_exact[uri]) for observation in self._observations_prefix.iter_prefix_values(uri): observations.append(observation) for observation in self._observations_wildcard.iter_matches(uri): observations.append(observation) return observations def best_matching_observation(self, uri): """ Returns the observation that best matches the given URI. This is the core method called by a dealer to actually forward calls. :param uri: The URI to match. :type uri: unicode :returns: The observation best matching the URI. This is an instance of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` or ``None``. :rtype: obj or None """ if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format( type(uri).__name__)) # a exact matching observation is always "best", if any # if uri in self._observations_exact: return self._observations_exact[uri] # "second best" is the longest prefix-matching observation, if any # FIXME: do we want this to take precedence over _any_ wildcard (see below)? # try: return self._observations_prefix.longest_prefix_value(uri) except KeyError: pass # FIXME: for wildcard observations, when there are multiple matching, we'd # like to deterministically select the "most selective one" # We first need a definition of "most selective", and then we need to implement # this here. # for observation in self._observations_wildcard.iter_matches(uri): return observation def get_observation_by_id(self, id): """ Get a observation by ID. :param id: The ID of the observation to retrieve. :type id: int :returns: The observation for the given ID or ``None``. :rtype: obj or None """ return self._observation_id_to_observation.get(id, None) def drop_observer(self, observer, observation): """ Drop a observer from a observation. :param observer: The observer to drop from the given observation. :type observer: obj :param observation: The observation from which to drop the observer. An instance of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` previously created and handed out by this observation map. :type observation: obj :returns: A tuple ``(was_observed, was_last_observer)``. :rtype: tuple """ if observer in observation.observers: was_observed = True # remove observer from observation # observation.observers.discard(observer) # no more observers on this observation! # if not observation.observers: if observation.match == u"exact": del self._observations_exact[observation.uri] elif observation.match == u"prefix": del self._observations_prefix[observation.uri] elif observation.match == u"wildcard": del self._observations_wildcard[observation.uri] else: # should not arrive here raise Exception("logic error") was_last_observer = True del self._observation_id_to_observation[observation.id] else: was_last_observer = False else: # observer wasn't on this observation was_observed = False return was_observed, was_last_observer
class UriObservationMap(object): """ Represents the current set of observations maintained by a broker/dealer. To test: trial crossbar.router.test.test_subscription """ def __init__(self, ordered=False): # flag indicating whether observers should be maintained in a SortedSet # or a regular set (unordered) self._ordered = ordered # map: URI => ExactUriObservation self._observations_exact = {} # map: URI => PrefixUriObservation self._observations_prefix = StringTrie() # map: URI => WildcardUriObservation self._observations_wildcard = WildcardTrieMatcher() # map: observation ID => UriObservation self._observation_id_to_observation = {} def add_observer(self, observer, uri, match=u"exact", extra=None): """ Adds a observer to the observation set and returns the respective observation. :param observer: The observer to add (this can be any opaque object). :type observer: obj :param uri: The URI (or URI pattern) to add the observer to add to. :type uri: unicode :param match: The matching policy for observing, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``. :type match: unicode :returns: A tuple ``(observation, was_already_observed, was_first_observer)``. Here, ``observation`` is an instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``. :rtype: tuple """ if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__)) if match == u"exact": # if the exact-matching URI isn't in our map, create a new observation # if uri not in self._observations_exact: self._observations_exact[uri] = ExactUriObservation(uri, ordered=self._ordered, extra=extra) is_first_observer = True else: is_first_observer = False # get the observation # observation = self._observations_exact[uri] elif match == u"prefix": # if the prefix-matching URI isn't in our map, create a new observation # if uri not in self._observations_prefix: self._observations_prefix[uri] = PrefixUriObservation(uri, ordered=self._ordered, extra=extra) is_first_observer = True else: is_first_observer = False # get the observation # observation = self._observations_prefix[uri] elif match == u"wildcard": # if the wildcard-matching URI isn't in our map, create a new observation # if uri not in self._observations_wildcard: self._observations_wildcard[uri] = WildcardUriObservation(uri, ordered=self._ordered, extra=extra) is_first_observer = True else: is_first_observer = False # get the observation # observation = self._observations_wildcard[uri] else: raise Exception("invalid match strategy '{}'".format(match)) # note observation in observation ID map # if is_first_observer: self._observation_id_to_observation[observation.id] = observation # add observer if not already in observation # if observer not in observation.observers: observation.observers.add(observer) was_already_observed = False else: was_already_observed = True return observation, was_already_observed, is_first_observer def get_observation(self, uri, match=u"exact"): """ Get a observation (if any) for given URI and match policy. :param uri: The URI (or URI pattern) to get the observation for. :type uri: unicode :param match: The matching policy for observation to retrieve, one of ``u"exact"``, ``u"prefix"`` or ``u"wildcard"``. :type match: unicode :returns: The observation (instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``) or ``None``. :rtype: obj or None """ if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__)) if match == u"exact": return self._observations_exact.get(uri, None) elif match == u"prefix": return self._observations_prefix.get(uri, None) elif match == u"wildcard": return self._observations_wildcard.get(uri, None) else: raise Exception("invalid match strategy '{}'".format(match)) def match_observations(self, uri): """ Returns the observations matching the given URI. This is the core method called by a broker to actually dispatch events. :param uri: The URI to match. :type uri: unicode :returns: A list of observations matching the URI. This is a list of instance of one of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation``. :rtype: list """ observations = [] if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__)) if uri in self._observations_exact: observations.append(self._observations_exact[uri]) for observation in self._observations_prefix.iter_prefix_values(uri): observations.append(observation) for observation in self._observations_wildcard.iter_matches(uri): observations.append(observation) return observations def best_matching_observation(self, uri): """ Returns the observation that best matches the given URI. This is the core method called by a dealer to actually forward calls. :param uri: The URI to match. :type uri: unicode :returns: The observation best matching the URI. This is an instance of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` or ``None``. :rtype: obj or None """ if not isinstance(uri, six.text_type): raise Exception("'uri' should be unicode, not {}".format(type(uri).__name__)) # a exact matching observation is always "best", if any # if uri in self._observations_exact: return self._observations_exact[uri] # "second best" is the longest prefix-matching observation, if any # FIXME: do we want this to take precedence over _any_ wildcard (see below)? # try: return self._observations_prefix.longest_prefix_value(uri) except KeyError: pass # FIXME: for wildcard observations, when there are multiple matching, we'd # like to deterministically select the "most selective one" # We first need a definition of "most selective", and then we need to implement # this here. # for observation in self._observations_wildcard.iter_matches(uri): return observation def get_observation_by_id(self, id): """ Get a observation by ID. :param id: The ID of the observation to retrieve. :type id: int :returns: The observation for the given ID or ``None``. :rtype: obj or None """ return self._observation_id_to_observation.get(id, None) def drop_observer(self, observer, observation): """ Drop a observer from a observation. :param observer: The observer to drop from the given observation. :type observer: obj :param observation: The observation from which to drop the observer. An instance of ``ExactUriObservation``, ``PrefixUriObservation`` or ``WildcardUriObservation`` previously created and handed out by this observation map. :type observation: obj :returns: A tuple ``(was_observed, was_last_observer)``. :rtype: tuple """ if observer in observation.observers: was_observed = True # remove observer from observation # observation.observers.discard(observer) # no more observers on this observation! # if not observation.observers: if observation.match == u"exact": del self._observations_exact[observation.uri] elif observation.match == u"prefix": del self._observations_prefix[observation.uri] elif observation.match == u"wildcard": del self._observations_wildcard[observation.uri] else: # should not arrive here raise Exception("logic error") was_last_observer = True del self._observation_id_to_observation[observation.id] else: was_last_observer = False else: # observer wasn't on this observation was_observed = False return was_observed, was_last_observer