Package pyraf :: Module iraftask
[hide private]
[frames] | no frames]

Source Code for Module pyraf.iraftask

   1  """module iraftask.py -- defines IrafTask and IrafPkg classes 
   2   
   3  $Id: iraftask.py 1463 2011-06-24 22:58:30Z stsci_embray $ 
   4   
   5  R. White, 2000 June 26 
   6   
   7  iraftask defines the original PyRAF task functionality which pre-dates 
   8  the creation of IRAF ECL.  irafecl is closely related and derived from 
   9  iraftask,  providing drop-in replacements for the Task classes defined 
  10  here which also support ECL syntax like "iferr" and $errno. 
  11  """ 
  12  from __future__ import division 
  13   
  14  import os, sys, copy, re 
  15  from stsci.tools import basicpar, minmatch, irafutils, irafglobals, taskpars 
  16  import subproc, irafinst, iraf, irafpar, irafexecute, cl2py 
  17  import fnmatch 
  18   
  19   
  20  # may be set to function to monitor task execution 
  21  # function gets called for every task execution 
  22  executionMonitor = None 
  23   
  24  # ----------------------------------------------------- 
  25  # IRAF task class 
  26  # ----------------------------------------------------- 
  27   
  28  # basic IrafTask attributes 
  29  _IrafTask_attr_dict = { 
  30          '_name': None, 
  31          '_pkgname': None, 
  32          '_pkgbinary': None, 
  33          '_hidden': 0, 
  34          '_hasparfile': 1, 
  35          '_tbflag': 0, 
  36          # full path names and parameter list get filled in on demand 
  37          '_fullpath': None, 
  38          # parameters have a current set of values and a default set 
  39          '_currentParList': None, 
  40          '_defaultParList': None, 
  41          '_runningParList': None, 
  42          '_currentParpath': None, 
  43          '_defaultParpath': None, 
  44          '_scrunchParpath': None, 
  45          '_parDictList': None, 
  46          '_foreign': 0, 
  47          } 
  48   
  49   
  50  # This is a list of all the IrafTask objects that have been created. 
  51  # There are variables in pyraf.iraffunctions that superficially 
  52  # resemble this, but none of them contain the complete list, so I 
  53  # keep it here.  This is currently used only for the "taskinfo" 
  54  # operation.   
  55  # 
  56  # We generally approach this list interactively and with a wildcards. 
  57  # I don't see any particularly useful indexing, so it is just a plain 
  58  # linear search. 
  59  # 
  60  all_task_definitions = [ ] 
  61   
  62   
  63  # use empty "tag" class from irafglobals as base class 
  64   
