mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-31 06:56:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			163 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """[HTTPX](https://www.python-httpx.org/) 驱动适配
 | |
| 
 | |
| ```bash
 | |
| nb driver install httpx
 | |
| # 或者
 | |
| pip install nonebot2[httpx]
 | |
| ```
 | |
| 
 | |
| :::tip 提示
 | |
| 本驱动仅支持客户端 HTTP 连接
 | |
| :::
 | |
| 
 | |
| FrontMatter:
 | |
|     mdx:
 | |
|         format: md
 | |
|     sidebar_position: 3
 | |
|     description: nonebot.drivers.httpx 模块
 | |
| """
 | |
| 
 | |
| from typing import TYPE_CHECKING, Optional, Union
 | |
| from typing_extensions import override
 | |
| 
 | |
| from multidict import CIMultiDict
 | |
| 
 | |
| from nonebot.drivers import (
 | |
|     URL,
 | |
|     HTTPClientMixin,
 | |
|     HTTPClientSession,
 | |
|     HTTPVersion,
 | |
|     Request,
 | |
|     Response,
 | |
|     combine_driver,
 | |
| )
 | |
| from nonebot.drivers.none import Driver as NoneDriver
 | |
| from nonebot.internal.driver import Cookies, CookieTypes, HeaderTypes, QueryTypes
 | |
| 
 | |
| try:
 | |
|     import httpx
 | |
| except ModuleNotFoundError as e:  # pragma: no cover
 | |
|     raise ImportError(
 | |
|         "Please install httpx first to use this driver. "
 | |
|         "Install with pip: `pip install nonebot2[httpx]`"
 | |
|     ) from e
 | |
| 
 | |
| 
 | |
| class Session(HTTPClientSession):
 | |
|     @override
 | |
|     def __init__(
 | |
|         self,
 | |
|         params: QueryTypes = None,
 | |
|         headers: HeaderTypes = None,
 | |
|         cookies: CookieTypes = None,
 | |
|         version: Union[str, HTTPVersion] = HTTPVersion.H11,
 | |
|         timeout: Optional[float] = None,
 | |
|         proxy: Optional[str] = None,
 | |
|     ):
 | |
|         self._client: Optional[httpx.AsyncClient] = None
 | |
| 
 | |
|         self._params = (
 | |
|             tuple(URL.build(query=params).query.items()) if params is not None else None
 | |
|         )
 | |
|         self._headers = (
 | |
|             tuple(CIMultiDict(headers).items()) if headers is not None else None
 | |
|         )
 | |
|         self._cookies = Cookies(cookies)
 | |
|         self._version = HTTPVersion(version)
 | |
|         self._timeout = timeout
 | |
|         self._proxy = proxy
 | |
| 
 | |
|     @property
 | |
|     def client(self) -> httpx.AsyncClient:
 | |
|         if self._client is None:
 | |
|             raise RuntimeError("Session is not initialized")
 | |
|         return self._client
 | |
| 
 | |
|     @override
 | |
|     async def request(self, setup: Request) -> Response:
 | |
|         response = await self.client.request(
 | |
|             setup.method,
 | |
|             str(setup.url),
 | |
|             content=setup.content,
 | |
|             data=setup.data,
 | |
|             files=setup.files,
 | |
|             json=setup.json,
 | |
|             # ensure the params priority
 | |
|             params=setup.url.raw_query_string,
 | |
|             headers=tuple(setup.headers.items()),
 | |
|             cookies=setup.cookies.jar,
 | |
|             timeout=setup.timeout,
 | |
|         )
 | |
|         return Response(
 | |
|             response.status_code,
 | |
|             headers=response.headers.multi_items(),
 | |
|             content=response.content,
 | |
|             request=setup,
 | |
|         )
 | |
| 
 | |
|     @override
 | |
|     async def setup(self) -> None:
 | |
|         if self._client is not None:
 | |
|             raise RuntimeError("Session has already been initialized")
 | |
|         self._client = httpx.AsyncClient(
 | |
|             params=self._params,
 | |
|             headers=self._headers,
 | |
|             cookies=self._cookies.jar,
 | |
|             http2=self._version == HTTPVersion.H2,
 | |
|             proxy=self._proxy,
 | |
|             follow_redirects=True,
 | |
|         )
 | |
|         await self._client.__aenter__()
 | |
| 
 | |
|     @override
 | |
|     async def close(self) -> None:
 | |
|         try:
 | |
|             if self._client is not None:
 | |
|                 await self._client.aclose()
 | |
|         finally:
 | |
|             self._client = None
 | |
| 
 | |
| 
 | |
| class Mixin(HTTPClientMixin):
 | |
|     """HTTPX Mixin"""
 | |
| 
 | |
|     @property
 | |
|     @override
 | |
|     def type(self) -> str:
 | |
|         return "httpx"
 | |
| 
 | |
|     @override
 | |
|     async def request(self, setup: Request) -> Response:
 | |
|         async with self.get_session(
 | |
|             version=setup.version, proxy=setup.proxy
 | |
|         ) as session:
 | |
|             return await session.request(setup)
 | |
| 
 | |
|     @override
 | |
|     def get_session(
 | |
|         self,
 | |
|         params: QueryTypes = None,
 | |
|         headers: HeaderTypes = None,
 | |
|         cookies: CookieTypes = None,
 | |
|         version: Union[str, HTTPVersion] = HTTPVersion.H11,
 | |
|         timeout: Optional[float] = None,
 | |
|         proxy: Optional[str] = None,
 | |
|     ) -> Session:
 | |
|         return Session(
 | |
|             params=params,
 | |
|             headers=headers,
 | |
|             cookies=cookies,
 | |
|             version=version,
 | |
|             timeout=timeout,
 | |
|             proxy=proxy,
 | |
|         )
 | |
| 
 | |
| 
 | |
| if TYPE_CHECKING:
 | |
| 
 | |
|     class Driver(Mixin, NoneDriver): ...
 | |
| 
 | |
| else:
 | |
|     Driver = combine_driver(NoneDriver, Mixin)
 | |
|     """HTTPX Driver"""
 |