PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/interpreter/error.py

https://bitbucket.org/nbtaylor/pypy
Python | 475 lines | 449 code | 13 blank | 13 comment | 14 complexity | 47b77cc281f0c25ac6cb86956af71eac MD5 | raw file
  1. import os, sys
  2. from pypy.rlib import jit
  3. from pypy.rlib.objectmodel import we_are_translated
  4. from errno import EINTR
  5. AUTO_DEBUG = os.getenv('PYPY_DEBUG')
  6. RECORD_INTERPLEVEL_TRACEBACK = True
  7. class OperationError(Exception):
  8. """Interpreter-level exception that signals an exception that should be
  9. sent to the application level.
  10. OperationError instances have three attributes (and no .args),
  11. w_type, _w_value and _application_traceback, which contain the wrapped
  12. type and value describing the exception, and a chained list of
  13. PyTraceback objects making the application-level traceback.
  14. """
  15. _w_value = None
  16. _application_traceback = None
  17. def __init__(self, w_type, w_value, tb=None):
  18. assert w_type is not None
  19. self.setup(w_type)
  20. self._w_value = w_value
  21. self._application_traceback = tb
  22. def setup(self, w_type):
  23. self.w_type = w_type
  24. if not we_are_translated():
  25. self.debug_excs = []
  26. def clear(self, space):
  27. # for sys.exc_clear()
  28. self.w_type = space.w_None
  29. self._w_value = space.w_None
  30. self._application_traceback = None
  31. if not we_are_translated():
  32. del self.debug_excs[:]
  33. def match(self, space, w_check_class):
  34. "Check if this application-level exception matches 'w_check_class'."
  35. return space.exception_match(self.w_type, w_check_class)
  36. def async(self, space):
  37. "Check if this is an exception that should better not be caught."
  38. return (self.match(space, space.w_SystemExit) or
  39. self.match(space, space.w_KeyboardInterrupt))
  40. def __str__(self):
  41. "NOT_RPYTHON: Convenience for tracebacks."
  42. s = self._w_value
  43. if self.__class__ is not OperationError and s is None:
  44. s = self._compute_value()
  45. return '[%s: %s]' % (self.w_type, s)
  46. def errorstr(self, space, use_repr=False):
  47. "The exception class and value, as a string."
  48. w_value = self.get_w_value(space)
  49. if space is None:
  50. # this part NOT_RPYTHON
  51. exc_typename = str(self.w_type)
  52. exc_value = str(w_value)
  53. else:
  54. w = space.wrap
  55. if space.is_w(space.type(self.w_type), space.w_str):
  56. exc_typename = space.str_w(self.w_type)
  57. else:
  58. exc_typename = space.str_w(
  59. space.getattr(self.w_type, w('__name__')))
  60. if space.is_w(w_value, space.w_None):
  61. exc_value = ""
  62. else:
  63. try:
  64. if use_repr:
  65. exc_value = space.str_w(space.repr(w_value))
  66. else:
  67. exc_value = space.str_w(space.str(w_value))
  68. except OperationError:
  69. # oups, cannot __str__ the exception object
  70. exc_value = "<oups, exception object itself cannot be str'd>"
  71. if not exc_value:
  72. return exc_typename
  73. else:
  74. return '%s: %s' % (exc_typename, exc_value)
  75. def record_interpreter_traceback(self):
  76. """Records the current traceback inside the interpreter.
  77. This traceback is only useful to debug the interpreter, not the
  78. application."""
  79. if not we_are_translated():
  80. if RECORD_INTERPLEVEL_TRACEBACK:
  81. self.debug_excs.append(sys.exc_info())
  82. def print_application_traceback(self, space, file=None):
  83. "NOT_RPYTHON: Dump a standard application-level traceback."
  84. if file is None: file = sys.stderr
  85. self.print_app_tb_only(file)
  86. print >> file, self.errorstr(space)
  87. def print_app_tb_only(self, file):
  88. "NOT_RPYTHON"
  89. tb = self._application_traceback
  90. if tb:
  91. import linecache
  92. print >> file, "Traceback (application-level):"
  93. while tb is not None:
  94. co = tb.frame.pycode
  95. lineno = tb.get_lineno()
  96. fname = co.co_filename
  97. if fname.startswith('<inline>\n'):
  98. lines = fname.split('\n')
  99. fname = lines[0].strip()
  100. try:
  101. l = lines[lineno]
  102. except IndexError:
  103. l = ''
  104. else:
  105. l = linecache.getline(fname, lineno)
  106. print >> file, " File \"%s\"," % fname,
  107. print >> file, "line", lineno, "in", co.co_name
  108. if l:
  109. if l.endswith('\n'):
  110. l = l[:-1]
  111. l = " " + l.lstrip()
  112. print >> file, l
  113. tb = tb.next
  114. def print_detailed_traceback(self, space=None, file=None):
  115. """NOT_RPYTHON: Dump a nice detailed interpreter- and
  116. application-level traceback, useful to debug the interpreter."""
  117. import traceback, cStringIO
  118. if file is None: file = sys.stderr
  119. f = cStringIO.StringIO()
  120. for i in range(len(self.debug_excs)-1, -1, -1):
  121. print >> f, "Traceback (interpreter-level):"
  122. traceback.print_tb(self.debug_excs[i][2], file=f)
  123. f.seek(0)
  124. debug_print(''.join(['|| ' + line for line in f.readlines()]), file)
  125. if self.debug_excs:
  126. from pypy.tool import tb_server
  127. tb_server.publish_exc(self.debug_excs[-1])
  128. self.print_app_tb_only(file)
  129. print >> file, '(application-level)', self.errorstr(space)
  130. if AUTO_DEBUG:
  131. import debug
  132. debug.fire(self)
  133. @jit.unroll_safe
  134. def normalize_exception(self, space):
  135. """Normalize the OperationError. In other words, fix w_type and/or
  136. w_value to make sure that the __class__ of w_value is exactly w_type.
  137. """
  138. #
  139. # This method covers all ways in which the Python statement
  140. # "raise X, Y" can produce a valid exception type and instance.
  141. #
  142. # In the following table, 'Class' means a subclass of BaseException
  143. # and 'inst' is an instance of either 'Class' or a subclass of it.
  144. # Or 'Class' can also be an old-style class and 'inst' an old-style
  145. # instance of it.
  146. #
  147. # The flow object space only deals with non-advanced case. Old-style
  148. # classes and instances *are* advanced.
  149. #
  150. # input (w_type, w_value)... becomes... advanced case?
  151. # ---------------------------------------------------------------------
  152. # (tuple, w_value) (tuple[0], w_value) yes
  153. # (Class, None) (Class, Class()) no
  154. # (Class, inst) (inst.__class__, inst) no
  155. # (Class, tuple) (Class, Class(*tuple)) yes
  156. # (Class, x) (Class, Class(x)) no
  157. # ("string", ...) ("string", ...) deprecated
  158. # (inst, None) (inst.__class__, inst) no
  159. #
  160. w_type = self.w_type
  161. w_value = self.get_w_value(space)
  162. while space.is_true(space.isinstance(w_type, space.w_tuple)):
  163. w_type = space.getitem(w_type, space.wrap(0))
  164. if space.exception_is_valid_obj_as_class_w(w_type):
  165. # this is for all cases of the form (Class, something)
  166. if space.is_w(w_value, space.w_None):
  167. # raise Type: we assume we have to instantiate Type
  168. w_value = space.call_function(w_type)
  169. w_type = self._exception_getclass(space, w_value)
  170. else:
  171. w_valuetype = space.exception_getclass(w_value)
  172. if space.exception_issubclass_w(w_valuetype, w_type):
  173. # raise Type, Instance: let etype be the exact type of value
  174. w_type = w_valuetype
  175. else:
  176. if space.is_true(space.isinstance(w_value, space.w_tuple)):
  177. # raise Type, tuple: assume the tuple contains the
  178. # constructor args
  179. w_value = space.call(w_type, w_value)
  180. else:
  181. # raise Type, X: assume X is the constructor argument
  182. w_value = space.call_function(w_type, w_value)
  183. w_type = self._exception_getclass(space, w_value)
  184. else:
  185. # the only case left here is (inst, None), from a 'raise inst'.
  186. w_inst = w_type
  187. w_instclass = self._exception_getclass(space, w_inst)
  188. if not space.is_w(w_value, space.w_None):
  189. raise OperationError(space.w_TypeError,
  190. space.wrap("instance exception may not "
  191. "have a separate value"))
  192. w_value = w_inst
  193. w_type = w_instclass
  194. self.w_type = w_type
  195. self._w_value = w_value
  196. def _exception_getclass(self, space, w_inst):
  197. w_type = space.exception_getclass(w_inst)
  198. if not space.exception_is_valid_class_w(w_type):
  199. typename = w_type.getname(space)
  200. msg = ("exceptions must be old-style classes or derived "
  201. "from BaseException, not %s")
  202. raise operationerrfmt(space.w_TypeError, msg, typename)
  203. return w_type
  204. def write_unraisable(self, space, where, w_object=None,
  205. with_traceback=False, extra_line=''):
  206. if w_object is None:
  207. objrepr = ''
  208. else:
  209. try:
  210. objrepr = space.str_w(space.repr(w_object))
  211. except OperationError:
  212. objrepr = '?'
  213. #
  214. try:
  215. if with_traceback:
  216. w_t = self.w_type
  217. w_v = self.get_w_value(space)
  218. w_tb = space.wrap(self.get_traceback())
  219. space.appexec([space.wrap(where),
  220. space.wrap(objrepr),
  221. space.wrap(extra_line),
  222. w_t, w_v, w_tb],
  223. """(where, objrepr, extra_line, t, v, tb):
  224. import sys, traceback
  225. sys.stderr.write('From %s%s:\\n' % (where, objrepr))
  226. if extra_line:
  227. sys.stderr.write(extra_line)
  228. traceback.print_exception(t, v, tb)
  229. """)
  230. else:
  231. msg = 'Exception %s in %s%s ignored\n' % (
  232. self.errorstr(space, use_repr=True), where, objrepr)
  233. space.call_method(space.sys.get('stderr'), 'write',
  234. space.wrap(msg))
  235. except OperationError:
  236. pass # ignored
  237. def get_w_value(self, space):
  238. w_value = self._w_value
  239. if w_value is None:
  240. value = self._compute_value()
  241. self._w_value = w_value = space.wrap(value)
  242. return w_value
  243. def _compute_value(self):
  244. raise NotImplementedError
  245. def get_traceback(self):
  246. """Calling this marks the PyTraceback as escaped, i.e. it becomes
  247. accessible and inspectable by app-level Python code. For the JIT.
  248. Note that this has no effect if there are already several traceback
  249. frames recorded, because in this case they are already marked as
  250. escaping by executioncontext.leave() being called with
  251. got_exception=True.
  252. """
  253. from pypy.interpreter.pytraceback import PyTraceback
  254. tb = self._application_traceback
  255. if tb is not None and isinstance(tb, PyTraceback):
  256. tb.frame.mark_as_escaped()
  257. return tb
  258. def set_traceback(self, traceback):
  259. """Set the current traceback. It should either be a traceback
  260. pointing to some already-escaped frame, or a traceback for the
  261. current frame. To support the latter case we do not mark the
  262. frame as escaped. The idea is that it will be marked as escaping
  263. only if the exception really propagates out of this frame, by
  264. executioncontext.leave() being called with got_exception=True.
  265. """
  266. self._application_traceback = traceback
  267. # ____________________________________________________________
  268. # optimization only: avoid the slowest operation -- the string
  269. # formatting with '%' -- in the common case were we don't
  270. # actually need the message. Only supports %s and %d.
  271. _fmtcache = {}
  272. _fmtcache2 = {}
  273. def decompose_valuefmt(valuefmt):
  274. """Returns a tuple of string parts extracted from valuefmt,
  275. and a tuple of format characters."""
  276. formats = []
  277. parts = valuefmt.split('%')
  278. i = 1
  279. while i < len(parts):
  280. if parts[i].startswith('s') or parts[i].startswith('d'):
  281. formats.append(parts[i][0])
  282. parts[i] = parts[i][1:]
  283. i += 1
  284. elif parts[i] == '': # support for '%%'
  285. parts[i-1] += '%' + parts[i+1]
  286. del parts[i:i+2]
  287. else:
  288. raise ValueError("invalid format string (only %s or %d supported)")
  289. assert len(formats) > 0, "unsupported: no % command found"
  290. return tuple(parts), tuple(formats)
  291. def get_operrcls2(valuefmt):
  292. strings, formats = decompose_valuefmt(valuefmt)
  293. assert len(strings) == len(formats) + 1
  294. try:
  295. OpErrFmt = _fmtcache2[formats]
  296. except KeyError:
  297. from pypy.rlib.unroll import unrolling_iterable
  298. attrs = ['x%d' % i for i in range(len(formats))]
  299. entries = unrolling_iterable(enumerate(attrs))
  300. #
  301. class OpErrFmt(OperationError):
  302. def __init__(self, w_type, strings, *args):
  303. self.setup(w_type)
  304. assert len(args) == len(strings) - 1
  305. self.xstrings = strings
  306. for i, attr in entries:
  307. setattr(self, attr, args[i])
  308. assert w_type is not None
  309. def _compute_value(self):
  310. lst = [None] * (len(formats) + len(formats) + 1)
  311. for i, attr in entries:
  312. string = self.xstrings[i]
  313. value = getattr(self, attr)
  314. lst[i+i] = string
  315. lst[i+i+1] = str(value)
  316. lst[-1] = self.xstrings[-1]
  317. return ''.join(lst)
  318. #
  319. _fmtcache2[formats] = OpErrFmt
  320. return OpErrFmt, strings
  321. def get_operationerr_class(valuefmt):
  322. try:
  323. result = _fmtcache[valuefmt]
  324. except KeyError:
  325. result = _fmtcache[valuefmt] = get_operrcls2(valuefmt)
  326. return result
  327. get_operationerr_class._annspecialcase_ = 'specialize:memo'
  328. def operationerrfmt(w_type, valuefmt, *args):
  329. """Equivalent to OperationError(w_type, space.wrap(valuefmt % args)).
  330. More efficient in the (common) case where the value is not actually
  331. needed."""
  332. OpErrFmt, strings = get_operationerr_class(valuefmt)
  333. return OpErrFmt(w_type, strings, *args)
  334. operationerrfmt._annspecialcase_ = 'specialize:arg(1)'
  335. # ____________________________________________________________
  336. # Utilities
  337. from pypy.tool.ansi_print import ansi_print
  338. def debug_print(text, file=None, newline=True):
  339. # 31: ANSI color code "red"
  340. ansi_print(text, esc="31", file=file, newline=newline)
  341. try:
  342. WindowsError
  343. except NameError:
  344. _WINDOWS = False
  345. else:
  346. _WINDOWS = True
  347. def wrap_windowserror(space, e, w_filename=None):
  348. from pypy.rlib import rwin32
  349. winerror = e.winerror
  350. try:
  351. msg = rwin32.FormatError(winerror)
  352. except ValueError:
  353. msg = 'Windows Error %d' % winerror
  354. exc = space.w_WindowsError
  355. if w_filename is not None:
  356. w_error = space.call_function(exc, space.wrap(winerror),
  357. space.wrap(msg), w_filename)
  358. else:
  359. w_error = space.call_function(exc, space.wrap(winerror),
  360. space.wrap(msg))
  361. return OperationError(exc, w_error)
  362. def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError',
  363. w_exception_class=None):
  364. assert isinstance(e, OSError)
  365. if _WINDOWS and isinstance(e, WindowsError):
  366. return wrap_windowserror(space, e, w_filename)
  367. errno = e.errno
  368. if errno == EINTR:
  369. space.getexecutioncontext().checksignals()
  370. try:
  371. msg = os.strerror(errno)
  372. except ValueError:
  373. msg = 'error %d' % errno
  374. if w_exception_class is None:
  375. exc = getattr(space, exception_name)
  376. else:
  377. exc = w_exception_class
  378. if w_filename is not None:
  379. w_error = space.call_function(exc, space.wrap(errno),
  380. space.wrap(msg), w_filename)
  381. else:
  382. w_error = space.call_function(exc, space.wrap(errno),
  383. space.wrap(msg))
  384. return OperationError(exc, w_error)
  385. wrap_oserror2._annspecialcase_ = 'specialize:arg(3)'
  386. def wrap_oserror(space, e, filename=None, exception_name='w_OSError',
  387. w_exception_class=None):
  388. if filename is not None:
  389. return wrap_oserror2(space, e, space.wrap(filename),
  390. exception_name=exception_name,
  391. w_exception_class=w_exception_class)
  392. else:
  393. return wrap_oserror2(space, e, None,
  394. exception_name=exception_name,
  395. w_exception_class=w_exception_class)
  396. wrap_oserror._annspecialcase_ = 'specialize:arg(3)'
  397. def exception_from_errno(space, w_type):
  398. from pypy.rlib.rposix import get_errno
  399. errno = get_errno()
  400. msg = os.strerror(errno)
  401. w_error = space.call_function(w_type, space.wrap(errno), space.wrap(msg))
  402. return OperationError(w_type, w_error)
  403. def new_exception_class(space, name, w_bases=None, w_dict=None):
  404. """Create a new exception type.
  405. @param name: the name of the type.
  406. @param w_bases: Either an exception type, or a wrapped tuple of
  407. exception types. default is space.w_Exception.
  408. @param w_dict: an optional dictionary to populate the class __dict__.
  409. """
  410. if '.' in name:
  411. module, name = name.rsplit('.', 1)
  412. else:
  413. module = None
  414. if w_bases is None:
  415. w_bases = space.newtuple([space.w_Exception])
  416. elif not space.isinstance_w(w_bases, space.w_tuple):
  417. w_bases = space.newtuple([w_bases])
  418. if w_dict is None:
  419. w_dict = space.newdict()
  420. w_exc = space.call_function(
  421. space.w_type, space.wrap(name), w_bases, w_dict)
  422. if module:
  423. space.setattr(w_exc, space.wrap("__module__"), space.wrap(module))
  424. return w_exc
  425. def typed_unwrap_error_msg(space, expected, w_obj):
  426. type_name = space.type(w_obj).getname(space)
  427. return space.wrap("expected %s, got %s object" % (expected, type_name))