Executing MIRIAD Tasks: mirexec

The mirexec module makes it convenient to launch tasks from within Python. The simplest invocation looks a lot like what one would run on the command line:

from mirexec import TaskUVFlag
TaskUVFlag (vis='fx64a-3c286-2700', select='ant(26)', flagval='f',
            noquery=True).run ()

If you need them, however, the mirexec module provides more sophisticated facilities allowing you to retrieve the output of a task, run many tasks in parallel, and so on.

Running a MIRIAD task in mirexec requires three steps which, as shown above, can often be condensed into a single line of Python:

  1. Create an instance of a “task” class corresponding to the task you wish to run.
  2. Set the task keywords and options.
  3. Call a method that actually launches the task.

The following code is equivalent to the first example, but breaks down the steps more explicitly:

from miriad import VisData
from mirexec import TaskUVFlag
v = VisData ('fx64a-3c286-2700')
# Create instance:
t = TaskUVFlag ()
# Set some keywords:
t.vis = v
t.select = 'ant(26)'
t.flagval = 'f'
# Set some options:
t.noquery = True
t.hms = False # i.e., do *not* specify the "hms" option
# Launch task.
# Executes: uvflag vis=fx64a-3c286-2700 select=ant(26) flagval=f options=noquery
t.run ()

Creating Task Instances

The TaskUVFlag class shown in the above examples is a subclass of the TaskBase class, which provides a generic structure for invoking MIRIAD tasks. The mirexec module defines such subclasses for many, but far from all, of the tasks provided with MIRIAD. It’s easy to create your own TaskBase subclass for anything you need that’s not provided with miriad-python, however. See below for more information.

The TaskBase class provides functions for setting keyword arguments and actually invoking the task. For the full details, see the detailed API documentation. Subclasses specify the name of the particular task that is run and the keywords and options it accepts.

Task instances can be reused: you can create an object, set arguments, and run it, then change some or all of the arguments and run it again. Among other uses, this makes it easy to apply a task to several datasets:

t = TaskUVAver ()
t.interval = 5
t.line = 'chan,800,101'
t.nocal = True
for v in listManyDatasets ():
   # The set() method returns 'self' for easy chaining of
   # method invocations.
   t.set (vis=v, out=v.vvis ('av')).run ()

Setting Task Parameters

You can set the task parameters in several ways: as a property on the object, as in the example above, as a keyword argument to the object’s constructor, or as a keyword argument to the object’s set() method. The latter two forms are shown in the example below:

from miriad import VisData
from mirexec import TaskUVFlag
v = VisData ('fx64a-3c286-2700')
# This is equivalent to the previous example.
t = TaskUVFlag (vis=v, flagval='f', noquery=True)
t.select = 'ant(26)'
t.run ()
# As is this.
t.set (vis=v, select='ant(26)', flagval='f', noquery=True)
t.run ()

Thus, the most succinct way to execute a task is to write something like:

TaskUVFlag (vis=v, flagval='f', select='pol(yy)').run ()

The names and values of keywords in Python are mapped to command-line arguments with the following rules:

  • Keyword arguments have the same name in Python as they do on the command-line if possible. If the MIRIAD keyword is a Python keyword (e.g., “in”), the keyword is accessible in Python by suffixing it with an underscore (“in_”).

  • In most cases, the textual value of each MIRIAD keyword is the stringification of the Python variable assigned to it. If the Python value is None, the keyword is not supplied on the command-line.

  • However, if the Python variable assigned to the keyword is a non-string iterable, the textual value of the keyword is the stringification of each item in the iterable, joined together with commas. For instance, if you run:

    from mirexec import TaskMfCal
    TaskMfCal (vis=foo, line=['chan', 60, 15]).run ()
    

    the line keyword of mfcal will be chan,60,15.

  • The keyword “options” isn’t used directly. Instead, each possible option to a task is a separate field on the task object that should be set to a bool. The option is supplied if the field is True. There are rare tasks that have an option with the same name as a keyword; in those cases, the keyword is the one controlled by the property on the task object.

