Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/pastebin.py: 33%

70 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

1"""Submit failure or test session information to a pastebin service.""" 

2import tempfile 

3from io import StringIO 

4from typing import IO 

5from typing import Union 

6 

7import pytest 

8from _pytest.config import Config 

9from _pytest.config import create_terminal_writer 

10from _pytest.config.argparsing import Parser 

11from _pytest.stash import StashKey 

12from _pytest.terminal import TerminalReporter 

13 

14 

15pastebinfile_key = StashKey[IO[bytes]]() 

16 

17 

18def pytest_addoption(parser: Parser) -> None: 

19 group = parser.getgroup("terminal reporting") 

20 group._addoption( 

21 "--pastebin", 

22 metavar="mode", 

23 action="store", 

24 dest="pastebin", 

25 default=None, 

26 choices=["failed", "all"], 

27 help="Send failed|all info to bpaste.net pastebin service", 

28 ) 

29 

30 

31@pytest.hookimpl(trylast=True) 

32def pytest_configure(config: Config) -> None: 

33 if config.option.pastebin == "all": 

34 tr = config.pluginmanager.getplugin("terminalreporter") 

35 # If no terminal reporter plugin is present, nothing we can do here; 

36 # this can happen when this function executes in a worker node 

37 # when using pytest-xdist, for example. 

38 if tr is not None: 

39 # pastebin file will be UTF-8 encoded binary file. 

40 config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") 

41 oldwrite = tr._tw.write 

42 

43 def tee_write(s, **kwargs): 

44 oldwrite(s, **kwargs) 

45 if isinstance(s, str): 

46 s = s.encode("utf-8") 

47 config.stash[pastebinfile_key].write(s) 

48 

49 tr._tw.write = tee_write 

50 

51 

52def pytest_unconfigure(config: Config) -> None: 

53 if pastebinfile_key in config.stash: 

54 pastebinfile = config.stash[pastebinfile_key] 

55 # Get terminal contents and delete file. 

56 pastebinfile.seek(0) 

57 sessionlog = pastebinfile.read() 

58 pastebinfile.close() 

59 del config.stash[pastebinfile_key] 

60 # Undo our patching in the terminal reporter. 

61 tr = config.pluginmanager.getplugin("terminalreporter") 

62 del tr._tw.__dict__["write"] 

63 # Write summary. 

64 tr.write_sep("=", "Sending information to Paste Service") 

65 pastebinurl = create_new_paste(sessionlog) 

66 tr.write_line("pastebin session-log: %s\n" % pastebinurl) 

67 

68 

69def create_new_paste(contents: Union[str, bytes]) -> str: 

70 """Create a new paste using the bpaste.net service. 

71 

72 :contents: Paste contents string. 

73 :returns: URL to the pasted contents, or an error message. 

74 """ 

75 import re 

76 from urllib.request import urlopen 

77 from urllib.parse import urlencode 

78 

79 params = {"code": contents, "lexer": "text", "expiry": "1week"} 

80 url = "https://bpa.st" 

81 try: 

82 response: str = ( 

83 urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") 

84 ) 

85 except OSError as exc_info: # urllib errors 

86 return "bad response: %s" % exc_info 

87 m = re.search(r'href="/raw/(\w+)"', response) 

88 if m: 

89 return f"{url}/show/{m.group(1)}" 

90 else: 

91 return "bad response: invalid format ('" + response + "')" 

92 

93 

94def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: 

95 if terminalreporter.config.option.pastebin != "failed": 

96 return 

97 if "failed" in terminalreporter.stats: 

98 terminalreporter.write_sep("=", "Sending information to Paste Service") 

99 for rep in terminalreporter.stats["failed"]: 

100 try: 

101 msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc 

102 except AttributeError: 

103 msg = terminalreporter._getfailureheadline(rep) 

104 file = StringIO() 

105 tw = create_terminal_writer(terminalreporter.config, file) 

106 rep.toterminal(tw) 

107 s = file.getvalue() 

108 assert len(s) 

109 pastebinurl = create_new_paste(s) 

110 terminalreporter.write_line(f"{msg} --> {pastebinurl}")