def __init__(self, *args, **kwargs): super(TestRecord, self).__init__(*args, **kwargs) # Cache data that does not change during execution. # Cache the metadata config so it does not recursively copied over and over # again. self._cached_config_from_metadata = self.metadata.get('config') self._cached_record = { 'station_id': data.convert_to_base_types(self.station_id), 'code_info': data.convert_to_base_types(self.code_info), }
def convert_to_dict(self, test_record): if self.inline_attachments: as_dict = data.convert_to_base_types(test_record) for phase in as_dict['phases']: for value in phase['attachments'].itervalues(): value['data'] = base64.standard_b64encode(value['data']) else: as_dict = data.convert_to_base_types(test_record, ignore_keys=('attachments',)) return as_dict
def as_base_types(self): """Convert to a dict representation composed exclusively of base types.""" base_types_dict = { k: data.convert_to_base_types(getattr(self, k)) for k in self.optional_attributes } base_types_dict.update( descriptor_id=self.descriptor_id, name=self.name, codeinfo=data.convert_to_base_types(self.codeinfo), ) return base_types_dict
def _summary_for_state_dict(state_dict): """Return a dict for state with counts swapped in for phase/log records.""" state_dict_summary = { k: v for k, v in six.iteritems(state_dict) if k != 'plugs'} state_dict_summary['test_record'] = data.convert_to_base_types( state_dict_summary['test_record']) state_dict_summary['test_record']['phases'] = len( state_dict_summary['test_record']['phases']) state_dict_summary['test_record']['log_records'] = len( state_dict_summary['test_record']['log_records']) state_dict_summary['running_phase_state'] = data.convert_to_base_types( state_dict_summary['running_phase_state'], tuple_type=list) return state_dict_summary
def _asdict(self): asdict = { key: data.convert_to_base_types(getattr(self, key), ignore_keys=('cls',)) for key in self.optional_attributes } asdict.update(name=self.name, doc=self.doc) return asdict
def cached_state_summary(self): """Get our cached state as a dict, with phases/records swapped out. Returns: Cached state used to generate the dict, and a dict representation of that state, with phases/records swapped out for respective counts. This is used to send to a running test so only have to send back new phases and log records. """ cached_state = self.cached_state if not cached_state: return None, None test_record = mutablerecords.CopyRecord( cached_state.test_record, phases=len(cached_state.test_record.phases), log_records=len(cached_state.test_record.log_records), ) return ( cached_state, { "status": cached_state.status.name, "running_phase_state": data.convert_to_base_types(cached_state.running_phase_state), "test_record": xmlrpclib.Binary(pickle.dumps(test_record)), }, )
def __init__(self, *args, **kwargs): super(PhaseState, self).__init__(*args, **kwargs) for m in six.itervalues(self.measurements): # Using functools.partial to capture the value of the loop variable. m.set_notification_callback(functools.partial(self._notify, m.name)) self._cached = { 'name': self.name, 'codeinfo': data.convert_to_base_types(self.phase_record.codeinfo), 'descriptor_id': data.convert_to_base_types( self.phase_record.descriptor_id), # Options are not set until the phase is finished. 'options': None, 'measurements': { k: m.as_base_types() for k, m in six.iteritems(self.measurements)}, 'attachments': {}, }
def get_history_after(self, test_uid, start_time_millis): """Get a list of TestRecords for test_uid from the History.""" _LOG.debug('RPC:get_history_after(%s)', start_time_millis) # TODO(madsci): We really should pull attachments out of band here. return [data.convert_to_base_types(test_record) for test_record in history.for_test_uid( test_uid, start_after_millis=start_time_millis)]
def __setitem__(self, coordinates, value): # pylint: disable=invalid-name coordinates_len = len(coordinates) if hasattr(coordinates, '__len__') else 1 if coordinates_len != self.num_dimensions: raise InvalidDimensionsError( 'Expected %s-dimensional coordinates, got %s' % (self.num_dimensions, coordinates_len)) # Wrap single dimensions in a tuple so we can assume value_dict keys are # always tuples later. if self.num_dimensions == 1: coordinates = (coordinates,) if coordinates in self.value_dict: _LOG.warning( 'Overriding previous measurement %s[%s] value of %s with %s', self.name, coordinates, self.value_dict[coordinates], value) self._cached_basetype_values = None elif self._cached_basetype_values is not None: self._cached_basetype_values.append(data.convert_to_base_types( coordinates + (value,))) self.value_dict[coordinates] = value if self.notify_value_set: self.notify_value_set()
def as_base_types(self): """Convert to a dict representation composed exclusively of base types.""" metadata = data.convert_to_base_types(self.metadata, ignore_keys=('config',)) metadata['config'] = self._cached_config_from_metadata ret = { 'dut_id': data.convert_to_base_types(self.dut_id), 'start_time_millis': self.start_time_millis, 'end_time_millis': self.end_time_millis, 'outcome': data.convert_to_base_types(self.outcome), 'outcome_details': data.convert_to_base_types(self.outcome_details), 'metadata': metadata, 'phases': self._cached_phases, 'log_records': self._cached_log_records, } ret.update(self._cached_record) return ret
def _serialize_state_dict(state_dict, remote_record=None): if (remote_record and remote_record['start_time_millis'] == state_dict['test_record'].start_time_millis): # Make a copy and delete phases/logs that are already known remotely. state_dict['test_record'] = mutablerecords.CopyRecord( state_dict['test_record']) del state_dict['test_record'].phases[:remote_record['phases']] del state_dict['test_record'].log_records[:remote_record['log_records']] return { 'status': state_dict['status'].name, 'test_record': data.convert_to_base_types(state_dict['test_record']), 'plugs': state_dict['plugs'], 'running_phase_state': data.convert_to_base_types(state_dict['running_phase_state']) }
def __init__(self, description='', unit=units.NO_DIMENSION): self._description = description self._unit = unit self._cached_dict = data.convert_to_base_types({ 'code': self.code, 'description': self.description, 'name': self.name, 'suffix': self.suffix, })
def _to_dict_with_event(cls, test_state): """Process a test state into the format we want to send to the frontend.""" original_dict, event = test_state.asdict_with_event() # This line may produce a 'dictionary changed size during iteration' error. test_state_dict = data.convert_to_base_types(original_dict) test_state_dict['execution_uid'] = test_state.execution_uid return test_state_dict, event
def convert_to_dict(self, test_record): as_dict = data.convert_to_base_types(test_record, json_safe=(not self.allow_nan)) if self.inline_attachments: for phase, original_phase in zip(as_dict['phases'], test_record.phases): for name, attachment in six.iteritems(phase['attachments']): original_data = original_phase.attachments[name].data attachment['data'] = base64.standard_b64encode(original_data).decode('utf-8') return as_dict
def as_base_types(self): """Convert to a dict representation composed exclusively of base types.""" running_phase_state = None if self.running_phase_state: running_phase_state = self.running_phase_state.as_base_types() return { 'status': data.convert_to_base_types(self._status), 'test_record': self.test_record.as_base_types(), 'plugs': self.plug_manager.as_base_types(), 'running_phase_state': running_phase_state, }
def as_base_types(self): """Convert this measurement to a dict of basic types.""" if not self._cached: # Create the single cache file the first time this is called. self._cached = { 'name': self.name, 'outcome': self.outcome.name, } if self.validators: self._cached['validators'] = data.convert_to_base_types( tuple(str(v) for v in self.validators)) if self.dimensions: self._cached['dimensions'] = data.convert_to_base_types(self.dimensions) if self.units: self._cached['units'] = data.convert_to_base_types(self.units) if self.docstring: self._cached['docstring'] = self.docstring if self.measured_value.is_value_set: self._cached['measured_value'] = self.measured_value.basetype_value() return self._cached
def _summary_for_state_dict(state_dict): """Return a dict for state with counts swapped in for phase/log records.""" state_dict_summary = {k: v for k, v in state_dict.iteritems() if k != "plugs"} state_dict_summary["test_record"] = mutablerecords.CopyRecord(state_dict_summary["test_record"]) state_dict_summary["test_record"].phases = len(state_dict_summary["test_record"].phases) state_dict_summary["test_record"].log_records = len(state_dict_summary["test_record"].log_records) state_dict_summary["running_phase_state"] = data.convert_to_base_types( state_dict_summary["running_phase_state"], tuple_type=list ) return state_dict_summary
def get_phase_descriptors(self, test_uid): """Get phase descriptor fields for the given test UID. Returns: List of dicts of RemotePhaseDescriptor fields. Raises: UnrecognizedTestUidError: The test_uid is not recognized. """ phases = openhtf.Test.from_uid(test_uid).descriptor.phases return [dict(id=id(phase), **data.convert_to_base_types(phase)) for phase in phases]
def get(self, test_uid): test, _ = self.get_test(test_uid) if test is None: return phase_descriptors = [ dict(id=id(phase), **data.convert_to_base_types(phase)) for phase in test.descriptor.phase_group] # Wrap value in a dict because writing a list directly is prohibited. self.write({'data': phase_descriptors})
def _serialize_state_dict(state_dict, remote_record=None): if remote_record and remote_record.start_time_millis == state_dict["test_record"].start_time_millis: # Make a copy and delete phases/logs that are already known remotely. state_dict["test_record"] = mutablerecords.CopyRecord(state_dict["test_record"]) del state_dict["test_record"].phases[: remote_record.phases] del state_dict["test_record"].log_records[: remote_record.log_records] return { "status": state_dict["status"].name, "test_record": xmlrpclib.Binary(pickle.dumps(state_dict["test_record"])), "plugs": state_dict["plugs"], "running_phase_state": data.convert_to_base_types(state_dict["running_phase_state"]), }
def open_output_file(self, test_record): """Open file based on pattern.""" record_dict = data.convert_to_base_types(test_record) pattern = self.filename_pattern if isinstance(pattern, basestring) or callable(pattern): output_file = self.open_file(util.format_string(pattern, **record_dict)) try: yield output_file finally: output_file.close() elif hasattr(self.filename_pattern, 'write'): yield self.filename_pattern else: raise ValueError( 'filename_pattern must be string, callable, or File-like object')
def set(self, value): """Set the value for this measurement, with some sanity checks.""" if self.is_value_set: # While we want to *allow* re-setting previously set measurements, we'd # rather promote the use of multidimensional measurements instead of # discarding data, so we make this somewhat chatty. _LOG.warning( 'Overriding previous measurement %s value of %s with %s, the old ' 'value will be lost. Use a dimensioned measurement if you need to ' 'save multiple values.', self.name, self.stored_value, value) if value is None: _LOG.warning('Measurement %s is set to None', self.name) self.stored_value = value self._cached_value = data.convert_to_base_types(value) self.is_value_set = True
def open_output_file(self, test_record): """Open file based on pattern.""" # Ignore keys for the log filename to not convert larger data structures. record_dict = data.convert_to_base_types( test_record, ignore_keys=('code_info', 'phases', 'log_records')) pattern = self.filename_pattern if isinstance(pattern, six.string_types) or callable(pattern): output_file = self.open_file(util.format_string(pattern, record_dict)) try: yield output_file finally: output_file.close() elif hasattr(self.filename_pattern, 'write'): yield self.filename_pattern else: raise ValueError( 'filename_pattern must be string, callable, or File-like object')
def cached_state_summary(self): """Get our cached state as a dict, with phases/records swapped out. Returns: Cached state used to generate the dict, and a dict representation of that state, with phases/records swapped out for respective counts. This is used to send to a running test so only have to send back new phases and log records. """ cached_state = self.cached_state if not cached_state: return None, None test_record = dict(cached_state.test_record) test_record['phases'] = len(test_record['phases']) test_record['log_records'] = len(test_record['log_records']) return cached_state, { 'status': cached_state.status.name, 'running_phase_state': data.convert_to_base_types( cached_state.running_phase_state), 'test_record': test_record }
def write_result(self, test): # Wrap value in a dict because a list is not allowed. self.write({'data': data.convert_to_base_types(test.history)})
def make_msg(cls, test_uid, remote_state): """Construct a message for publishing.""" return json.dumps({ 'test_uid': test_uid, 'state': data.convert_to_base_types(remote_state) })
def publish_test_record(cls, test_record): test_record_dict = data.convert_to_base_types(test_record) test_state_dict = _test_state_from_record(test_record_dict, cls._last_execution_uid) cls._publish_test_state(test_state_dict, 'record')
def make_message(cls): with cls.station_map_lock: return data.convert_to_base_types(cls.station_map)
def test_convert_to_base_types(self): class FloatSubclass(float): pass class SpecialBaseTypes( collections.namedtuple('SpecialBaseTypes', ['unsafe_value'])): def as_base_types(self): return {'safe_value': True} class AsDict(object): def _asdict(self): return None class NotRecursivelyCopied(object): def __init__(self): self.value = [] def as_base_types(self): return self.value not_copied = NotRecursivelyCopied() example_data = { 'list': [10], 'tuple': (10,), 'str': '10', 'unicode': '10', 'int': 2 ** 40, 'float': 10.0, 'long': 2 ** 80, 'bool': True, 'none': None, 'complex': 10j, 'float_subclass': FloatSubclass(10.0), 'special': SpecialBaseTypes('must_not_be_present'), 'not_copied': not_copied, # Some plugs such as UserInputPlug will return None as a response to # AsDict(). 'none_dict': AsDict(), } converted = data.convert_to_base_types(example_data) self.assertIsInstance(converted['list'], list) self.assertIsInstance(converted['tuple'], tuple) self.assertIsInstance(converted['str'], str) self.assertIsInstance(converted['unicode'], str) self.assertIsInstance(converted['int'], int) self.assertIsInstance(converted['float'], float) self.assertIsInstance(converted['long'], long) self.assertIsInstance(converted['bool'], bool) self.assertIsNone(converted['none']) self.assertIsInstance(converted['complex'], str) self.assertIsInstance(converted['float_subclass'], float) self.assertIsInstance(converted['special'], dict) self.assertEqual(converted['special'], {'safe_value': True}) self.assertEqual(converted['none_dict'], None) self.assertIs(converted['not_copied'], not_copied.value)
def make_msg(cls, test_uid, remote_state): """Construct a message for publishing.""" return json.dumps({"test_uid": test_uid, "state": convert_to_base_types(remote_state)})