def setup_parameter_from_dict(parameter: _BaseParameter, options: Dict[str, Any]) -> None: for attr, val in options.items(): if attr in PARAMETER_ATTRIBUTES: # set the attributes of the parameter, that map 1 to 1 setattr(parameter, attr, val) # extra attributes that need parsing elif attr == 'limits': if isinstance(val, str): issue_deprecation_warning( ('use of a comma separated string for the limits ' 'keyword'), alternative='an array like "[lower_lim, upper_lim]"' ) lower, upper = [float(x) for x in val.split(',')] else: lower, upper = val parameter.vals = validators.Numbers(lower, upper) elif attr == 'monitor' and val is True: self._monitor_parameters.append(parameter) elif attr == 'alias': setattr(parameter.instrument, val, parameter) elif attr == 'initial_value': # skip value attribute so that it gets set last # when everything else has been set up pass else: log.warning(f'Attribute {attr} not recognized when ' f'instatiating parameter \"{parameter.name}\"') if 'initial_value' in options: parameter.set(options['initial_value'])
def test_assert_deprecated_raises(): with assert_deprecated( 'The use of this function is deprecated, because ' 'of this being a test. Use \"a real function\" as an ' 'alternative.'): issue_deprecation_warning('use of this function', 'of this being a test', 'a real function')
def test_issue_deprecation_warning(): with warnings.catch_warnings(record=True) as w: issue_deprecation_warning('use of this function', 'of this being a test', 'a real function') assert issubclass(w[-1].category, QCoDeSDeprecationWarning) assert (str( w[-1].message) == 'The use of this function is deprecated, because ' 'of this being a test. Use \"a real function\" as an alternative.')
def test_deprecated_context_manager(): with _catch_deprecation_warnings() as ws: issue_deprecation_warning('something') issue_deprecation_warning('something more') warnings.warn('Some other warning') assert len(ws) == 2 with warnings.catch_warnings(record=True) as ws: issue_deprecation_warning('something') warnings.warn('Some other warning') assert len(ws) == 2
def test_deprecated_context_manager(): with _catch_deprecation_warnings() as ws: issue_deprecation_warning('something') issue_deprecation_warning('something more') warnings.warn('Some other warning') assert len(ws) == 2 with pytest.warns(expected_warning=QCoDeSDeprecationWarning) as ws: issue_deprecation_warning('something') warnings.warn('Some other warning') assert len(ws) == 2
def load_instrument(self, identifier: str, revive_instance: bool = False, **kwargs) -> Instrument: """ Creates an :class:`~.Instrument` instance as described by the loaded configuration file. Args: identifier: The identfying string that is looked up in the yaml configuration file, which identifies the instrument to be added. revive_instance: If ``True``, try to return an instrument with the specified name instead of closing it and creating a new one. **kwargs: Additional keyword arguments that get passed on to the ``__init__``-method of the instrument to be added. """ # try to revive the instrument if revive_instance and Instrument.exist(identifier): return Instrument.find_instrument(identifier) # load file # try to reload file on every call. This makes script execution a # little slower but makes the overall workflow more convenient. self.load_config_file(self.config_file) # load from config if identifier not in self._instrument_config.keys(): raise RuntimeError(f'Instrument {identifier} not found in ' 'instrument config file') instr_cfg = self._instrument_config[identifier] # TODO: add validation of config for better verbose errors: # check if instrument is already defined and close connection if instr_cfg.get('enable_forced_reconnect', get_config_enable_forced_reconnect()): with suppress(KeyError): self.close_and_remove_instrument(identifier) # instantiate instrument init_kwargs = instr_cfg.get('init', {}) # somebody might have a empty init section in the config init_kwargs = {} if init_kwargs is None else init_kwargs if 'address' in instr_cfg: init_kwargs['address'] = instr_cfg['address'] if 'port' in instr_cfg: init_kwargs['port'] = instr_cfg['port'] # make explicitly passed arguments overide the ones from the config # file. # We are mutating the dict below # so make a copy to ensure that any changes # does not leek into the station config object # specifically we may be passing non pickleable # instrument instances via kwargs instr_kwargs = deepcopy(init_kwargs) instr_kwargs.update(kwargs) name = instr_kwargs.pop('name', identifier) if 'driver' in instr_cfg: issue_deprecation_warning( 'use of the "driver"-keyword in the station ' 'configuration file', alternative='the "type"-keyword instead, prepending the ' 'driver value' ' to it') module_name = instr_cfg['driver'] instr_class_name = instr_cfg['type'] else: module_name = '.'.join(instr_cfg['type'].split('.')[:-1]) instr_class_name = instr_cfg['type'].split('.')[-1] module = importlib.import_module(module_name) instr_class = getattr(module, instr_class_name) instr = instr_class(name, **instr_kwargs) def resolve_instrument_identifier( instrument: ChannelOrInstrumentBase, identifier: str) -> ChannelOrInstrumentBase: """ Get the instrument, channel or channel_list described by a nested string. E.g: 'dac.ch1' will return the instance of ch1. """ try: for level in identifier.split('.'): instrument = checked_getattr(instrument, level, (InstrumentBase, ChannelList)) except TypeError: raise RuntimeError( f'Cannot resolve `{level}` in {identifier} to an ' f'instrument/channel for base instrument ' f'{instrument!r}.') return instrument def resolve_parameter_identifier(instrument: ChannelOrInstrumentBase, identifier: str) -> _BaseParameter: parts = identifier.split('.') if len(parts) > 1: instrument = resolve_instrument_identifier( instrument, '.'.join(parts[:-1])) try: return checked_getattr(instrument, parts[-1], _BaseParameter) except TypeError: raise RuntimeError( f'Cannot resolve parameter identifier `{identifier}` to ' f'a parameter on instrument {instrument!r}.') def setup_parameter_from_dict(parameter: _BaseParameter, options: Dict[str, Any]) -> None: for attr, val in options.items(): if attr in PARAMETER_ATTRIBUTES: # set the attributes of the parameter, that map 1 to 1 setattr(parameter, attr, val) # extra attributes that need parsing elif attr == 'limits': if isinstance(val, str): issue_deprecation_warning( ('use of a comma separated string for the limits ' 'keyword'), alternative='an array like "[lower_lim, upper_lim]"' ) lower, upper = [float(x) for x in val.split(',')] else: lower, upper = val parameter.vals = validators.Numbers(lower, upper) elif attr == 'monitor' and val is True: self._monitor_parameters.append(parameter) elif attr == 'alias': setattr(parameter.instrument, val, parameter) elif attr == 'initial_value': # skip value attribute so that it gets set last # when everything else has been set up pass else: log.warning(f'Attribute {attr} not recognized when ' f'instatiating parameter \"{parameter.name}\"') if 'initial_value' in options: parameter.set(options['initial_value']) def add_parameter_from_dict(instr: InstrumentBase, name: str, options: Dict[str, Any]) -> None: # keep the original dictionray intact for snapshot options = copy(options) param_type: type = _BaseParameter kwargs = {} if 'source' in options: param_type = DelegateParameter kwargs['source'] = resolve_parameter_identifier( instr.root_instrument, options['source']) options.pop('source') instr.add_parameter(name, param_type, **kwargs) setup_parameter_from_dict(instr.parameters[name], options) def update_monitor(): if ((self.use_monitor is None and get_config_use_monitor()) or self.use_monitor): # restart Monitor Monitor(*self._monitor_parameters) for name, options in instr_cfg.get('parameters', {}).items(): parameter = resolve_parameter_identifier(instr, name) setup_parameter_from_dict(parameter, options) for name, options in instr_cfg.get('add_parameters', {}).items(): parts = name.split('.') local_instr = (instr if len(parts) < 2 else resolve_instrument_identifier( instr, '.'.join(parts[:-1]))) add_parameter_from_dict(local_instr, parts[-1], options) self.add_component(instr) update_monitor() return instr
def test_assert_not_deprecated_raises(): with pytest.raises(AssertionError): with assert_not_deprecated(): issue_deprecation_warning('something')
def test_assert_deprecated_does_not_raise_wrong_msg(): with pytest.raises(AssertionError): with assert_deprecated('entirely different message'): issue_deprecation_warning('warning')