There are several functions that will actually execute the task. Each has different uses:

  • run() executes the task and waits for it to finish. The task output is sent to the stdout of the Python program and the task input is set to /dev/null.
  • snarf() executes a task and waits for it to finish. The task’s output to its standard output and standard error streams are returned to the caller.
  • runsilent() executes the task and waits for it to finish. The task output is sent to /dev/null.
  • launch() starts the task but doesn’t wait for it to finish; instead, it returns a MiriadSubprocess instance that allows interaction with the launched subprocess.
  • launchpipe() starts the task but doesn’t wait for it to finish. The output of the task is redirected to pipes that can be read using the MiriadSubprocess instance.
  • launchsilent() starts the task but doesn’t wait for it to finish. The output of the task is redirected to /dev/null.

Defining Your Own Task Classes

In most cases, it’s straightforward to define your own task class. To wrap the task “newtask”, you should write something like:

from mirexec import TaskBase

class TaskNewTask (TaskBase):
    _keywords = ['vis', 'line', 'flux', 'refant']
    _options =  ['nocal', 'nopass', 'mfs']

def demo (vis):
    t = TaskNewTask (vis=vis)
    t.flux = 1.0
    t.nocal = True
    t.run ()

The name of the task executable is inferred from the class name by stripping off the prefix “Task” and lower-casing the rest of the letters. If this heuristic won’t work, you can specify the task name explicitly by setting _name on the class:

from mirexec import TaskBase

class DifferentNames (TaskBase):
    _name = 'newtask'
    _keywords = ['vis', 'line', 'flux', 'refant']
    _options =  ['nocal', 'nopass', 'mfs']

If you’re feeling fancy, here’s a less typing-intensive way of generating arrays of short strings:

from mirexec import TaskBase

class TaskNewTask (TaskBase):
    _keywords = 'vis line flux refant'.split ()
    _options =  'nocal nopass mfs'.split ()

mirexec API Reference

This section presents a detailed API reference for the mirexec module.

Generic Task Class

class mirexec.TaskBase(**kwargs)
Synopsis :Generic MIRIAD task launcher
Parameters:kwargs – attributes to set on the object (with set())

A generic launcher class for MIRIAD tasks. Don’t create instances of this class — instead, create instances of its subclasses, such as TaskInvert or TaskUVCat. If you want to invoke a task that hasn’t been wrapped in the mirexec module, it’s easy to wrap it yourself: see Defining Your Own Task Classes.

launch(**kwargs)

Launch an invocation of the task with the current keywords

Parameters:kwargs – extra arguments to pass to the MiriadSubprocess constructor
Returns:a MiriadSubprocess instance
Raises :TaskLaunchError if there was an error launching the task

This task launches an instance of the task. It does not wait for that instance to complete; you must use the returned MiriadSubprocess instance to wait for completion and check results.

This function can be useful if you want to launch several tasks in parallel. To wait for the task to complete, use run(). To wait for the task and obtain its output, use snarf(). To wait for the task and discard its output, use runsilent().

launchpipe(**kwargs)

Launch an invocation of the task with the current keywords, redirecting its output so that it may be examined by the caller.

Parameters:kwargs – extra arguments to pass to the MiriadSubprocess constructor
Returns:a MiriadSubprocess instance
Raises :TaskLaunchError if there was an error launching the task

This task launches an instance of the task. It does not wait for that instance to complete; you must use the returned MiriadSubprocess instance to wait for completion and check results. To access the task output, use the methods and attributes provided by MiriadSubprocess.

This function can be useful if you want to launch several tasks in parallel. To just wait for the task, use snarf(). To wait for the task and ignore its output, use run(). To wait for the task and discard its output, use runsilent().

launchsilent(**kwargs)

Launch an invocation of the task with the current keywords, discarding its output.

Parameters:kwargs – extra arguments to pass to the MiriadSubprocess constructor
Returns:a MiriadSubprocess instance
Raises :TaskLaunchError if there was an error launching the task

