Replace aiohttp with httpx

This commit is contained in:
Andras Schmelczer 2022-07-08 20:30:57 +02:00
parent 46ffe7a70e
commit 299d644bc4
No known key found for this signature in database
GPG key ID: 0EA1BC97D0AB076E
15 changed files with 40 additions and 95 deletions

View file

@ -14,6 +14,7 @@
"finalise", "finalise",
"finalised", "finalised",
"gridfs", "gridfs",
"httpx",
"iloc", "iloc",
"initialisation", "initialisation",
"initialised", "initialised",
@ -62,7 +63,8 @@
"**/.mypy_cache": true, "**/.mypy_cache": true,
"**/.pytest_cache": true, "**/.pytest_cache": true,
"**/*.egg-info": true, "**/*.egg-info": true,
"**/*.cache": true "**/*.cache": true,
"**/tracing_database.json": true
}, },
"notebook.output.textLineLimit": 400, "notebook.output.textLineLimit": 400,
"python.defaultInterpreterPath": ".env/bin/python", "python.defaultInterpreterPath": ".env/bin/python",

View file

@ -4,9 +4,10 @@ __version__ = "0.0.12"
from .context import configure from .context import configure
from .deploy import GreatAI from .deploy import GreatAI
from .exceptions import ( from .errors import (
ArgumentValidationError, ArgumentValidationError,
MissingArgumentError, MissingArgumentError,
RemoteCallError,
WrongDecoratorOrderError, WrongDecoratorOrderError,
) )
from .models import save_model, use_model from .models import save_model, use_model
@ -15,13 +16,9 @@ from .output_views import (
MultiLabelClassificationOutput, MultiLabelClassificationOutput,
RegressionOutput, RegressionOutput,
) )
from .parameters import log_metric, parameter from .parameters.log_metric import log_metric
from .parameters.parameter import parameter
from .persistence import MongodbDriver, ParallelTinyDbDriver, TracingDatabaseDriver from .persistence import MongodbDriver, ParallelTinyDbDriver, TracingDatabaseDriver
from .remote import ( from .remote import call_remote_great_ai, call_remote_great_ai_async
HttpClient,
RemoteCallError,
call_remote_great_ai,
call_remote_great_ai_async,
)
from .tracing import add_ground_truth, delete_ground_truth, query_ground_truth from .tracing import add_ground_truth, delete_ground_truth, query_ground_truth
from .views import RouteConfig, Trace from .views import RouteConfig, Trace

View file

@ -17,7 +17,7 @@ from watchdog.observers import Observer
from great_ai.constants import SERVER_NAME from great_ai.constants import SERVER_NAME
from great_ai.context import _is_in_production_mode from great_ai.context import _is_in_production_mode
from great_ai.deploy import GreatAI from great_ai.deploy import GreatAI
from great_ai.exceptions import ArgumentValidationError, MissingArgumentError from great_ai.errors import ArgumentValidationError, MissingArgumentError
from great_ai.utilities import get_logger from great_ai.utilities import get_logger
from .parse_arguments import parse_arguments from .parse_arguments import parse_arguments

View file

@ -1,3 +1,4 @@
from .argument_validation_error import ArgumentValidationError from .argument_validation_error import ArgumentValidationError
from .missing_argument_error import MissingArgumentError from .missing_argument_error import MissingArgumentError
from .remote_call_error import RemoteCallError
from .wrong_decorator_order_error import WrongDecoratorOrderError from .wrong_decorator_order_error import WrongDecoratorOrderError

View file

@ -1,6 +1,6 @@
from typing import Callable from typing import Callable
from ..exceptions import WrongDecoratorOrderError from ..errors import WrongDecoratorOrderError
from .get_function_metadata_store import get_function_metadata_store from .get_function_metadata_store import get_function_metadata_store

View file

@ -1,3 +0,0 @@
from .automatically_decorate_parameters import automatically_decorate_parameters
from .log_metric import log_metric
from .parameter import parameter

View file

@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, TypeVar, cast
from typeguard import check_type from typeguard import check_type
from ..exceptions import ArgumentValidationError from ..errors import ArgumentValidationError
from ..helper import get_arguments, get_function_metadata_store from ..helper import get_arguments, get_function_metadata_store
from ..helper.assert_function_is_not_finalised import assert_function_is_not_finalised from ..helper.assert_function_is_not_finalised import assert_function_is_not_finalised
from ..tracing.tracing_context import TracingContext from ..tracing.tracing_context import TracingContext

View file

