def add_subtask(self, subtask): """Link a subtask to this parent task. This will cause stop() to block until the subtask has also finished. Calling stop will not directly cancel the subtask. It is expected that your finalizer for this parent task will cancel or otherwise stop the subtask. Args: subtask (BackgroundTask): Another task that will be stopped when this task is stopped. """ if self.stopped: raise InternalError( "Cannot add a subtask to a parent that is already stopped") if not isinstance(subtask, BackgroundTask): raise ArgumentError( "Subtasks must inherit from BackgroundTask, task={}".format( subtask)) # pylint:disable=protected-access;It is the same class as us so is equivalent to self access. if subtask._loop != self._loop: raise ArgumentError( "Subtasks must run in the same BackgroundEventLoop as their parent", subtask=subtask, parent=self) self.subtasks.append(subtask)
def _create_resp_format(result_type, result_format): if result_type is not None and result_format is not None: raise ArgumentError("Both result_type and result_format specified", result_format=result_format, result_type=result_type) if result_format is not None: return result_format if result_type is None: return "" try: int_count, has_buffer = result_type except ValueError as err: raise ArgumentError( "Invalid value for result_type, must be tuple(int_count, has_buffer)", result_type=result_type) from err if has_buffer is False and int_count == 0: return "" if has_buffer is False: return "%dH" % int_count return "%dHV" % int_count
def __init__(self, cor, name=None, finalizer=None, stop_timeout=1.0, loop=None): self._name = name self._finalizer = finalizer self._stop_timeout = stop_timeout self._logger = logging.getLogger(__name__) self.stopped = False if loop is None: loop = SharedLoop if not isinstance(loop, BackgroundEventLoop): raise ArgumentError( "A BackgroundTask must be created with a BackgroundEventLoop, loop={}" .format(loop)) self._loop = loop self.subtasks = [] if inspect.iscoroutine(cor): self.task = _create_task_threadsafe(cor, self._loop) elif inspect.iscoroutinefunction(cor): self.task = _create_task_threadsafe(cor(), self._loop) elif isinstance(cor, asyncio.Task): self.task = cor elif cor is None: self.task = None else: raise ArgumentError( "Unknown object passed to Background task: {}".format(cor))
def local_service(self, name_or_id): """Get the locally synced information for a service. Args: name_or_id (string or int): Either a short name for the service or a numeric id. Returns: ServiceState: the current state of the service synced locally at the time of the call. """ with self._state_lock: if isinstance(name_or_id, int): if name_or_id not in self._name_map: raise ArgumentError("Unknown ID used to look up service", id=name_or_id) name = self._name_map[name_or_id] else: name = name_or_id if name not in self.services: raise ArgumentError("Unknown service name", name=name) service = self.services[name] return copy(service)
def FromBinary(cls, record_data, record_count=1): """Create an UpdateRecord subclass from binary record data. This should be called with a binary record blob (NOT including the record type header) and it will decode it into a ReflashControllerRecord. Args: record_data (bytearray): The raw record data that we wish to parse into an UpdateRecord subclass NOT including its 8 byte record header. record_count (int): The number of records included in record_data. Raises: ArgumentError: If the record_data is malformed and cannot be parsed. Returns: ReflashControllerRecord: The decoded reflash tile record. """ if len(record_data) < ReflashControllerRecord.RecordHeaderLength: raise ArgumentError( "Record was too short to contain a full reflash record header", length=len(record_data), header_length=ReflashControllerRecord.RecordHeaderLength) offset, data_length = struct.unpack_from("<LL", record_data) bindata = record_data[ReflashControllerRecord.RecordHeaderLength:] if len(bindata) != data_length: raise ArgumentError( "Embedded firmware length did not agree with actual length of embeded data", length=len(bindata), embedded_length=data_length) return ReflashControllerRecord(bindata, offset)
def _convert_to_bytearray(type_name, value): """Convert a typed value to a binary array""" int_types = {'uint8_t': 'B', 'int8_t': 'b', 'uint16_t': 'H', 'int16_t': 'h', 'uint32_t': 'L', 'int32_t': 'l'} type_name = type_name.lower() is_array = False if type_name[-2:] == '[]': if value[0] != '[' or value[-1] != ']': raise ArgumentError("Array value improperly formated, must be a stringified list") is_array = True type_name = type_name[:-2] if type_name not in int_types and type_name not in ['string', 'binary']: raise ArgumentError('Type must be a known integer type, integer type array, string', known_integers=int_types.keys(), actual_type=type_name) if type_name == 'string': #value should be passed as a string bytevalue = bytearray(value, 'utf-8') elif type_name == 'binary': bytevalue = bytearray(value) elif is_array: value = [int(n,0) for n in value[1:-1].split(',')] bytevalue = bytearray(struct.pack("<%s" % (int_types[type_name]*len(value)), *value)) else: bytevalue = bytearray(struct.pack("<%s" % int_types[type_name], value)) return bytevalue
def run(self): cloud = IOTileCloud() info = cloud.device_info(self._uuid) if self._sensorgraph is not None: if info['sg'] != self._sensorgraph: if not self._overwrite: raise ArgumentError("Cloud has incorrect sensorgraph setting", \ cloud_sensorgraph=info['sg'], expect_sensorgraph=self._sensorgraph) else: print("--> Updating cloud sensorgraph from %s to %s" % \ (info['sg'], self._sensorgraph)) cloud.set_sensorgraph(self._uuid, self._sensorgraph, app_tag=self._expected_app_tag) if self._device_template is not None: if info['template'] != self._device_template: if not self._overwrite: raise ArgumentError("Cloud has incorrect device_template setting", \ cloud_device_template=info['template'], expect_device_template=self._device_template) else: print("--> Updating cloud device template from %s to %s" % \ (info['template'], self._device_template)) cloud.set_device_template(self._uuid, self._device_template, os_tag=self._expected_os_tag)
def archive(self, output_path): """Archive this recipe and all associated files into a .ship archive. Args: output_path (str): The path where the .ship file should be saved. """ if self.path is None: raise ArgumentError( "Cannot archive a recipe yet without a reference to its original yaml file in self.path" ) outfile = zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) outfile.write(self.path, arcname="recipe_script.yaml") written_files = set() for _factory, args, _resources, files in self.steps: for arg_name in files: file_path = args[arg_name] if file_path in written_files: continue if os.path.basename(file_path) != file_path: raise ArgumentError( "Cannot archive a recipe yet that references file not in the same directory as the recipe" ) full_path = os.path.join(os.path.dirname(self.path), file_path) outfile.write(full_path, arcname=file_path) written_files.add(file_path)
def FromString(cls, desc): """Create a slot identifier from a string description. The string needs to be either: controller OR slot <X> where X is an integer that can be converted with int(X, 0) Args: desc (str): The string description of the slot Returns: SlotIdentifier """ desc = str(desc) if desc == u'controller': return SlotIdentifier(controller=True) words = desc.split() if len(words) != 2 or words[0] != u'slot': raise ArgumentError(u"Illegal slot identifier", descriptor=desc) try: slot_id = int(words[1], 0) except ValueError: raise ArgumentError(u"Could not convert slot identifier to number", descriptor=desc, number=words[1]) return SlotIdentifier(slot=slot_id)
def run(self): process = Popen(shlex.split(self._context), stdout=PIPE, stdin=PIPE, stderr=STDOUT) out, err = process.communicate( input='\n'.join(self._commands).encode('utf-8')) if err is not None: raise ArgumentError("Output Errored", errors=err, commands=self._commands) out = out.decode('utf-8') #Split outputs and remove context strings outputs = [ section.split(') ')[-1].strip() for section in out.split('\n(') ] return_strings = [] for i in range(len(self._commands)): return_strings += [ "Command: %s\nOutput: %s" % (self._commands[i], outputs[i]) ] if self._expect is not None: expected_output = self._expect[i] if expected_output is not None: if outputs[i] != expected_output: raise ArgumentError("Unexpected output", command=self._commands[i], \ expected=expected_output, actual=outputs[i]) print('\n'.join(return_strings))
def __init__(self, source, comparator, reference_value): source = str(source) if source not in [u'count', u'value']: raise ArgumentError( "Unknwon source for input trigger, should be count or value", source=source) self.use_count = False if source == u'count': self.use_count = True known_comps = { u'>': self._gt_comp, u'>=': self._ge_comp, u'<': self._lt_comp, u'<=': self._le_comp, u'==': self._eq_comp } comparator = str(comparator) if comparator not in known_comps: raise ArgumentError("Unkown comparison function for input trigger", comparator=comparator) self.comp_function = known_comps[comparator] self.comp_string = comparator self.reference = reference_value
def _pack_version(tag, version): if tag >= (1 << 20): raise ArgumentError( "The tag number is too high. It must fit in 20-bits", max_tag=1 << 20, tag=tag) if "." not in version: raise ArgumentError("You must pass a version number in X.Y format", version=version) major, _, minor = version.partition('.') try: major = int(major) minor = int(minor) except ValueError: raise ArgumentError( "Unable to convert version string into major and minor version numbers", version=version) if major < 0 or minor < 0 or major >= (1 << 6) or minor >= (1 << 6): raise ArgumentError( "Invalid version numbers that must be in the range [0, 63]", major=major, minor=minor, version_string=version) version_number = (major << 6) | minor combined_tag = (version_number << 20) | tag return combined_tag
def adjust_monitor(self, monitor_id, add_events=None, remove_events=None): """Adjust the events that this monitor wishes to receive Args: monitor_id (string): The exact string returned from a previous call to register_monitor add_events (iterable): A list of events to begin receiving remove_events (iterable): A list of events to stop receiving """ dev_uuid, _, monitor_name = monitor_id.partition('/') if dev_uuid == '*': raise ArgumentError("You are not currently allowed to adjust a global monitor", monitor_id=monitor_id) dev_uuid = int(dev_uuid) if dev_uuid not in self.monitors or monitor_name not in self.monitors[dev_uuid]: raise ArgumentError("Could not find monitor by name", monitor_id=monitor_id) filters, callback = self.monitors[dev_uuid][monitor_name] if add_events is not None: filters.update(add_events) if remove_events is not None: filters.difference_update(remove_events) self.monitors[dev_uuid][monitor_name] = (filters, callback)
async def _write_ff_dump_cmd(self, start, length): start_bytes = start.to_bytes(4, byteorder='little') length_bytes = length.to_bytes(2, byteorder='little') cmd_id = 0 read_cmd = struct.pack("B", cmd_id) await self.write_memory(ff_cfg.ff_addresses['command_id'], read_cmd, chunk_size=1) written_cmd, = await self.read_memory(ff_cfg.ff_addresses['command_id'], 1, chunk_size=1, join=False) if written_cmd != cmd_id: raise ArgumentError("FF dump command id was not successfully written.") else: logger.info("FF dump command id written was %s", hex(cmd_id)) await self.write_memory(ff_cfg.ff_addresses['read_command_address'], start_bytes, chunk_size=1) written_start, = await self.read_memory(ff_cfg.ff_addresses['read_command_address'], 4, chunk_size=4, join=False) if written_start != start: raise ArgumentError("FF dump command address was not successfully written.") else: logger.info("FF dump command address written to %s", hex(start)) await self.write_memory(ff_cfg.ff_addresses['read_command_length'], length_bytes, chunk_size=1) written_length, = await self.read_memory(ff_cfg.ff_addresses['read_command_length'], 2, chunk_size=2, join=False) if written_length != length: raise ArgumentError("ff dump command length was not successfully written.") else: logger.info("ff dump command length written to %d", length)
def _read_memory_blocking(self, start_address, length, chunk_size=4, join=True): if chunk_size not in (1, 2, 4): raise ArgumentError("Invalid chunk size specified in read_memory command", chunk_size=chunk_size) if length % chunk_size != 0: raise ArgumentError("You must specify a length that is an integer multiple of chunk_size", length=length, chunk_size=chunk_size) if start_address % chunk_size != 0: raise ArgumentError("You must specify a start address that is aligned to your chunk_size", start_address=start_address, chunk_size=chunk_size) word_length = length // chunk_size if chunk_size == 1: words = self._jlink.memory_read8(start_address, word_length) pack_size = "B" elif chunk_size == 2: words = self._jlink.memory_read16(start_address, word_length) pack_size = "H" elif chunk_size == 4: words = self._jlink.memory_read32(start_address, word_length) pack_size = "L" if join: return struct.pack("<%d%s" % (word_length, pack_size), *words) return words
def update_state(self, short_name, state): """Set the current state of a service. If the state is unchanged from a previous attempt, this routine does nothing. Args: short_name (string): The short name of the service state (int): The new stae of the service """ if short_name not in self.services: raise ArgumentError("Service name is unknown", short_name=short_name) if state not in states.KNOWN_STATES: raise ArgumentError("Invalid service state", state=state) serv = self.services[short_name]['state'] if serv.state == state: return update = {} update['old_status'] = serv.state update['new_status'] = state update['new_status_string'] = states.KNOWN_STATES[state] serv.state = state self._notify_update(short_name, 'state_change', update)
def _parse_target(target): """Parse a binary targeting information structure. This function only supports extracting the slot number or controller from the target and will raise an ArgumentError if more complicated targeting is desired. Args: target (bytes): The binary targeting data blob. Returns: dict: The parsed targeting data """ if len(target) != 8: raise ArgumentError("Invalid targeting data length", expected=8, length=len(target)) slot, match_op = struct.unpack("<B6xB", target) if match_op == _MATCH_CONTROLLER: return {'controller': True, 'slot': 0} elif match_op == _MATCH_SLOT: return {'controller': False, 'slot': slot} raise ArgumentError("Unsupported complex targeting specified", match_op=match_op)
def get_config(self, slot, config_id): """Get a config variable assignment previously set on this sensor graph. Args: slot (SlotIdentifier): The slot that we are setting this config variable on. config_id (int): The 16-bit config variable identifier. Returns: (str, str|int): Returns a tuple with the type of the config variable and the value that is being set. Raises: ArgumentError: If the config variable is not currently set on the specified slot. """ if slot not in self.config_database: raise ArgumentError( "No config variables have been set on specified slot", slot=slot) if config_id not in self.config_database[slot]: raise ArgumentError( "Config variable has not been set on specified slot", slot=slot, config_id=config_id) return self.config_database[slot][config_id]
def init_virtual_device_info(self, args): self.virtual_info['advertising_version'] = int( args.get('advertising_version', 1), 0) if self.virtual_info['advertising_version'] not in (1, 2): raise ArgumentError( "Invalid advertising version specified in args", supported=(1, 2), found=self.virtual_info['advertising_version']) self.virtual_info['reboot_count'] = int(args.get('reboot_count', 1), 0) if self.virtual_info['reboot_count'] <= 0: raise ArgumentError("Reboot count must be greater than 0.", supported="> 0", found=self.virtual_info['reboot_count']) self.virtual_info['mac_value'] = int(args.get('mac_value', 0), 0) if self.virtual_info['mac_value'] < 0 or self.virtual_info[ 'mac_value'] > 0xFFFFFFFF: raise ArgumentError("MAC value is limited to 32bits.", supported="0 - 0xFFFFFFFF", found=self.virtual_info['mac_value']) self.virtual_info['battery_voltage'] = float( args.get('battery_voltage', "3.14159")) if self.virtual_info['battery_voltage'] < 0 or self.virtual_info[ 'battery_voltage'] > 7.9: raise ArgumentError("Battery voltage is invalid", supported="0 - 7.9", found=self.virtual_info['battery_voltage'])
def parse_size_name(type_name): """Calculate size and encoding from a type name. This method takes a C-style type string like uint8_t[10] and returns - the total size in bytes - the unit size of each member (if it's an array) - the scruct.{pack,unpack} format code for decoding the base type - whether it is an array. """ if ' ' in type_name: raise ArgumentError("There should not be a space in config variable type specifier", specifier=type_name) variable = False count = 1 base_type = type_name if type_name[-1] == ']': variable = True start_index = type_name.find('[') if start_index == -1: raise ArgumentError("Could not find matching [ for ] character", specifier=type_name) count = int(type_name[start_index+1:-1], 0) base_type = type_name[:start_index] matched_type = TYPE_CODES.get(base_type) if matched_type is None: raise ArgumentError("Could not find base type name", base_type=base_type, type_string=type_name) base_size = struct.calcsize("<%s" % matched_type) total_size = base_size*count return total_size, base_size, matched_type, variable
def _load_module_classes(cls, path, base_class): """Load a python module and return all classes that inherit from a given base.""" folder, basename = os.path.split(path) basename, ext = os.path.splitext(basename) if ext not in (".py", ".pyc", ""): raise ArgumentError( "Attempted to load module is not a python package or module (.py or .pyc)", path=path) try: fileobj, pathname, description = imp.find_module( basename, [folder]) #Don't load modules twice if basename in sys.modules: mod = sys.modules[basename] else: mod = imp.load_module(basename, fileobj, pathname, description) except ImportError as exc: cls.logger.exception( "Error importing module: %s looking for class %s", path, base_class) raise ArgumentError( "Could not import module in order to load external proxy modules", module_path=path, parent_directory=folder, module_name=basename, error=str(exc)) # Find all classes in this module that inherit from the given base class return [ x for x in itervalues(mod.__dict__) if inspect.isclass(x) and issubclass(x, base_class) and x != base_class ]
def __init__(self, selector, dest_tile, report_format, automatic, report_type=u'telegram', with_other=None): report_format = str(report_format) report_type = str(report_type) if report_format not in DataStreamer.KnownFormats: raise ArgumentError( "Unknown report format in DataStreamer constructor", report_format=report_format, known_formats=DataStreamer.KnownFormats.keys()) if report_type not in DataStreamer.KnownTypes: raise ArgumentError( "Unknown report type in DataStreamer constructor", report_type=report_type, known_types=DataStreamer.KnownTypes.keys()) self.selector = selector self.dest = dest_tile self.format = report_format self.automatic = automatic self.report_type = report_type self.with_other = with_other
def get_connection_id(self, conn_or_internal_id): """Get the connection id. Args: conn_or_internal_id (int, string): The external integer connection id or an internal string connection id Returns: int: The connection id associated with that connection Raises: ArgumentError: When the key is not found in the list of active connections or is invalid. """ key = conn_or_internal_id if isinstance(key, str): table = self._int_connections elif isinstance(key, int): table = self._connections else: raise ArgumentError( "You must supply either an int connection id or a string internal id to _get_connection_state", id=key) try: data = table[key] except KeyError: raise ArgumentError("Could not find connection by id", id=key) return data['connection_id']
def local_service(self, name_or_id): """Get the locally synced information for a service. This method is safe to call outside of the background event loop without any race condition. Internally it uses a thread-safe mutex to protect the local copies of supervisor data and ensure that it cannot change while this method is iterating over it. Args: name_or_id (string or int): Either a short name for the service or a numeric id. Returns: ServiceState: the current state of the service synced locally at the time of the call. """ if not self._loop.inside_loop(): self._state_lock.acquire() try: if isinstance(name_or_id, int): if name_or_id not in self._name_map: raise ArgumentError("Unknown ID used to look up service", id=name_or_id) name = self._name_map[name_or_id] else: name = name_or_id if name not in self.services: raise ArgumentError("Unknown service name", name=name) return copy(self.services[name]) finally: if not self._loop.inside_loop(): self._state_lock.release()
def finish_async_rpc(self, address, rpc_id, *response): """Finish a previous asynchronous RPC. This method should be called by a peripheral tile that previously had an RPC called on it and chose to response asynchronously by raising ``AsynchronousRPCResponse`` in the RPC handler itself. The response passed to this function will be returned to the caller as if the RPC had returned it immediately. This method must only ever be called from a coroutine inside the emulation loop that is handling background work on behalf of a tile. Args: address (int): The tile address the RPC was called on. rpc_id (int): The ID of the RPC that was called. *response: The response that should be returned to the caller. This can either be a single bytes or bytearray object or a str object containing the format code followed by the required number of python objects that will then be packed using pack_rpc_payload(format, args). If you pass no additional response arguments then an empty response will be given. """ self.verify_calling_thread( True, "All asynchronous rpcs must be finished from within the emulation loop" ) if len(response) == 0: response_bytes = b'' elif len(response) == 1: response_bytes = response[0] if not isinstance(response_bytes, (bytes, bytearray)): raise ArgumentError( "When passing a binary response to finish_async_rpc, you must " "pass a bytes or bytearray object", response=response_bytes) else: resp_format = response[0] resp_args = response[1:] if not isinstance(resp_format, str): raise ArgumentError( "When passing a formatted response to finish_async_rpc, you must " "pass a str object with the format code as the first parameter after " "the rpc id.", resp_format=resp_format, additional_args=resp_args) response_bytes = pack_rpc_payload(resp_format, resp_args) self._rpc_queue.finish_async_rpc(address, rpc_id, response_bytes)
def load_extension(self, path, name_filter=None, class_filter=None, unique=False, component=None): """Load a single python module extension. This function is similar to using the imp module directly to load a module and potentially inspecting the objects it declares to filter them by class. Args: path (str): The path to the python file to load name_filter (str): If passed, the basename of the module must match name or nothing is returned. class_filter (type): If passed, only instance of this class are returned. unique (bool): If True (default is False), there must be exactly one object found inside this extension that matches all of the other criteria. component (IOTile): The component that this extension comes from if it is loaded from an installed component. This is used to properly import the extension as a submodule of the component's support package. Returns: list of (name, type): A list of the objects found at the extension path. If unique is True, then the list only contains a single entry and that entry will be directly returned. """ import_name = None if component is not None: import_name = _ensure_package_loaded(path, component) name, ext = _try_load_module(path, import_name=import_name) if name_filter is not None and name != name_filter: return [] found = [(name, x) for x in self._filter_subclasses(ext, class_filter)] found = [(name, x) for name, x in found if self._filter_nonextensions(x)] if not unique: return found if len(found) > 1: raise ArgumentError( "Extension %s should have had exactly one instance of class %s, found %d" % (path, class_filter.__name__, len(found)), classes=found) elif len(found) == 0: raise ArgumentError("Extension %s had no instances of class %s" % (path, class_filter.__name__)) return found[0]
def allocate_stream(self, stream_type, stream_id=None, previous=None, attach=False): """Allocate a new stream of the given type. The stream is allocated with an incremental ID starting at StreamAllocator.StartingID. The returned data stream can always be used to to attach a NodeInput to this stream, however the attach_stream() function should always be called first since this stream's output may need to be split and a logically equivalent stream used instead to satisfy a device specific constraint on the maximum number of outputs attached to a given stream. You can call allocate_stream on the same stream multiple times without issue. Subsequent calls to allocate_stream are noops. Args: stream_type (int): A stream type specified in the DataStream class like DataStream.ConstantType stream_id (int): The ID we would like to use for this stream, if this is not specified, an ID is automatically allocated. previous (DataStream): If this stream was automatically derived from another stream, this parameter should be a link to the old stream. attach (bool): Call attach_stream immediately before returning. Convenience routine for streams that should immediately be attached to something. Returns: DataStream: The allocated data stream. """ if stream_type not in DataStream.TypeToString: raise ArgumentError("Unknown stream type in allocate_stream", stream_type=stream_type) if stream_id is not None and stream_id >= StreamAllocator.StartingID: raise ArgumentError("Attempted to explicitly allocate a stream id in the internally managed id range", stream_id=stream_id, started_id=StreamAllocator.StartingID) # If the stream id is not explicitly given, we need to manage and track it # from our autoallocate range if stream_id is None: if stream_type not in self._next_id: self._next_id[stream_type] = StreamAllocator.StartingID stream_id = self._next_id[stream_type] self._next_id[stream_type] += 1 # Keep track of how many downstream nodes are attached to this stream so # that we know when we need to split it into two. stream = DataStream(stream_type, stream_id) if stream not in self._allocated_streams: self._allocated_streams[stream] = (stream, 0, previous) if attach: stream = self.attach_stream(stream) return stream
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 """ search_dir, filename = os.path.split(script_path) if search_dir == '': search_dir = './' if filename == '' or not os.path.exists(script_path): raise ArgumentError("Could not find script to load virtual tile", path=script_path) module_name, ext = os.path.splitext(filename) if ext != '.py': raise ArgumentError("Script did not end with .py", filename=filename) try: file_obj = None file_obj, pathname, desc = imp.find_module(module_name, [search_dir]) mod = imp.load_module(module_name, file_obj, pathname, desc) finally: if file_obj is not None: file_obj.close() devs = [ x for x in itervalues(mod.__dict__) if inspect.isclass(x) and issubclass(x, VirtualTile) and x != VirtualTile ] if len(devs) == 0: raise ArgumentError( "No VirtualTiles subclasses were defined in script", path=script_path) elif len(devs) > 1: raise ArgumentError( "More than one VirtualTiles subclass was defined in script", path=script_path, tiles=devs) return devs[0]
def FromString(cls, string_rep): """Create a DataStreamSelector from a string. The format of the string should either be: all <type> OR <type> <id> Where type is [system] <stream type>, with <stream type> defined as in DataStream Args: rep (str): The string representation to convert to a DataStreamSelector """ rep = str(string_rep) rep = rep.replace(u'node', '') rep = rep.replace(u'nodes', '') if rep.startswith(u'all'): parts = rep.split() spec_string = u'' if len(parts) == 3: spec_string = parts[1] stream_type = parts[2] elif len(parts) == 2: stream_type = parts[1] else: raise ArgumentError("Invalid wildcard stream selector", string_rep=string_rep) try: # Remove pluralization that can come with e.g. 'all system outputs' if stream_type.endswith(u's'): stream_type = stream_type[:-1] stream_type = DataStream.StringToType[stream_type] except KeyError: raise ArgumentError("Invalid stream type given", stream_type=stream_type, known_types=DataStream.StringToType.keys()) stream_spec = DataStreamSelector.SpecifierNames.get(spec_string, None) if stream_spec is None: raise ArgumentError("Invalid stream specifier given (should be system, user, combined or blank)", string_rep=string_rep, spec_string=spec_string) return DataStreamSelector(stream_type, None, stream_spec) # If we're not matching a wildcard stream type, then the match is exactly # the same as a DataStream identifier, so use that to match it. stream = DataStream.FromString(rep) return DataStreamSelector.FromStream(stream)
def upload_report(self, report): """Upload an IOTile report to the cloud. This function currently supports uploading the following kinds of reports: SignedListReport FlexibleDictionaryReport If you pass an instance of IndividualReadingReport, an exception will be thrown because IOTile.cloud does not support receiving individual readings. Those are only for local use. The filename of the uploaded report will have an extension set based on the type of report that you are uploading. Args: report (IOTileReport): The report that you want to upload. This should not be an IndividualReadingReport. Returns: int: The number of new readings that were accepted by the cloud as novel. """ if isinstance(report, IndividualReadingReport): raise ArgumentError( "You cannot upload IndividualReadingReport objects to iotile.cloud", report=report) if isinstance(report, SignedListReport): file_ext = ".bin" elif isinstance(report, FlexibleDictionaryReport): file_ext = ".mp" else: raise ArgumentError( "Unknown report format passed to upload_report", classname=report.__class__.__name__, report=report) timestamp = '{}'.format(report.received_time.isoformat()) payload = {'file': ("report" + file_ext, BytesIO(report.encode()))} resource = self.api.streamer.report headers = {} authorization_str = '{0} {1}'.format(self.token_type, self.token) headers['Authorization'] = authorization_str resp = self.api.session.post(resource.url(), files=payload, headers=headers, params={'timestamp': timestamp}) count = resource._process_response(resp)['count'] return count