You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Feature request: Allow setting custom connection timeout in SMPClient() #69
asyncwithSMPClient(SMPBLETransport(), "00:11:22:33:44:55") asclient:
# do stuff
the timeout on the BLE scan when using an address is only 5 seconds and I don't see any way to modify it. 5 seconds is awfully short for BLE devices which have slow advertising intervals and when the environment has a lot of Bluetooth traffic. The specification allows the advertising interval to go up to 10 seconds.
I'm mainly concerned about the connect call which isn't directly called by my own script:
@gudnimg A good, non breaking, intermediate improvement may be to add a new asynccontextmanager. Context managers defined in this way can take arguments.
@JPHutchins Hi again 👋 , I took a look at this yesterday and can confirm this works well, this is all called inside a large main function where I have some custom argparse arguments like name and address.
importasynciofromcontextlibimportasynccontextmanagerfromcollections.abcimportAsyncGenerator@asynccontextmanagerasyncdefconnection(client: SMPClient, timeout_s: float=5.0) ->AsyncGenerator[SMPClient]:
awaitclient.connect(timeout_s)
try:
yieldclientexceptExceptionase:
print(e)
finally:
awaitclient.disconnect()
client=SMPClient(SMPBLETransport(), args.addressorargs.name)
asyncwithconnection(client, timeout_s=30.0):
# Performing FOTA over BLE
This @asynccontextmanager thing is pretty cool :)
I'm not sure what's the best approach for a PR if you're planning on changing the classes anyway (decouple them). But if I would make a PR, would it make senmse in this case with @asynccontextmanager to also define it as a static method (@staticmethod) inside the class? That way the function doesn't belong to the class instance.
@JPHutchins Hi again 👋 , I took a look at this yesterday and can confirm this works well, this is all called inside a large main function where I have some custom argparse arguments like name and address.
import asyncio
from contextlib import asynccontextmanager
from collections.abc import AsyncGenerator
client = SMPClient(SMPBLETransport(), args.address or args.name)
async with connection(client, timeout_s = 30.0):
# Performing FOTA over BLE
This @asynccontextmanager thing is pretty cool :)
I'm not sure what's the best approach for a PR if you're planning on changing the classes anyway (decouple them). But if I would make a PR, would it make senmse in this case with @asynccontextmanager to also define it as a static method (@staticmethod) inside the class? That way the function doesn't belong to the class instance.
Thanks for testing! I wouldn't wait for that rework 🤣!
I think that defining it in a static method or function outside the class makes sense. Putting it in a static method is more self-documenting as far as purpose, but also more verbose, and I don't have a strong opinion especially if the doc string contains a code block usage example.
Activity
gudnimg commentedon Jun 7, 2025
I have something like this in mind. I think it is backwards compatible. 🤔
Edit:
No its not backwards compatible if one calls
connectdirectly and used the timeout parameter. Probably better to keep it.JPHutchins commentedon Jun 7, 2025
@gudnimg This is definitely is a problem. There's an architectural issue here, that we're working on with these breaking changes: #58
@GHF Had what I think is a good idea of removing the connect and disconnect method from the client entirely: #58 (comment)
The idea is that an smpclient exists in the context of a connected client and does not have to worry about the transport connect/disconnect effects.
Anyway, all transports accept a timeout parameter:
smpclient/smpclient/transport/__init__.py
Lines 12 to 21 in dcace1a
And then the client passes that to the transport:
smpclient/smpclient/__init__.py
Lines 95 to 102 in dcace1a
AFAICT you wouldn't be able to use the (existing) async context manager, as you've observed that it calls connect without arguments 😢.
Looks like smpmgr implemented this with
wait_for, probably done before the timeout arg was added:https://github.com/intercreate/smpmgr/blob/6651700ec538284036371602b5cafa1e2ebae94c/smpmgr/main.py#L147
https://github.com/intercreate/smpmgr/blob/6651700ec538284036371602b5cafa1e2ebae94c/smpmgr/common.py#L83-L91
JPHutchins commentedon Jun 7, 2025
@gudnimg A good, non breaking, intermediate improvement may be to add a new asynccontextmanager. Context managers defined in this way can take arguments.
LMK if you're able to test and PR, thanks!
Edit: realizing that this cannot (or should not) be an instance method. Probably doesn't need to even be in the class:
gudnimg commentedon Jun 8, 2025
Thanks for the reply @JPHutchins !
@asynccontextmanageris new to me, I’ll give it a try.Decoupling
SMPBLETransportandSMPClientsounds reasonable to me.gudnimg commentedon Jun 10, 2025
@JPHutchins Hi again 👋 , I took a look at this yesterday and can confirm this works well, this is all called inside a large
mainfunction where I have some customargparsearguments likenameandaddress.This
@asynccontextmanagerthing is pretty cool :)I'm not sure what's the best approach for a PR if you're planning on changing the classes anyway (decouple them). But if I would make a PR, would it make senmse in this case with
@asynccontextmanagerto also define it as a static method (@staticmethod) inside the class? That way the function doesn't belong to the class instance.JPHutchins commentedon Jun 10, 2025
Thanks for testing! I wouldn't wait for that rework 🤣!
I think that defining it in a static method or function outside the class makes sense. Putting it in a static method is more self-documenting as far as purpose, but also more verbose, and I don't have a strong opinion especially if the doc string contains a code block usage example.