The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Astro::IRAF::CL - Perl interface to the IRAF CL interactive session.

VERSION

0.1

SYNOPSIS

 use Astro::IRAF::CL;

 my $iraf = Astro::IRAF::CL->new();

 my $output1 = $iraf->exec(command => 'print "hello world"',
                           timeout => 10);

 my $output2 = $iraf->print('"hello world"');

DESCRIPTION

This is a Perl module that provides an object-orientated interface to the IRAF CL interactive session, it is built on top of the Perl Expect module. You can script almost anything through this module that you can do in a normal interactive CL session.

This module provides several improved, and more Perl-like, interfaces to various IRAF systems, such as session variables, the management of loading/unloading IRAF packages and the session history. It also provides the ability to specify maximum run times for commands, and the clean handling of these time outs and other types of errors and exceptions. All functions are called in an object-orientated fashion allowing several concurrent interpreter sessions if desired.

Beginning an IRAF CL session

The IRAF CL session is started by creating an object via the new() call, for example:

  my $iraf = Astro::IRAF::CL->new();

Various input parameters can be specified: iraf_start, debug, work_dir, display_startup, log, packages (ARRAY), set (HASH), for example:

  my $iraf = Astro::IRAF::CL->new(debug => 1,
                                  log => *FH,
                                  set => {foo => 1,
                                          bar => 2},
                                  packages => ['mscred','cfh12k']);
  • debug controls how much output is sent to the stderr, this is generally just the command that was actually executed within the IRAF session, its default is zero, i.e. no extra output.

  • work_dir is where any commands should be executed, its default is the current directory.

  • display_startup controls whether or not to show all the information (e.g. motd) from the IRAF startup, not much use in a script so off (zero) by default.

  • log is the filehandle to which logging information should be sent, the default is STDERR.

  • set is a list of variables to setup when the IRAF session is started.

  • packages is list of IRAF packages to load on startup.

As with a normal IRAF CL session when a CL object is created it has to be done from the place where the uparm directory and login.cl file are located. If you have not created these files it is done using mkiraf(1). This place can be specified via the iraf_start parameter passed in through the new() routine call or set via the IRAF_START environment variable (iraf_start has precedence over IRAF_START). Otherwise this place is chosen automatically by the module based on a number of criteria. These are in order of priority:

  1. $HOME

  2. $HOME/iraf

  3. /home/$username/iraf

  4. /home/$username

the script uses the first directory where it finds the file login.cl and the directory uparm. $HOME is the environment variable of that name. The variable $username is found by the script using getlogin(3C), getpwuid(3C), the environment variable $USER or the command whoami(1), in that order of preference.

