Repository: microsoft/Qcodes

Issue:
ManualParameter calls validate twice
The qcodes `ManualParameter` calls validate twice for a single set.
``` python
import qcodes
from qcodes.utils.validators import Validator

class MyValidator(Validator):

    def __init__(self, min_value=-float("inf"), max_value=float("inf")):
        pass

    def validate(self, value, context=''):
        print('validate!')

    is_numeric=True

p = qcodes.ManualParameter('p', initial_value=0, vals=MyValidator())
p.set(10)
```
The output is:
```
validate!
validate!
```
The validator should only be called once.

@WilliamHPNielsen @jenshnielsen 


Discussion:
I guess this is fixed by #651, which should be merged soon.
Hmm... it is not. I'll look in to this.

Resolving patch:
diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py
--- a/qcodes/instrument/parameter.py
+++ b/qcodes/instrument/parameter.py
@@ -57,7 +57,7 @@
 import os
 import collections
 import warnings
-from typing import Optional, Sequence, TYPE_CHECKING, Union, Callable
+from typing import Optional, Sequence, TYPE_CHECKING, Union, Callable, List
 from functools import partial, wraps
 import numpy
 
@@ -361,7 +361,7 @@ def set_wrapper(value, **kwargs):
                 # a list containing only `value`.
                 steps = self.get_ramp_values(value, step=self.step)
 
-                for val_step in steps:
+                for step_index, val_step in enumerate(steps):
                     if self.val_mapping is not None:
                         # Convert set values using val_mapping dictionary
                         mapped_value = self.val_mapping[val_step]
@@ -398,7 +398,9 @@ def set_wrapper(value, **kwargs):
                     self.raw_value = parsed_scaled_mapped_value
                     self._save_val(val_step,
                                    validate=(self.val_mapping is None and
-                                             self.set_parser is None))
+                                             self.set_parser is None and
+                                             not(step_index == len(steps)-1 or
+                                                 len(steps) == 1)))
 
                     # Update last set time (used for calculating delays)
                     self._t_last_set = time.perf_counter()
@@ -414,7 +416,9 @@ def set_wrapper(value, **kwargs):
 
         return set_wrapper
 
-    def get_ramp_values(self, value, step=None):
+    def get_ramp_values(self, value: Union[float, int],
+                        step: Union[float, int]=None) -> List[Union[float,
+                                                                    int]]:
         """
         Return values to sweep from current value to target value.
         This method can be overridden to have a custom sweep behaviour.
@@ -435,8 +439,6 @@ def get_ramp_values(self, value, step=None):
                 self.get()
             start_value = self.raw_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


Test patch:
diff --git a/qcodes/tests/test_parameter.py b/qcodes/tests/test_parameter.py
--- a/qcodes/tests/test_parameter.py
+++ b/qcodes/tests/test_parameter.py
@@ -25,6 +25,19 @@ def get(self):
         return 42
 
 
+class BookkeepingValidator(vals.Validator):
+    """
+    Validator that keeps track of what it validates
+    """
+    def __init__(self, min_value=-float("inf"), max_value=float("inf")):
+        self.values_validated = []
+
+    def validate(self, value, context=''):
+        self.values_validated.append(value)
+
+    is_numeric = True
+
+
 blank_instruments = (
     None,  # no instrument at all
     namedtuple('noname', '')(),  # no .name
@@ -32,6 +45,7 @@ def get(self):
 )
 named_instrument = namedtuple('yesname', 'name')('astro')
 
+
 class MemoryParameter(Parameter):
     def __init__(self, get_cmd=None, **kwargs):
         self.set_values = []
@@ -134,6 +148,19 @@ def test_explicit_attributes(self):
                      'setpoint_labels', 'full_names']:
             self.assertFalse(hasattr(p, attr), attr)
 
+    def test_number_of_validations(self):
+
+        p = Parameter('p', set_cmd=None, initial_value=0,
+                      vals=BookkeepingValidator())
+
+        self.assertEqual(p.vals.values_validated, [0])
+
+        p.step = 1
+        p.set(10)
+
+        self.assertEqual(p.vals.values_validated,
+                         [0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+
     def test_snapshot_value(self):
         p_snapshot = Parameter('no_snapshot', set_cmd=None, get_cmd=None,
                                snapshot_value=True)