Create and use a drivers

Every driver is defined by a string:

module:function

that instructs vhc to load the module module and execute the function, having the signature libvhc.function_signature().

Anatomy of a driver

The majority of drivers act on single fits files and have this structure:

  1. Collect the fits files for the required recipe
  2. For each file, execute the proper cure tool
  3. Collect standard output and error and log it into the appropriate places
  4. Communicate to the html renderer the success or failure of the check
  5. Deal with exceptions

Factory functions

Since the above pattern is very common, we provide the module libvhc.factories, that provides three functions that automatize the creation of a driver.

Simple commands: command_line_factory()

Simple commands, that don’t require any external information, can be easily setup providing a string with the command to execute.

Example: common.exptime driver

# in common.py
command = "{curebin}/checkheader -E -r {recipe} {fname}"
# ``exptime`` is the callable implementing the driver
exptime = command_line_factory(command)

The variables within {...} are expanded in the body of the factory as the driver is executed. The available variables are

curebin cure binaries directory
recipe recipe at hand
driver driver at hand
fname name of the file to test
ifuid id of the ifu from the fname header
headkey instruct cure to write in the fits headers whether the check is successful or not

If by chance your command needs to have a set of curly brackets, you can escape them doubling them {{...}}.

Not so simple commands: command_func_factory()

Sometimes you need to build the string programmatically, e.g. reading information for some configuration file, do some computation, etc. This factory allow to easily do this

Example: common.check_overscan driver

# in common.py
import libvhc.reference_file_parser as vhcrfp
[...]
def _check_overscan_cmd(vcheck, fname, log, conf):
    """Build the call for the overscan check

    Parameters
    ----------
    vcheck : instance of :class:`~libvhc.VCheck`
        store the recipe name and the check currently
        executing
    fname : string
        name of the file to check
    log : instance of :class:`logging.LoggerAdapter`
        logger
    conf : instance of :class:`configparser.ConfigParser`
        configuration object

    Returns
    -------
    cmd : string
        string of the command to execute
    """
    # get the overscan reference value(s) from configuration file
    header = fits.getheader(fname)
    spectid = vhcutils.get_specid(header)
    channel = vhcutils.get_channel(header)
    amplifier = vhcutils.get_amplifier(header)
    vals = vhcrfp.picker("overscan").get_value(spectid, channel, amplifier)
    mean, stddev, dev_mean, dev_stddev = vals

    # build the checkheader call
    cmd_str = "{curebin}/checkheader -O -r {recipe}"
    # expected mean and standard deviation
    cmd_str += " --ovc_mean {} --ovc_stddev {}".format(mean, stddev)
    # maximum deviation from them
    cmd_str += " --max_ovc_dev_mean {} --max_ovc_dev_rms {}".format(dev_mean,
                                                                    dev_stddev)

    cmd_str += " {fname}"

    return cmd_str

# ``check_overscan`` is the callable implementing the driver
check_overscan = factories.command_func_factory(_check_overscan_cmd)

As before, the variables within {...} are expanded in the body of the factory.

Non standard drivers function_factory()

In case the standard checks do not suite you, we also provide a low level factory, which loop through the fits files and call the provided function on each of the files. But you have to implement yourself the communication the success or failure of the checks with the output files and the html renderer.

Example: foo.bar

import subprocess as sp

import libvhc.loggers as vhclog

# in foo.py
def _bar(vcheck, fname, lpath):
    "run ls on all files"
    log = vhclog.getLogger(fname[:lpath-1])  # vhc logger

    p = sp.Popen(["ls", "-l", fname], stdout=sp.PIPE, stderr=sp.PIPE)
    stdout, stderr = p.communicate()
    log.info(stdout)  # will got to the ``log.txt`` file
    html = "success"
    if stderr:  # if there is some error appears on standard error
        # will got to the ``log.txt`` and the ``v_results.txt`` file
        log.error(stderr)
        html = "fail"

    # tell the html renderer whether the test worked or not
    [...]
    # catch errors?
    [...]

bar = factories.function_factory(_bar)