Обновлены возвращаемые типы
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "aiohttpx"
|
name = "aiohttpx"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
description = "Custom HTTPX client with aiohttp transport, rate limiter and caching"
|
description = "Custom HTTPX client with aiohttp transport, rate limiter and caching"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [
|
authors = [
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
__version__: str = '0.1.0'
|
__version__: str = '0.2.0'
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class AioHTTPXClient(AsyncHTTPXClient):
|
|||||||
redis_url: str | None = None,
|
redis_url: str | None = None,
|
||||||
key: str | None = None,
|
key: str | None = None,
|
||||||
limit: int | None = None,
|
limit: int | None = None,
|
||||||
):
|
) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
auth=auth,
|
auth=auth,
|
||||||
params=params,
|
params=params,
|
||||||
|
|||||||
@ -5,5 +5,5 @@ from orjson import loads
|
|||||||
|
|
||||||
|
|
||||||
class Response(HTTPXResponse):
|
class Response(HTTPXResponse):
|
||||||
def json(self, **kwargs: Any):
|
def json(self, **kwargs: Any) -> Any:
|
||||||
return loads(self.content, **kwargs)
|
return loads(self.content, **kwargs)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import httpx
|
import httpx
|
||||||
@ -32,12 +32,27 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session: aiohttp.ClientSession | None = None,
|
session: aiohttp.ClientSession | None = None,
|
||||||
):
|
) -> None:
|
||||||
self.logger = getLogger(__name__)
|
self.logger = getLogger(__name__)
|
||||||
self._session = session or aiohttp.ClientSession()
|
self._session = session or aiohttp.ClientSession()
|
||||||
self._closed = False
|
self._closed = False
|
||||||
|
|
||||||
def map_aiohttp_exception(self, exc: Exception):
|
def map_aiohttp_exception(
|
||||||
|
self, exc: Exception
|
||||||
|
) -> (
|
||||||
|
httpx.ConnectError
|
||||||
|
| httpx.DecodingError
|
||||||
|
| httpx.HTTPStatusError
|
||||||
|
| httpx.InvalidURL
|
||||||
|
| httpx.NetworkError
|
||||||
|
| httpx.ProtocolError
|
||||||
|
| httpx.ProxyError
|
||||||
|
| httpx.ReadError
|
||||||
|
| httpx.RequestError
|
||||||
|
| httpx.TooManyRedirects
|
||||||
|
| httpx.TimeoutException
|
||||||
|
| httpx.HTTPError
|
||||||
|
):
|
||||||
for aiohttp_exc, httpx_exc in EXCEPTIONS.items():
|
for aiohttp_exc, httpx_exc in EXCEPTIONS.items():
|
||||||
if isinstance(exc, aiohttp_exc):
|
if isinstance(exc, aiohttp_exc):
|
||||||
return httpx_exc(message=str(exc)) # type: ignore
|
return httpx_exc(message=str(exc)) # type: ignore
|
||||||
@ -47,7 +62,7 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
|
|||||||
|
|
||||||
return httpx.HTTPError(f'Unknown error: {exc!s}')
|
return httpx.HTTPError(f'Unknown error: {exc!s}')
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self) -> Self:
|
||||||
await self._session.__aenter__()
|
await self._session.__aenter__()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -56,16 +71,16 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
|
|||||||
exc_type: type[BaseException] | None = None,
|
exc_type: type[BaseException] | None = None,
|
||||||
exc_value: BaseException | None = None,
|
exc_value: BaseException | None = None,
|
||||||
traceback: TracebackType | None = None,
|
traceback: TracebackType | None = None,
|
||||||
):
|
) -> None:
|
||||||
await self._session.__aexit__(exc_type, exc_value, traceback)
|
await self._session.__aexit__(exc_type, exc_value, traceback)
|
||||||
self._closed = True
|
self._closed = True
|
||||||
|
|
||||||
async def aclose(self):
|
async def aclose(self) -> None:
|
||||||
if not self._closed:
|
if not self._closed:
|
||||||
self._closed = True
|
self._closed = True
|
||||||
await self._session.close()
|
await self._session.close()
|
||||||
|
|
||||||
async def handle_async_request(self, request: httpx.Request):
|
async def handle_async_request(self, request: httpx.Request) -> Response:
|
||||||
headers = dict(request.headers)
|
headers = dict(request.headers)
|
||||||
method = request.method
|
method = request.method
|
||||||
url = str(request.url)
|
url = str(request.url)
|
||||||
@ -102,7 +117,7 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
|
|||||||
|
|
||||||
async def make_request(
|
async def make_request(
|
||||||
self, method: str, url: str, headers: dict[str, Any], data: bytes
|
self, method: str, url: str, headers: dict[str, Any], data: bytes
|
||||||
):
|
) -> Response:
|
||||||
if self._closed:
|
if self._closed:
|
||||||
msg = 'Cannot make request: Transport session is closed'
|
msg = 'Cannot make request: Transport session is closed'
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from aiohttpx.responses import Response
|
|||||||
from aiohttpx.transports.rate_limiter import AsyncRateLimit, Redis
|
from aiohttpx.transports.rate_limiter import AsyncRateLimit, Redis
|
||||||
|
|
||||||
|
|
||||||
def generate_cache_key(request: Request):
|
def generate_cache_key(request: Request) -> str:
|
||||||
request_data = {
|
request_data = {
|
||||||
'method': request.method,
|
'method': request.method,
|
||||||
'url': str(request.url),
|
'url': str(request.url),
|
||||||
@ -28,7 +28,7 @@ def cache_response(
|
|||||||
client.set(cache_key, serialized_response, ex=ttl)
|
client.set(cache_key, serialized_response, ex=ttl)
|
||||||
|
|
||||||
|
|
||||||
def get_ttl_from_headers(headers: m.Headers):
|
def get_ttl_from_headers(headers: m.Headers) -> int | None:
|
||||||
if 'X-Cache-TTL' in headers:
|
if 'X-Cache-TTL' in headers:
|
||||||
try:
|
try:
|
||||||
return int(headers['X-Cache-TTL'])
|
return int(headers['X-Cache-TTL'])
|
||||||
@ -36,7 +36,9 @@ def get_ttl_from_headers(headers: m.Headers):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_cached_response(client: Redis[bytes], cache_key: str):
|
def get_cached_response(
|
||||||
|
client: Redis[bytes], cache_key: str
|
||||||
|
) -> Response | None:
|
||||||
cached_data = client.get(cache_key)
|
cached_data = client.get(cache_key)
|
||||||
|
|
||||||
if cached_data:
|
if cached_data:
|
||||||
@ -45,7 +47,7 @@ def get_cached_response(client: Redis[bytes], cache_key: str):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def serialize_response(response: Response | HTTPXResponse):
|
def serialize_response(response: Response | HTTPXResponse) -> bytes:
|
||||||
response_data = {
|
response_data = {
|
||||||
'status_code': response.status_code,
|
'status_code': response.status_code,
|
||||||
'headers': dict(response.headers),
|
'headers': dict(response.headers),
|
||||||
@ -55,7 +57,7 @@ def serialize_response(response: Response | HTTPXResponse):
|
|||||||
return dumps(response_data)
|
return dumps(response_data)
|
||||||
|
|
||||||
|
|
||||||
def deserialize_response(serialized_response: bytes):
|
def deserialize_response(serialized_response: bytes) -> Response:
|
||||||
response_data = loads(serialized_response)
|
response_data = loads(serialized_response)
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
@ -68,7 +70,7 @@ def deserialize_response(serialized_response: bytes):
|
|||||||
class AsyncCacheTransport(AsyncRateLimit):
|
class AsyncCacheTransport(AsyncRateLimit):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, redis_url: str | None, key: str | None, limit: int | None
|
self, redis_url: str | None, key: str | None, limit: int | None
|
||||||
):
|
) -> None:
|
||||||
if redis_url:
|
if redis_url:
|
||||||
self.client = Redis.from_url(redis_url)
|
self.client = Redis.from_url(redis_url)
|
||||||
else:
|
else:
|
||||||
@ -76,7 +78,7 @@ class AsyncCacheTransport(AsyncRateLimit):
|
|||||||
|
|
||||||
self.transport = AsyncRateLimit(self.client, key, limit)
|
self.transport = AsyncRateLimit(self.client, key, limit)
|
||||||
|
|
||||||
async def handle_async_request(self, request: Request):
|
async def handle_async_request(self, request: Request) -> Response:
|
||||||
if not self.client:
|
if not self.client:
|
||||||
return await self.transport.handle_async_request(request)
|
return await self.transport.handle_async_request(request)
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from aiohttpx.transports.aio import AiohttpTransport
|
|||||||
class AsyncRateLimit(AiohttpTransport):
|
class AsyncRateLimit(AiohttpTransport):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, redis: Redis[bytes] | None, key: str | None, limit: int | None
|
self, redis: Redis[bytes] | None, key: str | None, limit: int | None
|
||||||
):
|
) -> None:
|
||||||
self.transport = AiohttpTransport()
|
self.transport = AiohttpTransport()
|
||||||
self.client = redis
|
self.client = redis
|
||||||
self.key = key
|
self.key = key
|
||||||
@ -25,7 +25,7 @@ class AsyncRateLimit(AiohttpTransport):
|
|||||||
msg = 'Incorrectly configured for rate limiting'
|
msg = 'Incorrectly configured for rate limiting'
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
async def request_is_limited(self):
|
async def request_is_limited(self) -> bool:
|
||||||
if self.client and self.key and self.limit:
|
if self.client and self.key and self.limit:
|
||||||
t: int = int(self.client.time()[0]) # type: ignore
|
t: int = int(self.client.time()[0]) # type: ignore
|
||||||
separation = round(60 / self.limit)
|
separation = round(60 / self.limit)
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
__version__: str = ...
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
from collections.abc import Callable, Mapping
|
|
||||||
from ssl import SSLContext
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from httpx import URL, Limits
|
|
||||||
from httpx import AsyncClient as AsyncHTTPXClient
|
|
||||||
from httpx import _types as t # type: ignore
|
|
||||||
|
|
||||||
class AioHTTPXClient(AsyncHTTPXClient):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
auth: t.AuthTypes | None = ...,
|
|
||||||
params: t.QueryParamTypes | None = ...,
|
|
||||||
headers: t.HeaderTypes | None = ...,
|
|
||||||
cookies: t.CookieTypes | None = ...,
|
|
||||||
verify: SSLContext | str | bool = ...,
|
|
||||||
cert: t.CertTypes | None = ...,
|
|
||||||
proxy: t.ProxyTypes | None = ...,
|
|
||||||
timeout: t.TimeoutTypes = ...,
|
|
||||||
follow_redirects: bool = ...,
|
|
||||||
limits: Limits = ...,
|
|
||||||
max_redirects: int = ...,
|
|
||||||
event_hooks: Mapping[str, list[Callable[..., Any]]] | None = ...,
|
|
||||||
base_url: URL | str = ...,
|
|
||||||
trust_env: bool = ...,
|
|
||||||
default_encoding: str | Callable[[bytes], str] = ...,
|
|
||||||
redis_url: str | None = ...,
|
|
||||||
key: str | None = ...,
|
|
||||||
limit: int | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
|
|
||||||
__all__ = ['AioHTTPXClient']
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from typing import Any
|
|
||||||
|
|
||||||
from httpx import Response as HTTPXResponse
|
|
||||||
|
|
||||||
class Response(HTTPXResponse):
|
|
||||||
def json(self, **kwargs: Any) -> Any: ...
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
from types import TracebackType
|
|
||||||
from typing import Any, Self
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from aiohttpx.responses import Response
|
|
||||||
|
|
||||||
EXCEPTIONS = ...
|
|
||||||
|
|
||||||
class AiohttpTransport(httpx.AsyncBaseTransport):
|
|
||||||
def __init__(
|
|
||||||
self, session: aiohttp.ClientSession | None = ...
|
|
||||||
) -> None: ...
|
|
||||||
def map_aiohttp_exception(
|
|
||||||
self, exc: Exception
|
|
||||||
) -> httpx.TimeoutException | httpx.HTTPError: ...
|
|
||||||
async def __aenter__(self) -> Self: ...
|
|
||||||
async def __aexit__(
|
|
||||||
self,
|
|
||||||
exc_type: type[BaseException] | None = ...,
|
|
||||||
exc_value: BaseException | None = ...,
|
|
||||||
traceback: TracebackType | None = ...,
|
|
||||||
) -> None: ...
|
|
||||||
async def aclose(self) -> None: ...
|
|
||||||
async def handle_async_request(
|
|
||||||
self, request: httpx.Request
|
|
||||||
) -> Response: ...
|
|
||||||
async def make_request(
|
|
||||||
self, method: str, url: str, headers: dict[str, Any], data: bytes
|
|
||||||
) -> Response: ...
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
from httpx import Request
|
|
||||||
from httpx import Response as HTTPXResponse
|
|
||||||
from httpx import _models as m # type: ignore
|
|
||||||
|
|
||||||
from aiohttpx.responses import Response
|
|
||||||
from aiohttpx.transports.rate_limiter import AsyncRateLimit, Redis
|
|
||||||
|
|
||||||
def generate_cache_key(request: Request) -> str: ...
|
|
||||||
def cache_response(
|
|
||||||
client: Redis[bytes],
|
|
||||||
cache_key: str,
|
|
||||||
request: Request,
|
|
||||||
response: Response | HTTPXResponse,
|
|
||||||
) -> None: ...
|
|
||||||
def get_ttl_from_headers(headers: m.Headers) -> int | None: ...
|
|
||||||
def get_cached_response(
|
|
||||||
client: Redis[bytes], cache_key: str
|
|
||||||
) -> Response | None: ...
|
|
||||||
def serialize_response(response: Response | HTTPXResponse) -> bytes: ...
|
|
||||||
def deserialize_response(serialized_response: bytes) -> Response: ...
|
|
||||||
|
|
||||||
class AsyncCacheTransport(AsyncRateLimit):
|
|
||||||
def __init__(
|
|
||||||
self, redis_url: str | None, key: str | None, limit: int | None
|
|
||||||
) -> None: ...
|
|
||||||
async def handle_async_request(self, request: Request) -> Response: ...
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
from httpx import Request
|
|
||||||
from redis import Redis
|
|
||||||
|
|
||||||
from aiohttpx.responses import Response
|
|
||||||
from aiohttpx.transports.aio import AiohttpTransport
|
|
||||||
|
|
||||||
class AsyncRateLimit(AiohttpTransport):
|
|
||||||
def __init__(
|
|
||||||
self, redis: Redis[bytes] | None, key: str | None, limit: int | None
|
|
||||||
) -> None: ...
|
|
||||||
async def request_is_limited(self) -> bool: ...
|
|
||||||
async def handle_async_request(self, request: Request) -> Response: ...
|
|
||||||
Reference in New Issue
Block a user