1 """This module adds IRAF ECL style error handling to PyRAF."""
2
3
4 from __future__ import division
5
6 import inspect
7 import sys
8 import iraf, pyrafglobals, iraftask, irafexecute, irafecl
9
10 executionMonitor = None
11
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 """
20 self._value = 0
21 self._linemap = linemap
22
24 self._value += value
25 return self
26
29
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
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
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
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
85
86 erract = Erract()
87
88
89
90
92 """Determines if frame corresponds to an IrafTask._run() method call."""
93
94 if frame.f_code.co_name != "_run":
95 return False
96 return True
97
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
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
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
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):
142 """Execute this task with the specified arguments"""
143
144 self.initTask(force=1)
145
146
147
148
149 if kw.has_key('_save'):
150 save = kw['_save']
151 del kw['_save']
152 else:
153 save = 0
154
155
156 specialKW = self._specialKW(kw)
157
158
159 redirKW, closeFHList = iraf.redirProcess(kw)
160
161
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
172
173 self._parDictList = None
174
175 resetList = self._applyRedir(redirKW)
176
177 self._ecl_clear_error_params()
178
179 def _runcore():
180 try:
181
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
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):
208
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
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
235
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
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
255
257 """Sets the ECL pseduo parameters for this task."""
258 self.DOLLARerrno = errno
259 self.DOLLARerrmsg = msg
260 self.DOLLARerrtask = taskname
261
266
276
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
290 """returns the EclState object corresponding to this task invocation."""
291 locals = _ecl_interpreted_frame(frame).f_locals
292 return locals["_ecl"]
293
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
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
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
327
348
351
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
379
380
381
382 -class EclTask(EclBase, iraftask.IrafTask):
386
387 IrafTask = EclTask
388
389 -class EclGKITask(SimpleTraceback, iraftask.IrafGKITask):
393
394 IrafGKITask = EclGKITask
395
396 -class EclPset(SimpleTraceback, iraftask.IrafPset):
400 - def _run(self, *args, **kw):
402
403 IrafPset = EclPset
404
409 - def _run(self, *args, **kw):
411
412 IrafPythonTask = EclPythonTask
413
414 -class EclCLTask(EclTraceback, iraftask.IrafCLTask):
418 - def _run(self, *args, **kw):
420
421 IrafCLTask = EclCLTask
422
427 - def _run(self, *args, **kw):
429
430 IrafForeignTask = EclForeignTask
431
432 -class EclPkg(EclTraceback, iraftask.IrafPkg):
436 - def _run(self, *args, **kw):
438
439 IrafPkg = EclPkg
440
443