Skip to content

Commit

Permalink
Drop request.timer attribute. (#1249)
Browse files Browse the repository at this point in the history
* Drop request.timer attribute
* Response(..., elapsed_func=...)
  • Loading branch information
tomchristie committed Sep 7, 2020
1 parent 78f2420 commit a783fe5
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 44 deletions.
7 changes: 7 additions & 0 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
)
from ._utils import (
NetRCInfo,
Timer,
URLPattern,
get_environment_proxies,
get_logger,
Expand Down Expand Up @@ -811,6 +812,8 @@ def _send_single_request(self, request: Request, timeout: Timeout) -> Response:
Sends a single request, without handling any redirections.
"""
transport = self._transport_for_url(request.url)
timer = Timer()
timer.sync_start()

with map_exceptions(HTTPCORE_EXC_MAP, request=request):
(
Expand All @@ -832,6 +835,7 @@ def _send_single_request(self, request: Request, timeout: Timeout) -> Response:
headers=headers,
stream=stream, # type: ignore
request=request,
elapsed_func=timer.sync_elapsed,
)

self.cookies.extract_cookies(response)
Expand Down Expand Up @@ -1434,6 +1438,8 @@ async def _send_single_request(
Sends a single request, without handling any redirections.
"""
transport = self._transport_for_url(request.url)
timer = Timer()
await timer.async_start()

with map_exceptions(HTTPCORE_EXC_MAP, request=request):
(
Expand All @@ -1455,6 +1461,7 @@ async def _send_single_request(
headers=headers,
stream=stream, # type: ignore
request=request,
elapsed_func=timer.async_elapsed,
)

self.cookies.extract_cookies(response)
Expand Down
14 changes: 7 additions & 7 deletions httpx/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
URLTypes,
)
from ._utils import (
ElapsedTimer,
flatten_queryparams,
guess_json_utf,
is_known_encoding,
Expand Down Expand Up @@ -606,7 +605,6 @@ def __init__(
else:
self.stream = encode(data, files, json)

self.timer = ElapsedTimer()
self.prepare()

def prepare(self) -> None:
Expand Down Expand Up @@ -678,6 +676,7 @@ def __init__(
stream: ContentStream = None,
content: bytes = None,
history: typing.List["Response"] = None,
elapsed_func: typing.Callable = None,
):
self.status_code = status_code
self.http_version = http_version
Expand All @@ -688,6 +687,7 @@ def __init__(
self.call_next: typing.Optional[typing.Callable] = None

self.history = [] if history is None else list(history)
self._elapsed_func = elapsed_func

self.is_closed = False
self.is_stream_consumed = False
Expand All @@ -708,7 +708,7 @@ def elapsed(self) -> datetime.timedelta:
"'.elapsed' may only be accessed after the response "
"has been read or closed."
)
return self._elapsed
return datetime.timedelta(seconds=self._elapsed)

@property
def request(self) -> Request:
Expand Down Expand Up @@ -976,8 +976,8 @@ def close(self) -> None:
"""
if not self.is_closed:
self.is_closed = True
if self._request is not None:
self._elapsed = self.request.timer.elapsed
if self._elapsed_func is not None:
self._elapsed = self._elapsed_func()
self._raw_stream.close()

async def aread(self) -> bytes:
Expand Down Expand Up @@ -1056,8 +1056,8 @@ async def aclose(self) -> None:
"""
if not self.is_closed:
self.is_closed = True
if self._request is not None:
self._elapsed = self.request.timer.elapsed
if self._elapsed_func is not None:
self._elapsed = await self._elapsed_func()
await self._raw_stream.aclose()


Expand Down
51 changes: 29 additions & 22 deletions httpx/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import os
import re
import sys
import time
import typing
import warnings
from datetime import timedelta
from pathlib import Path
from time import perf_counter
from types import TracebackType
from urllib.request import getproxies

import sniffio

from ._types import PrimitiveData

if typing.TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -392,28 +392,35 @@ def flatten_queryparams(
return items


class ElapsedTimer:
def __init__(self) -> None:
self.start: float = perf_counter()
self.end: typing.Optional[float] = None
class Timer:
async def _get_time(self) -> float:
library = sniffio.current_async_library()
if library == "trio":
import trio

def __enter__(self) -> "ElapsedTimer":
self.start = perf_counter()
return self
return trio.current_time()
elif library == "curio": # pragma: nocover
import curio

def __exit__(
self,
exc_type: typing.Type[BaseException] = None,
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
self.end = perf_counter()
return await curio.clock()

@property
def elapsed(self) -> timedelta:
if self.end is None:
return timedelta(seconds=perf_counter() - self.start)
return timedelta(seconds=self.end - self.start)
import asyncio

return asyncio.get_event_loop().time()

def sync_start(self) -> None:
self.started = time.perf_counter()

async def async_start(self) -> None:
self.started = await self._get_time()

def sync_elapsed(self) -> float:
now = time.perf_counter()
return now - self.started

async def async_elapsed(self) -> float:
now = await self._get_time()
return now - self.started


class URLPattern:
Expand Down
2 changes: 0 additions & 2 deletions tests/models/test_responses.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
import json
from unittest import mock

Expand Down Expand Up @@ -31,7 +30,6 @@ def test_response():
assert response.text == "Hello, world!"
assert response.request.method == "GET"
assert response.request.url == "https://example.org"
assert response.elapsed >= datetime.timedelta(0)
assert not response.is_error


Expand Down
13 changes: 0 additions & 13 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import asyncio
import os
import random

import pytest

import httpx
from httpx._utils import (
ElapsedTimer,
NetRCInfo,
URLPattern,
get_ca_bundle_from_env,
Expand Down Expand Up @@ -177,17 +175,6 @@ def test_get_ssl_cert_file():
assert get_ca_bundle_from_env() is None


@pytest.mark.asyncio
async def test_elapsed_timer():
with ElapsedTimer() as timer:
assert timer.elapsed.total_seconds() == pytest.approx(0, abs=0.05)
await asyncio.sleep(0.1)
await asyncio.sleep(
0.1
) # test to ensure time spent after timer exits isn't accounted for.
assert timer.elapsed.total_seconds() == pytest.approx(0.1, abs=0.05)


@pytest.mark.parametrize(
["environment", "proxies"],
[
Expand Down

0 comments on commit a783fe5

Please sign in to comment.