PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/interpreter/error.py

https://bitbucket.org/chence/pypy
Python | 456 lines | 430 code | 13 blank | 13 comment | 14 complexity | 54d01ff02f58ebb5b61fac3c0df46271 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. if w_object is None:
  206. objrepr = ''
  207. else:
  208. try:
  209. objrepr = space.str_w(space.repr(w_object))
  210. except OperationError:
  211. objrepr = '?'
  212. msg = 'Exception %s in %s%s ignored\n' % (
  213. self.errorstr(space, use_repr=True), where, objrepr)
  214. try:
  215. space.call_method(space.sys.get('stderr'), 'write', space.wrap(msg))
  216. except OperationError:
  217. pass # ignored
  218. def get_w_value(self, space):
  219. w_value = self._w_value
  220. if w_value is None:
  221. value = self._compute_value()
  222. self._w_value = w_value = space.wrap(value)
  223. return w_value
  224. def _compute_value(self):
  225. raise NotImplementedError
  226. def get_traceback(self):
  227. """Calling this marks the PyTraceback as escaped, i.e. it becomes
  228. accessible and inspectable by app-level Python code. For the JIT.
  229. Note that this has no effect if there are already several traceback
  230. frames recorded, because in this case they are already marked as
  231. escaping by executioncontext.leave() being called with
  232. got_exception=True.
  233. """
  234. from pypy.interpreter.pytraceback import PyTraceback
  235. tb = self._application_traceback
  236. if tb is not None and isinstance(tb, PyTraceback):
  237. tb.frame.mark_as_escaped()
  238. return tb
  239. def set_traceback(self, traceback):
  240. """Set the current traceback. It should either be a traceback
  241. pointing to some already-escaped frame, or a traceback for the
  242. current frame. To support the latter case we do not mark the
  243. frame as escaped. The idea is that it will be marked as escaping
  244. only if the exception really propagates out of this frame, by
  245. executioncontext.leave() being called with got_exception=True.
  246. """
  247. self._application_traceback = traceback
  248. # ____________________________________________________________
  249. # optimization only: avoid the slowest operation -- the string
  250. # formatting with '%' -- in the common case were we don't
  251. # actually need the message. Only supports %s and %d.
  252. _fmtcache = {}
  253. _fmtcache2 = {}
  254. def decompose_valuefmt(valuefmt):
  255. """Returns a tuple of string parts extracted from valuefmt,
  256. and a tuple of format characters."""
  257. formats = []
  258. parts = valuefmt.split('%')
  259. i = 1
  260. while i < len(parts):
  261. if parts[i].startswith('s') or parts[i].startswith('d'):
  262. formats.append(parts[i][0])
  263. parts[i] = parts[i][1:]
  264. i += 1
  265. elif parts[i] == '': # support for '%%'
  266. parts[i-1] += '%' + parts[i+1]
  267. del parts[i:i+2]
  268. else:
  269. raise ValueError("invalid format string (only %s or %d supported)")
  270. assert len(formats) > 0, "unsupported: no % command found"
  271. return tuple(parts), tuple(formats)
  272. def get_operrcls2(valuefmt):
  273. strings, formats = decompose_valuefmt(valuefmt)
  274. assert len(strings) == len(formats) + 1
  275. try:
  276. OpErrFmt = _fmtcache2[formats]
  277. except KeyError:
  278. from pypy.rlib.unroll import unrolling_iterable
  279. attrs = ['x%d' % i for i in range(len(formats))]
  280. entries = unrolling_iterable(enumerate(attrs))
  281. #
  282. class OpErrFmt(OperationError):
  283. def __init__(self, w_type, strings, *args):
  284. self.setup(w_type)
  285. assert len(args) == len(strings) - 1
  286. self.xstrings = strings
  287. for i, attr in entries:
  288. setattr(self, attr, args[i])
  289. assert w_type is not None
  290. def _compute_value(self):
  291. lst = [None] * (len(formats) + len(formats) + 1)
  292. for i, attr in entries:
  293. string = self.xstrings[i]
  294. value = getattr(self, attr)
  295. lst[i+i] = string
  296. lst[i+i+1] = str(value)
  297. lst[-1] = self.xstrings[-1]
  298. return ''.join(lst)
  299. #
  300. _fmtcache2[formats] = OpErrFmt
  301. return OpErrFmt, strings
  302. def get_operationerr_class(valuefmt):
  303. try:
  304. result = _fmtcache[valuefmt]
  305. except KeyError:
  306. result = _fmtcache[valuefmt] = get_operrcls2(valuefmt)
  307. return result
  308. get_operationerr_class._annspecialcase_ = 'specialize:memo'
  309. def operationerrfmt(w_type, valuefmt, *args):
  310. """Equivalent to OperationError(w_type, space.wrap(valuefmt % args)).
  311. More efficient in the (common) case where the value is not actually
  312. needed."""
  313. OpErrFmt, strings = get_operationerr_class(valuefmt)
  314. return OpErrFmt(w_type, strings, *args)
  315. operationerrfmt._annspecialcase_ = 'specialize:arg(1)'
  316. # ____________________________________________________________
  317. # Utilities
  318. from pypy.tool.ansi_print import ansi_print
  319. def debug_print(text, file=None, newline=True):
  320. # 31: ANSI color code "red"
  321. ansi_print(text, esc="31", file=file, newline=newline)
  322. try:
  323. WindowsError
  324. except NameError:
  325. _WINDOWS = False
  326. else:
  327. _WINDOWS = True
  328. def wrap_windowserror(space, e, w_filename=None):
  329. from pypy.rlib import rwin32
  330. winerror = e.winerror
  331. try:
  332. msg = rwin32.FormatError(winerror)
  333. except ValueError:
  334. msg = 'Windows Error %d' % winerror
  335. exc = space.w_WindowsError
  336. if w_filename is not None:
  337. w_error = space.call_function(exc, space.wrap(winerror),
  338. space.wrap(msg), w_filename)
  339. else:
  340. w_error = space.call_function(exc, space.wrap(winerror),
  341. space.wrap(msg))
  342. return OperationError(exc, w_error)
  343. def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError',
  344. w_exception_class=None):
  345. assert isinstance(e, OSError)
  346. if _WINDOWS and isinstance(e, WindowsError):
  347. return wrap_windowserror(space, e, w_filename)
  348. errno = e.errno
  349. if errno == EINTR:
  350. space.getexecutioncontext().checksignals()
  351. try:
  352. msg = os.strerror(errno)
  353. except ValueError:
  354. msg = 'error %d' % errno
  355. if w_exception_class is None:
  356. exc = getattr(space, exception_name)
  357. else:
  358. exc = w_exception_class
  359. if w_filename is not None:
  360. w_error = space.call_function(exc, space.wrap(errno),
  361. space.wrap(msg), w_filename)
  362. else:
  363. w_error = space.call_function(exc, space.wrap(errno),
  364. space.wrap(msg))
  365. return OperationError(exc, w_error)
  366. wrap_oserror2._annspecialcase_ = 'specialize:arg(3)'
  367. def wrap_oserror(space, e, filename=None, exception_name='w_OSError',
  368. w_exception_class=None):
  369. if filename is not None:
  370. return wrap_oserror2(space, e, space.wrap(filename),
  371. exception_name=exception_name,
  372. w_exception_class=w_exception_class)
  373. else:
  374. return wrap_oserror2(space, e, None,
  375. exception_name=exception_name,
  376. w_exception_class=w_exception_class)
  377. wrap_oserror._annspecialcase_ = 'specialize:arg(3)'
  378. def exception_from_errno(space, w_type):
  379. from pypy.rlib.rposix import get_errno
  380. errno = get_errno()
  381. msg = os.strerror(errno)
  382. w_error = space.call_function(w_type, space.wrap(errno), space.wrap(msg))
  383. return OperationError(w_type, w_error)
  384. def new_exception_class(space, name, w_bases=None, w_dict=None):
  385. """Create a new exception type.
  386. @param name: the name of the type.
  387. @param w_bases: Either an exception type, or a wrapped tuple of
  388. exception types. default is space.w_Exception.
  389. @param w_dict: an optional dictionary to populate the class __dict__.
  390. """
  391. if '.' in name:
  392. module, name = name.rsplit('.', 1)
  393. else:
  394. module = None
  395. if w_bases is None:
  396. w_bases = space.newtuple([space.w_Exception])
  397. elif not space.isinstance_w(w_bases, space.w_tuple):
  398. w_bases = space.newtuple([w_bases])
  399. if w_dict is None:
  400. w_dict = space.newdict()
  401. w_exc = space.call_function(
  402. space.w_type, space.wrap(name), w_bases, w_dict)
  403. if module:
  404. space.setattr(w_exc, space.wrap("__module__"), space.wrap(module))
  405. return w_exc
  406. def typed_unwrap_error_msg(space, expected, w_obj):
  407. type_name = space.type(w_obj).getname(space)
  408. return space.wrap("expected %s, got %s object" % (expected, type_name))