Skip to content

Commit 9aa4b45

Browse files
committed
Code review comments
1 parent dcd6be5 commit 9aa4b45

File tree

5 files changed

+135
-174
lines changed

5 files changed

+135
-174
lines changed

adafruit_connection_manager.py

Lines changed: 71 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636

3737
if not sys.implementation.name == "circuitpython":
38-
from typing import Optional, Tuple
38+
from typing import List, Optional, Tuple
3939

4040
from circuitpython_typing.socket import (
4141
CircuitPythonSocketType,
@@ -71,8 +71,7 @@ class _FakeSSLContext:
7171
def __init__(self, iface: InterfaceType) -> None:
7272
self._iface = iface
7373

74-
# pylint: disable=unused-argument
75-
def wrap_socket(
74+
def wrap_socket( # pylint: disable=unused-argument
7675
self, socket: CircuitPythonSocketType, server_hostname: Optional[str] = None
7776
) -> _FakeSSLSocket:
7877
"""Return the same socket"""
@@ -184,54 +183,75 @@ def __init__(
184183
) -> None:
185184
self._socket_pool = socket_pool
186185
# Hang onto open sockets so that we can reuse them.
187-
self._available_socket = {}
188-
self._open_sockets = {}
186+
self._available_sockets = set()
187+
self._managed_socket_by_key = {}
188+
self._managed_socket_by_socket = {}
189189

190190
def _free_sockets(self, force: bool = False) -> None:
191-
available_sockets = []
192-
for socket, free in self._available_socket.items():
193-
if free or force:
194-
available_sockets.append(socket)
195-
191+
# cloning lists since items are being removed
192+
available_sockets = list(self._available_sockets)
196193
for socket in available_sockets:
197194
self.close_socket(socket)
195+
if force:
196+
open_sockets = list(self._managed_socket_by_key.values())
197+
for socket in open_sockets:
198+
self.close_socket(socket)
198199

199-
def _get_key_for_socket(self, socket):
200+
def _get_connected_socket( # pylint: disable=too-many-arguments
201+
self,
202+
addr_info: List[Tuple[int, int, int, str, Tuple[str, int]]],
203+
host: str,
204+
port: int,
205+
timeout: float,
206+
is_ssl: bool,
207+
ssl_context: Optional[SSLContextType] = None,
208+
):
200209
try:
201-
return next(
202-
key for key, value in self._open_sockets.items() if value == socket
203-
)
204-
except StopIteration:
205-
return None
210+
socket = self._socket_pool.socket(addr_info[0], addr_info[1])
211+
except (OSError, RuntimeError) as exc:
212+
return exc
206213

207-
@property
208-
def open_sockets(self) -> int:
209-
"""Get the count of open sockets"""
210-
return len(self._open_sockets)
214+
if is_ssl:
215+
socket = ssl_context.wrap_socket(socket, server_hostname=host)
216+
connect_host = host
217+
else:
218+
connect_host = addr_info[-1][0]
219+
socket.settimeout(timeout) # socket read timeout
220+
221+
try:
222+
socket.connect((connect_host, port))
223+
except (MemoryError, OSError) as exc:
224+
socket.close()
225+
return exc
226+
227+
return socket
211228

212229
@property
213-
def freeable_open_sockets(self) -> int:
230+
def available_socket_count(self) -> int:
214231
"""Get the count of freeable open sockets"""
215-
return len(
216-
[socket for socket, free in self._available_socket.items() if free is True]
217-
)
232+
return len(self._available_sockets)
233+
234+
@property
235+
def managed_socket_count(self) -> int:
236+
"""Get the count of open sockets"""
237+
return len(self._managed_socket_by_key)
218238

219239
def close_socket(self, socket: SocketType) -> None:
220240
"""Close a previously opened socket."""
221-
if socket not in self._open_sockets.values():
241+
if socket not in self._managed_socket_by_key.values():
222242
raise RuntimeError("Socket not managed")
223-
key = self._get_key_for_socket(socket)
224243
socket.close()
225-
del self._available_socket[socket]
226-
del self._open_sockets[key]
244+
key = self._managed_socket_by_socket.pop(socket)
245+
del self._managed_socket_by_key[key]
246+
if socket in self._available_sockets:
247+
self._available_sockets.remove(socket)
227248

228249
def free_socket(self, socket: SocketType) -> None:
229250
"""Mark a previously opened socket as available so it can be reused if needed."""
230-
if socket not in self._open_sockets.values():
251+
if socket not in self._managed_socket_by_key.values():
231252
raise RuntimeError("Socket not managed")
232-
self._available_socket[socket] = True
253+
self._available_sockets.add(socket)
233254

234-
# pylint: disable=too-many-branches,too-many-locals,too-many-statements
235255
def get_socket(
236256
self,
237257
host: str,
@@ -247,10 +267,10 @@ def get_socket(
247267
if session_id:
248268
session_id = str(session_id)
249269
key = (host, port, proto, session_id)
250-
if key in self._open_sockets:
251-
socket = self._open_sockets[key]
252-
if self._available_socket[socket]:
253-
self._available_socket[socket] = False
270+
if key in self._managed_socket_by_key:
271+
socket = self._managed_socket_by_key[key]
272+
if socket in self._available_sockets:
273+
self._available_sockets.remove(socket)
254274
return socket
255275

256276
raise RuntimeError(f"Socket already connected to {proto}//{host}:{port}")
@@ -266,54 +286,22 @@ def get_socket(
266286
host, port, 0, self._socket_pool.SOCK_STREAM
267287
)[0]
268288

269-
try_count = 0
270-
socket = None
271-
last_exc = None
272-
while try_count < 2 and socket is None:
273-
try_count += 1
274-
if try_count > 1:
275-
if any(
276-
socket
277-
for socket, free in self._available_socket.items()
278-
if free is True
279-
):
280-
self._free_sockets()
281-
else:
282-
break
283-
284-
try:
285-
socket = self._socket_pool.socket(addr_info[0], addr_info[1])
286-
except OSError as exc:
287-
last_exc = exc
288-
continue
289-
except RuntimeError as exc:
290-
last_exc = exc
291-
continue
292-
293-
if is_ssl:
294-
socket = ssl_context.wrap_socket(socket, server_hostname=host)
295-
connect_host = host
296-
else:
297-
connect_host = addr_info[-1][0]
298-
socket.settimeout(timeout) # socket read timeout
299-
300-
try:
301-
socket.connect((connect_host, port))
302-
except MemoryError as exc:
303-
last_exc = exc
304-
socket.close()
305-
socket = None
306-
except OSError as exc:
307-
last_exc = exc
308-
socket.close()
309-
socket = None
310-
311-
if socket is None:
312-
raise RuntimeError(f"Error connecting socket: {last_exc}") from last_exc
313-
314-
self._available_socket[socket] = False
315-
self._open_sockets[key] = socket
316-
return socket
289+
result = self._get_connected_socket(
290+
addr_info, host, port, timeout, is_ssl, ssl_context
291+
)
292+
if isinstance(result, Exception):
293+
# Got an error, if there are any available sockets, free them and try again
294+
if self.available_socket_count:
295+
self._free_sockets()
296+
result = self._get_connected_socket(
297+
addr_info, host, port, timeout, is_ssl, ssl_context
298+
)
299+
if isinstance(result, Exception):
300+
raise RuntimeError(f"Error connecting socket: {result}") from result
301+
302+
self._managed_socket_by_key[key] = result
303+
self._managed_socket_by_socket[result] = key
304+
return result
317305

318306

319307
# global helpers

examples/connectionmanager_helpers.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
connection_manager = adafruit_connection_manager.get_connection_manager(pool)
2828
print("-" * 40)
2929
print("Nothing yet opened")
30-
print(f"Open Sockets: {connection_manager.open_sockets}")
31-
print(f"Freeable Open Sockets: {connection_manager.freeable_open_sockets}")
30+
print(f"Open Sockets: {connection_manager.managed_socket_count}")
31+
print(f"Freeable Open Sockets: {connection_manager.available_socket_count}")
3232

3333
# make request
3434
print("-" * 40)
@@ -39,23 +39,23 @@
3939

4040
print("-" * 40)
4141
print("1 request, opened and freed")
42-
print(f"Open Sockets: {connection_manager.open_sockets}")
43-
print(f"Freeable Open Sockets: {connection_manager.freeable_open_sockets}")
42+
print(f"Open Sockets: {connection_manager.managed_socket_count}")
43+
print(f"Freeable Open Sockets: {connection_manager.available_socket_count}")
4444

4545
print("-" * 40)
4646
print(f"Fetching from {TEXT_URL} not in a context handler")
4747
response = requests.get(TEXT_URL)
4848

4949
print("-" * 40)
5050
print("1 request, opened but not freed")
51-
print(f"Open Sockets: {connection_manager.open_sockets}")
52-
print(f"Freeable Open Sockets: {connection_manager.freeable_open_sockets}")
51+
print(f"Open Sockets: {connection_manager.managed_socket_count}")
52+
print(f"Freeable Open Sockets: {connection_manager.available_socket_count}")
5353

5454
print("-" * 40)
5555
print("Closing everything in the pool")
5656
adafruit_connection_manager.connection_manager_close_all(pool)
5757

5858
print("-" * 40)
5959
print("Everything closed")
60-
print(f"Open Sockets: {connection_manager.open_sockets}")
61-
print(f"Freeable Open Sockets: {connection_manager.freeable_open_sockets}")
60+
print(f"Open Sockets: {connection_manager.managed_socket_count}")
61+
print(f"Freeable Open Sockets: {connection_manager.available_socket_count}")

tests/close_socket_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ def test_close_socket():
2121
socket = connection_manager.get_socket(mocket.MOCK_HOST_1, 80, "http:")
2222
key = (mocket.MOCK_HOST_1, 80, "http:", None)
2323
assert socket == mock_socket_1
24-
assert socket in connection_manager._available_socket
25-
assert key in connection_manager._open_sockets
24+
assert socket not in connection_manager._available_sockets
25+
assert key in connection_manager._managed_socket_by_key
2626

2727
# validate socket is no longer tracked
2828
connection_manager.close_socket(socket)
29-
assert socket not in connection_manager._available_socket
30-
assert key not in connection_manager._open_sockets
29+
assert socket not in connection_manager._available_sockets
30+
assert key not in connection_manager._managed_socket_by_key
3131

3232

3333
def test_close_socket_not_managed():

tests/connection_manager_close_all_test.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,29 @@ def test_connection_manager_close_all_all():
1818
connection_manager_1 = adafruit_connection_manager.get_connection_manager(
1919
mock_pool_1
2020
)
21-
assert connection_manager_1.open_sockets == 0
22-
assert connection_manager_1.freeable_open_sockets == 0
21+
assert connection_manager_1.managed_socket_count == 0
22+
assert connection_manager_1.available_socket_count == 0
2323
connection_manager_2 = adafruit_connection_manager.get_connection_manager(
2424
mock_pool_2
2525
)
26-
assert connection_manager_2.open_sockets == 0
27-
assert connection_manager_2.freeable_open_sockets == 0
26+
assert connection_manager_2.managed_socket_count == 0
27+
assert connection_manager_2.available_socket_count == 0
2828
assert len(adafruit_connection_manager._global_connection_managers) == 2
2929

3030
socket_1 = connection_manager_1.get_socket(mocket.MOCK_HOST_1, 80, "http:")
31-
assert connection_manager_1.open_sockets == 1
32-
assert connection_manager_1.freeable_open_sockets == 0
33-
assert connection_manager_2.open_sockets == 0
34-
assert connection_manager_2.freeable_open_sockets == 0
31+
assert connection_manager_1.managed_socket_count == 1
32+
assert connection_manager_1.available_socket_count == 0
33+
assert connection_manager_2.managed_socket_count == 0
34+
assert connection_manager_2.available_socket_count == 0
3535
socket_2 = connection_manager_2.get_socket(mocket.MOCK_HOST_1, 80, "http:")
36-
assert connection_manager_2.open_sockets == 1
37-
assert connection_manager_2.freeable_open_sockets == 0
36+
assert connection_manager_2.managed_socket_count == 1
37+
assert connection_manager_2.available_socket_count == 0
3838

3939
adafruit_connection_manager.connection_manager_close_all()
40-
assert connection_manager_1.open_sockets == 0
41-
assert connection_manager_1.freeable_open_sockets == 0
42-
assert connection_manager_2.open_sockets == 0
43-
assert connection_manager_2.freeable_open_sockets == 0
40+
assert connection_manager_1.managed_socket_count == 0
41+
assert connection_manager_1.available_socket_count == 0
42+
assert connection_manager_2.managed_socket_count == 0
43+
assert connection_manager_2.available_socket_count == 0
4444
socket_1.close.assert_called_once()
4545
socket_2.close.assert_called_once()
4646

@@ -53,29 +53,29 @@ def test_connection_manager_close_all_single():
5353
connection_manager_1 = adafruit_connection_manager.get_connection_manager(
5454
mock_pool_1
5555
)
56-
assert connection_manager_1.open_sockets == 0
57-
assert connection_manager_1.freeable_open_sockets == 0
56+
assert connection_manager_1.managed_socket_count == 0
57+
assert connection_manager_1.available_socket_count == 0
5858
connection_manager_2 = adafruit_connection_manager.get_connection_manager(
5959
mock_pool_2
6060
)
61-
assert connection_manager_2.open_sockets == 0
62-
assert connection_manager_2.freeable_open_sockets == 0
61+
assert connection_manager_2.managed_socket_count == 0
62+
assert connection_manager_2.available_socket_count == 0
6363
assert len(adafruit_connection_manager._global_connection_managers) == 2
6464

6565
socket_1 = connection_manager_1.get_socket(mocket.MOCK_HOST_1, 80, "http:")
66-
assert connection_manager_1.open_sockets == 1
67-
assert connection_manager_1.freeable_open_sockets == 0
68-
assert connection_manager_2.open_sockets == 0
69-
assert connection_manager_2.freeable_open_sockets == 0
66+
assert connection_manager_1.managed_socket_count == 1
67+
assert connection_manager_1.available_socket_count == 0
68+
assert connection_manager_2.managed_socket_count == 0
69+
assert connection_manager_2.available_socket_count == 0
7070
socket_2 = connection_manager_2.get_socket(mocket.MOCK_HOST_1, 80, "http:")
71-
assert connection_manager_2.open_sockets == 1
72-
assert connection_manager_2.freeable_open_sockets == 0
71+
assert connection_manager_2.managed_socket_count == 1
72+
assert connection_manager_2.available_socket_count == 0
7373

7474
adafruit_connection_manager.connection_manager_close_all(mock_pool_1)
75-
assert connection_manager_1.open_sockets == 0
76-
assert connection_manager_1.freeable_open_sockets == 0
77-
assert connection_manager_2.open_sockets == 1
78-
assert connection_manager_2.freeable_open_sockets == 0
75+
assert connection_manager_1.managed_socket_count == 0
76+
assert connection_manager_1.available_socket_count == 0
77+
assert connection_manager_2.managed_socket_count == 1
78+
assert connection_manager_2.available_socket_count == 0
7979
socket_1.close.assert_called_once()
8080
socket_2.close.assert_not_called()
8181

0 commit comments

Comments
 (0)