Skip to content

Commit

Permalink
Gracefully handle HTTP errors from pastebin
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
goerz committed Aug 27, 2019
1 parent 5bf9f9a commit d47b9d0
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 5 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ mbyt
Michael Aquilina
Michael Birtwell
Michael Droettboom
Michael Goerz
Michael Seifert
Michal Wajszczuk
Mihai Capotă
Expand Down
1 change: 1 addition & 0 deletions changelog/5764.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run
13 changes: 9 additions & 4 deletions src/_pytest/pastebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,25 @@ 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
from urllib.parse import urlencode

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):
Expand Down
56 changes: 55 additions & 1 deletion testing/test_pastebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="/invalid/3c0c6750bd">raw</a>.'

return DummyFile()

import urllib.request

monkeypatch.setattr(urllib.request, "urlopen", mocked)
return calls

@pytest.fixture
def mocked_urlopen(self, monkeypatch):
"""
Expand All @@ -105,6 +146,19 @@ def read(self):
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 <a href=\"/invalid/3c0c6750bd\">raw</a>.')"
)
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"
Expand All @@ -127,4 +181,4 @@ def response(url, data):

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')"

0 comments on commit d47b9d0

Please sign in to comment.