-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make default timeout configurable #192
Conversation
for more information, see https://pre-commit.ci
Codecov Report
@@ Coverage Diff @@
## main #192 +/- ##
=======================================
- Coverage 89.9% 89.7% -0.3%
=======================================
Files 6 6
Lines 729 734 +5
Branches 154 156 +2
=======================================
+ Hits 656 659 +3
Misses 48 48
- Partials 25 27 +2
|
Thanks for your pull-request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Scott, thanks for opening this pull request. 👍 Let me have a look. :)
Great idea to add "layered" timeout (use argument if given; otherwise, use instance default). 👍
Overall, it looks good. :) I only have minor comments (see the review). Let me know if anything is unclear, or, if you have questions.
~Frederik
asyncio_mqtt/client.py
Outdated
@@ -250,6 +250,7 @@ def __init__( # noqa: C901 | |||
will: Will | None = None, | |||
clean_session: bool | None = None, | |||
transport: str = "tcp", | |||
timeout: int | float = 10, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose that we use timeout: float | None = None
. Note that int
is redundant as per PEP 484:
[...] when an argument is annotated as having type float, an argument of type int is acceptable; [...]
Thanks to @JonathanPlasse for pointing that out. 👍
Note that we use None
to mean "Give me the default value". It doesn't mean "no timeout". If you want "no timeout", use math.inf
instead. :)
@@ -360,6 +361,7 @@ def __init__( # noqa: C901 | |||
if socket_options is None: | |||
socket_options = () | |||
self._socket_options = tuple(socket_options) | |||
self.timeout = timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace with:
if timeout is None:
timeout = 10
self._timeout: float = timeout
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I intentionally made timeout
public. Did you intentionally change it to protected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that was out of habit. 😄 We can keep it public. 👍 Just make a comment about it like this:
# This property is intentionally public since it's safe to modify at any point during the lifetime
# of this instance. Aka: There are no race conditions.
self.timeout = timeout
Otherwise, I'll probably forget that it is "safe" and think it was left public by accident. 😅
asyncio_mqtt/client.py
Outdated
@@ -380,7 +382,7 @@ def _pending_calls(self) -> Generator[int, None, None]: | |||
yield from self._pending_unsubscribes.keys() | |||
yield from self._pending_publishes.keys() | |||
|
|||
async def connect(self, *, timeout: int = 10) -> None: | |||
async def connect(self, *, timeout: int | float | None = None) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea to have method-local overrides to timeout
. 👍
I propose that we use timeout: float | None = None
(see the previous comment).
Furthermore, I propose the following semantics (written in psedo-code here):
if timeout is None:
"Use the instance default (`self._timeout`)"
else:
"Use the `timeout` argument"
asyncio_mqtt/client.py
Outdated
@@ -404,15 +406,19 @@ async def connect(self, *, timeout: int = 10) -> None: | |||
# See: https://github.com/eclipse/paho.mqtt.python/blob/v1.5.0/src/paho/mqtt/client.py#L1770 | |||
except (OSError, mqtt.WebsocketConnectionError) as error: | |||
raise MqttError(str(error)) from None | |||
await self._wait_for(self._connected, timeout=timeout) | |||
await self._wait_for( | |||
self._connected, timeout=self.timeout if timeout is None else timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, good job with the "layered" timeout (use argument if given; otherwise, use instance default). Since we use that everywhere, let's move it inside _wait_for
itself (to remove redundancy).
Inside _wait_for
we have:
if timeout is None:
timeout = self._timeout
# Note that asyncio uses `None` to mean "No timeout". We use `math.inf`.
timeout_for_asyncio = None if timeout == math.inf else timeout
try:
return await asyncio.wait_for(fut, timeout=timeout_for_asyncio, **kwargs)
except ...
asyncio_mqtt/client.py
Outdated
rc = self._client.disconnect() | ||
# Early out on error | ||
if rc != mqtt.MQTT_ERR_SUCCESS: | ||
raise MqttCodeError(rc, "Could not disconnect") | ||
# Wait for acknowledgement | ||
await self._wait_for(self._disconnected, timeout=timeout) | ||
await self._wait_for( | ||
self._disconnected, timeout=self.timeout if timeout is None else timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment about _wait_for
.
asyncio_mqtt/client.py
Outdated
@@ -441,15 +447,17 @@ async def subscribe( | |||
] = asyncio.Future() | |||
with self._pending_call(mid, callback_result, self._pending_subscribes): | |||
# Wait for callback_result | |||
return await self._wait_for(callback_result, timeout=timeout) | |||
return await self._wait_for( | |||
callback_result, timeout=self.timeout if timeout is None else timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment about _wait_for
.
asyncio_mqtt/client.py
Outdated
await self._wait_for(confirmation.wait(), timeout=timeout) | ||
await self._wait_for( | ||
confirmation.wait(), | ||
timeout=self.timeout if timeout is None else timeout, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment about _wait_for
.
asyncio_mqtt/client.py
Outdated
await self._wait_for(confirmation.wait(), timeout=timeout) | ||
await self._wait_for( | ||
confirmation.wait(), | ||
timeout=self.timeout if timeout is None else timeout, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment about _wait_for
.
Credit where credit is due: Use of |
Hi skewty, thanks for the changes. 👍 It looks good to me except for one bug: The user can't disable the timeout. To allow the user to disable the timeout (by setting if timeout is None:
timeout = self._timeout
# Note that asyncio uses `None` to mean "No timeout". We use `math.inf`.
timeout_for_asyncio = None if timeout == math.inf else timeout
try:
return await asyncio.wait_for(fut, timeout=timeout_for_asyncio, **kwargs)
except ... Does it make sense? Let me know if you want to make the change. Otherwise, I can commit this PR as is and push the change myself. 👍 |
Please go ahead and merge what is here and make the fixes you want. 🎉 |
Done! Thank you for your contribution to asyncio-mqtt. 👍 |
fixes #116
NOTE:
timeout
field ofClient._wait_for
has typefloat
but default value all over code isint
.