Loading/Unloading IRAF packages

  • load_package($package) - load an IRAF package, will check to make sure the package exists and will die if it does not.

  • package_is_loaded($package) - Returns 1 if package is loaded, else 0, useful for ensuring a package is not loaded twice (this isn't fatal).

  • package_exists($package) - Checks if an IRAF package is available for loading, (via the IRAF command "deftask") returns 1, if true, else 0.

  • unload_package($package) - Unload a package (the same as typing "bye" in CL). Note that you must unload in the correct order (last in - first out) or the script will die as it would not be able to keep a correct track of the current package and its associated cl prompt.

  • unload_all_packages - Unload all packages that have been loaded in the current session, this is called automatically when the script ends or the object goes out of scope in anyway.

  • get_current_package - Returns the name of the current package, if none is loaded you get an undefined string.

  • list_available_packages - List all the currently available packages.

  • list_available_commands - List all the currently available commands.

  • package_is_available($package) - Slightly different from package_exists(), as it is less rigorous. It purely checks whether the package should exist, not whether it is actually defined.

  • command_is_available($command) - Similar to package_is_available() but for commands.

Setting/Reading IRAF variables

These are similar in style to shell environment variables and last for the full length of the IRAF CL session. I have effectively overloaded a couple of the functions and added the exists() command.

  • set(key1 => $value1, key2 => $value2, key3 => $value3) - set any number of variables to their associated values.

  • show($key) - returns the value of the variable.

  • exists($key) - checks for the existence of the variable, returns 1 or 0.

Executing commands

There are two ways in which to execute IRAF CL commands, the full featured way provides error handling and time out capabilities, as seen below:

  $iraf->exec(command => 'print "hello world"',
         timeout => 10,
         timeout_handler => \&timeout_sub,
         death_handler => \&death_sub,
         error_handler => \&error_sub);

The second method I have named "direct invocation", this is a much simpler method of calling an IRAF command, for example:

  $iraf->print('"hello world"')

Note here that strings where double-quotes are needed in IRAF should be protected from Perl with single-quotes. There are various ways to ensure the continued existence of quotes, see the perl documentation for examples.

The direct invocation method does not provide any system for defining exception handlers this may change in the future. There is, however, something to be said for keeping this simple method and the complex exec method for more explicitly defining commands.

Note that some IRAF commands are overloaded in this module, for example, set() and show(). This allows the commands to be extended or made more Perl like, in general overloading commands may be a bad idea but for simple things like variable control it is very useful.

Error/Exception handling

As can be seen from the exec() example above references to subroutines can be passed in to deal with any problems that are encountered on execution of the command. A maximum run time (timeout) can be specified and a handler to deal with the timeout, although this isn't required. If the handlers are not specified the script will die on encountering any of the exceptions. It is hoped that in the future the code will be able to deal with non-fatal warnings and possibly and generic event to allow for a fully event-driven system to be developed.

  • Timeout handling

    By default a script will die upon timeout, this can be modified in anyway you like to keep the script going and handle the exception cleanly, an example of a handler is:

      my $timed_out = 0;
      $iraf->exec(command => 'print "hello"',
                  timeout => 2,
                  timeout_handler => sub {print $command . " timed out\n"; $timed_out = 1});

    The script can then see from the $timed_out variable that the timeout has occured and can clean up accordingly and move on. Here i have used an anonymous subroutine instead of a reference to a subroutine as it is fairly small.

  • EOF/Death handling

    This is the situation when the whole CL interpreter has died, this is a not uncommon occurence with IRAF as some packages are not quite as robust as they should and the CL is quite fragile in certain situations. If have a large number of jobs to do you do not want to find the program has died fairly early on so you should nominate a handler. In this case the handler will need to call the restart() routine and will also need to change to the correct directory, (this will probably be automated at some point). The restart routine will reload any previously loaded packages. restart will begin a new session with all the parameters that were specified when the object was created.

      $iraf->cd($workdir);
      $iraf->exec(command => 'print "hello"',
                  death_handler => sub {$iraf->restart(); $iraf->cd($workdir)});
  • CL error handling

    A CL error is encountered when the command executed returns with the ERROR code and a message as to what happened. This is dealt with in the same way as the timeout handler above:

      my $error = 0;
      $iraf->exec(command => 'print "hello"',
                  error_handler => sub {print "error occurred\n"; $error = 1});
  • CL warning handling

    There is currently no way to handle CL warnings, it is my intention to implement this feature at some point in the future.

Ending and restarting

The CL object will automatically unload all packages and cleanly shut down the IRAF CL interpreter when your script ends or the object goes out of scope. You can force it to end by calling the end() function, i've never found this to be necessary though. The CL interpreter can be restarted using the restart() function, this is most useful, as demonstrated above, when the CL session has died.

Loading Tasks

  load_task(name => $name,         # Required
            task => $task,         # Not required
            file => $filename,     # Required
            parfile => $yes_or_no) # Defaults to no (zero)

If the task parameter is defined it is considered to contain the full text of the task and if the filename is also specified then the task is written into that file. If the task parameter is not specified the task is considered to be already contained in the file and that is loaded. An IRAF task may or may not have an associated parameter file, the syntax for loading a task is dependent on whether there is a parameter file. Once the task has been loaded it can be called using exec() or the direct invocation method, for example:

  $iraf->load_task(name => 'hello_world',
                   task => 'print "hello world"',
                   file => 'hello_world.cl');

  $iraf->hello_world();

If you call the load_task command more than once with the same name then it will correctly redefine the task.

Logging the session

By default the session is logged to STDERR, you can change this to any file handle you like via:

  set_log(*FILEHANDLE)

Session history

Every command that you execute in the interpreter is stored in the Perl object and can be recalled and executed, the module often executes IRAF commands 'behind-the-scenes' these are not logged to avoid confusion. The commands available are:

  • get_from_command_history($position) - returns the command as a string.

  • exec_from_history($position,%params) - returns any output from the command that has been executed.

The position in the list is based on them being pushed onto a Perl array, so the most recent command is 0 (zero). The %params is the same as the hash passed into the exec() function.

NOTES

You can create as many concurrent IRAF objects as you want (or your system can manage anyway). You should note that each one really needs its own uparm directoctory from which to work to avoid parameter value collisions which could cause chaos. This is not a bug of this Perl module but a problem with the IRAF CL session not knowing when param files are already in use and how to cope with this issue.

Some IRAF programs may need more persuasion than others to believe that they are being used in a non-interactive mode (well non-graphical anyway). If you have problems with this, you could try using the IRAF command "stty xterm", this has worked for me in the past for problems with the nmisc package not co-operating.

The script will convert long commands into lots of shorter strings and enter each into the CL interpreter followed by a line continuation. This allows much longer commands to be entered than by just sending it all in one go. The routine tries to break the string into lengths of 75 characters, as a standard terminal is 80 characters wide - 4 are required for the prompt and 1 for the continuation "\" character. A string may not be broken inside an assignment, (i.e. foo = 1), and also a continuous long string (i.e. no spaces) must not be broken so occasionally the rule of 75 characters must be ignored, in this case you will get a warning printed to STDERR, this is not a problem, it's just good to know about it.

BUGS

These are not so much bugs as short-comings with the current code.

There is no way to specify a time out length, or any error/exception handlers when executing the command directly rather than via the exec() call.

There is currently no way when executing a command from the history to also recall any previously specified time out or error/exception handlers. This can be thought of as both a bug and a feature.

Any bugs, modifications or suggestions for improvements should be sent to the email address given below.

COPYRIGHT

Copyright (C) 1999-2002 Stephen Quinney. All Rights Reserved.

This program was written as part of the NOAO Fundamental Plane project whilst Stephen Quinney was funded by a grant from PPARC and is free software; you can redistribute it and/or modify it under the terms of the GNU Public License. This software comes with absolutely no warranty and the author accepts no responsibility for any unpleasant outcomes from its usage. If this code eats your data it is not my fault... ;-)

AUTHOR

S.J.Quinney, <irafperl@jadevine.org.uk>

SEE ALSO

perl(1). cl(1). mkiraf(1).