This task launches an instance of the task. It does not wait for that instance to complete; you must use the returned MiriadSubprocess instance to wait for completion and check results.

The task output is discarded. This is not recommended in most situations, since usually the task output is the only means by which errors can be diagnosed.

This function can be useful if you want to launch several tasks in parallel. To just wait for the task, use runsilent(). To launch the task and ignore its output, use launch(). To wait for the task and ignore its output, use run().

options

This is a read/write synthetic property that encodes the current options in the form of a comma-separated string as per standard MIRIAD usage. It is recommended that you use the independent boolean fields to get and set option states, but this mechanism is provided for completeness. Here are some examples:

t = TaskUVPlot (source=True, hours=True)
print t.options # yields 'hours,source'
t.options = 'sou,ava'
print t.options # yields 'avall,source'
print t.avall # yields True
del t.options
print t.options # yields ''

As shown in the example above, abbreviations of option names are allowed when setting options using this property. Because of this effect and possible reordering, option strings do not roundtrip:

t.options = s
s == t.options # not necessarily True

Because there is no way in an option string to express the deactivation of an option, setting this property clears all options not explicitly activated. Deleting the options property clears all options.

run(failok=False, log=None, **kwargs)

Run the task with the current keywords.

Parameters:
  • failok (bool) – if True, no exception will be raised if the task returns a nonzero exit code.
  • log (filelike) – where to log debugging information if the task fails, or None (the default) not to log this information.
  • kwargs – extra arguments to pass to the MiriadSubprocess constructor.
Raises :

TaskLaunchError if there was an error launching the task.

Raises :

TaskFailError if the task returns a nonzero exit code.

Returns:

self

Runs the task and waits for it to complete. By default, if the task returns an error code, a TaskFailError is raised, but this can be disabled by setting failok to True. The standard output and error of the task will not be redirected.

To retrieve the output of the task, use snarf(). To not wait for the task to complete, use launch(). To discard the output of the task, use runsilent().

runsilent(failok=False, log=None, **kwargs)

Run the task with the current keywords, discarding its output.

Parameters:
  • failok (bool) – if True, no exception will be raised if the task returns a nonzero exit code.
  • log (filelike) – where to log debugging information if the task fails, or None (the default) not to log this information.
  • kwargs – extra arguments to pass to the MiriadSubprocess constructor.
Raises :

TaskLaunchError if there was an error launching the task.

Raises :

TaskFailError if the task returns a nonzero exit code.

Returns:

self

Runs the task and waits for it to complete. By default, if the task returns an error code, a TaskFailError is raised, but this can be disabled by setting failok to True.

The task output is discarded. This is not recommended in most situations, since usually the task output is the only means by which errors can be diagnosed.

To ignore the output of the task, use run(). To not wait for the task to complete, use launchsilent().

set(**kwargs)

Set keywords and options on the task

Parameters:kwargs – the values to set
Returns:self

This function is merely a shorthand that sets attributes on the instance via setattr(). To provide for consistent semantics, if one of the arguments sets the synthetic “options” attribute, this attribute is set before all others. That is:

t.set (a=True, b=False, options='b,c')

will result in the options a and c being True. Without this ordering constraint, the results of the above operation would vary unpredictably with the order of items returned by iterating through kwargs.

snarf(send=None, failok=False, log=<open file '<stderr>', mode 'w' at 0x2aaaaaaf61e0>, **kwargs)

Run the task and retrieve its output.

Parameters:
  • send (str) – input to send the task on its standard input, or None not to do so
  • failok (bool) – if True, no exception will be raised if the task returns a nonzero exit code
  • log – where to log the task’s output if it fails, or None not to log the output. Default is sys.stderr.
  • kwargs – extra arguments to pass to the MiriadSubprocess constructor
Raises :

TaskLaunchError if there was an error launching the task.

Raises :

TaskFailError if the task returns a nonzero exit code

Returns:

(stdout, stderr), both of which are arrays of strings of the task’s output split on line boundaries (via str.splitlines()). They correspond to the standard output and standard error of the task subprocess, respectively. Note that the proper interleaving of the outputs (i.e., where the standard error messages appear, relative to the standard output lines) is unknowable.

Runs the task, wais for it to complete, and returns its textual output. By default, if the task returns an error code, a TaskFailError is raised, but this can be disabled by setting failok to True.

To leave the output of the task un-redirected, use run(). To not wait for the task to complete, use launch(). To discard the output of the task, use runsilent().

class mirexec.MiriadSubprocess(command, **kwargs)
Synopsis :

a handle to a MIRIAD task that was launched.

Parameters:
  • command – an iterable of strings giving the command and arguments to launch. Saved as the command attribute after initialization.
  • kwargs – extra arguments to be passed to subprocess.Popen.
Raises :

the same things that subprocess.Popen may raise; OSError in most cases.

This class allows you to interact with a running MIRIAD task and interrogate it after the task finishes. Generally, you won’t create MiriadSubprocess instances yourself, but instead will have them returned to you from calls to TaskBase.launch(), launchpipe(), or launchsilent().

MiriadSubprocess is a subclass of subprocess.Popen and offers all of the same features. This class additionally provides checkwait() which waits for the task to finish, then raises TaskFailError if the task failed. The checkcommunicate() method does the same thing but also allows the task output to be captured. (It also allows input to be sent to the task, though this is less often useful.)

The creation of a MiriadSubprocess instance is synonymous with launching a subprocess – if the creation succeeds, the subprocess is launched.

checkFailNoPipe(log=None)

Raise an exception if the task failed; to be used if the task output was not captured. Assumes the subprocess has finished executing.

Parameters:log (filelike) – if the task did indeed fail, print extra debugging information to this stream, unless it is None (the default).
Returns:self
Raises :StandardError if the task has not yet finished running
Raises :TaskFailError if the task failed.

Note that this function only returns if the task finished successfully. Otherwise, an exception is raised.

checkFailPipe(stdout, stderr, log=None)

Raise an exception if the task failed; to be used if the task output was captured. Assumes the subprocess has finished executing.

Parameters:
  • stdout (str) – a single string giving the captured standard output of the task.
  • stderr (str) – a single string giving the captured standard error of the task.
  • log (filelike) – if the task did indeed fail, print extra debugging information to this stream, unless it is None (the default). Unlike checkFailNoPipe(), this debugging information contains the contents of stdout and stderr.
Returns:

self

Raises :

StandardError if the task has not yet finished running

Raises :

TaskFailError if the task failed.

Note that this function only returns if the task finished successfully. Otherwise, an exception is raised.

Also note that stdout and stderr are only used to log extra debugging information. It is important to do this, because if the task output is captured and the task fails, the user will have no way of knowing why the task failed unless the task output is logged somewhere.

checkcommunicate(send=None, failok=False, log=None)

Exchange input and output with the task, wait for it to complete, and then raise an exception if it failed.

Parameters:
  • send (str) – input to send to the subprocess in its standard input stream, or None to send nothing.
  • failok (bool) – if True, don’t raise an exception if the task failed – this is equivalent to just calling communicate(). The default is False.
  • log (filelike) – if the task did indeed fail, extra debugging information will be printed to this stream, unless it is None (the default).
Returns:

a tuple (stdout, stderr), two strings of the output captured from the subprocess.

Raises :

TaskFailError if the task failed and failok was False.

The returned stdout and stderr contents are all buffered into single Python strings, so this method is not appropriate if the task will generate very large amounts of output.

This method captures the task output and hence uses checkFailPipe(). If the output should be not captured, used checkwait().

checkwait(failok=False, log=None)

Wait for the task to complete, then raise an exception if it failed.

Parameters:
  • failok (bool) – if True, don’t raise an exception if the task failed – this is equivalent to just calling wait(). The default is False.
  • log (filelike) – if the task did indeed fail, extra debugging information will be printed to this stream, unless it is None (the default).
Returns:

self

Raises :

