lewis.core.devices

This module contains DeviceBase as a base class for other device classes and infrastructure that can import devices from a module (DeviceRegistry). The latter also produces factory-like objects that create device instances and interfaces based on setups (DeviceBuilder).

Members

DeviceBase

This class is a common base for Device and StateMachineDevice.

DeviceBuilder

This class takes a module object (for example imported via importlib.import_module or via the DeviceRegistry) and inspects it so that it's possible to construct devices and interfaces.

DeviceRegistry

This class takes the name of a module and constructs a DeviceBuilder from each sub-module.

InterfaceBase

This class is a common base for protocol specific interfaces that are exposed by a subclass of Adapter.

is_device

Returns True if obj is a device type (derived from DeviceBase), but not defined in lewis.core.devices or lewis.devices.

is_interface

Returns True if obj is an interface (derived from InterfaceBase), but not defined in lewis.adapters, where concrete interfaces for protocols are defined.

class lewis.core.devices.DeviceBase[source]

Bases: object

This class is a common base for Device and StateMachineDevice. It is mainly used in the device discovery process.

class lewis.core.devices.DeviceBuilder(module)[source]

Bases: object

This class takes a module object (for example imported via importlib.import_module or via the DeviceRegistry) and inspects it so that it’s possible to construct devices and interfaces.

In order for the class to work properly, the device module has to adhere to a few rules. Device types, which means classes inheriting from DeviceBase, are imported directly from the device module, equivalent to the following:

from device_name import SimulatedDeviceType

If SimulatedDeviceType is defined in the __init__.py, there’s nothing else to do. If the device class is defined elsewhere, it must be imported in the __init__.py file as written above. If there is only one device type (which is probably the most common case), it is assumed to be default device type.

Setups are discovered in two locations, the first one is a dict called setups in the device module, which must contain setup names as keys and as values again a dict. This inner dict has one mandatory key called device_type and one optional key parameters containing the constructor arguments for the specified device type:

setups = dict(
    broken=dict(
        device_type=SimulatedDeviceType,
        parameters=dict(
            override_initial_state="error",
            override_initial_data=dict(target=-10, position=-20.0),
        ),
    )
)

The other location is a sub-package called setups, which should in turn contain modules. Each module must contain a variable device_type and a variable parameters which are analogous to the keys in the dict described above. This allows for more complex setups which define additional classes and so on.

The default setup is special, it is used when no setup is supplied to create_device(). If the setup default is not defined, one is created with the default device type. This has two consequences, no setups need to be defined for very simple devices, but if multiple device types are defined, a default setup must be defined.

A setup can be supplied to the create_device().

Lastly, the builder tries to discover device interfaces, which are currently classes based on lewis.adapters.InterfaceBase. These are looked for in the module and in a sub-package called interfaces (which should contain modules with adapters like the setups package).

Each interface has a protocol, if a protocol occurs more than once in a device module, a RuntimeError is raised.

create_device(setup=None)[source]

Creates a device object according to the provided setup. If no setup is provided, the default setup is used. If the setup can’t be found, a LewisException is raised. This can also happen if the device type specified in the setup is invalid.

Parameters:

setup – Name of the setup from which to create device.

Returns:

Device object initialized according to the provided setup.

create_interface(protocol=None, *args, **kwargs)[source]

Returns an interface that implements the provided protocol. If the protocol is not known, a LewisException is raised. All additional arguments are forwarded to the interface constructor (see Adapter for details).

Parameters:
  • protocol – Protocol which the interface must implement.

  • args – Positional arguments that are passed on to the interface.

  • kwargs – Keyword arguments that are passed on to the interface.

Returns:

Instance of the interface type.

property default_device_type

If the module only defines one device type, it is the default device type. It is used whenever a setup does not provide a device_type.

property default_protocol

In case only one protocol exists for the device, this is the default protocol.

property device_types

This property contains a dict of all device types in the device module. The keys are type names, the values are the types themselves.

property interfaces

This property contains a map with protocols as keys and interface types as values. The types are imported from the interfaces sub-module and from the device module itself. If two interfaces with the same protocol are discovered, a RuntimeError is raiesed.

property name

The name of the device, which is also the name of the device module.

property protocols

All available protocols for this device.

property setups

A map with all available setups. Setups are imported from the setups dictionary in a device module and from the setups sub-module. If no default-setup exists, one is created using the default_device_type. If there are several device types in the module, the default setup must be provided explicitly.

class lewis.core.devices.DeviceRegistry(device_module)[source]

Bases: object

This class takes the name of a module and constructs a DeviceBuilder from each sub-module. The available devices can be queried and a DeviceBuilder can be obtained for each device:

from lewis.core.devices import DeviceRegistry

registry = DeviceRegistry("lewis.devices")
chopper_builder = registry.device_builder("chopper")

# construct device, interface, ...

If the module can not be imported, a LewisException is raised.

Parameters:

device_module – Name of device module from which devices are loaded.

device_builder(name)[source]

Returns a DeviceBuilder instance that can be used to create device objects based on setups, as well as device interfaces. If the device name is not stored in the internal map, a LewisException is raised.

Each DeviceBuilder has a framework_version-member, which specifies the version of Lewis the device has been written for. If the version does not match the current framework version, it is only possible to obtain those device builders calling the method with strict_versions set to False, otherwise a LewisException is raised. A warning message is logged in all cases. If framework_version is None (e.g. not specified at all), it is accepted unless strict_versions is set to True.

Parameters:

name – Name of the device.

Returns:

DeviceBuilder-object for requested device.

property devices

All available device names.

class lewis.core.devices.InterfaceBase[source]

Bases: object

This class is a common base for protocol specific interfaces that are exposed by a subclass of Adapter. This base class is not meant to be used directly in a device package - this is what the interfaces in lewis.adapters are for.

There is a 1:1 correspondence between device and interface, where the interface holds a reference to the device. It can be changed through the device-property.

property adapter: NoReturn

Adapter type that is required to process and expose interfaces of this type. Must be implemented in subclasses.

property device

The device this interface is bound to. When a new device is set, _bind_device() is called, where the interface can react to the device change if necessary.

lewis.core.devices.is_device(obj)[source]

Returns True if obj is a device type (derived from DeviceBase), but not defined in lewis.core.devices or lewis.devices.

Parameters:

obj – Object to test.

Returns:

True if obj is a device type.

lewis.core.devices.is_interface(obj)[source]

Returns True if obj is an interface (derived from InterfaceBase), but not defined in lewis.adapters, where concrete interfaces for protocols are defined.

Parameters:

obj – Object to test.

Returns:

True if obj is an interface type.