Creating New Device Types

Container objects are built using both the base Device class as well as instances of the EntryInfo.

Entry Info

In order to regulate and template the information that gets entered into the Happi database, we use the concept of device containers. This allows a developer to specify fields that are inherit to every device of a specific type, in addition by specifying these keys using the EntryInfo object, you have even greater control by declaring optional vs. mandatory, default options when a user does not enter the value, and enforce specific types.

class happi.device.EntryInfo(doc=None, optional=True, enforce=None, default=None)

A piece of information related to a specific device

These are entered as class attributes for a given device container. They help control the information entered into a device

Parameters:
  • doc (str) – A short string to document the device
  • optional (bool, optional) – By default all EntryInfo is optional, but in certain cases you may want to demand a particular piece of information upon initialization
  • enforce (type, list, compiled regex, optional) – Specify that all entered information is entered in a specific format. This can either by a Python type i.e. int, float e.t.c., a list of acceptable values, or a compiled regex pattern i.e re.compile(...)
  • default (optional) – A default value for the trait to have if the user does not specify. Keep in mind that this should be the same type as enforce if you are demanding a certain type.
Raises:

ContainerError: – If there is an error with the way the enforced value interacts with its default value, or if the piece of information entered is unenforcable based on the the settings

Example

class MyDevice(Device):

    my_field = EntryInfo('My generated field')
    number   = EntryInfo('Device number', enforce=int, default=0)
enforce_value(value)

Enforce the rules of the EntryInfo

Parameters:value
Returns:Identical to the provided value except it may have been converted to the correct type
Return type:value
Raises:ValueError: – If the value is not the correct type, or does not match the pattern

Using a Device

In order to ensure that information is entered into the database in an organized fashion, the client will only accept classes that inherit from Device. Each device will have the key information represented as class attributes, available to be manipulated like any other regular property

Editing the information for a container is a simple as:

In [1]: from happi import Device

In [2]: device = Device(name='my_device')

In [3]: device.name = 'new_name'

The Device class also supports extraneous data that may not have been captured when the original container was created. You can either enter this as a keyword, or enter it into the Device.extraneous dictionary.

While you are free to play around with the device attributes, when loading the object into the database you will need to make sure that all of the Device.mandatory_info has been entered, otherwise the client will reject the device.

Example Class

In order to show the flexibility of the EntryInfo class, we’ll put together an example container. The class can be invoked in the same way you would usually handle class inheritance, the only difference is that you specify class attributes as EntryInfo objects:

import re
from happi.device import Device, EntryInfo

class MyDevice(Device):

    model_no      = EntryInfo('Model Number of Device', optional=False)
    count         = EntryInfo('Count of Device', enforce=int, default=0)
    choices       = EntryInfo('Choice Info', enforce=['a','b','c'])
    no_whitespace = EntryInfo('Enforce no whitespace',
                               enforce = re.compile(r'[\S]*$')

By default, EntryInfo will create an optional keyword, whose default is None with the same name as the class attribute. You can always see how this information will be put into the the database by looking at the output of the Device.post() method. As shown in the example above, using the EntryInfo keywords, you can put a short doc string to give a better explanation of the field, and also enforce that user enter a specific format of information.

While the user will always be able to enter None for the attribute, if a real value is given it will be checked against the specified enforce keyword, raising ValueError if invalid. Here is a table for how the EntryInfo check the type

Enforce Method of enforcement
None Any value will work
type type(value)
list list.index(value)
regex regex.match(value) != None

Finally, fields that are important to the device can be marked as mandatory. These should have no default value. When entering information you will not neccesarily see a difference in between optional and mandatory EntryInfo, however the database client will reject the device if these fields don’t have values associated with them.