@ -1,4 +1,2 @@
from .call_remote_great_ai import call_remote_great_ai from .call_remote_great_ai import call_remote_great_ai
from .call_remote_great_ai_async import call_remote_great_ai_async from .call_remote_great_ai_async import call_remote_great_ai_async
from .http_client import HttpClient
from .remote_call_error import RemoteCallError

View file

@ -1,12 +1,10 @@
from typing import Any, Mapping, Optional, Type, TypeVar from typing import Any, Mapping, Optional, Type, TypeVar
import httpx
from pydantic import BaseModel from pydantic import BaseModel
from ..errors.remote_call_error import RemoteCallError
from ..views import Trace from ..views import Trace
from .http_client import HttpClient
from .remote_call_error import RemoteCallError
http: Optional[HttpClient] = None
T = TypeVar("T", bound=BaseModel) T = TypeVar("T", bound=BaseModel)
@ -15,23 +13,35 @@ async def call_remote_great_ai_async(
base_uri: str, base_uri: str,
data: Mapping[str, Any], data: Mapping[str, Any],
retry_count: int = 4, retry_count: int = 4,
timeout_in_seconds: int = 60,
model_class: Optional[Type[T]] = None, model_class: Optional[Type[T]] = None,
) -> Trace[T]: ) -> Trace[T]:
global http
if http is None:
http = HttpClient()
if base_uri.endswith("/"): if base_uri.endswith("/"):
base_uri = base_uri[:-1] base_uri = base_uri[:-1]
url = f"{base_uri}/predict/" if not base_uri.endswith("/predict"):
response = await http.post( base_uri = f"{base_uri}/predict"
url=url, data=data, retry_count=retry_count, expected_status=200
) transport = httpx.AsyncHTTPTransport(retries=retry_count)
async with httpx.AsyncClient(
transport=transport, timeout=timeout_in_seconds
) as client:
response = await client.post(base_uri, json=data)
try:
response.raise_for_status()
except Exception:
raise RemoteCallError("Unexpected status code")
try: try:
if model_class is not None: trace = response.json()
response["output"] = model_class.parse_obj(response["output"])
return Trace.parse_obj(response)
except Exception: except Exception:
raise RemoteCallError("Could not parse response") raise RemoteCallError(
f"JSON parsing failed {response.text}",
)
try:
if model_class is not None:
trace["output"] = model_class.parse_obj(trace["output"])
return Trace.parse_obj(trace)
except Exception:
raise RemoteCallError("Could not parse Trace")

View file

@ -1,60 +0,0 @@
import logging
from asyncio import sleep
from typing import Any, Mapping, Optional
import aiohttp
from .remote_call_error import RemoteCallError
logger = logging.getLogger("http")
class HttpClient:
timeout_seconds: int = 600
wait_between_retries_seconds: float = 5
def __init__(
self,
) -> None:
timeout = aiohttp.ClientTimeout(total=self.timeout_seconds)
self._session = aiohttp.ClientSession(
raise_for_status=False,
timeout=timeout,
)
async def post(
self,
url: str,
data: Mapping[str, Any],
retry_count: int = 0,
expected_status: Optional[int] = None,
**kwargs: Any,
) -> Any:
for i in range(retry_count + 1):
try:
async with self._session.post(url, json=data, **kwargs) as r:
if (
expected_status is not None and r.status != expected_status
) or r.status >= 500:
response_text = await r.text()
raise ValueError(
f"Found not-expected status code: {r.status}, response is: {response_text}"
)
try:
return await r.json()
except Exception:
raise RemoteCallError(
"JSON parsing failed",
)
except Exception as e:
if retry_count - i > 1:
logger.warning(
f"Request failed ({e}), {retry_count - i - 1} retries left",
)
await sleep(self.wait_between_retries_seconds)
raise RemoteCallError(f"Request has failed too many ({retry_count + 1}) times")
async def close(self) -> None:
await self._session.close()

View file

@ -37,10 +37,11 @@ dependencies = [
"uvicorn[standard] >= 0.18.0", "uvicorn[standard] >= 0.18.0",
"watchdog >= 2.1.0", "watchdog >= 2.1.0",
"typeguard >= 2.10.0", "typeguard >= 2.10.0",
"pymongo >= 3.0.0", "pymongo >= 4.0.0",
"dill >= 0.3.5.0", "dill >= 0.3.5.0",
'tqdm',
"async_lru >= 1.0.0", "async_lru >= 1.0.0",
"aiohttp[speedups] >= 3.8.0", "httpx >= 0.20.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]
@ -60,7 +61,6 @@ dev = [
"pytest-cov", "pytest-cov",
"pytest-subtests", "pytest-subtests",
"pytest-asyncio", "pytest-asyncio",
'tqdm',
] ]
[project.urls] [project.urls]