tcs_proxy – Proxy to the TCS modules

Testing code that uses TCS logging and the TCS subsystem interface outside of HET is not possible. Therefore one need to create classes that mocks the original and a way to easily switch between the development and the production environment.

The tcs_lib.tcs_proxy provides a proxy between an application and the TCS systems. It is a thin layer that initialize the necessary objects and allow easy access to them in a transparent and flexible way.

After importing the module:

from tcs_lib import tcs_proxy

we must initialize the proxy before being able to use it:

tcs_proxy.init(conf, section='urls')

conf is a configuration object containing in the section section all the entries needed to create the logging object and the necessary TCS subsystems. The configuration entries necessary to initialize the proxy are described in init(). An example configuration to use to initialize the logger and two TCS subsystems is:

[urls]
# url to use for the TCS logger, if the module implementing it is found
tcs_log = tcp://127.0.0.1:5555
# if the TCS logger is not found, use a mock object and use the file of the
# next option to configuration the python loggers to used for the mocks
tcs_log_mock_path = /path/to/conf_file.cfg

# We also want two TCS subsystems called tcs and virus
subsystem_names = tcs, virus

# the tcs subsystem url. But we don't need the tcs_mock_path since the
# configuration is already in ``tcs_log_mock_path``
tcs = tcp://127.0.0.1:5556
# and now the virus subsystem
virus = tcp://127.0.0.1:5557
# for virus we might have a difference logger configuration file for the
# mock object
virus_mock_path = /path/to/virus_logger.cfg

After the initialisation, the following attributes become available:

  • tcs_proxy.tcs_log: this attribute refers either to an instance of TCSLog.TCSLog or a wrapper around a standard python logger that exposes the log_* methods listend in _MockTCSLog
  • tcs_proxy.tcs, tcs_proxy.virus: these attributes refer either to instances of tcssubsystem.TCSSubSystem or of a mock class that echoes the method called and their arguments via a standard python logger.

Besides those, the TCS proxy exposes also an other attribute: tcs_proxy.errors. If the tcssubsystem is importable, this attribute is an alias of the module itself: i.e. tcs_proxy.errors.error is equivalent to tcssubsystem.error. If the module is not found, any variable name is associated with Exception: i.e. both tcs_proxy.errors.error and tcs_proxy.errors.MyPersonalError are equivalent to Exception. This attribute allows to catch and/or raise TCS exceptions, when available, or generic ones otherwise.

As said the mock objects use python loggers to log messages and method calls, whose names are {mod_name}.{name} where mod_name is the value of tcs_proxy.mod_name, defaulting to tcs_lib.tcs_proxy, and name is either tcs_log or the name of the TCS subsystem. For other software using the proxy it might be preferable to set the mod_name before initializing it, in order to own the loggers. So for a package my_package I advice to do the following:

from tcs_lib import tcs_proxy
tcs_proxy.mod_name = 'my_package.tcs_proxy'
tcs_proxy.init(conf, section='urls')

The initialised proxies can be cleared using the clear() function. The TCS proxy will be also marked as not initialized.

Note

Assigning tcs_proxy.mod_name = 'my_package.tcs_proxy' after initializing the proxy results in AttributeError. If necessary it is possible to clear() the proxy, set the mod_name and then re-initialize.

Implementation

Proxy for the python interface to the HET Telescope Control System (TCS).

This module tries to import tcssubsystem and TCSLog:

  • if it succeeds, initialize all the requested TCS instances.
  • otherwise use some mock classes that log every command are used
class tcs_lib._tcs_proxy.TCSProxy(mod_name='tcs_lib._tcs_proxy')[source]

Bases: object

Proxy implementation. One instance is exposed as tcs_lib.tcs_proxy.

The only method public method is init() and must be called before the proxy attributes can be used.

Parameters:
mod_name : string, optional

name of the module to use by the mock TCSLog and tcssubsystem

Attributes:
mod_name

Name of the module used to initialize the proxy.

tcs_log : TCSLog.TCSLog or _MockTCSLog

instance of the TCSLog class or of the corresponding mock version

{name} : tcssubsystem.TCSSubSystem or _MockTCSSubsystem

for each name in the subsystem_names configuration option, instance of the TCSSubSystem class or of the corresponding mock version

errors

proxy objects for TCS exceptions. If the tcssubsystem module is found, this attribute it’s an alias of the module, otherwise it is an instances of _MockErrors

mod_name

Name of the module used to initialize the proxy. Can be modified only before calling init()

init(conf, section='urls')[source]

Initialize the TCSLog and tcssubsystems, or their mock counterpart.

