diff --git a/AUTHORS b/AUTHORS
index 88bbfe352..6d1a2a816 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -173,6 +173,7 @@ mbyt
Michael Aquilina
Michael Birtwell
Michael Droettboom
+Michael Goerz
Michael Seifert
Michal Wajszczuk
Mihai Capotă
diff --git a/changelog/5764.feature.rst b/changelog/5764.feature.rst
new file mode 100644
index 000000000..3ac77b8fe
--- /dev/null
+++ b/changelog/5764.feature.rst
@@ -0,0 +1 @@
+New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run
diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py
index 91aa5f1fd..38ff97f2d 100644
--- a/src/_pytest/pastebin.py
+++ b/src/_pytest/pastebin.py
@@ -59,7 +59,7 @@ def create_new_paste(contents):
Creates a new paste using bpaste.net service.
:contents: paste contents as utf-8 encoded bytes
- :returns: url to the pasted contents
+ :returns: url to the pasted contents or error message
"""
import re
from urllib.request import urlopen
@@ -67,12 +67,17 @@ def create_new_paste(contents):
params = {"code": contents, "lexer": "python3", "expiry": "1week"}
url = "https://bpaste.net"
- response = urlopen(url, data=urlencode(params).encode("ascii")).read()
- m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8"))
+ try:
+ response = (
+ urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8")
+ )
+ except OSError as exc_info: # urllib errors
+ return "bad response: %s" % exc_info
+ m = re.search(r'href="/raw/(\w+)"', response)
if m:
return "{}/show/{}".format(url, m.group(1))
else:
- return "bad response: " + response.decode("utf-8")
+ return "bad response: invalid format ('" + response + "')"
def pytest_terminal_summary(terminalreporter):
diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py
index 4e8bac56c..a1bc0622e 100644
--- a/testing/test_pastebin.py
+++ b/testing/test_pastebin.py
@@ -82,6 +82,47 @@ class TestPaste:
def pastebin(self, request):
return request.config.pluginmanager.getplugin("pastebin")
+ @pytest.fixture
+ def mocked_urlopen_fail(self, monkeypatch):
+ """
+ monkeypatch the actual urlopen call to emulate a HTTP Error 400
+ """
+ calls = []
+
+ import urllib.error
+ import urllib.request
+
+ def mocked(url, data):
+ calls.append((url, data))
+ raise urllib.error.HTTPError(url, 400, "Bad request", None, None)
+
+ monkeypatch.setattr(urllib.request, "urlopen", mocked)
+ return calls
+
+ @pytest.fixture
+ def mocked_urlopen_invalid(self, monkeypatch):
+ """
+ monkeypatch the actual urlopen calls done by the internal plugin
+ function that connects to bpaste service, but return a url in an
+ unexpected format
+ """
+ calls = []
+
+ def mocked(url, data):
+ calls.append((url, data))
+
+ class DummyFile:
+ def read(self):
+ # part of html of a normal response
+ return b'View raw.'
+
+ return DummyFile()
+
+ import urllib.request
+
+ monkeypatch.setattr(urllib.request, "urlopen", mocked)
+ return calls
+
@pytest.fixture
def mocked_urlopen(self, monkeypatch):
"""
@@ -105,6 +146,19 @@ class TestPaste:
monkeypatch.setattr(urllib.request, "urlopen", mocked)
return calls
+ def test_pastebin_invalid_url(self, pastebin, mocked_urlopen_invalid):
+ result = pastebin.create_new_paste(b"full-paste-contents")
+ assert (
+ result
+ == "bad response: invalid format ('View raw.')"
+ )
+ assert len(mocked_urlopen_invalid) == 1
+
+ def test_pastebin_http_error(self, pastebin, mocked_urlopen_fail):
+ result = pastebin.create_new_paste(b"full-paste-contents")
+ assert result == "bad response: HTTP Error 400: Bad request"
+ assert len(mocked_urlopen_fail) == 1
+
def test_create_new_paste(self, pastebin, mocked_urlopen):
result = pastebin.create_new_paste(b"full-paste-contents")
assert result == "https://bpaste.net/show/3c0c6750bd"
@@ -127,4 +181,4 @@ class TestPaste:
monkeypatch.setattr(urllib.request, "urlopen", response)
result = pastebin.create_new_paste(b"full-paste-contents")
- assert result == "bad response: something bad occurred"
+ assert result == "bad response: invalid format ('something bad occurred')"