65 -class IrafTask(irafglobals.IrafTask, taskpars.TaskPars):
66 67 """IRAF task class""" 68 69 # We remember parameters to the __init__ function 70 # 71 # prefix 72 # ? - see obj._saved_prefix 73 # 74 # name 75 # the name of the task. We don't actually save this, but 76 # obj._name contains the name as we may have modified it. 77 # 78 # suffix 79 # ? - see obj._saved_suffix 80 # 81 # filename 82 # ? - obviously more than just a file name. obj._filename 83 # is the modified value 84 # 85 # pkgname 86 # the name of the package that this task is in. see obj._pkgname 87 # 88 # pkgbinary 89 # places that the binary may be. see obj._pkgbinary 90 # 91 92
93 - def __init__(self, prefix, name, suffix, filename, pkgname, pkgbinary):
94 95 # for this heavily used code, pull out the dictionary and 96 # initialize it directly to avoid calls to __setattr__ 97 # 98 # b.t.w. do not try to set the attributes directly - it doesn't 99 # work. (not sure if that is a bug or a "feature") 100 objdict = self.__dict__ 101 102 # remember the task definition in case we want to see it later 103 all_task_definitions.append( self ) 104 105 objdict['_saved_suffix'] = suffix 106 objdict['_saved_prefix'] = prefix 107 108 # stuff all the parameters into the object 109 objdict.update(_IrafTask_attr_dict) 110 sname = name.replace('.', '_') 111 if sname != name: 112 print "Warning: '.' illegal in task name, changing", name, \ 113 "to", sname 114 spkgname = pkgname.replace('.', '_') 115 if spkgname != pkgname: 116 print "Warning: '.' illegal in pkgname, changing", pkgname, \ 117 "to", spkgname 118 objdict['_name'] = sname 119 objdict['_pkgname'] = spkgname 120 objdict['_pkgbinary'] = [] 121 self.addPkgbinary(pkgbinary) 122 # tasks with names starting with '_' are implicitly hidden 123 if name[0:1] == '_': objdict['_hidden'] = 1 124 if prefix == '$': objdict['_hasparfile'] = 0 125 if suffix == '.tb': objdict['_tbflag'] = 1 126 if filename and filename[0] == '$': 127 # this is a foreign task 128 objdict['_foreign'] = 1 129 objdict['_filename'] = filename[1:] 130 # handle weird syntax for names 131 if self._filename == 'foreign': 132 objdict['_filename'] = name 133 elif self._filename[:8] == 'foreign ': 134 objdict['_filename'] = name + self._filename[7:] 135 elif filename[:2] == '$0': 136 objdict['_filename'] = name + filename[2:] 137 else: 138 objdict['_filename'] = filename
139
140 - def initTask(self, force=0):
141 """Fill in full pathnames of files and read parameter file(s) 142 143 Force indicates whether shortcut initialization can be used 144 or not. (No difference for base IrafTask.) 145 """ 146 if self._filename and not self._fullpath: 147 if irafinst.EXISTS: 148 self._initFullpath() # allow to throw on error 149 else: # be more accommodating 150 try: 151 self._initFullpath() 152 except iraf.IrafError: 153 self._initNoIrafTask() 154 if self._currentParList is None: 155 self._initParpath() 156 self._initParList()
157 158
159 - def _initNoIrafTask(self):
160 """ Special-case handle the initialization that is going awry due 161 to a missing IRAF installation. """ 162 # Handle non-IRAF installs - could not find the file anywhere 163 # Handle .par files differently from other types 164 orig = self._filename 165 base = os.path.basename(self._filename) 166 base = base[1+base.rfind('$'):] 167 if base and base.endswith('.par'): 168 self._filename = irafinst.tmpParFile(base) 169 else: 170 self._filename = irafinst.NO_IRAF_PFX+base # to be built on the fly 171 if iraf.Verbose>1: 172 print 'Task "'+self._name+'" needed "'+orig+'" got: '+self._filename
173 174 175 #========================================================= 176 # public accessor methods for attributes 177 #========================================================= 178 179 #--------------------------------------------------------- 180 # first set returns current values (which may be None if 181 # initTask has not been executed yet) 182 #--------------------------------------------------------- 183
184 - def getName(self): return self._name
185 - def getPkgname(self): return self._pkgname
186 - def getPkgbinary(self): return self._pkgbinary
187 - def isHidden(self): return self._hidden
188 - def hasParfile(self): return self._hasparfile
189 - def getTbflag(self): return self._tbflag
190 - def getForeign(self): return self._foreign
191 - def getFilename(self): return self._filename
192 193 #--------------------------------------------------------- 194 # second set initializes task variables (which were deferred to 195 # speed up initial instance creation) 196 #--------------------------------------------------------- 197
198 - def getFullpath(self):
199 """Return full path name of executable""" 200 self.initTask() 201 return self._fullpath
202
203 - def getParpath(self):
204 """Return full path name of parameter file""" 205 self.initTask() 206 return self._currentParpath
207
208 - def getParList(self, docopy=0):
209 """Return list of all parameter objects""" 210 self.initTask(force=1) 211 plist = self._runningParList or self._currentParList 212 if plist: 213 return plist.getParList(docopy=docopy) 214 else: 215 return []
216
217 - def getDefaultParList(self):
218 """Return default list of all parameter objects""" 219 self.initTask(force=1) 220 plist = self._defaultParList 221 if plist: 222 return plist.getParList() 223 else: 224 return []
225
226 - def getParDict(self):
227 """Return (min-match) dictionary of all parameter objects""" 228 self.initTask(force=1) 229 plist = self._runningParList or self._currentParList 230 if plist: 231 return plist.getParDict() 232 else: 233 return minmatch.MinMatchDict()
234
235 - def getParObject(self,paramname,exact=0,alldict=0):
236 """Get the IrafPar object for a parameter 237 238 If exact is set, param name must match exactly. 239 If alldict is set, look in all dictionaries (default is 240 just this task's dictionaries.) 241 """ 242 self.initTask() 243 244 # search the standard dictionaries for the parameter 245 # most of the time it will be in the active task dictionary 246 try: 247 paramdict = self.getParDict() 248 if paramdict.has_key(paramname,exact=exact): 249 return paramdict[paramname] 250 except minmatch.AmbiguousKeyError, e: 251 # re-raise the error with a bit more info 252 raise iraf.IrafError("Cannot get parameter `%s'\n%s" % 253 (paramname, str(e))) 254 255 if alldict: 256 # OK, the easy case didn't work -- now initialize the 257 # complete parDictList (if necessary) and search them all 258 259 if self._parDictList is None: self._setParDictList() 260 for dictname, paramdict in self._parDictList: 261 if paramdict.has_key(paramname,exact=exact): 262 return paramdict[paramname] 263 264 raise iraf.IrafError("Unknown parameter requested: " + paramname)
265
266 - def getAllMatches(self,param):
267 """Return list of names of all parameters that may match param""" 268 self.initTask(force=1) 269 plist = self._runningParList or self._currentParList 270 if plist: 271 return plist.getAllMatches(param) 272 else: 273 return []
274 275 #--------------------------------------------------------- 276 # modify and test attributes 277 #--------------------------------------------------------- 278
279 - def addPkgbinary(self, pkgbinary):
280 """Add another entry in list of possible package binary locations 281 282 Parameter can be a string or a list of strings""" 283 284 if not pkgbinary: 285 return 286 elif isinstance(pkgbinary,str): 287 if pkgbinary and (pkgbinary not in self._pkgbinary): 288 self._pkgbinary.append(pkgbinary) 289 else: 290 for pbin in pkgbinary: 291 if pbin and (pbin not in self._pkgbinary): 292 self._pkgbinary.append(pbin)
293 294
295 - def setHidden(self,value=1):
296 """set hidden attribute, which can be specified in 297 a separate 'hide' statement 298 """ 299 self._hidden = value
300
301 - def isConsistent(self, other):
302 """Returns true if this task is consistent with another task object""" 303 return self.__class__ == other.__class__ and \ 304 self.getFilename() == other.getFilename() and \ 305 self.hasParfile() == other.hasParfile() and \ 306 self.getForeign() == other.getForeign() and \ 307 self.getTbflag() == other.getTbflag()
308 309 #--------------------------------------------------------- 310 # run the task 311 #--------------------------------------------------------- 312
313 - def run(self,*args,**kw):
314 """Execute this task with the specified arguments""" 315 316 self.initTask(force=1) 317 318 # Special _save keyword turns on parameter-saving. 319 # Default is *not* to save parameters (so it is necessary 320 # to use _save=1 to get parameter changes to be persistent.) 321 if kw.has_key('_save'): 322 save = kw['_save'] 323 del kw['_save'] 324 else: 325 save = 0 326 327 # Handle other special keywords 328 specialKW = self._specialKW(kw) 329 330 # Special Stdout, Stdin, Stderr keywords are used to redirect IO 331 redirKW, closeFHList = iraf.redirProcess(kw) 332 333 # set parameters 334 kw['_setMode'] = 1 335 apply(self.setParList,args,kw) 336 337 if iraf.Verbose>1: 338 print "run %s (%s: %s)" % (self._name, 339 self.__class__.__name__, self._fullpath) 340 if self._runningParList: 341 self._runningParList.lParam() 342 343 # delete list of param dictionaries so it will be 344 # recreated in up-to-date version if needed 345 self._parDictList = None 346 # apply IO redirection 347 resetList = self._applyRedir(redirKW) 348 try: 349 # Hook for execution monitor 350 if executionMonitor: 351 executionMonitor(self) 352 self._run(redirKW, specialKW) 353 self._updateParList(save) 354 if iraf.Verbose>1: print 'Successful task termination' 355 finally: 356 rv = self._resetRedir(resetList, closeFHList) 357 self._deleteRunningParList() 358 if self._parDictList: 359 self._parDictList[0] = (self._name, self.getParDict()) 360 if executionMonitor: 361 executionMonitor() 362 return rv
363
364 - def getMode(self, parList=None):
365 """Returns mode string for this task 366 367 Searches up the task, package, cl hierarchy for automatic modes 368 """ 369 if parList is not None: 370 mode = parList.getValue('mode',prompt=0) 371 else: 372 pdict = self.getParDict() 373 if pdict: 374 mode = pdict['mode'].get(prompt=0) 375 else: 376 mode = "a" 377 if mode[:1] != "a": return mode 378 379 # cl is the court of last resort, don't look at its packages 380 if self is iraf.cl: return "h" 381 382 # package name is undefined only at very start of initialization 383 # just use the standard default 384 if not self._pkgname: return "ql" 385 386 # up we go -- look in parent package 387 pkg = iraf.getPkg(self._pkgname) 388 # clpackage is at top and is its own parent 389 if pkg is not self: 390 return pkg.getMode() 391 # didn't find it in the package hierarchy, so use cl mode 392 mode = iraf.cl.mode 393 # default is hidden if automatic all the way to top 394 if mode[:1] == "a": 395 return "h" 396 else: 397 return mode
398
399 - def setParList(self,*args,**kw):
400 """Set arguments to task in _runningParList copy of par list 401 402 Creates a copy of the task parameter list and sets the 403 parameters. It is up to subsequent code (in the run method) 404 to propagate these changes to the persistent parameter list. 405 406 Special arguments: 407 _setMode=1 to set modes of automatic parameters 408 ParList can be used to pass in an entire parameter list object 409 """ 410 self.initTask(force=1) 411 412 if not self._currentParList: 413 return None 414 415 # Special ParList parameter is used to pass in an entire 416 # parameter list 417 if kw.has_key('ParList'): 418 parList = kw['ParList'] 419 del kw['ParList'] 420 if isinstance(parList, str): 421 # must be a .par filename 422 filename = parList 423 parList = irafpar.IrafParList(self.getName(), filename) 424 elif parList and not isinstance(parList, irafpar.IrafParList): 425 raise TypeError("ParList parameter must be a filename or " 426 "an IrafParList object") 427 else: 428 parList = None 429 430 if self._runningParList is not None: 431 # only one runningParList at a time -- all tasks use it 432 newParList = self._runningParList 433 parList = None 434 else: 435 newParList = copy.deepcopy(parList or self._currentParList) 436 437 if kw.has_key('_setMode'): 438 _setMode = kw['_setMode'] 439 del kw['_setMode'] 440 else: 441 _setMode = 0 442 # create parlist copies for pset tasks too 443 for p in newParList.getParList(): 444 if isinstance(p, irafpar.IrafParPset): 445 p.get().setParList() 446 # set the parameters 447 apply(newParList.setParList, args, kw) 448 if _setMode: 449 # set mode of automatic parameters 450 mode = self.getMode(newParList) 451 for p in newParList.getParList(): 452 p.mode = p.mode.replace("a",mode) 453 if parList: 454 #XXX Set all command-line flags for parameters when a 455 #XXX parlist is supplied so that it does not prompt for 456 #XXX missing parameters. Is this the preferred behavior? 457 newParList.setAllFlags() 458 459 self._runningParList = newParList
460 461 #--------------------------------------------------------- 462 # task parameter access 463 #--------------------------------------------------------- 464
465 - def setParam(self, qualifiedName, newvalue, check=1, exact=0, scope='', 466 idxHint=None):
467 """Set parameter specified by qualifiedName to newvalue. 468 469 qualifiedName can be a simple parameter name or can be 470 [[package.]task.]paramname[.field]. 471 If check is set to zero, does not check value to make sure it 472 satisfies min-max range or choice list. scope, idxHint are ignored. 473 """ 474 475 package, task, paramname, pindex, field = _splitName(qualifiedName) 476 477 # special syntax for package parameters 478 if task == "_": task = self._pkgname 479 480 if task or package: 481 if not package: 482 # maybe this task is the name of one of the dictionaries? 483 if self._parDictList is None: self._setParDictList() 484 for dictname, paramdict in self._parDictList: 485 if dictname == task: 486 if paramdict.has_key(paramname): 487 paramdict[paramname].set(newvalue,index=pindex, 488 field=field,check=check) 489 return 490 else: 491 raise iraf.IrafError("Attempt to set unknown parameter " + 492 qualifiedName+' for task '+task) 493 # Not one of our dictionaries, so must find the relevant task 494 if package: task = package + '.' + task 495 try: 496 tobj = iraf.getTask(task) 497 # reattach the index and/or field 498 if pindex: paramname = paramname + '[' + `pindex+1` + ']' 499 if field: paramname = paramname + '.' + field 500 tobj.setParam(paramname,newvalue,check=check) 501 return 502 except KeyError: 503 raise iraf.IrafError("Could not find task " + task + 504 " to get parameter " + qualifiedName) 505 except iraf.IrafError, e: 506 raise iraf.IrafError(str(e) + "\nFailed to set parameter " + 507 qualifiedName) 508 509 # no task specified, just search the standard dictionaries 510 # most of the time it will be in the active task dictionary 511 512 paramdict = self.getParDict() 513 if paramdict.has_key(paramname,exact=exact): 514 paramdict[paramname].set(newvalue,index=pindex, 515 field=field,check=check) 516 return 517 518 # OK, the easy case didn't work -- now initialize the 519 # complete parDictList (if necessary) and search them all 520 521 if self._parDictList is None: self._setParDictList() 522 for dictname, paramdict in self._parDictList: 523 if paramdict.has_key(paramname,exact=exact): 524 paramdict[paramname].set(newvalue,index=pindex, 525 field=field,check=check) 526 return 527 else: 528 raise iraf.IrafError("Attempt to set unknown lone parameter " + 529 qualifiedName)
530
531 - def getParam(self,qualifiedName,native=1,mode=None,exact=0,prompt=1):
532 """Return parameter specified by qualifiedName. 533 534 qualifiedName can be a simple parameter name or can be 535 [[package.]task.]paramname[.field]. 536 Paramname can also have an optional subscript, "param[1]". 537 If native is non-zero (default), returns native format (e.g. float 538 for floating point parameter.), otherwise returns string value. 539 If exact is set, parameter name must match exactly. Default 540 is to do minimum match. 541 If prompt is 0, does not prompt for parameter value (even if 542 parameter is undefined.) 543 """ 544 545 package, task, paramname, pindex, field = _splitName(qualifiedName) 546 547 if (not task) or (task == self._name): 548 # no task specified, just search the standard dictionaries 549 return self._getParValue(paramname, pindex, field, native, mode, 550 exact=exact, prompt=prompt) 551 552 # when task is specified, ignore exact flag -- always do minmatch 553 554 # special syntax for package parameters 555 if task == "_": task = self._pkgname 556 557 if not package: 558 # maybe this task is the name of one of the dictionaries? 559 if self._parDictList is None: self._setParDictList() 560 for dictname, paramdict in self._parDictList: 561 if dictname == task: 562 if paramdict.has_key(paramname): 563 return self._getParFromDict(paramdict, paramname, 564 pindex, field, native, mode="h", prompt=prompt) 565 else: 566 raise iraf.IrafError("Unknown parameter requested: " + 567 qualifiedName) 568 569 # Not one of our dictionaries, so must find the relevant task 570 if package: task = package + '.' + task 571 try: 572 tobj = iraf.getTask(task) 573 return tobj._getParValue(paramname, pindex, field, native, mode="h", 574 prompt=prompt) 575 except KeyError: 576 raise iraf.IrafError("Could not find task " + task + 577 " to get parameter " + qualifiedName) 578 except iraf.IrafError, e: 579 raise iraf.IrafError(str(e) + "\nFailed to get parameter " + 580 qualifiedName)
581
582 - def _getParValue(self, paramname, pindex, field, native, mode, exact=0, 583 prompt=1):
584 # search the standard dictionaries for the parameter 585 # most of the time it will be in the active task dictionary 586 paramdict = self.getParDict() 587 try: 588 if paramdict.has_key(paramname,exact=exact): 589 return self._getParFromDict(paramdict, paramname, pindex, 590 field, native, mode=mode, prompt=prompt) 591 except minmatch.AmbiguousKeyError, e: 592 # re-raise the error with a bit more info 593 raise iraf.IrafError("Cannot get parameter `%s'\n%s" % 594 (paramname, str(e))) 595 596 # OK, the easy case didn't work -- now initialize the 597 # complete parDictList (if necessary) and search them all 598 if self._parDictList is None: self._setParDictList() 599 for dictname, paramdict in self._parDictList: 600 if paramdict.has_key(paramname,exact=exact): 601 return self._getParFromDict(paramdict, paramname, pindex, 602 field, native, mode="h", prompt=prompt) 603 else: 604 raise iraf.IrafError('Unknown parameter requested: "'+paramname+ 605 '" for task: "'+self._name+'" in pkg: "'+self._pkgname+'"')
606 607 #--------------------------------------------------------- 608 # task parameter utility methods 609 #--------------------------------------------------------- 610
611 - def lParam(self,verbose=0):
612 """List the task parameters""" 613 self.initTask(force=1) 614 plist = self._runningParList or self._currentParList 615 if plist: 616 plist.lParam(verbose=verbose) 617 else: 618 sys.stderr.write("Task %s has no parameter file\n" % self._name) 619 sys.stderr.flush()
620
621 - def eParam(self):
622 """Edit the task parameters, PyRAF Tk style""" 623 self.initTask(force=1) 624 #XXX always runs on current par list, not running par list? 625 if self._currentParList: 626 import epar 627 epar.epar(self) 628 else: 629 sys.stderr.write("Task %s has no parameter file\n" % self._name) 630 sys.stderr.flush()
631
632 - def tParam(self):
633 """Edit the task parameters, IRAF curses style""" 634 self.initTask(force=1) 635 #XXX always runs on current par list, not running par list? 636 if self._currentParList: 637 import tpar 638 tpar.tpar(self) 639 else: 640 sys.stderr.write("Task %s has no parameter file\n" % self._name) 641 sys.stderr.flush()
642
643 - def dParam(self, cl=1):
644 """Dump the task parameters 645 646 Default is to write CL version of code; if cl parameter is 647 false, writes Python executable code instead. 648 """ 649 self.initTask(force=1) 650 plist = self._runningParList or self._currentParList 651 if plist: 652 if cl: 653 taskname = self._name 654 else: 655 taskname = "iraf.%s" % self._name 656 plist.dParam(taskname, cl=cl) 657 else: 658 sys.stderr.write("Task %s has no parameter file\n" % self._name) 659 sys.stderr.flush()
660
661 - def saveParList(self,filename=None, comment=None):
662 """Write task parameters in .par format to filename (name or handle) 663 664 If filename is omitted, writes to uparm scrunch file (if possible) 665 Returns a string with the results. 666 """ 667 self.initTask() 668 #XXX always runs on current par list, not running par list? 669 if not self._currentParList: 670 return "No parameters to save for task %s" % (self._name,) 671 if filename is None: 672 if self._scrunchParpath: 673 filename = self._scrunchParpath 674 else: 675 status = "Unable to save parameters for task %s" % \ 676 (self._name,) 677 if iraf.Verbose>0: print status 678 return status 679 rv = self._currentParList.saveParList(filename, comment) 680 return rv
681
682 - def unlearn(self):
683 """Reset task parameters to their default values""" 684 self.initTask(force=1) 685 #XXX always runs on current par list, not running par list? 686 if not self._currentParList: 687 return 688 if self._defaultParList is not None: 689 # update defaultParList from file if necessary 690 self._defaultParList.Update() 691 if self._scrunchParpath and \ 692 (self._scrunchParpath == self._currentParpath): 693 try: 694 os.remove(iraf.Expand(self._scrunchParpath, noerror=1)) 695 except OSError: 696 pass 697 self._currentParList = copy.deepcopy(self._defaultParList) 698 self._currentParpath = self._defaultParpath 699 else: 700 raise iraf.IrafError("Cannot find default .par file for task " + 701 self._name)
702
703 - def scrunchName(self):
704 """Return scrunched version of filename (used for uparm files) 705 706 Scrunched version of filename is chars 1,2,last from package 707 name and chars 1-5,last from task name. 708 """ 709 s = self._pkgname[0:2] 710 if len(self._pkgname) > 2: 711 s = s + self._pkgname[-1:] 712 s = s + self._name[0:5] 713 if len(self._name) > 5: 714 s = s + self._name[-1:] 715 return s
716 717 #========================================================= 718 # special methods to give desired object syntax 719 #========================================================= 720 721 # parameters are accessible as attributes 722
723 - def __getattr__(self,name):
724 if name[:1] == '_': 725 raise AttributeError(name) 726 self.initTask() 727 try: 728 return self.getParam(name,native=1) 729 except SyntaxError, e: 730 raise AttributeError(str(e))
731
732 - def __setattr__(self,name,value):
733 # hidden Python parameters go into the standard dictionary 734 # (hope there are none of these in IRAF tasks) 735 if name[:1] == '_': 736 self.__dict__[name] = value 737 elif self.is_pseudo(name): 738 self.__dict__[name] = value 739 else: 740 self.initTask() 741 self.setParam(name,value)
742
743 - def is_pseudo(self, paramname):
744 """Hook enabling ECL pseudos... always returns False""" 745 return False
746 747 # allow running task using taskname() or with 748 # parameters as arguments, including keyword=value form. 749
750 - def __call__(self,*args,**kw):
751 return apply(self.run,args,kw)
752
753 - def __repr__(self):
754 s = '<%s %s (%s) Pkg: %s Bin: %s' % \ 755 (self.__class__.__name__, self._name, self._filename, 756 self._pkgname, ':'.join(self._pkgbinary)) 757 if self._foreign: s = s + ' Foreign' 758 if self._hidden: s = s + ' Hidden' 759 if self._hasparfile == 0: s = s + ' No parfile' 760 if self._tbflag: s = s + ' .tb' 761 return s + '>'
762
763 - def __str__(self):
764 return repr(self)
765 766 #========================================================= 767 # private methods -- may be used by subclasses, but should 768 # not be needed outside this module 769 #========================================================= 770
771 - def _specialKW(self, kw):
772 """Return dictionary of any special keywords (subclass hook)""" 773 return {}
774
775 - def _applyRedir(self, redirKW):
776 """Apply I/O redirection (irafexecute does this for executables) 777 778 Return a list of redirections that need to be restored when done. 779 """ 780 return []
781
782 - def _resetRedir(self, resetList, closeFHList):
783 """Restore redirected I/O and close files""" 784 return iraf.redirReset(resetList, closeFHList)
785
786 - def _run(self, redirKW, specialKW):
787 """Execute task after parameters, I/O redirection are prepared. 788 789 The implementation of this can differ for each type of task. 790 """ 791 try: 792 apply(irafexecute.IrafExecute, 793 (self, iraf.getVarDict()), redirKW) 794 except irafexecute.IrafProcessError, value: 795 raise iraf.IrafError("Error running IRAF task " + self._name + 796 "\n" + str(value))
797
798 - def _updateParList(self, save=0):
799 """Update parameter list after successful task completion 800 801 Updates parameter save file if any parameters change. If save 802 flag is set, all changes are saved; if save flag is false, only 803 explicit parameter changes requested by the task are saved. 804 """ 805 if not (self._currentParList and self._runningParList): 806 return 807 newParList = self._runningParList 808 self._runningParList = None 809 mode = self.getMode(newParList) 810 changed = 0 811 for par in newParList.getParList(): 812 if par.name != "$nargs" and (par.isChanged() or 813 (save and par.isCmdline() and par.isLearned(mode))): 814 changed = 1 815 # get task parameter object 816 tpar = self._currentParList.getParObject(par.name) 817 # set its value -- don't bother with type checks since 818 # the new and old parameters must be identical 819 tpar.value = par.value 820 # propagate other mutable fields too 821 # don't propagate modes since I changed them 822 # (note IRAF does not propagate prompt, which I consider a bug) 823 tpar.min = par.min 824 tpar.max = par.max 825 tpar.choice = par.choice 826 tpar.prompt = par.prompt 827 tpar.setChanged() 828 if isinstance(par, irafpar.IrafParPset): 829 par.get()._updateParList(save) 830 # save to disk if there were changes 831 if changed: 832 rv = self.saveParList() 833 if iraf.Verbose>1: print rv
834
835 - def _deleteRunningParList(self):
836 """Delete the _runningParList parameter list for this and psets""" 837 if self._currentParList and self._runningParList: 838 newParList = self._runningParList 839 self._runningParList = None 840 for par in newParList.getParList(): 841 if isinstance(par, irafpar.IrafParPset): 842 par.get()._deleteRunningParList()
843
844 - def _setParDictList(self):
845 """Set the list of (up to 3) parameter dictionaries for task execution. 846 847 Parameter dictionaries for execution consist of this 848 task's parameters (which includes any psets 849 referenced), all the parameters for the task of the package 850 loaded for the current task, and the cl parameters. Each 851 dictionary has an associated name (because parameters could be 852 asked for as task.parname as well as just parname). 853 854 Create this list anew for each execution in case the 855 list of loaded packages has changed. It is stored as 856 an attribute of this object so it can be accessed by 857 the getParam() and setParam() methods. 858 """ 859 860 # Start with the parameters for the current task 861 self.initTask() 862 parDictList = [(self._name,self.getParDict())] 863 864 # Next, parameters from the package to which the current task belongs 865 # [Ticket 59: mimic behavior of param.c:lookup_param()] 866 pd = iraf.getTask(self.getPkgname()).getParDict() 867 if (pd): # do not include null dictionaries 868 parDictList.append( (self.getPkgname(),pd) ) 869 870 # Lastly, cl parameters 871 cl = iraf.cl 872 if cl is not None: 873 parDictList.append( (cl.getName(),cl.getParDict()) ) 874 875 # Done 876 self._parDictList = parDictList
877 878
879 - def _getParFromDict(self, paramdict, paramname, pindex, field, 880 native, mode, prompt):
881 # helper method for getting parameter value (with indirection) 882 # once we find a dictionary that contains it 883 par = paramdict[paramname] 884 pmode = par.mode[:1] 885 if pmode == "a": 886 pmode = mode or self.getMode() 887 v = par.get(index=pindex,field=field, 888 native=native,mode=pmode,prompt=prompt) 889 if isinstance(v,str) and v[:1] == ")": 890 891 # parameter indirection: call getParam recursively 892 # I'm making the assumption that indirection in a 893 # field (e.g. in the min or max value) is allowed 894 # and that it uses exactly the same syntax as 895 # the argument to getParam, i.e. ')task.param' 896 # refers to the p_value of the parameter, 897 # ')task.param.p_min' refers to the min or 898 # choice string, etc. 899 900 return self.getParam(v[1:],native=native,mode="h",prompt=prompt) 901 else: 902 return v
903
904 - def _initFullpath(self):
905 """Fill in full pathname of executable""" 906 907 # This follows the search strategy used by findexe in 908 # cl/exec.c: first it checks in the BIN directory for the 909 # "installed" version of the executable, and if that is not 910 # found it tries the pathname given in the TASK declaration. 911 # Expand iraf variables. We will try both paths if the expand fails. 912 try: 913 exename1 = iraf.Expand(self._filename) 914 # get name of executable file without path 915 basedir, basename = os.path.split(exename1) 916 except iraf.IrafError, e: 917 if iraf.Verbose>0: 918 print "Error searching for executable for task " + \ 919 self._name 920 print str(e) 921 exename1 = "" 922 # make our best guess that the basename is what follows the 923 # last '$' in _filename 924 basedir = "" 925 s = self._filename.split("$") 926 basename = s[-1] 927 if basename == "": 928 self._fullpath = "" 929 raise iraf.IrafError("No filename in task %s definition: `%s'" 930 % (self._name, self._filename)) 931 # for foreign tasks, just set path to filename (XXX will 932 # want to improve this by checking os path for existence) 933 if self._foreign: 934 self._fullpath = self._filename 935 else: 936 # first look in the task binary directories 937 exelist = [] 938 for pbin in self._pkgbinary: # e.g. ['bin$'] 939 try: 940 exelist.append(iraf.Expand(pbin + basename)) 941 except iraf.IrafError, e: 942 if iraf.Verbose>0: 943 print "Error searching for executable for task " + \ 944 self._name 945 print str(e) 946 for exename2 in exelist: 947 if os.path.exists(exename2): 948 self._fullpath = exename2 949 break 950 else: 951 if os.path.exists(exename1): 952 self._fullpath = exename1 953 else: 954 self._fullpath = "" 955 exelist.append(exename1) 956 raise iraf.IrafError( 957 "Cannot find executable for task %s\nTried %s" % 958 (self._name, ", ".join(exelist)))
959
960 - def _initParpath(self):
961 """Initialize parameter file paths""" 962 963 if not self._filename: 964 # if filename is missing we won't be able to find parameter file 965 # set hasparfile flag to zero if that is OK 966 self._noParFile() 967 self._hasparfile = 0 968 969 if not self._hasparfile: 970 # no parameter file 971 self._defaultParpath = "" 972 self._currentParpath = "" 973 self._scrunchParpath = "" 974 return 975 976 try: 977 exename1 = iraf.Expand(self._filename) 978 basedir, basename = os.path.split(exename1) 979 if basedir=="": basedir = "." 980 except iraf.IrafError, e: 981 if iraf.Verbose>0: 982 print "Error expanding executable name for task " + \ 983 self._name + ", tried: " + self._filename 984 print str(e) 985 exename1 = "" 986 basedir = "" 987 988 # default parameters are found with task 989 self._defaultParpath = os.path.join(basedir,self._name + ".par") 990 if not os.path.exists(iraf.Expand(self._defaultParpath, noerror=1)): 991 self._noParFile() 992 self._defaultParpath = "" 993 994 # uparm has scrunched version of par filename with saved parameters 995 # (also handle if they forgot the end-slash on the uparm var) 996 self._scrunchParpath = "uparm$/"+self.scrunchName()+".par"
997
998 - def _noParFile(self):
999 """Decide what to do if .par file is not found""" 1000 # Here I raise an exception, but subclasses (e.g., CL tasks) 1001 # can do something different. 1002 raise iraf.IrafError("Cannot find .par file for task "+self._name+\ 1003 ", tried: "+self._defaultParpath+", for file: "+self._filename)
1004
1005 - def _initParList(self):
1006 1007 """Initialize parameter list by reading parameter file""" 1008 1009 if not self._hasparfile: 1010 return 1011 1012 self._defaultParList = irafpar.IrafParList(self._name, 1013 iraf.Expand(self._defaultParpath, noerror=1)) 1014 1015 if self._scrunchParpath and \ 1016 os.path.exists(iraf.Expand(self._scrunchParpath, noerror=1)): 1017 self._currentParpath = self._scrunchParpath 1018 self._currentParList = irafpar.IrafParList(self._name, 1019 iraf.Expand(self._currentParpath, noerror=1)) 1020 # are lists consistent? 1021 if not self._isConsistentPar(): 1022 sys.stderr.write("uparm parameter list `%s' inconsistent " 1023 "with default parameters for %s `%s'\n" % 1024 (self._currentParpath, self.__class__.__name__, self._name,)) 1025 sys.stderr.flush() 1026 #XXX just toss it for now -- later can try to merge new,old 1027 try: 1028 os.remove(iraf.Expand(self._scrunchParpath, noerror=1)) 1029 except OSError: 1030 pass 1031 self._currentParpath = self._defaultParpath 1032 self._currentParList = copy.deepcopy(self._defaultParList) 1033 else: 1034 self._currentParpath = self._defaultParpath 1035 self._currentParList = copy.deepcopy(self._defaultParList)
1036
1037 - def _isConsistentPar(self):
1038 """Check current par list and default par list for consistency""" 1039 return (not self._currentParList) or \ 1040 self._currentParList.isConsistent(self._defaultParList)
1041 1042 # ----------------------------------------------------- 1043 # IRAF graphics kernel class 1044 # ----------------------------------------------------- 1045
1046 -class IrafGKITask(IrafTask):
1047 1048 """IRAF graphics kernel class (special case of IRAF task)""" 1049
1050 - def __init__(self, name, filename):
1051 """Initialize: only name and executable filename are needed""" 1052 IrafTask.__init__(self,'',name,'',filename,'clpackage','bin$') 1053 self.setHidden() 1054 # all graphics kernel tasks have the same parameters 1055 pars = irafpar.IrafParList(name) 1056 makepar = irafpar.makeIrafPar 1057 pars.addParam(makepar('', datatype='string', name='input', mode='ql')) 1058 pars.addParam(makepar('', datatype='string', name='device', mode='h')) 1059 pars.addParam(makepar('yes', datatype='bool', name='generic', mode='h')) 1060 self._defaultParList = pars 1061 self._currentParList = pars
1062
1063 - def saveParList(self,filename=None):
1064 """Never save parameters for kernels""" 1065 return ""
1066 1067 1068 # ----------------------------------------------------- 1069 # IRAF Pset class 1070 # ----------------------------------------------------- 1071
1072 -class IrafPset(IrafTask):
1073 1074 """IRAF pset class (special case of IRAF task)""" 1075
1076 - def __init__(self, prefix, name, suffix, filename, pkgname, pkgbinary):
1077 IrafTask.__init__(self,prefix,name,suffix,filename,pkgname,pkgbinary) 1078 # check that parameters are consistent with pset: 1079 # - not a foreign task 1080 # - has a parameter file 1081 if self.getForeign(): 1082 raise iraf.IrafError("Bad filename for pset %s: %s" % 1083 (self.getName(), filename)) 1084 if not self.hasParfile(): 1085 raise KeyError("Pset "+self.getName()+" has no parameter file")
1086
1087 - def _run(self, redirKW, specialKW):
1088 # executing a pset 1089 self.eParam() # the cl runs the param editor here; so shall we
1090
1091 - def __str__(self):
1092 # when coerced to a string, pset is name of task 1093 # this makes assignment of a pset to a string do the right thing 1094 return self.getName()
1095 1096 1097 # ----------------------------------------------------- 1098 # IRAF Python task class 1099 # ----------------------------------------------------- 1100
1101 -class IrafPythonTask(IrafTask):
1102 1103 """IRAF Python task class""" 1104
1105 - def __init__(self, prefix, name, suffix, filename, pkgname, pkgbinary, 1106 function):
1107 # filename is the .par file for this task 1108 IrafTask.__init__(self,prefix,name,suffix,filename,pkgname,pkgbinary) 1109 if self.getForeign(): 1110 raise iraf.IrafError( 1111 "Python task `%s' cannot be foreign (filename=`%s')" % 1112 (self.getName(), filename)) 1113 self.__dict__['_pyFunction'] = function
1114
1115 - def isConsistent(self, other):
1116 """Returns true if this task is consistent with another task object""" 1117 return IrafTask.isConsistent(self, other) and \ 1118 self._pyFunction == other._pyFunction
1119 1120 #========================================================= 1121 # special methods 1122 #========================================================= 1123
1124 - def __getstate__(self):
1125 1126 """Return state for pickling 1127 1128 Note that __setstate__ is not needed because 1129 returned state is a dictionary 1130 """ 1131 1132 # Dictionary is OK except for function pointer, which can't 1133 # be restored unless function is in the pyraf package 1134 if self._pyFunction is None: 1135 return self.__dict__ 1136 try: 1137 module = self._pyFunction.func_globals['__name__'] 1138 if module[:6] == 'pyraf.': 1139 return self.__dict__ 1140 except KeyError: 1141 pass 1142 file = self._pyFunction.func_code.co_filename 1143 # oh well, replace _pyFunction in shallow copy of dictionary 1144 dict = self.__dict__.copy() 1145 dict['_pyFunction'] = None 1146 return dict
1147 1148 #========================================================= 1149 # private methods 1150 #========================================================= 1151
1152 - def _applyRedir(self, redirKW):
1153 """Apply I/O redirection""" 1154 return iraf.redirApply(redirKW)
1155
1156 - def _run(self, redirKW, specialKW):
1157 """Execute task after parameters, I/O redirection are prepared.""" 1158 # extract all parameters 1159 parList = self.getParList() 1160 pl = [] 1161 for par in parList: 1162 if par.name not in ['mode', '$nargs']: 1163 if isinstance(par, irafpar.IrafParL): 1164 # list parameters get passed as objects 1165 pl.append(par) 1166 elif par.mode == "h" and not par.isLegal(): 1167 # illegal hidden value (generally undefined) passed as None 1168 pl.append(None) 1169 else: 1170 # other parameters get passed by value 1171 pl.append(par.get(native=1)) 1172 # run function on the parameters 1173 apply(self._pyFunction, pl)
1174 1175 1176 # ----------------------------------------------------- 1177 # parDictList search class (helper for IrafCLTask) 1178 # ----------------------------------------------------- 1179
1180 -class ParDictListSearch:
1181 - def __init__(self, taskObj):
1182 self.__dict__['_taskObj'] = taskObj
1183
1184 - def __getattr__(self, paramname):
1185 if self._taskObj.is_pseudo(paramname): 1186 return getattr(self._taskObj, paramname) 1187 if paramname[:1] == '_': 1188 raise AttributeError(paramname) 1189 # try exact match 1190 try: 1191 return self._taskObj.getParam(paramname,native=1,mode="h",exact=1) 1192 except iraf.IrafError, e: 1193 pass 1194 # try minimum match 1195 try: 1196 p = self._taskObj.getParObject(paramname,alldict=1) 1197 except iraf.IrafError, e: 1198 # not found at all 1199 raise AttributeError(str(e)) 1200 # it was found, but we don't allow min-match in CL scripts 1201 # print a more useful message 1202 raise AttributeError( 1203 "Unknown parameter `%s' (possibly intended `%s'?)" % 1204 (paramname, p.name))
1205
1206 - def getParObject(self, paramname):
1207 # try exact match 1208 try: 1209 return self._taskObj.getParObject(paramname,exact=1,alldict=1) 1210 except iraf.IrafError, e: 1211 pass 1212 # try minimum match 1213 try: 1214 p = self._taskObj.getParObject(paramname,alldict=1) 1215 except iraf.IrafError, e: 1216 # not found at all 1217 raise AttributeError(str(e)) 1218 # it was found, but we don't allow min-match in CL scripts 1219 # print a more useful message 1220 raise AttributeError( 1221 "Unknown parameter `%s' (possibly intended `%s'?)" % 1222 (paramname, p.name))
1223
1224 - def __setattr__(self, paramname, value):
1225 if self._taskObj.is_pseudo(paramname): 1226 return setattr(self._taskObj, paramname, value) 1227 if paramname[:1] == '_': raise AttributeError(paramname) 1228 # try exact match 1229 try: 1230 return self._taskObj.setParam(paramname,value,exact=1) 1231 except iraf.IrafError, e: 1232 pass 1233 # try minimum match 1234 try: 1235 p = self._taskObj.getParObject(paramname,alldict=1) 1236 except iraf.IrafError, e: 1237 # not found at all 1238 raise AttributeError(str(e)) 1239 # it was found, but we don't allow min-match in CL scripts 1240 # print a more useful message 1241 raise AttributeError( 1242 "Unknown parameter `%s' (possibly intended `%s'?)" % 1243 (paramname, p.name))
1244 1245 1246 # ----------------------------------------------------- 1247 # IRAF CL task class 1248 # ----------------------------------------------------- 1249
1250 -class IrafCLTask(IrafTask):
1251 1252 """IRAF CL task class""" 1253
1254 - def __init__(self, prefix, name, suffix, filename, pkgname, pkgbinary):
1255 # allow filename to be a filehandle or a filename 1256 if isinstance(filename,str): 1257 fh = None 1258 else: 1259 if not hasattr(filename,'read'): 1260 raise TypeError( 1261 'filename must be either a string or a filehandle') 1262 fh = filename 1263 if hasattr(fh,'name'): 1264 filename = fh.name 1265 else: 1266 filename = None 1267 IrafTask.__init__(self,prefix,name,suffix,filename,pkgname,pkgbinary) 1268 if self.getForeign(): 1269 raise iraf.IrafError( 1270 "CL task `%s' cannot be foreign (filename=`%s')" % 1271 (self.getName(), filename)) 1272 # placeholder for Python translation of CL code 1273 # (lazy instantiation) 1274 self.__dict__['_pycode'] = None 1275 self.__dict__['_codeObject'] = None 1276 self.__dict__['_clFunction'] = None 1277 if fh is not None: 1278 # if filehandle was specified, go ahead and do the 1279 # initialization now 1280 self.initTask(filehandle=fh)
1281 1282 #========================================================= 1283 # new public methods for CL task 1284 #========================================================= 1285
1286 - def getCode(self):
1287 """Return a string with the Python code for this task""" 1288 self.initTask(force=1) 1289 return self._pycode.code
1290
1291 - def reCompile(self):
1292 """Force recompilation of CL code""" 1293 if self._pycode is not None: 1294 self._pycode.index = None 1295 cl2py.codeCache.remove(self) 1296 self.initTask(force=1)
1297 1298 #========================================================= 1299 # other public methods 1300 #========================================================= 1301
1302 - def initTask(self,force=0,filehandle=None):
1303 """Fill in full pathnames of files, read par file, compile CL code 1304 1305 If filehandle is specified, reads CL code from there 1306 """ 1307 1308 if (not force) and (self._pycode is not None): 1309 # quick return if recheck of source code is not forced 1310 return 1311 1312 if self._filename is None and filehandle is None and \ 1313 self._pycode is not None: 1314 # another quick return -- if filename and filehandle are 1315 # both None and pycode is defined, input must have come 1316 # from a filehandle. Then pycode does not need to be 1317 # recreated (and in fact, it cannot be recreated.) 1318 return 1319 1320 if filehandle is not None and self._filename: 1321 self._fullpath = iraf.Expand(self._filename) 1322 1323 IrafTask.initTask(self) 1324 1325 if filehandle is None: 1326 filehandle = self._fullpath 1327 1328 justMade = False 1329 fcopy = self._filename 1330 if not irafinst.EXISTS and fcopy.startswith(irafinst.NO_IRAF_PFX): 1331 # translate code to python 1332 if iraf.Verbose>0: 1333 print "Compiling No-IRAF CL task %s" % (self._name,) 1334 fcopy = os.path.basename(fcopy) 1335 self._codeObject = None 1336 self._pycode = cl2py.cl2py(None, 1337 string=irafinst.getNoIrafClFor(fcopy), 1338 parlist=self._defaultParList, 1339 parfile=self._defaultParpath) 1340 justMade = True 1341 1342 if not justMade and not cl2py.checkCache(filehandle, self._pycode): 1343 # File has changed, force recompilation 1344 self._pycode = None 1345 if iraf.Verbose>1: 1346 print "Cached version of %s is out-of-date" % (self._name,) 1347 1348 if self._pycode is None: 1349 # translate code to python 1350 if iraf.Verbose>1: 1351 print "Compiling CL task %s" % (self._name,) 1352 self._codeObject = None 1353 self._pycode = cl2py.cl2py(filehandle, 1354 parlist=self._defaultParList, parfile=self._defaultParpath) 1355 1356 if self._codeObject is None: 1357 # No code object, which can happen if function has not 1358 # been compiled or if compilation failed. Try compiling 1359 # again in any case. 1360 self._clFunction = None 1361 if self._pkgname: 1362 scriptname = '<CL script %s.%s>' % (self._pkgname, self._name) 1363 else: 1364 # null pkgname -- just use task in name 1365 scriptname = '<CL script %s>' % self._name 1366 # force compile to inherit future div. so we don't rely on 2.x div. 1367 self._codeObject = compile(self._pycode.code,scriptname,'exec',0,0) 1368 1369 1370 if self._clFunction is None: 1371 # Execute the code to define the Python function in clDict 1372 clDict = {} 1373 exec self._codeObject in clDict 1374 self._clFunction = clDict[self._pycode.vars.proc_name] 1375 1376 # get parameter list from CL code 1377 # This may replace an existing list -- that's OK since 1378 # the cl2py code has already checked it for consistency. 1379 self._defaultParList = self._pycode.vars.parList 1380 # use currentParList from .par file if exists and consistent 1381 if self._currentParpath: 1382 if not self._defaultParList.isConsistent(self._currentParList): 1383 sys.stderr.write("uparm parameter list `%s' inconsistent " 1384 "with default parameters for %s `%s'\n" % 1385 (self._currentParpath, self.__class__.__name__, 1386 self._name,)) 1387 sys.stderr.flush() 1388 #XXX just toss it for now -- later can try to merge new,old 1389 if self._currentParpath == self._scrunchParpath: 1390 try: 1391 os.remove(iraf.Expand(self._scrunchParpath, noerror=1)) 1392 except OSError: 1393 pass 1394 self._currentParpath = self._defaultParpath 1395 self._currentParList = copy.deepcopy(self._defaultParList) 1396 else: 1397 self._currentParList = copy.deepcopy(self._pycode.vars.parList) 1398 self._currentParpath = self._defaultParpath
1399 1400 #========================================================= 1401 # special methods 1402 #========================================================= 1403
1404 - def __getstate__(self):
1405 """Return state for pickling""" 1406 # Dictionary is OK except for function pointer 1407 # Note that __setstate__ is not needed because 1408 # returned state is a dictionary 1409 if self._clFunction is None: 1410 return self.__dict__ 1411 # replace _clFunction in shallow copy of dictionary 1412 dict = self.__dict__.copy() 1413 dict['_clFunction'] = None 1414 return dict
1415 1416 #========================================================= 1417 # private methods 1418 #========================================================= 1419
1420 - def _applyRedir(self, redirKW):
1421 """Apply I/O redirection""" 1422 return iraf.redirApply(redirKW)
1423
1424 - def _run(self, redirKW, specialKW):
1425 """Execute task after parameters, I/O redirection are prepared.""" 1426 self._runCode()
1427
1428 - def _runCode(self, parList=None, kw={}):
1429 """Run the procedure with current parameters""" 1430 # add the searchable task object to keywords 1431 kw['taskObj'] = ParDictListSearch(self) 1432 if parList is None: parList = self.getParList() 1433 #XXX 1434 # It might be better to pass all parameters as 1435 # keywords instead of as positional arguments? 1436 # That would be more robust against some errors 1437 # but would also not allow certain IRAF-like 1438 # behaviors (where the .par file gives a different 1439 # name for the parameter.) 1440 #XXX 1441 apply(self._clFunction, parList, kw)
1442
1443 - def _noParFile(self):
1444 """Decide what to do if .par file is not found""" 1445 # For CL tasks, it is OK if no .par 1446 pass
1447
1448 - def _isConsistentPar(self):
1449 """Check current par list and default par list for consistency""" 1450 # they do not have to be consistent for CL task (at least not 1451 # where this is called, in IrafTask.initTask). 1452 #XXX This is a bit lax, eh? Need something a bit stricter. 1453 return 1
1454 1455 1456 # ----------------------------------------------------- 1457 # IRAF package class 1458 # ----------------------------------------------------- 1459 1460 # use empty "tag" class from irafglobals as base class 1461
1462 -class IrafPkg(IrafCLTask, irafglobals.IrafPkg):
1463 1464 """IRAF package class (special case of IRAF task)""" 1465
1466 - def __init__(self, prefix, name, suffix, filename, pkgname, pkgbinary):
1467 IrafCLTask.__init__(self,prefix,name,suffix,filename,pkgname,pkgbinary) 1468 self._loaded = 0 1469 self._tasks = minmatch.MinMatchDict() 1470 self._subtasks = minmatch.MinMatchDict() 1471 self._pkgs = minmatch.MinMatchDict()
1472 1473 #========================================================= 1474 # new public methods for package 1475 #========================================================= 1476
1477 - def isLoaded(self):
1478 """Returns true if this package has already been loaded""" 1479 return self._loaded
1480
1481 - def addTask(self, task, fullname):
1482 """Add a task to the task list for this package 1483 1484 Just store the name of the task to avoid cycles 1485 """ 1486 name = task.getName() 1487 self._tasks.add(name, fullname) 1488 # sub-packages get added to a separate list 1489 if isinstance(task, IrafPkg): self._pkgs.add(name, name)
1490 1491 #========================================================= 1492 # other public methods 1493 #========================================================= 1494
1495 - def getAllMatches(self, name, triedpkgs=None):
1496 """Return list of names of all parameters/tasks that may match name""" 1497 self.initTask(force=1) 1498 plist = self._runningParList or self._currentParList 1499 if plist: 1500 matches = plist.getAllMatches(name) 1501 else: 1502 matches = [] 1503 if self._loaded: 1504 # tasks in this package 1505 if name == "": 1506 matches.extend(self._tasks.keys()) 1507 else: 1508 matches.extend(self._tasks.getallkeys(name, [])) 1509 # tasks in subpackages 1510 if not triedpkgs: triedpkgs = {} 1511 triedpkgs[id(self)] = 1 1512 getPkg = iraf.getPkg 1513 getTried = triedpkgs.get 1514 for fullname in self._pkgs.values(): 1515 p = getPkg(fullname) 1516 if p._loaded and (not getTried(id(p))): 1517 try: 1518 matches.extend(p.getAllMatches(name, 1519 triedpkgs=triedpkgs)) 1520 except AttributeError, e: 1521 pass 1522 return matches
1523
1524 - def unlearn(self):
1525 """Resets parameters for all tasks in the package to their default values""" 1526 # If package isn't loaded, just unlearn the top-level package parameters 1527 if not self._loaded: 1528 IrafCLTask.unlearn(self) 1529 else: 1530 # Loop over all tasks in the package 1531 for task in self._tasks.keys(): 1532 iraf.getTask(task).unlearn()
1533
1534 - def __getattr__(self, name):
1535 """Return the task or param 'name' from this package (if it exists).""" 1536 if name[:1] == '_': 1537 raise AttributeError(name) 1538 self.initTask() 1539 # return package parameter if it exists 1540 plist = self._runningParList or self._currentParList 1541 if plist and plist.hasPar(name): 1542 return plist.getValue(name,native=1,mode=self.getMode()) 1543 # else search for task with the given name 1544 if not self._loaded: 1545 raise AttributeError("Package " + self.getName() + 1546 " has not been loaded; no tasks are defined") 1547 fullname = self._getTaskFullname(name) 1548 if fullname: 1549 return iraf.getTask(fullname) 1550 else: 1551 raise AttributeError("Parameter %s not found" % name)
1552 1553 #========================================================= 1554 # private methods 1555 #========================================================= 1556
1557 - def _getTaskFullname(self, name, triedpkgs=None):
1558 """Return the full name (pkg.task) of task 'name' from this package 1559 1560 Returns None if task is not found. 1561 1562 Also searches subpackages for the task. triedpkgs is 1563 a dictionary with all the packages that have already been 1564 tried. It is used to avoid infinite recursion when 1565 packages contain themselves. 1566 1567 Tasks that are found are added to _tasks dictionary to speed 1568 repeated searches. 1569 """ 1570 if not self._loaded: 1571 return None 1572 task = self._tasks.get(name) 1573 if task: 1574 return task 1575 # try subpackages 1576 task = self._subtasks.get(name) 1577 if task: 1578 return task 1579 # search subpackages 1580 if not triedpkgs: triedpkgs = {} 1581 triedpkgs[id(self)] = 1 1582 getPkg = iraf.getPkg 1583 getTried = triedpkgs.get 1584 for fullname in self._pkgs.values(): 1585 p = getPkg(fullname) 1586 if p._loaded and (not getTried(id(p))): 1587 task = p._getTaskFullname(name,triedpkgs=triedpkgs) 1588 if task: 1589 self._subtasks.add(name,task) 1590 return task 1591 return None
1592
1593 - def _specialKW(self, kw):
1594 """Handle special _doprint, _hush keywords""" 1595 1596 # Special _hush keyword is used to suppress most output when loading 1597 # packages. Default is to print output. 1598 # Implement by redirecting stdout to /dev/null (but don't override 1599 # other redirection requests) 1600 if kw.has_key('_hush'): 1601 if kw['_hush'] and \ 1602 not (kw.has_key('Stdout') or kw.has_key('StdoutAppend')): 1603 kw['Stdout'] = '/dev/null' 1604 del kw['_hush'] 1605 # Special _doprint keyword is used to control whether tasks are listed 1606 # after package has been loaded. Default is to list them if cl.menus 1607 # is set, or not to list them if it is not set. 1608 if kw.has_key('_doprint'): 1609 doprint = kw['_doprint'] 1610 del kw['_doprint'] 1611 else: 1612 doprint = iraf.cl.menus 1613 return {'doprint': doprint}
1614
1615 - def _run(self, redirKW, specialKW):
1616 """Execute task after parameters, I/O redirection are prepared.""" 1617 doprint = specialKW['doprint'] 1618 # if already loaded, just add to iraf.loadedPath 1619 iraf.loadedPath.append(self) 1620 if not self._loaded: 1621 self._loaded = 1 1622 iraf.addLoaded(self) 1623 if iraf.Verbose>1: 1624 print "Loading pkg ",self.getName(), "("+self.getFullpath()+")", 1625 menus = iraf.cl.menus 1626 try: 1627 iraf.cl.menus = 0 1628 self._runCode() 1629 # if other packages were loaded, put this on the 1630 # loadedPath list one more time 1631 if iraf.loadedPath[-1] is not self: 1632 iraf.loadedPath.append(self) 1633 if doprint: iraf.listTasks(self) 1634 finally: 1635 iraf.cl.menus = menus
1636 1637 # ----------------------------------------------------- 1638 # Turn an IrafCLTask into an IrafPkg 1639 # This is necessary because sometimes package scripts 1640 # are incorrectly defined as simple CL tasks. (Currently 1641 # the only example I know of is the imred/ccdred/ccdtest 1642 # package, but there could be others.) Need to keep 1643 # the same object (because there may be multiple references 1644 # to it) but repair the mistake by changing its class. 1645 # 1646 # A bit scary, but it works (at least in the current version 1647 # of Python.) 1648 # 1649 # This doesn't do everything that might be necessary. E.g., it does 1650 # not print the package contents after loading and does not put the 1651 # package on the list of loaded pcakges. Leave that up to the calling 1652 # routine. 1653 # ----------------------------------------------------- 1654
1655 -def mutateCLTask2Pkg(o, loaded=1, klass=IrafPkg):
1656 1657 """Hack an IRAF CL task object into an IRAF package object""" 1658 1659 if isinstance(o, IrafPkg): 1660 return 1661 if not isinstance(o, IrafCLTask): 1662 raise TypeError("Cannot turn object `%s' into an IrafPkg" % `o`) 1663 1664 # add the extra attributes used in IrafPkg 1665 # this is usually called while actually loading the package, so by 1666 # default loaded flag is set to true 1667 o._loaded = loaded 1668 o._tasks = minmatch.MinMatchDict() 1669 o._pkgs = minmatch.MinMatchDict() 1670 1671 # Presto, you're an IrafPkg! 1672 o.__class__ = klass
1673 1674 # ----------------------------------------------------- 1675 # IRAF foreign task class 1676 # ----------------------------------------------------- 1677 1678 # regular expressions for parameter substitution 1679 _re_foreign_par = re.compile(r'\$' + 1680 r'((?P<n>[0-9]+)' + 1681 r'|(?P<all>\*)' + 1682 r'|(\((?P<paren>[0-9]+)\))' + 1683 r'|(\((?P<allparen>\*)\))' + 1684 r')') 1685
1686 -class IrafForeignTask(IrafTask):
1687 1688 """IRAF foreign task class""" 1689
1690 - def __init__(self, prefix, name, suffix, filename, pkgname, pkgbinary):
1691 IrafTask.__init__(self,prefix,name,suffix,filename,pkgname,pkgbinary) 1692 # check that parameters are consistent with foreign task: 1693 # - foreign flag set 1694 # - no parameter file 1695 if not self.getForeign(): 1696 raise iraf.IrafError("Bad filename for foreign task %s: %s" % 1697 (self.getName(), filename)) 1698 if self.hasParfile(): 1699 if iraf.Verbose>0: 1700 print "Foreign task " + self.getName() + \ 1701 " cannot have a parameter file" 1702 self._hasparfile = 0
1703
1704 - def setParList(self, *args, **kw):
1705 """Set arguments to task 1706 1707 Does not use IrafParList structure -- just keeps list of 1708 the arguments 1709 """ 1710 if kw.has_key('_setMode'): 1711 del kw['_setMode'] 1712 if len(kw)>0: 1713 raise ValueError('Illegal keyword parameters %s for task %s' % 1714 (kw.keys(), self._name,)) 1715 #self._args = args 1716 # Insure that all arguments passed to ForeignTasks are 1717 # converted to strings, including objects which are not 1718 # naturally converted to strings. 1719 #self._args = map(re.escape,map(str,args)) 1720 self._args = map(self._str_escape, args)
1721 1722 #========================================================= 1723 # private methods 1724 #=========================================================
1725 - def _str_escape(self, arg):
1726 if not isinstance(arg, str): 1727 _arg = re.escape(str(arg)) 1728 else: _arg = arg 1729 return _arg
1730
1731 - def _applyRedir(self, redirKW):
1732 """Apply I/O redirection""" 1733 return iraf.redirApply(redirKW)
1734
1735 - def _run(self, redirKW, specialKW):
1736 """Execute task after parameters, I/O redirection are prepared.""" 1737 args = self._args 1738 self._nsub = 0 1739 # create command line 1740 cmdline = _re_foreign_par.sub(self._parSub,self._filename) 1741 if self._nsub==0 and args: 1742 # no argument substitution, just append all args 1743 cmdline = cmdline + ' ' + ' '.join(args) 1744 if iraf.Verbose>1: print "Running foreign task:", cmdline 1745 # create and run the sub-process 1746 subproc.subshellRedir(cmdline)
1747
1748 - def _parSub(self, mo):
1749 """Substitute an argument for this match object""" 1750 self._nsub = self._nsub+1 1751 n = mo.group('n') 1752 if n is not None: 1753 # $n -- simple substitution 1754 n = int(n) 1755 if n>len(self._args): 1756 return '' 1757 elif n==0: 1758 return self._name 1759 else: 1760 return self._args[n-1] 1761 n = mo.group('paren') 1762 if n is not None: 1763 # $(n) -- expand IRAF virtual filenames 1764 n = int(n) 1765 if n>len(self._args): 1766 return '' 1767 elif n==0: 1768 return self._name 1769 else: 1770 return iraf.Expand(self._args[n-1]) 1771 n = mo.group('all') 1772 if n is not None: 1773 # $* -- append all arguments 1774 return ' '.join(self._args) 1775 n = mo.group('allparen') 1776 if n is not None: 1777 # $(*) -- append all arguments with virtual filenames converted 1778 return ' '.join(map(iraf.Expand,self._args)) 1779 raise iraf.IrafError("Cannot handle foreign string `%s' " 1780 "for task %s" % (self._filename, self._name))
1781 1782 1783 # ----------------------------------------------------- 1784 # Utility function to split qualified names into components 1785 # ----------------------------------------------------- 1786
1787 -def _splitName(qualifiedName):
1788 """Split qualifiedName into components. 1789 1790 qualifiedName looks like [[package.]task.]paramname[subscript][.field], 1791 where subscript is an index in brackets. Returns a tuple with 1792 (package, task, paramname, subscript, field). IRAF one-based subscript 1793 is changed to Python zero-based subscript. 1794 """ 1795 # name components may have had 'PY' appended if they match Python keywords 1796 slist = map(irafutils.untranslateName, qualifiedName.split('.')) 1797 1798 # add field=None if not present 1799 1800 if len(slist)==1 or not basicpar.isParField(slist[-1]): 1801 # no field 1802 slist.append(None) 1803 if len(slist) > 4: 1804 raise iraf.IrafError("Illegal syntax for parameter: " + qualifiedName) 1805 slist = [None]*(4-len(slist)) + slist 1806 1807 # parse possible subscript and insert into list 1808 1809 paramname = slist[2] 1810 pstart = paramname.find('[') 1811 if pstart >= 0: 1812 try: 1813 pend = paramname.rindex(']') 1814 pindex = int(paramname[pstart+1:pend])-1 1815 slist[2:3] = [paramname[:pstart], pindex] 1816 except (TypeError, ValueError): 1817 raise iraf.IrafError("Illegal syntax for array parameter: " + 1818 qualifiedName) 1819 else: 1820 slist[3:3] = [None] 1821 return slist
1822 1823 # 1824 # When a user has a problem, I often wonder where the task they are 1825 # using came from and how it is defined. This set of tools shows a 1826 # hierarchy of the task/package definitions that led us here. 1827 # 1828 # (I would also like to know what file contained the task definition, but 1829 # that information is not available) 1830 # 1831 # This is used by the task "taskinfo" in iraffunctions.py; there is some 1832 # user documentation there. 1833 # 1834 1835 # find all the task definitions that match a particular wildcard
1836 -def gettask( name ) :
1837 return [ x for x in all_task_definitions if fnmatch.fnmatch(x._name, name) ]
1838 1839 1840 # make a printable line about a single task object - this doesn't say everything, 1841 # but it may say enough without being too cluttered.
1842 -def printable_task_def(x) :
1843 cl = str(x.__class__) 1844 if '.' in cl : 1845 cl=cl.split('.')[-1] 1846 1847 # I wonder if there is a significance to using getFullpath instead of _filename 1848 1849 s= "%s : %s - pkgbinary=%s class=%s"% ( x._name, x.getFullpath(), x._pkgbinary, cl ) 1850 return s
1851 1852 # show a task line, then show the package that task may have come from, then where 1853 # that came from, and so on. The top level is normally "clpackage" which is its 1854 # own parent.
1855 -def showtasklong(name, level=0) :
1856 l = gettask(name) 1857 if len(l) < 1 : 1858 print (" "*level)+'%s : NOT FOUND'%name 1859 else : 1860 l.sort() 1861 for x in l : 1862 print (" "*level)+printable_task_def(x) 1863 next_name = x._pkgname 1864 if (next_name == name) or ( level > 15 ) : 1865 pass 1866 else : 1867 showtasklong(next_name, level=level+1)
1868