Source code for exopy.instruments.drivers.driver_decl

# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Declarator for registering drivers.

"""
from atom.api import Str, Dict, Property, Enum
from enaml.core.api import d_

from ...utils.traceback import format_exc
from ...utils.declarator import Declarator, GroupDeclarator, import_and_get
from ..infos import DriverInfos, INSTRUMENT_KINDS


[docs]class Driver(Declarator): """Declarator used to register a new driver for an instrument. """ #: Path to the driver object. Path should be dot separated and the class #: name preceded by ':'. #: TODO complete : ex: exopy_hqc_legacy.instruments. #: The path of any parent Drivers object will be prepended to it. driver = d_(Str()) #: Name identifying the system the driver is built on top of (lantz, hqc, #: slave, etc ...). Allow to handle properly multiple drivers declared in #: a single extension package for the same instrument. architecture = d_(Str()) #: Name of the instrument manufacturer. Can be inferred from parent #: Drivers. manufacturer = d_(Str()) #: Serie this instrument is part of. This is optional as it does not always #: make sense to be specified but in some cases it can help finding a #: a driver. Can be inferred from parent Drivers. serie = d_(Str()) #: Model of the instrument this driver has been written for. model = d_(Str()) #: Kind of the instrument, to ease instrument look up. If no kind match, #: leave 'Other' as the kind. Can be inferred from parent #: Drivers. kind = d_(Enum(None, *INSTRUMENT_KINDS)) #: Starter to use when initializing/finialzing this driver. #: Can be inferred from parent Drivers. starter = d_(Str()) #: Supported connections and default values for some parameters. The #: admissible values for a given kind can be determined by looking at the #: Connection object whose id match. #: ex : {'VisaTCPIP' : {'port': 7500, 'resource_class': 'SOCKET'}} #: Can be inferred from parent Drivers. connections = d_(Dict()) #: Special settings for the driver, not fitting the connections. Multiple #: identical connection infos with different settings can co-exist in a #: profile. The admissible values for a given kind can be determined by #: looking at the Settings object whose id match. #: ex : {'lantz': {'resource_manager': '@py'}} #: Can be inferred from parent Drivers. settings = d_(Dict()) #: Id of the driver computed from the top-level package and the driver name id = Property(cached=True)
[docs] def register(self, collector, traceback): """Collect driver and add infos to the DeclaratorCollector contributions member. """ # Build the driver id by assembling the package name, the architecture # and the class name try: driver_id = self.id except KeyError: # Handle the lack of architecture traceback[self.driver] = format_exc() return # Determine the path to the driver. path = self.get_path() try: d_path, driver = (path + '.' + self.driver if path else self.driver).split(':') except ValueError: msg = 'Incorrect %s (%s), path must be of the form a.b.c:Class' traceback[driver_id] = msg % (driver_id, self.driver) return # Check that the driver does not already exist. if driver_id in collector.contributions or driver_id in traceback: i = 0 while True: i += 1 err_id = '%s_duplicate%d' % (driver_id, i) if err_id not in traceback: break msg = 'Duplicate definition of {}, found in {}' traceback[err_id] = msg.format(self.architecture + '.' + driver, d_path) return try: meta_infos = {k: getattr(self, k) for k in ('architecture', 'manufacturer', 'serie', 'model', 'kind') } infos = DriverInfos(id=driver_id, infos=meta_infos, starter=self.starter, connections=self.connections, settings=self.settings) # ValueError catch wrong kind value except (KeyError, ValueError): traceback[driver_id] = format_exc() return # Get the driver class. d_cls = import_and_get(d_path, driver, traceback, driver_id) if d_cls is None: return try: infos.cls = d_cls except TypeError: msg = '{} should be a callable.\n{}' traceback[driver_id] = msg.format(d_cls, format_exc()) return collector.contributions[driver_id] = infos self.is_registered = True
[docs] def unregister(self, collector): """Remove contributed infos from the collector. """ if self.is_registered: # Remove infos. driver_id = self.id try: del collector.contributions[driver_id] except KeyError: pass self.is_registered = False
def __str__(self): """Identify the decl by its members. """ members = ('driver', 'architecture', 'manufacturer', 'serie', 'model', 'kind', 'starter', 'connections', 'settings') st = '{} whose known members are :\n{}' st_m = '\n'.join(' - {} : {}'.format(m, v) for m, v in [(m, getattr(self, m)) for m in members] ) return st.format(type(self).__name__, st_m) # ========================================================================= # --- Private API --------------------------------------------------------- # ========================================================================= def _default_manufacturer(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('manufacturer') def _default_serie(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('serie') def _default_kind(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('kind') def _default_architecture(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('architecture') def _default_starter(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('starter') def _default_connections(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('connections') def _default_settings(self): """Default value grabbed from parent if not provided explicitely. """ return self._get_inherited_member('settings') def _get_inherited_member(self, member, parent=None): """Get the value of a member found in a parent declarator. """ parent = parent or self.parent if isinstance(parent, Drivers): value = getattr(parent, member) if value: return value else: parent = parent.parent else: parent = None if parent is None: if member == 'settings': return {} # Settings can be empty elif member == 'serie': return '' # An instrument can have no serie elif member == 'kind': return 'Other' raise KeyError('No inherited member was found for %s' % member) return self._get_inherited_member(member, parent) def _get_id(self): """Create the unique identifier of the driver using the top level package the architecture and the class name. """ if ':' in self.driver: path = self.get_path() d_path, d = (path + '.' + self.driver if path else self.driver).split(':') # Build the driver id by assembling the package name, architecture # and the class name return '.'.join((d_path.split('.', 1)[0], self.architecture, d)) else: return self.driver
[docs]class Drivers(GroupDeclarator): """Declarator to group driver declarations. For the full documentation of the members values please the Driver class. """ #: Name identifying the system the driver is built on top of for the #: declared children. architecture = d_(Str()) #: Instrument manufacturer of the declared children. manufacturer = d_(Str()) #: Serie of the declared children. serie = d_(Str()) #: Kind of the declared children. kind = d_(Enum(None, *INSTRUMENT_KINDS)) #: Starter to use for the declared children. starter = d_(Str()) #: Supported connections of the declared children. connections = d_(Dict()) #: Settings of the declared children. settings = d_(Dict()) def __str__(self): """Identify the group by its mmebers and declared children. """ members = ('path', 'architecture', 'manufacturer', 'serie', 'kind', 'starter', 'connections', 'settings') st = '{} whose known members are :\n{}\n and declaring :\n{}' st_m = '\n'.join(' - {} : {}'.format(m, v) for m, v in [(m, getattr(self, m)) for m in members if getattr(self, m)] ) return st.format(type(self).__name__, st_m, '\n'.join(' - {}'.format(c) for c in self.children))