# Path: grpclib/utils.py
# class Wrapper(ContextManager[None]):
#     """Special wrapper for coroutines to wake them up in case of some error.
# 
#     Example:
# 
#     .. code-block:: python3
# 
#         w = Wrapper()
# 
#         async def blocking_call():
#             with w:
#                 await asyncio.sleep(10)
# 
#         # and somewhere else:
#         w.cancel(NoNeedToWaitError('With explanation'))
# 
#     """
#     _error: Optional[Exception] = None
# 
#     cancelled: Optional[bool] = None
#     cancel_failed: Optional[bool] = None
# 
#     def __init__(self) -> None:
#         self._tasks: Set['asyncio.Task[Any]'] = set()
# 
#     def __enter__(self) -> None:
#         if self._error is not None:
#             raise self._error
# 
#         task = _current_task()
#         if task is None:
#             raise RuntimeError('Called not inside a task')
# 
#         self._tasks.add(task)
# 
#     def __exit__(
#         self,
#         exc_type: Optional[Type[BaseException]],
#         exc_val: Optional[BaseException],
#         exc_tb: Optional[TracebackType],
#     ) -> None:
#         task = _current_task()
#         assert task
#         self._tasks.discard(task)
#         if self._error is not None:
#             self.cancel_failed = exc_type is not asyncio.CancelledError
#             raise self._error
# 
#     def cancel(self, error: Exception) -> None:
#         self._error = error
#         for task in self._tasks:
#             task.cancel()
#         self.cancelled = True
# 
# class DeadlineWrapper(Wrapper):
#     """Deadline wrapper to specify deadline once for any number of awaiting
#     method calls.
# 
#     Example:
# 
#     .. code-block:: python3
# 
#         dw = DeadlineWrapper()
# 
#         with dw.start(deadline):
#             await handle_request()
# 
#         # somewhere during request handling:
# 
#         async def blocking_call():
#             with dw:
#                 await asyncio.sleep(10)
# 
#     """
#     @contextmanager
#     def start(self, deadline: 'Deadline') -> Iterator[None]:
#         timeout = deadline.time_remaining()
#         if not timeout:
#             raise asyncio.TimeoutError('Deadline exceeded')
# 
#         def callback() -> None:
#             self.cancel(asyncio.TimeoutError('Deadline exceeded'))
# 
#         loop = asyncio.get_event_loop()
#         timer = loop.call_later(timeout, callback)
#         try:
#             yield
#         finally:
#             timer.cancel()
# 
# def _cached(func: Callable[[], Any]) -> Callable[[], Any]:
#     @wraps(func)
#     def wrapper() -> Any:
#         try:
#             return func.__result__  # type: ignore
#         except AttributeError:
#             func.__result__ = func()  # type: ignore
#             return func.__result__  # type: ignore
#     return wrapper
# 
# Path: grpclib/metadata.py
# class Deadline:
#     """Represents request's deadline - fixed point in time
#     """
#     def __init__(self, *, _timestamp: float) -> None:
#         self._timestamp = _timestamp
# 
#     def __lt__(self, other: object) -> bool:
#         if not isinstance(other, Deadline):
#             raise TypeError('comparison is not supported between '
#                             'instances of \'{}\' and \'{}\''
#                             .format(type(self).__name__, type(other).__name__))
#         return self._timestamp < other._timestamp
# 
#     def __eq__(self, other: object) -> bool:
#         if not isinstance(other, Deadline):
#             return False
#         return self._timestamp == other._timestamp
# 
#     @classmethod
#     def from_headers(cls, headers: _Headers) -> Optional['Deadline']:
#         timeout = min(map(decode_timeout,
#                           (v for k, v in headers if k == 'grpc-timeout')),
#                       default=None)
#         if timeout is not None:
#             return cls.from_timeout(timeout)
#         else:
#             return None
# 
#     @classmethod
#     def from_timeout(cls, timeout: float) -> 'Deadline':
#         return cls(_timestamp=time.monotonic() + timeout)
# 
#     def time_remaining(self) -> float:
#         """Calculates remaining time for the current request completion
# 
#         This function returns time in seconds as a floating point number,
#         greater or equal to zero.
#         """
#         return max(0, self._timestamp - time.monotonic())


import sys
import time
import signal
import asyncio
import subprocess
import pytest
from unittest.mock import Mock
from grpclib.utils import Wrapper, DeadlineWrapper, _cached
from grpclib.metadata import Deadline
    pass


class UserAPI:

    def __init__(self, wrapper):
        self.wrapper = wrapper

    async def foo(self, *, time=.0001):
        with self.wrapper:
            await asyncio.sleep(time)


@pytest.mark.asyncio
async def test_wrapper(loop):
    api = UserAPI(Wrapper())
    await api.foo()

    loop.call_soon(lambda: api.wrapper.cancel(CustomError('Some explanation')))

    with pytest.raises(CustomError) as err:
        await api.foo()
    err.match('Some explanation')

    with pytest.raises(CustomError):
        await api.foo()


@pytest.mark.asyncio
async def test_wrapper_concurrent(loop):

