diff options
author | Joel Rivera <rivera@joel.mx> | 2016-03-03 11:08:38 -0600 |
---|---|---|
committer | Joel Rivera <rivera@joel.mx> | 2016-03-03 11:08:38 -0600 |
commit | 94d6a3e8fe973169e07d02374de4c997edeb4929 (patch) | |
tree | 12d8df440c61073f3bf92a58cc378745ebe749e9 | |
parent | 305e8cd9c2cac57a2c1b9556f1ecddcdd67004ce (diff) | |
download | cherrypy-94d6a3e8fe973169e07d02374de4c997edeb4929.tar.gz |
Add new argument to openURL for the testsuite to be able to
raise subclasses of socket.error (since python3.3 OSError)
instead of retrying.
This fixes the KeyboardError problem for 3.5 by specifying that
subclasses of BadStatusLine can be raised.
The longer explanation of the fix:
From python3.5 a new exception is retuned when the connection
ends abruptly:
http.client.RemoteDisconnected
RemoteDisconnected is a subclass of:
(ConnectionResetError, http.client.BadStatusLine)
and ConnectionResetError is an indirect subclass of:
OSError
From python 3.3 an up socket.error is an alias to OSError
following PEP-3151, therefore http.client.RemoteDisconnected
is considered a socket.error.
raise_subcls specifies the classes that are not going
to be considered as a socket.error for the retries.
Given that RemoteDisconnected is part BadStatusLine
we can use the same call for all py3 versions without
sideffects. python < 3.5 will raise directly BadStatusLine
which is not a subclass for socket.error/OSError.
Fixes issue #1406.
-rw-r--r-- | cherrypy/test/helper.py | 12 | ||||
-rw-r--r-- | cherrypy/test/test_states.py | 20 | ||||
-rw-r--r-- | cherrypy/test/webtest.py | 33 |
3 files changed, 52 insertions, 13 deletions
diff --git a/cherrypy/test/helper.py b/cherrypy/test/helper.py index fd8bb914..adc225df 100644 --- a/cherrypy/test/helper.py +++ b/cherrypy/test/helper.py @@ -341,12 +341,18 @@ class CPWebCase(webtest.WebCase): sys.exit() def getPage(self, url, headers=None, method="GET", body=None, - protocol=None): - """Open the url. Return status, headers, body.""" + protocol=None, raise_subcls=None): + """Open the url. Return status, headers, body. + + `raise_subcls` must be a tuple with the exceptions classes + or a single exception class that are not going to be considered + a socket.error regardless that they were are subclass of a + socket.error and therefore not considered for a connection retry. + """ if self.script_name: url = httputil.urljoin(self.script_name, url) return webtest.WebCase.getPage(self, url, headers, method, body, - protocol) + protocol, raise_subcls) def skip(self, msg='skipped '): raise nose.SkipTest(msg) diff --git a/cherrypy/test/test_states.py b/cherrypy/test/test_states.py index d86e5cf5..dc6891bc 100644 --- a/cherrypy/test/test_states.py +++ b/cherrypy/test/test_states.py @@ -204,9 +204,25 @@ class ServerStateTests(helper.CPWebCase): # thread will just die without writing a response. engine.start() cherrypy.server.start() - + # From python3.5 a new exception is retuned when the connection + # ends abruptly: + # http.client.RemoteDisconnected + # RemoteDisconnected is a subclass of: + # (ConnectionResetError, http.client.BadStatusLine) + # and ConnectionResetError is an indirect subclass of: + # OSError + # From python 3.3 an up socket.error is an alias to OSError + # following PEP-3151, therefore http.client.RemoteDisconnected + # is considered a socket.error. + # + # raise_subcls specifies the classes that are not going + # to be considered as a socket.error for the retries. + # Given that RemoteDisconnected is part BadStatusLine + # we can use the same call for all py3 versions without + # sideffects. python < 3.5 will raise directly BadStatusLine + # which is not a subclass for socket.error/OSError. try: - self.getPage("/ctrlc") + self.getPage("/ctrlc", raise_subcls=BadStatusLine) except BadStatusLine: pass else: diff --git a/cherrypy/test/webtest.py b/cherrypy/test/webtest.py index fc3cab49..c9aeb71d 100644 --- a/cherrypy/test/webtest.py +++ b/cherrypy/test/webtest.py @@ -238,8 +238,13 @@ class WebCase(TestCase): return interface(self.HOST) def getPage(self, url, headers=None, method="GET", body=None, - protocol=None): + protocol=None, raise_subcls=None): """Open the url with debugging support. Return status, headers, body. + + `raise_subcls` must be a tuple with the exceptions classes + or a single exception class that are not going to be considered + a socket.error regardless that they were are subclass of a + socket.error and therefore not considered for a connection retry. """ ServerError.on = False @@ -252,7 +257,8 @@ class WebCase(TestCase): self.time = None start = time.time() result = openURL(url, headers, method, body, self.HOST, self.PORT, - self.HTTP_CONN, protocol or self.PROTOCOL) + self.HTTP_CONN, protocol or self.PROTOCOL, + raise_subcls) self.time = time.time() - start self.status, self.headers, self.body = result @@ -492,9 +498,15 @@ def shb(response): def openURL(url, headers=None, method="GET", body=None, host="127.0.0.1", port=8000, http_conn=HTTPConnection, - protocol="HTTP/1.1"): - """Open the given HTTP resource and return status, headers, and body.""" + protocol="HTTP/1.1", raise_subcls=None): + """ + Open the given HTTP resource and return status, headers, and body. + `raise_subcls` must be a tuple with the exceptions classes + or a single exception class that are not going to be considered + a socket.error regardless that they were are subclass of a + socket.error and therefore not considered for a connection retry. + """ headers = cleanHeaders(headers, method, body, host, port) # Trying 10 times is simply in case of socket errors. @@ -512,7 +524,6 @@ def openURL(url, headers=None, method="GET", body=None, if py3k and isinstance(url, bytes): url = url.decode() - conn.putrequest(method.upper(), url, skip_host=True, skip_accept_encoding=True) @@ -533,10 +544,16 @@ def openURL(url, headers=None, method="GET", body=None, conn.close() return s, h, b - except socket.error: - time.sleep(0.5) - if trial == 9: + except socket.error as e: + if (raise_subcls is not None and + issubclass(e.__class__, raise_subcls)): raise + else: + time.sleep(0.5) + if trial == 9: + raise + + # Add any exceptions which your web framework handles |