From 0d16636d790f437777c18b50dcdb4a58356b8cd1 Mon Sep 17 00:00:00 2001 From: JahongirHakimjonov Date: Thu, 2 Oct 2025 15:11:56 +0500 Subject: [PATCH 1/3] refactor: migrate from requests to httpx for synchronous and asynchronous HTTP clients --- click_up/classes/http.py | 12 +++++--- click_up/typing/response/shop_api.py | 2 +- clickup_fastapi/core/http.py | 45 +++++++++++++++++++++------- requirements/requirements.txt | 2 +- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/click_up/classes/http.py b/click_up/classes/http.py index 1d2299b..3c2a016 100644 --- a/click_up/classes/http.py +++ b/click_up/classes/http.py @@ -1,12 +1,17 @@ import json -from requests import request +from httpx import Client, HTTPTransport + +# Global reusable sync client +sync_http_transport = HTTPTransport(retries=3, http2=True) +http_client = Client(timeout=10, transport=sync_http_transport) class Http: """ A class for making HTTP requests. """ + def __init__(self): self.headers = { "Content-Type": "application/json", @@ -29,9 +34,8 @@ def post(self, url, body, headers, timeout=10): """ headers = self.headers | headers data = json.dumps(body) - result = request( - method="POST", url=url, - headers=headers, data=data, timeout=timeout + result = http_client.post( + url=url, headers=headers, json=data, timeout=timeout ) result.raise_for_status() return result.json() diff --git a/click_up/typing/response/shop_api.py b/click_up/typing/response/shop_api.py index 6f0ed2c..0d3e32f 100644 --- a/click_up/typing/response/shop_api.py +++ b/click_up/typing/response/shop_api.py @@ -3,7 +3,7 @@ @dataclass -class ClickShopApiRespone: +class ClickShopApiResponse: """ Represents a payment transaction in the CLICK system. diff --git a/clickup_fastapi/core/http.py b/clickup_fastapi/core/http.py index 0e63bff..04cbc46 100644 --- a/clickup_fastapi/core/http.py +++ b/clickup_fastapi/core/http.py @@ -1,27 +1,52 @@ import httpx -import json +from httpx import AsyncClient, AsyncHTTPTransport + +# Global reusable async client +async_http_transport = AsyncHTTPTransport(retries=3, http2=True) +async_http_client = AsyncClient( + transport=async_http_transport, + timeout=10, +) class Http: def __init__(self): - self.headers = { + self.default_headers = { "Content-Type": "application/json", "Accept": "application/json", } async def post( - self, url: str, body: dict, headers: dict, timeout: int = 10 + self, + url: str, + body: dict, + headers: dict, + timeout: int = 10 ): """ POST so‘rovini asinxron yuborish. """ - headers = self.headers | headers - async with httpx.AsyncClient() as client: - result = await client.post( + headers = {**self.default_headers, **(headers or {})} + + try: + response = await async_http_client.post( url, headers=headers, - content=json.dumps(body), - timeout=timeout + json=body, + timeout=timeout, ) - result.raise_for_status() - return result.json() + response.raise_for_status() + return response.json() + except httpx.HTTPStatusError as e: + # 2xx bo'lmagan javob + return { + "error": True, + "status_code": e.response.status_code, + "detail": e.response.text, + } + except httpx.RequestError as e: + # Tarmoq xatosi, vaqt tugashi, DNS va boshqalar. + return { + "error": True, + "detail": str(e), + } diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 7e05367..92fe86b 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,3 +1,3 @@ -requests==2.* dataclasses==0.* djangorestframework==3.* +httpx[http2]==0.28.* From 38d93d59e6672be0af556cee1b75e75b359d0a48 Mon Sep 17 00:00:00 2001 From: JahongirHakimjonov Date: Fri, 3 Oct 2025 15:32:29 +0500 Subject: [PATCH 2/3] refactor: update header handling and streamline async POST request in http client --- click_up/classes/http.py | 9 ++++++--- clickup_fastapi/core/http.py | 33 +++++++++------------------------ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/click_up/classes/http.py b/click_up/classes/http.py index 3c2a016..2f2744d 100644 --- a/click_up/classes/http.py +++ b/click_up/classes/http.py @@ -13,7 +13,7 @@ class Http: """ def __init__(self): - self.headers = { + self.default_headers = { "Content-Type": "application/json", "Accept": "application/json", } @@ -32,10 +32,13 @@ def post(self, url, body, headers, timeout=10): Returns: dict: The response from the server. """ - headers = self.headers | headers + headers = self.default_headers | headers data = json.dumps(body) result = http_client.post( - url=url, headers=headers, json=data, timeout=timeout + url=url, + headers=headers, + json=data, + timeout=timeout ) result.raise_for_status() return result.json() diff --git a/clickup_fastapi/core/http.py b/clickup_fastapi/core/http.py index 04cbc46..98c6cc4 100644 --- a/clickup_fastapi/core/http.py +++ b/clickup_fastapi/core/http.py @@ -1,4 +1,3 @@ -import httpx from httpx import AsyncClient, AsyncHTTPTransport # Global reusable async client @@ -26,27 +25,13 @@ async def post( """ POST so‘rovini asinxron yuborish. """ - headers = {**self.default_headers, **(headers or {})} + headers = self.default_headers | headers - try: - response = await async_http_client.post( - url, - headers=headers, - json=body, - timeout=timeout, - ) - response.raise_for_status() - return response.json() - except httpx.HTTPStatusError as e: - # 2xx bo'lmagan javob - return { - "error": True, - "status_code": e.response.status_code, - "detail": e.response.text, - } - except httpx.RequestError as e: - # Tarmoq xatosi, vaqt tugashi, DNS va boshqalar. - return { - "error": True, - "detail": str(e), - } + response = await async_http_client.post( + url, + headers=headers, + json=body, + timeout=timeout, + ) + response.raise_for_status() + return response.json() From a10a0bc90bebf45835f513af095fc34bfe4f6907 Mon Sep 17 00:00:00 2001 From: JahongirHakimjonov Date: Fri, 3 Oct 2025 15:36:12 +0500 Subject: [PATCH 3/3] refactor: update httpx dependency to support HTTP/2 in setup.py --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 63a8e8a..4e9bf2e 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ python_requires='>=3.6', install_requires=[ - 'requests>=2.0,<3.0', + 'httpx[http2]>=0.28.1,<1.0', "dataclasses>=0.6,<1.0; python_version<'3.7'", ], @@ -27,10 +27,11 @@ 'django': [ 'django>=3.0,<5.0', 'djangorestframework>=3.0,<4.0', + 'httpx[http2]>=0.28.1,<1.0', ], 'fastapi': [ 'sqlalchemy>=1.4,<3.0', - 'httpx>=0.20,<1.0', + 'httpx[http2]>=0.28.1,<1.0', 'python-multipart==0.0.20', 'pydantic>=1.8,<2.0', ],