The following configuration entries from the section are used:

  • tcs_log, optional: url to use to initialise the TCSLog.TCSLog class. If not given, empty or the TCSLog module is not found, initialize a mock log class
  • tcs_log_mock_path, optional: if the TCSLog.TCSLog cannot be initialized, a mock class is used instead. This class is a proxy for a standard python logger with name {mod_name}.tcs_log. By default the NullHandler is attached. However it is possible to customize the loggers, using a configuration file according to the logger configuration file format. The name of this configuration file can be passed using the tcs_log_mock_path option.
  • subsystem_names, optional: list of comma separated names of TCS subsystems to initialize. For each name the following options are used
    • {name}, optional: url to use to initialise the tcssubsystem.TCSSubSystem class. If not given, empty or the tcssubsystem module is not found, initialize a mock subsystem class
    • {name}_mock_path, optional: the option has the same meaning of the tcs_log_mock_path one, with the difference that the python logger name use is {mod_name}.{name}

Note

If the logger configuration file contains the configurations for the mock logger and subsystems, there is no need to assign it to every *_mock_path, but only to the first one used, e.g. tcs_log_mock_path.

Parameters:
conf : configparser.ConfigParser instance

configuration files necessary to initialize the TCS log and subsystems.

section : string, optional

name of the section containing the configuration

clear()[source]

Clear the attributes created by init() and reset the status to uninitialized.

_init_tcs_log(conf, section)[source]

Initialize the tcs logging

Parameters:
conf : configparser.ConfigParser instance

configuration files necessary to initialize the TCS log and subsystems.

section : string, optional

name of the section containing the configuration

Returns:
:class:`TCSLog.TCSLog` or :class:`_MockTCSLog`
_init_tcs_subystem(name, conf, section)[source]

Initialize the TCS subsystem or mock subsystem and return it.

Parameters:
name : string

name of the subsystem

conf : configparser.ConfigParser instance

configuration files necessary to initialize the TCS log and subsystems.

section : string, optional

name of the section containing the configuration

Returns:
:class:`tcssubsystem.TCSSubSystem` or :class:`_MockTCSSubsystem`
__getattr__(name)[source]

Try to get TCS log/subsystem called name. For sure this will fail if used before calling init()

Parameters:
name : string

attribute name

Raises:
AttributeError

if name is not a valid attribute

class tcs_lib._tcs_proxy._MockErrors[source]

Bases: object

Instances of this class return an Exception as attribute.

class tcs_lib._tcs_proxy._MockTCSSubsystem(name, mod_name, log_conf_file)[source]

Bases: object

Class that log the methods called and their positional and keyword arguments.

The name of the logger used is {mod_name}.{name} where name is the name to the constructor.

In order to customize this logger, use the ocd configuration file as described here. If log_conf_file is an empty string or if the configuration parsing fails, the logging.NullHandler is added to the logger used here. In case of a parsing failure a warning will be printed.

Parameters:
name: string

name of the instance created

mod_name : string

name of module to associate to the mock proxy objects; used to create the logger name

log_conf_file : string

name of the configuration files necessary to initialize the mock tcs log

__getattr__(name)[source]

Return a function that logs the call

Parameters:
name : string

attribute name

class tcs_lib._tcs_proxy._MockTCSLog(name, mod_name, log_conf_file)[source]

Bases: tcs_lib._tcs_proxy._MockTCSSubsystem

Replacement for the TCSLog.TCSLog when the TCSLog module is not available.

This class translates the TCSLog.TCSLog log_* calls to the appropriate python logging levels:

method log level
log_debug 10 (DEBUG)
log_info 20 (INFO)
log_warn 30 (WARNING)
log_error 40 (ERROR)
log_fatal 50 (CRITICAL)
log_alarm 60

The TCSLog.TCSLog.log_*() methods have the following signature:

log_info(msg, *args)

The log message is formatted with msg.format(*args) before emitting it.

Warning

Because of the formatting, log messages are emitted by a function in this module and not in the place where the log_* method is called.

The name of the logger used is {mod_name}.{name}. When initialised by TCSProxy {name} is tcs_log.

Parameters:
name: string

name of the logger to use

mod_name : string

name of module to associate to the mock proxy objects; used to create the logger name

log_conf_file : string

name of the configuration files necessary to initialize the mock tcs log

__getattr__(name)[source]

convert calls to log_*.

Parameters:
name : string

attribute name

Raises:
AttributeError

if name is not one of the valid log_* names

class tcs_lib._tcs_proxy._LogCall(log, obj, function)[source]

Bases: object

Callable class log the call to function with all its positional and keyword arguments.

The function is logged as info.

Parameters:
log : logging.Logger

logger

obj, function : string

name of the object and the function called

__call__(*args, **kwargs)[source]

Log the call of the function as "{obj}.{function}({args}, {kwargs})".

Returns:
:class:`_MockResponse`

current instance that mimic the dictionary returned by each TCSSubSystem method

class tcs_lib._tcs_proxy._MockResponse[source]

Bases: dict

Extension of a dictionary that returns the key if not present.

Examples

>>> d = _MockResponse(a=42)
>>> d['a']
42
>>> key = 'b'
>>> d[key] == key
True
__getitem__(key)[source]

If the key is not found, return it, otherwise returns the associated value