Last active
June 15, 2020 17:38
-
-
Save carlos-jenkins/886351907cc8a758de28630b7056520c to your computer and use it in GitHub Desktop.
Just a generic polling function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from time import time | |
from asyncio import sleep | |
from .poll import NotReadyYet | |
async def poll( | |
func, | |
timeout_s=30.0, polling_s=5.0, | |
catchexcs=(NotReadyYet, ), pollcb=None, | |
): | |
""" | |
Poll until the polling function ``func`` returns. | |
:param function func: The polling function to call. | |
The function is called without arguments. | |
In case argument passing is required use a partial:: | |
from functools import partial | |
result = poll(partial(myfunc, myarg1, myarg2)) | |
:param float timeout_s: Maximum timeout, in seconds, for execution of the | |
polling. | |
:param float polling_s: Time in seconds to wait between calls to the | |
polling function. | |
:param tuple catchexcs: Collection of typed exceptions to catch to continue | |
polling. Any exception raised by the polling function that isn't included | |
in this tuple will be raised. By default, ``(Exception, )`` is used, which | |
will catch all exceptions except system exceptions. | |
:param function pollcb: Callback to call after each poll. Callback must | |
have a signature of:: | |
def mycallback(retrynum, exc): | |
return True | |
With: | |
:param int retrynum: Times the polling function has been called. | |
:param Exception exc: Exception raised by the polling function ``func``. | |
:return: ``True`` continue polling, ``False`` to stop right away. | |
:rtype: bool | |
:return: The return value of polling function ``func``, or ``None`` if the | |
polling callback ``pollcb`` returns ``False``. | |
:rtype: Same as ``func`` return. | |
:raises TimeoutError: if timeout is reached. | |
""" | |
end = time() + timeout_s | |
retrynum = 0 | |
exc = None | |
while True: | |
retrynum += 1 | |
try: | |
return await func() | |
except catchexcs as e: | |
exc = e | |
if pollcb is not None and not await pollcb(retrynum, exc): | |
return None | |
if end - time() >= polling_s: | |
await sleep(polling_s) | |
continue | |
raise TimeoutError( | |
'Timeout waiting for {}'.format(getattr(func, '__name__', str(func))) | |
) | |
__all__ = [ | |
'NotReadyYet', | |
'poll', | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from time import time, sleep | |
class NotReadyYet(Exception): | |
""" | |
Typed exception that functions to be polled can raise to continue polling. | |
""" | |
def poll( | |
func, | |
timeout_s=30.0, polling_s=5.0, | |
catchexcs=(NotReadyYet, ), pollcb=None, | |
): | |
""" | |
Poll until the function to be polled returns. | |
:param function func: The function to be polled. | |
The function is called without arguments. | |
In case argument passing is required use a partial:: | |
from functools import partial | |
result = poll(partial(myfunc, myarg1, myarg2)) | |
:param float timeout_s: Timeout, in seconds, for execution of the polling. | |
This is an approximate, real execution is between:: | |
timeout_s - polling_s <= execution <= timeout_s + time(func) + time(pollcb) | |
:param float polling_s: Time in seconds to wait between calls to the | |
polling function. | |
:param tuple catchexcs: Collection of typed exceptions to catch to continue | |
polling. Any exception raised by the polling function that isn't included | |
in this tuple will be raised. By default, ``(NotReadyYet, )`` is used so | |
the function to be polled will need to raise this exception to continue | |
execution. Alternatively, ``(Exception, )`` may be used to catch all | |
exceptions except interpreter exceptions. | |
:param function pollcb: Callback to call after each poll. Callback must | |
have a signature of:: | |
def mycallback(retrynum, exc): | |
return True | |
With: | |
:param int retrynum: Times the polling function has been called. | |
:param Exception exc: Exception raised by the polling function ``func``. | |
:return: ``True`` continue polling, ``False`` to stop right away. | |
:rtype: bool | |
:return: The return value of polling function ``func``, or ``None`` if the | |
polling callback ``pollcb`` returns ``False``. | |
:rtype: Same as ``func`` return. | |
:raises TimeoutError: if timeout is reached. | |
""" # noqa | |
end = time() + timeout_s | |
retrynum = 0 | |
exc = None | |
while True: | |
retrynum += 1 | |
try: | |
return func() | |
except catchexcs as e: | |
exc = e | |
if pollcb is not None and not pollcb(retrynum, exc): | |
return None | |
if end - time() >= polling_s: | |
sleep(polling_s) | |
continue | |
raise TimeoutError( | |
'Timeout waiting for {}'.format(getattr(func, '__name__', str(func))) | |
) | |
__all__ = [ | |
'NotReadyYet', | |
'poll', | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment