From 1b0f89dca6a85e91281d13da2280c419e4ccc1bf Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 10:47:06 +1000 Subject: [PATCH 01/88] refactor: Remove max_delay Remove max_delay because it did not seem to work. For more info see bullet point #600. This breaks code that used max_delay --- qcodes/instrument/parameter.py | 48 +++++----------------------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 4822903c1b4..52ea5818dc9 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -766,10 +766,6 @@ class StandardParameter(Parameter): the *start* of each set, whether part of a sweep or not. Can be set to 0 to go maximum speed with no errors. - max_delay (Optional[Union[int, float]]): If > delay, we don't emit a - warning unless the time taken during a single set is greater than - this, even though we aim for delay. - step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. @@ -785,7 +781,7 @@ class StandardParameter(Parameter): def __init__(self, name, instrument=None, get_cmd=None, get_parser=None, set_cmd=None, set_parser=None, - delay=None, max_delay=None, step=None, max_val_age=3600, + delay=None, step=None, max_val_age=3600, vals=None, val_mapping=None, **kwargs): # handle val_mapping before super init because it impacts # vals / validation in the base class @@ -822,7 +818,7 @@ def __init__(self, name, instrument=None, self._set_get(get_cmd, get_parser) self._set_set(set_cmd, set_parser) - self.set_delay(delay, max_delay) + self.set_delay(delay) self.set_step(step, max_val_age) if not (self.has_get or self.has_set): @@ -921,17 +917,9 @@ def _sweep_steps(self, value): return permissive_range(start_value, value, self._step)[1:] def _update_set_ts(self, step_clock): - # calculate the delay time to the *max* delay, - # then take off up to the tolerance - tolerance = self._delay_tolerance - step_clock += self._delay - remainder = wait_secs(step_clock + tolerance) - if remainder <= tolerance: - # don't allow extra delays to compound - step_clock = time.perf_counter() - remainder = 0 - else: - remainder -= tolerance + # TODO(nulinspiratie) remove function alltogether + step_clock = time.perf_counter() + remainder = 0 return step_clock, remainder def _validate_and_sweep(self, value): @@ -1015,53 +1003,29 @@ def get_delay(self): """Return the delay time of this parameter. Also see `set_delay` """ return self._delay - def set_delay(self, delay, max_delay=None): + def set_delay(self, delay): """ Configure this parameter with a delay between set operations. Typically used in conjunction with set_step to create an effective ramp rate, but can also be used without a step to enforce a delay after every set. - If delay and max_delay are both None or 0, we never emit warnings - no matter how long the set takes. Args: delay(Union[int, float]): the target time between set calls. The actual time will not be shorter than this, but may be longer if the underlying set call takes longer. - max_delay(Optional[Union[int, float]]): if given, the longest time - allowed for the underlying set call before we emit a warning. - Raises: TypeError: If delay is not int nor float - TypeError: If max_delay is not int nor float ValueError: If delay is negative - ValueError: If max_delay is smaller than delay """ - if delay is None: - delay = 0 if not isinstance(delay, (int, float)): raise TypeError('delay must be a number') if delay < 0: raise ValueError('delay must not be negative') self._delay = delay - if max_delay is not None: - if not isinstance(max_delay, (int, float)): - raise TypeError( - 'max_delay must be a either int or a float') - if max_delay < delay: - raise ValueError('max_delay must be no shorter than delay') - self._delay_tolerance = max_delay - delay - else: - self._delay_tolerance = 0 - - if not (self._delay or self._delay_tolerance): - # denotes that we shouldn't follow the wait code or - # emit any warnings - self._delay = None - class ManualParameter(Parameter): """ From b0ff6837ba074b9bf35db5df7ed5c094b965084c Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 10:53:13 +1000 Subject: [PATCH 02/88] Refactor: Change get/set_delay to dependent properties --- qcodes/instrument/parameter.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 52ea5818dc9..a995805aae9 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -818,7 +818,7 @@ def __init__(self, name, instrument=None, self._set_get(get_cmd, get_parser) self._set_set(set_cmd, set_parser) - self.set_delay(delay) + self.delay = delay self.set_step(step, max_val_age) if not (self.has_get or self.has_set): @@ -999,11 +999,13 @@ def set_step(self, step, max_val_age=None): self._step = step self.set = self._validate_and_sweep - def get_delay(self): - """Return the delay time of this parameter. Also see `set_delay` """ + @property + def delay(self): + """Property that returns the delay time of this parameter""" return self._delay - def set_delay(self, delay): + @delay.setter + def delay(self, delay): """ Configure this parameter with a delay between set operations. From df911e456f9b6ae94b3dbbe19098c7cb263e8962 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 11:08:13 +1000 Subject: [PATCH 03/88] Refactor: Change get/set_step to dependent property Additional changes - max_val_age is temporarily useless (to be moved to GetLatest) - creating set val temporarily moved to init max_val_age temporarily unusable --- qcodes/instrument/parameter.py | 66 +++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index a995805aae9..b5e7a8d9a0e 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -814,12 +814,19 @@ def __init__(self, name, instrument=None, # stored value from last .set() or .get() # normally only used by set with a sweep, to avoid # having to call .get() for every .set() + # TODO(nulinspiratie) Figure out why this is needed self._max_val_age = 0 self._set_get(get_cmd, get_parser) self._set_set(set_cmd, set_parser) self.delay = delay - self.set_step(step, max_val_age) + self.step = step + + # TODO(nulinspiratie) Create single set function + if self.step is None: + self.set = self._validate_and_set + else: + self.set = self._validate_and_sweep if not (self.has_get or self.has_set): raise NoCommandError('neither set nor get cmd found in' + @@ -945,7 +952,12 @@ def _validate_and_sweep(self, value): 'setting {} to {}'.format(self.full_name, repr(value)),) raise e - def set_step(self, step, max_val_age=None): + @property + def step(self): + return self._step + + @step.setter + def step(self, step): """ Configure whether this Parameter uses steps during set operations. If step is a positive number, this is the maximum value change @@ -956,48 +968,44 @@ def set_step(self, step, max_val_age=None): step (Union[int, float]): A positive number, the largest change allowed in one call. All but the final change will attempt to change by +/- step exactly - - max_val_age (Optional[int]): Only used with stepping, the max time - (in seconds) to trust a saved value. If this parameter has not - been set or measured more recently than this, it will be - measured before starting to step, so we're confident in the - value we're starting from. - + Raises: TypeError: if step is not numeric ValueError: if step is negative TypeError: if step is not integer for an integer parameter TypeError: if step is not a number - TypeError: if max_val_age is not numeric - ValueError: if max_val_age is negative """ - if not step: - # single-command setting - self.set = self._validate_and_set - - elif not self._vals.is_numeric: + if not self._vals.is_numeric: raise TypeError('you can only step numeric parameters') elif step <= 0: raise ValueError('step must be positive') elif (isinstance(self._vals, Ints) and not isinstance(step, int)): - raise TypeError( - 'step must be a positive int for an Ints parameter') + raise TypeError('step must be a positive int for an Ints parameter') elif not isinstance(step, (int, float)): raise TypeError('step must be a number') - else: - # stepped setting - if max_val_age is not None: - if not isinstance(max_val_age, (int, float)): - raise TypeError( - 'max_val_age must be a number') - if max_val_age < 0: - raise ValueError('max_val_age must be non-negative') - self._max_val_age = max_val_age - self._step = step - self.set = self._validate_and_sweep + + #TODO(nulinspiratie) Move max_val_age to GetLatest + """ + max_val_age (Optional[int]): Only used with stepping, the max time + (in seconds) to trust a saved value. If this parameter has not + been set or measured more recently than this, it will be + measured before starting to step, so we're confident in the + value we're starting from. + + Raises: + TypeError: if max_val_age is not numeric + ValueError: if max_val_age is negative + """ + # if max_val_age is not None: + # if not isinstance(max_val_age, (int, float)): + # raise TypeError( + # 'max_val_age must be a number') + # if max_val_age < 0: + # raise ValueError('max_val_age must be non-negative') + # self._max_val_age = max_val_age @property def delay(self): From 9038a2cbaa4e1f70c341740d04a8597752c06bb9 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 11:32:16 +1000 Subject: [PATCH 04/88] Refactor: Delay -> Post_delay --- qcodes/instrument/parameter.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index b5e7a8d9a0e..d2a23b42eb3 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -762,9 +762,10 @@ class StandardParameter(Parameter): vals (Optional[Validator]): a Validator object for this parameter - delay (Optional[Union[int, float]]): time (in seconds) to wait after - the *start* of each set, whether part of a sweep or not. Can be - set to 0 to go maximum speed with no errors. + post_delay (Optional[Union[int, float]]): time (in seconds) to wait + after the *start* of each set, whether part of a sweep or not. + Can be set to 0 to go maximum speed with no errors. Any time + taken by the actual set operation is subtracted from the delay. step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. @@ -781,7 +782,7 @@ class StandardParameter(Parameter): def __init__(self, name, instrument=None, get_cmd=None, get_parser=None, set_cmd=None, set_parser=None, - delay=None, step=None, max_val_age=3600, + post_delay=None, step=None, max_val_age=3600, vals=None, val_mapping=None, **kwargs): # handle val_mapping before super init because it impacts # vals / validation in the base class @@ -819,7 +820,7 @@ def __init__(self, name, instrument=None, self._set_get(get_cmd, get_parser) self._set_set(set_cmd, set_parser) - self.delay = delay + self.post_delay = post_delay self.step = step # TODO(nulinspiratie) Create single set function @@ -1008,12 +1009,12 @@ def step(self, step): # self._max_val_age = max_val_age @property - def delay(self): + def post_delay(self): """Property that returns the delay time of this parameter""" - return self._delay + return self._post_delay - @delay.setter - def delay(self, delay): + @post_delay.setter + def post_delay(self, post_delay): """ Configure this parameter with a delay between set operations. @@ -1022,19 +1023,19 @@ def delay(self, delay): after every set. Args: - delay(Union[int, float]): the target time between set calls. The - actual time will not be shorter than this, but may be longer + post_delay(Union[int, float]): the target time between set calls. + The actual time will not be shorter than this, but may be longer if the underlying set call takes longer. Raises: TypeError: If delay is not int nor float ValueError: If delay is negative """ - if not isinstance(delay, (int, float)): + if not isinstance(post_delay, (int, float)): raise TypeError('delay must be a number') - if delay < 0: + if post_delay < 0: raise ValueError('delay must not be negative') - self._delay = delay + self._post_delay = post_delay class ManualParameter(Parameter): From 1720401b00e5dc129092c34378b4ee2e272494cb Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 11:46:26 +1000 Subject: [PATCH 05/88] Refactor: Merge _validate_and_set/sweep to set Set now handles both cases --- qcodes/instrument/parameter.py | 52 ++++++++++++---------------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index d2a23b42eb3..ea6678c070a 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -782,7 +782,7 @@ class StandardParameter(Parameter): def __init__(self, name, instrument=None, get_cmd=None, get_parser=None, set_cmd=None, set_parser=None, - post_delay=None, step=None, max_val_age=3600, + post_delay=0, step=None, max_val_age=3600, vals=None, val_mapping=None, **kwargs): # handle val_mapping before super init because it impacts # vals / validation in the base class @@ -823,12 +823,6 @@ def __init__(self, name, instrument=None, self.post_delay = post_delay self.step = step - # TODO(nulinspiratie) Create single set function - if self.step is None: - self.set = self._validate_and_set - else: - self.set = self._validate_and_sweep - if not (self.has_get or self.has_set): raise NoCommandError('neither set nor get cmd found in' + ' Parameter {}'.format(self.name)) @@ -887,20 +881,6 @@ def _set_set(self, set_cmd, set_parser): self.has_set = set_cmd is not None - def _validate_and_set(self, value): - try: - clock = time.perf_counter() - self.validate(value) - self._set(value) - self._save_val(value) - if self._delay is not None: - clock, remainder = self._update_set_ts(clock) - time.sleep(remainder) - except Exception as e: - e.args = e.args + ( - 'setting {} to {}'.format(self.full_name, repr(value)),) - raise e - def _sweep_steps(self, value): oldest_ok_val = datetime.now() - timedelta(seconds=self._max_val_age) state = self._latest() @@ -930,24 +910,28 @@ def _update_set_ts(self, step_clock): remainder = 0 return step_clock, remainder - def _validate_and_sweep(self, value): + def set(self, value): try: self.validate(value) - step_clock = time.perf_counter() - for step_val in self._sweep_steps(value): - self._set(step_val) - self._save_val(step_val) - if self._delay is not None: - step_clock, remainder = self._update_set_ts(step_clock) - time.sleep(remainder) + if self.step is not None: + # Multiple intermediate steps used to reach target value + sweep_vals = self._sweep_steps(value) + [value] + else: + # Immediately set to target value + sweep_vals = [value] - self._set(value) - self._save_val(value) + for sweep_val in sweep_vals: + t0 = time.perf_counter() + + self._set(sweep_val) + self._save_val(sweep_val) + + t_elapsed = time.perf_counter() - t0 + if t_elapsed < self.post_delay: + # Sleep until total time is larger than self.post_delay + time.sleep(self.post_delay - t_elapsed) - if self._delay is not None: - step_clock, remainder = self._update_set_ts(step_clock) - time.sleep(remainder) except Exception as e: e.args = e.args + ( 'setting {} to {}'.format(self.full_name, repr(value)),) From 842222e4ba2eb778251e917fe1aa74c64380236b Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 11:48:36 +1000 Subject: [PATCH 06/88] Refactor: rename _set_get/set to _initialize_get/set Naming was confusing --- qcodes/instrument/parameter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index ea6678c070a..a0a707cd3b1 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -818,8 +818,8 @@ def __init__(self, name, instrument=None, # TODO(nulinspiratie) Figure out why this is needed self._max_val_age = 0 - self._set_get(get_cmd, get_parser) - self._set_set(set_cmd, set_parser) + self._initialize_get(get_cmd, get_parser) + self._initialize_set(set_cmd, set_parser) self.post_delay = post_delay self.step = step @@ -862,7 +862,7 @@ def _valmapping_get_parser(self, val): def _valmapping_with_preparser(self, val): return self._valmapping_get_parser(self._get_preparser(val)) - def _set_get(self, get_cmd, get_parser): + def _initialize_get(self, get_cmd, get_parser): exec_str = self._instrument.ask if self._instrument else None self._get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, output_parser=get_parser, @@ -870,7 +870,7 @@ def _set_get(self, get_cmd, get_parser): self.has_get = (get_cmd is not None) - def _set_set(self, set_cmd, set_parser): + def _initialize_set(self, set_cmd, set_parser): # note: this does not set the final setter functions. that's handled # in self.set_sweep, when we choose a swept or non-swept setter. # TODO(giulioungaretti) lies! that method does not exis. From ecad7b6d8591d2d5ee3078bdfc2fc3b8845e2495 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 11:50:10 +1000 Subject: [PATCH 07/88] Refactor: remoed _update_last_ts Was unnecessary --- qcodes/instrument/parameter.py | 220 ++++++++++++++++----------------- 1 file changed, 107 insertions(+), 113 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index a0a707cd3b1..32d14d4480e 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -827,116 +827,6 @@ def __init__(self, name, instrument=None, raise NoCommandError('neither set nor get cmd found in' + ' Parameter {}'.format(self.name)) - def get(self): - try: - value = self._get() - self._save_val(value) - return value - except Exception as e: - e.args = e.args + ('getting {}'.format(self.full_name),) - raise e - - def _valmapping_get_parser(self, val): - """ - Get parser to be used in the case that a val_mapping is defined - and a separate get_parser is not defined. - - Tries to match against defined strings in the mapping dictionary. If - there are no matches, we try to convert the val into an integer. - """ - - # Try and match the raw value from the instrument directly - try: - return self._get_mapping[val] - except KeyError: - pass - - # If there is no match, we can try to convert the parameter into a - # numeric value - try: - val = int(val) - return self._get_mapping[val] - except (ValueError, KeyError): - raise KeyError('Unmapped value from instrument: {!r}'.format(val)) - - def _valmapping_with_preparser(self, val): - return self._valmapping_get_parser(self._get_preparser(val)) - - def _initialize_get(self, get_cmd, get_parser): - exec_str = self._instrument.ask if self._instrument else None - self._get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, - output_parser=get_parser, - no_cmd_function=no_getter) - - self.has_get = (get_cmd is not None) - - def _initialize_set(self, set_cmd, set_parser): - # note: this does not set the final setter functions. that's handled - # in self.set_sweep, when we choose a swept or non-swept setter. - # TODO(giulioungaretti) lies! that method does not exis. - # probably alexj left it out :( - exec_str = self._instrument.write if self._instrument else None - self._set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, - input_parser=set_parser, no_cmd_function=no_setter) - - self.has_set = set_cmd is not None - - def _sweep_steps(self, value): - oldest_ok_val = datetime.now() - timedelta(seconds=self._max_val_age) - state = self._latest() - if state['ts'] is None or state['ts'] < oldest_ok_val: - start_value = self.get() - else: - start_value = state['value'] - - self.validate(start_value) - - if not (isinstance(start_value, (int, float)) and - isinstance(value, (int, float))): - # something weird... parameter is numeric but one of the ends - # isn't, even though it's valid. - # probably a MultiType with a mix of numeric and non-numeric types - # just set the endpoint and move on - logging.warning('cannot sweep {} from {} to {} - jumping.'.format( - self.name, start_value, value)) - return [] - - # drop the initial value, we're already there - return permissive_range(start_value, value, self._step)[1:] - - def _update_set_ts(self, step_clock): - # TODO(nulinspiratie) remove function alltogether - step_clock = time.perf_counter() - remainder = 0 - return step_clock, remainder - - def set(self, value): - try: - self.validate(value) - - if self.step is not None: - # Multiple intermediate steps used to reach target value - sweep_vals = self._sweep_steps(value) + [value] - else: - # Immediately set to target value - sweep_vals = [value] - - for sweep_val in sweep_vals: - t0 = time.perf_counter() - - self._set(sweep_val) - self._save_val(sweep_val) - - t_elapsed = time.perf_counter() - t0 - if t_elapsed < self.post_delay: - # Sleep until total time is larger than self.post_delay - time.sleep(self.post_delay - t_elapsed) - - except Exception as e: - e.args = e.args + ( - 'setting {} to {}'.format(self.full_name, repr(value)),) - raise e - @property def step(self): return self._step @@ -953,7 +843,7 @@ def step(self, step): step (Union[int, float]): A positive number, the largest change allowed in one call. All but the final change will attempt to change by +/- step exactly - + Raises: TypeError: if step is not numeric ValueError: if step is negative @@ -965,14 +855,14 @@ def step(self, step): elif step <= 0: raise ValueError('step must be positive') elif (isinstance(self._vals, Ints) and - not isinstance(step, int)): + not isinstance(step, int)): raise TypeError('step must be a positive int for an Ints parameter') elif not isinstance(step, (int, float)): raise TypeError('step must be a number') else: self._step = step - #TODO(nulinspiratie) Move max_val_age to GetLatest + # TODO(nulinspiratie) Move max_val_age to GetLatest """ max_val_age (Optional[int]): Only used with stepping, the max time (in seconds) to trust a saved value. If this parameter has not @@ -1021,6 +911,110 @@ def post_delay(self, post_delay): raise ValueError('delay must not be negative') self._post_delay = post_delay + def get(self): + try: + value = self._get() + self._save_val(value) + return value + except Exception as e: + e.args = e.args + ('getting {}'.format(self.full_name),) + raise e + + def set(self, value): + try: + self.validate(value) + + if self.step is not None: + # Multiple intermediate steps used to reach target value + sweep_vals = self._sweep_steps(value) + [value] + else: + # Immediately set to target value + sweep_vals = [value] + + for sweep_val in sweep_vals: + t0 = time.perf_counter() + + self._set(sweep_val) + self._save_val(sweep_val) + + t_elapsed = time.perf_counter() - t0 + if t_elapsed < self.post_delay: + # Sleep until total time is larger than self.post_delay + time.sleep(self.post_delay - t_elapsed) + + except Exception as e: + e.args = e.args + ( + 'setting {} to {}'.format(self.full_name, repr(value)),) + raise e + + def _valmapping_get_parser(self, val): + """ + Get parser to be used in the case that a val_mapping is defined + and a separate get_parser is not defined. + + Tries to match against defined strings in the mapping dictionary. If + there are no matches, we try to convert the val into an integer. + """ + + # Try and match the raw value from the instrument directly + try: + return self._get_mapping[val] + except KeyError: + pass + + # If there is no match, we can try to convert the parameter into a + # numeric value + try: + val = int(val) + return self._get_mapping[val] + except (ValueError, KeyError): + raise KeyError('Unmapped value from instrument: {!r}'.format(val)) + + def _valmapping_with_preparser(self, val): + return self._valmapping_get_parser(self._get_preparser(val)) + + def _initialize_get(self, get_cmd, get_parser): + exec_str = self._instrument.ask if self._instrument else None + self._get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, + output_parser=get_parser, + no_cmd_function=no_getter) + + self.has_get = (get_cmd is not None) + + def _initialize_set(self, set_cmd, set_parser): + # note: this does not set the final setter functions. that's handled + # in self.set_sweep, when we choose a swept or non-swept setter. + # TODO(giulioungaretti) lies! that method does not exis. + # probably alexj left it out :( + exec_str = self._instrument.write if self._instrument else None + self._set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, + input_parser=set_parser, no_cmd_function=no_setter) + + self.has_set = set_cmd is not None + + def _sweep_steps(self, value): + oldest_ok_val = datetime.now() - timedelta(seconds=self._max_val_age) + state = self._latest() + if state['ts'] is None or state['ts'] < oldest_ok_val: + start_value = self.get() + else: + start_value = state['value'] + + self.validate(start_value) + + if not (isinstance(start_value, (int, float)) and + isinstance(value, (int, float))): + # something weird... parameter is numeric but one of the ends + # isn't, even though it's valid. + # probably a MultiType with a mix of numeric and non-numeric types + # just set the endpoint and move on + logging.warning('cannot sweep {} from {} to {} - jumping.'.format( + self.name, start_value, value)) + return [] + + # drop the initial value, we're already there + return permissive_range(start_value, value, self._step)[1:] + class ManualParameter(Parameter): """ From 5e8eed0e7c6c808fcc338d41bd0a6fb2b5f9da99 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 16 May 2017 16:10:07 +1000 Subject: [PATCH 08/88] Refactor: Moved max_val_age to base_parameter and GetLatest() Max val age is now always checked when get_latest is asked. A consequence is that parameter.get_latest will perform a get() if the last get was performed more than max_val_age ago. --- qcodes/instrument/parameter.py | 66 +++++++++++++++------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 32d14d4480e..95e450935ea 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -93,12 +93,17 @@ class _BaseParameter(Metadatable, DeferredOperations): parameter during a snapshot, even if the snapshot was called with ``update=True``, for example if it takes too long to update. Default True. + + max_val_age (Optional[int]): The max time (in seconds) to trust a + saved value obtained from get_latest(). If this parameter has not + been set or measured more recently than this, perform an + additional measurement. metadata (Optional[dict]): extra information to include with the JSON snapshot of the parameter """ def __init__(self, name, instrument, snapshot_get, metadata, - snapshot_value=True): + snapshot_value=True, max_val_age=None): super().__init__(metadata) self._snapshot_get = snapshot_get self.name = str(name) @@ -117,7 +122,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, # but they all use the same attributes so snapshot is consistent. self._latest_value = None self._latest_ts = None - self.get_latest = GetLatest(self) + self.get_latest = GetLatest(self, max_val_age=max_val_age) # subclasses should extend this list with extra attributes they # want automatically included in the snapshot @@ -782,7 +787,7 @@ class StandardParameter(Parameter): def __init__(self, name, instrument=None, get_cmd=None, get_parser=None, set_cmd=None, set_parser=None, - post_delay=0, step=None, max_val_age=3600, + post_delay=0, step=None, vals=None, val_mapping=None, **kwargs): # handle val_mapping before super init because it impacts # vals / validation in the base class @@ -812,12 +817,6 @@ def __init__(self, name, instrument=None, self._meta_attrs.extend(['sweep_step', 'sweep_delay', 'max_sweep_delay']) - # stored value from last .set() or .get() - # normally only used by set with a sweep, to avoid - # having to call .get() for every .set() - # TODO(nulinspiratie) Figure out why this is needed - self._max_val_age = 0 - self._initialize_get(get_cmd, get_parser) self._initialize_set(set_cmd, set_parser) self.post_delay = post_delay @@ -862,26 +861,6 @@ def step(self, step): else: self._step = step - # TODO(nulinspiratie) Move max_val_age to GetLatest - """ - max_val_age (Optional[int]): Only used with stepping, the max time - (in seconds) to trust a saved value. If this parameter has not - been set or measured more recently than this, it will be - measured before starting to step, so we're confident in the - value we're starting from. - - Raises: - TypeError: if max_val_age is not numeric - ValueError: if max_val_age is negative - """ - # if max_val_age is not None: - # if not isinstance(max_val_age, (int, float)): - # raise TypeError( - # 'max_val_age must be a number') - # if max_val_age < 0: - # raise ValueError('max_val_age must be non-negative') - # self._max_val_age = max_val_age - @property def post_delay(self): """Property that returns the delay time of this parameter""" @@ -993,12 +972,7 @@ def _initialize_set(self, set_cmd, set_parser): self.has_set = set_cmd is not None def _sweep_steps(self, value): - oldest_ok_val = datetime.now() - timedelta(seconds=self._max_val_age) - state = self._latest() - if state['ts'] is None or state['ts'] < oldest_ok_val: - start_value = self.get() - else: - start_value = state['value'] + start_value = self.get_latest() self.validate(start_value) @@ -1068,16 +1042,32 @@ class GetLatest(DelegateAttributes, DeferredOperations): Args: parameter (Parameter): Parameter to be wrapped + + max_val_age (Optional[int]): The max time (in seconds) to trust a + saved value obtained from get_latest(). If this parameter has not + been set or measured more recently than this, perform an + additional measurement. """ - def __init__(self, parameter): + def __init__(self, parameter, max_val_age=None): self.parameter = parameter + self.max_val_age = max_val_age delegate_attr_objects = ['parameter'] omit_delegate_attrs = ['set'] def get(self): - """ Return latest value""" - return self.parameter._latest()['value'] + """Return latest value if time since get was less than + `self.max_val_age`, otherwise perform `get()` and return result"""" + state = self.parameter._latest() + if self.max_val_age is None: + # Return last value since max_val_age is not specified + return state + else: + oldest_ok_val = datetime.now() - timedelta(seconds=self.max_val_age) + if state['ts'] is None or state['ts'] < oldest_ok_val: + return self.get() + else: + return state['value'] def __call__(self): return self.get() From acc479dc07c4f40ee467005a47f0ce4277be66c6 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 21 Jun 2017 13:07:02 +1000 Subject: [PATCH 09/88] fix: Removed unnecessary quote --- qcodes/instrument/parameter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 95e450935ea..99e636b12fb 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1057,7 +1057,8 @@ def __init__(self, parameter, max_val_age=None): def get(self): """Return latest value if time since get was less than - `self.max_val_age`, otherwise perform `get()` and return result"""" + `self.max_val_age`, otherwise perform `get()` and return result + """ state = self.parameter._latest() if self.max_val_age is None: # Return last value since max_val_age is not specified From edf08034ce98a4148dcbcc1549690fb88ab8b16d Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 16:10:17 +1000 Subject: [PATCH 10/88] refactor: rename _get/_set to _get_command/_set_command --- qcodes/instrument/parameter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index f3cfc06e2e0..58d4133dae7 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -892,7 +892,7 @@ def post_delay(self, post_delay): def get(self): try: - value = self._get() + value = self._get_command() self._save_val(value) return value except Exception as e: @@ -954,9 +954,9 @@ def _valmapping_with_preparser(self, val): def _initialize_get(self, get_cmd, get_parser): exec_str = self._instrument.ask if self._instrument else None - self._get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, - output_parser=get_parser, - no_cmd_function=no_getter) + self._get_command = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, + output_parser=get_parser, + no_cmd_function=no_getter) self.has_get = (get_cmd is not None) From 2cfc83999ad6c60412d74d5ff751291dfbbb3263 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 16:26:07 +1000 Subject: [PATCH 11/88] fix: GetLatest now performs get on parameter --- qcodes/instrument/parameter.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 58d4133dae7..a0aa2d8777f 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -287,7 +287,7 @@ class Parameter(_BaseParameter): JSON snapshot of the parameter """ def __init__(self, name, instrument=None, label=None, - unit=None, units=None, vals=None, docstring=None, + unit=None, units=None, vals=None, docstring=None, snapshot_get=True, snapshot_value=True, metadata=None): super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) @@ -1066,7 +1066,9 @@ def get(self): else: oldest_ok_val = datetime.now() - timedelta(seconds=self.max_val_age) if state['ts'] is None or state['ts'] < oldest_ok_val: - return self.get() + # Time of last get exceeds max_val_age seconds, need to + # perform new .get() + return self.parameter.get() else: return state['value'] From 7b72b94eb04dffc22f69a89b92bd69411b22c4f8 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 17:37:50 +1000 Subject: [PATCH 12/88] refactor: removed has_set/has_get --- qcodes/instrument/parameter.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index a0aa2d8777f..fd4d27cc69d 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -110,13 +110,6 @@ def __init__(self, name, instrument, snapshot_get, metadata, self._instrument = instrument self._snapshot_value = snapshot_value - self.has_get = hasattr(self, 'get') - self.has_set = hasattr(self, 'set') - - if not (self.has_get or self.has_set): - raise AttributeError('A parameter must have either a get or a ' - 'set method, or both.') - # record of latest value and when it was set or measured # what exactly this means is different for different subclasses # but they all use the same attributes so snapshot is consistent. @@ -133,18 +126,19 @@ def __repr__(self): def __call__(self, *args): if len(args) == 0: - if self.has_get: + if hasattr(self, 'get'): return self.get() else: raise NotImplementedError('no get cmd found in' + ' Parameter {}'.format(self.name)) else: - if self.has_set: + if hasattr(self, 'set'): self.set(*args) else: raise NotImplementedError('no set cmd found in' + ' Parameter {}'.format(self.name)) + def _latest(self): return { 'value': self._latest_value, @@ -189,8 +183,8 @@ def snapshot_base(self, update=False): dict: base snapshot """ - if self.has_get and self._snapshot_get and self._snapshot_value and \ - update: + if hasattr(self, 'get') and self._snapshot_get \ + and self._snapshot_value and update: self.get() state = self._latest() @@ -287,13 +281,17 @@ class Parameter(_BaseParameter): JSON snapshot of the parameter """ def __init__(self, name, instrument=None, label=None, - unit=None, units=None, vals=None, docstring=None, + get_cmd=None, set_cmd=False, + unit=None, units=None, vals=None, docstring=None, snapshot_get=True, snapshot_value=True, metadata=None): super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) self._meta_attrs.extend(['label', 'unit', '_vals']) + + + self.label = name if label is None else label if units is not None: @@ -468,7 +466,7 @@ def __init__(self, name, shape, instrument=None, super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) - if self.has_set: # TODO (alexcjohnson): can we support, ala Combine? + if hasattr(self, 'set'): # TODO (alexcjohnson): can we support, ala Combine? raise AttributeError('ArrayParameters do not support set ' 'at this time.') @@ -635,7 +633,7 @@ def __init__(self, name, names, shapes, instrument=None, super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) - if self.has_set: # TODO (alexcjohnson): can we support, ala Combine? + if hasattr(self, 'set'): # TODO (alexcjohnson): can we support, ala Combine? warnings.warn('MultiParameters do not fully support set ' 'at this time.') @@ -822,7 +820,7 @@ def __init__(self, name, instrument=None, self.post_delay = post_delay self.step = step - if not (self.has_get or self.has_set): + if not (hasattr(self, 'get') or hasattr(self, 'set')): raise NoCommandError('neither set nor get cmd found in' + ' Parameter {}'.format(self.name)) @@ -958,8 +956,6 @@ def _initialize_get(self, get_cmd, get_parser): output_parser=get_parser, no_cmd_function=no_getter) - self.has_get = (get_cmd is not None) - def _initialize_set(self, set_cmd, set_parser): # note: this does not set the final setter functions. that's handled # in self.set_sweep, when we choose a swept or non-swept setter. @@ -969,8 +965,6 @@ def _initialize_set(self, set_cmd, set_parser): self._set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, input_parser=set_parser, no_cmd_function=no_setter) - self.has_set = set_cmd is not None - def _sweep_steps(self, value): start_value = self.get_latest() From 2df5771a670dd5ef6d9d914568b3869afa627a6c Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 18:02:14 +1000 Subject: [PATCH 13/88] Add get/set wrappers --- qcodes/instrument/parameter.py | 75 ++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index fd4d27cc69d..be95238e22f 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -58,6 +58,7 @@ import os import collections import warnings +import functools import numpy @@ -117,6 +118,11 @@ def __init__(self, name, instrument, snapshot_get, metadata, self._latest_ts = None self.get_latest = GetLatest(self, max_val_age=max_val_age) + if hasattr(self, 'get'): + self.get = self._get_wrapper(self.get) + if hasattr(self, 'set'): + self.set = self._set_wrapper(self.set) + # subclasses should extend this list with extra attributes they # want automatically included in the snapshot self._meta_attrs = ['name', 'instrument'] @@ -217,6 +223,72 @@ def _save_val(self, value): self._latest_value = value self._latest_ts = datetime.now() + def _get_wrapper(self, get_function): + @functools.wraps + def get_wrapper(*args, **kwargs): + try: + # There might be cases where a .get also has args/kwargs + value = get_function(*args, **kwargs) + self._save_val(value) + return value + except Exception as e: + e.args = e.args + ('getting {}'.format(self.full_name),) + raise e + + return get_wrapper + + def _set_wrapper(self, set_function): + @functools.wraps + def set_wrapper(*values, **kwargs): + try: + self.validate(*values) + + # In some cases intermediate sweep values must be used. + # Unless `self.step` is defined, get_sweep_values will return + # a list containing only `value`. + for val in self.get_sweep_values(*values): + + set_function(val, **kwargs) + self._save_val(val) + except Exception as e: + e.args = e.args + ('setting {} to {}'.format(self.full_name, + values),) + raise e + + return set_wrapper + + def get_sweep_values(self, value): + """ + Sweep to a given value from a starting value + This method can be overridden to have a custom sweep behaviour. + It can even be overridden by a generator. + Args: + value: + + Returns: + + """ + if self.step is None: + return [value] + else: + start_value = self.get_latest() + + self.validate(start_value) + + if not (isinstance(start_value, (int, float)) and + isinstance(value, (int, float))): + # something weird... parameter is numeric but one of the ends + # isn't, even though it's valid. + # probably a MultiType with a mix of numeric and non-numeric types + # just set the endpoint and move on + logging.warning('cannot sweep {} from {} to {} - jumping.'.format( + self.name, start_value, value)) + return [] + + # drop the initial value, we're already there + return permissive_range(start_value, value, self._step)[1:] + + @property def full_name(self): """Include the instrument name with the Parameter name if possible.""" @@ -893,9 +965,6 @@ def get(self): value = self._get_command() self._save_val(value) return value - except Exception as e: - e.args = e.args + ('getting {}'.format(self.full_name),) - raise e def set(self, value): try: From 794af7066919696c0de7332129499935765530d6 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 18:03:28 +1000 Subject: [PATCH 14/88] feat: add kwargs to set from call() --- qcodes/instrument/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index be95238e22f..ceceedc9dba 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -130,7 +130,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, def __repr__(self): return named_repr(self) - def __call__(self, *args): + def __call__(self, *args, **kwargs): if len(args) == 0: if hasattr(self, 'get'): return self.get() @@ -139,7 +139,7 @@ def __call__(self, *args): ' Parameter {}'.format(self.name)) else: if hasattr(self, 'set'): - self.set(*args) + self.set(*args, **kwargs) else: raise NotImplementedError('no set cmd found in' + ' Parameter {}'.format(self.name)) From b5c91f0fd40f8b143a1e1407d743c0f463d8c17a Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 18:09:20 +1000 Subject: [PATCH 15/88] refactor: move step to base_parameter, re-add exception to StandardParameter.get() Accidentally removed exception in a previous commit --- qcodes/instrument/parameter.py | 75 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index ceceedc9dba..e52e7f99167 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -288,7 +288,6 @@ def get_sweep_values(self, value): # drop the initial value, we're already there return permissive_range(start_value, value, self._step)[1:] - @property def full_name(self): """Include the instrument name with the Parameter name if possible.""" @@ -301,6 +300,40 @@ def full_name(self): return self.name + @property + def step(self): + return self._step + + @step.setter + def step(self, step): + """ + Configure whether this Parameter uses steps during set operations. + If step is a positive number, this is the maximum value change + allowed in one hardware call, so a single set can result in many + calls to the hardware if the starting value is far from the target. + + Args: + step (Union[int, float]): A positive number, the largest change + allowed in one call. All but the final change will attempt to + change by +/- step exactly + + Raises: + TypeError: if step is not numeric + ValueError: if step is negative + TypeError: if step is not integer for an integer parameter + TypeError: if step is not a number + """ + if not self._vals.is_numeric: + raise TypeError('you can only step numeric parameters') + elif not isinstance(step, (int, float)): + raise TypeError('step must be a number') + elif step <= 0: + raise ValueError('step must be positive') + elif (isinstance(self._vals, Ints) and not isinstance(step, int)): + raise TypeError('step must be a positive int for an Ints parameter') + else: + self._step = step + class Parameter(_BaseParameter): """ @@ -896,41 +929,6 @@ def __init__(self, name, instrument=None, raise NoCommandError('neither set nor get cmd found in' + ' Parameter {}'.format(self.name)) - @property - def step(self): - return self._step - - @step.setter - def step(self, step): - """ - Configure whether this Parameter uses steps during set operations. - If step is a positive number, this is the maximum value change - allowed in one hardware call, so a single set can result in many - calls to the hardware if the starting value is far from the target. - - Args: - step (Union[int, float]): A positive number, the largest change - allowed in one call. All but the final change will attempt to - change by +/- step exactly - - Raises: - TypeError: if step is not numeric - ValueError: if step is negative - TypeError: if step is not integer for an integer parameter - TypeError: if step is not a number - """ - if not self._vals.is_numeric: - raise TypeError('you can only step numeric parameters') - elif step <= 0: - raise ValueError('step must be positive') - elif (isinstance(self._vals, Ints) and - not isinstance(step, int)): - raise TypeError('step must be a positive int for an Ints parameter') - elif not isinstance(step, (int, float)): - raise TypeError('step must be a number') - else: - self._step = step - @property def post_delay(self): """Property that returns the delay time of this parameter""" @@ -946,7 +944,7 @@ def post_delay(self, post_delay): after every set. Args: - post_delay(Union[int, float]): the target time between set calls. + post_delay(Union[int, float]): the target time between set calls. The actual time will not be shorter than this, but may be longer if the underlying set call takes longer. @@ -965,6 +963,9 @@ def get(self): value = self._get_command() self._save_val(value) return value + except Exception as e: + e.args = e.args + ('getting {}'.format(self.full_name),) + raise e def set(self, value): try: From 0642eaf7a54a90afc3a04da8e046a6b879cc9b39 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 19:29:40 +1000 Subject: [PATCH 16/88] feat: Added inter_delay, post_delay Still need to update docstrings --- qcodes/instrument/parameter.py | 118 ++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 31 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index e52e7f99167..486430d4614 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -104,6 +104,7 @@ class _BaseParameter(Metadatable, DeferredOperations): JSON snapshot of the parameter """ def __init__(self, name, instrument, snapshot_get, metadata, + step=None, inter_delay=0, post_delay=0, snapshot_value=True, max_val_age=None): super().__init__(metadata) self._snapshot_get = snapshot_get @@ -111,6 +112,10 @@ def __init__(self, name, instrument, snapshot_get, metadata, self._instrument = instrument self._snapshot_value = snapshot_value + self.step = step + self.inter_delay = inter_delay + self.post_delay = post_delay + # record of latest value and when it was set or measured # what exactly this means is different for different subclasses # but they all use the same attributes so snapshot is consistent. @@ -127,6 +132,10 @@ def __init__(self, name, instrument, snapshot_get, metadata, # want automatically included in the snapshot self._meta_attrs = ['name', 'instrument'] + # Specify time of last set operation, used when comparing to delay to + # check if additional waiting time is needed before next set + self._t_last_set = time.perf_counter() + def __repr__(self): return named_repr(self) @@ -248,8 +257,27 @@ def set_wrapper(*values, **kwargs): # a list containing only `value`. for val in self.get_sweep_values(*values): + # Check if delay between set operations is required + t_elapsed = time.perf_counter() - self._t_last_set + if t_elapsed < self.inter_delay: + # Sleep until time since last set is larger than + # self.post_delay + time.sleep(self.inter_delay - t_elapsed) + + # Start timer to measure execution time of set_function + t0 = time.perf_counter() + set_function(val, **kwargs) self._save_val(val) + + # Update last set time (used for calculating delays) + self._t_last_set = time.perf_counter() + + # Check if any delay after setting is required + t_elapsed = self._t_last_set - t0 + if t_elapsed < self.post_delay: + # Sleep until total time is larger than self.post_delay + time.sleep(self.post_delay - t_elapsed) except Exception as e: e.args = e.args + ('setting {} to {}'.format(self.full_name, values),) @@ -334,6 +362,64 @@ def step(self, step): else: self._step = step + @property + def post_delay(self): + """Property that returns the delay time of this parameter""" + return self._post_delay + + @post_delay.setter + def post_delay(self, post_delay): + """ + Configure this parameter with a delay between set operations. + + Typically used in conjunction with set_step to create an effective + ramp rate, but can also be used without a step to enforce a delay + after every set. + + Args: + post_delay(Union[int, float]): the target time between set calls. + The actual time will not be shorter than this, but may be longer + if the underlying set call takes longer. + + Raises: + TypeError: If delay is not int nor float + ValueError: If delay is negative + """ + if not isinstance(post_delay, (int, float)): + raise TypeError('delay must be a number') + if post_delay < 0: + raise ValueError('delay must not be negative') + self._post_delay = post_delay + + @property + def inter_delay(self): + """Property that returns the delay time of this parameter""" + return self._inter_delay + + @inter_delay.setter + def inter_delay(self, inter_delay): + """ + Configure this parameter with a delay between set operations. + + Typically used in conjunction with set_step to create an effective + ramp rate, but can also be used without a step to enforce a delay + between sets. + + Args: + inter_delay(Union[int, float]): the target time between set calls. + The actual time will not be shorter than this, but may be longer + if the underlying set call takes longer. + + Raises: + TypeError: If delay is not int nor float + ValueError: If delay is negative + """ + if not isinstance(inter_delay, (int, float)): + raise TypeError('delay must be a number') + if inter_delay < 0: + raise ValueError('delay must not be negative') + self._inter_delay = inter_delay + class Parameter(_BaseParameter): """ @@ -890,7 +976,6 @@ class StandardParameter(Parameter): def __init__(self, name, instrument=None, get_cmd=None, get_parser=None, set_cmd=None, set_parser=None, - post_delay=0, step=None, vals=None, val_mapping=None, **kwargs): # handle val_mapping before super init because it impacts # vals / validation in the base class @@ -922,41 +1007,11 @@ def __init__(self, name, instrument=None, self._initialize_get(get_cmd, get_parser) self._initialize_set(set_cmd, set_parser) - self.post_delay = post_delay - self.step = step if not (hasattr(self, 'get') or hasattr(self, 'set')): raise NoCommandError('neither set nor get cmd found in' + ' Parameter {}'.format(self.name)) - @property - def post_delay(self): - """Property that returns the delay time of this parameter""" - return self._post_delay - - @post_delay.setter - def post_delay(self, post_delay): - """ - Configure this parameter with a delay between set operations. - - Typically used in conjunction with set_step to create an effective - ramp rate, but can also be used without a step to enforce a delay - after every set. - - Args: - post_delay(Union[int, float]): the target time between set calls. - The actual time will not be shorter than this, but may be longer - if the underlying set call takes longer. - - Raises: - TypeError: If delay is not int nor float - ValueError: If delay is negative - """ - if not isinstance(post_delay, (int, float)): - raise TypeError('delay must be a number') - if post_delay < 0: - raise ValueError('delay must not be negative') - self._post_delay = post_delay def get(self): try: @@ -984,6 +1039,7 @@ def set(self, value): self._set(sweep_val) self._save_val(sweep_val) + # Check if any delay after setting is required t_elapsed = time.perf_counter() - t0 if t_elapsed < self.post_delay: # Sleep until total time is larger than self.post_delay From e4d8f273a7d00a9dfc89e84a7af0b65976d4ad78 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 19:54:08 +1000 Subject: [PATCH 17/88] refactor: moved _initialize_get/set to parameter init, changed _vals to vals --- qcodes/instrument/parameter.py | 40 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 486430d4614..4807389aeba 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -472,17 +472,32 @@ class Parameter(_BaseParameter): JSON snapshot of the parameter """ def __init__(self, name, instrument=None, label=None, - get_cmd=None, set_cmd=False, - unit=None, units=None, vals=None, docstring=None, + get_cmd=None, get_parser=None, + set_cmd=False, set_parser=None, + unit=None, units=None, vals=Numbers(), docstring=None, snapshot_get=True, snapshot_value=True, metadata=None): + + # Enable set/get methods if get_cmd/set_cmd is given + # Called first so super().__init__ can wrap get/set methods + if not hasattr(self, 'get') and get_cmd is not False: + self.get = Command(arg_count=0, + cmd=get_cmd, + exec_str=instrument.ask if instrument else None, + output_parser=get_parser, + no_cmd_function=no_getter) + if not hasattr(self, 'set') and set_cmd is not False: + self.set = Command(arg_count=1, + cmd=set_cmd, + exec_str=instrument.ask if instrument else None, + input_parser=set_parser, + no_cmd_function=no_setter) + super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) self._meta_attrs.extend(['label', 'unit', '_vals']) - - self.label = name if label is None else label if units is not None: @@ -500,7 +515,7 @@ def __init__(self, name, instrument=None, label=None, '* `name` %s' % self.name, '* `label` %s' % self.label, '* `unit` %s' % self.unit, - '* `vals` %s' % repr(self._vals))) + '* `vals` %s' % repr(self.vals))) if docstring is not None: self.__doc__ = os.linesep.join(( @@ -515,12 +530,11 @@ def set_validator(self, vals): Args: vals (Validator): validator to set """ - if vals is None: - self._vals = Numbers() - elif isinstance(vals, Validator): - self._vals = vals - else: + + if not isinstance(vals, Validator): raise TypeError('vals must be a Validator') + else: + self.vals = vals def validate(self, value): """ @@ -536,7 +550,7 @@ def validate(self, value): else: context = self.name - self._vals.validate(value, 'Parameter: ' + context) + self.vals.validate(value, 'Parameter: ' + context) def sweep(self, start, stop, step=None, num=None): """ @@ -1396,8 +1410,8 @@ def set_validator(self, vals): vals (Validator): validator to set """ if vals is None: - self._vals = Strings() + self.vals = Strings() elif isinstance(vals, Validator): - self._vals = vals + self.vals = vals else: raise TypeError('vals must be a Validator') From 73d11138767f6c94b86a7cc41cc1730d0e09566e Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 19:56:49 +1000 Subject: [PATCH 18/88] refactor: Removed set_validator --- qcodes/instrument/parameter.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 4807389aeba..12083f08f30 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -497,7 +497,6 @@ def __init__(self, name, instrument=None, label=None, self._meta_attrs.extend(['label', 'unit', '_vals']) - self.label = name if label is None else label if units is not None: @@ -506,7 +505,9 @@ def __init__(self, name, instrument=None, label=None, unit = units self.unit = unit if unit is not None else '' - self.set_validator(vals) + if not isinstance(vals, Validator): + raise TypeError('vals must be a Validator') + self.vals = vals # generate default docstring self.__doc__ = os.linesep.join(( @@ -523,19 +524,6 @@ def __init__(self, name, instrument=None, label=None, '', self.__doc__)) - def set_validator(self, vals): - """ - Set a validator `vals` for this parameter. - - Args: - vals (Validator): validator to set - """ - - if not isinstance(vals, Validator): - raise TypeError('vals must be a Validator') - else: - self.vals = vals - def validate(self, value): """ Validate value From 74c40e533b48b1fb9847d52c50e0eb4ebb3f23cc Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 20:14:20 +1000 Subject: [PATCH 19/88] refactor: validate to BaseParameter, get/set_cmd also handle None Other parameters, such as MultiParameter, may also need some sort of validation. Will see in the future if validate may instead be an abstract base method that should be overridden When get_cmd is None and .get is not set, it will be equal to get_latest When set_cmd is None and .set is not set, it will be equal to saving the value. This behaviour is equal to the ManualParameter --- qcodes/instrument/parameter.py | 80 ++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 12083f08f30..b242ade8ac8 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -58,8 +58,7 @@ import os import collections import warnings -import functools - +from functools import partial, wraps import numpy from qcodes.utils.deferred_operations import DeferredOperations @@ -153,7 +152,6 @@ def __call__(self, *args, **kwargs): raise NotImplementedError('no set cmd found in' + ' Parameter {}'.format(self.name)) - def _latest(self): return { 'value': self._latest_value, @@ -228,12 +226,14 @@ def snapshot_base(self, update=False): return state - def _save_val(self, value): + def _save_val(self, value, validate=False): + if validate: + self.validate(value) self._latest_value = value self._latest_ts = datetime.now() def _get_wrapper(self, get_function): - @functools.wraps + @wraps def get_wrapper(*args, **kwargs): try: # There might be cases where a .get also has args/kwargs @@ -247,7 +247,7 @@ def get_wrapper(*args, **kwargs): return get_wrapper def _set_wrapper(self, set_function): - @functools.wraps + @wraps def set_wrapper(*values, **kwargs): try: self.validate(*values) @@ -316,6 +316,22 @@ def get_sweep_values(self, value): # drop the initial value, we're already there return permissive_range(start_value, value, self._step)[1:] + def validate(self, value): + """ + Validate value + + Args: + value (any): value to validate + + """ + if self._instrument: + context = (getattr(self._instrument, 'name', '') or + str(self._instrument.__class__)) + '.' + self.name + else: + context = self.name + + self.vals.validate(value, 'Parameter: ' + context) + @property def full_name(self): """Include the instrument name with the Parameter name if possible.""" @@ -480,17 +496,23 @@ def __init__(self, name, instrument=None, label=None, # Enable set/get methods if get_cmd/set_cmd is given # Called first so super().__init__ can wrap get/set methods if not hasattr(self, 'get') and get_cmd is not False: - self.get = Command(arg_count=0, - cmd=get_cmd, - exec_str=instrument.ask if instrument else None, - output_parser=get_parser, - no_cmd_function=no_getter) + if get_cmd is None: + # TODO(nulinspiratie) handle if max_val_age is set + self.get = self.get_latest + else: + exec_str = instrument.ask if instrument else None + self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, + output_parser=get_parser, + no_cmd_function=no_getter) + if not hasattr(self, 'set') and set_cmd is not False: - self.set = Command(arg_count=1, - cmd=set_cmd, - exec_str=instrument.ask if instrument else None, - input_parser=set_parser, - no_cmd_function=no_setter) + if set_cmd is None: + self.set = partial(self._save_val, validate=True) + else: + exec_str = instrument.ask if instrument else None + self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, + input_parser=set_parser, + no_cmd_function=no_setter) super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) @@ -524,21 +546,12 @@ def __init__(self, name, instrument=None, label=None, '', self.__doc__)) - def validate(self, value): + def __getitem__(self, keys): """ - Validate value - - Args: - value (any): value to validate - + Slice a Parameter to get a SweepValues object + to iterate over during a sweep """ - if self._instrument: - context = (getattr(self._instrument, 'name', '') or - str(self._instrument.__class__)) + '.' + self.name - else: - context = self.name - - self.vals.validate(value, 'Parameter: ' + context) + return SweepFixedValues(self, keys) def sweep(self, start, stop, step=None, num=None): """ @@ -567,13 +580,6 @@ def sweep(self, start, stop, step=None, num=None): return SweepFixedValues(self, start=start, stop=stop, step=step, num=num) - def __getitem__(self, keys): - """ - Slice a Parameter to get a SweepValues object - to iterate over during a sweep - """ - return SweepFixedValues(self, keys) - @property def units(self): warn_units('Parameter', self) @@ -1128,7 +1134,7 @@ class ManualParameter(Parameter): **kwargs: Passed to Parameter parent class """ - def __init__(self, name, instrument=None, initial_value=None, **kwargs): + def __init__(self, name, instrument=None, **kwargs): super().__init__(name=name, instrument=instrument, **kwargs) self._meta_attrs.extend(['initial_value']) From d766fbd8531b0b9089e17c89096775d1e7ab0a8b Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 20:16:10 +1000 Subject: [PATCH 20/88] move initial_val to Parameter, remove ManualParameter --- qcodes/instrument/parameter.py | 43 ++++------------------------------ 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index b242ade8ac8..f4d1222bd2b 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -490,6 +490,7 @@ class Parameter(_BaseParameter): def __init__(self, name, instrument=None, label=None, get_cmd=None, get_parser=None, set_cmd=False, set_parser=None, + initial_value=None, unit=None, units=None, vals=Numbers(), docstring=None, snapshot_get=True, snapshot_value=True, metadata=None): @@ -531,6 +532,9 @@ def __init__(self, name, instrument=None, label=None, raise TypeError('vals must be a Validator') self.vals = vals + if initial_value is not None: + self._save_val(initial_value, validate=True) + # generate default docstring self.__doc__ = os.linesep.join(( 'Parameter class:', @@ -1118,45 +1122,6 @@ def _sweep_steps(self, value): return permissive_range(start_value, value, self._step)[1:] -class ManualParameter(Parameter): - """ - Define one parameter that reflects a manual setting / configuration. - - Args: - name (str): the local name of this parameter - - instrument (Optional[Instrument]): the instrument this applies to, - if any. - - initial_value (Optional[str]): starting value, may be None even if - None does not pass the validator. None is only allowed as an - initial value and cannot be set after initiation. - - **kwargs: Passed to Parameter parent class - """ - def __init__(self, name, instrument=None, **kwargs): - super().__init__(name=name, instrument=instrument, **kwargs) - self._meta_attrs.extend(['initial_value']) - - if initial_value is not None: - self.validate(initial_value) - self._save_val(initial_value) - - def set(self, value): - """ - Validate and saves value - - Args: - value (any): value to validate and save - """ - self.validate(value) - self._save_val(value) - - def get(self): - """ Return latest value""" - return self._latest()['value'] - - class GetLatest(DelegateAttributes, DeferredOperations): """ Wrapper for a Parameter that just returns the last set or measured value From ffe60497467cbedcb52c54780242e0f2fc7c92ac Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 20:17:30 +1000 Subject: [PATCH 21/88] fix: Subclass InstrumentRefParamter from Parameter --- qcodes/instrument/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index f4d1222bd2b..2ee58c3efea 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1329,7 +1329,7 @@ def snapshot_base(self, update=False): return meta_data -class InstrumentRefParameter(ManualParameter): +class InstrumentRefParameter(Parameter): """ An InstrumentRefParameter @@ -1349,7 +1349,7 @@ class InstrumentRefParameter(ManualParameter): from within an instrument, e.g., when creating a meta instrument that sets parameters on instruments it contains. """ - + # TODO(nulinspiratie) check class works now it's subclassed from Parameter def get_instr(self): """ Returns the instance of the instrument with the name equal to the From b683dffd3ecef47bf56a7d00c4513e1cb9c39d9c Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 20:49:50 +1000 Subject: [PATCH 22/88] feat: add valmapping to BaseParameter, remove StandardParameter --- qcodes/instrument/parameter.py | 241 ++++----------------------------- 1 file changed, 29 insertions(+), 212 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 2ee58c3efea..be5d01d511a 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -104,6 +104,7 @@ class _BaseParameter(Metadatable, DeferredOperations): """ def __init__(self, name, instrument, snapshot_get, metadata, step=None, inter_delay=0, post_delay=0, + val_mapping=None, get_parser=None, set_parser=None, snapshot_value=True, max_val_age=None): super().__init__(metadata) self._snapshot_get = snapshot_get @@ -115,6 +116,16 @@ def __init__(self, name, instrument, snapshot_get, metadata, self.inter_delay = inter_delay self.post_delay = post_delay + self.val_mapping = val_mapping + if val_mapping is None: + self.inverse_val_mapping = None + else: + self.inverse_val_mapping = {v: k for k, v in val_mapping.items()} + + # TODO (nulinspiratie) implement + self.get_parser = get_parser + self.set_parser = set_parser + # record of latest value and when it was set or measured # what exactly this means is different for different subclasses # but they all use the same attributes so snapshot is consistent. @@ -135,6 +146,8 @@ def __init__(self, name, instrument, snapshot_get, metadata, # check if additional waiting time is needed before next set self._t_last_set = time.perf_counter() + # TODO(nulinspiratie) add vals + def __repr__(self): return named_repr(self) @@ -238,6 +251,10 @@ def get_wrapper(*args, **kwargs): try: # There might be cases where a .get also has args/kwargs value = get_function(*args, **kwargs) + + if self.val_mapping is not None: + value = self.inverse_val_mapping[value] + self._save_val(value) return value except Exception as e: @@ -252,10 +269,14 @@ def set_wrapper(*values, **kwargs): try: self.validate(*values) + if self.val_mapping is not None: + # Convert set values using val_mapping dictionary + values = tuple(self.val_mapping[value] for value in values) + # In some cases intermediate sweep values must be used. # Unless `self.step` is defined, get_sweep_values will return # a list containing only `value`. - for val in self.get_sweep_values(*values): + for val in self.get_sweep_values(*values, step=self.step): # Check if delay between set operations is required t_elapsed = time.perf_counter() - self._t_last_set @@ -285,7 +306,7 @@ def set_wrapper(*values, **kwargs): return set_wrapper - def get_sweep_values(self, value): + def get_sweep_values(self, value, step=None): """ Sweep to a given value from a starting value This method can be overridden to have a custom sweep behaviour. @@ -296,7 +317,7 @@ def get_sweep_values(self, value): Returns: """ - if self.step is None: + if step is None: return [value] else: start_value = self.get_latest() @@ -314,7 +335,7 @@ def get_sweep_values(self, value): return [] # drop the initial value, we're already there - return permissive_range(start_value, value, self._step)[1:] + return permissive_range(start_value, value, step)[1:] def validate(self, value): """ @@ -488,8 +509,7 @@ class Parameter(_BaseParameter): JSON snapshot of the parameter """ def __init__(self, name, instrument=None, label=None, - get_cmd=None, get_parser=None, - set_cmd=False, set_parser=None, + get_cmd=None, set_cmd=False, initial_value=None, unit=None, units=None, vals=Numbers(), docstring=None, snapshot_get=True, snapshot_value=True, metadata=None): @@ -503,7 +523,7 @@ def __init__(self, name, instrument=None, label=None, else: exec_str = instrument.ask if instrument else None self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, - output_parser=get_parser, + output_parser=self.get_parser, no_cmd_function=no_getter) if not hasattr(self, 'set') and set_cmd is not False: @@ -512,7 +532,7 @@ def __init__(self, name, instrument=None, label=None, else: exec_str = instrument.ask if instrument else None self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, - input_parser=set_parser, + input_parser=self.set_parser, no_cmd_function=no_setter) super().__init__(name, instrument, snapshot_get, metadata, @@ -918,210 +938,6 @@ def no_getter(*args, **kwargs): 'set value.') -class StandardParameter(Parameter): - """ - Define one measurement parameter. - - Args: - name (str): the local name of this parameter - - instrument (Optional[Instrument]): the instrument this parameter - belongs to, if any - - get_cmd (Optional[Union[str, function]]): a string or function to - get this parameter. You can only use a string if an instrument is - provided, then this string will be passed to instrument.ask - - get_parser ( Optional[function]): function to transform the response - from get to the final output value. - See also val_mapping - - set_cmd (Optional[Union[str, function]]): command to set this - parameter, either: - - - a string (containing one field to .format, like "{}" etc) - you can only use a string if an instrument is provided, - this string will be passed to instrument.write - - a function (of one parameter) - - set_parser (Optional[function]): function to transform the input set - value to an encoded value sent to the instrument. - See also val_mapping - - val_mapping (Optional[dict]): a bidirectional map data/readable values - to instrument codes, expressed as a dict: - ``{data_val: instrument_code}`` - For example, if the instrument uses '0' to mean 1V and '1' to mean - 10V, set val_mapping={1: '0', 10: '1'} and on the user side you - only see 1 and 10, never the coded '0' and '1' - - If vals is omitted, will also construct a matching Enum validator. - NOTE: only applies to get if get_cmd is a string, and to set if - set_cmd is a string. - - You can use ``val_mapping`` with ``get_parser``, in which case - ``get_parser`` acts on the return value from the instrument first, - then ``val_mapping`` is applied (in reverse). - - You CANNOT use ``val_mapping`` and ``set_parser`` together - that - would just provide too many ways to do the same thing. - - vals (Optional[Validator]): a Validator object for this parameter - - post_delay (Optional[Union[int, float]]): time (in seconds) to wait - after the *start* of each set, whether part of a sweep or not. - Can be set to 0 to go maximum speed with no errors. Any time - taken by the actual set operation is subtracted from the delay. - - step (Optional[Union[int, float]]): max increment of parameter value. - Larger changes are broken into multiple steps this size. - - max_val_age (Optional[Union[int, float]]): max time (in seconds) to - trust a saved value from this parameter as the starting point of - a sweep. - - **kwargs: Passed to Parameter parent class - - Raises: - NoCommandError: if get and set are not found - """ - def __init__(self, name, instrument=None, - get_cmd=None, get_parser=None, - set_cmd=None, set_parser=None, - vals=None, val_mapping=None, **kwargs): - # handle val_mapping before super init because it impacts - # vals / validation in the base class - if val_mapping: - if vals is None: - vals = Enum(*val_mapping.keys()) - - self._get_mapping = {v: k for k, v in val_mapping.items()} - - if get_parser is None: - get_parser = self._valmapping_get_parser - else: - # First run get_parser, then run the result through - # val_mapping - self._get_preparser = get_parser - get_parser = self._valmapping_with_preparser - - if set_parser is None: - self._set_mapping = val_mapping - set_parser = self._set_mapping.__getitem__ - else: - raise TypeError( - 'You cannot use set_parser and val_mapping together.') - - super().__init__(name=name, instrument=instrument, vals=vals, **kwargs) - - self._meta_attrs.extend(['sweep_step', 'sweep_delay', - 'max_sweep_delay']) - - self._initialize_get(get_cmd, get_parser) - self._initialize_set(set_cmd, set_parser) - - if not (hasattr(self, 'get') or hasattr(self, 'set')): - raise NoCommandError('neither set nor get cmd found in' + - ' Parameter {}'.format(self.name)) - - - def get(self): - try: - value = self._get_command() - self._save_val(value) - return value - except Exception as e: - e.args = e.args + ('getting {}'.format(self.full_name),) - raise e - - def set(self, value): - try: - self.validate(value) - - if self.step is not None: - # Multiple intermediate steps used to reach target value - sweep_vals = self._sweep_steps(value) + [value] - else: - # Immediately set to target value - sweep_vals = [value] - - for sweep_val in sweep_vals: - t0 = time.perf_counter() - - self._set(sweep_val) - self._save_val(sweep_val) - - # Check if any delay after setting is required - t_elapsed = time.perf_counter() - t0 - if t_elapsed < self.post_delay: - # Sleep until total time is larger than self.post_delay - time.sleep(self.post_delay - t_elapsed) - - except Exception as e: - e.args = e.args + ( - 'setting {} to {}'.format(self.full_name, repr(value)),) - raise e - - def _valmapping_get_parser(self, val): - """ - Get parser to be used in the case that a val_mapping is defined - and a separate get_parser is not defined. - - Tries to match against defined strings in the mapping dictionary. If - there are no matches, we try to convert the val into an integer. - """ - - # Try and match the raw value from the instrument directly - try: - return self._get_mapping[val] - except KeyError: - pass - - # If there is no match, we can try to convert the parameter into a - # numeric value - try: - val = int(val) - return self._get_mapping[val] - except (ValueError, KeyError): - raise KeyError('Unmapped value from instrument: {!r}'.format(val)) - - def _valmapping_with_preparser(self, val): - return self._valmapping_get_parser(self._get_preparser(val)) - - def _initialize_get(self, get_cmd, get_parser): - exec_str = self._instrument.ask if self._instrument else None - self._get_command = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, - output_parser=get_parser, - no_cmd_function=no_getter) - - def _initialize_set(self, set_cmd, set_parser): - # note: this does not set the final setter functions. that's handled - # in self.set_sweep, when we choose a swept or non-swept setter. - # TODO(giulioungaretti) lies! that method does not exis. - # probably alexj left it out :( - exec_str = self._instrument.write if self._instrument else None - self._set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, - input_parser=set_parser, no_cmd_function=no_setter) - - def _sweep_steps(self, value): - start_value = self.get_latest() - - self.validate(start_value) - - if not (isinstance(start_value, (int, float)) and - isinstance(value, (int, float))): - # something weird... parameter is numeric but one of the ends - # isn't, even though it's valid. - # probably a MultiType with a mix of numeric and non-numeric types - # just set the endpoint and move on - logging.warning('cannot sweep {} from {} to {} - jumping.'.format( - self.name, start_value, value)) - return [] - - # drop the initial value, we're already there - return permissive_range(start_value, value, self._step)[1:] - - class GetLatest(DelegateAttributes, DeferredOperations): """ Wrapper for a Parameter that just returns the last set or measured value @@ -1362,6 +1178,7 @@ def get_instr(self): return self._instrument.find_instrument(ref_instrument_name) def set_validator(self, vals): + # TODO(nulinspiratie) Remove """ Set a validator `vals` for this parameter. From 1d93da02f4e3450ac544056f861b598bf17db8a4 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 21:04:00 +1000 Subject: [PATCH 23/88] get/set_parser now implemented in BaseParameter get/set wrappers --- qcodes/instrument/parameter.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index be5d01d511a..9ab4da598d2 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -116,6 +116,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, self.inter_delay = inter_delay self.post_delay = post_delay + # TODO (nulinspiratie) handle int vs string conversion self.val_mapping = val_mapping if val_mapping is None: self.inverse_val_mapping = None @@ -252,6 +253,9 @@ def get_wrapper(*args, **kwargs): # There might be cases where a .get also has args/kwargs value = get_function(*args, **kwargs) + if self.get_parser is not None: + value = self.get_parser(value) + if self.val_mapping is not None: value = self.inverse_val_mapping[value] @@ -273,6 +277,9 @@ def set_wrapper(*values, **kwargs): # Convert set values using val_mapping dictionary values = tuple(self.val_mapping[value] for value in values) + if self.set_parser is not None: + values = tuple(self.set_parser(value) for value in values) + # In some cases intermediate sweep values must be used. # Unless `self.step` is defined, get_sweep_values will return # a list containing only `value`. @@ -512,7 +519,8 @@ def __init__(self, name, instrument=None, label=None, get_cmd=None, set_cmd=False, initial_value=None, unit=None, units=None, vals=Numbers(), docstring=None, - snapshot_get=True, snapshot_value=True, metadata=None): + snapshot_get=True, snapshot_value=True, metadata=None, + **kwargs): # Enable set/get methods if get_cmd/set_cmd is given # Called first so super().__init__ can wrap get/set methods @@ -522,21 +530,18 @@ def __init__(self, name, instrument=None, label=None, self.get = self.get_latest else: exec_str = instrument.ask if instrument else None - self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str, - output_parser=self.get_parser, - no_cmd_function=no_getter) + self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str) if not hasattr(self, 'set') and set_cmd is not False: if set_cmd is None: self.set = partial(self._save_val, validate=True) else: - exec_str = instrument.ask if instrument else None - self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str, - input_parser=self.set_parser, - no_cmd_function=no_setter) + exec_str = instrument.write if instrument else None + self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) + # TODO (nulinspiratie) make kwargs explicit super().__init__(name, instrument, snapshot_get, metadata, - snapshot_value=snapshot_value) + snapshot_value=snapshot_value, **kwargs) self._meta_attrs.extend(['label', 'unit', '_vals']) From cdff448c803212b67632e855e6978e8c97380941 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 21:05:35 +1000 Subject: [PATCH 24/88] refactor: removal of units in (Array)Parameter --- qcodes/instrument/parameter.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 9ab4da598d2..c979ee7b06c 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -518,7 +518,7 @@ class Parameter(_BaseParameter): def __init__(self, name, instrument=None, label=None, get_cmd=None, set_cmd=False, initial_value=None, - unit=None, units=None, vals=Numbers(), docstring=None, + unit=None, vals=Numbers(), docstring=None, snapshot_get=True, snapshot_value=True, metadata=None, **kwargs): @@ -546,11 +546,6 @@ def __init__(self, name, instrument=None, label=None, self._meta_attrs.extend(['label', 'unit', '_vals']) self.label = name if label is None else label - - if units is not None: - warn_units('Parameter', self) - if unit is None: - unit = units self.unit = unit if unit is not None else '' if not isinstance(vals, Validator): @@ -609,11 +604,6 @@ def sweep(self, start, stop, step=None, num=None): return SweepFixedValues(self, start=start, stop=stop, step=step, num=num) - @property - def units(self): - warn_units('Parameter', self) - return self.unit - class ArrayParameter(_BaseParameter): """ @@ -687,7 +677,7 @@ class ArrayParameter(_BaseParameter): JSON snapshot of the parameter """ def __init__(self, name, shape, instrument=None, - label=None, unit=None, units=None, + label=None, unit=None, setpoints=None, setpoint_names=None, setpoint_labels=None, setpoint_units=None, docstring=None, snapshot_get=True, snapshot_value=True, metadata=None): @@ -702,11 +692,6 @@ def __init__(self, name, shape, instrument=None, 'label', 'unit']) self.label = name if label is None else label - - if units is not None: - warn_units('ArrayParameter', self) - if unit is None: - unit = units self.unit = unit if unit is not None else '' nt = type(None) @@ -755,11 +740,6 @@ def __init__(self, name, shape, instrument=None, '', self.__doc__)) - @property - def units(self): - warn_units('ArrayParameter', self) - return self.unit - def _is_nested_sequence_or_none(obj, types, shapes): """Validator for MultiParameter setpoints/names/labels""" From 241978f8e12b106057b9a922042e582d1b08f1b6 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Wed, 28 Jun 2017 21:13:57 +1000 Subject: [PATCH 25/88] feat: add scale --- qcodes/instrument/parameter.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index c979ee7b06c..ea17cc7af27 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -103,7 +103,7 @@ class _BaseParameter(Metadatable, DeferredOperations): JSON snapshot of the parameter """ def __init__(self, name, instrument, snapshot_get, metadata, - step=None, inter_delay=0, post_delay=0, + step=None, scale=None, inter_delay=0, post_delay=0, val_mapping=None, get_parser=None, set_parser=None, snapshot_value=True, max_val_age=None): super().__init__(metadata) @@ -115,6 +115,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, self.step = step self.inter_delay = inter_delay self.post_delay = post_delay + self.scale = scale # TODO (nulinspiratie) handle int vs string conversion self.val_mapping = val_mapping @@ -256,6 +257,18 @@ def get_wrapper(*args, **kwargs): if self.get_parser is not None: value = self.get_parser(value) + if self.scale is not None: + # Scale values + if isinstance(self.scale, collections.Iterable): + # Scale contains multiple elements, one for each value + value = tuple(value / scale for value, scale + in zip(value, self.scale)) + elif isinstance(value, collections.Iterable): + # Use single scale for all values + value = tuple(value / self.scale for value in value) + else: + value /= self.scale + if self.val_mapping is not None: value = self.inverse_val_mapping[value] @@ -277,8 +290,18 @@ def set_wrapper(*values, **kwargs): # Convert set values using val_mapping dictionary values = tuple(self.val_mapping[value] for value in values) + if self.scale is not None: + if isinstance(self.scale, collections.Iterable): + # Scale contains multiple elements, one for each value + values = tuple(value * scale for value, scale + in zip(values, self.scale)) + else: + # Use single scale for all values + values = tuple(value * self.scale for value in values) + if self.set_parser is not None: - values = tuple(self.set_parser(value) for value in values) + values = tuple( + self.set_parser(value) for value in values) # In some cases intermediate sweep values must be used. # Unless `self.step` is defined, get_sweep_values will return @@ -552,6 +575,7 @@ def __init__(self, name, instrument=None, label=None, raise TypeError('vals must be a Validator') self.vals = vals + if initial_value is not None: self._save_val(initial_value, validate=True) From e5b6398959ded9bb4910e19d2dacf7ea5d08b3ff Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Thu, 29 Jun 2017 10:01:29 +1000 Subject: [PATCH 26/88] Added StandardParameter and Manualparameter as deprecated --- qcodes/instrument/parameter.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index ea17cc7af27..c857afdb8c9 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -575,7 +575,6 @@ def __init__(self, name, instrument=None, label=None, raise TypeError('vals must be a Validator') self.vals = vals - if initial_value is not None: self._save_val(initial_value, validate=True) @@ -1200,3 +1199,28 @@ def set_validator(self, vals): self.vals = vals else: raise TypeError('vals must be a Validator') + + +# Deprecated parameters +class StandardParameter(Parameter): + def __init__(self, name, instrument=None, + get_cmd=False, get_parser=None, + set_cmd=False, set_parser=None, + delay=None, max_delay=None, step=None, max_val_age=3600, + vals=None, val_mapping=None, **kwargs): + logging.warning('`StandardParameter` is deprecated, ' + 'use `Parameter` instead. {}'.format(self)) + super().__init__(self, name, instrument=instrument, + get_cmd=get_cmd, get_parser=get_parser, + set_cmd=set_cmd, set_parser=set_parser, + post_delay=delay, step=step, max_val_age=max_val_age, + vals=vals, val_mapping=val_mapping, **kwargs) + + +class ManualParameter(Parameter): + def __init__(self, name, instrument=None, initial_value=None, **kwargs): + logging.warning('`ManualParameter` is deprecated, use `Parameter` ' + 'instead with `set_cmd=None`. {}'.format(self)) + super().__init__(name=name, instrument=instrument, + get_cmd=None, set_cmd=None, + initial_value=initial_value, **kwargs) \ No newline at end of file From bc788f6d1cfafb5c91b92e0aa3071026fedf48c5 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Thu, 29 Jun 2017 10:04:59 +1000 Subject: [PATCH 27/88] feat: added raw_value --- qcodes/instrument/parameter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index c857afdb8c9..24135b66a90 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -111,6 +111,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, self.name = str(name) self._instrument = instrument self._snapshot_value = snapshot_value + self.raw_value = None self.step = step self.inter_delay = inter_delay @@ -142,7 +143,8 @@ def __init__(self, name, instrument, snapshot_get, metadata, # subclasses should extend this list with extra attributes they # want automatically included in the snapshot - self._meta_attrs = ['name', 'instrument'] + # TODO (nulinspiratie) add other relevant meta_attrs + self._meta_attrs = ['name', 'instrument', 'raw_value'] # Specify time of last set operation, used when comparing to delay to # check if additional waiting time is needed before next set @@ -253,6 +255,7 @@ def get_wrapper(*args, **kwargs): try: # There might be cases where a .get also has args/kwargs value = get_function(*args, **kwargs) + self.raw_value = value if self.get_parser is not None: value = self.get_parser(value) @@ -319,6 +322,7 @@ def set_wrapper(*values, **kwargs): t0 = time.perf_counter() set_function(val, **kwargs) + self.raw_value = val self._save_val(val) # Update last set time (used for calculating delays) From 3ac3f46f584eb607704846ffaa3cfbf36abab74b Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Thu, 29 Jun 2017 10:21:27 +1000 Subject: [PATCH 28/88] fix: Placed deprecation warnings after super().__init__ --- qcodes/instrument/parameter.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 24135b66a90..fd42be24dc6 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1212,19 +1212,19 @@ def __init__(self, name, instrument=None, set_cmd=False, set_parser=None, delay=None, max_delay=None, step=None, max_val_age=3600, vals=None, val_mapping=None, **kwargs): - logging.warning('`StandardParameter` is deprecated, ' - 'use `Parameter` instead. {}'.format(self)) super().__init__(self, name, instrument=instrument, get_cmd=get_cmd, get_parser=get_parser, set_cmd=set_cmd, set_parser=set_parser, post_delay=delay, step=step, max_val_age=max_val_age, vals=vals, val_mapping=val_mapping, **kwargs) + logging.warning('`StandardParameter` is deprecated, ' + 'use `Parameter` instead. {}'.format(self)) class ManualParameter(Parameter): def __init__(self, name, instrument=None, initial_value=None, **kwargs): - logging.warning('`ManualParameter` is deprecated, use `Parameter` ' - 'instead with `set_cmd=None`. {}'.format(self)) super().__init__(name=name, instrument=instrument, get_cmd=None, set_cmd=None, - initial_value=initial_value, **kwargs) \ No newline at end of file + initial_value=initial_value, **kwargs) + logging.warning('`ManualParameter` is deprecated, use `Parameter` ' + 'instead with `set_cmd=None`. {}'.format(self)) \ No newline at end of file From 38dce43d1a5d2e87f0f61ad93a04122d2b1bc698 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Thu, 29 Jun 2017 10:32:22 +1000 Subject: [PATCH 29/88] refactor: change _get/set_wrapper to _wrap_get/set --- qcodes/instrument/parameter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index fd42be24dc6..68e3250df47 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -137,9 +137,9 @@ def __init__(self, name, instrument, snapshot_get, metadata, self.get_latest = GetLatest(self, max_val_age=max_val_age) if hasattr(self, 'get'): - self.get = self._get_wrapper(self.get) + self.get = self._wrap_get(self.get) if hasattr(self, 'set'): - self.set = self._set_wrapper(self.set) + self.set = self._wrap_set(self.set) # subclasses should extend this list with extra attributes they # want automatically included in the snapshot @@ -249,7 +249,7 @@ def _save_val(self, value, validate=False): self._latest_value = value self._latest_ts = datetime.now() - def _get_wrapper(self, get_function): + def _wrap_get(self, get_function): @wraps def get_wrapper(*args, **kwargs): try: @@ -283,7 +283,7 @@ def get_wrapper(*args, **kwargs): return get_wrapper - def _set_wrapper(self, set_function): + def _wrap_set(self, set_function): @wraps def set_wrapper(*values, **kwargs): try: From c4ff66ada1ecaedaa57a7c1c15239f41bd5f9eec Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Thu, 29 Jun 2017 10:33:19 +1000 Subject: [PATCH 30/88] fix: get/set after super().__init__ to ensure all attrs are set Travis was failing since get_latest was not yet defined --- qcodes/instrument/parameter.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 68e3250df47..03480cda5a4 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -548,6 +548,9 @@ def __init__(self, name, instrument=None, label=None, unit=None, vals=Numbers(), docstring=None, snapshot_get=True, snapshot_value=True, metadata=None, **kwargs): + # TODO (nulinspiratie) make kwargs explicit + super().__init__(name, instrument, snapshot_get, metadata, + snapshot_value=snapshot_value, **kwargs) # Enable set/get methods if get_cmd/set_cmd is given # Called first so super().__init__ can wrap get/set methods @@ -558,6 +561,7 @@ def __init__(self, name, instrument=None, label=None, else: exec_str = instrument.ask if instrument else None self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str) + self.get = self.wrap_get(self.get) if not hasattr(self, 'set') and set_cmd is not False: if set_cmd is None: @@ -565,10 +569,7 @@ def __init__(self, name, instrument=None, label=None, else: exec_str = instrument.write if instrument else None self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) - - # TODO (nulinspiratie) make kwargs explicit - super().__init__(name, instrument, snapshot_get, metadata, - snapshot_value=snapshot_value, **kwargs) + self.set = self.wrap_set(self.set) self._meta_attrs.extend(['label', 'unit', '_vals']) From a0d1048a53b7f0584c09dcb13280815ad7c1b8bb Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 10:41:18 +1000 Subject: [PATCH 31/88] refactor: replace full_name by str(parameter) --- qcodes/instrument/parameter.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 03480cda5a4..20a4a2efbf9 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -152,6 +152,14 @@ def __init__(self, name, instrument, snapshot_get, metadata, # TODO(nulinspiratie) add vals + def __str__(self): + """Include the instrument name with the Parameter name if possible.""" + inst_name = getattr(self._instrument, 'name', None) + if inst_name is not None: + return '{}_{}'.format(inst_name, self.name) + else: + return self.name + def __repr__(self): return named_repr(self) @@ -389,15 +397,9 @@ def validate(self, value): @property def full_name(self): - """Include the instrument name with the Parameter name if possible.""" - try: - inst_name = self._instrument.name - if inst_name: - return inst_name + '_' + self.name - except AttributeError: - pass - - return self.name + warnings.warn('Attribute `full_name` is deprecated, please use ' + 'str(parameter)') + return str(self) @property def step(self): From 0eaf1489ffdc5d42dd426716551bd603076eea02 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 11:11:40 +1000 Subject: [PATCH 32/88] refactor: remove get_attrs, replace full_name occurrences --- qcodes/instrument/parameter.py | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 20a4a2efbf9..6bd4d5a266b 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -183,31 +183,6 @@ def _latest(self): 'ts': self._latest_ts } - # get_attrs ignores leading underscores, unless they're in this list - _keep_attrs = ['__doc__', '_vals'] - - def get_attrs(self): - """ - Attributes recreated as properties in the RemoteParameter proxy. - - Grab the names of all attributes that the RemoteParameter needs - to function like the main one (in loops etc) - - Returns: - list: All public attribute names, plus docstring and _vals - """ - out = [] - - for attr in dir(self): - # while we're keeping units as a deprecated attribute in some - # classes, avoid calling it here so we don't get spurious errors - if ((attr[0] == '_' and attr not in self._keep_attrs) or - (attr != 'units' and callable(getattr(self, attr)))): - continue - out.append(attr) - - return out - def snapshot_base(self, update=False): """ State of the parameter as a JSON-compatible dict. @@ -286,7 +261,7 @@ def get_wrapper(*args, **kwargs): self._save_val(value) return value except Exception as e: - e.args = e.args + ('getting {}'.format(self.full_name),) + e.args = e.args + ('getting {}'.format(self),) raise e return get_wrapper @@ -342,8 +317,7 @@ def set_wrapper(*values, **kwargs): # Sleep until total time is larger than self.post_delay time.sleep(self.post_delay - t_elapsed) except Exception as e: - e.args = e.args + ('setting {} to {}'.format(self.full_name, - values),) + e.args = e.args + ('setting {} to {}'.format(self, values),) raise e return set_wrapper @@ -1152,10 +1126,9 @@ def snapshot_base(self, update=False): meta_data['__class__'] = full_class(self) meta_data['unit'] = self.parameter.unit meta_data['label'] = self.parameter.label - meta_data['full_name'] = self.parameter.full_name meta_data['aggreagator'] = repr(getattr(self, 'f', None)) for param in self.parameters: - meta_data[param.full_name] = param.snapshot() + meta_data[str(param)] = param.snapshot() return meta_data From 8db77ea397786a65fe6d2a15b5fc796795e0c005 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 11:16:13 +1000 Subject: [PATCH 33/88] fix: aggregator typo --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 6bd4d5a266b..c7b6e5edfc3 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1126,7 +1126,7 @@ def snapshot_base(self, update=False): meta_data['__class__'] = full_class(self) meta_data['unit'] = self.parameter.unit meta_data['label'] = self.parameter.label - meta_data['aggreagator'] = repr(getattr(self, 'f', None)) + meta_data['aggregator'] = repr(getattr(self, 'f', None)) for param in self.parameters: meta_data[str(param)] = param.snapshot() From 4902d4119a90b68c8672f50142fbd9aa5b703301 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 11:32:15 +1000 Subject: [PATCH 34/88] fix: replace is_sequence Iterator by Iterable Doing this enables numpy arrays to be included as sweep values --- qcodes/utils/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/utils/helpers.py b/qcodes/utils/helpers.py index 3cbfcb993b8..a9668d807d9 100644 --- a/qcodes/utils/helpers.py +++ b/qcodes/utils/helpers.py @@ -6,7 +6,7 @@ import sys import time -from collections import Iterator, Sequence, Mapping +from collections import Iterable, Sequence, Mapping from copy import deepcopy import numpy as np @@ -72,7 +72,7 @@ def is_sequence(obj): We do not consider strings or unordered collections like sets to be sequences, but we do accept iterators (such as generators) """ - return (isinstance(obj, (Iterator, Sequence)) and + return (isinstance(obj, (Iterable, Sequence)) and not isinstance(obj, (str, bytes, io.IOBase))) From 0d5b9b0991cbcfc77293033c9707115064822311 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:26:06 +1000 Subject: [PATCH 35/88] refactor: change InstrumentRefParameter set_validators --- qcodes/instrument/parameter.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index c7b6e5edfc3..baf84a9970e 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1153,6 +1153,11 @@ class InstrumentRefParameter(Parameter): from within an instrument, e.g., when creating a meta instrument that sets parameters on instruments it contains. """ + + def __init__(self, *args, **kwargs): + kwargs['vals'] = kwargs.get('vals', Strings()) + super().__init__(*args, **kwargs) + # TODO(nulinspiratie) check class works now it's subclassed from Parameter def get_instr(self): """ @@ -1165,21 +1170,6 @@ def get_instr(self): # of this parameter. return self._instrument.find_instrument(ref_instrument_name) - def set_validator(self, vals): - # TODO(nulinspiratie) Remove - """ - Set a validator `vals` for this parameter. - - Args: - vals (Validator): validator to set - """ - if vals is None: - self.vals = Strings() - elif isinstance(vals, Validator): - self.vals = vals - else: - raise TypeError('vals must be a Validator') - # Deprecated parameters class StandardParameter(Parameter): From 024f8c6b0ba5f53225b679ba11178f905b25a8c6 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:42:30 +1000 Subject: [PATCH 36/88] refactor: combine _latest_value, _latest_ts by _latest dict attr, remove _latest method --- qcodes/instrument/parameter.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index baf84a9970e..153e7167f9d 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -132,8 +132,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, # record of latest value and when it was set or measured # what exactly this means is different for different subclasses # but they all use the same attributes so snapshot is consistent. - self._latest_value = None - self._latest_ts = None + self._latest = {'value': None, 'ts': None} self.get_latest = GetLatest(self, max_val_age=max_val_age) if hasattr(self, 'get'): @@ -177,12 +176,6 @@ def __call__(self, *args, **kwargs): raise NotImplementedError('no set cmd found in' + ' Parameter {}'.format(self.name)) - def _latest(self): - return { - 'value': self._latest_value, - 'ts': self._latest_ts - } - def snapshot_base(self, update=False): """ State of the parameter as a JSON-compatible dict. @@ -200,7 +193,7 @@ def snapshot_base(self, update=False): and self._snapshot_value and update: self.get() - state = self._latest() + state = self._latest state['__class__'] = full_class(self) if not self._snapshot_value: @@ -229,8 +222,7 @@ def snapshot_base(self, update=False): def _save_val(self, value, validate=False): if validate: self.validate(value) - self._latest_value = value - self._latest_ts = datetime.now() + self._latest = {'value': value, 'ts': datetime.now()} def _wrap_get(self, get_function): @wraps @@ -957,7 +949,7 @@ def get(self): """Return latest value if time since get was less than `self.max_val_age`, otherwise perform `get()` and return result """ - state = self.parameter._latest() + state = self.parameter._latest if self.max_val_age is None: # Return last value since max_val_age is not specified return state From 5d5430d73fe7296367602e9b713528e016dfdf94 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:42:51 +1000 Subject: [PATCH 37/88] refactor: _save_val validates by default --- qcodes/instrument/parameter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 153e7167f9d..80c9242cbf2 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -219,7 +219,7 @@ def snapshot_base(self, update=False): return state - def _save_val(self, value, validate=False): + def _save_val(self, value, validate=True): if validate: self.validate(value) self._latest = {'value': value, 'ts': datetime.now()} @@ -250,7 +250,7 @@ def get_wrapper(*args, **kwargs): if self.val_mapping is not None: value = self.inverse_val_mapping[value] - self._save_val(value) + self._save_val(value, validate=False) return value except Exception as e: e.args = e.args + ('getting {}'.format(self),) @@ -533,7 +533,7 @@ def __init__(self, name, instrument=None, label=None, if not hasattr(self, 'set') and set_cmd is not False: if set_cmd is None: - self.set = partial(self._save_val, validate=True) + self.set = self._save_val else: exec_str = instrument.write if instrument else None self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) @@ -549,7 +549,7 @@ def __init__(self, name, instrument=None, label=None, self.vals = vals if initial_value is not None: - self._save_val(initial_value, validate=True) + self._save_val(initial_value) # generate default docstring self.__doc__ = os.linesep.join(( From 31c9912a3359cc6998940c3c6bb23f65ba3663b3 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:44:57 +1000 Subject: [PATCH 38/88] fix: remove partial import --- qcodes/instrument/parameter.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 80c9242cbf2..8bdaa7ce902 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -58,7 +58,7 @@ import os import collections import warnings -from functools import partial, wraps +from functools import wraps import numpy from qcodes.utils.deferred_operations import DeferredOperations @@ -361,12 +361,6 @@ def validate(self, value): self.vals.validate(value, 'Parameter: ' + context) - @property - def full_name(self): - warnings.warn('Attribute `full_name` is deprecated, please use ' - 'str(parameter)') - return str(self) - @property def step(self): return self._step @@ -459,6 +453,13 @@ def inter_delay(self, inter_delay): raise ValueError('delay must not be negative') self._inter_delay = inter_delay + # Deprecated + @property + def full_name(self): + warnings.warn('Attribute `full_name` is deprecated, please use ' + 'str(parameter)') + return str(self) + class Parameter(_BaseParameter): """ From a063206db13967b31f245788a802f0982ba398b9 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:48:37 +1000 Subject: [PATCH 39/88] refactor: remove no_getter/setter, unused imports --- qcodes/instrument/parameter.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 8bdaa7ce902..90e68d6d343 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -62,12 +62,12 @@ import numpy from qcodes.utils.deferred_operations import DeferredOperations -from qcodes.utils.helpers import (permissive_range, wait_secs, is_sequence, - is_sequence_of, DelegateAttributes, - full_class, named_repr, warn_units) +from qcodes.utils.helpers import (permissive_range, is_sequence_of, + DelegateAttributes, full_class, named_repr, + warn_units) from qcodes.utils.metadata import Metadatable -from qcodes.utils.command import Command, NoCommandError -from qcodes.utils.validators import Validator, Numbers, Ints, Enum, Strings +from qcodes.utils.command import Command +from qcodes.utils.validators import Validator, Numbers, Ints, Strings from qcodes.instrument.sweep_values import SweepFixedValues from qcodes.data.data_array import DataArray @@ -910,16 +910,6 @@ def full_names(self): return self.names -def no_setter(*args, **kwargs): - raise NotImplementedError('This Parameter has no setter defined.') - - -def no_getter(*args, **kwargs): - raise NotImplementedError( - 'This Parameter has no getter, use .get_latest to get the most recent ' - 'set value.') - - class GetLatest(DelegateAttributes, DeferredOperations): """ Wrapper for a Parameter that just returns the last set or measured value From 07ae82e59cd48cc7016a011193c53c4f44d7feb8 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:53:54 +1000 Subject: [PATCH 40/88] refactor: tidy up code, remove todo --- qcodes/instrument/parameter.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 90e68d6d343..34037a110bb 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -107,25 +107,24 @@ def __init__(self, name, instrument, snapshot_get, metadata, val_mapping=None, get_parser=None, set_parser=None, snapshot_value=True, max_val_age=None): super().__init__(metadata) - self._snapshot_get = snapshot_get self.name = str(name) self._instrument = instrument + self._snapshot_get = snapshot_get self._snapshot_value = snapshot_value - self.raw_value = None self.step = step + self.scale = scale + self.raw_value = None self.inter_delay = inter_delay self.post_delay = post_delay - self.scale = scale - # TODO (nulinspiratie) handle int vs string conversion + # TODO (nulinspiratie) handle int vs string conversion in val_mapping self.val_mapping = val_mapping if val_mapping is None: self.inverse_val_mapping = None else: self.inverse_val_mapping = {v: k for k, v in val_mapping.items()} - # TODO (nulinspiratie) implement self.get_parser = get_parser self.set_parser = set_parser @@ -685,8 +684,8 @@ def __init__(self, name, shape, instrument=None, raise AttributeError('ArrayParameters do not support set ' 'at this time.') - self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', 'setpoint_units', - 'label', 'unit']) + self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', + 'setpoint_units', 'label', 'unit']) self.label = name if label is None else label self.unit = unit if unit is not None else '' @@ -737,7 +736,6 @@ def __init__(self, name, shape, instrument=None, '', self.__doc__)) - def _is_nested_sequence_or_none(obj, types, shapes): """Validator for MultiParameter setpoints/names/labels""" if obj is None: From 2a2b1e81a5383a85bf8510d1f105f6fb2d40abd7 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 13:56:54 +1000 Subject: [PATCH 41/88] refactor: change back save_val by default to validator=False Forgot that it was used by other custom parameters, and so changing default behaviour might lead to issues --- qcodes/instrument/parameter.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 34037a110bb..9aa6a54c9af 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -58,7 +58,7 @@ import os import collections import warnings -from functools import wraps +from functools import partial, wraps import numpy from qcodes.utils.deferred_operations import DeferredOperations @@ -218,7 +218,7 @@ def snapshot_base(self, update=False): return state - def _save_val(self, value, validate=True): + def _save_val(self, value, validate=False): if validate: self.validate(value) self._latest = {'value': value, 'ts': datetime.now()} @@ -249,7 +249,7 @@ def get_wrapper(*args, **kwargs): if self.val_mapping is not None: value = self.inverse_val_mapping[value] - self._save_val(value, validate=False) + self._save_val(value) return value except Exception as e: e.args = e.args + ('getting {}'.format(self),) @@ -297,7 +297,7 @@ def set_wrapper(*values, **kwargs): set_function(val, **kwargs) self.raw_value = val - self._save_val(val) + self._save_val(val, validate=True) # Update last set time (used for calculating delays) self._t_last_set = time.perf_counter() @@ -533,7 +533,7 @@ def __init__(self, name, instrument=None, label=None, if not hasattr(self, 'set') and set_cmd is not False: if set_cmd is None: - self.set = self._save_val + self.set = partial(self._save_val, validate=True) else: exec_str = instrument.write if instrument else None self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) @@ -549,7 +549,7 @@ def __init__(self, name, instrument=None, label=None, self.vals = vals if initial_value is not None: - self._save_val(initial_value) + self._save_val(initial_value, validate=True) # generate default docstring self.__doc__ = os.linesep.join(( From 804e98c0fc190fc9dfdb332ca50bd5cf25f0065d Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 14:00:07 +1000 Subject: [PATCH 42/88] fix: snapshot does not change self._latest --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 9aa6a54c9af..52292390267 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -192,7 +192,7 @@ def snapshot_base(self, update=False): and self._snapshot_value and update: self.get() - state = self._latest + state = copy(self._latest) state['__class__'] = full_class(self) if not self._snapshot_value: From 07ef88096814c315b881afbff782288286d3d6ed Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 14:06:48 +1000 Subject: [PATCH 43/88] feat: only include meta_attrs that are not None --- qcodes/instrument/parameter.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 52292390267..028148bef82 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -141,8 +141,8 @@ def __init__(self, name, instrument, snapshot_get, metadata, # subclasses should extend this list with extra attributes they # want automatically included in the snapshot - # TODO (nulinspiratie) add other relevant meta_attrs - self._meta_attrs = ['name', 'instrument', 'raw_value'] + self._meta_attrs = ['name', 'instrument', 'step', 'scale','raw_value', + 'inter_delay', 'post_delay', 'val_mapping'] # Specify time of last set operation, used when comparing to delay to # check if additional waiting time is needed before next set @@ -208,13 +208,15 @@ def snapshot_base(self, update=False): 'instrument_name': self._instrument.name }) - elif hasattr(self, attr): - val = getattr(self, attr) - attr_strip = attr.lstrip('_') # eg _vals - do not include _ - if isinstance(val, Validator): - state[attr_strip] = repr(val) - else: - state[attr_strip] = val + + else: + val = getattr(self, attr, None) + if val is not None: + attr_strip = attr.lstrip('_') # eg _vals - do not include _ + if isinstance(val, Validator): + state[attr_strip] = repr(val) + else: + state[attr_strip] = val return state From 426f6a8b4d0f6b1d8d1030b0a82f11ceda6aa2db Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 14:18:43 +1000 Subject: [PATCH 44/88] fix: vals in base_parameter --- qcodes/instrument/parameter.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 028148bef82..70178e9164a 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -105,7 +105,7 @@ class _BaseParameter(Metadatable, DeferredOperations): def __init__(self, name, instrument, snapshot_get, metadata, step=None, scale=None, inter_delay=0, post_delay=0, val_mapping=None, get_parser=None, set_parser=None, - snapshot_value=True, max_val_age=None): + snapshot_value=True, max_val_age=None, vals=None): super().__init__(metadata) self.name = str(name) self._instrument = instrument @@ -118,6 +118,10 @@ def __init__(self, name, instrument, snapshot_get, metadata, self.inter_delay = inter_delay self.post_delay = post_delay + if not isinstance(vals, (Validator, type(None))): + raise TypeError('vals must be None or a Validator') + self.vals = vals + # TODO (nulinspiratie) handle int vs string conversion in val_mapping self.val_mapping = val_mapping if val_mapping is None: @@ -142,7 +146,7 @@ def __init__(self, name, instrument, snapshot_get, metadata, # subclasses should extend this list with extra attributes they # want automatically included in the snapshot self._meta_attrs = ['name', 'instrument', 'step', 'scale','raw_value', - 'inter_delay', 'post_delay', 'val_mapping'] + 'inter_delay', 'post_delay', 'val_mapping', 'vals'] # Specify time of last set operation, used when comparing to delay to # check if additional waiting time is needed before next set @@ -207,12 +211,10 @@ def snapshot_base(self, update=False): 'instrument': full_class(self._instrument), 'instrument_name': self._instrument.name }) - - else: val = getattr(self, attr, None) if val is not None: - attr_strip = attr.lstrip('_') # eg _vals - do not include _ + attr_strip = attr.lstrip('_') # strip leading underscores if isinstance(val, Validator): state[attr_strip] = repr(val) else: @@ -359,8 +361,8 @@ def validate(self, value): str(self._instrument.__class__)) + '.' + self.name else: context = self.name - - self.vals.validate(value, 'Parameter: ' + context) + if self.vals is not None: + self.vals.validate(value, 'Parameter: ' + context) @property def step(self): @@ -385,13 +387,13 @@ def step(self, step): TypeError: if step is not integer for an integer parameter TypeError: if step is not a number """ - if not self._vals.is_numeric: + if not getattr(self.vals, 'is_numeric', True): raise TypeError('you can only step numeric parameters') elif not isinstance(step, (int, float)): raise TypeError('step must be a number') elif step <= 0: raise ValueError('step must be positive') - elif (isinstance(self._vals, Ints) and not isinstance(step, int)): + elif isinstance(self.vals, Ints) and not isinstance(step, int): raise TypeError('step must be a positive int for an Ints parameter') else: self._step = step @@ -520,7 +522,7 @@ def __init__(self, name, instrument=None, label=None, **kwargs): # TODO (nulinspiratie) make kwargs explicit super().__init__(name, instrument, snapshot_get, metadata, - snapshot_value=snapshot_value, **kwargs) + snapshot_value=snapshot_value, vals=vals, **kwargs) # Enable set/get methods if get_cmd/set_cmd is given # Called first so super().__init__ can wrap get/set methods @@ -541,15 +543,11 @@ def __init__(self, name, instrument=None, label=None, self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) self.set = self.wrap_set(self.set) - self._meta_attrs.extend(['label', 'unit', '_vals']) + self._meta_attrs.extend(['label', 'unit', 'vals']) self.label = name if label is None else label self.unit = unit if unit is not None else '' - if not isinstance(vals, Validator): - raise TypeError('vals must be a Validator') - self.vals = vals - if initial_value is not None: self._save_val(initial_value, validate=True) From 9669f6e8e2460290dad88987b24e8164d32ca770 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 14:37:29 +1000 Subject: [PATCH 45/88] fix: raise error when max_val_age is set but no get/get_cmd --- qcodes/instrument/parameter.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 70178e9164a..51c38962287 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -152,8 +152,6 @@ def __init__(self, name, instrument, snapshot_get, metadata, # check if additional waiting time is needed before next set self._t_last_set = time.perf_counter() - # TODO(nulinspiratie) add vals - def __str__(self): """Include the instrument name with the Parameter name if possible.""" inst_name = getattr(self._instrument, 'name', None) @@ -519,16 +517,19 @@ def __init__(self, name, instrument=None, label=None, initial_value=None, unit=None, vals=Numbers(), docstring=None, snapshot_get=True, snapshot_value=True, metadata=None, - **kwargs): + max_val_age=None, **kwargs): # TODO (nulinspiratie) make kwargs explicit super().__init__(name, instrument, snapshot_get, metadata, - snapshot_value=snapshot_value, vals=vals, **kwargs) + snapshot_value=snapshot_value, vals=vals, + max_val_age=max_val_age, **kwargs) # Enable set/get methods if get_cmd/set_cmd is given # Called first so super().__init__ can wrap get/set methods if not hasattr(self, 'get') and get_cmd is not False: if get_cmd is None: - # TODO(nulinspiratie) handle if max_val_age is set + if max_val_age is not None: + raise SyntaxError('Must have get method or specify get_cmd ' + 'when max_val_age is set') self.get = self.get_latest else: exec_str = instrument.ask if instrument else None From d44958d6e618ef6ac7187f9ad23216af76d16178 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 14:39:56 +1000 Subject: [PATCH 46/88] fix: move setting of vals attr earlier --- qcodes/instrument/parameter.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 51c38962287..1fb82c6e6cb 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -112,15 +112,16 @@ def __init__(self, name, instrument, snapshot_get, metadata, self._snapshot_get = snapshot_get self._snapshot_value = snapshot_value + if not isinstance(vals, (Validator, type(None))): + raise TypeError('vals must be None or a Validator') + self.vals = vals + self.step = step self.scale = scale self.raw_value = None self.inter_delay = inter_delay self.post_delay = post_delay - if not isinstance(vals, (Validator, type(None))): - raise TypeError('vals must be None or a Validator') - self.vals = vals # TODO (nulinspiratie) handle int vs string conversion in val_mapping self.val_mapping = val_mapping From 8e22174d81183e6eb1d63e30f35e019fc82d119a Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 15:08:58 +1000 Subject: [PATCH 47/88] fix: Multiparameter accepts array as setpoints --- qcodes/instrument/parameter.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 1fb82c6e6cb..8801667d00c 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -115,14 +115,13 @@ def __init__(self, name, instrument, snapshot_get, metadata, if not isinstance(vals, (Validator, type(None))): raise TypeError('vals must be None or a Validator') self.vals = vals - + self.step = step self.scale = scale self.raw_value = None self.inter_delay = inter_delay self.post_delay = post_delay - # TODO (nulinspiratie) handle int vs string conversion in val_mapping self.val_mapping = val_mapping if val_mapping is None: @@ -338,7 +337,7 @@ def get_sweep_values(self, value, step=None): isinstance(value, (int, float))): # something weird... parameter is numeric but one of the ends # isn't, even though it's valid. - # probably a MultiType with a mix of numeric and non-numeric types + # probably MultiType with a mix of numeric and non-numeric types # just set the endpoint and move on logging.warning('cannot sweep {} from {} to {} - jumping.'.format( self.name, start_value, value)) @@ -839,14 +838,13 @@ def __init__(self, name, names, shapes, instrument=None, snapshot_value=snapshot_value) if hasattr(self, 'set'): # TODO (alexcjohnson): can we support, ala Combine? - warnings.warn('MultiParameters do not fully support set ' - 'at this time.') + warnings.warn('MultiParameters do not support set at this time.') - self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', 'setpoint_units', - 'names', 'labels', 'units']) + self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', + 'setpoint_units', 'names', 'labels', 'units']) if not is_sequence_of(names, str): - raise ValueError('names must be a tuple of strings, not' + + raise ValueError('names must be a tuple of strings, not ' + repr(names)) self.names = names @@ -862,7 +860,7 @@ def __init__(self, name, names, shapes, instrument=None, self.shapes = shapes sp_types = (nt, DataArray, collections.Sequence, - collections.Iterator) + collections.Iterable) if not _is_nested_sequence_or_none(setpoints, sp_types, shapes): raise ValueError('setpoints must be a tuple of tuples of arrays') From e215dc156689cff2668d04b81b7aa13da52bd40e Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 15:16:30 +1000 Subject: [PATCH 48/88] fix: change setter values to value --- qcodes/instrument/parameter.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 8801667d00c..180e8b4fd20 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -93,10 +93,10 @@ class _BaseParameter(Metadatable, DeferredOperations): parameter during a snapshot, even if the snapshot was called with ``update=True``, for example if it takes too long to update. Default True. - - max_val_age (Optional[int]): The max time (in seconds) to trust a + + max_val_age (Optional[int]): The max time (in seconds) to trust a saved value obtained from get_latest(). If this parameter has not - been set or measured more recently than this, perform an + been set or measured more recently than this, perform an additional measurement. metadata (Optional[dict]): extra information to include with the @@ -261,31 +261,30 @@ def get_wrapper(*args, **kwargs): def _wrap_set(self, set_function): @wraps - def set_wrapper(*values, **kwargs): + def set_wrapper(value, **kwargs): try: - self.validate(*values) + self.validate(value) if self.val_mapping is not None: # Convert set values using val_mapping dictionary - values = tuple(self.val_mapping[value] for value in values) + value = self.val_mapping[value] if self.scale is not None: if isinstance(self.scale, collections.Iterable): # Scale contains multiple elements, one for each value - values = tuple(value * scale for value, scale - in zip(values, self.scale)) + value = tuple(val * scale for val, scale + in zip(value, self.scale)) else: # Use single scale for all values - values = tuple(value * self.scale for value in values) + value *= self.scale if self.set_parser is not None: - values = tuple( - self.set_parser(value) for value in values) + value = self.set_parser(value) # In some cases intermediate sweep values must be used. # Unless `self.step` is defined, get_sweep_values will return # a list containing only `value`. - for val in self.get_sweep_values(*values, step=self.step): + for val in self.get_sweep_values(value, step=self.step): # Check if delay between set operations is required t_elapsed = time.perf_counter() - self._t_last_set @@ -310,7 +309,7 @@ def set_wrapper(*values, **kwargs): # Sleep until total time is larger than self.post_delay time.sleep(self.post_delay - t_elapsed) except Exception as e: - e.args = e.args + ('setting {} to {}'.format(self, values),) + e.args = e.args + ('setting {} to {}'.format(self, value),) raise e return set_wrapper @@ -327,7 +326,7 @@ def get_sweep_values(self, value, step=None): """ if step is None: - return [value] + return value else: start_value = self.get_latest() From 59939fd8dac37a463a23ca7b2919cdab962537db Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 15:21:03 +1000 Subject: [PATCH 49/88] fix: allow step=None --- qcodes/instrument/parameter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 180e8b4fd20..c68da749889 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -384,6 +384,8 @@ def step(self, step): TypeError: if step is not integer for an integer parameter TypeError: if step is not a number """ + if step is None: + self._step = step if not getattr(self.vals, 'is_numeric', True): raise TypeError('you can only step numeric parameters') elif not isinstance(step, (int, float)): @@ -934,7 +936,7 @@ def __init__(self, parameter, max_val_age=None): omit_delegate_attrs = ['set'] def get(self): - """Return latest value if time since get was less than + """Return latest value if time since get was less than `self.max_val_age`, otherwise perform `get()` and return result """ state = self.parameter._latest From 3aa7be6221231068b02929eb42f7abd95b33bff7 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 15:33:16 +1000 Subject: [PATCH 50/88] fix: setpoints in ArrayParameter can now be an np.array --- qcodes/instrument/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index c68da749889..de430b952fd 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -46,7 +46,7 @@ stepped ``write`` calls from a single ``set``. Does not need to be subclassed, just instantiated. -- ``ManualParameter`` is for values you want to keep track of but cannot +- ``ManualParameter`` is for valAues you want to keep track of but cannot set or get electronically. Holds the last value it was ``set`` to, and returns it on ``get``. """ @@ -703,7 +703,7 @@ def __init__(self, name, shape, instrument=None, sp_shape = (len(shape),) sp_types = (nt, DataArray, collections.Sequence, - collections.Iterator) + collections.Iterable) if (setpoints is not None and not is_sequence_of(setpoints, sp_types, shape=sp_shape)): raise ValueError('setpoints must be a tuple of arrays') From 670d3f1ba3493a01d1f51648c7bb8f6e1d1f3db7 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 1 Jul 2017 15:37:02 +1000 Subject: [PATCH 51/88] doc: remove trailing whitespaces --- qcodes/instrument/parameter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index de430b952fd..2358a4e7eb8 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -922,10 +922,10 @@ class GetLatest(DelegateAttributes, DeferredOperations): Args: parameter (Parameter): Parameter to be wrapped - - max_val_age (Optional[int]): The max time (in seconds) to trust a + + max_val_age (Optional[int]): The max time (in seconds) to trust a saved value obtained from get_latest(). If this parameter has not - been set or measured more recently than this, perform an + been set or measured more recently than this, perform an additional measurement. """ def __init__(self, parameter, max_val_age=None): From 79c20a05007ebf903573d17b6c9c911ce0a3ab19 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sun, 2 Jul 2017 15:52:50 +1000 Subject: [PATCH 52/88] fix: docstring typo --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 2358a4e7eb8..4333d4a60de 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -46,7 +46,7 @@ stepped ``write`` calls from a single ``set``. Does not need to be subclassed, just instantiated. -- ``ManualParameter`` is for valAues you want to keep track of but cannot +- ``ManualParameter`` is for values you want to keep track of but cannot set or get electronically. Holds the last value it was ``set`` to, and returns it on ``get``. """ From 22477f7b2ed9b5d4951650e296355f4ba4a49890 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sun, 2 Jul 2017 15:54:13 +1000 Subject: [PATCH 53/88] fix: allow step=None --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 4333d4a60de..41c9870c93c 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -386,7 +386,7 @@ def step(self, step): """ if step is None: self._step = step - if not getattr(self.vals, 'is_numeric', True): + elif not getattr(self.vals, 'is_numeric', True): raise TypeError('you can only step numeric parameters') elif not isinstance(step, (int, float)): raise TypeError('step must be a number') From 18164ba2cd8caa9604b5ddc13cea9821cfd51e59 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sun, 2 Jul 2017 17:00:49 +1000 Subject: [PATCH 54/88] fix: functools wraps function wraps get/set fuction --- qcodes/instrument/parameter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 41c9870c93c..8053c381d5b 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -226,7 +226,7 @@ def _save_val(self, value, validate=False): self._latest = {'value': value, 'ts': datetime.now()} def _wrap_get(self, get_function): - @wraps + @wraps(get_function) def get_wrapper(*args, **kwargs): try: # There might be cases where a .get also has args/kwargs @@ -260,7 +260,7 @@ def get_wrapper(*args, **kwargs): return get_wrapper def _wrap_set(self, set_function): - @wraps + @wraps(set_function) def set_wrapper(value, **kwargs): try: self.validate(value) @@ -535,7 +535,7 @@ def __init__(self, name, instrument=None, label=None, else: exec_str = instrument.ask if instrument else None self.get = Command(arg_count=0, cmd=get_cmd, exec_str=exec_str) - self.get = self.wrap_get(self.get) + self.get = self._wrap_get(self.get) if not hasattr(self, 'set') and set_cmd is not False: if set_cmd is None: @@ -543,7 +543,7 @@ def __init__(self, name, instrument=None, label=None, else: exec_str = instrument.write if instrument else None self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) - self.set = self.wrap_set(self.set) + self.set = self._wrap_set(self.set) self._meta_attrs.extend(['label', 'unit', 'vals']) From 21b2be4e18709811c4bbc546afc5e54e14ba1abd Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sun, 2 Jul 2017 17:01:20 +1000 Subject: [PATCH 55/88] fix: instrument with empty name not included in full_name --- qcodes/instrument/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 8053c381d5b..16efa550d90 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -154,8 +154,8 @@ def __init__(self, name, instrument, snapshot_get, metadata, def __str__(self): """Include the instrument name with the Parameter name if possible.""" - inst_name = getattr(self._instrument, 'name', None) - if inst_name is not None: + inst_name = getattr(self._instrument, 'name', '') + if inst_name: return '{}_{}'.format(inst_name, self.name) else: return self.name From b1f8cd5f6ef1853b7eabb9649deb4825f2328225 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sun, 2 Jul 2017 17:07:46 +1000 Subject: [PATCH 56/88] refactor: change get_sweep_values to get_ramp_values --- qcodes/instrument/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 16efa550d90..9b46c41f5e3 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -284,7 +284,7 @@ def set_wrapper(value, **kwargs): # In some cases intermediate sweep values must be used. # Unless `self.step` is defined, get_sweep_values will return # a list containing only `value`. - for val in self.get_sweep_values(value, step=self.step): + for val in self.get_ramp_values(value, step=self.step): # Check if delay between set operations is required t_elapsed = time.perf_counter() - self._t_last_set @@ -314,7 +314,7 @@ def set_wrapper(value, **kwargs): return set_wrapper - def get_sweep_values(self, value, step=None): + def get_ramp_values(self, value, step=None): """ Sweep to a given value from a starting value This method can be overridden to have a custom sweep behaviour. From 54d358724f9ecee6e3e760a89982aac7642021dc Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sun, 2 Jul 2017 19:34:31 +1000 Subject: [PATCH 57/88] fix: change add_parameter default parameter, ramping fix --- qcodes/instrument/base.py | 5 ++--- qcodes/instrument/parameter.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 24f1efcaa52..bf0c9e44fb7 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -8,7 +8,7 @@ from qcodes.utils.metadata import Metadatable from qcodes.utils.helpers import DelegateAttributes, strip_attrs, full_class from qcodes.utils.validators import Anything -from .parameter import StandardParameter +from .parameter import Parameter from .function import Function @@ -252,8 +252,7 @@ def find_instrument(cls, name, instrument_class=None): return ins - def add_parameter(self, name, parameter_class=StandardParameter, - **kwargs): + def add_parameter(self, name, parameter_class=Parameter, **kwargs): """ Bind one Parameter to this instrument. diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 9b46c41f5e3..71b4110ffab 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -326,7 +326,7 @@ def get_ramp_values(self, value, step=None): """ if step is None: - return value + return [value] else: start_value = self.get_latest() @@ -1160,7 +1160,7 @@ def __init__(self, name, instrument=None, set_cmd=False, set_parser=None, delay=None, max_delay=None, step=None, max_val_age=3600, vals=None, val_mapping=None, **kwargs): - super().__init__(self, name, instrument=instrument, + super().__init__(name, instrument=instrument, get_cmd=get_cmd, get_parser=get_parser, set_cmd=set_cmd, set_parser=set_parser, post_delay=delay, step=step, max_val_age=max_val_age, From d147395fa015603e05718bbcbae91c65a8963083 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 8 Jul 2017 15:17:42 +1000 Subject: [PATCH 58/88] fix: reverted Iterable to Iterator, explicitly added np.ndarray --- qcodes/instrument/parameter.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 71b4110ffab..3d37896efd8 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -421,9 +421,11 @@ def post_delay(self, post_delay): ValueError: If delay is negative """ if not isinstance(post_delay, (int, float)): - raise TypeError('delay must be a number') + raise TypeError( + 'post_delay ({}) must be a number'.format(post_delay)) if post_delay < 0: - raise ValueError('delay must not be negative') + raise ValueError( + 'post_delay ({}) must not be negative'.format(post_delay)) self._post_delay = post_delay @property @@ -450,9 +452,11 @@ def inter_delay(self, inter_delay): ValueError: If delay is negative """ if not isinstance(inter_delay, (int, float)): - raise TypeError('delay must be a number') + raise TypeError( + 'inter_delay ({}) must be a number'.format(inter_delay)) if inter_delay < 0: - raise ValueError('delay must not be negative') + raise ValueError( + 'inter_delay ({}) must not be negative'.format(inter_delay)) self._inter_delay = inter_delay # Deprecated @@ -703,7 +707,7 @@ def __init__(self, name, shape, instrument=None, sp_shape = (len(shape),) sp_types = (nt, DataArray, collections.Sequence, - collections.Iterable) + collections.Iterator, numpy.ndarray) if (setpoints is not None and not is_sequence_of(setpoints, sp_types, shape=sp_shape)): raise ValueError('setpoints must be a tuple of arrays') @@ -861,7 +865,7 @@ def __init__(self, name, names, shapes, instrument=None, self.shapes = shapes sp_types = (nt, DataArray, collections.Sequence, - collections.Iterable) + collections.Iterator, numpy.ndarray) if not _is_nested_sequence_or_none(setpoints, sp_types, shapes): raise ValueError('setpoints must be a tuple of tuples of arrays') @@ -942,7 +946,7 @@ def get(self): state = self.parameter._latest if self.max_val_age is None: # Return last value since max_val_age is not specified - return state + return state['value'] else: oldest_ok_val = datetime.now() - timedelta(seconds=self.max_val_age) if state['ts'] is None or state['ts'] < oldest_ok_val: @@ -1138,7 +1142,7 @@ class InstrumentRefParameter(Parameter): def __init__(self, *args, **kwargs): kwargs['vals'] = kwargs.get('vals', Strings()) - super().__init__(*args, **kwargs) + super().__init__(set_cmd=None, *args, **kwargs) # TODO(nulinspiratie) check class works now it's subclassed from Parameter def get_instr(self): @@ -1158,7 +1162,7 @@ class StandardParameter(Parameter): def __init__(self, name, instrument=None, get_cmd=False, get_parser=None, set_cmd=False, set_parser=None, - delay=None, max_delay=None, step=None, max_val_age=3600, + delay=0, max_delay=None, step=None, max_val_age=3600, vals=None, val_mapping=None, **kwargs): super().__init__(name, instrument=instrument, get_cmd=get_cmd, get_parser=get_parser, @@ -1174,5 +1178,5 @@ def __init__(self, name, instrument=None, initial_value=None, **kwargs): super().__init__(name=name, instrument=instrument, get_cmd=None, set_cmd=None, initial_value=initial_value, **kwargs) - logging.warning('`ManualParameter` is deprecated, use `Parameter` ' - 'instead with `set_cmd=None`. {}'.format(self)) \ No newline at end of file + logging.warning('Parameter {}: `ManualParameter` is deprecated, use ' + '`Parameter` instead with `set_cmd=None`.'.format(self)) \ No newline at end of file From 2902e985586f2cc71e66206b6c833028aceb25b4 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Sat, 8 Jul 2017 15:24:10 +1000 Subject: [PATCH 59/88] fix: Forgot to add helpers to previous commit --- qcodes/utils/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/utils/helpers.py b/qcodes/utils/helpers.py index a9668d807d9..3500f404b29 100644 --- a/qcodes/utils/helpers.py +++ b/qcodes/utils/helpers.py @@ -6,7 +6,7 @@ import sys import time -from collections import Iterable, Sequence, Mapping +from collections import Iterator, Sequence, Mapping from copy import deepcopy import numpy as np @@ -72,7 +72,7 @@ def is_sequence(obj): We do not consider strings or unordered collections like sets to be sequences, but we do accept iterators (such as generators) """ - return (isinstance(obj, (Iterable, Sequence)) and + return (isinstance(obj, (Iterator, Sequence, np.ndarray)) and not isinstance(obj, (str, bytes, io.IOBase))) From 3454aadb5f44ad3adfebd4b3feff0a58f8e09501 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Sun, 23 Jul 2017 18:09:21 +0200 Subject: [PATCH 60/88] correct conflict resolution all these methods have been moved to the instrumentbase class --- qcodes/instrument/base.py | 161 +------------------------------------- 1 file changed, 1 insertion(+), 160 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 0f17ef0746c..34390318a84 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -44,8 +44,7 @@ def __init__(self, name, **kwargs): self.submodules = {} super().__init__(**kwargs) - def add_parameter(self, name, parameter_class=StandardParameter, - **kwargs): + def add_parameter(self, name, parameter_class=Parameter, **kwargs): """ Bind one Parameter to this instrument. @@ -524,164 +523,6 @@ def find_instrument(cls, name, instrument_class=None): return ins - def add_parameter(self, name, parameter_class=Parameter, **kwargs): - """ - Bind one Parameter to this instrument. - - Instrument subclasses can call this repeatedly in their ``__init__`` - for every real parameter of the instrument. - - In this sense, parameters are the state variables of the instrument, - anything the user can set and/or get - - Args: - name (str): How the parameter will be stored within - ``instrument.parameters`` and also how you address it using the - shortcut methods: ``instrument.set(param_name, value)`` etc. - - parameter_class (Optional[type]): You can construct the parameter - out of any class. Default ``StandardParameter``. - - **kwargs: constructor arguments for ``parameter_class``. - - Raises: - KeyError: if this instrument already has a parameter with this - name. - """ - if name in self.parameters: - raise KeyError('Duplicate parameter name {}'.format(name)) - param = parameter_class(name=name, instrument=self, **kwargs) - self.parameters[name] = param - - def add_function(self, name, **kwargs): - """ - Bind one Function to this instrument. - - Instrument subclasses can call this repeatedly in their ``__init__`` - for every real function of the instrument. - - This functionality is meant for simple cases, principally things that - map to simple commands like '\*RST' (reset) or those with just a few - arguments. It requires a fixed argument count, and positional args - only. If your case is more complicated, you're probably better off - simply making a new method in your ``Instrument`` subclass definition. - - Args: - name (str): how the Function will be stored within - ``instrument.Functions`` and also how you address it using the - shortcut methods: ``instrument.call(func_name, *args)`` etc. - - **kwargs: constructor kwargs for ``Function`` - - Raises: - KeyError: if this instrument already has a function with this - name. - """ - if name in self.functions: - raise KeyError('Duplicate function name {}'.format(name)) - func = Function(name=name, instrument=self, **kwargs) - self.functions[name] = func - - def add_submodule(self, name, submodule): - """ - Bind one submodule to this instrument. - - Instrument subclasses can call this repeatedly in their ``__init__`` - method for every submodule of the instrument. - - Submodules can effectively be considered as instruments within the main - instrument, and should at minimum be snapshottable. For example, they can - be used to either store logical groupings of parameters, which may or may - not be repeated, or channel lists. - - Args: - name (str): how the submodule will be stored within ``instrument.submodules`` - and also how it can be addressed. - - submodule (Metadatable): The submodule to be stored. - - Raises: - KeyError: if this instrument already contains a submodule with this - name. - TypeError: if the submodule that we are trying to add is not an instance - of an Metadatable object. - """ - if name in self.submodules: - raise KeyError('Duplicate submodule name {}'.format(name)) - if not isinstance(submodule, Metadatable): - raise TypeError('Submodules must be metadatable.') - self.submodules[name] = submodule - - def snapshot_base(self, update=False): - """ - State of the instrument as a JSON-compatible dict. - - Args: - update (bool): If True, update the state by querying the - instrument. If False, just use the latest values in memory. - - Returns: - dict: base snapshot - """ - snap = {'parameters': dict((name, param.snapshot(update=update)) - for name, param in self.parameters.items()), - 'functions': dict((name, func.snapshot(update=update)) - for name, func in self.functions.items()), - 'submodules': dict((name, subm.snapshot(update=update)) - for name, subm in self.submodules.items()), - '__class__': full_class(self), - } - for attr in set(self._meta_attrs): - if hasattr(self, attr): - snap[attr] = getattr(self, attr) - return snap - - def print_readable_snapshot(self, update=False, max_chars=80): - """ - Prints a readable version of the snapshot. - The readable snapshot includes the name, value and unit of each - parameter. - A convenience function to quickly get an overview of the status of an instrument. - - Args: - update (bool) : If True, update the state by querying the - instrument. If False, just use the latest values in memory. - This argument gets passed to the snapshot function. - max_chars (int) : the maximum number of characters per line. The - readable snapshot will be cropped if this value is exceeded. - Defaults to 80 to be consistent with default terminal width. - """ - floating_types = (float, np.integer, np.floating) - snapshot = self.snapshot(update=update) - - par_lengths = [len(p) for p in snapshot['parameters']] - - # Min of 50 is to prevent a super long parameter name to break this - # function - par_field_len = min(max(par_lengths)+1, 50) - - print(self.name + ':') - print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') - print('-'*80) - for par in sorted(snapshot['parameters']): - name = snapshot['parameters'][par]['name'] - msg = '{0:<{1}}:'.format(name, par_field_len) - val = snapshot['parameters'][par]['value'] - unit = snapshot['parameters'][par].get('unit', None) - if unit is None: - # this may be a multi parameter - unit = snapshot['parameters'][par].get('units', None) - if isinstance(val, floating_types): - msg += '\t{:.5g} '.format(val) - else: - msg += '\t{} '.format(val) - if unit is not '': # corresponds to no unit - msg += '({})'.format(unit) - # Truncate the message if it is longer than max length - if len(msg) > max_chars and not max_chars == -1: - msg = msg[0:max_chars-3] + '...' - print(msg) - # `write_raw` and `ask_raw` are the interface to hardware # # `write` and `ask` are standard wrappers to help with error reporting # # From b48bc07a14aca5d43ac056cb33f1a6f529a81170 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Mon, 24 Jul 2017 09:40:24 +1000 Subject: [PATCH 61/88] fix: replaced reference to `has_get`, `has_set` --- qcodes/instrument/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 34390318a84..69d081f2362 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -281,7 +281,7 @@ def validate_status(self, verbose=False): """ for k, p in self.parameters.items(): - if p.has_get and p.has_set: + if hasattr(p, 'get') and hasattr(p, 'set'): value = p.get() if verbose: print('validate_status: param %s: %s' % (k, value)) From 1d8ef645366f44b7bd24161a86d388c892f757c9 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Mon, 28 Aug 2017 16:36:19 +1000 Subject: [PATCH 62/88] test: fixed testParameter tests --- qcodes/tests/test_parameter.py | 84 +++++++++++----------------------- 1 file changed, 26 insertions(+), 58 deletions(-) diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index f709eaab9a0..eee3a2a0814 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -60,7 +60,7 @@ def set(self, v): class TestParameter(TestCase): def test_no_name(self): with self.assertRaises(TypeError): - GettableParam() + Parameter() def test_default_attributes(self): # Test the default attributes, providing only a name @@ -137,74 +137,42 @@ def test_explicit_attributes(self): 'setpoint_labels', 'full_names']: self.assertFalse(hasattr(p, attr), attr) - def test_units(self): - with LogCapture() as logs: - p = GettableParam('p', units='V') - - self.assertIn('deprecated', logs.value) - self.assertEqual(p.unit, 'V') - - with LogCapture() as logs: - self.assertEqual(p.units, 'V') - - self.assertIn('deprecated', logs.value) - - with LogCapture() as logs: - p = GettableParam('p', unit='Tesla', units='Gauss') - - self.assertIn('deprecated', logs.value) - self.assertEqual(p.unit, 'Tesla') - - with LogCapture() as logs: - self.assertEqual(p.units, 'Tesla') - - self.assertIn('deprecated', logs.value) - - def test_repr(self): - for i in [0, "foo", "", "fåil"]: - with self.subTest(i=i): - param = GettableParam(name=i) - s = param.__repr__() - st = '<{}.{}: {} at {}>'.format( - param.__module__, param.__class__.__name__, - param.name, id(param)) - self.assertEqual(s, st) - def test_has_set_get(self): - # you can't instantiate a Parameter directly anymore, only a subclass, - # because you need a get or a set method. - with self.assertRaises(AttributeError): - Parameter('no_get_or_set') - - gp = GettableParam('1') - self.assertTrue(gp.has_get) - self.assertFalse(gp.has_set) + # Create parameter that has no set_cmd, and get_cmd returns last value + gettable_parameter = Parameter('1', set_cmd=False, get_cmd=None) + self.assertTrue(hasattr(gettable_parameter, 'get')) + self.assertFalse(hasattr(gettable_parameter, 'set')) with self.assertRaises(NotImplementedError): - gp(1) - - sp = SettableParam('2') - self.assertFalse(sp.has_get) - self.assertTrue(sp.has_set) + gettable_parameter(1) + # Initial value is None if not explicitly set + self.assertIsNone(gettable_parameter()) + + # Create parameter that saves value during set, and has no get_cmd + settable_parameter = Parameter('2', set_cmd=None, get_cmd=False) + self.assertFalse(hasattr(settable_parameter, 'get')) + self.assertTrue(hasattr(settable_parameter, 'set')) with self.assertRaises(NotImplementedError): - sp() + settable_parameter() + settable_parameter(42) - sgp = SimpleManualParam('3') - self.assertTrue(sgp.has_get) - self.assertTrue(sgp.has_set) - sgp(22) - self.assertEqual(sgp(), 22) + settable_gettable_parameter = Parameter('3', set_cmd=None, get_cmd=None) + self.assertTrue(hasattr(settable_gettable_parameter, 'set')) + self.assertTrue(hasattr(settable_gettable_parameter, 'get')) + self.assertIsNone(settable_gettable_parameter()) + settable_gettable_parameter(22) + self.assertEqual(settable_gettable_parameter(), 22) - def test_full_name(self): + def test_str_representation(self): # three cases where only name gets used for full_name for instrument in blank_instruments: - p = GettableParam(name='fred') + p = Parameter(name='fred') p._instrument = instrument - self.assertEqual(p.full_name, 'fred') + self.assertEqual(str(p), 'fred') # and finally an instrument that really has a name - p = GettableParam(name='wilma') + p = Parameter(name='wilma') p._instrument = named_instrument - self.assertEqual(p.full_name, 'astro_wilma') + self.assertEqual(str(p), 'astro_wilma') def test_bad_validator(self): with self.assertRaises(TypeError): From ae655b67f95cc25beea2d4a5ffec5931e791fa44 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Mon, 28 Aug 2017 16:54:26 +1000 Subject: [PATCH 63/88] test:fix most other tests --- qcodes/tests/test_parameter.py | 102 +++++++++++---------------------- 1 file changed, 32 insertions(+), 70 deletions(-) diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index eee3a2a0814..4e44bd77f13 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -14,6 +14,7 @@ class GettableParam(Parameter): + """ Parameter that keeps track of number of get operations""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._get_count = 0 @@ -24,31 +25,6 @@ def get(self): return 42 -class SimpleManualParam(Parameter): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._save_val(0) - self._v = 0 - - def get(self): - return self._v - - def set(self, v): - self._save_val(v) - self._v = v - - -class SettableParam(Parameter): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._save_val(0) - self._v = 0 - - def set(self, v): - self._save_val(v) - self._v = v - - blank_instruments = ( None, # no instrument at all namedtuple('noname', '')(), # no .name @@ -120,6 +96,7 @@ def test_explicit_attributes(self): # test snapshot_get by looking at _get_count self.assertEqual(p._get_count, 0) + # Snapshot should not perform get since snapshot_get is False snap = p.snapshot(update=True) self.assertEqual(p._get_count, 0) snap_expected = { @@ -137,6 +114,18 @@ def test_explicit_attributes(self): 'setpoint_labels', 'full_names']: self.assertFalse(hasattr(p, attr), attr) + def test_snapshot_value(self): + p_snapshot = Parameter('no_snapshot', set_cmd=None, get_cmd=None, + snapshot_value=True) + p_snapshot(42) + snap = p_snapshot.snapshot() + self.assertIn('value', snap) + p_no_snapshot = Parameter('no_snapshot', set_cmd=None, get_cmd=None, + snapshot_value=False) + p_no_snapshot(42) + snap = p_no_snapshot.snapshot() + self.assertNotIn('value', snap) + def test_has_set_get(self): # Create parameter that has no set_cmd, and get_cmd returns last value gettable_parameter = Parameter('1', set_cmd=False, get_cmd=None) @@ -176,7 +165,7 @@ def test_str_representation(self): def test_bad_validator(self): with self.assertRaises(TypeError): - GettableParam('p', vals=[1, 2, 3]) + Parameter('p', vals=[1, 2, 3]) class SimpleArrayParam(ArrayParameter): @@ -270,30 +259,6 @@ def test_explicit_attrbutes(self): self.assertIn(name, p.__doc__) self.assertIn(docstring, p.__doc__) - def test_units(self): - with LogCapture() as logs: - p = SimpleArrayParam([6, 7], 'p', (2,), units='V') - - self.assertIn('deprecated', logs.value) - self.assertEqual(p.unit, 'V') - - with LogCapture() as logs: - self.assertEqual(p.units, 'V') - - self.assertIn('deprecated', logs.value) - - with LogCapture() as logs: - p = SimpleArrayParam([6, 7], 'p', (2,), - unit='Tesla', units='Gauss') - - self.assertIn('deprecated', logs.value) - self.assertEqual(p.unit, 'Tesla') - - with LogCapture() as logs: - self.assertEqual(p.units, 'Tesla') - - self.assertIn('deprecated', logs.value) - def test_has_set_get(self): name = 'array_param' shape = (3,) @@ -450,8 +415,8 @@ def test_has_set_get(self): p = SimpleMultiParam([0, [1, 2, 3], [[4, 5], [6, 7]]], name, names, shapes) - self.assertTrue(p.has_get) - self.assertFalse(p.has_set) + self.assertTrue(hasattr(p, 'get')) + self.assertFalse(hasattr(p, 'set')) # We allow creation of Multiparameters with set to support # instruments that already make use of them. with self.assertWarns(UserWarning): @@ -539,20 +504,18 @@ def test_param_cmd_with_parsing(self): self.assertEqual(p(), 5) def test_settable(self): - p = StandardParameter('p', set_cmd=self.set_p) + p = Parameter('p', set_cmd=self.set_p, get_cmd=False) p(10) self.assertEqual(self._p, 10) with self.assertRaises(NotImplementedError): p() - with self.assertRaises(NotImplementedError): - p.get() - self.assertTrue(p.has_set) - self.assertFalse(p.has_get) + self.assertTrue(hasattr(p, 'set')) + self.assertFalse(hasattr(p, 'get')) def test_gettable(self): - p = StandardParameter('p', get_cmd=self.get_p) + p = Parameter('p', get_cmd=self.get_p) self._p = 21 self.assertEqual(p(), 21) @@ -560,15 +523,13 @@ def test_gettable(self): with self.assertRaises(NotImplementedError): p(10) - with self.assertRaises(NotImplementedError): - p.set(10) - self.assertTrue(p.has_get) - self.assertFalse(p.has_set) + self.assertTrue(hasattr(p, 'get')) + self.assertFalse(hasattr(p, 'set')) def test_val_mapping_basic(self): - p = StandardParameter('p', set_cmd=self.set_p, get_cmd=self.get_p, - val_mapping={'off': 0, 'on': 1}) + p = Parameter('p', set_cmd=self.set_p, get_cmd=self.get_p, + val_mapping={'off': 0, 'on': 1}) p('off') self.assertEqual(self._p, 0) @@ -587,17 +548,18 @@ def test_val_mapping_basic(self): p() def test_val_mapping_with_parsers(self): + # TODO change comments, this is now possible # you can't use set_parser with val_mapping... just too much # indirection since you also have set_cmd with self.assertRaises(TypeError): - StandardParameter('p', set_cmd=self.set_p, get_cmd=self.get_p, - val_mapping={'off': 0, 'on': 1}, - set_parser=self.parse_set_p) + Parameter('p', set_cmd=self.set_p, get_cmd=self.get_p, + val_mapping={'off': 0, 'on': 1}, + set_parser=self.parse_set_p) # but you *can* use get_parser with val_mapping - p = StandardParameter('p', set_cmd=self.set_p_prefixed, - get_cmd=self.get_p, get_parser=self.strip_prefix, - val_mapping={'off': 0, 'on': 1}) + p = Parameter('p', set_cmd=self.set_p_prefixed, + get_cmd=self.get_p, get_parser=self.strip_prefix, + val_mapping={'off': 0, 'on': 1}) p('off') self.assertEqual(self._p, 'PVAL: 0') From 1fc36945037104e136c6d146785530d3e0531ce5 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 12:20:38 +1000 Subject: [PATCH 64/88] fix: CombinedParameter defaults to set_cmd=None --- qcodes/tests/test_parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index 4e44bd77f13..640ec31c1c3 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -410,7 +410,7 @@ def test_has_set_get(self): names = ['0D', '1D', '2D'] shapes = ((), (3,), (2, 2)) with self.assertRaises(AttributeError): - MultiParameter(name, names, shapes) + MultiParameter(name, names, shapes, set_cmd=None) p = SimpleMultiParam([0, [1, 2, 3], [[4, 5], [6, 7]]], name, names, shapes) From 07fef09c5ab2594ec11b489804a80481264f3325 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 16:24:39 +1000 Subject: [PATCH 65/88] fix: all existing tests are working --- qcodes/instrument/parameter.py | 16 ++++++++++++++-- qcodes/tests/test_parameter.py | 35 +++++++++++++++++----------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index c683cd4b3fc..d30be785f50 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -250,7 +250,12 @@ def get_wrapper(*args, **kwargs): value /= self.scale if self.val_mapping is not None: - value = self.inverse_val_mapping[value] + if value in self.inverse_val_mapping: + value = self.inverse_val_mapping[value] + elif int(value) in self.inverse_val_mapping: + value = self.inverse_val_mapping[int(value)] + else: + raise KeyError("'{}' not in val_mapping".format(value)) self._save_val(value) return value @@ -299,7 +304,8 @@ def set_wrapper(value, **kwargs): set_function(val, **kwargs) self.raw_value = val - self._save_val(val, validate=True) + self._save_val(val, validate=(self.val_mapping is None and + self.set_parser is None)) # Update last set time (used for calculating delays) self._t_last_set = time.perf_counter() @@ -753,6 +759,9 @@ def __init__(self, name, shape, instrument=None, '', self.__doc__)) + if not hasattr(self, 'get') and not hasattr(self, 'set'): + raise AttributeError('ArrayParameter must have a get, set or both') + def _is_nested_sequence_or_none(obj, types, shapes): """Validator for MultiParameter setpoints/names/labels""" if obj is None: @@ -912,6 +921,9 @@ def __init__(self, name, names, shapes, instrument=None, '', self.__doc__)) + if not hasattr(self, 'get') and not hasattr(self, 'set'): + raise AttributeError('MultiParameter must have a get, set or both') + @property def full_names(self): """Include the instrument name with the Parameter names if possible.""" diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index 640ec31c1c3..bffc2cc6cb4 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -9,7 +9,7 @@ Parameter, ArrayParameter, MultiParameter, ManualParameter, StandardParameter, InstrumentRefParameter) from qcodes.utils.helpers import LogCapture -from qcodes.utils.validators import Numbers +import qcodes.utils.validators as vals from qcodes.tests.instrument_mocks import DummyInstrument @@ -64,7 +64,7 @@ def test_default_attributes(self): 'label': name, 'unit': '', 'value': 42, - 'vals': repr(Numbers()) + 'vals': repr(vals.Numbers()) } for k, v in snap_expected.items(): self.assertEqual(snap[k], v) @@ -77,7 +77,7 @@ def test_explicit_attributes(self): docstring = 'DOCS!' metadata = {'gain': 100} p = GettableParam(name, label=label, unit=unit, - vals=Numbers(5, 10), docstring=docstring, + vals=vals.Numbers(5, 10), docstring=docstring, snapshot_get=False, metadata=metadata) self.assertEqual(p.name, name) @@ -103,7 +103,7 @@ def test_explicit_attributes(self): 'name': name, 'label': label, 'unit': unit, - 'vals': repr(Numbers(5, 10)), + 'vals': repr(vals.Numbers(5, 10)), 'metadata': metadata } for k, v in snap_expected.items(): @@ -267,8 +267,8 @@ def test_has_set_get(self): p = SimpleArrayParam([1, 2, 3], name, shape) - self.assertTrue(p.has_get) - self.assertFalse(p.has_set) + self.assertTrue(hasattr(p, 'get')) + self.assertFalse(hasattr(p, 'set')) with self.assertRaises(AttributeError): SettableArray([1, 2, 3], name, shape) @@ -410,7 +410,7 @@ def test_has_set_get(self): names = ['0D', '1D', '2D'] shapes = ((), (3,), (2, 2)) with self.assertRaises(AttributeError): - MultiParameter(name, names, shapes, set_cmd=None) + MultiParameter(name, names, shapes) p = SimpleMultiParam([0, [1, 2, 3], [[4, 5], [6, 7]]], name, names, shapes) @@ -471,7 +471,7 @@ def test_bare_function(self): def doubler(x): p.set(x * 2) - f = Function('f', call_cmd=doubler, args=[Numbers(-10, 10)]) + f = Function('f', call_cmd=doubler, args=[vals.Numbers(-10, 10)]) f(4) self.assertEqual(p.get(), 8) @@ -529,7 +529,8 @@ def test_gettable(self): def test_val_mapping_basic(self): p = Parameter('p', set_cmd=self.set_p, get_cmd=self.get_p, - val_mapping={'off': 0, 'on': 1}) + val_mapping={'off': 0, 'on': 1}, + vals=vals.Enum('off', 'on')) p('off') self.assertEqual(self._p, 0) @@ -548,18 +549,16 @@ def test_val_mapping_basic(self): p() def test_val_mapping_with_parsers(self): - # TODO change comments, this is now possible - # you can't use set_parser with val_mapping... just too much - # indirection since you also have set_cmd - with self.assertRaises(TypeError): - Parameter('p', set_cmd=self.set_p, get_cmd=self.get_p, - val_mapping={'off': 0, 'on': 1}, - set_parser=self.parse_set_p) + # set_parser with val_mapping + Parameter('p', set_cmd=self.set_p, get_cmd=self.get_p, + val_mapping={'off': 0, 'on': 1}, + set_parser=self.parse_set_p) - # but you *can* use get_parser with val_mapping + # get_parser with val_mapping p = Parameter('p', set_cmd=self.set_p_prefixed, get_cmd=self.get_p, get_parser=self.strip_prefix, - val_mapping={'off': 0, 'on': 1}) + val_mapping={'off': 0, 'on': 1}, + vals=vals.Enum('off', 'on')) p('off') self.assertEqual(self._p, 'PVAL: 0') From c99111d8bfbc8e3f3ca796ef8698bfc6dc7c2d93 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 16:40:16 +1000 Subject: [PATCH 66/88] fix: get_ramp_values also returns final value --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index d30be785f50..31a0cf71f25 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -350,7 +350,7 @@ def get_ramp_values(self, value, step=None): return [] # drop the initial value, we're already there - return permissive_range(start_value, value, step)[1:] + return permissive_range(start_value, value, step)[1:] + [value] def validate(self, value): """ From 1c5eae17d801d776c04ecca880b69b480e178b4e Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 16:40:55 +1000 Subject: [PATCH 67/88] test: add test for step, ramp, scale, raw_value --- qcodes/tests/test_parameter.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index bffc2cc6cb4..a73ed80ee74 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -32,6 +32,13 @@ def get(self): ) named_instrument = namedtuple('yesname', 'name')('astro') +class MemoryParameter(Parameter): + def __init__(self, **kwargs): + self.last_values = [] + super().__init__(set_cmd=self.add_value, **kwargs) + + def add_value(self, value): + self.last_values.append(value) class TestParameter(TestCase): def test_no_name(self): @@ -167,6 +174,30 @@ def test_bad_validator(self): with self.assertRaises(TypeError): Parameter('p', vals=[1, 2, 3]) + def test_step_ramp(self): + p = MemoryParameter(name='test_step') + p(42) + self.assertEqual(p.last_values, [42]) + p.step = 1 + + self.assertEqual(p.get_ramp_values(44.5, 1), [43, 44, 44.5]) + + p(44.5) + self.assertEqual(p.last_values, [42, 43, 44, 44.5]) + + def test_scale_raw_value(self): + p = Parameter(name='test_scale_raw_value', set_cmd=None) + p(42) + self.assertEqual(p.raw_value, 42) + + p.scale = 2 + self.assertEqual(p.raw_value, 42) # No set/get cmd performed + self.assertEqual(p(), 21) + + p(10) + self.assertEqual(p.raw_value, 20) + self.assertEqual(p(), 10) + class SimpleArrayParam(ArrayParameter): def __init__(self, return_val, *args, **kwargs): From c3e1fa11d7d40a4291653ddbea27472e6afbf6cc Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 16:59:07 +1000 Subject: [PATCH 68/88] test: add test for GetLatest, including max_val_age --- qcodes/tests/test_parameter.py | 42 ++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index a73ed80ee74..99c20b50840 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -3,12 +3,12 @@ """ from collections import namedtuple from unittest import TestCase +from time import sleep from qcodes import Function from qcodes.instrument.parameter import ( Parameter, ArrayParameter, MultiParameter, ManualParameter, StandardParameter, InstrumentRefParameter) -from qcodes.utils.helpers import LogCapture import qcodes.utils.validators as vals from qcodes.tests.instrument_mocks import DummyInstrument @@ -33,12 +33,25 @@ def get(self): named_instrument = namedtuple('yesname', 'name')('astro') class MemoryParameter(Parameter): - def __init__(self, **kwargs): - self.last_values = [] - super().__init__(set_cmd=self.add_value, **kwargs) + def __init__(self, get_cmd=None, **kwargs): + self.set_values = [] + self.get_values = [] + super().__init__(set_cmd=self.add_set_value, + get_cmd=self.create_get_func(get_cmd), **kwargs) + + def add_set_value(self, value): + self.set_values.append(value) + + def create_get_func(self, func): + def get_func(): + if func is not None: + val = func() + else: + val = self._latest['value'] + self.get_values.append(val) + return val + return get_func - def add_value(self, value): - self.last_values.append(value) class TestParameter(TestCase): def test_no_name(self): @@ -177,13 +190,13 @@ def test_bad_validator(self): def test_step_ramp(self): p = MemoryParameter(name='test_step') p(42) - self.assertEqual(p.last_values, [42]) + self.assertListEqual(p.set_values, [42]) p.step = 1 - self.assertEqual(p.get_ramp_values(44.5, 1), [43, 44, 44.5]) + self.assertListEqual(p.get_ramp_values(44.5, 1), [43, 44, 44.5]) p(44.5) - self.assertEqual(p.last_values, [42, 43, 44, 44.5]) + self.assertListEqual(p.set_values, [42, 43, 44, 44.5]) def test_scale_raw_value(self): p = Parameter(name='test_scale_raw_value', set_cmd=None) @@ -198,6 +211,17 @@ def test_scale_raw_value(self): self.assertEqual(p.raw_value, 20) self.assertEqual(p(), 10) + def test_latest_value(self): + p = MemoryParameter(name='test_latest_value', get_cmd=lambda: 21) + + p(42) + self.assertEqual(p.get_latest(), 42) + self.assertListEqual(p.get_values, []) + + p.get_latest.max_val_age = 0.1 + sleep(0.2) + self.assertEqual(p.get_latest(), 21) + self.assertEqual(p.get_values, [21]) class SimpleArrayParam(ArrayParameter): def __init__(self, return_val, *args, **kwargs): From 552569d364103105581eb26f57387a4e08d7758f Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 17:19:50 +1000 Subject: [PATCH 69/88] test: fix combined tests --- qcodes/instrument/parameter.py | 2 ++ qcodes/tests/test_combined_par.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 31a0cf71f25..17aec10c9de 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -197,6 +197,7 @@ def snapshot_base(self, update=False): state = copy(self._latest) state['__class__'] = full_class(self) + state['full_name'] = str(self) if not self._snapshot_value: state.pop('value') @@ -1136,6 +1137,7 @@ def snapshot_base(self, update=False): meta_data['__class__'] = full_class(self) meta_data['unit'] = self.parameter.unit meta_data['label'] = self.parameter.label + meta_data['full_name'] = self.parameter.full_name meta_data['aggregator'] = repr(getattr(self, 'f', None)) for param in self.parameters: meta_data[str(param)] = param.snapshot() diff --git a/qcodes/tests/test_combined_par.py b/qcodes/tests/test_combined_par.py index 683ce929642..efa4cb0f394 100644 --- a/qcodes/tests/test_combined_par.py +++ b/qcodes/tests/test_combined_par.py @@ -23,6 +23,9 @@ def __init__(self, name): self.name = name self.full_name = name + def __str__(self): + return self.full_name + def set(self, value): value = value * 2 return value @@ -115,7 +118,7 @@ def testMeta(self): out["unit"] = unit out["label"] = label out["full_name"] = name - out["aggreagator"] = repr(linear) + out["aggregator"] = repr(linear) for param in sweep_values.parameters: out[param.full_name] = {} self.assertEqual(out, snap) From a079f05b2190d944d78b5ac7bf178f432c2e7190 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 17:31:25 +1000 Subject: [PATCH 70/88] test: fix test_loop error --- qcodes/tests/test_loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/tests/test_loop.py b/qcodes/tests/test_loop.py index 6409e67e1cf..6c5a6e85ef2 100644 --- a/qcodes/tests/test_loop.py +++ b/qcodes/tests/test_loop.py @@ -462,7 +462,7 @@ def get(self): self._count -= 1 if self._count <= 0: raise _QcodesBreak - return super().get() + return self.get_latest() def reset(self): self._count = self._initial_count From c950c7b86280c955ef178fa51cba5197189e9cf4 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 18:14:11 +1000 Subject: [PATCH 71/88] docs: update global docstring --- qcodes/instrument/parameter.py | 46 +++++++++++++++++----------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 17aec10c9de..4a84edb1979 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -6,19 +6,29 @@ by either using or subclassing one of the classes defined here, but you can also use any class with the right attributes. -TODO (alexcjohnson) update this with the real duck-typing requirements or -create an ABC for Parameter and MultiParameter - or just remove this statement -if everyone is happy to use these classes. +All parameter classes are subclassed from _BaseParameter (except CombinedParameter). +The _BaseParameter provides functionality that is common to all parameter types, +such as ramping and scaling of values, adding delays (see documentation for +details). This file defines four classes of parameters: -``Parameter``, ``ArrayParameter``, and ``MultiParameter`` must be subclassed: - -- ``Parameter`` is the base class for scalar-valued parameters, if you have - custom code to read or write a single value. Provides ``sweep`` and - ``__getitem__`` (slice notation) methods to use a settable parameter as - the swept variable in a ``Loop``. To use, fill in ``super().__init__``, - and provide a ``get`` method, a ``set`` method, or both. +- ``Parameter`` is the base class for scalar-valued parameters. + Two primary ways in which it can be used: + 1. As an ``Instrument`` parameter that sends/receives commands. Provides a + standardized interface to construct strings to pass to the + instrument's ``write`` and ``ask`` methods + 2. As a variable that stores and returns a value. For instance, for storing + of values you want to keep track of but cannot set or get electronically. + Provides ``sweep`` and ``__getitem__`` (slice notation) methods to use a + settable parameter as the swept variable in a ``Loop``. + + By default only gettable, returning its last value. + This behaviour can be modified in two ways: + 1. Providing a ``get_cmd``/``set_cmd``, which can either be a callable, + or a VISA command string + 2. Creating a subclass with an explicit ``get``/``set`` method. This + enables more advanced functionality. - ``ArrayParameter`` is a base class for array-valued parameters, ie anything for which each ``get`` call returns an array of values that all have the @@ -35,20 +45,10 @@ that returns a sequence of values, and describe those values in ``super().__init__``. -``StandardParameter`` and ``ManualParameter`` can be instantiated directly: -- ``StandardParameter`` is the default class for instrument parameters - (see ``Instrument.add_parameter``). Can be gettable, settable, or both. - Provides a standardized interface to construct strings to pass to the - instrument's ``write`` and ``ask`` methods (but can also be given other - functions to execute on ``get`` or ``set``), to convert the string - responses to meaningful output, and optionally to ramp a setpoint with - stepped ``write`` calls from a single ``set``. Does not need to be - subclassed, just instantiated. - -- ``ManualParameter`` is for values you want to keep track of but cannot - set or get electronically. Holds the last value it was ``set`` to, and - returns it on ``get``. +TODO (alexcjohnson) update this with the real duck-typing requirements or +create an ABC for Parameter and MultiParameter - or just remove this statement +if everyone is happy to use these classes. """ from datetime import datetime, timedelta From 63afc677010e7b38c9609d6c7bf3b124b7e1d62b Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 18:27:55 +1000 Subject: [PATCH 72/88] docs: start with BaseParameter docs --- qcodes/instrument/parameter.py | 44 ++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 4a84edb1979..877ece194d4 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -25,8 +25,12 @@ By default only gettable, returning its last value. This behaviour can be modified in two ways: - 1. Providing a ``get_cmd``/``set_cmd``, which can either be a callable, - or a VISA command string + 1. Providing a ``get_cmd``/``set_cmd``, which can of the following: + a. callable, with zero args for get_cmd, one arg for set_cmd + b. VISA command string + c. None, in which case it retrieves its last value for ``get_cmd``, + and stores a value for ``set_cmd`` + d. False, in which case trying to get/set will raise an error. 2. Creating a subclass with an explicit ``get``/``set`` method. This enables more advanced functionality. @@ -44,13 +48,16 @@ feed data to a ``DataSet``. To use, provide a ``get`` method that returns a sequence of values, and describe those values in ``super().__init__``. + + ``CombinedParameter`` # TODO -TODO (alexcjohnson) update this with the real duck-typing requirements or -create an ABC for Parameter and MultiParameter - or just remove this statement -if everyone is happy to use these classes. """ +# TODO (alexcjohnson) update this with the real duck-typing requirements or +# create an ABC for Parameter and MultiParameter - or just remove this statement +# if everyone is happy to use these classes. + from datetime import datetime, timedelta from copy import copy import time @@ -74,10 +81,10 @@ class _BaseParameter(Metadatable, DeferredOperations): """ - Shared behavior for simple and multi parameters. Not intended to be used - directly, normally you should use ``StandardParameter`` or - ``ManualParameter``, or create your own subclass of ``Parameter`` or - ``MultiParameter``. + Shared behavior for all parameters. Not intended to be used + directly, normally you should use ``Parameter``, ``ArrayParameter``, + ``MultiParameter``, or ``CombinedParameter``. + Note that ``CombinedParameter`` is not yet a subclass of ``_BaseParameter`` Args: name (str): the local name of the parameter. Should be a valid @@ -94,13 +101,24 @@ class _BaseParameter(Metadatable, DeferredOperations): ``update=True``, for example if it takes too long to update. Default True. - max_val_age (Optional[int]): The max time (in seconds) to trust a + metadata (Optional[dict]): extra information to include with the + JSON snapshot of the parameter + + # TODO + step (Optional[float]): + scale (Optional[float]): + inter_delay (Optional[float]): + post_delay (Optional[float]): + val_mapping + get_parser + set_parser + snapshot_value (Optional[bool]) + vals (Optional[Validators]) + + max_val_age (Optional[float]): The max time (in seconds) to trust a saved value obtained from get_latest(). If this parameter has not been set or measured more recently than this, perform an additional measurement. - - metadata (Optional[dict]): extra information to include with the - JSON snapshot of the parameter """ def __init__(self, name, instrument, snapshot_get, metadata, From 5e77014612dcc00d54a47a0d01883762b4bde576 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 18:44:30 +1000 Subject: [PATCH 73/88] docs: finish BaseParameter --- qcodes/instrument/parameter.py | 47 +++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 877ece194d4..a637835e0d7 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -104,16 +104,45 @@ class _BaseParameter(Metadatable, DeferredOperations): metadata (Optional[dict]): extra information to include with the JSON snapshot of the parameter - # TODO - step (Optional[float]): - scale (Optional[float]): - inter_delay (Optional[float]): - post_delay (Optional[float]): - val_mapping - get_parser - set_parser + step (Optional[Union[int, float]]): max increment of parameter value. + Larger changes are broken into multiple steps this size. + When combined with delays, this acts as a ramp. + + scale (Optional[float]): Scale to multiply value with before + performing set. the internally multiplied value is stored in + `raw_value`. Can account for a voltage divider. + + inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) + between successive sets. If the previous set was less than this, + it will wait until the condition is met. + Can be set to 0 to go maximum speed with no errors. + + post_delay (Optional[Union[int, float]]): time (in seconds) to wait + after the *start* of each set, whether part of a sweep or not. + Can be set to 0 to go maximum speed with no errors. + + val_mapping (Optional[dict]): a bidirectional map data/readable values + to instrument codes, expressed as a dict: + ``{data_val: instrument_code}`` + For example, if the instrument uses '0' to mean 1V and '1' to mean + 10V, set val_mapping={1: '0', 10: '1'} and on the user side you + only see 1 and 10, never the coded '0' and '1' + If vals is omitted, will also construct a matching Enum validator. + NOTE: only applies to get if get_cmd is a string, and to set if + set_cmd is a string. + You can use ``val_mapping`` with ``get_parser``, in which case + ``get_parser`` acts on the return value from the instrument first, + then ``val_mapping`` is applied (in reverse). + + get_parser ( Optional[function]): function to transform the response + from get to the final output value. See also val_mapping + + set_parser (Optional[function]): function to transform the input set + value to an encoded value sent to the instrument. + See also val_mapping. snapshot_value (Optional[bool]) - vals (Optional[Validators]) + + vals (Optional[Validator]): a Validator object for this parameter max_val_age (Optional[float]): The max time (in seconds) to trust a saved value obtained from get_latest(). If this parameter has not From 76e6bc2e1b78b18fb4ed093560c0c64edb807a79 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 19:01:39 +1000 Subject: [PATCH 74/88] docs: finish Parameters docs --- qcodes/instrument/parameter.py | 121 ++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index a637835e0d7..28ae378ff72 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -21,18 +21,8 @@ 2. As a variable that stores and returns a value. For instance, for storing of values you want to keep track of but cannot set or get electronically. Provides ``sweep`` and ``__getitem__`` (slice notation) methods to use a - settable parameter as the swept variable in a ``Loop``. - - By default only gettable, returning its last value. - This behaviour can be modified in two ways: - 1. Providing a ``get_cmd``/``set_cmd``, which can of the following: - a. callable, with zero args for get_cmd, one arg for set_cmd - b. VISA command string - c. None, in which case it retrieves its last value for ``get_cmd``, - and stores a value for ``set_cmd`` - d. False, in which case trying to get/set will raise an error. - 2. Creating a subclass with an explicit ``get``/``set`` method. This - enables more advanced functionality. + settable parameter as the swept variable in a ``Loop``. + The get/set functionality can be modified. - ``ArrayParameter`` is a base class for array-valued parameters, ie anything for which each ``get`` call returns an array of values that all have the @@ -49,7 +39,9 @@ that returns a sequence of values, and describe those values in ``super().__init__``. - ``CombinedParameter`` # TODO + ``CombinedParameter`` Combines several parameters into a ``MultiParameter``. + can be easily used via the ``combine`` function. + Note that it is not yet a subclass of BaseParameter. """ @@ -101,8 +93,8 @@ class _BaseParameter(Metadatable, DeferredOperations): ``update=True``, for example if it takes too long to update. Default True. - metadata (Optional[dict]): extra information to include with the - JSON snapshot of the parameter + snapshot_value (Optional[bool]): False prevents parameter value to be + stored in the snapshot. Useful if the value is large. step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. @@ -140,14 +132,16 @@ class _BaseParameter(Metadatable, DeferredOperations): set_parser (Optional[function]): function to transform the input set value to an encoded value sent to the instrument. See also val_mapping. - snapshot_value (Optional[bool]) - + vals (Optional[Validator]): a Validator object for this parameter max_val_age (Optional[float]): The max time (in seconds) to trust a saved value obtained from get_latest(). If this parameter has not been set or measured more recently than this, perform an additional measurement. + + metadata (Optional[dict]): extra information to include with the + JSON snapshot of the parameter """ def __init__(self, name, instrument, snapshot_get, metadata, @@ -525,21 +519,28 @@ def full_name(self): class Parameter(_BaseParameter): """ A parameter that represents a single degree of freedom. - Not necessarily part of an instrument. - - Subclasses should define either a ``set`` method, a ``get`` method, or - both. + This is the standard parameter for Instruments, though it can also be + used as a variable, i.e. storing/retrieving a value, or be subclassed for + more complex uses. + By default only gettable, returning its last value. + This behaviour can be modified in two ways: + 1. Providing a ``get_cmd``/``set_cmd``, which can of the following: + a. callable, with zero args for get_cmd, one arg for set_cmd + b. VISA command string + c. None, in which case it retrieves its last value for ``get_cmd``, + and stores a value for ``set_cmd`` + d. False, in which case trying to get/set will raise an error. + 2. Creating a subclass with an explicit ``get``/``set`` method. This + enables more advanced functionality. + Parameters have a ``.get_latest`` method that simply returns the most recent set or measured value. This can be called ( ``param.get_latest()`` ) or used in a ``Loop`` as if it were a (gettable-only) parameter itself: ``Loop(...).each(param.get_latest)`` - Note: If you want ``.get`` or ``.set`` to save the measurement for - ``.get_latest``, you must explicitly call ``self._save_val(value)`` - inside ``.get`` and ``.set``. - + Args: name (str): the local name of the parameter. Should be a valid identifier, ie no spaces or special characters. If this parameter @@ -555,34 +556,72 @@ class Parameter(_BaseParameter): unit (Optional[str]): The unit of measure. Use ``''`` for unitless. - units (Optional[str]): DEPRECATED, redirects to ``unit``. + snapshot_get (Optional[bool]): False prevents any update to the + parameter during a snapshot, even if the snapshot was called with + ``update=True``, for example if it takes too long to update. + Default True. + + snapshot_value (Optional[bool]): False prevents parameter value to be + stored in the snapshot. Useful if the value is large. + step (Optional[Union[int, float]]): max increment of parameter value. + Larger changes are broken into multiple steps this size. + When combined with delays, this acts as a ramp. + + scale (Optional[float]): Scale to multiply value with before + performing set. the internally multiplied value is stored in + `raw_value`. Can account for a voltage divider. + + inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) + between successive sets. If the previous set was less than this, + it will wait until the condition is met. + Can be set to 0 to go maximum speed with no errors. + + post_delay (Optional[Union[int, float]]): time (in seconds) to wait + after the *start* of each set, whether part of a sweep or not. + Can be set to 0 to go maximum speed with no errors. + + val_mapping (Optional[dict]): a bidirectional map data/readable values + to instrument codes, expressed as a dict: + ``{data_val: instrument_code}`` + For example, if the instrument uses '0' to mean 1V and '1' to mean + 10V, set val_mapping={1: '0', 10: '1'} and on the user side you + only see 1 and 10, never the coded '0' and '1' + If vals is omitted, will also construct a matching Enum validator. + NOTE: only applies to get if get_cmd is a string, and to set if + set_cmd is a string. + You can use ``val_mapping`` with ``get_parser``, in which case + ``get_parser`` acts on the return value from the instrument first, + then ``val_mapping`` is applied (in reverse). + + get_parser ( Optional[function]): function to transform the response + from get to the final output value. See also val_mapping + + set_parser (Optional[function]): function to transform the input set + value to an encoded value sent to the instrument. + See also val_mapping. + vals (Optional[Validator]): Allowed values for setting this parameter. Only relevant if settable. Defaults to ``Numbers()`` + max_val_age (Optional[float]): The max time (in seconds) to trust a + saved value obtained from get_latest(). If this parameter has not + been set or measured more recently than this, perform an + additional measurement. + docstring (Optional[str]): documentation string for the __doc__ field of the object. The __doc__ field of the instance is used by some help systems, but not all - snapshot_get (Optional[bool]): False prevents any update to the - parameter during a snapshot, even if the snapshot was called with - ``update=True``, for example if it takes too long to update. - Default True. - metadata (Optional[dict]): extra information to include with the JSON snapshot of the parameter + """ - def __init__(self, name, instrument=None, label=None, - get_cmd=None, set_cmd=False, - initial_value=None, - unit=None, vals=Numbers(), docstring=None, - snapshot_get=True, snapshot_value=True, metadata=None, - max_val_age=None, **kwargs): - # TODO (nulinspiratie) make kwargs explicit - super().__init__(name, instrument, snapshot_get, metadata, - snapshot_value=snapshot_value, vals=vals, - max_val_age=max_val_age, **kwargs) + def __init__(self, name, instrument=None, label=None, unit=None, + get_cmd=None, set_cmd=False, initial_value=None, + max_val_age=None, vals=Numbers(), docstring=None, **kwargs): + super().__init__(name=name, instrument=instrument, vals=vals, **kwargs) # Enable set/get methods if get_cmd/set_cmd is given # Called first so super().__init__ can wrap get/set methods From ae9e73ab4c5288b2f2aa39a7226cb528745049fc Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 5 Sep 2017 19:07:34 +1000 Subject: [PATCH 75/88] fix: defaults for snapshot_get, metadata --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 28ae378ff72..78541cc212b 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -144,7 +144,7 @@ class _BaseParameter(Metadatable, DeferredOperations): JSON snapshot of the parameter """ - def __init__(self, name, instrument, snapshot_get, metadata, + def __init__(self, name, instrument, snapshot_get=True, metadata=None, step=None, scale=None, inter_delay=0, post_delay=0, val_mapping=None, get_parser=None, set_parser=None, snapshot_value=True, max_val_age=None, vals=None): From 2e197a907db7010030aeaf647343f3caab8c662b Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:20:56 +1000 Subject: [PATCH 76/88] fix: set vals to Enum if val_mapping provided --- qcodes/instrument/parameter.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 78541cc212b..5b288c8b1d0 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -66,7 +66,7 @@ warn_units) from qcodes.utils.metadata import Metadatable from qcodes.utils.command import Command -from qcodes.utils.validators import Validator, Numbers, Ints, Strings +from qcodes.utils.validators import Validator, Numbers, Ints, Strings, Enum from qcodes.instrument.sweep_values import SweepFixedValues from qcodes.data.data_array import DataArray @@ -156,6 +156,8 @@ def __init__(self, name, instrument, snapshot_get=True, metadata=None, if not isinstance(vals, (Validator, type(None))): raise TypeError('vals must be None or a Validator') + elif val_mapping is not None: + vals = Enum(*val_mapping.keys()) self.vals = vals self.step = step @@ -164,7 +166,6 @@ def __init__(self, name, instrument, snapshot_get=True, metadata=None, self.inter_delay = inter_delay self.post_delay = post_delay - # TODO (nulinspiratie) handle int vs string conversion in val_mapping self.val_mapping = val_mapping if val_mapping is None: self.inverse_val_mapping = None @@ -638,7 +639,7 @@ def __init__(self, name, instrument=None, label=None, unit=None, if not hasattr(self, 'set') and set_cmd is not False: if set_cmd is None: - self.set = partial(self._save_val, validate=True) + self.set = partial(self._save_val, validate=False) else: exec_str = instrument.write if instrument else None self.set = Command(arg_count=1, cmd=set_cmd, exec_str=exec_str) From 46bcc899a4492cfa30d4220d0fffa424ef92a5d7 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:21:36 +1000 Subject: [PATCH 77/88] feat: Remove default vals=Numbers() for Parameter --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 5b288c8b1d0..aadf2fbc145 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -621,7 +621,7 @@ class Parameter(_BaseParameter): def __init__(self, name, instrument=None, label=None, unit=None, get_cmd=None, set_cmd=False, initial_value=None, - max_val_age=None, vals=Numbers(), docstring=None, **kwargs): + max_val_age=None, vals=None, docstring=None, **kwargs): super().__init__(name=name, instrument=instrument, vals=vals, **kwargs) # Enable set/get methods if get_cmd/set_cmd is given From dc2bd38a659ae178f00f540434d8558bdf08745e Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:24:12 +1000 Subject: [PATCH 78/88] fix: remove unused import --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index aadf2fbc145..25710bb2c43 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -66,7 +66,7 @@ warn_units) from qcodes.utils.metadata import Metadatable from qcodes.utils.command import Command -from qcodes.utils.validators import Validator, Numbers, Ints, Strings, Enum +from qcodes.utils.validators import Validator, Ints, Strings, Enum from qcodes.instrument.sweep_values import SweepFixedValues from qcodes.data.data_array import DataArray From e4ab53041091e200b58a0f5c1b46ef1bc32e3ab7 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:31:03 +1000 Subject: [PATCH 79/88] doc: fixes --- qcodes/instrument/parameter.py | 35 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 25710bb2c43..231503414f5 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -6,10 +6,10 @@ by either using or subclassing one of the classes defined here, but you can also use any class with the right attributes. -All parameter classes are subclassed from _BaseParameter (except CombinedParameter). -The _BaseParameter provides functionality that is common to all parameter types, -such as ramping and scaling of values, adding delays (see documentation for -details). +All parameter classes are subclassed from _BaseParameter (except +CombinedParameter). The _BaseParameter provides functionality that is common +to all parameter types, such as ramping and scaling of values, adding delays +(see documentation for details). This file defines four classes of parameters: @@ -188,7 +188,7 @@ def __init__(self, name, instrument, snapshot_get=True, metadata=None, # subclasses should extend this list with extra attributes they # want automatically included in the snapshot - self._meta_attrs = ['name', 'instrument', 'step', 'scale','raw_value', + self._meta_attrs = ['name', 'instrument', 'step', 'scale', 'raw_value', 'inter_delay', 'post_delay', 'val_mapping', 'vals'] # Specify time of last set operation, used when comparing to delay to @@ -322,7 +322,7 @@ def set_wrapper(value, **kwargs): if isinstance(self.scale, collections.Iterable): # Scale contains multiple elements, one for each value value = tuple(val * scale for val, scale - in zip(value, self.scale)) + in zip(value, self.scale)) else: # Use single scale for all values value *= self.scale @@ -366,14 +366,15 @@ def set_wrapper(value, **kwargs): def get_ramp_values(self, value, step=None): """ - Sweep to a given value from a starting value + Return values to sweep from current value to target value. This method can be overridden to have a custom sweep behaviour. It can even be overridden by a generator. Args: - value: + value: target value + step: maximum step size Returns: - + List of stepped values, including target value. """ if step is None: return [value] @@ -388,8 +389,9 @@ def get_ramp_values(self, value, step=None): # isn't, even though it's valid. # probably MultiType with a mix of numeric and non-numeric types # just set the endpoint and move on - logging.warning('cannot sweep {} from {} to {} - jumping.'.format( - self.name, start_value, value)) + logging.warning( + 'cannot sweep {} from {} to {} - jumping.'.format( + self.name, start_value, value)) return [] # drop the initial value, we're already there @@ -748,8 +750,6 @@ class ArrayParameter(_BaseParameter): unit (Optional[str]): The unit of measure. Use ``''`` for unitless. - units (Optional[str]): DEPRECATED, redirects to ``unit``. - setpoints (Optional[Tuple[setpoint_array]]): ``setpoint_array`` can be a DataArray, numpy.ndarray, or sequence. The setpoints for each dimension of the returned array. An @@ -791,7 +791,8 @@ def __init__(self, name, shape, instrument=None, super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) - if hasattr(self, 'set'): # TODO (alexcjohnson): can we support, ala Combine? + if hasattr(self, 'set'): + # TODO (alexcjohnson): can we support, ala Combine? raise AttributeError('ArrayParameters do not support set ' 'at this time.') @@ -850,6 +851,7 @@ def __init__(self, name, shape, instrument=None, if not hasattr(self, 'get') and not hasattr(self, 'set'): raise AttributeError('ArrayParameter must have a get, set or both') + def _is_nested_sequence_or_none(obj, types, shapes): """Validator for MultiParameter setpoints/names/labels""" if obj is None: @@ -951,7 +953,8 @@ def __init__(self, name, names, shapes, instrument=None, super().__init__(name, instrument, snapshot_get, metadata, snapshot_value=snapshot_value) - if hasattr(self, 'set'): # TODO (alexcjohnson): can we support, ala Combine? + if hasattr(self, 'set'): + # TODO (alexcjohnson): can we support, ala Combine? warnings.warn('MultiParameters do not support set at this time.') self._meta_attrs.extend(['setpoint_names', 'setpoint_labels', @@ -1078,7 +1081,7 @@ def combine(*parameters, name, label=None, unit=None, units=None, Combine parameters into one sweepable parameter Args: - *paramters (qcodes.Parameter): the parameters to combine + *parameters (qcodes.Parameter): the parameters to combine name (str): the name of the paramter label (Optional[str]): the label of the combined parameter unit (Optional[str]): the unit of the combined parameter From 9062494f9798a9ebc797ff215df4543c8c257469 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:31:38 +1000 Subject: [PATCH 80/88] forgot call to super().__init__() --- qcodes/instrument/parameter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 231503414f5..d725e5d44fd 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1048,6 +1048,7 @@ class GetLatest(DelegateAttributes, DeferredOperations): additional measurement. """ def __init__(self, parameter, max_val_age=None): + super().__init__() self.parameter = parameter self.max_val_age = max_val_age From 4fe202586d8961bb5766124623020ad43bf1e45e Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:33:20 +1000 Subject: [PATCH 81/88] doc: unindent --- .idea/misc.xml | 4 ++++ qcodes/instrument/parameter.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 .idea/misc.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000000..538fce22ee2 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index d725e5d44fd..ab284347b5d 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -528,14 +528,14 @@ class Parameter(_BaseParameter): By default only gettable, returning its last value. This behaviour can be modified in two ways: - 1. Providing a ``get_cmd``/``set_cmd``, which can of the following: - a. callable, with zero args for get_cmd, one arg for set_cmd - b. VISA command string - c. None, in which case it retrieves its last value for ``get_cmd``, - and stores a value for ``set_cmd`` - d. False, in which case trying to get/set will raise an error. - 2. Creating a subclass with an explicit ``get``/``set`` method. This - enables more advanced functionality. + 1. Providing a ``get_cmd``/``set_cmd``, which can of the following: + a. callable, with zero args for get_cmd, one arg for set_cmd + b. VISA command string + c. None, in which case it retrieves its last value for ``get_cmd``, + and stores a value for ``set_cmd`` + d. False, in which case trying to get/set will raise an error. + 2. Creating a subclass with an explicit ``get``/``set`` method. This + enables more advanced functionality. Parameters have a ``.get_latest`` method that simply returns the most recent set or measured value. This can be called ( ``param.get_latest()`` ) From b379088fc999560b8fd1a8e5b6fe1056312def2a Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:37:38 +1000 Subject: [PATCH 82/88] doc: remove trailing whitespaces --- qcodes/instrument/parameter.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index ab284347b5d..7e923ef647c 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -13,14 +13,14 @@ This file defines four classes of parameters: -- ``Parameter`` is the base class for scalar-valued parameters. +- ``Parameter`` is the base class for scalar-valued parameters. Two primary ways in which it can be used: - 1. As an ``Instrument`` parameter that sends/receives commands. Provides a - standardized interface to construct strings to pass to the + 1. As an ``Instrument`` parameter that sends/receives commands. Provides a + standardized interface to construct strings to pass to the instrument's ``write`` and ``ask`` methods 2. As a variable that stores and returns a value. For instance, for storing of values you want to keep track of but cannot set or get electronically. - Provides ``sweep`` and ``__getitem__`` (slice notation) methods to use a + Provides ``sweep`` and ``__getitem__`` (slice notation) methods to use a settable parameter as the swept variable in a ``Loop``. The get/set functionality can be modified. @@ -38,9 +38,9 @@ feed data to a ``DataSet``. To use, provide a ``get`` method that returns a sequence of values, and describe those values in ``super().__init__``. - + ``CombinedParameter`` Combines several parameters into a ``MultiParameter``. - can be easily used via the ``combine`` function. + can be easily used via the ``combine`` function. Note that it is not yet a subclass of BaseParameter. @@ -74,7 +74,7 @@ class _BaseParameter(Metadatable, DeferredOperations): """ Shared behavior for all parameters. Not intended to be used - directly, normally you should use ``Parameter``, ``ArrayParameter``, + directly, normally you should use ``Parameter``, ``ArrayParameter``, ``MultiParameter``, or ``CombinedParameter``. Note that ``CombinedParameter`` is not yet a subclass of ``_BaseParameter`` @@ -93,24 +93,24 @@ class _BaseParameter(Metadatable, DeferredOperations): ``update=True``, for example if it takes too long to update. Default True. - snapshot_value (Optional[bool]): False prevents parameter value to be + snapshot_value (Optional[bool]): False prevents parameter value to be stored in the snapshot. Useful if the value is large. step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. When combined with delays, this acts as a ramp. - scale (Optional[float]): Scale to multiply value with before - performing set. the internally multiplied value is stored in + scale (Optional[float]): Scale to multiply value with before + performing set. the internally multiplied value is stored in `raw_value`. Can account for a voltage divider. - inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) - between successive sets. If the previous set was less than this, - it will wait until the condition is met. + inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) + between successive sets. If the previous set was less than this, + it will wait until the condition is met. Can be set to 0 to go maximum speed with no errors. - post_delay (Optional[Union[int, float]]): time (in seconds) to wait - after the *start* of each set, whether part of a sweep or not. + post_delay (Optional[Union[int, float]]): time (in seconds) to wait + after the *start* of each set, whether part of a sweep or not. Can be set to 0 to go maximum speed with no errors. val_mapping (Optional[dict]): a bidirectional map data/readable values @@ -522,8 +522,8 @@ def full_name(self): class Parameter(_BaseParameter): """ A parameter that represents a single degree of freedom. - This is the standard parameter for Instruments, though it can also be - used as a variable, i.e. storing/retrieving a value, or be subclassed for + This is the standard parameter for Instruments, though it can also be + used as a variable, i.e. storing/retrieving a value, or be subclassed for more complex uses. By default only gettable, returning its last value. From 13f36abfb210190aa752c363fb6e9c17b038fada Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:46:44 +1000 Subject: [PATCH 83/88] added super.__init__(None) --- qcodes/instrument/parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 7e923ef647c..351ee68d6f7 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1048,7 +1048,7 @@ class GetLatest(DelegateAttributes, DeferredOperations): additional measurement. """ def __init__(self, parameter, max_val_age=None): - super().__init__() + super().__init__(None) self.parameter = parameter self.max_val_age = max_val_age From 128cce578fce06baf2bf3998d3170944a9e87ef9 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:52:50 +1000 Subject: [PATCH 84/88] doc: damn white spaces --- qcodes/instrument/parameter.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 351ee68d6f7..fab7931f998 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -95,20 +95,20 @@ class _BaseParameter(Metadatable, DeferredOperations): snapshot_value (Optional[bool]): False prevents parameter value to be stored in the snapshot. Useful if the value is large. - + step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. When combined with delays, this acts as a ramp. - + scale (Optional[float]): Scale to multiply value with before performing set. the internally multiplied value is stored in `raw_value`. Can account for a voltage divider. - + inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) between successive sets. If the previous set was less than this, it will wait until the condition is met. Can be set to 0 to go maximum speed with no errors. - + post_delay (Optional[Union[int, float]]): time (in seconds) to wait after the *start* of each set, whether part of a sweep or not. Can be set to 0 to go maximum speed with no errors. @@ -564,24 +564,24 @@ class Parameter(_BaseParameter): ``update=True``, for example if it takes too long to update. Default True. - snapshot_value (Optional[bool]): False prevents parameter value to be + snapshot_value (Optional[bool]): False prevents parameter value to be stored in the snapshot. Useful if the value is large. step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. When combined with delays, this acts as a ramp. - scale (Optional[float]): Scale to multiply value with before - performing set. the internally multiplied value is stored in + scale (Optional[float]): Scale to multiply value with before + performing set. the internally multiplied value is stored in `raw_value`. Can account for a voltage divider. - inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) - between successive sets. If the previous set was less than this, - it will wait until the condition is met. + inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) + between successive sets. If the previous set was less than this, + it will wait until the condition is met. Can be set to 0 to go maximum speed with no errors. - post_delay (Optional[Union[int, float]]): time (in seconds) to wait - after the *start* of each set, whether part of a sweep or not. + post_delay (Optional[Union[int, float]]): time (in seconds) to wait + after the *start* of each set, whether part of a sweep or not. Can be set to 0 to go maximum speed with no errors. val_mapping (Optional[dict]): a bidirectional map data/readable values From 6b1f429af4142252b01b94363ffe051d900bb81d Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 13:54:06 +1000 Subject: [PATCH 85/88] fix: remove init from getlatest --- qcodes/instrument/parameter.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index fab7931f998..4af57ed3bbb 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -112,7 +112,7 @@ class _BaseParameter(Metadatable, DeferredOperations): post_delay (Optional[Union[int, float]]): time (in seconds) to wait after the *start* of each set, whether part of a sweep or not. Can be set to 0 to go maximum speed with no errors. - + val_mapping (Optional[dict]): a bidirectional map data/readable values to instrument codes, expressed as a dict: ``{data_val: instrument_code}`` @@ -125,21 +125,21 @@ class _BaseParameter(Metadatable, DeferredOperations): You can use ``val_mapping`` with ``get_parser``, in which case ``get_parser`` acts on the return value from the instrument first, then ``val_mapping`` is applied (in reverse). - + get_parser ( Optional[function]): function to transform the response from get to the final output value. See also val_mapping - + set_parser (Optional[function]): function to transform the input set value to an encoded value sent to the instrument. See also val_mapping. - + vals (Optional[Validator]): a Validator object for this parameter max_val_age (Optional[float]): The max time (in seconds) to trust a saved value obtained from get_latest(). If this parameter has not been set or measured more recently than this, perform an additional measurement. - + metadata (Optional[dict]): extra information to include with the JSON snapshot of the parameter """ @@ -536,14 +536,14 @@ class Parameter(_BaseParameter): d. False, in which case trying to get/set will raise an error. 2. Creating a subclass with an explicit ``get``/``set`` method. This enables more advanced functionality. - + Parameters have a ``.get_latest`` method that simply returns the most recent set or measured value. This can be called ( ``param.get_latest()`` ) or used in a ``Loop`` as if it were a (gettable-only) parameter itself: ``Loop(...).each(param.get_latest)`` - + Args: name (str): the local name of the parameter. Should be a valid identifier, ie no spaces or special characters. If this parameter @@ -570,20 +570,20 @@ class Parameter(_BaseParameter): step (Optional[Union[int, float]]): max increment of parameter value. Larger changes are broken into multiple steps this size. When combined with delays, this acts as a ramp. - + scale (Optional[float]): Scale to multiply value with before performing set. the internally multiplied value is stored in `raw_value`. Can account for a voltage divider. - + inter_delay (Optional[Union[int, float]]): Minimum time (in seconds) between successive sets. If the previous set was less than this, it will wait until the condition is met. Can be set to 0 to go maximum speed with no errors. - + post_delay (Optional[Union[int, float]]): time (in seconds) to wait after the *start* of each set, whether part of a sweep or not. Can be set to 0 to go maximum speed with no errors. - + val_mapping (Optional[dict]): a bidirectional map data/readable values to instrument codes, expressed as a dict: ``{data_val: instrument_code}`` @@ -596,14 +596,14 @@ class Parameter(_BaseParameter): You can use ``val_mapping`` with ``get_parser``, in which case ``get_parser`` acts on the return value from the instrument first, then ``val_mapping`` is applied (in reverse). - + get_parser ( Optional[function]): function to transform the response from get to the final output value. See also val_mapping - + set_parser (Optional[function]): function to transform the input set value to an encoded value sent to the instrument. See also val_mapping. - + vals (Optional[Validator]): Allowed values for setting this parameter. Only relevant if settable. Defaults to ``Numbers()`` @@ -611,14 +611,14 @@ class Parameter(_BaseParameter): saved value obtained from get_latest(). If this parameter has not been set or measured more recently than this, perform an additional measurement. - + docstring (Optional[str]): documentation string for the __doc__ field of the object. The __doc__ field of the instance is used by some help systems, but not all metadata (Optional[dict]): extra information to include with the JSON snapshot of the parameter - + """ def __init__(self, name, instrument=None, label=None, unit=None, @@ -1048,7 +1048,6 @@ class GetLatest(DelegateAttributes, DeferredOperations): additional measurement. """ def __init__(self, parameter, max_val_age=None): - super().__init__(None) self.parameter = parameter self.max_val_age = max_val_age From 3eb158012e6bb69b66c286ca35ac3587e1972f22 Mon Sep 17 00:00:00 2001 From: nulinspiratie Date: Tue, 26 Sep 2017 18:21:36 +1000 Subject: [PATCH 86/88] fix: test fix for vals=Numbers() --- qcodes/tests/test_parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py index 99c20b50840..86c491f88ec 100644 --- a/qcodes/tests/test_parameter.py +++ b/qcodes/tests/test_parameter.py @@ -61,7 +61,7 @@ def test_no_name(self): def test_default_attributes(self): # Test the default attributes, providing only a name name = 'repetitions' - p = GettableParam(name) + p = GettableParam(name, vals=vals.Numbers()) self.assertEqual(p.name, name) self.assertEqual(p.label, name) self.assertEqual(p.unit, '') From 1fca63e502f39276d8caedffa9a9489bb225a93c Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 29 Sep 2017 13:49:14 +0200 Subject: [PATCH 87/88] readd deprecated set_validator this will be removed after a deprecation cycle --- qcodes/instrument/parameter.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 4af57ed3bbb..9c43860e394 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -518,6 +518,20 @@ def full_name(self): 'str(parameter)') return str(self) + def set_validator(self, vals): + """ + Deprecated Set a validator `vals` for this parameter. + Args: + vals (Validator): validator to set + + """ + warnings.warn( + "set_validator is deprected use `inst.vals = MyValidator` instead") + if isinstance(vals, Validator): + self.vals = vals + else: + raise TypeError('vals must be a Validator') + class Parameter(_BaseParameter): """ From b5ecebb636d62dd0ebb01c7102a972070b11e363 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 29 Sep 2017 13:49:32 +0200 Subject: [PATCH 88/88] use warnings module for deprecation warnings --- qcodes/instrument/parameter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py index 9c43860e394..8d4fe186a05 100644 --- a/qcodes/instrument/parameter.py +++ b/qcodes/instrument/parameter.py @@ -1299,7 +1299,7 @@ def __init__(self, name, instrument=None, set_cmd=set_cmd, set_parser=set_parser, post_delay=delay, step=step, max_val_age=max_val_age, vals=vals, val_mapping=val_mapping, **kwargs) - logging.warning('`StandardParameter` is deprecated, ' + warnings.warn('`StandardParameter` is deprecated, ' 'use `Parameter` instead. {}'.format(self)) @@ -1308,5 +1308,5 @@ def __init__(self, name, instrument=None, initial_value=None, **kwargs): super().__init__(name=name, instrument=instrument, get_cmd=None, set_cmd=None, initial_value=initial_value, **kwargs) - logging.warning('Parameter {}: `ManualParameter` is deprecated, use ' - '`Parameter` instead with `set_cmd=None`.'.format(self)) \ No newline at end of file + warnings.warn('Parameter {}: `ManualParameter` is deprecated, use ' + '`Parameter` instead with `set_cmd=None`.'.format(self))