def FindByName(cls, name): """Find an installed VirtualTile by name. This function searches for installed virtual tiles using the pkg_resources entry_point `iotile.virtual_tile`. If name is a path ending in .py, it is assumed to point to a module on disk and loaded directly rather than using pkg_resources. Args: name (str): The name of the tile to search for. Returns: VirtualTile class: A virtual tile subclass that can be instantiated to create a virtual tile. """ if name.endswith('.py'): return cls.LoadFromFile(name) reg = ComponentRegistry() for _name, tile in reg.load_extensions('iotile.virtual_tile', name_filter=name, class_filter=BaseVirtualTile): return tile raise ArgumentError("VirtualTile could not be found by name", name=name)
def require(builder_name): """Find an advertised autobuilder and return it This function searches through all installed distributions to find if any advertise an entry point with group 'iotile.autobuild' and name equal to builder_name. The first one that is found is returned. This function raises a BuildError if it cannot find the required autobuild function Args: builder_name (string): The name of the builder to find Returns: callable: the autobuilder function found in the search """ reg = ComponentRegistry() for _name, autobuild_func in reg.load_extensions('iotile.autobuild', name_filter=builder_name): return autobuild_func raise BuildError( 'Cannot find required autobuilder, make sure the distribution providing it is installed', name=builder_name)
def FindByName(cls, name): """Find a specific installed auth provider by name.""" reg = ComponentRegistry() for _, entry in reg.load_extensions('iotile.auth_provider', name_filter=name): return entry
def LoadPlugins(cls): """Load all registered iotile.update_record plugins.""" if cls.PLUGINS_LOADED: return reg = ComponentRegistry() for _, record in reg.load_extensions('iotile.update_record'): cls.RegisterRecordType(record) cls.PLUGINS_LOADED = True
def _load_device(self, name, config): """Load a device either from a script or from an installed module""" if config is None: config_dict = {} elif isinstance(config, dict): config_dict = config elif config[0] == '#': # Allow passing base64 encoded json directly in the port string to ease testing. import base64 config_str = str(base64.b64decode(config[1:]), 'utf-8') config_dict = json.loads(config_str) else: try: with open(config, "r") as conf: data = json.load(conf) except IOError as exc: raise ArgumentError("Could not open config file", error=str(exc), path=config) if 'device' not in data: raise ArgumentError( "Invalid configuration file passed to VirtualDeviceAdapter", device_name=name, config_path=config, missing_key='device') config_dict = data['device'] reg = ComponentRegistry() if name.endswith('.py'): _name, device_factory = reg.load_extension( name, class_filter=BaseVirtualDevice, unique=True) return _instantiate_virtual_device(device_factory, config_dict, self._loop) seen_names = [] for device_name, device_factory in reg.load_extensions( 'iotile.virtual_device', class_filter=BaseVirtualDevice, product_name="virtual_device"): if device_name == name: return _instantiate_virtual_device(device_factory, config_dict, self._loop) seen_names.append(device_name) raise ArgumentError("Could not find virtual_device by name", name=name, known_names=seen_names)
def __init__(self): self._recipe_actions = {} self._recipe_resources = {} self._recipes = {} reg = ComponentRegistry() for name, action in reg.load_extensions('iotile.recipe_action', product_name='build_step'): self._recipe_actions[name] = action for name, resource in reg.load_extensions('iotile.recipe_resource'): self._recipe_resources[name] = resource
def register_apps(): reg = ComponentRegistry() reg.clear_extensions('iotile.app') reg.register_extension('iotile.app', 'app', FakeApp) yield reg.clear_extensions('iotile.app')
def _build_type_map(cls): """Build a map of all of the known report format processors""" return { report_format.ReportType: report_format for _, report_format in ComponentRegistry().load_extensions( 'iotile.report_format') }
def __init__(self, args=None): super(ChainedAuthProvider, self).__init__(args) # FIXME: Allow overwriting default providers via args self._load_installed_providers() reg = ComponentRegistry() sub_providers = [] for _, (priority, provider, provider_args ) in reg.load_extensions('iotile.default_auth_providers'): if provider not in self._auth_factories: raise ExternalError( "Default authentication provider list references unknown auth provider", provider_name=provider, known_providers=self._auth_factories.keys()) configured = self._auth_factories[provider](provider_args) sub_providers.append((priority, configured)) sub_providers.sort(key=lambda x: x[0]) self.providers = sub_providers
def instantiate_interface(virtual_iface, config): """Find a virtual interface by name and instantiate it Args: virtual_iface (string): The name of the pkg_resources entry point corresponding to the interface. It should be in group iotile.virtual_interface config (dict): A dictionary with a 'interface' key with the config info for configuring this virtual interface. This is optional. Returns: VirtualInterface: The instantiated subclass of VirtualInterface """ # Allow the null virtual interface for testing if virtual_iface == 'null': return VirtualIOTileInterface() conf = {} if 'interface' in config: conf = config['interface'] try: reg = ComponentRegistry() if virtual_iface.endswith('.py'): _name, iface = reg.load_extension( virtual_iface, class_filter=VirtualIOTileInterface, unique=True) else: _name, iface = reg.load_extensions( 'iotile.virtual_interface', name_filter=virtual_iface, class_filter=VirtualIOTileInterface, unique=True) return iface(conf) except ArgumentError as err: print("ERROR: Could not load virtual interface (%s): %s" % (virtual_iface, err.msg)) sys.exit(1)
def instantiate_device(virtual_dev, config, loop): """Find a virtual device by name and instantiate it Args: virtual_dev (string): The name of the pkg_resources entry point corresponding to the device. It should be in group iotile.virtual_device. If virtual_dev ends in .py, it is interpreted as a python script and loaded directly from the script. config (dict): A dictionary with a 'device' key with the config info for configuring this virtual device. This is optional. Returns: BaseVirtualDevice: The instantiated subclass of BaseVirtualDevice """ conf = {} if 'device' in config: conf = config['device'] # If we're given a path to a script, try to load and use that rather than search for an installed module try: reg = ComponentRegistry() if virtual_dev.endswith('.py'): _name, dev = reg.load_extension(virtual_dev, class_filter=BaseVirtualDevice, unique=True) else: _name, dev = reg.load_extensions('iotile.virtual_device', name_filter=virtual_dev, class_filter=BaseVirtualDevice, product_name="virtual_device", unique=True) return dev(conf) except ArgumentError as err: print("ERROR: Could not load virtual device (%s): %s" % (virtual_dev, err.msg)) sys.exit(1)
def __init__(self, settings_file=None): self.rules = [] logger = logging.getLogger('iotile.build.warnings') logger.addHandler(logging.NullHandler) #FIXME: Load settings_file #Find all registered default builders and load them in priority order #Each default resolver should be a 4-tuple with (priority, matching_regex, factory, default args) reg = ComponentRegistry() for name, resolver_entry in reg.load_extensions( 'iotile.build.default_depresolver'): try: priority, regex, factory, settings = resolver_entry except TypeError: logger.warn( 'Invalid default resolver entry %s that was not a 4-tuple: %s', name, str(resolver_entry)) continue self.rules.append((priority, (regex, factory, settings))) self.rules.sort(key=lambda x: x[0]) self._known_resolvers = {} for _, factory in reg.load_extensions('iotile.build.depresolver'): name = factory.__name__ if name in self._known_resolvers: raise ExternalError( "The same dependency resolver class name is provided by more than one entry point", name=name) self._known_resolvers[name] = factory
def tile_based(): conf_file = os.path.join(os.path.dirname(__file__), 'tile_config.json') if '@' in conf_file or ',' in conf_file or ';' in conf_file: pytest.skip('Cannot pass device config because path has [@,;] in it') reg = ComponentRegistry() reg.register_extension('iotile.proxy', 'virtual_tile', 'test/test_hw/virtual_tile.py') reg.register_extension('iotile.proxy', 'simple_virtual_tile', 'test/test_hw/simple_virtual_tile.py') hw = HardwareManager('virtual:tile_based@%s' % conf_file) yield hw reg.clear_extensions() hw.disconnect() hw.close()
def proxy_variants_2(): conf_file = os.path.join(os.path.dirname(__file__), 'proxy_match_tile_config.json') if '@' in conf_file or ',' in conf_file or ';' in conf_file: pytest.skip('Cannot pass device config because path has [@,;] in it') reg = ComponentRegistry() # None, =1.0.0, ^1.0.0 reg.register_extension('iotile.proxy', 'proxy_match_tile', ProxyMatchTest1) reg.register_extension('iotile.proxy', 'proxy_match_tile', ProxyMatchTest3) reg.register_extension('iotile.proxy', 'proxy_match_tile', ProxyMatchTest4) hw = HardwareManager('virtual:tile_based@%s' % conf_file) yield hw reg.clear_extensions() hw.close()
def linked_tile(rpc_agent, tmpdir): """Create a connected HardwareManager instance with a proxy pointed at an RPCDispatcher.""" visor, _client1, _client2 = rpc_agent # Create a config file that we can use to initialize a virtual device that will point at our # BasicRPCDispatch via a running IOTileSupervisor. Currently, HardwareManager can only # load configurations for virtual devices from actual files, so we need to save this to a # temp file. config = { "device": { "iotile_id": 1, "tiles": [{ "name": "service_delegate", "address": 11, "args": { "url": "ws://127.0.0.1:%d/services" % visor. port, # This has to match the port of the supervisor instance that we want to connect to "service": "service_1", # This has to match the service name that the RPCDispatcher is registered as an agent for "name": "bsctst" # This is the 6 character string that must match the ModuleName() of the proxy and is used to find the right proxy } }] } } # This is a special py.path.local object from pytest # https://docs.pytest.org/en/latest/tmpdir.html config_path_obj = tmpdir.join('config.json') config_path_obj.write(json.dumps(config)) config_path = str(config_path_obj) reg = ComponentRegistry() reg.register_extension('iotile.proxy', 'test_proxy', BasicRPCDispatcherProxy) # This will create a HardwareManager pointed at a virtual tile based device # where the tiles that are added to the virtual device are found using the config # file specified after the @ symbol. hw = HardwareManager(port="virtual:tile_based@%s" % config_path) # pylint:disable=invalid-name; We use hw throughout CoreTools to denote a HardwareManager instance # We specified that the virtual device should be at uuid 1 (using iotile_id above) # so we know how to connect to it. We also know that we specified a single tile # at address 11 inside that virtual device so we will be able to get its proxy # object by calling hw.get(11) once we are connected. hw.connect(1) yield hw hw.disconnect() reg.clear_extensions('iotile.proxy')
def test_recording_rpcs(tmpdir): """Make sure we can record RPCs.""" record_path = tmpdir.join('recording.csv') conf_file = os.path.join(os.path.dirname(__file__), 'tile_config.json') if '@' in conf_file or ',' in conf_file or ';' in conf_file: pytest.skip('Cannot pass device config because path has [@,;] in it') reg = ComponentRegistry() reg.register_extension('iotile.proxy', 'virtual_tile', 'test/test_hw/virtual_tile.py') try: with HardwareManager('virtual:tile_based@%s' % conf_file, record=str(record_path)) as hw: hw.connect(1) con = hw.get(9) tile1 = hw.get(11) con.count() tile1.add(3, 5) tile1.count() finally: reg.clear_extensions() assert record_path.exists() rpcs = record_path.readlines(cr=False) assert len(rpcs) == 12 assert rpcs[:3] == ['# IOTile RPC Recording', '# Format: 1.0', ''] # Patch out the timestamps and run times for better comparison rpc_lines = [x.split(',') for x in rpcs[4:-1]] for rpc in rpc_lines: assert len(rpc) == 9 rpc[1] = "" rpc[5] = "" rpc_lines = [",".join(x) for x in rpc_lines] print(rpc_lines[4]) assert rpc_lines == [ '1,, 9,0x0004,0xc0,, ,ffff74657374303101000003 ,', '1,, 9,0x0004,0xc0,, ,ffff74657374303101000003 ,', '1,,11,0x0004,0xc0,, ,ffff74657374303101000003 ,', '1,,11,0x0004,0xc0,, ,ffff74657374303101000003 ,', '1,, 9,0x8001,0xc0,, ,00000000 ,', '1,,11,0x8000,0xc0,,0300000005000000 ,08000000 ,', '1,,11,0x8001,0xc0,, ,00000000 ,' ]
def LoadFromFile(cls, script_path): """Import a virtual tile from a file rather than an installed module script_path must point to a python file ending in .py that contains exactly one VirtualTile class definition. That class is loaded and executed as if it were installed. To facilitate development, if there is a proxy object defined in the same file, it is also added to the HardwareManager proxy registry so that it can be found and used with the device. Args: script_path (string): The path to the script to load Returns: VirtualTile: A subclass of VirtualTile that was loaded from script_path """ _name, dev = ComponentRegistry().load_extension(script_path, class_filter=VirtualTile, unique=True) return dev
def _load_installed_providers(self): self._auth_factories = {} reg = ComponentRegistry() for name, entry in reg.load_extensions('iotile.auth_provider'): self._auth_factories[name] = entry
def _find_release_providers(): reg = ComponentRegistry() return { name: entry for name, entry in reg.load_extensions('iotile.build.release_provider') }
def test_build_command(): """Make sure iotile.build has been properly registered as a plugin.""" reg = ComponentRegistry() plugs = reg.list_plugins() assert 'build' in plugs
def main(argv=None, loop=SharedLoop): """Serve access to a virtual IOTile device using a virtual iotile interface.""" if argv is None: argv = sys.argv[1:] list_parser = argparse.ArgumentParser(add_help=False) list_parser.add_argument( '-l', '--list', action='store_true', help="List all known installed interfaces and devices and then exit") list_parser.add_argument( '-v', '--verbose', action="count", default=0, help="Increase logging level (goes error, warn, info, debug)") parser = argparse.ArgumentParser( description= "Serve acess to a virtual IOTile device using a virtual IOTile interface" ) parser.add_argument('interface', help="The name of the virtual device interface to use") parser.add_argument('device', help="The name of the virtual device to create") parser.add_argument( '-c', '--config', help= "An optional JSON config file with arguments for the interface and device" ) parser.add_argument( '-l', '--list', action='store_true', help="List all known installed interfaces and devices and then exit") parser.add_argument('-n', '--scenario', help="Load a test scenario from the given file") parser.add_argument( '-s', '--state', help= "Load a given state into the device before starting to serve it. Only works with emulated devices." ) parser.add_argument( '-d', '--dump', help= "Dump the device's state when we exit the program. Only works with emulated devices." ) parser.add_argument( '-t', '--track', help= "Track all changes to the device's state. Only works with emulated devices." ) parser.add_argument( '-v', '--verbose', action="count", default=0, help="Increase logging level (goes error, warn, info, debug)") args, _rest = list_parser.parse_known_args(argv) if args.list: configure_logging(args.verbose) reg = ComponentRegistry() print("Installed Device Servers:") for name, _iface in reg.load_extensions( 'iotile.device_server', class_filter=AbstractDeviceServer): print('- {}'.format(name)) print("\nInstalled Virtual Devices:") for name, dev in reg.load_extensions('iotile.virtual_device', class_filter=BaseVirtualDevice, product_name="virtual_device"): print('- {}: {}'.format(name, one_line_desc(dev))) return 0 args = parser.parse_args(argv) configure_logging(args.verbose) config = {} if args.config is not None: with open(args.config, "r") as conf_file: config = json.load(conf_file) started = False device = None stop_immediately = args.interface == 'null' try: server = instantiate_interface(args.interface, config, loop) device = instantiate_device(args.device, config, loop) if args.state is not None: print("Loading device state from file %s" % args.state) device.load_state(args.state) if args.scenario is not None: print("Loading scenario from file %s" % args.scenario) with open(args.scenario, "r") as infile: scenario = json.load(infile) # load_metascenario expects a list of scenarios even when there is only one if isinstance(scenario, dict): scenario = [scenario] device.load_metascenario(scenario) if args.track is not None: print("Tracking all state changes to device") device.state_history.enable() adapter = VirtualDeviceAdapter(devices=[device], loop=loop) server.adapter = adapter loop.run_coroutine(adapter.start()) try: loop.run_coroutine(server.start()) except: loop.run_coroutine(adapter.stop()) adapter = None raise started = True print("Starting to serve virtual IOTile device") if stop_immediately: return 0 # We need to periodically process events that are queued up in the interface while True: time.sleep(0.5) except KeyboardInterrupt: print("Break received, cleanly exiting...") finally: if args.dump is not None and device is not None: print("Dumping final device state to %s" % args.dump) device.save_state(args.dump) if started: loop.run_coroutine(server.stop()) loop.run_coroutine(adapter.stop()) if args.track is not None and device is not None: print("Saving state history to file %s" % args.track) device.state_history.dump(args.track) return 0