TaskFailError if the task failed and failok was False.

This method assumes that the task output has not been captured and hence uses checkFailNoPipe(). If the output should be captured, used checkcommunicate().

command = None

An iterable of strings giving the command and arguments that were executed.

exception mirexec.TaskLaunchError(command, fmt, *args)

Indicates that it was not possible to launch the desired task. A common cause of this is that the task was not found in the executable search path.

Instances have an attribute command, an array of strings giving the command and arguments whose invocation was attempted. The zeroth string is the task name.

Instances also have an attribute detail, which is a string giving a more detailed explanation of what failed. This is currently the stringification of any OSError that was raised in the attempt to launch the task.

exception mirexec.TaskFailError(returncode, cmd)

Signals that a task exited indicating failure, though it was able to be launched.

TaskFailError may be a subclass of subprocess.CalledProcessError, if such a class exists. (It was introduced in Python 2.5.) Otherwise, it is a functional equivalent to that class.

Instances have an attribute returncode indicating the exit code of the task. This will be nonzero, since zero indicates success. As far as I know, all MIRIAD tasks exit with a code of 1 unless they die due to a POSIX signal (in which case, the exit code is conventionally the negative of the signal number).

Instances also have an attribute cmd which is a string version of the command line that was executed. The arguments are joined together with spaces, so there’s potential for ambiguity if some of the argument values contain spaces.

Specific Task Classes

We try to keep this list up-to-date, but it may not be complete. If you discover a wrapped task that isn’t documented here, please notify the author. As mentioned above, it’s straightforward to wrap a new task yourself: see Defining Your Own Task Classes.

class mirexec.TaskCgDisp(**kwargs)
class mirexec.TaskUVList(**kwargs)
class mirexec.TaskUVPlot(**kwargs)
class mirexec.TaskInvert(**kwargs)
class mirexec.TaskClean(**kwargs)
class mirexec.TaskRestore(**kwargs)
class mirexec.TaskImStat(**kwargs)
class mirexec.TaskImHead(**kwargs)
class mirexec.TaskIMom(**kwargs)
class mirexec.TaskImFit(**kwargs)
class mirexec.TaskUVAver(**kwargs)
class mirexec.TaskGPCopy(**kwargs)
class mirexec.TaskMSelfCal(**kwargs)
class mirexec.TaskSelfCal(**kwargs)
class mirexec.TaskPutHead(**kwargs)
class mirexec.TaskGPPlot(**kwargs)
class mirexec.TaskPrintHead(**kwargs)
class mirexec.TaskClosure(**kwargs)
class mirexec.TaskUVFlag(**kwargs)
class mirexec.TaskUVSpec(**kwargs)
class mirexec.TaskUVSort(**kwargs)
class mirexec.TaskMfCal(**kwargs)
class mirexec.TaskUVIndex(**kwargs)
class mirexec.TaskUVCat(**kwargs)
class mirexec.SmaUVPlot(**kwargs)
class mirexec.SmaUVSpec(**kwargs)
class mirexec.TaskUVGen(**kwargs)
class mirexec.TaskUVGen2(**kwargs)
class mirexec.TaskUVCal(**kwargs)
class mirexec.TaskUVFlux(**kwargs)
class mirexec.TaskUVFit(**kwargs)
class mirexec.SmaMfCal(**kwargs)
class mirexec.TaskGPCal(**kwargs)
class mirexec.TaskMaths(**kwargs)
class mirexec.TaskImGen(**kwargs)
class mirexec.TaskLinMos(**kwargs)
class mirexec.TaskImSub(**kwargs)
class mirexec.TaskImMedian(**kwargs)
class mirexec.TaskRegrid(**kwargs)
class mirexec.TaskSFind(**kwargs)
class mirexec.TaskFFT(**kwargs)

Setting up Subprocess Environment

mirexec.addEnvironmentClassic(home, hosttype)
mirexec.addEnvironmentAutotools(home)

Utility Classes

class mirexec.DefaultedTaskType(name, bases, mdict)