def _unflatten_params_cls(cls: type(Params), parsed_params: dict, prefix: Optional[str] = None, params_to_omit: Optional[Set[str]] = None, params_to_hide: Optional[Set[str]] = None) -> Params: """ Recursively construct a Params class or subclass (handles nested Params classes) using values parsed from the command line, and apply units. :param cls: The Params class to create an instance of :param parsed_params: The params parsed from the command line :param prefix: The prefix for all param names :param params_to_omit: Params to set to None when de-serializing the parsed params dictionary :return: An instance of cls with params set from the parsed params dictionary """ cls_specific_params = {} for k, v in cls.__dict__.items(): if isinstance(v, BaseDescriptor): if params_to_omit is not None and k in params_to_omit: cls_specific_params[k] = None continue param_name = prefix + k if k not in parsed_params and prefix is not None else k if (params_to_hide is not None and param_name in params_to_hide) or \ param_name.startswith('_') or getattr(v, 'hide', False): continue if getattr(v, 'expand', False): unexpanded_param = _extract_expanded_param( parsed_params, param_name, v, prefix) cls_specific_params.update({k: unexpanded_param}) else: if isinstance(parsed_params[param_name], Iterable) and \ not isinstance(parsed_params[param_name], str): cls_specific_params[k] = [ convert_to_si_units(p, getattr(v, 'unit', None)) for p in parsed_params[param_name] ] else: cls_specific_params[k] = convert_to_si_units( parsed_params[param_name], getattr(v, 'unit', None)) elif isinstance(v, Params): _params_to_omit = _get_omitted_params(v) next_prefix = _create_param_name_prefix( k, getattr(cls, '__nested_prefixes__', None)) _prefix = prefix + next_prefix if prefix is not None else next_prefix cls_specific_params[k] = _unflatten_params_cls( type(v), parsed_params, prefix=_prefix, params_to_omit=_params_to_omit, params_to_hide=_get_hidden_params(cls)) return cls(**cls_specific_params)
def __get__(self, instance, owner) -> Optional[np.ndarray]: if self.required and self.name not in instance.__dict__: raise ValueError(f'{self.name} is a required argument and must be set first!') v = instance.__dict__.get(self.name, self.default) if not _check_numpy_fn_param_value(self.name, v, self.nargs): return v return convert_to_si_units(self._numpy_function(v), self.unit)
def __get__(self, instance, owner) -> Optional[Set]: if self.required and self.name not in instance.__dict__: raise ValueError(f'{self.name} is a required argument and must be set first!') if instance.__dict__.get(self.name, self.default) is None: return None return set(convert_to_si_units(v, self.unit) for v in instance.__dict__.get(self.name, self.default))
def _extract_expanded_param(parsed_values: dict, name: str, param: BaseDescriptor, prefix: Optional[str]) -> Optional[List]: """ Convert [start, stop, num] or [start, stop, step], etc. from expanded form back into a list to be easily fed into the un-expanded param :param parsed_values: The parsed values from the command line as a dictionary (comes directly from the namespace) :param name: Original name of the param that was expanded (may have a prefix) :param param: The param that was expanded :param prefix: If the param is a nested param, there's a chance we had to append the enclosing class name as a prefix to deconflict arguments with the same name. See _create_param_name_prefix docstring for more info. """ old_arg_names = [ prefix + n if prefix is not None else n for n in _expand_param_name(param) ] assert parsed_values.get(name) is None, f'param {name} was expanded! ' \ f'Please provide {old_arg_names} instead' start_stop_x_list = [parsed_values[n] for n in old_arg_names] if all([x is None for x in start_stop_x_list]): return None units = _expand_param_units(param) si_unit_start_stop_x = [ convert_to_si_units(p, u) for p, u in zip(start_stop_x_list, units) ] return si_unit_start_stop_x
def __get__(self, instance, owner) -> Optional[np.ndarray]: if self.required and self.name not in instance.__dict__: raise ValueError(f'{self.name} is a required argument and must be set first!') v = instance.__dict__.get(self.name, self.default) if v is None or any([i is None for i in v]): return v return convert_to_si_units(np.linspace(*instance.__dict__.get(self.name, self.default)), self.unit)
def __get__(self, instance, owner): if self.required and self.name not in instance.__dict__: raise ValueError(f'{self.name} is a required argument and must be set first!') v = instance.__dict__.get(self.name, self.default) if v is None or any([i is None for i in v]): return v center, width, step = instance.__dict__.get(self.name, self.default) return convert_to_si_units(np.arange(center - 0.5 * width, center + 0.5 * width, step), self.unit)
def unit_set(self, param_name: str, value: Union[float, int, Iterable]) -> None: """ Set the parameter from a value that's in the param units specified in the Params class definition Ex. ``` class A(Params): f = FloatParam(help='float', unit='ns') a = A() a.unit_set('a', 5) print(a.f) # prints 5e-9 ``` :param param_name: The parameter name to set :param value: The new value in SI units """ param = type(self).__dict__.get(param_name) expanded_units = _expand_param_units(param) if expanded_units is not None: values = [ convert_to_si_units(v, u) for v, u in zip(value, expanded_units) ] setattr(self, param_name, values) else: unit = get_param_unit(type(self), param_name) if not isinstance(value, Iterable): setattr(self, param_name, convert_to_si_units(value, unit)) else: # need to keep parameter type val_type = type(value) setattr( self, param_name, val_type([convert_to_si_units(v, unit) for v in value]))