Skip to content

Commit 35fd193

Browse files
committed
Convert to SocketPool
1 parent 956d6a0 commit 35fd193

File tree

5 files changed

+98
-74
lines changed

5 files changed

+98
-74
lines changed

adafruit_esp32spi/adafruit_esp32spi_socket.py renamed to adafruit_esp32spi/adafruit_esp32spi_socketpool.py

+85-61
Original file line numberDiff line numberDiff line change
@@ -3,78 +3,103 @@
33
# SPDX-License-Identifier: MIT
44

55
"""
6-
`adafruit_esp32spi_socket`
6+
`adafruit_esp32spi_socketpool`
77
================================================================================
88
99
A socket compatible interface thru the ESP SPI command set
1010
1111
* Author(s): ladyada
1212
"""
13+
from __future__ import annotations
14+
15+
try:
16+
from typing import TYPE_CHECKING, Optional
17+
18+
if TYPE_CHECKING:
19+
from esp32spi.adafruit_esp32spi import ESP_SPIcontrol
20+
except ImportError:
21+
pass
1322

14-
# pylint: disable=no-name-in-module
1523

1624
import time
1725
import gc
1826
from micropython import const
19-
from adafruit_esp32spi import adafruit_esp32spi
27+
from adafruit_esp32spi import adafruit_esp32spi as esp32spi
2028

21-
_the_interface = None # pylint: disable=invalid-name
29+
_global_socketpool = {}
2230

2331

24-
def set_interface(iface):
25-
"""Helper to set the global internet interface"""
26-
global _the_interface # pylint: disable=global-statement, invalid-name
27-
_the_interface = iface
32+
class SocketPoolContants: # pylint: disable=too-few-public-methods
33+
"""Helper class for the constants that are needed everywhere"""
2834

35+
SOCK_STREAM = const(0)
36+
SOCK_DGRAM = const(1)
37+
AF_INET = const(2)
38+
NO_SOCKET_AVAIL = const(255)
2939

30-
SOCK_STREAM = const(0)
31-
SOCK_DGRAM = const(1)
32-
AF_INET = const(2)
33-
NO_SOCKET_AVAIL = const(255)
40+
MAX_PACKET = const(4000)
3441

35-
MAX_PACKET = const(4000)
3642

43+
class SocketPool(SocketPoolContants):
44+
"""ESP32SPI SocketPool library"""
3745

38-
# pylint: disable=too-many-arguments, unused-argument
39-
def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0):
40-
"""Given a hostname and a port name, return a 'socket.getaddrinfo'
41-
compatible list of tuples. Honestly, we ignore anything but host & port"""
42-
if not isinstance(port, int):
43-
raise ValueError("Port must be an integer")
44-
ipaddr = _the_interface.get_host_by_name(host)
45-
return [(AF_INET, socktype, proto, "", (ipaddr, port))]
46+
def __new__(cls, iface: ESP_SPIcontrol):
47+
# We want to make sure to return the same pool for the same interface
48+
if iface not in _global_socketpool:
49+
_global_socketpool[iface] = object.__new__(cls)
50+
return _global_socketpool[iface]
4651

52+
def __init__(self, iface: ESP_SPIcontrol):
53+
self._interface = iface
4754

48-
# pylint: enable=too-many-arguments, unused-argument
55+
def getaddrinfo( # pylint: disable=too-many-arguments,unused-argument
56+
self, host, port, family=0, socktype=0, proto=0, flags=0
57+
):
58+
"""Given a hostname and a port name, return a 'socket.getaddrinfo'
59+
compatible list of tuples. Honestly, we ignore anything but host & port"""
60+
if not isinstance(port, int):
61+
raise ValueError("Port must be an integer")
62+
ipaddr = self._interface.get_host_by_name(host)
63+
return [(SocketPoolContants.AF_INET, socktype, proto, "", (ipaddr, port))]
64+
65+
def socket( # pylint: disable=redefined-builtin
66+
self,
67+
family=SocketPoolContants.AF_INET,
68+
type=SocketPoolContants.SOCK_STREAM,
69+
proto=0,
70+
fileno=None,
71+
):
72+
"""Create a new socket and return it"""
73+
return Socket(self, family, type, proto, fileno)
4974

5075

51-
# pylint: disable=unused-argument, redefined-builtin, invalid-name
52-
class socket:
76+
class Socket:
5377
"""A simplified implementation of the Python 'socket' class, for connecting
5478
through an interface to a remote device"""
5579

56-
# pylint: disable=too-many-arguments
57-
def __init__(
58-
self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, socknum=None
80+
def __init__( # pylint: disable=redefined-builtin,too-many-arguments,unused-argument
81+
self,
82+
socket_pool: SocketPool,
83+
family: int = SocketPool.AF_INET,
84+
type: int = SocketPool.SOCK_STREAM,
85+
proto: int = 0,
86+
fileno: Optional[int] = None,
5987
):
60-
if family != AF_INET:
88+
if family != SocketPool.AF_INET:
6189
raise ValueError("Only AF_INET family supported")
90+
self._socket_pool = socket_pool
91+
self._interface = self._socket_pool._interface
6292
self._type = type
6393
self._buffer = b""
64-
self._socknum = socknum if socknum else _the_interface.get_socket()
94+
self._socknum = self._interface.get_socket()
6595
self.settimeout(0)
6696

67-
# pylint: enable=too-many-arguments
68-
6997
def __enter__(self):
7098
return self
7199

72100
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
73101
self.close()
74-
while (
75-
_the_interface.socket_status(self._socknum)
76-
!= adafruit_esp32spi.SOCKET_CLOSED
77-
):
102+
while self._interface.socket_status(self._socknum) != esp32spi.SOCKET_CLOSED:
78103
pass
79104

80105
def connect(self, address, conntype=None):
@@ -83,20 +108,20 @@ def connect(self, address, conntype=None):
83108
depending on the underlying interface"""
84109
host, port = address
85110
if conntype is None:
86-
conntype = _the_interface.TCP_MODE
87-
if not _the_interface.socket_connect(
111+
conntype = self._interface.TCP_MODE
112+
if not self._interface.socket_connect(
88113
self._socknum, host, port, conn_mode=conntype
89114
):
90115
raise ConnectionError("Failed to connect to host", host)
91116
self._buffer = b""
92117

93-
def send(self, data): # pylint: disable=no-self-use
118+
def send(self, data):
94119
"""Send some data to the socket."""
95-
if self._type is SOCK_DGRAM:
96-
conntype = _the_interface.UDP_MODE
120+
if self._type is SocketPool.SOCK_DGRAM:
121+
conntype = self._interface.UDP_MODE
97122
else:
98-
conntype = _the_interface.TCP_MODE
99-
_the_interface.socket_write(self._socknum, data, conn_mode=conntype)
123+
conntype = self._interface.TCP_MODE
124+
self._interface.socket_write(self._socknum, data, conn_mode=conntype)
100125
gc.collect()
101126

102127
def recv(self, bufsize: int) -> bytes:
@@ -140,7 +165,7 @@ def recv_into(self, buffer, nbytes: int = 0):
140165
num_avail = self._available()
141166
if num_avail > 0:
142167
last_read_time = time.monotonic()
143-
bytes_read = _the_interface.socket_read(
168+
bytes_read = self._interface.socket_read(
144169
self._socknum, min(num_to_read, num_avail)
145170
)
146171
buffer[num_read : num_read + len(bytes_read)] = bytes_read
@@ -162,43 +187,42 @@ def settimeout(self, value):
162187

163188
def _available(self):
164189
"""Returns how many bytes of data are available to be read (up to the MAX_PACKET length)"""
165-
if self._socknum != NO_SOCKET_AVAIL:
166-
return min(_the_interface.socket_available(self._socknum), MAX_PACKET)
190+
if self._socknum != SocketPool.NO_SOCKET_AVAIL:
191+
return min(
192+
self._interface.socket_available(self._socknum), SocketPool.MAX_PACKET
193+
)
167194
return 0
168195

169196
def _connected(self):
170197
"""Whether or not we are connected to the socket"""
171-
if self._socknum == NO_SOCKET_AVAIL:
198+
if self._socknum == SocketPool.NO_SOCKET_AVAIL:
172199
return False
173200
if self._available():
174201
return True
175-
status = _the_interface.socket_status(self._socknum)
202+
status = self._interface.socket_status(self._socknum)
176203
result = status not in (
177-
adafruit_esp32spi.SOCKET_LISTEN,
178-
adafruit_esp32spi.SOCKET_CLOSED,
179-
adafruit_esp32spi.SOCKET_FIN_WAIT_1,
180-
adafruit_esp32spi.SOCKET_FIN_WAIT_2,
181-
adafruit_esp32spi.SOCKET_TIME_WAIT,
182-
adafruit_esp32spi.SOCKET_SYN_SENT,
183-
adafruit_esp32spi.SOCKET_SYN_RCVD,
184-
adafruit_esp32spi.SOCKET_CLOSE_WAIT,
204+
esp32spi.SOCKET_LISTEN,
205+
esp32spi.SOCKET_CLOSED,
206+
esp32spi.SOCKET_FIN_WAIT_1,
207+
esp32spi.SOCKET_FIN_WAIT_2,
208+
esp32spi.SOCKET_TIME_WAIT,
209+
esp32spi.SOCKET_SYN_SENT,
210+
esp32spi.SOCKET_SYN_RCVD,
211+
esp32spi.SOCKET_CLOSE_WAIT,
185212
)
186213
if not result:
187214
self.close()
188-
self._socknum = NO_SOCKET_AVAIL
215+
self._socknum = SocketPool.NO_SOCKET_AVAIL
189216
return result
190217

191218
def close(self):
192219
"""Close the socket, after reading whatever remains"""
193-
_the_interface.socket_close(self._socknum)
220+
self._interface.socket_close(self._socknum)
194221

195222

196-
class timeout(TimeoutError):
223+
class timeout(TimeoutError): # pylint: disable=invalid-name
197224
"""TimeoutError class. An instance of this error will be raised by recv_into() if
198225
the timeout has elapsed and we haven't received any data yet."""
199226

200227
def __init__(self, msg):
201228
super().__init__(msg)
202-
203-
204-
# pylint: enable=unused-argument, redefined-builtin, invalid-name

docs/api.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
.. automodule:: adafruit_esp32spi.adafruit_esp32spi
88
:members:
99

10-
.. automodule:: adafruit_esp32spi.adafruit_esp32spi_socket
10+
.. automodule:: adafruit_esp32spi.adafruit_esp32spi_socketpool
1111
:members:
1212

1313
.. automodule:: adafruit_esp32spi.adafruit_esp32spi_wifimanager

examples/esp32spi_ipconfig.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import board
77
import busio
88
from digitalio import DigitalInOut
9-
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
9+
import adafruit_esp32spi.adafruit_esp32spi_socketpool as socketpool
1010
from adafruit_esp32spi import adafruit_esp32spi
1111

1212
# Get wifi details and more from a settings.toml file
@@ -51,9 +51,9 @@
5151
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
5252

5353
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
54-
socket.set_interface(esp)
54+
pool = socketpool.SocketPool(esp)
5555

56-
s_in = socket.socket(type=socket.SOCK_DGRAM)
56+
s_in = pool.socket(type=pool.SOCK_DGRAM)
5757
s_in.settimeout(UDP_TIMEOUT)
5858
print("set hostname:", HOSTNAME)
5959
esp.set_hostname(HOSTNAME)
@@ -96,7 +96,7 @@
9696
print("My IP address is", esp.pretty_ip(esp.ip_address))
9797
print("udp in addr: ", UDP_IN_ADDR, UDP_IN_PORT)
9898

99-
socketaddr_udp_in = socket.getaddrinfo(UDP_IN_ADDR, UDP_IN_PORT)[0][4]
99+
socketaddr_udp_in = pool.getaddrinfo(UDP_IN_ADDR, UDP_IN_PORT)[0][4]
100100
s_in.connect(socketaddr_udp_in, conntype=esp.UDP_MODE)
101101
print("connected local UDP")
102102

examples/esp32spi_tcp_client.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import busio
77
from digitalio import DigitalInOut
88
from adafruit_esp32spi import adafruit_esp32spi
9-
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
9+
import adafruit_esp32spi.adafruit_esp32spi_socketpool as socketpool
1010

1111
# Get wifi details and more from a settings.toml file
1212
# tokens used by this Demo: CIRCUITPY_WIFI_SSID, CIRCUITPY_WIFI_PASSWORD
@@ -48,9 +48,9 @@
4848
print("Server ping:", esp.ping(HOST), "ms")
4949

5050
# create the socket
51-
socket.set_interface(esp)
52-
socketaddr = socket.getaddrinfo(HOST, PORT)[0][4]
53-
s = socket.socket()
51+
pool = socketpool.SocketPool(esp)
52+
socketaddr = pool.getaddrinfo(HOST, PORT)[0][4]
53+
s = pool.socket()
5454
s.settimeout(TIMEOUT)
5555

5656
print("Connecting")

examples/esp32spi_udp_client.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import busio
99
from digitalio import DigitalInOut
1010
from adafruit_esp32spi import adafruit_esp32spi
11-
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
11+
import adafruit_esp32spi.adafruit_esp32spi_socketpool as socketpool
1212

1313
# Get wifi details and more from a settings.toml file
1414
# tokens used by this Demo: CIRCUITPY_WIFI_SSID, CIRCUITPY_WIFI_PASSWORD
@@ -51,9 +51,9 @@
5151
print("Server ping:", esp.ping(HOST), "ms")
5252

5353
# create the socket
54-
socket.set_interface(esp)
55-
socketaddr = socket.getaddrinfo(HOST, PORT)[0][4]
56-
s = socket.socket(type=socket.SOCK_DGRAM)
54+
pool = socketpool.SocketPool(esp)
55+
socketaddr = pool.getaddrinfo(HOST, PORT)[0][4]
56+
s = pool.socket(type=pool.SOCK_DGRAM)
5757

5858
s.settimeout(TIMEOUT)
5959

0 commit comments

Comments
 (0)