Skip to content

Commit a2bacb2

Browse files
[7.x] Tolerate RecursionError not being defined in Python<3.5
Co-authored-by: Seth Michael Larson <[email protected]>
1 parent b2c05d4 commit a2bacb2

File tree

7 files changed

+85
-7
lines changed

7 files changed

+85
-7
lines changed

elasticsearch/_async/http_aiohttp.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
import urllib3 # type: ignore
2424

25-
from ..compat import urlencode
25+
from ..compat import reraise_exceptions, urlencode
2626
from ..connection.base import Connection
2727
from ..exceptions import (
2828
ConnectionError,
@@ -304,7 +304,7 @@ async def perform_request(
304304
duration = self.loop.time() - start
305305

306306
# We want to reraise a cancellation or recursion error.
307-
except (asyncio.CancelledError, RecursionError):
307+
except reraise_exceptions:
308308
raise
309309
except Exception as e:
310310
self.log_request_fail(

elasticsearch/compat.py

+14
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,22 @@ def to_bytes(x, encoding="ascii"):
5858
from collections import Mapping
5959

6060

61+
try:
62+
reraise_exceptions = (RecursionError,)
63+
except NameError:
64+
reraise_exceptions = ()
65+
66+
try:
67+
import asyncio
68+
69+
reraise_exceptions += (asyncio.CancelledError,)
70+
except (ImportError, AttributeError):
71+
pass
72+
73+
6174
__all__ = [
6275
"string_types",
76+
"reraise_exceptions",
6377
"quote_plus",
6478
"quote",
6579
"urlencode",

elasticsearch/compat.pyi

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
# under the License.
1717

1818
import sys
19-
from typing import Callable, Tuple, Union
19+
from typing import Callable, Tuple, Type, Union
2020

2121
PY2: bool
2222
string_types: Tuple[type, ...]
2323

2424
to_str: Callable[[Union[str, bytes]], str]
2525
to_bytes: Callable[[Union[str, bytes]], bytes]
26+
reraise_exceptions: Tuple[Type[Exception], ...]
2627

2728
if sys.version_info[0] == 2:
2829
from itertools import imap as map

elasticsearch/connection/http_requests.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import time
1919
import warnings
2020

21-
from ..compat import string_types, urlencode
21+
from ..compat import reraise_exceptions, string_types, urlencode
2222
from ..exceptions import (
2323
ConnectionError,
2424
ConnectionTimeout,
@@ -166,7 +166,7 @@ def perform_request(
166166
response = self.session.send(prepared_request, **send_kwargs)
167167
duration = time.time() - start
168168
raw_data = response.content.decode("utf-8", "surrogatepass")
169-
except RecursionError:
169+
except reraise_exceptions:
170170
raise
171171
except Exception as e:
172172
self.log_request_fail(

elasticsearch/connection/http_urllib3.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from urllib3.exceptions import SSLError as UrllibSSLError # type: ignore
2525
from urllib3.util.retry import Retry # type: ignore
2626

27-
from ..compat import urlencode
27+
from ..compat import reraise_exceptions, urlencode
2828
from ..exceptions import (
2929
ConnectionError,
3030
ConnectionTimeout,
@@ -253,7 +253,7 @@ def perform_request(
253253
)
254254
duration = time.time() - start
255255
raw_data = response.data.decode("utf-8", "surrogatepass")
256-
except RecursionError:
256+
except reraise_exceptions:
257257
raise
258258
except Exception as e:
259259
self.log_request_fail(

test_elasticsearch/test_async/test_connection.py

+21
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
from multidict import CIMultiDict
3030

3131
from elasticsearch import AIOHttpConnection, __versionstr__
32+
from elasticsearch.compat import reraise_exceptions
33+
from elasticsearch.exceptions import ConnectionError
3234

3335
pytestmark = pytest.mark.asyncio
3436

@@ -318,6 +320,20 @@ async def test_surrogatepass_into_bytes(self):
318320
status, headers, data = await con.perform_request("GET", "/")
319321
assert u"你好\uda6a" == data
320322

323+
@pytest.mark.parametrize("exception_cls", reraise_exceptions)
324+
async def test_recursion_error_reraised(self, exception_cls):
325+
conn = AIOHttpConnection()
326+
327+
def request_raise(*_, **__):
328+
raise exception_cls("Wasn't modified!")
329+
330+
await conn._create_aiohttp_session()
331+
conn.session.request = request_raise
332+
333+
with pytest.raises(exception_cls) as e:
334+
await conn.perform_request("GET", "/")
335+
assert str(e.value) == "Wasn't modified!"
336+
321337

322338
class TestConnectionHttpbin:
323339
"""Tests the HTTP connection implementations against a live server E2E"""
@@ -389,3 +405,8 @@ async def test_aiohttp_connection(self):
389405
"Header2": "value2",
390406
"User-Agent": user_agent,
391407
}
408+
409+
async def test_aiohttp_connection_error(self):
410+
conn = AIOHttpConnection("not.a.host.name")
411+
with pytest.raises(ConnectionError):
412+
await conn.perform_request("GET", "/")

test_elasticsearch/test_connection.py

+42
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@
3232
from urllib3._collections import HTTPHeaderDict
3333

3434
from elasticsearch import __versionstr__
35+
from elasticsearch.compat import reraise_exceptions
3536
from elasticsearch.connection import (
3637
Connection,
3738
RequestsHttpConnection,
3839
Urllib3HttpConnection,
3940
)
4041
from elasticsearch.exceptions import (
4142
ConflictError,
43+
ConnectionError,
4244
NotFoundError,
4345
RequestError,
4446
TransportError,
@@ -466,6 +468,21 @@ def test_surrogatepass_into_bytes(self):
466468
status, headers, data = con.perform_request("GET", "/")
467469
self.assertEqual(u"你好\uda6a", data)
468470

471+
@pytest.mark.skipif(
472+
not reraise_exceptions, reason="RecursionError isn't defined in Python <3.5"
473+
)
474+
def test_recursion_error_reraised(self):
475+
conn = Urllib3HttpConnection()
476+
477+
def urlopen_raise(*_, **__):
478+
raise RecursionError("Wasn't modified!")
479+
480+
conn.pool.urlopen = urlopen_raise
481+
482+
with pytest.raises(RecursionError) as e:
483+
conn.perform_request("GET", "/")
484+
assert str(e.value) == "Wasn't modified!"
485+
469486

470487
class TestRequestsConnection(TestCase):
471488
def _get_mock_connection(
@@ -868,6 +885,21 @@ def test_surrogatepass_into_bytes(self):
868885
status, headers, data = con.perform_request("GET", "/")
869886
self.assertEqual(u"你好\uda6a", data)
870887

888+
@pytest.mark.skipif(
889+
not reraise_exceptions, reason="RecursionError isn't defined in Python <3.5"
890+
)
891+
def test_recursion_error_reraised(self):
892+
conn = RequestsHttpConnection()
893+
894+
def send_raise(*_, **__):
895+
raise RecursionError("Wasn't modified!")
896+
897+
conn.session.send = send_raise
898+
899+
with pytest.raises(RecursionError) as e:
900+
conn.perform_request("GET", "/")
901+
assert str(e.value) == "Wasn't modified!"
902+
871903

872904
class TestConnectionHttpbin:
873905
"""Tests the HTTP connection implementations against a live server E2E"""
@@ -942,6 +974,11 @@ def test_urllib3_connection(self):
942974
"User-Agent": user_agent,
943975
}
944976

977+
def test_urllib3_connection_error(self):
978+
conn = Urllib3HttpConnection("not.a.host.name")
979+
with pytest.raises(ConnectionError):
980+
conn.perform_request("GET", "/")
981+
945982
def test_requests_connection(self):
946983
# Defaults
947984
conn = RequestsHttpConnection("httpbin.org", port=443, use_ssl=True)
@@ -1003,3 +1040,8 @@ def test_requests_connection(self):
10031040
"Header2": "value2",
10041041
"User-Agent": user_agent,
10051042
}
1043+
1044+
def test_requests_connection_error(self):
1045+
conn = RequestsHttpConnection("not.a.host.name")
1046+
with pytest.raises(ConnectionError):
1047+
conn.perform_request("GET", "/")

0 commit comments

Comments
 (0)