Обновлены возвращаемые типы

This commit is contained in:
2025-10-26 14:24:58 +03:00
parent e380eda03a
commit 107729ec8b
14 changed files with 38 additions and 130 deletions

View File

@ -1,6 +1,6 @@
[project]
name = "aiohttpx"
version = "0.1.0"
version = "0.2.0"
description = "Custom HTTPX client with aiohttp transport, rate limiter and caching"
readme = "README.md"
authors = [

View File

@ -1 +1 @@
__version__: str = '0.1.0'
__version__: str = '0.2.0'

View File

@ -33,7 +33,7 @@ class AioHTTPXClient(AsyncHTTPXClient):
redis_url: str | None = None,
key: str | None = None,
limit: int | None = None,
):
) -> None:
super().__init__(
auth=auth,
params=params,

View File

@ -5,5 +5,5 @@ from orjson import loads
class Response(HTTPXResponse):
def json(self, **kwargs: Any):
def json(self, **kwargs: Any) -> Any:
return loads(self.content, **kwargs)

View File

@ -1,7 +1,7 @@
import asyncio
from logging import getLogger
from types import TracebackType
from typing import Any
from typing import Any, Self
import aiohttp
import httpx
@ -32,12 +32,27 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
def __init__(
self,
session: aiohttp.ClientSession | None = None,
):
) -> None:
self.logger = getLogger(__name__)
self._session = session or aiohttp.ClientSession()
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():
if isinstance(exc, aiohttp_exc):
return httpx_exc(message=str(exc)) # type: ignore
@ -47,7 +62,7 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
return httpx.HTTPError(f'Unknown error: {exc!s}')
async def __aenter__(self):
async def __aenter__(self) -> Self:
await self._session.__aenter__()
return self
@ -56,16 +71,16 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
exc_type: type[BaseException] | None = None,
exc_value: BaseException | None = None,
traceback: TracebackType | None = None,
):
) -> None:
await self._session.__aexit__(exc_type, exc_value, traceback)
self._closed = True
async def aclose(self):
async def aclose(self) -> None:
if not self._closed:
self._closed = True
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)
method = request.method
url = str(request.url)
@ -102,7 +117,7 @@ class AiohttpTransport(httpx.AsyncBaseTransport):
async def make_request(
self, method: str, url: str, headers: dict[str, Any], data: bytes
):
) -> Response:
if self._closed:
msg = 'Cannot make request: Transport session is closed'
raise RuntimeError(msg)

View File

@ -7,7 +7,7 @@ from aiohttpx.responses import Response
from aiohttpx.transports.rate_limiter import AsyncRateLimit, Redis
def generate_cache_key(request: Request):
def generate_cache_key(request: Request) -> str:
request_data = {
'method': request.method,
'url': str(request.url),
@ -28,7 +28,7 @@ def cache_response(
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:
try:
return int(headers['X-Cache-TTL'])
@ -36,7 +36,9 @@ def get_ttl_from_headers(headers: m.Headers):
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)
if cached_data:
@ -45,7 +47,7 @@ def get_cached_response(client: Redis[bytes], cache_key: str):
return None
def serialize_response(response: Response | HTTPXResponse):
def serialize_response(response: Response | HTTPXResponse) -> bytes:
response_data = {
'status_code': response.status_code,
'headers': dict(response.headers),
@ -55,7 +57,7 @@ def serialize_response(response: Response | HTTPXResponse):
return dumps(response_data)
def deserialize_response(serialized_response: bytes):
def deserialize_response(serialized_response: bytes) -> Response:
response_data = loads(serialized_response)
return Response(
@ -68,7 +70,7 @@ def deserialize_response(serialized_response: bytes):
class AsyncCacheTransport(AsyncRateLimit):
def __init__(
self, redis_url: str | None, key: str | None, limit: int | None
):
) -> None:
if redis_url:
self.client = Redis.from_url(redis_url)
else:
@ -76,7 +78,7 @@ class AsyncCacheTransport(AsyncRateLimit):
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:
return await self.transport.handle_async_request(request)

View File

@ -10,7 +10,7 @@ from aiohttpx.transports.aio import AiohttpTransport
class AsyncRateLimit(AiohttpTransport):
def __init__(
self, redis: Redis[bytes] | None, key: str | None, limit: int | None
):
) -> None:
self.transport = AiohttpTransport()
self.client = redis
self.key = key
@ -25,7 +25,7 @@ class AsyncRateLimit(AiohttpTransport):
msg = 'Incorrectly configured for rate limiting'
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:
t: int = int(self.client.time()[0]) # type: ignore
separation = round(60 / self.limit)

View File

@ -1 +0,0 @@
__version__: str = ...

View File

@ -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']

View File

@ -1,6 +0,0 @@
from typing import Any
from httpx import Response as HTTPXResponse
class Response(HTTPXResponse):
def json(self, **kwargs: Any) -> Any: ...

View File

@ -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: ...

View File

@ -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: ...

View File

@ -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: ...