aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/async-timeout/async_timeout
diff options
context:
space:
mode:
authorrekby <rekby@ydb.tech>2023-12-14 16:56:50 +0300
committerrekby <rekby@ydb.tech>2023-12-14 18:09:44 +0300
commitb2b2bb5997507072ca64548efe64447dd6395426 (patch)
treebbfbf77d11f1972c93ae4101fe561fd440d6ad6a /contrib/python/async-timeout/async_timeout
parent8b8678a6a4f57c62e348cdad8afd3849011a5f11 (diff)
downloadydb-b2b2bb5997507072ca64548efe64447dd6395426.tar.gz
KIKIMR-19900 switch arcadia to python ydb sdk from contrib
Этот PR создан скриптом - для переключения зависимостей на python ydb sdk с версии внутри ydb на код, приезжающий через контриб. Код в обеих версиях одинаковый, так что поломок/изменения функционала на ожидается. На всякий случай посмотрите свои проекты и если будут возражения пишите сюда в issues или в тикет KIKIMR-19900. Если всё ок - шипните, для определённости. При отсутствии блокеров PR будет перегенерирован и влит с force-мёрджем в четверг, 14 декабря.
Diffstat (limited to 'contrib/python/async-timeout/async_timeout')
-rw-r--r--contrib/python/async-timeout/async_timeout/__init__.py239
-rw-r--r--contrib/python/async-timeout/async_timeout/py.typed1
2 files changed, 240 insertions, 0 deletions
diff --git a/contrib/python/async-timeout/async_timeout/__init__.py b/contrib/python/async-timeout/async_timeout/__init__.py
new file mode 100644
index 0000000000..1ffb069fce
--- /dev/null
+++ b/contrib/python/async-timeout/async_timeout/__init__.py
@@ -0,0 +1,239 @@
+import asyncio
+import enum
+import sys
+import warnings
+from types import TracebackType
+from typing import Optional, Type
+
+
+if sys.version_info >= (3, 8):
+ from typing import final
+else:
+ from typing_extensions import final
+
+
+if sys.version_info >= (3, 11):
+
+ def _uncancel_task(task: "asyncio.Task[object]") -> None:
+ task.uncancel()
+
+else:
+
+ def _uncancel_task(task: "asyncio.Task[object]") -> None:
+ pass
+
+
+__version__ = "4.0.3"
+
+
+__all__ = ("timeout", "timeout_at", "Timeout")
+
+
+def timeout(delay: Optional[float]) -> "Timeout":
+ """timeout context manager.
+
+ Useful in cases when you want to apply timeout logic around block
+ of code or in cases when asyncio.wait_for is not suitable. For example:
+
+ >>> async with timeout(0.001):
+ ... async with aiohttp.get('https://github.com') as r:
+ ... await r.text()
+
+
+ delay - value in seconds or None to disable timeout logic
+ """
+ loop = asyncio.get_running_loop()
+ if delay is not None:
+ deadline = loop.time() + delay # type: Optional[float]
+ else:
+ deadline = None
+ return Timeout(deadline, loop)
+
+
+def timeout_at(deadline: Optional[float]) -> "Timeout":
+ """Schedule the timeout at absolute time.
+
+ deadline argument points on the time in the same clock system
+ as loop.time().
+
+ Please note: it is not POSIX time but a time with
+ undefined starting base, e.g. the time of the system power on.
+
+ >>> async with timeout_at(loop.time() + 10):
+ ... async with aiohttp.get('https://github.com') as r:
+ ... await r.text()
+
+
+ """
+ loop = asyncio.get_running_loop()
+ return Timeout(deadline, loop)
+
+
+class _State(enum.Enum):
+ INIT = "INIT"
+ ENTER = "ENTER"
+ TIMEOUT = "TIMEOUT"
+ EXIT = "EXIT"
+
+
+@final
+class Timeout:
+ # Internal class, please don't instantiate it directly
+ # Use timeout() and timeout_at() public factories instead.
+ #
+ # Implementation note: `async with timeout()` is preferred
+ # over `with timeout()`.
+ # While technically the Timeout class implementation
+ # doesn't need to be async at all,
+ # the `async with` statement explicitly points that
+ # the context manager should be used from async function context.
+ #
+ # This design allows to avoid many silly misusages.
+ #
+ # TimeoutError is raised immediately when scheduled
+ # if the deadline is passed.
+ # The purpose is to time out as soon as possible
+ # without waiting for the next await expression.
+
+ __slots__ = ("_deadline", "_loop", "_state", "_timeout_handler", "_task")
+
+ def __init__(
+ self, deadline: Optional[float], loop: asyncio.AbstractEventLoop
+ ) -> None:
+ self._loop = loop
+ self._state = _State.INIT
+
+ self._task: Optional["asyncio.Task[object]"] = None
+ self._timeout_handler = None # type: Optional[asyncio.Handle]
+ if deadline is None:
+ self._deadline = None # type: Optional[float]
+ else:
+ self.update(deadline)
+
+ def __enter__(self) -> "Timeout":
+ warnings.warn(
+ "with timeout() is deprecated, use async with timeout() instead",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ self._do_enter()
+ return self
+
+ def __exit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> Optional[bool]:
+ self._do_exit(exc_type)
+ return None
+
+ async def __aenter__(self) -> "Timeout":
+ self._do_enter()
+ return self
+
+ async def __aexit__(
+ self,
+ exc_type: Optional[Type[BaseException]],
+ exc_val: Optional[BaseException],
+ exc_tb: Optional[TracebackType],
+ ) -> Optional[bool]:
+ self._do_exit(exc_type)
+ return None
+
+ @property
+ def expired(self) -> bool:
+ """Is timeout expired during execution?"""
+ return self._state == _State.TIMEOUT
+
+ @property
+ def deadline(self) -> Optional[float]:
+ return self._deadline
+
+ def reject(self) -> None:
+ """Reject scheduled timeout if any."""
+ # cancel is maybe better name but
+ # task.cancel() raises CancelledError in asyncio world.
+ if self._state not in (_State.INIT, _State.ENTER):
+ raise RuntimeError(f"invalid state {self._state.value}")
+ self._reject()
+
+ def _reject(self) -> None:
+ self._task = None
+ if self._timeout_handler is not None:
+ self._timeout_handler.cancel()
+ self._timeout_handler = None
+
+ def shift(self, delay: float) -> None:
+ """Advance timeout on delay seconds.
+
+ The delay can be negative.
+
+ Raise RuntimeError if shift is called when deadline is not scheduled
+ """
+ deadline = self._deadline
+ if deadline is None:
+ raise RuntimeError("cannot shift timeout if deadline is not scheduled")
+ self.update(deadline + delay)
+
+ def update(self, deadline: float) -> None:
+ """Set deadline to absolute value.
+
+ deadline argument points on the time in the same clock system
+ as loop.time().
+
+ If new deadline is in the past the timeout is raised immediately.
+
+ Please note: it is not POSIX time but a time with
+ undefined starting base, e.g. the time of the system power on.
+ """
+ if self._state == _State.EXIT:
+ raise RuntimeError("cannot reschedule after exit from context manager")
+ if self._state == _State.TIMEOUT:
+ raise RuntimeError("cannot reschedule expired timeout")
+ if self._timeout_handler is not None:
+ self._timeout_handler.cancel()
+ self._deadline = deadline
+ if self._state != _State.INIT:
+ self._reschedule()
+
+ def _reschedule(self) -> None:
+ assert self._state == _State.ENTER
+ deadline = self._deadline
+ if deadline is None:
+ return
+
+ now = self._loop.time()
+ if self._timeout_handler is not None:
+ self._timeout_handler.cancel()
+
+ self._task = asyncio.current_task()
+ if deadline <= now:
+ self._timeout_handler = self._loop.call_soon(self._on_timeout)
+ else:
+ self._timeout_handler = self._loop.call_at(deadline, self._on_timeout)
+
+ def _do_enter(self) -> None:
+ if self._state != _State.INIT:
+ raise RuntimeError(f"invalid state {self._state.value}")
+ self._state = _State.ENTER
+ self._reschedule()
+
+ def _do_exit(self, exc_type: Optional[Type[BaseException]]) -> None:
+ if exc_type is asyncio.CancelledError and self._state == _State.TIMEOUT:
+ assert self._task is not None
+ _uncancel_task(self._task)
+ self._timeout_handler = None
+ self._task = None
+ raise asyncio.TimeoutError
+ # timeout has not expired
+ self._state = _State.EXIT
+ self._reject()
+ return None
+
+ def _on_timeout(self) -> None:
+ assert self._task is not None
+ self._task.cancel()
+ self._state = _State.TIMEOUT
+ # drop the reference early
+ self._timeout_handler = None
diff --git a/contrib/python/async-timeout/async_timeout/py.typed b/contrib/python/async-timeout/async_timeout/py.typed
new file mode 100644
index 0000000000..3b94f91573
--- /dev/null
+++ b/contrib/python/async-timeout/async_timeout/py.typed
@@ -0,0 +1 @@
+Placeholder