Create and use a drivers¶
Every driver is defined by a string in the v_driver.txt
file. Historically,
they are defined as:
group:driver
although such a naming scheme is not necessary.
The drivers can be advertised from multiple packages using the vhc.drivers
entry point group. Each entry of the group associate a string, e.g.
module:callable
, with a callable that must be compatible with
-
libvhc.drivers.
driver_signature
(vcheck, path, argv, *args, **kwargs)[source] Prototype of a function implementing a VHC driver.
Parameters: - vcheck : instance of
VCheck
store the recipe name and the check currently executing
- path : string
path provided to the
vhc
executable.- argv : list of strings
remaining of the command line
- args : list
positional arguments. Not used when calling from
vhc
- kwargs : dictionary
keyword arguments. Not used when calling from
vhc
- vcheck : instance of
Here we illustrate various ways to create a typical driver. In Plug new drivers we will show how to to advertise them.
Anatomy of a driver¶
The majority of drivers act, on their core, on single fits files and in general have this structure:
- Setup: get the file name template for the recipe name, tell the html renderer the expected number of files per ifu, get the configuration and the logger, etc.
- Collect the fits files for the required recipe
- For each file, execute the proper
cure
tool - Collect standard output and error and log them into the appropriate places
- Communicate to the html renderer the success or failure of the check
- Deal smoothly with exceptions: they should be logged and notified to the
them renderer, but they should not cause
vhc
to exit
Factory functions¶
Since the above pattern is very common, we provide the module
libvhc.factories
, that provides three classes 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 = "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
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
the version of VHC and
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
from astropy.io import fits
import libvhc.reference_file_parser as vhcrfp
import libvhc.utils as vhcutils
[...]
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 = "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)
Non standard drivers function_factory()
¶
In case the standard checks do not suite you, we also provide a lower 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: foobar
driver¶
# in foo.py
import subprocess as sp
import libvhc.loggers as vhclog
import libvhc.utils as vhcutils
def _bar(vcheck, fname, lpath):
"run ls on all files"
log = vhclog.getLogger(fname[:lpath-1]) # vhc logger
ifuid = vhcutils.get_ifuid(fname)
self.format_dic['ifuid'] = ifuid
try:
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 = "success"
msg = 'it worked'
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_success = "fail"
msg = 'no, it did not'
except Exception as e:
# catch errors?
[...]
# tell the html renderer whether the test worked or not
vhchtml.add_ifu_test(vcheck, ifuid, msg, html_success)
[...]
bar = factories.function_factory(_bar)