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

Source Code for Module pyraf.irafecl

  1  """This module adds IRAF ECL style error handling to PyRAF.""" 
  2  # $Id: irafecl.py 1463 2011-06-24 22:58:30Z stsci_embray $ 
  3   
  4  from __future__ import division # confidence high 
  5   
  6  import inspect 
  7  import sys 
  8  import iraf, pyrafglobals, iraftask, irafexecute, irafecl 
  9   
 10  executionMonitor = None 
 11   
12 -class EclState(object):
13 """An object which records the ECL state for one invocation of a CL proc: 14 15 1. The procedure's linemap converting Python line numberss to CL line numbers. 16 17 2. A mutable counter for tracking iferr blocking. 18 """
19 - def __init__(self, linemap):
20 self._value = 0 21 self._linemap = linemap
22
23 - def __iadd__(self, value):
24 self._value += value 25 return self
26
27 - def __int__(self):
28 return self._value
29
30 -def getTaskModule():
31 """Returns the module which supplies Task classes for the current 32 language mode, either ECL or classic CL. 33 """ 34 if pyrafglobals._use_ecl: 35 return irafecl 36 else: 37 return iraftask
38
39 -class Erract(object):
40 """Erract is a state variable (singleton) which corresponds to the IRAF ECL 41 environment variable 'erract'. erract has the following properties which 42 control ECL exception handling: 43 44 abort | noabort An ECL task should stop and unwind when it encounters an untrapped error. 45 46 trace | notrace Output to stderr for each task failure. 47 48 flpr | noflpr Flush the process cache for failed tasks. 49 50 clear | noclear Reset the $errno, $errmsg, $errtask variables with each task invocation, or not. 51 52 full | nofull Show tracebacks for the entire ECL call stack or just the erring task. 53 54 ecl | noecl Use ECL style error handling or classic PyRAF exception handling. 55 """
56 - def __init__(self, clear=True, flpr=True, abort=True, trace=True, full=True, ecl=True):
57 self.clear = clear 58 self.flpr = flpr 59 self.abort = abort 60 self.trace = trace 61 self.full = full 62 self.ecl = ecl 63 self._fields = ["abort", "trace", "flpr", "clear", "full", "ecl"]
64
65 - def states(self):
66 ans = "" 67 for s in self._fields: 68 if not self.__dict__[s]: 69 s = "no" + s 70 ans += s + " " 71 return ans
72
73 - def set_one(self, field):
74 flag = not field.startswith("no") 75 if not flag: 76 field = field[2:] 77 if field in self._fields: 78 self.__dict__[field] = flag 79 else: 80 raise ValueError("set erract: unknown behavior '" + field + "'")
81
82 - def adjust(self, values):
83 for a in values.split(): 84 self.set_one(a)
85 86 erract = Erract() 87 88 # IrafExecute --> IrafTask._run --> IrafTask.run 89 # user_code --> IrafPyTask._run --> IrafTask.run 90
91 -def _ecl_runframe(frame):
92 """Determines if frame corresponds to an IrafTask._run() method call.""" 93 # print "runframe:",frame.f_code.co_name 94 if frame.f_code.co_name != "_run": # XXXX necessary but not sufficient 95 return False 96 return True
97
98 -def _ecl_parent_task():
99 """Returns the local variables of the task which called this one. 100 """ 101 f = inspect.currentframe() 102 while f and not _ecl_runframe(f): 103 f = f.f_back 104 if not f: 105 return iraf.cl 106 return f.f_locals["self"]
107
108 -def _ecl_interpreted_frame(frame=None):
109 """Returns the stack frame corresponding to the executing Python code of 110 the nearest enclosing CL task. 111 """ 112 if frame is None: 113 f = inspect.currentframe() 114 else: 115 f = frame 116 priors = [] 117 while f and not _ecl_runframe(f): 118 priors.append(f) 119 f = f.f_back 120 if len(priors) >= 2: 121 return priors[-2] 122 else: 123 return None
124
125 -class EclBase:
126 - def __init__(self, *args, **kw):
127 self.__dict__['DOLLARerrno'] = 0 128 self.__dict__['DOLLARerrmsg'] = "" 129 self.__dict__['DOLLARerrtask'] = "" 130 self.__dict__['DOLLARerr_dzvalue'] = 1 131 self.__dict__['_ecl_pseudos'] = [ 'DOLLARerrno', 132 'DOLLARerrmsg', 133 'DOLLARerrtask', 134 'DOLLARerr_dzvalue' 135 ]
136
137 - def is_pseudo(self, name):
138 """Returns True iff 'name' is a pseudo variable or begins with _ecl""" 139 return (name in self.__dict__["_ecl_pseudos"]) or name.startswith("_ecl")
140
141 - def run(self,*args,**kw): # OVERRIDE IrafTask.run
142 """Execute this task with the specified arguments""" 143 144 self.initTask(force=1) 145 146 # Special _save keyword turns on parameter-saving. 147 # Default is *not* to save parameters (so it is necessary 148 # to use _save=1 to get parameter changes to be persistent.) 149 if kw.has_key('_save'): 150 save = kw['_save'] 151 del kw['_save'] 152 else: 153 save = 0 154 155 # Handle other special keywords 156 specialKW = self._specialKW(kw) 157 158 # Special Stdout, Stdin, Stderr keywords are used to redirect IO 159 redirKW, closeFHList = iraf.redirProcess(kw) 160 161 # set parameters 162 kw['_setMode'] = 1 163 apply(self.setParList,args,kw) 164 165 if iraf.Verbose>1: 166 print "run %s (%s: %s)" % (self._name, 167 self.__class__.__name__, self._fullpath) 168 if self._runningParList: 169 self._runningParList.lParam() 170 171 # delete list of param dictionaries so it will be 172 # recreated in up-to-date version if needed 173 self._parDictList = None 174 # apply IO redirection 175 resetList = self._applyRedir(redirKW) 176 177 self._ecl_clear_error_params() 178 179 def _runcore(): 180 try: 181 # Hook for execution monitor 182 if executionMonitor: 183 executionMonitor(self) 184 self._run(redirKW, specialKW) 185 self._updateParList(save) 186 if iraf.Verbose>1: print 'Successful task termination' 187 finally: 188 rv = self._resetRedir(resetList, closeFHList) 189 self._deleteRunningParList() 190 if self._parDictList: 191 self._parDictList[0] = (self._name, self.getParDict()) 192 if executionMonitor: 193 executionMonitor() 194 return rv
195 196 # if self._ecl_iferr_entered() and 197 if erract.ecl: 198 try: 199 return _runcore() 200 except Exception, e: 201 self._ecl_handle_error(e) 202 else: 203 return _runcore() 204
205 - def _run(self, redirKW, specialKW):
206 # OVERRIDE IrafTask._run for primitive (SPP, C, etc.) tasks to avoid exception trap. 207 apply(irafexecute.IrafExecute, (self, iraf.getVarDict()), redirKW)
208
209 - def _ecl_push_err(self):
210 """Method call emitted in compiled CL code to start an iferr 211 block. Increments local iferr state counter to track iferr 212 block nesting. 213 """ 214 s = self._ecl_state() 215 s += 1 216 self._ecl_set_error_params(0, '', '')
217
218 - def _ecl_pop_err(self):
219 """Method call emitted in compiled CL code to close an iferr 220 block and start the handler. Returns $errno which is 0 iff no 221 error occurred. Decrements local iferr state counter to track block nesting. 222 """ 223 s = self._ecl_state() 224 s += -1 225 return self.DOLLARerrno
226
227 - def _ecl_handle_error(self, e):
228 """IrafTask version of handle error: register error with calling task but continue.""" 229 self._ecl_record_error(e) 230 if erract.flpr: 231 iraf.flpr(self) 232 parent = _ecl_parent_task() 233 parent._ecl_record_error(e) 234 self._ecl_trace(parent._ecl_err_msg(e))
235
236 - def _ecl_trace(self, *args):
237 """Outputs an ECL error message to stderr iff erract.trace is True.""" 238 if erract.trace: 239 s = "" 240 for a in args: 241 s += str(a) + " " 242 sys.stderr.write(s+"\n") 243 sys.stderr.flush()
244
245 - def _ecl_exception_properties(self, e):
246 """This is a 'safe wrapper' which extracts the ECL pseudo parameter values from an 247 exception. It works for both ECL and non-ECL exceptions. 248 """ 249 return (getattr(e, "errno", -1), 250 getattr(e, "errmsg", str(e)), 251 getattr(e, "errtask", ""))
252
253 - def _ecl_record_error(self, e):
254 self._ecl_set_error_params(*self._ecl_exception_properties(e))
255
256 - def _ecl_set_error_params(self, errno, msg, taskname):
257 """Sets the ECL pseduo parameters for this task.""" 258 self.DOLLARerrno = errno 259 self.DOLLARerrmsg = msg 260 self.DOLLARerrtask = taskname
261
262 - def _ecl_clear_error_params(self):
263 """Clears the ECL pseudo parameters to a non-error condition.""" 264 if erract.clear: 265 self._ecl_set_error_params(0, "", "")
266
267 - def _ecl_err_msg(self, e):
268 """Formats an ECL error message from an exception and returns it as a string.""" 269 errno, errmsg, errtask = self._ecl_exception_properties(e) 270 if errno and errmsg and errtask: 271 text = "Error (%d): on line %d of '%s' from '%s':\n\t'%s'" % \ 272 (errno, self._ecl_get_lineno(), self._name, errtask, errmsg) 273 else: 274 text = str(e) 275 return text
276
277 - def _ecl_get_lineno(self, frame=None):
278 """_ecl_get_lineno fetches the innermost frame of Python code compiled from a CL task. 279 and then translates the current line number in that frame into it's CL line number 280 and returns it. 281 """ 282 try: 283 f = _ecl_interpreted_frame(frame) 284 map = f.f_locals["_ecl"]._linemap 285 return map[f.f_lineno] 286 except: 287 return 0
288
289 - def _ecl_state(self, frame=None):
290 """returns the EclState object corresponding to this task invocation.""" 291 locals = _ecl_interpreted_frame(frame).f_locals 292 return locals["_ecl"]
293
294 - def _ecl_iferr_entered(self):
295 """returns True iff the current invocation of the task self is in an iferr or ifnoerr guarded block.""" 296 try: 297 return int(self._ecl_state()) > 0 298 except KeyError: 299 return False
300
301 - def _ecl_safe_divide(self, a, b):
302 """_ecl_safe_divide is used to wrap the division operator for ECL code and trap divide-by-zero errors.""" 303 if b == 0: 304 if not erract.abort or self._ecl_iferr_entered(): 305 self._ecl_trace("Warning on line %d of '%s': divide by zero - using $err_dzvalue =" % 306 (self._ecl_get_lineno(), self._name), self.DOLLARerr_dzvalue) 307 return self.DOLLARerr_dzvalue 308 else: 309 iraf.error(1, "divide by zero", self._name, suppress=False) 310 return a / b
311
312 - def _ecl_safe_modulo(self, a, b):
313 """_ecl_safe_modulus is used to wrap the modulus operator for ECL code and trap mod-by-zero errors.""" 314 if b == 0: 315 if not erract.abort or self._ecl_iferr_entered(): 316 self._ecl_trace("Warning on line %d of task '%s': modulo by zero - using $err_dzvalue =" % 317 (self._ecl_get_lineno(), self._name), self.DOLLARerr_dzvalue) 318 return self.DOLLARerr_dzvalue 319 else: 320 iraf.error(1, "modulo by zero", self._name, suppress=False) 321 return a % b
322
323 -class SimpleTraceback(EclBase):
324 - def _ecl_handle_error(self, e):
325 self._ecl_record_error(e) 326 raise e
327
328 -class EclTraceback(EclBase):
329 - def _ecl_handle_error(self, e):
330 """Python task version of handle_error: do traceback and possibly abort.""" 331 self._ecl_record_error(e) 332 parent =_ecl_parent_task() 333 if parent: 334 parent._ecl_record_error(e) 335 if hasattr(e, "_ecl_traced"): 336 if erract.full: 337 self._ecl_traceback(e) 338 raise e 339 else: 340 try: 341 self._ecl_trace("ERROR (%d): %s" % (e.errno, e.errmsg)) 342 except: 343 self._ecl_trace("ERROR:", str(e)) 344 self._ecl_traceback(e) 345 if erract.abort: # and not self._ecl_iferr_entered(): 346 e._ecl_traced = True 347 raise e
348
349 - def _ecl_get_code(self, task, frame=None):
350 pass
351
352 - def _ecl_traceback(self, e):
353 raising_frame = inspect.trace()[-1][0] 354 lineno = self._ecl_get_lineno(frame=raising_frame) 355 cl_file = self.getFilename() 356 try: 357 cl_code = open(cl_file).readlines()[lineno-1].strip() 358 except: 359 cl_code = "<source code not available>" 360 if hasattr(e, "_ecl_suppress_first_trace") and \ 361 e._ecl_suppress_first_trace: 362 del e._ecl_suppress_first_trace 363 else: 364 self._ecl_trace(" ", repr(cl_code)) 365 self._ecl_trace(" line %d: %s" % (lineno , cl_file)) 366 parent = _ecl_parent_task() 367 if parent: 368 parent_lineno = self._ecl_get_lineno() 369 parent_file = parent.getFilename() 370 try: 371 parent_code = open(parent_file).readlines()[parent_lineno-1].strip() 372 self._ecl_trace(" called as:", repr(parent_code)) 373 except: 374 pass
375 376 377 378 ## The following classes exist as "ECL enabled" drop in replacements for the original 379 ## PyRAF task classes. I factored things this way in an attempt to minimize the impact 380 ## of ECL changes on ordinary PyRAF CL. 381
382 -class EclTask(EclBase, iraftask.IrafTask):
383 - def __init__(self, *args, **kw):
384 EclBase.__init__(self, *args, **kw) 385 iraftask.IrafTask.__init__(self, *args, **kw)
386 387 IrafTask = EclTask 388
389 -class EclGKITask(SimpleTraceback, iraftask.IrafGKITask):
390 - def __init__(self, *args, **kw):
391 SimpleTraceback.__init__(self, *args, **kw) 392 iraftask.IrafGKITask.__init__(self, *args, **kw)
393 394 IrafGKITask = EclGKITask 395
396 -class EclPset(SimpleTraceback, iraftask.IrafPset):
397 - def __init__(self, *args, **kw):
398 SimpleTraceback.__init__(self, *args, **kw) 399 iraftask.IrafPset.__init__(self, *args, **kw)
400 - def _run(self, *args, **kw):
401 return iraftask.IrafPset._run(self, *args, **kw)
402 403 IrafPset = EclPset 404
405 -class EclPythonTask(EclTraceback, iraftask.IrafPythonTask):
406 - def __init__(self, *args, **kw):
407 EclTraceback.__init__(self, *args, **kw) 408 iraftask.IrafPythonTask.__init__(self, *args, **kw)
409 - def _run(self, *args, **kw):
410 return iraftask.IrafPythonTask._run(self, *args, **kw)
411 412 IrafPythonTask = EclPythonTask 413
414 -class EclCLTask(EclTraceback, iraftask.IrafCLTask):
415 - def __init__(self, *args, **kw):
416 EclTraceback.__init__(self, *args, **kw) 417 iraftask.IrafCLTask.__init__(self, *args, **kw)
418 - def _run(self, *args, **kw):
419 return iraftask.IrafCLTask._run(self, *args, **kw)
420 421 IrafCLTask = EclCLTask 422
423 -class EclForeignTask(SimpleTraceback, iraftask.IrafForeignTask):
424 - def __init__(self, *args, **kw):
425 SimpleTraceback.__init__(self, *args, **kw) 426 iraftask.IrafForeignTask.__init__(self, *args, **kw)
427 - def _run(self, *args, **kw):
428 return iraftask.IrafForeignTask._run(self, *args, **kw)
429 430 IrafForeignTask = EclForeignTask 431
432 -class EclPkg(EclTraceback, iraftask.IrafPkg):
433 - def __init__(self, *args, **kw):
434 EclTraceback.__init__(self, *args, **kw) 435 iraftask.IrafPkg.__init__(self, *args, **kw)
436 - def _run(self, *args, **kw):
437 return iraftask.IrafPkg._run(self, *args, **kw)
438 439 IrafPkg = EclPkg 440
441 -def mutateCLTask2Pkg(o, loaded=1, klass=EclPkg):
442 return iraftask.mutateCLTask2Pkg(o, loaded=loaded, klass=klass)
443