179 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
| 
 | |
| """ This file intends to gather all methods of representing
 | |
| failures/tracebacks etc. which should be used among
 | |
| all terminal-based reporters. This methods should be general,
 | |
| to allow further use outside the pylib
 | |
| """
 | |
| 
 | |
| import py
 | |
| from py.__.code import safe_repr
 | |
| 
 | |
| class Presenter(object):
 | |
|     """ Class used for presentation of various objects,
 | |
|     sharing common output style
 | |
|     """
 | |
|     def __init__(self, out, config):
 | |
|         """ out is a file-like object (we can write to it)
 | |
|         """
 | |
|         assert hasattr(out, 'write')
 | |
|         self.out = out
 | |
|         self.config = config
 | |
| 
 | |
|     def repr_source(self, source, marker=">", marker_location=-1):
 | |
|         """ This one represents piece of source with possible
 | |
|         marker at requested position
 | |
|         """
 | |
|         if isinstance(source, str):
 | |
|             # why the hell, string is iterable?
 | |
|             source = source.split("\n")
 | |
|         if marker_location < 0:
 | |
|             marker_location += len(source)
 | |
|             if marker_location < 0:
 | |
|                 marker_location = 0
 | |
|         if marker_location >= len(source):
 | |
|             marker_location = len(source) - 1
 | |
|         for i in range(len(source)):
 | |
|             if i == marker_location:
 | |
|                 prefix = marker + "   "
 | |
|             else:
 | |
|                 prefix = "    "
 | |
|             self.out.line(prefix + source[i])
 | |
| 
 | |
|     def repr_item_info(self, item):
 | |
|         """ This method represents py.test.collect.Item info (path and module)
 | |
|         """
 | |
|         root = item.fspath 
 | |
|         modpath = item._getmodpath() 
 | |
|         try: 
 | |
|             fn, lineno = item._getpathlineno() 
 | |
|         except TypeError: 
 | |
|             assert isinstance(item.parent, py.test.collect.Generator) 
 | |
|             # a generative test yielded a non-callable 
 | |
|             fn, lineno = item.parent._getpathlineno() 
 | |
|         if root == fn:
 | |
|             self.out.sep("_", "entrypoint: %s" %(modpath))
 | |
|         else:
 | |
|             self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath))
 | |
| 
 | |
|     def repr_failure_explanation(self, excinfo, source):
 | |
|         try:
 | |
|             s = str(source.getstatement(len(source)-1))
 | |
|         except KeyboardInterrupt: 
 | |
|             raise 
 | |
|         except: 
 | |
|             s = str(source[-1])
 | |
|         indent = " " * (4 + (len(s) - len(s.lstrip())))
 | |
|         # get the real exception information out 
 | |
|         lines = excinfo.exconly(tryshort=True).split('\n')
 | |
|         self.out.line('>' + indent[:-1] + lines.pop(0))
 | |
|         for x in lines:
 | |
|             self.out.line(indent + x)
 | |
| 
 | |
|     def getentrysource(self, entry):
 | |
|         try:
 | |
|             source = entry.getsource()
 | |
|         except py.error.ENOENT:
 | |
|             source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry)
 | |
|         return source.deindent()
 | |
| 
 | |
|     def repr_locals(self, f_locals):
 | |
|         if self.config.option.showlocals:
 | |
|             self.out.sep('- ', 'locals')
 | |
|             for name, value in f_locals.items():
 | |
|                 if name == '__builtins__': 
 | |
|                     self.out.line("__builtins__ = <builtins>")
 | |
|                 else:
 | |
|                     # This formatting could all be handled by the _repr() function, which is 
 | |
|                     # only repr.Repr in disguise, so is very configurable.
 | |
|                     str_repr = safe_repr._repr(value)
 | |
|                     if len(str_repr) < 70 or not isinstance(value,
 | |
|                                                 (list, tuple, dict)):
 | |
|                         self.out.line("%-10s = %s" %(name, str_repr))
 | |
|                     else:
 | |
|                         self.out.line("%-10s =\\" % (name,))
 | |
|                         py.std.pprint.pprint(value, stream=self.out)
 | |
| 
 | |
|     def repr_failure_tblong(self, item, excinfo, traceback, out_err_reporter):
 | |
|         if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
 | |
|             recursionindex = traceback.recursionindex()
 | |
|         else:
 | |
|             recursionindex = None
 | |
|         last = traceback[-1]
 | |
|         first = traceback[0]
 | |
|         for index, entry in py.builtin.enumerate(traceback): 
 | |
|             if entry == first:
 | |
|                 if item: 
 | |
|                     self.repr_item_info(item) 
 | |
|                     self.out.line()
 | |
|             else: 
 | |
|                 self.out.line("")
 | |
|             source = self.getentrysource(entry)
 | |
|             firstsourceline = entry.getfirstlinesource()
 | |
|             marker_location = entry.lineno - firstsourceline
 | |
|             if entry == last: 
 | |
|                 self.repr_source(source, 'E', marker_location)
 | |
|                 self.repr_failure_explanation(excinfo, source) 
 | |
|             else:
 | |
|                 self.repr_source(source, '>', marker_location)
 | |
|             self.out.line("") 
 | |
|             self.out.line("[%s:%d]" %(entry.path, entry.lineno+1))
 | |
|             self.repr_locals(entry.locals)
 | |
| 
 | |
|             # trailing info 
 | |
|             if entry == last:
 | |
|                 out_err_reporter() 
 | |
|                 self.out.sep("_")
 | |
|             else: 
 | |
|                 self.out.sep("_ ")
 | |
|                 if index == recursionindex:
 | |
|                     self.out.line("Recursion detected (same locals & position)")
 | |
|                     self.out.sep("!")
 | |
|                     break 
 | |
| 
 | |
|     def repr_failure_tbshort(self, item, excinfo, traceback, out_err_reporter):
 | |
|         # print a Python-style short traceback
 | |
|         if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
 | |
|             recursionindex = traceback.recursionindex()
 | |
|         else:
 | |
|             recursionindex = None
 | |
|         last = traceback[-1]
 | |
|         first = traceback[0]
 | |
|         self.out.line()
 | |
|         for index, entry in py.builtin.enumerate(traceback):
 | |
|             path = entry.path.basename
 | |
|             firstsourceline = entry.getfirstlinesource()
 | |
|             relline = entry.lineno - firstsourceline
 | |
|             self.out.line('  File "%s", line %d, in %s' % (
 | |
|                 path, entry.lineno+1, entry.name))
 | |
|             try:
 | |
|                 source = entry.getsource().lines
 | |
|             except py.error.ENOENT:
 | |
|                 source = ["?"]
 | |
|             else:
 | |
|                 try:
 | |
|                     if len(source) > 1:
 | |
|                         source = source[relline]
 | |
|                 except IndexError:
 | |
|                     source = []
 | |
|             if entry == last:
 | |
|                 if source:
 | |
|                     self.repr_source(source, 'E')
 | |
|                 self.repr_failure_explanation(excinfo, source) 
 | |
|             else:
 | |
|                 if source:
 | |
|                     self.repr_source(source, ' ')
 | |
|             self.repr_locals(entry.locals) 
 | |
| 
 | |
|             # trailing info 
 | |
|             if entry == last:
 | |
|                 out_err_reporter()
 | |
|                 self.out.sep("_")
 | |
|             else: 
 | |
|                 if index == recursionindex:
 | |
|                     self.out.line("Recursion detected (same locals & position)")
 | |
|                     self.out.sep("!")
 | |
|                     break 
 | |
| 
 | |
|     # the following is only used by the combination '--pdb --tb=no'
 | |
|     repr_failure_tbno = repr_failure_tbshort
 |