Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

timeout.py 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. # This code is originally sourced from the aio-libs project "async_timeout",
  2. # under the Apache 2.0 license. You may see the original project at
  3. # https://github.com/aio-libs/async-timeout
  4. # It is vendored here to reduce chain-dependencies on this library, and
  5. # modified slightly to remove some features we don't use.
  6. import asyncio
  7. import warnings
  8. from types import TracebackType
  9. from typing import Any # noqa
  10. from typing import Optional, Type
  11. class timeout:
  12. """timeout context manager.
  13. Useful in cases when you want to apply timeout logic around block
  14. of code or in cases when asyncio.wait_for is not suitable. For example:
  15. >>> with timeout(0.001):
  16. ... async with aiohttp.get('https://github.com') as r:
  17. ... await r.text()
  18. timeout - value in seconds or None to disable timeout logic
  19. loop - asyncio compatible event loop
  20. """
  21. def __init__(
  22. self,
  23. timeout: Optional[float],
  24. *,
  25. loop: Optional[asyncio.AbstractEventLoop] = None,
  26. ) -> None:
  27. self._timeout = timeout
  28. if loop is None:
  29. loop = asyncio.get_running_loop()
  30. else:
  31. warnings.warn(
  32. """The loop argument to timeout() is deprecated.""", DeprecationWarning
  33. )
  34. self._loop = loop
  35. self._task = None # type: Optional[asyncio.Task[Any]]
  36. self._cancelled = False
  37. self._cancel_handler = None # type: Optional[asyncio.Handle]
  38. self._cancel_at = None # type: Optional[float]
  39. def __enter__(self) -> "timeout":
  40. return self._do_enter()
  41. def __exit__(
  42. self,
  43. exc_type: Type[BaseException],
  44. exc_val: BaseException,
  45. exc_tb: TracebackType,
  46. ) -> Optional[bool]:
  47. self._do_exit(exc_type)
  48. return None
  49. async def __aenter__(self) -> "timeout":
  50. return self._do_enter()
  51. async def __aexit__(
  52. self,
  53. exc_type: Type[BaseException],
  54. exc_val: BaseException,
  55. exc_tb: TracebackType,
  56. ) -> None:
  57. self._do_exit(exc_type)
  58. @property
  59. def expired(self) -> bool:
  60. return self._cancelled
  61. @property
  62. def remaining(self) -> Optional[float]:
  63. if self._cancel_at is not None:
  64. return max(self._cancel_at - self._loop.time(), 0.0)
  65. else:
  66. return None
  67. def _do_enter(self) -> "timeout":
  68. # Support Tornado 5- without timeout
  69. # Details: https://github.com/python/asyncio/issues/392
  70. if self._timeout is None:
  71. return self
  72. self._task = asyncio.current_task(self._loop)
  73. if self._task is None:
  74. raise RuntimeError(
  75. "Timeout context manager should be used " "inside a task"
  76. )
  77. if self._timeout <= 0:
  78. self._loop.call_soon(self._cancel_task)
  79. return self
  80. self._cancel_at = self._loop.time() + self._timeout
  81. self._cancel_handler = self._loop.call_at(self._cancel_at, self._cancel_task)
  82. return self
  83. def _do_exit(self, exc_type: Type[BaseException]) -> None:
  84. if exc_type is asyncio.CancelledError and self._cancelled:
  85. self._cancel_handler = None
  86. self._task = None
  87. raise asyncio.TimeoutError
  88. if self._timeout is not None and self._cancel_handler is not None:
  89. self._cancel_handler.cancel()
  90. self._cancel_handler = None
  91. self._task = None
  92. return None
  93. def _cancel_task(self) -> None:
  94. if self._task is not None:
  95. self._task.cancel()
  96. self._cancelled = True