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.
This commit is contained in:
		
							parent
							
								
									5bf9f9a711
								
							
						
					
					
						commit
						d47b9d04d4
					
				
							
								
								
									
										1
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										1
									
								
								AUTHORS
								
								
								
								
							|  | @ -173,6 +173,7 @@ mbyt | ||||||
| Michael Aquilina | Michael Aquilina | ||||||
| Michael Birtwell | Michael Birtwell | ||||||
| Michael Droettboom | Michael Droettboom | ||||||
|  | Michael Goerz | ||||||
| Michael Seifert | Michael Seifert | ||||||
| Michal Wajszczuk | Michal Wajszczuk | ||||||
| Mihai Capotă | Mihai Capotă | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run | ||||||
|  | @ -59,7 +59,7 @@ def create_new_paste(contents): | ||||||
|     Creates a new paste using bpaste.net service. |     Creates a new paste using bpaste.net service. | ||||||
| 
 | 
 | ||||||
|     :contents: paste contents as utf-8 encoded bytes |     :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 |     import re | ||||||
|     from urllib.request import urlopen |     from urllib.request import urlopen | ||||||
|  | @ -67,12 +67,17 @@ def create_new_paste(contents): | ||||||
| 
 | 
 | ||||||
|     params = {"code": contents, "lexer": "python3", "expiry": "1week"} |     params = {"code": contents, "lexer": "python3", "expiry": "1week"} | ||||||
|     url = "https://bpaste.net" |     url = "https://bpaste.net" | ||||||
|     response = urlopen(url, data=urlencode(params).encode("ascii")).read() |     try: | ||||||
|     m = re.search(r'href="/raw/(\w+)"', response.decode("utf-8")) |         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: |     if m: | ||||||
|         return "{}/show/{}".format(url, m.group(1)) |         return "{}/show/{}".format(url, m.group(1)) | ||||||
|     else: |     else: | ||||||
|         return "bad response: " + response.decode("utf-8") |         return "bad response: invalid format ('" + response + "')" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def pytest_terminal_summary(terminalreporter): | def pytest_terminal_summary(terminalreporter): | ||||||
|  |  | ||||||
|  | @ -82,6 +82,47 @@ class TestPaste: | ||||||
|     def pastebin(self, request): |     def pastebin(self, request): | ||||||
|         return request.config.pluginmanager.getplugin("pastebin") |         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 |     @pytest.fixture | ||||||
|     def mocked_urlopen(self, monkeypatch): |     def mocked_urlopen(self, monkeypatch): | ||||||
|         """ |         """ | ||||||
|  | @ -105,6 +146,19 @@ class TestPaste: | ||||||
|         monkeypatch.setattr(urllib.request, "urlopen", mocked) |         monkeypatch.setattr(urllib.request, "urlopen", mocked) | ||||||
|         return calls |         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): |     def test_create_new_paste(self, pastebin, mocked_urlopen): | ||||||
|         result = pastebin.create_new_paste(b"full-paste-contents") |         result = pastebin.create_new_paste(b"full-paste-contents") | ||||||
|         assert result == "https://bpaste.net/show/3c0c6750bd" |         assert result == "https://bpaste.net/show/3c0c6750bd" | ||||||
|  | @ -127,4 +181,4 @@ class TestPaste: | ||||||
| 
 | 
 | ||||||
|         monkeypatch.setattr(urllib.request, "urlopen", response) |         monkeypatch.setattr(urllib.request, "urlopen", response) | ||||||
|         result = pastebin.create_new_paste(b"full-paste-contents") |         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')" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue