33# SPDX-License-Identifier: MIT
44
55"""
6- `adafruit_esp32spi_socket `
6+ `adafruit_esp32spi_socketpool `
77================================================================================
88
99A 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
1624import time
1725import gc
1826from 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
0 commit comments