Errors.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. #
  2. # Errors
  3. #
  4. from __future__ import absolute_import
  5. try:
  6. from __builtin__ import basestring as any_string_type
  7. except ImportError:
  8. any_string_type = (bytes, str)
  9. import sys
  10. from contextlib import contextmanager
  11. from ..Utils import open_new_file
  12. from . import DebugFlags
  13. from . import Options
  14. class PyrexError(Exception):
  15. pass
  16. class PyrexWarning(Exception):
  17. pass
  18. def context(position):
  19. source = position[0]
  20. assert not (isinstance(source, any_string_type)), (
  21. "Please replace filename strings with Scanning.FileSourceDescriptor instances %r" % source)
  22. try:
  23. F = source.get_lines()
  24. except UnicodeDecodeError:
  25. # file has an encoding problem
  26. s = u"[unprintable code]\n"
  27. else:
  28. s = u''.join(F[max(0, position[1]-6):position[1]])
  29. s = u'...\n%s%s^\n' % (s, u' '*(position[2]-1))
  30. s = u'%s\n%s%s\n' % (u'-'*60, s, u'-'*60)
  31. return s
  32. def format_position(position):
  33. if position:
  34. return u"%s:%d:%d: " % (position[0].get_error_description(),
  35. position[1], position[2])
  36. return u''
  37. def format_error(message, position):
  38. if position:
  39. pos_str = format_position(position)
  40. cont = context(position)
  41. message = u'\nError compiling Cython file:\n%s\n%s%s' % (cont, pos_str, message or u'')
  42. return message
  43. class CompileError(PyrexError):
  44. def __init__(self, position = None, message = u""):
  45. self.position = position
  46. self.message_only = message
  47. self.formatted_message = format_error(message, position)
  48. self.reported = False
  49. # Deprecated and withdrawn in 2.6:
  50. # self.message = message
  51. Exception.__init__(self, self.formatted_message)
  52. # Python Exception subclass pickling is broken,
  53. # see http://bugs.python.org/issue1692335
  54. self.args = (position, message)
  55. def __str__(self):
  56. return self.formatted_message
  57. class CompileWarning(PyrexWarning):
  58. def __init__(self, position = None, message = ""):
  59. self.position = position
  60. # Deprecated and withdrawn in 2.6:
  61. # self.message = message
  62. Exception.__init__(self, format_position(position) + message)
  63. class InternalError(Exception):
  64. # If this is ever raised, there is a bug in the compiler.
  65. def __init__(self, message):
  66. self.message_only = message
  67. Exception.__init__(self, u"Internal compiler error: %s"
  68. % message)
  69. class AbortError(Exception):
  70. # Throw this to stop the compilation immediately.
  71. def __init__(self, message):
  72. self.message_only = message
  73. Exception.__init__(self, u"Abort error: %s" % message)
  74. class CompilerCrash(CompileError):
  75. # raised when an unexpected exception occurs in a transform
  76. def __init__(self, pos, context, message, cause, stacktrace=None):
  77. if message:
  78. message = u'\n' + message
  79. else:
  80. message = u'\n'
  81. self.message_only = message
  82. if context:
  83. message = u"Compiler crash in %s%s" % (context, message)
  84. if stacktrace:
  85. import traceback
  86. message += (
  87. u'\n\nCompiler crash traceback from this point on:\n' +
  88. u''.join(traceback.format_tb(stacktrace)))
  89. if cause:
  90. if not stacktrace:
  91. message += u'\n'
  92. message += u'%s: %s' % (cause.__class__.__name__, cause)
  93. CompileError.__init__(self, pos, message)
  94. # Python Exception subclass pickling is broken,
  95. # see http://bugs.python.org/issue1692335
  96. self.args = (pos, context, message, cause, stacktrace)
  97. class NoElementTreeInstalledException(PyrexError):
  98. """raised when the user enabled options.gdb_debug but no ElementTree
  99. implementation was found
  100. """
  101. listing_file = None
  102. num_errors = 0
  103. echo_file = None
  104. def open_listing_file(path, echo_to_stderr = 1):
  105. # Begin a new error listing. If path is None, no file
  106. # is opened, the error counter is just reset.
  107. global listing_file, num_errors, echo_file
  108. if path is not None:
  109. listing_file = open_new_file(path)
  110. else:
  111. listing_file = None
  112. if echo_to_stderr:
  113. echo_file = sys.stderr
  114. else:
  115. echo_file = None
  116. num_errors = 0
  117. def close_listing_file():
  118. global listing_file
  119. if listing_file:
  120. listing_file.close()
  121. listing_file = None
  122. def report_error(err, use_stack=True):
  123. if error_stack and use_stack:
  124. error_stack[-1].append(err)
  125. else:
  126. global num_errors
  127. # See Main.py for why dual reporting occurs. Quick fix for now.
  128. if err.reported: return
  129. err.reported = True
  130. try: line = u"%s\n" % err
  131. except UnicodeEncodeError:
  132. # Python <= 2.5 does this for non-ASCII Unicode exceptions
  133. line = format_error(getattr(err, 'message_only', "[unprintable exception message]"),
  134. getattr(err, 'position', None)) + u'\n'
  135. if listing_file:
  136. try: listing_file.write(line)
  137. except UnicodeEncodeError:
  138. listing_file.write(line.encode('ASCII', 'replace'))
  139. if echo_file:
  140. try: echo_file.write(line)
  141. except UnicodeEncodeError:
  142. echo_file.write(line.encode('ASCII', 'replace'))
  143. num_errors += 1
  144. if Options.fast_fail:
  145. raise AbortError("fatal errors")
  146. def error(position, message):
  147. #print("Errors.error:", repr(position), repr(message)) ###
  148. if position is None:
  149. raise InternalError(message)
  150. err = CompileError(position, message)
  151. if DebugFlags.debug_exception_on_error: raise Exception(err) # debug
  152. report_error(err)
  153. return err
  154. LEVEL = 1 # warn about all errors level 1 or higher
  155. def message(position, message, level=1):
  156. if level < LEVEL:
  157. return
  158. warn = CompileWarning(position, message)
  159. line = "note: %s\n" % warn
  160. if listing_file:
  161. listing_file.write(line)
  162. if echo_file:
  163. echo_file.write(line)
  164. return warn
  165. def warning(position, message, level=0):
  166. if level < LEVEL:
  167. return
  168. if Options.warning_errors and position:
  169. return error(position, message)
  170. warn = CompileWarning(position, message)
  171. line = "warning: %s\n" % warn
  172. if listing_file:
  173. listing_file.write(line)
  174. if echo_file:
  175. echo_file.write(line)
  176. return warn
  177. _warn_once_seen = {}
  178. def warn_once(position, message, level=0):
  179. if level < LEVEL or message in _warn_once_seen:
  180. return
  181. warn = CompileWarning(position, message)
  182. line = "warning: %s\n" % warn
  183. if listing_file:
  184. listing_file.write(line)
  185. if echo_file:
  186. echo_file.write(line)
  187. _warn_once_seen[message] = True
  188. return warn
  189. # These functions can be used to momentarily suppress errors.
  190. error_stack = []
  191. def hold_errors():
  192. error_stack.append([])
  193. def release_errors(ignore=False):
  194. held_errors = error_stack.pop()
  195. if not ignore:
  196. for err in held_errors:
  197. report_error(err)
  198. def held_errors():
  199. return error_stack[-1]
  200. # same as context manager:
  201. @contextmanager
  202. def local_errors(ignore=False):
  203. errors = []
  204. error_stack.append(errors)
  205. try:
  206. yield errors
  207. finally:
  208. release_errors(ignore=ignore)
  209. # this module needs a redesign to support parallel cythonisation, but
  210. # for now, the following works at least in sequential compiler runs
  211. def reset():
  212. _warn_once_seen.clear()
  213. del error_stack[:]