Usage¶
To use kalasiris in a project:
import kalasiris
kalasiris Core¶
The central piece of the kalasiris library provides Python function calls that allow you to easily run ISIS programs from within Python in a manner similar to the way that you run ISIS programs on the command line.
So if you would type this in an ISIS enabled command line:
(isis) % catlab from=some.cub to=some.pvl
To achieve the same thing from within a Python program, you could do this (you don’t need to alias kalasiris as isis in your programs, but it is shorter to type):
import kalasiris as isis
isis.catlab('some.cub', to='some.pvl')
Of course, you could use variables instead of strings:
import kalasiris as isis
from_cube = 'some.cub'
pvl_file = 'some.pvl'
isis.catlab(from_cube, to=pvl_file)
Or with pathlib.Path
objects:
from pathlib import Path
import kalasiris as isis
from_path = Path('some.cub')
isis.catlab(from_path, to=from_path.with_suffix('.pvl'))
Possibilities abound.
Each of the 300+ ISIS programs that you can use on the command line can be called this way from within Python.
Most ISIS programs have a FROM=
parameter, and so all kalasiris
versions of them will assume that the first item in the argument
signature is what should be assigned to the FROM=
parameter if
you were typing the ISIS program at the command line, that’s one
of the reasons why the above examples aren’t written like isis.catlab(from=from_cub,
to=pvl_file)
but they could be. If you do this, your syntax-aware
editor might highlight or complain that from
is a reserved word
in Python, and you think maybe it shouldn’t be used as a named
argument like this. Your editor might be concerned, but Python
isn’t. However, if you don’t like the look of this, you can append
an underbar to any parameter, like this:
isis.catlab(from_=from_cub, to=pvl_file)
Or even like this:
isis.catlab(from_=from_cub, to_=pvl_file)
There are some other reserved words like min
, that your editor
might also not like, so if you don’t want to have a min=something
in your Python, you can do either of these:
isis.hist('some.cub', min_=5)
isis.hist('some.cub', minimum=5)
Trailing underbars can be handy. In addition to the parameters
that each ISIS program has (like FROM=
, TO=
, etc.), ISIS
programs can also take what ISIS calls ‘reserved parameters’ which
are things like -restore=file
or -log
. In order to use those
kinds of parameters from kalasiris, either add them with their leading
dash or with two underbars (_
) as string parameters, the following two lines are
identical:
isis.hist('some.cub', "gui__", min_=5)
isis.hist('some.cub', "-gui", min_=5)
Which, when called in your Python program, would actually fire up
the GUI window for ISIS hist, with a default value for MINIMUM
set to 5, where you could fiddle with controls, hit the run button,
and when you closed the window, your Python program would start
right back up where it left off.
The reserved parmeters that take an argument must use the form with trailing underbars since those are passed as keys with Python variable names, like so:
isis.spiceinit('some.cub', restore__=Path("to/some/file"))
If you are a user of preference files with ISIS, you might find yourself constantly passing the pref__ parameter to all of your kalasiris calls in a program file. As a convenience, we provide kalasiris.set_persistent_preferences() so that you can set this once, and the “-pref” argument with that path will be added to every kalasiris call you make.
Logging¶
The kalasiris library uses the Logging Facility API provided by the Python standard library. When you run any of the ISIS “functions” with kalasiris, what would be entered on the command line (the ISIS program name, the arguments, etc.) are logged with a log level of INFO.
So if you wanted to have your program write out information about the ISIS programs that are being called, you just need to set up a basic logger in your program like so:
import logging
import kalasiris as isis
logging.basicConfig(level=logging.INFO)
isis.spiceinit("my.cub")
Then on stderr, you’d get this message:
spiceinit from=my.cub
Without importing the logging module and calling the basicConfig method, nothing would be printed to stderr, but my.cub would still be spiceinit’ed. This logging functionality is meant to stay out of your way when you don’t want it, but easier to use than having to format this yourself every time you want to see what’s going on.
Maybe by default, you’d set your logger to only log at logging.WARNING, but if someone gave your program the -v flag or something, you’d set it to logging.INFO so they could see everything.
kalasiris as wrapper¶
We mention this from time to time, but what does it mean? Well,
it means that whenever you call one of the ‘ISIS’ functions with
the kalasiris library, it basically just gathers the inputs, does
some stuff to build the right ‘command line’ and then passes that
to a call of Python’s subprocess.run()
function which takes care
of actually running the ISIS program. Of course, what this means
is that you can also give a kalasiris ISIS program bad inputs, just
like you can on the command line:
isis.spiceinit('some.cub', jesse='Awesome!')
which subprocess.call()
would dutifully run spiceinit
with.
Doing so would be the equivalent to typing this on the command line:
(isis) % spiceinit fr= some_file.cub jesse=awesome
**USER ERROR** Invalid command line.
**USER ERROR** Unknown parameter [jesse].
If you tried to do that in your Python, calling the above function
would throw a subprocess.CalledProcessError
(because that’s what
subprocess.run()
throws when something goes wrong). And you
can either be prepared for that with a try-block, or the exception
will bubble up and halt your program, and you’ll get errors that
you’ll have to deal with.
If you have a program that is using a lot of kalasiris calls, you might want to consider running them wrapped in a try-block that looks like this:
try:
# various kalasiris calls or calls to functions which
# use kalasiris.
except subprocess.CalledProcessError as err:
print('Had an ISIS error:')
print(' '.join(err.cmd))
print(err.stdout)
print(err.stderr)
raise err
If you don’t catch the subprocess.CalledProcessError
like this
and print out all of its elements, you won’t have good visibility
into the problem that ISIS had. You’ll see the error Python had
(“this subprocess failed”) but not the error ISIS had (“this ISIS
program failed in this way”). The other advantage is that this
also prints out the actual command that was given to ISIS, so you
can copy this from the printed error message and paste it to your
own command line to run directly, which can help diagnose the
problem.
Finally, the Python subprocess.run() command also has arguments that you might want to take advantage of, and you can do so by passing arguments to your kalasiris ISIS function with “leading” underbars, like so:
import kalasiris as isis
working_dir = Path("to/some/other/directory")
edr = Path("/some/hirise.IMG")
cube = Path("/output/hirise.cub")
isis.hi2isis(edr, to=cube, _cwd=working_dir)
In this case, the first argument is used as ‘FROM=’ for ISIS, and the cube
variable is the ‘TO=” parameter for hi2isis, but the _cwd
is stripped out
and its value (working_dir
) is given to the subprocess.run()
as the cwd
argument. This means that subprocess.run()
will change to
that directory and run there, (so you’ll probably end up with a print.prt
file there. That may not seem very important, and it is unlikely you will
need this often, but if you are trying to run some ISIS programs in parallel,
and they all need to write to the same file, then being able to create a
set of different working directories and having them each rooted in their own,
so they don’t clash, can be helpful. Surely, there are lots of other handy
uses for arguments to subprocess.run()
, and you have access to all of them.
What do kalasiris ISIS functions return?¶
Since the ISIS functions that kalasiris provides are wrappers around
a call to subprocess.run()
, every kalasiris ISIS function returns
a subprocess.CompletedProcess
Python Standard Library object.
Most of the time, you’re either going to want to ignore it, or get
at the output of the ISIS program, like so:
import kalasiris as isis
completed = isis.getkey('some.cub',
grp='Dimensions',
keyword='Samples')
value = completed.stdout
print(value)
# prints '512' or whatever the string
# is that's returned from getkey
# you could also do it in one go:
print(isis.getkey('some.cub',
grp='Dimensions',
keyword='Samples').stdout)
Of course, a subprocess.CompletedProcess
object has other
methods and attributes that you can use, if you need to.
ISIS Interaction¶
When you import kalasiris, it looks for the ISISROOT
and
ISISDATA
(also accepts ISIS3DATA
) environment variables,
so that it knows where to find those programs and resources on your system.
In the post ISIS 3.6.0 era, ISIS is installed via conda. So you have a base environment, and perhaps an isis environment.
You can probably install kalasiris in the isis environment via any method of your choice, and then things will run as expected.
The trick is when you want to write a Python program that needs a Python library that the isis conda environment doesn’t support.
For example, you may want to write a Python program that uses kalasiris and also the GDAL library, so you might do this:
% conda activate isis
(isis) % conda install gdal
Collecting package metadata: done
...
The following packages will be REMOVED:
isis3-3.6.0-py36_5
...
Whoa! What? The isis conda distribution needs to peg some dependencies, so if you want to install GDAL, it needs to uninstall isis (detailed in this ISIS issue).
So the solution is to install GDAL (or whatever library you wanted that caused this collision) in some other conda environment with kalasiris, and run your Python there. If you do that, you need a way to tell kalasiris where the ISIS programs and data are.
Let’s assume that you installed isis, such that when you are in your isis environment, these are the values of the ISIS environment variables:
ISISROOT=$HOME/anaconda3/envs/isis
ISISDATA=$HOME/anaconda3/envs/isis/data
Where $HOME
is your home directory.
You have at least three options:
- Use conda stacking:
First
conda activate isis
and thenconda activate --stack other-env
which enables these enviroments like nested dolls, so that you’ll end up in a situation with the ISIS environment variables set correctly for kalasiris to find, and your other-env with kalasiris and whatever else you need.
- Set it in your environment manually:
When you activate your other conda environment (the one with GDAL–or whatever–and kalasiris), just set those same variables in your environment, and kalasiris will see them when you import it in your Python code (even without having to run any kind of ISIS setup, just set the environment variables, but you could run the whole ISIS setup if you wanted to, or get fancy and install activate.d and deactivate.d scripts in your other environment).
- Set it in your Python program:
You can add those paths to
os.environ
manually before you import kalasiris, like so (your argument toos.path.join
may vary depending on where your isis conda environment is):import os my_isisroot = os.path.join(os.eviron['HOME'], 'anaconda3','envs','isis') os.environ['ISISROOT'] = my_isisroot os.environ['ISISDATA'] = os.path.join(my_isisroot, 'data') import kalasiris
Those environment variables were only set internally to the Python runtime, not your actual shell, so they aren’t there when the program exits.
Other possibilities certainly exist, but these allow you to write Python programs using kalasiris and run them from a conda environment (or anywhere) that isn’t the isis conda environment.