pax_global_header00006660000000000000000000000064141430321250014505gustar00rootroot0000000000000052 comment=3cda73789867a066f1071356058b700d1689cbb0 async-timeout-4.0.1/000077500000000000000000000000001414303212500143105ustar00rootroot00000000000000async-timeout-4.0.1/.codecov.yml000066400000000000000000000000521414303212500165300ustar00rootroot00000000000000codecov: notify: after_n_builds: 10 async-timeout-4.0.1/.github/000077500000000000000000000000001414303212500156505ustar00rootroot00000000000000async-timeout-4.0.1/.github/dependabot.yml000066400000000000000000000001751414303212500205030ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: pip directory: "/" schedule: interval: daily open-pull-requests-limit: 10 async-timeout-4.0.1/.github/workflows/000077500000000000000000000000001414303212500177055ustar00rootroot00000000000000async-timeout-4.0.1/.github/workflows/auto-merge.yml000066400000000000000000000011401414303212500224710ustar00rootroot00000000000000name: Dependabot auto-merge on: pull_request_target permissions: pull-requests: write contents: write jobs: dependabot: runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v1.1.1 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs run: gh pr merge --auto --squash "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} async-timeout-4.0.1/.github/workflows/ci.yml000066400000000000000000000063501414303212500210270ustar00rootroot00000000000000name: CI on: push: branches: - master - '[0-9].[0-9]+' # matches to backport branches, e.g. 3.6 tags: [ 'v*' ] pull_request: branches: - master - '[0-9].[0-9]+' schedule: - cron: '0 6 * * *' # Daily 6AM UTC build jobs: lint: name: Linter runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v2 - name: Cache PyPI uses: actions/cache@v2 with: key: pip-lint-${{ hashFiles('requirements.txt') }} path: ~/.cache/pip restore-keys: | pip-lint- - name: Install dependencies uses: py-actions/py-dependency-install@v2 with: path: requirements.txt - name: Install itself run: | python setup.py install - name: Run linter run: | make lint - name: Prepare twine checker run: | pip install -U twine wheel python setup.py sdist bdist_wheel - name: Run twine checker run: | twine check dist/* test: name: Test needs: lint strategy: matrix: pyver: ["3.6", "3.7", "3.8", "3.9", "3.10"] os: [ubuntu, macos, windows] include: - pyver: pypy3 os: ubuntu runs-on: ${{ matrix.os }}-latest timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Python ${{ matrix.pyver }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.pyver }} - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" # - name: Cache - name: Cache PyPI uses: actions/cache@v2 with: key: pip-ci-${{ runner.os }}-${{ matrix.pyver }}-${{ hashFiles('requirements/*.txt') }} path: ${{ steps.pip-cache.outputs.dir }} restore-keys: | pip-ci-${{ runner.os }}-${{ matrix.pyver }}- - name: Install dependencies uses: py-actions/py-dependency-install@v2 with: path: requirements.txt - name: Run unittests env: COLOR: 'yes' run: | pytest tests python -m coverage xml - name: Upload coverage uses: codecov/codecov-action@v1 with: file: ./coverage.xml flags: unit fail_ci_if_error: false deploy: name: Deploy runs-on: ubuntu-latest needs: test # Run only on pushing a tag if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v2 - name: Install dependencies run: python -m pip install -U pip wheel twine build - name: Make dists run: python -m build - name: Make Release uses: aio-libs/create-release@v1.2.3 with: changes_file: CHANGES.rst name: async-timeout version_file: async_timeout/__init__.py github_token: ${{ secrets.GITHUB_TOKEN }} pypi_token: ${{ secrets.PYPI_TOKEN }} fix_issue_regex: '\(`#(\\d+) `_\)' fix_issue_repl: "(#\\1)" async-timeout-4.0.1/.gitignore000066400000000000000000000020601414303212500162760ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .hypothesis/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # IPython Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # dotenv .env # virtualenv venv/ ENV/ # Spyder project settings .spyderproject # Rope project settings .ropeproject .mypy_cache .pytest_cache async-timeout-4.0.1/.mypy.ini000066400000000000000000000010751414303212500160700ustar00rootroot00000000000000[mypy] files = async_timeout, tests check_untyped_defs = True follow_imports_for_stubs = True disallow_any_decorated = True disallow_any_generics = True disallow_incomplete_defs = True disallow_subclassing_any = True disallow_untyped_calls = True disallow_untyped_decorators = True disallow_untyped_defs = True implicit_reexport = False no_implicit_optional = True show_error_codes = True strict_equality = True warn_incomplete_stub = True warn_redundant_casts = True warn_unreachable = True warn_unused_ignores = True disallow_any_unimported = True warn_return_any = True async-timeout-4.0.1/.pre-commit-config.yaml000066400000000000000000000023451414303212500205750ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: 'v4.0.1' hooks: - id: check-merge-conflict exclude: "rst$" - repo: https://github.com/asottile/yesqa rev: v1.3.0 hooks: - id: yesqa - repo: https://github.com/PyCQA/isort rev: '5.10.0' hooks: - id: isort - repo: https://github.com/psf/black rev: '21.10b0' hooks: - id: black language_version: python3 # Should be a command that runs python3.6+ - repo: https://github.com/pre-commit/pre-commit-hooks rev: 'v4.0.1' hooks: - id: check-case-conflict - id: check-json - id: check-xml - id: check-yaml - id: debug-statements - id: check-added-large-files - id: end-of-file-fixer exclude: "[.]md$" - id: requirements-txt-fixer - id: trailing-whitespace exclude: "[.]md$" - id: check-symlinks - id: debug-statements # Another entry is required to apply file-contents-sorter to another file - repo: https://github.com/pre-commit/pre-commit-hooks rev: 'v4.0.1' hooks: - id: file-contents-sorter files: | .gitignore - repo: https://github.com/asottile/pyupgrade rev: 'v2.29.0' hooks: - id: pyupgrade args: ['--py36-plus'] - repo: https://github.com/PyCQA/flake8 rev: '4.0.1' hooks: - id: flake8 async-timeout-4.0.1/CHANGES.rst000066400000000000000000000045551414303212500161230ustar00rootroot00000000000000======= CHANGES ======= .. towncrier release notes start 4.0.1 (2121-11-10) ================== - Fix regression: 1. Don't raise TimeoutError from timeout object that doesn't enter into async context manager 2. Use call_soon() for raising TimeoutError if deadline is reached on entering into async context manager (#258) - Make ``Timeout`` class available in ``__all__``. 4.0.0 (2021-11-01) ================== * Implemented ``timeout_at(deadline)`` (#117) * Supported ``timeout.deadline`` and ``timeout.expired`` properties. * Dropped ``timeout.remaining`` property: it can be calculated as ``timeout.deadline - loop.time()`` * Dropped ``timeout.timeout`` property that returns a relative timeout based on the timeout object creation time; the absolute ``timeout.deadline`` should be used instead. * Added the deadline modification methods: ``timeout.reject()``, ``timeout.shift(delay)``, ``timeout.update(deadline)``. * Deprecated synchronous context manager usage 3.0.1 (2018-10-09) ================== * More aggressive typing (#48) 3.0.0 (2018-05-05) ================== * Drop Python 3.4, the minimal supported version is Python 3.5.3 * Provide type annotations 2.0.1 (2018-03-13) ================== * Fix ``PendingDeprecationWarning`` on Python 3.7 (#33) 2.0.0 (2017-10-09) ================== * Changed ``timeout <= 0`` behaviour * Backward incompatibility change, prior this version ``0`` was shortcut for ``None`` * when timeout <= 0 ``TimeoutError`` raised faster 1.4.0 (2017-09-09) ================== * Implement ``remaining`` property (#20) * If timeout is not started yet or started unconstrained: ``remaining`` is ``None`` * If timeout is expired: ``remaining`` is ``0.0`` * All others: roughly amount of time before ``TimeoutError`` is triggered 1.3.0 (2017-08-23) ================== * Don't suppress nested exception on timeout. Exception context points on cancelled line with suspended ``await`` (#13) * Introduce ``.timeout`` property (#16) * Add methods for using as async context manager (#9) 1.2.1 (2017-05-02) ================== * Support unpublished event loop's "current_task" api. 1.2.0 (2017-03-11) ================== * Extra check on context manager exit * 0 is no-op timeout 1.1.0 (2016-10-20) ================== * Rename to ``async-timeout`` 1.0.0 (2016-09-09) ================== * The first release. async-timeout-4.0.1/LICENSE000066400000000000000000000010701414303212500153130ustar00rootroot00000000000000Copyright 2016-2020 aio-libs collaboration. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. async-timeout-4.0.1/MANIFEST.in000066400000000000000000000002031414303212500160410ustar00rootroot00000000000000include LICENSE include CHANGES.rst include README.rst graft async_timeout graft tests global-exclude *.pyc global-exclude *.cache async-timeout-4.0.1/Makefile000066400000000000000000000004231414303212500157470ustar00rootroot00000000000000SOURCES = setup.py async_timeout tests test: lint pytest tests lint: mypy check black flake8 mypy: mypy black: isort -c $(SOURCES) black --check $(SOURCES) flake8: flake8 $(SOURCES) fmt: isort $(SOURCES) black $(SOURCES) check: python setup.py check -rms async-timeout-4.0.1/README.rst000066400000000000000000000052531414303212500160040ustar00rootroot00000000000000async-timeout ============= .. image:: https://travis-ci.com/aio-libs/async-timeout.svg?branch=master :target: https://travis-ci.com/aio-libs/async-timeout .. image:: https://codecov.io/gh/aio-libs/async-timeout/branch/master/graph/badge.svg :target: https://codecov.io/gh/aio-libs/async-timeout .. image:: https://img.shields.io/pypi/v/async-timeout.svg :target: https://pypi.python.org/pypi/async-timeout .. image:: https://badges.gitter.im/Join%20Chat.svg :target: https://gitter.im/aio-libs/Lobby :alt: Chat on Gitter asyncio-compatible timeout context manager. Usage example ------------- The context manager is useful in cases when you want to apply timeout logic around block of code or in cases when ``asyncio.wait_for()`` is not suitable. Also it's much faster than ``asyncio.wait_for()`` because ``timeout`` doesn't create a new task. The ``timeout(delay, *, loop=None)`` call returns a context manager that cancels a block on *timeout* expiring:: async with timeout(1.5): await inner() 1. If ``inner()`` is executed faster than in ``1.5`` seconds nothing happens. 2. Otherwise ``inner()`` is cancelled internally by sending ``asyncio.CancelledError`` into but ``asyncio.TimeoutError`` is raised outside of context manager scope. *timeout* parameter could be ``None`` for skipping timeout functionality. Alternatively, ``timeout_at(when)`` can be used for scheduling at the absolute time:: loop = asyncio.get_event_loop() now = loop.time() async with timeout_at(now + 1.5): await inner() Please note: it is not POSIX time but a time with undefined starting base, e.g. the time of the system power on. Context manager has ``.expired`` property for check if timeout happens exactly in context manager:: async with timeout(1.5) as cm: await inner() print(cm.expired) The property is ``True`` if ``inner()`` execution is cancelled by timeout context manager. If ``inner()`` call explicitly raises ``TimeoutError`` ``cm.expired`` is ``False``. The scheduled deadline time is available as ``.deadline`` property:: async with timeout(1.5) as cm: cm.deadline Not finished yet timeout can be rescheduled by ``shift_by()`` or ``shift_to()`` methods:: async with timeout(1.5) as cm: cm.shift(1) # add another second on waiting cm.update(loop.time() + 5) # reschedule to now+5 seconds Rescheduling is forbidden if the timeout is expired or after exit from ``async with`` code block. Installation ------------ :: $ pip install async-timeout The library is Python 3 only! Authors and License ------------------- The module is written by Andrew Svetlov. It's *Apache 2* licensed and freely available. async-timeout-4.0.1/async_timeout/000077500000000000000000000000001414303212500171735ustar00rootroot00000000000000async-timeout-4.0.1/async_timeout/__init__.py000066400000000000000000000162641414303212500213150ustar00rootroot00000000000000import asyncio import enum import sys import warnings from types import TracebackType from typing import Any, Optional, Type from typing_extensions import final __version__ = "4.0.1" __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 = _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 = _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 immadiatelly when scheduled # if the deadline is passed. # The purpose is to time out as sson as possible # without waiting for the next await expression. __slots__ = ("_deadline", "_loop", "_state", "_timeout_handler") def __init__( self, deadline: Optional[float], loop: asyncio.AbstractEventLoop ) -> None: self._loop = loop self._state = _State.INIT 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: Type[BaseException], exc_val: BaseException, exc_tb: 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: Type[BaseException], exc_val: BaseException, exc_tb: 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: 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 immediatelly. 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() task = _current_task(self._loop) if deadline <= now: self._timeout_handler = self._loop.call_soon(self._on_timeout, task) else: self._timeout_handler = self._loop.call_at(deadline, self._on_timeout, task) 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: Type[BaseException]) -> None: if exc_type is asyncio.CancelledError and self._state == _State.TIMEOUT: self._timeout_handler = None raise asyncio.TimeoutError # timeout has not expired self._state = _State.EXIT self._reject() return None def _on_timeout(self, task: "asyncio.Task[None]") -> None: task.cancel() self._state = _State.TIMEOUT # drop the reference early self._timeout_handler = None if sys.version_info >= (3, 7): def _current_task(loop: asyncio.AbstractEventLoop) -> "Optional[asyncio.Task[Any]]": return asyncio.current_task(loop=loop) else: def _current_task(loop: asyncio.AbstractEventLoop) -> "Optional[asyncio.Task[Any]]": return asyncio.Task.current_task(loop=loop) if sys.version_info >= (3, 7): def _get_running_loop() -> asyncio.AbstractEventLoop: return asyncio.get_running_loop() else: def _get_running_loop() -> asyncio.AbstractEventLoop: loop = asyncio.get_event_loop() if not loop.is_running(): raise RuntimeError("no running event loop") return loop async-timeout-4.0.1/async_timeout/py.typed000066400000000000000000000000141414303212500206650ustar00rootroot00000000000000Placeholder async-timeout-4.0.1/pyproject.toml000066400000000000000000000001511414303212500172210ustar00rootroot00000000000000[build-system] requires = [ "setuptools>=45", "wheel", ] build-backend = "setuptools.build_meta" async-timeout-4.0.1/requirements.txt000066400000000000000000000003231414303212500175720ustar00rootroot00000000000000-e . black==21.10b0; implementation_name=="cpython" docutils==0.18 flake8==4.0.1 isort==5.10.1 mypy==0.910; implementation_name=="cpython" pre-commit==2.15 pytest==6.2.5 pytest-asyncio==0.16.0 pytest-cov==3.0.0 async-timeout-4.0.1/setup.cfg000066400000000000000000000034201414303212500161300ustar00rootroot00000000000000[metadata] name = async-timeout version = attr: async_timeout.__version__ url = https://github.com/aio-libs/async-timeout project_urls = Chat: Gitter = https://gitter.im/aio-libs/Lobby CI: GitHub Actions = https://github.com/aio-libs/async-timeout/actions Coverage: codecov = https://codecov.io/github/aio-libs/async-timeout GitHub: issues = https://github.com/aio-libs/async-timeout/issues GitHub: repo = https://github.com/aio-libs/async-timeout description = Timeout context manager for asyncio programs long_description = file: README.rst long_description_content_type = text/x-rst author = Andrew Svetlov author_email = andrew.svetlov@gmail.com license = Apache 2 license_files = LICENSE classifiers = Development Status :: 5 - Production/Stable Topic :: Software Development :: Libraries Framework :: AsyncIO Intended Audience :: Developers License :: OSI Approved :: Apache Software License Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 [options] python_requires = >=3.6 packages = async_timeout zip_safe = True include_package_data = True install_requires = typing_extensions>=3.6.5 [flake8] exclude = .git,.env,__pycache__,.eggs max-line-length = 88 ignore = N801,N802,N803,E252,W503,E133,E203 [isort] line_length=88 include_trailing_comma=True multi_line_output=3 force_grid_wrap=0 combine_as_imports=True lines_after_imports=2 [tool:pytest] addopts= --cov=async_timeout --cov-report=term --cov-report=html --cov-branch [mypy-pytest] ignore_missing_imports = true async-timeout-4.0.1/setup.py000066400000000000000000000000471414303212500160230ustar00rootroot00000000000000from setuptools import setup setup() async-timeout-4.0.1/tests/000077500000000000000000000000001414303212500154525ustar00rootroot00000000000000async-timeout-4.0.1/tests/test_timeout.py000066400000000000000000000223351414303212500205560ustar00rootroot00000000000000import asyncio import time from functools import wraps from typing import Any, Callable, List, TypeVar import pytest from async_timeout import timeout, timeout_at _Func = TypeVar("_Func", bound=Callable[..., Any]) def log_func(func: _Func, msg: str, call_order: List[str]) -> _Func: """Simple wrapper to add a log to call_order when the function is called.""" @wraps(func) def wrapper(*args: Any, **kwargs: Any) -> Any: # type: ignore[misc] call_order.append(msg) return func(*args, **kwargs) return wrapper # type: ignore[return-value] @pytest.mark.asyncio async def test_timeout() -> None: canceled_raised = False async def long_running_task() -> None: try: await asyncio.sleep(10) except asyncio.CancelledError: nonlocal canceled_raised canceled_raised = True raise with pytest.raises(asyncio.TimeoutError): async with timeout(0.01) as t: await long_running_task() assert t._loop is asyncio.get_event_loop() assert canceled_raised, "CancelledError was not raised" @pytest.mark.asyncio async def test_timeout_finish_in_time() -> None: async def long_running_task() -> str: await asyncio.sleep(0.01) return "done" async with timeout(0.1): resp = await long_running_task() assert resp == "done" @pytest.mark.asyncio async def test_timeout_disable() -> None: async def long_running_task() -> str: await asyncio.sleep(0.1) return "done" loop = asyncio.get_event_loop() t0 = loop.time() async with timeout(None): resp = await long_running_task() assert resp == "done" dt = loop.time() - t0 assert 0.09 < dt < 0.3, dt @pytest.mark.asyncio async def test_timeout_is_none_no_schedule() -> None: async with timeout(None) as cm: assert cm._timeout_handler is None assert cm.deadline is None def test_timeout_no_loop() -> None: with pytest.raises(RuntimeError, match="no running event loop"): timeout(None) @pytest.mark.asyncio async def test_timeout_zero() -> None: with pytest.raises(asyncio.TimeoutError): async with timeout(0): await asyncio.sleep(10) @pytest.mark.asyncio async def test_timeout_not_relevant_exception() -> None: await asyncio.sleep(0) with pytest.raises(KeyError): async with timeout(0.1): raise KeyError @pytest.mark.asyncio async def test_timeout_canceled_error_is_not_converted_to_timeout() -> None: await asyncio.sleep(0) with pytest.raises(asyncio.CancelledError): async with timeout(0.001): raise asyncio.CancelledError @pytest.mark.asyncio async def test_timeout_blocking_loop() -> None: async def long_running_task() -> str: time.sleep(0.1) return "done" async with timeout(0.01): result = await long_running_task() assert result == "done" @pytest.mark.asyncio async def test_for_race_conditions() -> None: loop = asyncio.get_event_loop() fut = loop.create_future() loop.call_later(0.1, fut.set_result, "done") async with timeout(0.5): resp = await fut assert resp == "done" @pytest.mark.asyncio async def test_timeout_time() -> None: foo_running = None loop = asyncio.get_event_loop() start = loop.time() with pytest.raises(asyncio.TimeoutError): async with timeout(0.1): foo_running = True try: await asyncio.sleep(0.2) finally: foo_running = False dt = loop.time() - start assert 0.09 < dt < 0.3 assert not foo_running @pytest.mark.asyncio async def test_outer_coro_is_not_cancelled() -> None: has_timeout = False async def outer() -> None: nonlocal has_timeout try: async with timeout(0.001): await asyncio.sleep(1) except asyncio.TimeoutError: has_timeout = True task = asyncio.ensure_future(outer()) await task assert has_timeout assert not task.cancelled() assert task.done() @pytest.mark.asyncio async def test_cancel_outer_coro() -> None: loop = asyncio.get_event_loop() fut = loop.create_future() async def outer() -> None: fut.set_result(None) await asyncio.sleep(1) task = asyncio.ensure_future(outer()) await fut task.cancel() with pytest.raises(asyncio.CancelledError): await task assert task.cancelled() assert task.done() @pytest.mark.asyncio async def test_timeout_suppress_exception_chain() -> None: with pytest.raises(asyncio.TimeoutError) as ctx: async with timeout(0.01): await asyncio.sleep(10) assert not ctx.value.__suppress_context__ @pytest.mark.asyncio async def test_timeout_expired() -> None: with pytest.raises(asyncio.TimeoutError): async with timeout(0.01) as cm: await asyncio.sleep(10) assert cm.expired @pytest.mark.asyncio async def test_timeout_inner_timeout_error() -> None: with pytest.raises(asyncio.TimeoutError): async with timeout(0.01) as cm: raise asyncio.TimeoutError assert not cm.expired @pytest.mark.asyncio async def test_timeout_inner_other_error() -> None: class MyError(RuntimeError): pass with pytest.raises(MyError): async with timeout(0.01) as cm: raise MyError assert not cm.expired @pytest.mark.asyncio async def test_timeout_at() -> None: loop = asyncio.get_event_loop() with pytest.raises(asyncio.TimeoutError): now = loop.time() async with timeout_at(now + 0.01) as cm: await asyncio.sleep(10) assert cm.expired @pytest.mark.asyncio async def test_timeout_at_not_fired() -> None: loop = asyncio.get_event_loop() now = loop.time() async with timeout_at(now + 1) as cm: await asyncio.sleep(0) assert not cm.expired @pytest.mark.asyncio async def test_expired_after_rejecting() -> None: t = timeout(10) assert not t.expired t.reject() assert not t.expired @pytest.mark.asyncio async def test_reject_finished() -> None: async with timeout(10) as t: await asyncio.sleep(0) assert not t.expired with pytest.raises(RuntimeError, match="invalid state EXIT"): t.reject() @pytest.mark.asyncio async def test_expired_after_timeout() -> None: with pytest.raises(asyncio.TimeoutError): async with timeout(0.01) as t: assert not t.expired await asyncio.sleep(10) assert t.expired @pytest.mark.asyncio async def test_deadline() -> None: loop = asyncio.get_event_loop() t0 = loop.time() async with timeout(1) as cm: t1 = loop.time() assert cm.deadline is not None assert t0 + 1 <= cm.deadline <= t1 + 1 @pytest.mark.asyncio async def test_async_timeout() -> None: with pytest.raises(asyncio.TimeoutError): async with timeout(0.01) as cm: await asyncio.sleep(10) assert cm.expired @pytest.mark.asyncio async def test_async_no_timeout() -> None: async with timeout(1) as cm: await asyncio.sleep(0) assert not cm.expired @pytest.mark.asyncio async def test_shift() -> None: loop = asyncio.get_event_loop() t0 = loop.time() async with timeout(1) as cm: t1 = loop.time() assert cm.deadline is not None assert t0 + 1 <= cm.deadline <= t1 + 1 cm.shift(1) assert t0 + 2 <= cm.deadline <= t0 + 2.1 @pytest.mark.asyncio async def test_shift_nonscheduled() -> None: async with timeout(None) as cm: with pytest.raises( RuntimeError, match="cannot shift timeout if deadline is not scheduled", ): cm.shift(1) @pytest.mark.asyncio async def test_shift_negative_expired() -> None: async with timeout(1) as cm: with pytest.raises(asyncio.CancelledError): cm.shift(-1) await asyncio.sleep(10) @pytest.mark.asyncio async def test_shift_by_expired() -> None: async with timeout(0.001) as cm: with pytest.raises(asyncio.CancelledError): await asyncio.sleep(10) with pytest.raises(RuntimeError, match="cannot reschedule expired timeout"): cm.shift(10) @pytest.mark.asyncio async def test_shift_to_expired() -> None: loop = asyncio.get_event_loop() t0 = loop.time() async with timeout_at(t0 + 0.001) as cm: with pytest.raises(asyncio.CancelledError): await asyncio.sleep(10) with pytest.raises(RuntimeError, match="cannot reschedule expired timeout"): cm.update(t0 + 10) @pytest.mark.asyncio async def test_shift_by_after_cm_exit() -> None: async with timeout(1) as cm: await asyncio.sleep(0) with pytest.raises( RuntimeError, match="cannot reschedule after exit from context manager" ): cm.shift(1) @pytest.mark.asyncio async def test_enter_twice() -> None: async with timeout(10) as t: await asyncio.sleep(0) with pytest.raises(RuntimeError, match="invalid state EXIT"): async with t: await asyncio.sleep(0) @pytest.mark.asyncio async def test_deprecated_with() -> None: with pytest.warns(DeprecationWarning): with timeout(1): await asyncio.sleep(0)