From 4f343b39ef5f7040044fe35cd7c4ca2e2aeabe32 Mon Sep 17 00:00:00 2001 From: NEFORCEO Date: Wed, 1 Apr 2026 02:48:46 +0700 Subject: [PATCH 01/16] edit uv.lock --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index 8983ea7..2e13dc6 100644 --- a/uv.lock +++ b/uv.lock @@ -465,7 +465,7 @@ wheels = [ [[package]] name = "fasthttp-client" -version = "1.1.4" +version = "1.2.2" source = { editable = "." } dependencies = [ { name = "annotated-doc" }, From 1ae27edadad6ab4ab23fdee81259589565214497 Mon Sep 17 00:00:00 2001 From: NEFORCEO Date: Wed, 1 Apr 2026 02:50:07 +0700 Subject: [PATCH 02/16] edit response.py --- fasthttp/response.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/fasthttp/response.py b/fasthttp/response.py index 6607a62..3dcd360 100644 --- a/fasthttp/response.py +++ b/fasthttp/response.py @@ -1,9 +1,30 @@ import json +import re from typing import Annotated, Any +from urllib.parse import urljoin from annotated_doc import Doc +def extract_assets(html: str, base_url: str) -> dict: + css_pattern = re.compile( + r']+rel=["\']stylesheet["\'][^>]+href=["\']([^"\']+)["\']', + re.IGNORECASE + ) + js_pattern = re.compile( + r']+src=["\']([^"\']+)["\']', + re.IGNORECASE + ) + + css_links = [urljoin(base_url, href) for href in css_pattern.findall(html)] + js_links = [urljoin(base_url, src) for src in js_pattern.findall(html)] + + return { + "css": css_links, + "js": js_links + } + + class Response: """ HTTP response object. @@ -116,6 +137,14 @@ def __init__( self._query = query self._req_json = req_json self._req_data = req_data + self._url: str | None = None + + def _set_url(self, url: str | None) -> None: + self._url = url + + @property + def url(self) -> str | None: + return self._url @property def method( @@ -245,6 +274,20 @@ def req_text( return str(self._req_data) return None + def assets( + self, + *, + css: bool = True, + js: bool = True + ) -> dict[str, list[str]]: + base_url = self._url if self._url else "" + result = extract_assets(self.text, base_url) + if not css: + result["css"] = [] + if not js: + result["js"] = [] + return result + def __repr__(self) -> str: """ Return a debug-friendly string representation From 46d5de432cd66456446e1d402064b99a60999694 Mon Sep 17 00:00:00 2001 From: NEFORCEO Date: Wed, 1 Apr 2026 03:01:00 +0700 Subject: [PATCH 03/16] =?UTF-8?q?=D0=B2=D1=88=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fasthttp/client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fasthttp/client.py b/fasthttp/client.py index 6f2c597..afff8a6 100644 --- a/fasthttp/client.py +++ b/fasthttp/client.py @@ -239,7 +239,7 @@ def _build_response( config: dict, response: httpx.Response ) -> Response: - return Response( + resp = Response( status=response.status_code, text=response.text, headers=dict(response.headers), @@ -249,6 +249,8 @@ def _build_response( req_json=route.json, req_data=route.data, ) + resp._set_url(route.url) + return resp async def _process_handler_result( self, @@ -334,6 +336,7 @@ async def send( headers={}, method=route.method, ) + empty_response._set_url(route.url) handler_result = await route.handler(empty_response) return await self._process_handler_result( empty_response, From 5b1743a7e0b4ec49ef5816c01d6b050f18f4e714 Mon Sep 17 00:00:00 2001 From: NEFORCEO Date: Wed, 1 Apr 2026 03:01:57 +0700 Subject: [PATCH 04/16] edit response.py --- fasthttp/response.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fasthttp/response.py b/fasthttp/response.py index 3dcd360..82917b5 100644 --- a/fasthttp/response.py +++ b/fasthttp/response.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import re from typing import Annotated, Any From 442c09715a7cbe6bc52cef6abbb46dde297b5214 Mon Sep 17 00:00:00 2001 From: NEFORCEO Date: Wed, 1 Apr 2026 03:02:24 +0700 Subject: [PATCH 05/16] create extract_assets.py --- examples/basic/extract_assets.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/basic/extract_assets.py diff --git a/examples/basic/extract_assets.py b/examples/basic/extract_assets.py new file mode 100644 index 0000000..e038882 --- /dev/null +++ b/examples/basic/extract_assets.py @@ -0,0 +1,26 @@ +from fasthttp import FastHTTP +from fasthttp.response import Response + + +app = FastHTTP(debug=True) + + +@app.get(url="https://v7-agency.com") +async def get_assets(resp: Response) -> dict: + return resp.assets() + + +@app.get(url="https://v7-agency.com") +async def get_css_only(resp: Response) -> dict: + return resp.assets(js=False) + + +@app.get(url="https://v7-agency.com") +async def get_js_only(resp: Response) -> dict: + return resp.assets(css=False) + +@app.get(url="https://v7-agency.com") +async def get_js_only_all(resp: Response) -> dict: + return resp.assets() + +app.run() From 713eb26b6070261d3998eccf01f18d5e5f02eaf7 Mon Sep 17 00:00:00 2001 From: NEFORCEO Date: Wed, 1 Apr 2026 03:02:58 +0700 Subject: [PATCH 06/16] edit response.md --- docs/ru/reference/response.md | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/ru/reference/response.md b/docs/ru/reference/response.md index 16894e1..a07a79a 100644 --- a/docs/ru/reference/response.md +++ b/docs/ru/reference/response.md @@ -11,6 +11,7 @@ | `headers` | `dict` | Заголовки ответа | | `content` | `bytes` | Сырые байты | | `method` | `str` | HTTP метод | +| `url` | `str` | URL запроса | ## Методы @@ -30,6 +31,46 @@ data = resp.json() # Возвращает dict или list sent = resp.req_json() # Возвращает dict или None ``` +### assets() + +Извлечь URL-адреса CSS и JavaScript ресурсов из HTML-ответа: + +```python +result = resp.assets() +# Возвращает: {"css": [...], "js": [...]} +``` + +**Параметры:** + +| Параметр | Тип | По умолчанию | Описание | +|----------|-----|--------------|----------| +| `css` | `bool` | `True` | Включать ссылки CSS | +| `js` | `bool` | `True` | Включать ссылки JavaScript | + +**Примеры:** + +```python +# Получить все ресурсы (CSS + JS) +@app.get(url="https://example.com") +async def handler(resp: Response): + return resp.assets() +# Возвращает: {"css": ["https://example.com/style.css"], "js": ["https://example.com/app.js"]} + +# Только CSS +@app.get(url="https://example.com") +async def handler(resp: Response): + return resp.assets(js=False) +# Возвращает: {"css": [...], "js": []} + +# Только JS +@app.get(url="https://example.com") +async def handler(resp: Response): + return resp.assets(css=False) +# Возвращает: {"css": [], "js": [...]} +``` + +Метод парсит `` для CSS и `