# Path: grpclib/utils.py
# class Wrapper(ContextManager[None]):
#     """Special wrapper for coroutines to wake them up in case of some error.
# 
#     Example:
# 
#     .. code-block:: python3
# 
#         w = Wrapper()
# 
#         async def blocking_call():
#             with w:
#                 await asyncio.sleep(10)
# 
#         # and somewhere else:
#         w.cancel(NoNeedToWaitError('With explanation'))
# 
#     """
#     _error: Optional[Exception] = None
# 
#     cancelled: Optional[bool] = None
#     cancel_failed: Optional[bool] = None
# 
#     def __init__(self) -> None:
#         self._tasks: Set['asyncio.Task[Any]'] = set()
# 
#     def __enter__(self) -> None:
#         if self._error is not None:
#             raise self._error
# 
#         task = _current_task()
#         if task is None:
#             raise RuntimeError('Called not inside a task')
# 
#         self._tasks.add(task)
# 
#     def __exit__(
#         self,
#         exc_type: Optional[Type[BaseException]],
#         exc_val: Optional[BaseException],
#         exc_tb: Optional[TracebackType],
#     ) -> None:
#         task = _current_task()
#         assert task
#         self._tasks.discard(task)
#         if self._error is not None:
#             self.cancel_failed = exc_type is not asyncio.CancelledError
#             raise self._error
# 
#     def cancel(self, error: Exception) -> None:
#         self._error = error
#         for task in self._tasks:
#             task.cancel()
#         self.cancelled = True
# 
# class DeadlineWrapper(Wrapper):
#     """Deadline wrapper to specify deadline once for any number of awaiting
#     method calls.
# 
#     Example:
# 
#     .. code-block:: python3
# 
#         dw = DeadlineWrapper()
# 
#         with dw.start(deadline):
#             await handle_request()
# 
#         # somewhere during request handling:
# 
#         async def blocking_call():
#             with dw:
#                 await asyncio.sleep(10)
# 
#     """
#     @contextmanager
#     def start(self, deadline: 'Deadline') -> Iterator[None]:
#         timeout = deadline.time_remaining()
#         if not timeout:
#             raise asyncio.TimeoutError('Deadline exceeded')
# 
#         def callback() -> None:
#             self.cancel(asyncio.TimeoutError('Deadline exceeded'))
# 
#         loop = asyncio.get_event_loop()
#         timer = loop.call_later(timeout, callback)
#         try:
#             yield
#         finally:
#             timer.cancel()
# 
# def _cached(func: Callable[[], Any]) -> Callable[[], Any]:
#     @wraps(func)
#     def wrapper() -> Any:
#         try:
#             return func.__result__  # type: ignore
#         except AttributeError:
#             func.__result__ = func()  # type: ignore
#             return func.__result__  # type: ignore
#     return wrapper
# 
# Path: grpclib/metadata.py
# class Deadline:
#     """Represents request's deadline - fixed point in time
#     """
#     def __init__(self, *, _timestamp: float) -> None:
#         self._timestamp = _timestamp
# 
#     def __lt__(self, other: object) -> bool:
#         if not isinstance(other, Deadline):
#             raise TypeError('comparison is not supported between '
#                             'instances of \'{}\' and \'{}\''
#                             .format(type(self).__name__, type(other).__name__))
#         return self._timestamp < other._timestamp
# 
#     def __eq__(self, other: object) -> bool:
#         if not isinstance(other, Deadline):
#             return False
#         return self._timestamp == other._timestamp
# 
#     @classmethod
#     def from_headers(cls, headers: _Headers) -> Optional['Deadline']:
#         timeout = min(map(decode_timeout,
#                           (v for k, v in headers if k == 'grpc-timeout')),
#                       default=None)
#         if timeout is not None:
#             return cls.from_timeout(timeout)
#         else:
#             return None
# 
#     @classmethod
#     def from_timeout(cls, timeout: float) -> 'Deadline':
#         return cls(_timestamp=time.monotonic() + timeout)
# 
#     def time_remaining(self) -> float:
#         """Calculates remaining time for the current request completion
# 
#         This function returns time in seconds as a floating point number,
#         greater or equal to zero.
#         """
#         return max(0, self._timestamp - time.monotonic())


# Path: tests/test_utils.py
import sys
import time
import signal
import asyncio
import subprocess
import pytest
from unittest.mock import Mock
from grpclib.utils import Wrapper, DeadlineWrapper, _cached
from grpclib.metadata import Deadline

    pass


class UserAPI:

    def __init__(self, wrapper):
        self.wrapper = wrapper

    async def foo(self, *, time=.0001):
        with self.wrapper:
            await asyncio.sleep(time)


@pytest.mark.asyncio
async def test_wrapper(loop):
    api = UserAPI(Wrapper())
    await api.foo()

    loop.call_soon(lambda: api.wrapper.cancel(CustomError('Some explanation')))

    with pytest.raises(CustomError) as err:
        await api.foo()
    err.match('Some explanation')

    with pytest.raises(CustomError):
        await api.foo()


@pytest.mark.asyncio
async def test_wrapper_concurrent(loop):

    api = UserAPI(Wrapper())