Ticket #22 (new enhancement)

Opened 3 years ago

various improvements to parameter lists

Reported by: perry Assigned to: somebody
Priority: normal Milestone:
Component: none Version: 1.1.2
Severity: normal Keywords:
Cc:

Description

Hi Phil,

On Mon, 12 Apr 2004, Phil Hodge wrote:

Why is '**kw' listed as an argument for setVerbose()? No keyword argument is used, as far as I can tell, and it makes it possible to make this simple mistake without getting any error or warning:

--> setVerbose (level=2)

It's there so you can invoke setVerbose from the command line using the simple task syntax, e.g.

---> setVerbose 2

There are various keyword parameters that get passed to tasks. I don't at the moment recall them all, but I think they include $nargs, maybe the current iraf package, the redirection keywords Stdin/Stdout/Stderr, _save (discussed below), and others? To make a Python function usable as a task, it has to accept all these keywords. But just accepting **kw without check for erroneous values is indeed very lazy of me.

Probably the right approach here would be to make setVerbose a hidden function and to wrap it as an IrafPythonTask?. That would provide the proper task interface and would also check for bad parameters. It would also check the level parameter for correct values. I think Warren knows how to do the wrapping (I've almost forgotten myself.)

There are other similar task-like functions that also ought to be turned into IrafPythonTasks?. That's been on my to-do list for a really long time... They may not all be as lazy as setVerbose, but it would reduce duplicated code to pull out all the stuff the creates task behavior from individual functions.

In the getParam() method of IrafTask?, the default for prompt is true, but it only prompts when the parameter is in the par file for the task, e.g.:

# this prompts: print iraf.fxheader.getParam("fits_file")

# this doesn't prompt: print iraf.images.getParam("fxheader.fits_file")

Hmm, seems like the prompt parameter is not getting passed on when the parameter is nested. Do you know how the CL handles this? I guess this is bug if the CL prompts in this case.

# exact=1, prompt=1:

--> print iraf.images.getParam("tlcol.t", exact=1, prompt=1) l61hm1a30_asn.fits[1]

Hmm, so here would you expect the exact flag to be propagated as well? I guess we get to choose the behavior we want since the CL has no equivalent of the exact flag.

--> print iraf.tlcol.getParam("t", exact=1, prompt=1) Traceback (innermost last):

File "<console>", line 1, in ?

IrafError?: Unknown parameter requested: t

--> print iraf.tlcol.getParam("t", exact=0, prompt=1) name of table ('l61hm1a30_asn.fits[1]'): l61hm1a30_asn.fits[1]

These are both correct, right?

setParam() updates the running par list (if it exists), but the copy in the uparm directory does not always get updated. In general, I find the various par lists rather confusing; I don't know for sure what par list in PyRAF corresponds to what in IRAF. Default I understand (probably). I thought the running par list was the one in memory that was held until after a task ran successfully, and the current par list was the uparm file. But there are times when the uparm par file is not updated. Here's an example where uparm$imslistaa.par (and, apparently, at least one of the various par lists) was not updated:

# uparm par file was not updated: --> iraf.listarea ("x.fits[1][371:375,290:281]") Image: x.fits[1][371:375,290:281]

...

--> lpar listarea

input = l61h0000r_03185154012_flt_a.fits[1][8203:8207,519:516] > input image(s)

(pformat = f7.1) > format for printing values

(pagewidth = 132) > page width if output is redirected

(mode = al)

# uparm par file was updated as expected: --> listarea x.fits[1][371:375,290:281] Image: x.fits[1][371:375,290:281]

...

--> lpar listarea

input = x.fits[1][371:375,290:281] > input image(s)

(pformat = f7.1) > format for printing values

(pagewidth = 132) > page width if output is redirected

(mode = al)

This one at least I understand and think is correct! There is another one of those special task keyword parameters, _save, which specifies whether the parameters are to be saved in the uparm file or not. It is off by default because when CL tasks are called by other tasks the uparm file is *not* updated. The command-line interpreter automatically adds _save=1 to every task call. If you change the first call to:

--> iraf.listarea ("x.fits[1][371:375,290:281]", _save=1)

you will find that the uparm file gets updated as expected. Remember that you can run in verbose CL compatibility mode to see the Python equivalent of any CL command:

--> clCompatibilityMode 1 # I always type cl<tab> to figure out the name Entering CL-compatibility (verbose) mode... cl>imhead dev$pix dev$pix[512,512][short]: m51 B 600s ----- Python ----- iraf.imhead('dev$pix', _save=1) ------------------

. and there is the magic _save parameter.

Your description of the various parameter lists is basically correct.

_defaultParList Default parameter values (what you get when you do

unlearn)

_runningParList Parameter values being used for actively running task.

When the task completes this gets copied to _currentParList and saved to uparm if _save is true. Otherwise it is discarded.

_currentParList Current values (the defaults that you get when you don't

specify a value for a parameter.)

The _currentParList is generally what is found in the uparm file, although it is possible to make it disagree with the uparm values. If you set a parameter explicitly and then exit without running the task, the uparm file is not changed:

--> iraf.imhead.long = yes --> lpar imhead

images = dev$pix image names

(imlist = *.imh,*.fits,*.pl,*.qp,*.hhh) default image names

(longheader = yes) print header in multi-line format (userfields = yes) print the user fields (instrument parameters)

(mode = al)

--> .exit

% pyraf --> lpar imhead

images = dev$pix image names

(imlist = *.imh,*.fits,*.pl,*.qp,*.hhh) default image names

(longheader = no) print header in multi-line format

(userfields = yes) print the user fields (instrument parameters)

(mode = al)

You do have to work pretty hard to make this uparm mismatch a problem. If you actually run the task from the command line, things get into sync again. But if you run it from inside another CL script, it uses the _currentParList value but does not update uparm. I think that the CL's behavior in this case is actual very similar if not identical.

I would agree this set of multiple par lists is a bit confusing, but I think this is the minimally complicated way to replicate the CL's behavior.

Cheers, Rick

Hi Phil,

On Mon, 12 Apr 2004, Phil Hodge wrote:

--> setVerbose (level=2)

It's there so you can invoke setVerbose from the command line using the simple task syntax, e.g.:

---> setVerbose 2

This uses a keyword argument? I thought it would use the value argument. The signature is 'def setVerbose(value=1, **kw)'.

No, this gets translated to:

iraf.setVerbose(2, _save=1)

So the **kw argument is needed not for the value argument but for the _save keyword in this case. Similarly, here is another translation:

---> setVerbose 2 > dev$null

becomes:

iraf.setVerbose(2, _save=1, Stdout='dev$null')

# this doesn't prompt:

print iraf.images.getParam("fxheader.fits_file")

Hmm, seems like the prompt parameter is not getting passed on when the parameter is nested. Do you know how the CL handles this? I guess this is bug if the CL prompts in this case.

The cl doesn't have this option. I asked for a parameter from fxheader, using getParam() for an instance of another task. (That was a typo of mine, though. I meant to specify a task such as imcopy, but I used the images package name instead.) It's a curious feature to be able to get a parameter from task x via task y. I expected to be prompted, since prompt=1, but if that's not the way you want it to work, we can just say so in the write-up.

I agree that's pretty weird, but I think it must be there because each task needs a way to get parameter values either from it's own parameter list or from other tasks. I personally think that it ought to pass the 'exact' and 'prompt' keywords on to the task in question.

--> print iraf.images.getParam("tlcol.t", exact=1, prompt=1) l61hm1a30_asn.fits[1]

Hmm, so here would you expect the exact flag to be propagated as well? I guess we get to choose the behavior we want since the CL has no equivalent of the exact flag.

It was just a surprise, since I had specified exact=1. I don't understand why you have this argument and then ignore it.

Well, I don't ignore it for accesses to parameters of this task. But I do consider it a bug not to pass it on.

This one at least I understand and think is correct! There is another one of those special task keyword parameters, _save, which specifies whether the parameters are to be saved in the uparm file or not. It is off by default because when CL tasks are called by other tasks the uparm file is *not* updated. The command-line interpreter automatically adds _save=1 to every task call. If you change the first call to: ...

I recall seeing the _save keyword parameter for the run() method, but I didn't realize that run() was invoked directly when a task was run using Python syntax. (more below)

You do have to work pretty hard to make this uparm mismatch a problem. If you actually run the task from the command line, things get into sync again.

If I use setParam() to set a parameter in a pset (e.g. pltpar for sgraph), the new value shows up when I 'lpar pltpar', and it is used correctly when sgraph is run, but uparm$sttpltpar.par still shows the old value.

That's correct.

But if you run it from inside another CL script, it uses the _currentParList value but does not update uparm. I think that the CL's behavior in this case is actual very similar if not identical.

I don't think this is the CL behavior. (but I'd better check) The trick that the CL pulls on us is that when a task is run as a background job, any statement that would ordinarily change a task parameter does not affect the value in the uparm directory. It isn't being in a script that counts; it's the fact that it's in the background. The reason is to allow multiple background jobs without collisions, in case one job calls a task without specifying all the parameters. That's a good reason, but it doesn't solve the problem (one can have multiple CL sessions just by using different windows), and it results in obscure and silent failures in some tasks. For example, if a task runs imgets and then reads the value from the 'value' parameter, this works fine when run interactively or from a CL script, but if the script is run in the background the step to read the value from 'value' simply gets whatever happens to be in the par file at that time and may have nothing to do with what was in the image header. I consider this a serious bug in the CL, and I'm not happy to hear that you are trying to emulate it in PyRAF.

But being in a script does matter too. E.g. if in a CL script you call imhead('myfile.fits'), then do 'lpar imhead' you will find that the images parameter for imhead has not been changed. No parameters are learned when tasks are called from CL scripts. The only learning is at the command line. (Maybe there are some settings of the mode parameter that change this, I'm not certain about that.)

It is very hard not to emulate this general behavior in PyRAF. I think loads of scripts would break if we start learning all the parameters. On the other hand, maybe it would be possible to have some sort of behavior where at least the task parameters in memory are allowed to be changed even if the uparm files do not. This would be a big change and would require a lot of thought and testing, I think.

Rick