@@ -1050,6 +1050,50 @@ def h():
10501050 assert line .endswith ('mod.py' )
10511051 assert tw .lines [47 ] == ":15: AttributeError"
10521052
1053+ @pytest .mark .skipif ("sys.version_info[0] < 3" )
1054+ @pytest .mark .parametrize ('reason, description' , [
1055+ ('cause' , 'The above exception was the direct cause of the following exception:' ),
1056+ ('context' , 'During handling of the above exception, another exception occurred:' ),
1057+ ])
1058+ def test_exc_chain_repr_without_traceback (self , importasmod , reason , description ):
1059+ """
1060+ Handle representation of exception chains where one of the exceptions doesn't have a
1061+ real traceback, such as those raised in a subprocess submitted by the multiprocessing
1062+ module (#1984).
1063+ """
1064+ from _pytest .pytester import LineMatcher
1065+ exc_handling_code = ' from e' if reason == 'cause' else ''
1066+ mod = importasmod ("""
1067+ def f():
1068+ try:
1069+ g()
1070+ except Exception as e:
1071+ raise RuntimeError('runtime problem'){exc_handling_code}
1072+ def g():
1073+ raise ValueError('invalid value')
1074+ """ .format (exc_handling_code = exc_handling_code ))
1075+
1076+ with pytest .raises (RuntimeError ) as excinfo :
1077+ mod .f ()
1078+
1079+ # emulate the issue described in #1984
1080+ attr = '__%s__' % reason
1081+ getattr (excinfo .value , attr ).__traceback__ = None
1082+
1083+ r = excinfo .getrepr ()
1084+ tw = py .io .TerminalWriter (stringio = True )
1085+ tw .hasmarkup = False
1086+ r .toterminal (tw )
1087+
1088+ matcher = LineMatcher (tw .stringio .getvalue ().splitlines ())
1089+ matcher .fnmatch_lines ([
1090+ "ValueError: invalid value" ,
1091+ description ,
1092+ "* except Exception as e:" ,
1093+ "> * raise RuntimeError('runtime problem')" + exc_handling_code ,
1094+ "E *RuntimeError: runtime problem" ,
1095+ ])
1096+
10531097
10541098@pytest .mark .parametrize ("style" , ["short" , "long" ])
10551099@pytest .mark .parametrize ("encoding" , [None , "utf8" , "utf16" ])
0 commit comments