Skip to content

Commit d47b9d0

Browse files
committed
Gracefully handle HTTP errors from pastebin
We find that the --pastebin option to pytest sometimes fails with "HTTP Error 400: Bad Request". We're still investigating the exact cause of these errors, but in the meantime, a failure to upload to the pastebin service should probably not crash pytest and cause a test failure in the continuous-integration. This patch catches exceptions like HTTPError that may be thrown while trying to communicate with the pastebin service, and reports them as a "bad response", without crashing with a backtrace or failing the entire test suite.
1 parent 5bf9f9a commit d47b9d0

File tree

4 files changed

+66
-5
lines changed

4 files changed

+66
-5
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ mbyt
173173
Michael Aquilina
174174
Michael Birtwell
175175
Michael Droettboom
176+
Michael Goerz
176177
Michael Seifert
177178
Michal Wajszczuk
178179
Mihai Capotă

changelog/5764.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run

src/_pytest/pastebin.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,25 @@ def create_new_paste(contents):
5959
Creates a new paste using bpaste.net service.
6060
6161
:contents: paste contents as utf-8 encoded bytes
62-
:returns: url to the pasted contents
62+
:returns: url to the pasted contents or error message
6363
"""
6464
import re
6565
from urllib.request import urlopen
6666
from urllib.parse import urlencode
6767

6868
params = {"code": contents, "lexer": "python3", "expiry": "1week"}
6969
url = "https://bpaste.net"
70-
response = urlopen(url, data=urlencode(params).encode("ascii")).read()
71-
m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8"))
70+
try:
71+
response = (
72+
urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8")
73+
)
74+
except OSError as exc_info: # urllib errors
75+
return "bad response: %s" % exc_info
76+
m = re.search(r'href="/raw/(\w+)"', response)
7277
if m:
7378
return "{}/show/{}".format(url, m.group(1))
7479
else:
75-
return "bad response: " + response.decode("utf-8")
80+
return "bad response: invalid format ('" + response + "')"
7681

7782

7883
def pytest_terminal_summary(terminalreporter):

testing/test_pastebin.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,47 @@ class TestPaste:
8282
def pastebin(self, request):
8383
return request.config.pluginmanager.getplugin("pastebin")
8484

85+
@pytest.fixture
86+
def mocked_urlopen_fail(self, monkeypatch):
87+
"""
88+
monkeypatch the actual urlopen call to emulate a HTTP Error 400
89+
"""
90+
calls = []
91+
92+
import urllib.error
93+
import urllib.request
94+
95+
def mocked(url, data):
96+
calls.append((url, data))
97+
raise urllib.error.HTTPError(url, 400, "Bad request", None, None)
98+
99+
monkeypatch.setattr(urllib.request, "urlopen", mocked)
100+
return calls
101+
102+
@pytest.fixture
103+
def mocked_urlopen_invalid(self, monkeypatch):
104+
"""
105+
monkeypatch the actual urlopen calls done by the internal plugin
106+
function that connects to bpaste service, but return a url in an
107+
unexpected format
108+
"""
109+
calls = []
110+
111+
def mocked(url, data):
112+
calls.append((url, data))
113+
114+
class DummyFile:
115+
def read(self):
116+
# part of html of a normal response
117+
return b'View <a href="/invalid/3c0c6750bd">raw</a>.'
118+
119+
return DummyFile()
120+
121+
import urllib.request
122+
123+
monkeypatch.setattr(urllib.request, "urlopen", mocked)
124+
return calls
125+
85126
@pytest.fixture
86127
def mocked_urlopen(self, monkeypatch):
87128
"""
@@ -105,6 +146,19 @@ def read(self):
105146
monkeypatch.setattr(urllib.request, "urlopen", mocked)
106147
return calls
107148

149+
def test_pastebin_invalid_url(self, pastebin, mocked_urlopen_invalid):
150+
result = pastebin.create_new_paste(b"full-paste-contents")
151+
assert (
152+
result
153+
== "bad response: invalid format ('View <a href=\"/invalid/3c0c6750bd\">raw</a>.')"
154+
)
155+
assert len(mocked_urlopen_invalid) == 1
156+
157+
def test_pastebin_http_error(self, pastebin, mocked_urlopen_fail):
158+
result = pastebin.create_new_paste(b"full-paste-contents")
159+
assert result == "bad response: HTTP Error 400: Bad request"
160+
assert len(mocked_urlopen_fail) == 1
161+
108162
def test_create_new_paste(self, pastebin, mocked_urlopen):
109163
result = pastebin.create_new_paste(b"full-paste-contents")
110164
assert result == "https://bpaste.net/show/3c0c6750bd"
@@ -127,4 +181,4 @@ def response(url, data):
127181

128182
monkeypatch.setattr(urllib.request, "urlopen", response)
129183
result = pastebin.create_new_paste(b"full-paste-contents")
130-
assert result == "bad response: something bad occurred"
184+
assert result == "bad response: invalid format ('something bad occurred')"

0 commit comments

Comments
 (0)