pastebin.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. """ submit failure or test session information to a pastebin service. """
  2. from __future__ import absolute_import, division, print_function
  3. import pytest
  4. import six
  5. import sys
  6. import tempfile
  7. def pytest_addoption(parser):
  8. group = parser.getgroup("terminal reporting")
  9. group._addoption(
  10. "--pastebin",
  11. metavar="mode",
  12. action="store",
  13. dest="pastebin",
  14. default=None,
  15. choices=["failed", "all"],
  16. help="send failed|all info to bpaste.net pastebin service.",
  17. )
  18. @pytest.hookimpl(trylast=True)
  19. def pytest_configure(config):
  20. if config.option.pastebin == "all":
  21. tr = config.pluginmanager.getplugin("terminalreporter")
  22. # if no terminal reporter plugin is present, nothing we can do here;
  23. # this can happen when this function executes in a slave node
  24. # when using pytest-xdist, for example
  25. if tr is not None:
  26. # pastebin file will be utf-8 encoded binary file
  27. config._pastebinfile = tempfile.TemporaryFile("w+b")
  28. oldwrite = tr._tw.write
  29. def tee_write(s, **kwargs):
  30. oldwrite(s, **kwargs)
  31. if isinstance(s, six.text_type):
  32. s = s.encode("utf-8")
  33. config._pastebinfile.write(s)
  34. tr._tw.write = tee_write
  35. def pytest_unconfigure(config):
  36. if hasattr(config, "_pastebinfile"):
  37. # get terminal contents and delete file
  38. config._pastebinfile.seek(0)
  39. sessionlog = config._pastebinfile.read()
  40. config._pastebinfile.close()
  41. del config._pastebinfile
  42. # undo our patching in the terminal reporter
  43. tr = config.pluginmanager.getplugin("terminalreporter")
  44. del tr._tw.__dict__["write"]
  45. # write summary
  46. tr.write_sep("=", "Sending information to Paste Service")
  47. pastebinurl = create_new_paste(sessionlog)
  48. tr.write_line("pastebin session-log: %s\n" % pastebinurl)
  49. def create_new_paste(contents):
  50. """
  51. Creates a new paste using bpaste.net service.
  52. :contents: paste contents as utf-8 encoded bytes
  53. :returns: url to the pasted contents
  54. """
  55. import re
  56. if sys.version_info < (3, 0):
  57. from urllib import urlopen, urlencode
  58. else:
  59. from urllib.request import urlopen
  60. from urllib.parse import urlencode
  61. params = {
  62. "code": contents,
  63. "lexer": "python3" if sys.version_info[0] == 3 else "python",
  64. "expiry": "1week",
  65. }
  66. url = "https://bpaste.net"
  67. response = urlopen(url, data=urlencode(params).encode("ascii")).read()
  68. m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8"))
  69. if m:
  70. return "%s/show/%s" % (url, m.group(1))
  71. else:
  72. return "bad response: " + response
  73. def pytest_terminal_summary(terminalreporter):
  74. import _pytest.config
  75. if terminalreporter.config.option.pastebin != "failed":
  76. return
  77. tr = terminalreporter
  78. if "failed" in tr.stats:
  79. terminalreporter.write_sep("=", "Sending information to Paste Service")
  80. for rep in terminalreporter.stats.get("failed"):
  81. try:
  82. msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc
  83. except AttributeError:
  84. msg = tr._getfailureheadline(rep)
  85. tw = _pytest.config.create_terminal_writer(
  86. terminalreporter.config, stringio=True
  87. )
  88. rep.toterminal(tw)
  89. s = tw.stringio.getvalue()
  90. assert len(s)
  91. pastebinurl = create_new_paste(s)
  92. tr.write_line("%s --> %s" % (msg, pastebinurl))