Exemple #1
0
def fibo_backoff() -> Iterator[int]:
    """
    Fibonacci backoff, with the first 6 elements consumed.
    In other words, this starts at 13, 21, ....
    """
    fib = backoff.fibo()
    for _ in range(6):
        next(fib)
    yield from fib
Exemple #2
0
def fibo_long() -> Iterator[int]:
    f = fibo()
    for _ in range(5):
        next(f)
    yield from f
Exemple #3
0
def test_fibo_max_value():
    gen = backoff.fibo(max_value=8)
    expected = [1, 1, 2, 3, 5, 8, 8, 8]
    for expect in expected:
        assert expect == next(gen)
Exemple #4
0
def test_fibo():
    gen = backoff.fibo()
    expected = [1, 1, 2, 3, 5, 8, 13]
    for expect in expected:
        assert expect == next(gen)
Exemple #5
0
def fibo_long() -> Iterator[int]:
    f = backoff.fibo()
    for _ in range(4):
        next(f)
    yield from f
Exemple #6
0
def test_fibo_max_value():
    gen = backoff.fibo(max_value=8)
    expected = [1, 1, 2, 3, 5, 8, 8, 8]
    for expect in expected:
        assert expect == next(gen)
Exemple #7
0
def test_fibo():
    gen = backoff.fibo()
    expected = [1, 1, 2, 3, 5, 8, 13]
    for expect in expected:
        assert expect == next(gen)
Exemple #8
0
 def wait_gen():
     # shorten the wait times between retries a little to fit our
     # scale a little better (aim to give up within 10 s)
     for n in backoff.fibo():
         yield n / 2.0
Exemple #9
0
class Network(ClientModule):
    client: Parent["Client"] = field(repr=False)

    last_replies: Runtime[Deque[Reply]] = field(
        init=False,
        repr=False,
        default_factory=lambda: Deque(maxlen=256),
    )

    _session: Runtime[aiohttp.ClientSession] = field(
        init=False,
        repr=False,
        default_factory=aiohttp.ClientSession,
    )

    @property
    def api(self) -> URL:
        return URL(self.client.server) / "_matrix" / "client" / "r0"

    @property
    def media_api(self) -> URL:
        return URL(self.client.server) / "_matrix" / "media" / "r0"

    async def get(self, url: URL, headers: MethHeaders = None) -> Reply:
        return await self.send(Request("GET", url, None, headers or {}))

    async def post(
        self,
        url: URL,
        data: ReqData = None,
        headers: MethHeaders = None,
    ) -> Reply:
        return await self.send(Request("POST", url, data, headers or {}))

    async def put(
        self,
        url: URL,
        data: ReqData = None,
        headers: MethHeaders = None,
    ) -> Reply:
        return await self.send(Request("PUT", url, data, headers or {}))

    @backoff.on_exception(
        lambda: backoff.fibo(max_value=60),
        (ServerError, TimeoutError, asyncio.TimeoutError, aiohttp.ClientError),
        giveup=lambda e: isinstance(e, ServerError) and not e.can_retry,
        on_backoff=_on_backoff,
        on_giveup=_on_giveup,
        logger=None,
    )
    async def send(self, request: Request) -> Reply:
        async with try_rewind(request.data):
            return await self._send_once(request)

    async def _send_once(self, request: Request) -> Reply:
        if self.client._terminated:
            raise RuntimeError(f"{self.client} terminated, create a new one")

        token = self.client.access_token
        data = request.data

        if token:
            request.headers["Authorization"] = f"Bearer {token}"

        if isinstance(data, (Seekable, AsyncSeekable)):
            data = read_chunked_binary(data)
        elif isinstance(data, dict):
            data = None

        resp = await self._session.request(
            method=request.method,
            url=str(request.url),
            data=data,
            json=request.data if isinstance(request.data, dict) else None,
            headers=request.headers,
        )

        mime = resp.content_type
        disp = resp.content_disposition

        reply = Reply(
            request=request,
            status=resp.status,
            data=resp.content,
            json=await resp.json() if mime == "application/json" else {},
            mime=mime,
            size=resp.content_length,
            filename=disp.filename if disp else None,
        )

        try:
            resp.raise_for_status()
        except aiohttp.ClientResponseError:
            error = ServerError.from_reply(reply)
            reply.error = error
            raise error
        else:
            self.client.debug("{}", reply, depth=3)
            return reply
        finally:
            self.last_replies.appendleft(reply)

    async def disconnect(self) -> None:
        await self._session.close()