nonebot-plugin-htmlrender 0.6.2__py3-none-any.whl → 0.6.5__py3-none-any.whl

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.
@@ -13,7 +13,7 @@ from playwright.async_api import (
13
13
 
14
14
  from nonebot_plugin_htmlrender.config import plugin_config
15
15
  from nonebot_plugin_htmlrender.install import install_browser
16
- from nonebot_plugin_htmlrender.utils import proxy_settings, suppress_and_log
16
+ from nonebot_plugin_htmlrender.utils import proxy_settings, suppress_and_log, with_lock
17
17
 
18
18
  _browser: Optional[Browser] = None
19
19
  _playwright: Optional[Playwright] = None
@@ -53,8 +53,6 @@ async def init_browser(**kwargs) -> Browser:
53
53
  Raises:
54
54
  RuntimeError: 如果浏览器无法启动或安装失败。
55
55
  """
56
- await install_browser() # update playwright when start
57
- await check_playwright_env()
58
56
  return await start_browser(**kwargs)
59
57
 
60
58
 
@@ -72,12 +70,11 @@ async def get_new_page(device_scale_factor: float = 2, **kwargs) -> AsyncIterato
72
70
  """
73
71
  ctx = await get_browser()
74
72
  page = await ctx.new_page(device_scale_factor=device_scale_factor, **kwargs)
75
- try:
73
+ async with page:
76
74
  yield page
77
- finally:
78
- await page.close()
79
75
 
80
76
 
77
+ @with_lock
81
78
  async def get_browser(**kwargs) -> Browser:
82
79
  """
83
80
  获取浏览器实例。
@@ -117,6 +114,32 @@ async def _connect_via_cdp(**kwargs) -> Browser:
117
114
  raise RuntimeError("Playwright 未初始化")
118
115
 
119
116
 
117
+ async def _connect(browser_type: str, **kwargs) -> Browser:
118
+ """
119
+ 通过 Playwright 协议连接浏览器。
120
+
121
+ Args:
122
+ browser_type (str): 浏览器类型。
123
+ **kwargs: 传递给`playwright.connect`的关键字参数。
124
+
125
+ Returns:
126
+ Browser: 启动的浏览器实例。
127
+
128
+ Raises:
129
+ RuntimeError: 如果 Playwright 未初始化。
130
+ """
131
+ _browser_cls: BrowserType = getattr(_playwright, browser_type)
132
+ kwargs["ws_endpoint"] = plugin_config.htmlrender_connect
133
+ logger.info(
134
+ f"正在使用 Playwright 协议连接 {browser_type}({plugin_config.htmlrender_connect})"
135
+ )
136
+ if _playwright is not None:
137
+ return await _browser_cls.connect(**kwargs)
138
+ else:
139
+ raise RuntimeError("Playwright 未初始化")
140
+
141
+
142
+ @with_lock
120
143
  async def start_browser(**kwargs) -> Browser:
121
144
  """
122
145
  启动 Playwright 浏览器实例。
@@ -128,24 +151,35 @@ async def start_browser(**kwargs) -> Browser:
128
151
  Browser: 启动的浏览器实例。
129
152
  """
130
153
  global _browser, _playwright
154
+
155
+ await shutdown_browser()
131
156
  _playwright = await async_playwright().start()
132
157
 
133
158
  if (
134
159
  plugin_config.htmlrender_browser == "chromium"
135
160
  and plugin_config.htmlrender_connect_over_cdp
136
161
  ):
137
- return await _connect_via_cdp(**kwargs)
162
+ _browser = await _connect_via_cdp(**kwargs)
163
+ elif plugin_config.htmlrender_connect:
164
+ _browser = await _connect(plugin_config.htmlrender_browser, **kwargs)
165
+ else:
166
+ if plugin_config.htmlrender_browser_channel:
167
+ kwargs["channel"] = plugin_config.htmlrender_browser_channel
138
168
 
139
- if plugin_config.htmlrender_browser_channel:
140
- kwargs["channel"] = plugin_config.htmlrender_browser_channel
169
+ if plugin_config.htmlrender_proxy_host:
170
+ kwargs["proxy"] = proxy_settings(plugin_config.htmlrender_proxy_host)
141
171
 
142
- if plugin_config.htmlrender_proxy_host:
143
- kwargs["proxy"] = proxy_settings(plugin_config.htmlrender_proxy_host)
172
+ if plugin_config.htmlrender_browser_executable_path:
173
+ kwargs["executable_path"] = plugin_config.htmlrender_browser_executable_path
174
+ else:
175
+ try:
176
+ await check_playwright_env()
177
+ except RuntimeError:
178
+ await install_browser()
179
+ await check_playwright_env()
144
180
 
145
- if plugin_config.htmlrender_browser_executable_path:
146
- kwargs["executable_path"] = plugin_config.htmlrender_browser_executable_path
181
+ _browser = await _launch(plugin_config.htmlrender_browser, **kwargs)
147
182
 
148
- _browser = await _launch(plugin_config.htmlrender_browser, **kwargs)
149
183
  return _browser
150
184
 
151
185
 
@@ -164,7 +198,7 @@ async def check_playwright_env():
164
198
  logger.info("Checking Playwright environment...")
165
199
  try:
166
200
  async with async_playwright() as p:
167
- await p.chromium.launch()
201
+ await getattr(p, plugin_config.htmlrender_browser).launch()
168
202
  except Exception as e:
169
203
  raise RuntimeError(
170
204
  "Playwright environment is not set up correctly. "
@@ -1,10 +1,9 @@
1
- from typing import Optional
2
- from typing_extensions import Self
1
+ from typing import Any, Optional
3
2
 
4
3
  from nonebot import get_driver, get_plugin_config
4
+ from nonebot.compat import model_validator
5
5
  from pydantic import BaseModel, Field
6
6
 
7
- from nonebot_plugin_htmlrender.compat import model_validator
8
7
  from nonebot_plugin_htmlrender.consts import BROWSER_CHANNEL_TYPES, BROWSER_ENGINE_TYPES
9
8
 
10
9
 
@@ -36,25 +35,39 @@ class Config(BaseModel):
36
35
  htmlrender_connect_over_cdp: Optional[str] = Field(
37
36
  default=None, description="通过 CDP 连接Playwright浏览器的端点地址。"
38
37
  )
38
+ htmlrender_connect: Optional[str] = Field(
39
+ default=None, description="通过Playwright协议连接Playwright浏览器的端点地址。"
40
+ )
39
41
 
40
42
  @model_validator(mode="after")
41
- def check_browser_channel(self) -> Self:
42
- if (
43
- self.htmlrender_browser_channel is not None
44
- and self.htmlrender_browser_channel not in BROWSER_CHANNEL_TYPES
45
- ):
43
+ @classmethod
44
+ def check_browser_channel(cls, data: Any) -> Any:
45
+ browser_channel = (
46
+ data.get("htmlrender_browser_channel")
47
+ if isinstance(data, dict)
48
+ else getattr(data, "htmlrender_browser_channel", None)
49
+ )
50
+
51
+ if browser_channel is not None and browser_channel not in BROWSER_CHANNEL_TYPES:
46
52
  raise ValueError(
47
53
  f"Invalid browser channel type. Must be one of {BROWSER_CHANNEL_TYPES}"
48
54
  )
49
- return self
55
+ return data
50
56
 
51
57
  @model_validator(mode="after")
52
- def check_browser(self) -> Self:
53
- if self.htmlrender_browser not in BROWSER_ENGINE_TYPES:
58
+ @classmethod
59
+ def check_browser(cls, data: Any) -> Any:
60
+ browser = (
61
+ data.get("htmlrender_browser", "chromium")
62
+ if isinstance(data, dict)
63
+ else getattr(data, "htmlrender_browser", "chromium")
64
+ )
65
+
66
+ if browser not in BROWSER_ENGINE_TYPES:
54
67
  raise ValueError(
55
68
  f"Invalid browser type. Must be one of {BROWSER_ENGINE_TYPES}"
56
69
  )
57
- return self
70
+ return data
58
71
 
59
72
 
60
73
  global_config = get_driver().config
@@ -1,3 +1,5 @@
1
+ from asyncio import Lock
2
+ from collections.abc import Awaitable
1
3
  from contextlib import contextmanager
2
4
  from functools import wraps
3
5
  import re
@@ -146,3 +148,14 @@ def proxy_settings(proxy_host: Optional[str]) -> Optional[dict]:
146
148
  proxy["bypass"] = plugin_config.htmlrender_proxy_host_bypass
147
149
 
148
150
  return proxy
151
+
152
+
153
+ def with_lock(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]:
154
+ lock = Lock()
155
+
156
+ @wraps(func)
157
+ async def wrapper(*args, **kwargs) -> R:
158
+ async with lock:
159
+ return await func(*args, **kwargs)
160
+
161
+ return wrapper
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-htmlrender
3
- Version: 0.6.2
3
+ Version: 0.6.5
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
@@ -31,7 +31,7 @@ Requires-Python: <4.0,>=3.9
31
31
  Requires-Dist: aiofiles>=0.8.0
32
32
  Requires-Dist: jinja2>=3.0.3
33
33
  Requires-Dist: markdown>=3.3.6
34
- Requires-Dist: nonebot2>=2.2.0
34
+ Requires-Dist: nonebot2>=2.4.2
35
35
  Requires-Dist: playwright>=1.48.0
36
36
  Requires-Dist: pygments>=2.10.0
37
37
  Requires-Dist: pymdown-extensions>=9.1
@@ -1,13 +1,12 @@
1
1
  nonebot_plugin_htmlrender/__init__.py,sha256=fzqf-XaZ_lxzr18kHgxD16Zq4BaU2e-UidoHGVzDyHg,1825
2
- nonebot_plugin_htmlrender/browser.py,sha256=zMdFvDQrZ1sZtV6c3Kh7RAwj4FBcoWA85jt147-4jaA,4925
3
- nonebot_plugin_htmlrender/compat.py,sha256=eeAOMbJB0qMYkUmO8pbM11hxGCZuVUQ2sYEy8XTV81g,826
4
- nonebot_plugin_htmlrender/config.py,sha256=-fywpLhQHVCZ2s6c3wG5ywREZbUFcW9ZKrrRj8ptEfQ,2272
2
+ nonebot_plugin_htmlrender/browser.py,sha256=gFYv1x-i7uWrNm3_9RBRLUI41uORyPzZ0j0B6pz1sig,6036
3
+ nonebot_plugin_htmlrender/config.py,sha256=5rsY0KTAqYqZGrwx9sq1VRIZ7lN1S12wSvnOFB63j9o,2731
5
4
  nonebot_plugin_htmlrender/consts.py,sha256=eMujEJaBDsQw8wwIJH3sb3twS0eo7jCiQd-TD671950,930
6
5
  nonebot_plugin_htmlrender/data_source.py,sha256=rFHyW0JYFRI9bhnbGHRw8c7sMWGMkvpb66LziFn2Ns0,10270
7
6
  nonebot_plugin_htmlrender/install.py,sha256=ejfoPkyixHN83ldFhNihCGA25IjOLqtncEjEbh_C6Sw,8186
8
7
  nonebot_plugin_htmlrender/process.py,sha256=BqU0GBj_NYl5mSxyVIpF35PApcQDjiF3jT6hdhLqniA,4526
9
8
  nonebot_plugin_htmlrender/signal.py,sha256=sOOqIns5QIi5404mZoEtt3ssliXS3IGndkt0WMreNe0,3920
10
- nonebot_plugin_htmlrender/utils.py,sha256=IYiJWWLzWRqCZUC1mmGyaXCiDO15C-JEd3datX_lhUE,4019
9
+ nonebot_plugin_htmlrender/utils.py,sha256=hnaJZ9wI9RNLm6dSiar2HnjtfpHzikmbsYbi8bDydkI,4334
11
10
  nonebot_plugin_htmlrender/templates/github-markdown-light.css,sha256=bWV9QiirgHYgauZBcR9YJ3IvEoA3fP2Cf5aOdshBVrI,17297
12
11
  nonebot_plugin_htmlrender/templates/markdown.html,sha256=y-_F5YMVLHceU9nuuEAtw2tw3qOdSKzfmciuS13cZkM,630
13
12
  nonebot_plugin_htmlrender/templates/pygments-default.css,sha256=lwkE40qSsvPe52nGAZW1luJ7qNeIWpgw7vDX-MZpCDA,4889
@@ -17,7 +16,7 @@ nonebot_plugin_htmlrender/templates/katex/katex.min.b64_fonts.css,sha256=8KXk7iV
17
16
  nonebot_plugin_htmlrender/templates/katex/katex.min.js,sha256=cXQ4tsltOzinSxpNqnPldAdfpstIPXBDKihVb7ccZL0,270288
18
17
  nonebot_plugin_htmlrender/templates/katex/mathtex-script-type.min.js,sha256=d-R93Fs6-ZBEU3VEH7u_qz-b02iCPKEPmLJEgRf2-MU,1290
19
18
  nonebot_plugin_htmlrender/templates/katex/mhchem.min.js,sha256=8MoD3xlLjD1gF_9FXbag75iFeQVmP6MRps3teIsVNAs,33730
20
- nonebot_plugin_htmlrender-0.6.2.dist-info/METADATA,sha256=7S6E1LOFfirWqx_jpJyzesvje_g23NKUJuRnPSX_hzM,6721
21
- nonebot_plugin_htmlrender-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- nonebot_plugin_htmlrender-0.6.2.dist-info/licenses/LICENSE,sha256=4HHgpu3ihnBZ9AVZDgOzGnAJRt8W7IQo5VSBEJKRr5g,1062
23
- nonebot_plugin_htmlrender-0.6.2.dist-info/RECORD,,
19
+ nonebot_plugin_htmlrender-0.6.5.dist-info/METADATA,sha256=ciQ2iK4ZFx3ajnOCiGl3E0nphEY44j7tLlVc7VARfgU,6721
20
+ nonebot_plugin_htmlrender-0.6.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ nonebot_plugin_htmlrender-0.6.5.dist-info/licenses/LICENSE,sha256=4HHgpu3ihnBZ9AVZDgOzGnAJRt8W7IQo5VSBEJKRr5g,1062
22
+ nonebot_plugin_htmlrender-0.6.5.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- from typing import Literal, overload
2
-
3
- from nonebot.compat import PYDANTIC_V2
4
-
5
- __all__ = ("model_validator",)
6
-
7
- # todo)) 等nb更新切换到nb提供的兼容层
8
- if PYDANTIC_V2:
9
- from pydantic import field_validator as field_validator
10
- from pydantic import model_validator as model_validator
11
- else:
12
- from pydantic import root_validator, validator
13
-
14
- @overload
15
- def model_validator(*, mode: Literal["before"]): ...
16
-
17
- @overload
18
- def model_validator(*, mode: Literal["after"]): ...
19
-
20
- def model_validator(*, mode: Literal["before", "after"] = "after"):
21
- return root_validator(pre=mode == "before", allow_reuse=True)
22
-
23
- def field_validator(__field, *fields, mode: Literal["before", "after"] = "after"): # noqa: PYI063
24
- return validator(__field, *fields, pre=mode == "before", allow_reuse=True)