adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.1k stars 1.22k forks source link

SocketPool requirements and documentation #9481

Open mMerlin opened 2 months ago

mMerlin commented 2 months ago

CircuitPython version

Adafruit CircuitPython 9.0.5 on 2024-05-22; Adafruit-Qualia-S3-RGB666 with ESP32S3
«Using 9.0.5 instead of 9.1.1 due to display issues (jitter) on the newer version for that board»

Code/REPL

import os
import time
import socketpool
import wifi
print(f'{wifi.radio.connected =}')
pool1 = socketpool.SocketPool(wifi.radio)
addr_info = pool1.getaddrinfo('0.adafruit.pool.ntp.org', 123)
print(f'pool1 {addr_info =}')
pool2 = socketpool.SocketPool(wifi.radio)
print(f'{pool1 is pool2 =}')
print(f'{pool1 == pool2 =}')
addr_info = pool2.getaddrinfo('pool.ntp.org', 123)
print(f'pool2 {addr_info =}')
print('separate pool instances both work')
print('Turn off the radio')
wifi.radio.enabled = False
time.sleep(10)
print(f'{wifi.radio.connected =}')
print(f'{wifi.radio.enabled =}')
pool3 = socketpool.SocketPool(wifi.radio)
print(f'{pool1 is pool3 =}')
print(f'{pool2 is pool3 =}')
print(f'{type(pool3.getaddrinfo) =}')
print('Turn on the radio')
wifi.radio.enabled = True
print(f'{wifi.radio.connected =}')
print(f'{wifi.radio.enabled =}')
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))
print(f'{wifi.radio.connected =}')
addr_info = pool3.getaddrinfo('time.google.com', 123)
print(f'pool3 {addr_info =}')

Behavior

main.py output:
wifi.radio.connected =True
pool1 addr_info =[(2, 1, 0, '', ('216.232.132.95', 123))]
pool1 is pool2 =False
pool1 == pool2 =False
pool2 addr_info =[(2, 1, 0, '', ('142.4.192.253', 123))]
separate pool instances both work
Turn off the radio
wifi.radio.connected =False
wifi.radio.enabled =False
pool1 is pool3 =False
pool2 is pool3 =False
type(pool3.getaddrinfo) =<class 'bound_method'>
Turn on the radio
wifi.radio.connected =False
wifi.radio.enabled =True
wifi.radio.connected =True
pool3 addr_info =[(2, 1, 0, '', ('216.239.35.8', 123))]

Code done running.

Description

I don't know if this is a code bug, a documentation error, or simply not understanding what the documentation means. The documentation at https://docs.circuitpython.org/en/latest/shared-bindings/socketpool/index.html#socketpool.SocketPool shows:

class socketpool.SocketPool(radio: wifi.Radio)
A pool of socket resources available for the given radio. Only one SocketPool can be created for each radio.
…
Create a new SocketPool object for the provided radio
Parameters:
radio (wifi.Radio) – The (connected) network hardware to associate with this SocketPool; currently, this will always be the object returned by wifi.radio

To me that says SocketPool should act like a singleton (one instance per Radio), or should fail when attempting to create a second instance. It also seems to say that wifi.radio needs to be connected to be able to create a SocketPool instance. None of that is true in my testing.

It is possible to create multiple SocketPool instances, That does not fail, and they are not singletons (pool1 is pool2 == False). Each instance is functional, in that .getaddressinfo(…) works for them. It is also possible to create a pool instance from wifi.radio while the radio is not connected and not enabled. As long as a later radio.connect(…) succeeds, the created pool works. The radio needs to be connected for the pool to be used, but that is not required to create the pool. A variant of that is to create a pool while the radio is connected, then disable the radio, enable it again, and connect. The pools still works. Disconnecting did not 'break' the pool. As long as it is not actually used while the radio is not connected.

Should SocketPool enforce a single instance per Radio? Not quite a singleton, if more than one Radio can ever be available on a single board. Should the documentation be adjusted to show that the network hardware needs to be connected to use the pool, but not to create it?

Additional information

No response

tannewt commented 2 months ago

These are good questions! We should figure it out for CP 10 because we may choose to break things.