2. Features¶
Features are descriptors just like standard Python property. They live on the class and define what happens when one gets or sets the attribute associated with the feature on a class instance, that is to say when one writes:
a = d.myfeature
or:
d.myfeature = 1
The following sections will describe the different steps involved in the getting and setting process and how they can be customized using the different arguments it takes. Even more advanced customizations are possible and will be described in their own part Advanced customization.
2.1. Working principle¶
First we will describe the process involved in retrieving a feature value then switch to describing the setting of it.
Note
The first two arguments of a feature are always the getter and setter. If their value is set to None, the corresponding operation won’t be possible for the feature. A feature is always deletable and deleting it corresponds to discarding the cached value if any exists.
2.1.1. Getting chain¶
First when getting a feature, we check if the instrument options allow to access it, and if not an AttributeError is raised. Next, we check whether or not its current value is known. If it is, the cached value is directly returned otherwise the system proceed with the retrieval sequence, in three steps as follows:
pre_get()
: This step is in charge to check that we can actually retrieve the value from the instrument. Some assertions about the instrument current state can for example be performed.get()
: This step is tasked with the actual communication with the instrument. It should retrieve the value from the instrument and pass to the next step without performing any conversion. By default, it will call thedefault_get_feature()
method defined on the class it belongs and pass it the value of the getter argument passed to the feature when it was created.post_get()
: This step is tasked with converting the value obtained at the get step into a more user friendly representation than what was returned by the instrument. It can for example extract the meaningful part of the instrument response and turn it into the proper type, such as an integer or a float. It can also check for an error state on the instrument even so get operation should not cause any issue and such checks should be left to the setting.
The value coming out of the post_get step is cached and then returned
to the user. If an error occurs during any of the step, if it is one
of the ones listed in the retries_exceptions attribute of the driver
the connection will be closed and re-opened and the operation attempted
anew. Otherwise or if the re-opening fails too many times (more than
specified in the retries argument of the feature), an I3pyFailedGet
exceptions will be raised while still pointing to the original errors.
2.1.2. Setting chain¶
First when setting a feature, we check if the instrument options allow to access it, and if not an AttributeError is raised. Next, the value is checked against the cached value. If both values are found to be equal, the set is not performed as it would be useless. Otherwise, we proceed with the setting sequence, which, like the getting, happens in three steps:
pre_set()
: During this step, the state of the instrument can be checked and the value passed by the user validated and converted to a format appropriate to pass to the instrument.set()
: This step is dedicated to actually communicating with the instrument to set the value. If the instrument returns any value that can be used to check that the operation went without issue, it should be returned so that it can be passed up to the next method. By default, it will call thedefault_set_feature()
method defined on the class it belongs and pass it the value of the setter argument passed to the feature when it was created.post_set()
: This step main goal is to check that the operation of setting the value went without trouble. By default, it simply calls thedefault_check_operation()
on the parent class.
Once the value has been set and if no error occurred, the value
specified by the user is cached. If an error occurs during any of the
step, if it is one of the ones listed in the retries_exceptions
attribute of the driver the connection will be closed and re-opened
and the operation attempted anew. Otherwise or if the re-opening fails
too many times (more than specified in the retries argument of the
feature), an I3pyFailedSet
exceptions will be raised while still
pointing to the original errors.
2.2. Usual configurations¶
In addition to the ‘getter’ and ‘setter’ previously mentioned I3py features provides a number of often required checks, data extraction and data conversion utilities. The following list illustrates them:
‘options’: available on all
Feature
subclassesA “;” separated list of checks to perform on options values to determine if the Feature can be used. Options are defined using the
Options
feature. The test is performed a single time and then cached.‘checks’: available on all
Feature
subclassesSimilar to options, but can be used to check any value and is performed each time the feature is get or set.
‘extract’: available on all
Feature
subclassesA format string specifying how to extract the value of interest from the instrument response.
‘discard’: available on all
Feature
subclassesA list of features whose cache value should be discarded when the feature value is set. Alternatively a dict whose keys are ‘features’ and ‘limits’ can be used to also specify to discard some cached limits. One can access to the features and limits defined on the parent component using leading dots.
‘values’: available on
Str
,Int
andFloat
A tuple of acceptable values for the feature.
‘mapping’: available on
Str
,Int
andFloat
A mapping between user meaningful values and instrument meaningful ones.
‘limits’: available on
Int
andFloat
A 2-tuple, 3-tuple or str specifying the minimal and maximal values allowed and optionally the resolution that the feature can take. In the case of a str, the string specifies the named limit to use (see the following paragraph about defining limits).
‘aliases’: available on
Bool
A dictionary whose keys are True and False and whose values (list) specifies accepted aliases for True and False for setting.
Note
In many cases, the range of allowed values for a specific feature is not
fixed but may be related to another feature value. To handle this case,
I3py allows to define dynamic limits using the limit()
decorator. The
decorated method should return an instance of IntLimitsValidator
or
FloatLimitsValidator
depending the kind of value this limit applies to.
2.2.1. Specialized features¶
The
Alias
feature is a special feature allowing to delegate the actual work of getting/setting to another feature.The
Register
is a specialized feature which can be used to get and set the value of a binary register such as the ones commonly used by VISA based instrument. It will create a dedicated subclass of IntFlag and will handle the conversion. It takes two special arguments:- names: a list of names describing each bit in order (from least significant to most significant) or a dictionary mapping each name to the bit it describe. Those names should be valid python attribute names and ideally be all upper case.
- length: the length of the register (8 by default but some instrument use 16 bits register).
The
Options
is feature dedicated to the handling of hardware/firmware level options that cannot change while the instrument is running. It is expected to return a dictionary containing the values of the instrument options. To improve clarity the declaration of the feature should include the names of all the options to which it gives access along with hint about their possible values either as type or as a tuple of values. These information should be provided as a dictionary to the names argument.
2.3. Flexible getter/setter¶
In some cases, the command to use to get/set a Feature may depend on the state
of the instrument. This use case can be handled by using a custom get/set
method as described in Advanced customization. However as such cases can be
quite common, I3py provides an alternative mechanism based on factory class to
which the building of the get/set method can be deferred. Such factory classes
should inherit from AbstractGetSetFactory
and can be used for the
getter/setter arguments of a feature.
The factories implemented in I3py can be found in i3py.core.features.factories.