def add_service_listener(self, listener, info_filter=None, initial=False): """ Listens for changes in services that are available. listener is a function listener(service_id, host, port, info, event) which will be called whenever a service becomes available, a service disappears, or the host/port that should be used to access a particular service changes. service_id is the id of the service; host/port is the host/port at which the service can be found, info is the service's info object, and event is one of DISCOVERED, UNDISCOVERED, or CHANGED. If info_filter is a dictionary, only services with info objects matching that particular filter (as per the filter_matches function) will cause the listener to be called. If info_filter is None (the default), or the empty dictionary (since all info objects match the empty dictionary), the listener will be called for all services. If initial is True, the listener will be immediately (and synchronously) called once for each service that already exists, passing in DISCOVERED as the event. Otherwise, the listener will only be called once the next """ # Add the listener to our list of listeners self.service_listeners.append((info_filter, listener)) # Check to see if we're supposed to notify the listener about all # matching services that already exist if initial: # Scan all of the services for service_id, discovered_service in self.discovered_services.items(): if filter_matches(discovered_service.info, info_filter): # If this service matches, notify the listener about it host, port = discovered_service.locations.keys()[0] with print_exceptions: listener(service_id, host, port, discovered_service.info, DISCOVERED)
def connect_to(self, info_filter, timeout=10, open_listener=None, close_listener=None, fail_listener=None, lock=None): """ Locates the first service in the list of discovered services and uses self.connect to connect to it. The connection is then returned. This function will be going away soon. Service proxies (which can be obtained using self.get_service_proxy) are the replacement; a single service proxy is quite similar to this method, but it can follow the service across restarts of the underlying process publishing the service, which this method can't. """ with self.lock: for service_id, d in self.discovered_services.items(): if filter_matches(d.info, info_filter): host, port = d.locations.keys()[0] return self.connect(host, port, service_id, timeout, open_listener, close_listener, fail_listener, lock) raise exceptions.NoMatchingServiceException()
def remove_service_listener(self, listener, initial=False): # Scan the list of listeners and remove this one. Inefficient, it's # true, and I hope to make it more efficient later on. for index, (info_filter, l) in enumerate(self.service_listeners[:]): # See if we've hit the right listener if l == listener: # If we have, remove the listener del self.service_listeners[index] if initial: # Scan through the list of services for service_id, discovered_service in self.discovered_services.items(): if filter_matches(discovered_service.info, info_filter): # This service matched, so we notify this # listener that the service was removed with print_exceptions: listener(service_id, None, None, None, UNDISCOVERED) # We've found our listener and deleted it, so we return now return
def notify_service_listeners(self, service_id, host, port, info, event): for filter, listener in self.service_listeners: if filter_matches(info, filter): with print_exceptions: listener(service_id, host, port, info, event)
def main(): args = parser.parse_args() if args.mode is None and args.type is None: print "Use autosend2 --help for more information." return if args.mode == "help": print parser.format_help().replace("!!!INFO!!!", description.strip()) return mode = args.mode if mode is None: # Type, at least, will be present, since the "Use autosend2 --help ..." # message will have stopped us if it wasn't. So basically we need to # check to see if name is present, and if it is, then we call the # specified function, and if it isn't, then we search for the # introspection service and print a list of all supported functions. if args.name is not None: mode = "call" else: mode = "list" # Parse out the info filer from the arguments info_filter = parse_info_filter(args) # Now create a bus. with Bus() as bus: if mode == "discovery": # Print the discovery table header print discovery_mode_header # Add a listener listening for services that will print out # information to stdout bus.add_service_listener(partial(discovery_mode_listener, args), info_filter=info_filter, initial=True) # Wait until we're interrupted wait_for_interrupt() # This empty print is to add a new line after the one with ^C on it # so that things look a bit prettier when all of the REMOVED # messages are printed. print return if mode == "call": if not args.name: print "You need to specify the name of the function to call." return if args.multiple: # Open a multiple-service proxy, with a # bind_function that will call the requested function whenever # it binds to a service. The advantage of doing this instead of # creating the service proxy, waiting a few seconds, then # calling the function on it is that the function is called # nearly immediately instead of after a delay. with bus.get_service_proxy( info_filter, bind_function=partial(call_mode_onbind, args), multiple=True ) as proxy: # Wait a few seconds before stopping the service proxy time.sleep(args.time) else: # Open a single-service proxy, wait for it to bind to # something, then call the function and pass the result to # call_mode_print_result, which prints it out. with bus.get_service_proxy(info_filter) as proxy: proxy.wait_for_bind(timeout=args.time) try: call_mode_print_result(proxy[args.name](*parse_value_list(args.values))) except Exception as e: call_mode_print_result(e) return if mode == "list": # Open a service proxy on the available introspection services. # Note that we use a multiple service proxy here regardless of # what the user requested as we've no guarantee that the first # introspection service discovered will provide a service matching # the requested criteria. TODO: Perhaps consider watching the proxy # in the future if a single-service lookup has been requested to # immediately return once a suitable service is found. with bus.get_service_proxy({"type": "autobus.details"}, multiple=True) as proxy: # Wait a few seconds for the proxy to bind time.sleep(args.time) # Call the introspection function to get information about the # services int_results = proxy["get_details"]() # int_results will be a dict whose keys are the service ids of # the introspection services and whose values are dicts whose # keys are the actual service ids. We need to translate this # into results, which is simply a dict whose keys are service # ids and whose values are services. We also need to strip out # entries whose info objects don't match what we're looking for. results = {} # Iterate over the introspection services for _, services_dict in int_results.items(): # Iterate over the services this introspector knows about for service_id, service in services_dict.items(): # See if the service matches our filter, and see if # we've either specified --all or this service isn't # an introspection service if filter_matches(service["info"], info_filter) and ( args.all or not service["info"].get("type", "").startswith("autobus.") ): # It matches, so add it to the results results[service_id] = service # We've got the actual results. Now print a message if there # weren't any. if len(results) == 0: print "No matching services available." # If there were results, print them. for i, (service_id, service) in enumerate(results.items()): if i > 0: print "#" * 70 print "Service %s on %s (%s) -- %r:" % ( service["info"].get("type", "<unknown>"), service["info"].get("hostname", "<unknown>"), service_id, service["info"], ) if service.get("doc", None): print "\n" + service["doc"] print "=" * 70 functions = filter_hidden(args, service["functions"]) if len(functions) == 0: print "No functions available on this service." for j, (name, function) in enumerate(functions.items()): if j > 0: print "-" * 70 print "Function " + name + ":" if function.get("doc", None): print "\n" + function["doc"] print "=" * 70 events = filter_hidden(args, service["events"]) if len(events) == 0: print "No events available on this service." for j, (name, event) in enumerate(events.items()): if j > 0: print "-" * 70 print "Event " + name + ":" if event.get("doc"): print "\n" + event["doc"] print "=" * 70 objects = filter_hidden(args, service["objects"]) if len(objects) == 0: print "No objects available on this service." for j, (name, object) in enumerate(objects.items()): if j > 0: print "-" * 70 print "Object " + name + ":" if object.get("doc"): print "\n" + object["doc"] return print "Unsupported mode used: " + str(mode)