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 Birtwell | ||||
| Michael Droettboom | ||||
| Michael Goerz | ||||
| Michael Seifert | ||||
| Michal Wajszczuk | ||||
| 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. | ||||
| 
 | ||||
|     :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): | ||||
|  |  | |||
|  | @ -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): | ||||
|         """ | ||||
|  | @ -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 <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" | ||||
|  | @ -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')" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue