nonebot-plugin-htmlrender 0.6.1__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/PKG-INFO +1 -1
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/browser.py +1 -1
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/data_source.py +27 -12
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/pyproject.toml +1 -1
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/test_htmlrender.py +74 -1
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/uv.lock +255 -240
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/.github/workflows/codecov.yml +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/.github/workflows/publish.yml +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/.gitignore +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/.pre-commit-config.yaml +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/.python-version +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/LICENSE +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/README.md +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/docs/example.md +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/docs/html2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/docs/md2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/docs/template2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/docs/text2pic.png +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/.env.dev +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/bot.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/__init__.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/html2pic.html +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/templates/markdown.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/templates/mystyle.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/templates/progress.html.jinja2 +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/templates/text.html +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/example/plugins/render/utils.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/__init__.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/compat.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/config.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/consts.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/install.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/process.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/signal.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/github-markdown-light.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/katex/katex.min.b64_fonts.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/katex/katex.min.js +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/katex/mathtex-script-type.min.js +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/katex/mhchem.min.js +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/markdown.html +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/pygments-default.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/text.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/templates/text.html +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/nonebot_plugin_htmlrender/utils.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/__init__.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/conftest.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/resources/test_template_filter.png +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/templates/markdown.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/templates/mystyle.css +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/templates/progress.html.jinja2 +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/templates/text.html +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/test_browser.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/test_deprecated_decorator.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/test_install.py +0 -0
- {nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/test_process.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nonebot-plugin-htmlrender
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: 通过浏览器渲染图片
|
|
5
5
|
Project-URL: Homepage, https://github.com/kexue-z/nonebot-plugin-htmlrender
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/kexue-z/nonebot-plugin-htmlrender/issues
|
|
@@ -61,7 +61,7 @@ async def init_browser(**kwargs) -> Browser:
|
|
|
61
61
|
@asynccontextmanager
|
|
62
62
|
async def get_new_page(device_scale_factor: float = 2, **kwargs) -> AsyncIterator[Page]:
|
|
63
63
|
"""
|
|
64
|
-
|
|
64
|
+
获取一个新的页面的上下文管理器, 这里的 page 默认使用设备缩放因子为 2。
|
|
65
65
|
|
|
66
66
|
Args:
|
|
67
67
|
device_scale_factor (float): 设备缩放因子。
|
|
@@ -284,17 +284,32 @@ async def template_to_pic(
|
|
|
284
284
|
async def capture_element(
|
|
285
285
|
url: str,
|
|
286
286
|
element: str,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
screenshot_timeout: Optional[float] = 30_000,
|
|
291
|
-
**kwargs,
|
|
287
|
+
page_kwargs: Optional[dict] = None,
|
|
288
|
+
goto_kwargs: Optional[dict] = None,
|
|
289
|
+
screenshot_kwargs: Optional[dict] = None,
|
|
292
290
|
) -> bytes:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
291
|
+
"""捕获网页中指定元素的截图, 通过CSS选择器或XPath表达式指定元素。
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
url: 目标网页URL
|
|
295
|
+
element: CSS选择器或XPath表达式
|
|
296
|
+
page_kwargs: 传递给get_new_page的参数
|
|
297
|
+
goto_kwargs: 传递给page.goto方法的额外参数
|
|
298
|
+
screenshot_kwargs: 传递给screenshot方法的额外参数
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
bytes: 元素截图数据
|
|
302
|
+
"""
|
|
303
|
+
page_kwargs = page_kwargs or {}
|
|
304
|
+
goto_kwargs = goto_kwargs or {}
|
|
305
|
+
screenshot_kwargs = screenshot_kwargs or {}
|
|
306
|
+
|
|
307
|
+
async with get_new_page(**page_kwargs) as page:
|
|
308
|
+
page.on(
|
|
309
|
+
"console",
|
|
310
|
+
lambda msg: logger.opt(colors=True).debug(
|
|
311
|
+
f"<cyan>[Browser Console]</cyan> {msg.text}"
|
|
312
|
+
),
|
|
300
313
|
)
|
|
314
|
+
await page.goto(url, **goto_kwargs)
|
|
315
|
+
return await page.locator(element).screenshot(**screenshot_kwargs)
|
{nonebot_plugin_htmlrender-0.6.1 → nonebot_plugin_htmlrender-0.6.2}/tests/test_htmlrender.py
RENAMED
|
@@ -5,6 +5,7 @@ from typing import Any
|
|
|
5
5
|
from nonebug import App
|
|
6
6
|
from PIL import Image, ImageChops
|
|
7
7
|
import pytest
|
|
8
|
+
from pytest_mock import MockerFixture
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@pytest.fixture
|
|
@@ -39,7 +40,7 @@ def template_resources(request: Any) -> tuple[str, str, list[str]]:
|
|
|
39
40
|
elif template_type == "text":
|
|
40
41
|
template_name = "text.html"
|
|
41
42
|
data_list = ["1", "2", "3", "4"]
|
|
42
|
-
else:
|
|
43
|
+
else: # pragma: no cover
|
|
43
44
|
raise ValueError(f"Unsupported template type: {template_type}")
|
|
44
45
|
|
|
45
46
|
return template_path, template_name, data_list
|
|
@@ -137,3 +138,75 @@ async def test_template_filter(
|
|
|
137
138
|
image = Image.open(BytesIO(initial_bytes=image_byte))
|
|
138
139
|
diff = ImageChops.difference(image, test_image)
|
|
139
140
|
assert diff.getbbox() is None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@pytest.mark.asyncio
|
|
144
|
+
async def test_capture_element(mocker: MockerFixture) -> None:
|
|
145
|
+
"""测试网页元素捕获功能"""
|
|
146
|
+
from nonebot_plugin_htmlrender.data_source import capture_element
|
|
147
|
+
|
|
148
|
+
mock_screenshot = b"test_image_bytes"
|
|
149
|
+
|
|
150
|
+
mock_locator = mocker.AsyncMock()
|
|
151
|
+
mock_locator.screenshot.return_value = mock_screenshot
|
|
152
|
+
|
|
153
|
+
mock_page = mocker.AsyncMock()
|
|
154
|
+
mock_page.goto = mocker.AsyncMock()
|
|
155
|
+
mock_page.on = mocker.AsyncMock()
|
|
156
|
+
mock_page.locator = mocker.MagicMock(return_value=mock_locator)
|
|
157
|
+
|
|
158
|
+
mock_cm = mocker.MagicMock()
|
|
159
|
+
mock_cm.__aenter__ = mocker.AsyncMock(return_value=mock_page)
|
|
160
|
+
mock_cm.__aexit__ = mocker.AsyncMock(return_value=None)
|
|
161
|
+
|
|
162
|
+
mocker.patch(
|
|
163
|
+
"nonebot_plugin_htmlrender.data_source.get_new_page", return_value=mock_cm
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
result = await capture_element("https://example.com", "#target-element")
|
|
167
|
+
|
|
168
|
+
assert result == mock_screenshot
|
|
169
|
+
mock_page.goto.assert_called_once_with("https://example.com")
|
|
170
|
+
mock_page.locator.assert_called_once_with("#target-element")
|
|
171
|
+
mock_locator.screenshot.assert_called_once_with()
|
|
172
|
+
|
|
173
|
+
mock_page.goto.reset_mock()
|
|
174
|
+
mock_page.locator.reset_mock()
|
|
175
|
+
mock_locator.screenshot.reset_mock()
|
|
176
|
+
|
|
177
|
+
page_kwargs = {"device_scale_factor": 2.0}
|
|
178
|
+
goto_kwargs = {"timeout": 5000}
|
|
179
|
+
screenshot_kwargs = {"type": "jpeg", "quality": 80}
|
|
180
|
+
|
|
181
|
+
result = await capture_element(
|
|
182
|
+
"https://example.com",
|
|
183
|
+
"//div[@id='xpath-element']",
|
|
184
|
+
page_kwargs=page_kwargs,
|
|
185
|
+
goto_kwargs=goto_kwargs,
|
|
186
|
+
screenshot_kwargs=screenshot_kwargs,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
assert result == mock_screenshot
|
|
190
|
+
mock_page.goto.assert_called_once_with("https://example.com", timeout=5000)
|
|
191
|
+
mock_page.locator.assert_called_once_with("//div[@id='xpath-element']")
|
|
192
|
+
mock_locator.screenshot.assert_called_once_with(type="jpeg", quality=80)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@pytest.mark.asyncio
|
|
196
|
+
async def test_capture_element_exceptions_propagate(mocker: MockerFixture) -> None:
|
|
197
|
+
"""测试网页元素捕获时的异常能正确传递"""
|
|
198
|
+
from playwright.async_api import Error
|
|
199
|
+
|
|
200
|
+
from nonebot_plugin_htmlrender.data_source import capture_element
|
|
201
|
+
|
|
202
|
+
mock_cm = mocker.MagicMock()
|
|
203
|
+
mock_cm.__aenter__ = mocker.AsyncMock(side_effect=Error("Browser error"))
|
|
204
|
+
|
|
205
|
+
mocker.patch(
|
|
206
|
+
"nonebot_plugin_htmlrender.data_source.get_new_page", return_value=mock_cm
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
with pytest.raises(Error) as exc_info:
|
|
210
|
+
await capture_element("https://example.com", "#element")
|
|
211
|
+
|
|
212
|
+
assert "Browser error" in str(exc_info.value)
|