ayaka.driver.ayakabot.model

  1import json
  2import dataclasses
  3from enum import Enum
  4from http.cookiejar import Cookie, CookieJar
  5from typing import (
  6    IO,
  7    Any,
  8    Dict,
  9    List,
 10    Tuple,
 11    Union,
 12    Mapping,
 13    Callable,
 14    Iterator,
 15    Optional,
 16    MutableMapping,
 17)
 18
 19from yarl import URL
 20from multidict import CIMultiDict
 21
 22RawURL = Tuple[bytes, bytes, Optional[int], bytes]
 23
 24SimpleQuery = Union[str, int, float]
 25QueryVariable = Union[SimpleQuery, List[SimpleQuery]]
 26QueryTypes = Union[
 27    None, str, Mapping[str, QueryVariable], List[Tuple[str, QueryVariable]]
 28]
 29
 30HeaderTypes = Union[
 31    None,
 32    CIMultiDict[str],
 33    Dict[str, str],
 34    List[Tuple[str, str]],
 35]
 36
 37CookieTypes = Union[None, "Cookies", CookieJar, Dict[str, str], List[Tuple[str, str]]]
 38
 39ContentTypes = Union[str, bytes, None]
 40DataTypes = Union[dict, None]
 41FileContent = Union[IO[bytes], bytes]
 42FileType = Tuple[Optional[str], FileContent, Optional[str]]
 43FileTypes = Union[
 44    # file (or bytes)
 45    FileContent,
 46    # (filename, file (or bytes))
 47    Tuple[Optional[str], FileContent],
 48    # (filename, file (or bytes), content_type)
 49    FileType,
 50]
 51FilesTypes = Union[Dict[str, FileTypes], List[Tuple[str, FileTypes]], None]
 52
 53
 54class HTTPVersion(Enum):
 55    H10 = "1.0"
 56    H11 = "1.1"
 57    H2 = "2"
 58
 59
 60class Request:
 61    def __init__(
 62        self,
 63        method: Union[str, bytes],
 64        url: Union["URL", str, RawURL],
 65        *,
 66        params: QueryTypes = None,
 67        headers: HeaderTypes = None,
 68        cookies: CookieTypes = None,
 69        content: ContentTypes = None,
 70        data: DataTypes = None,
 71        json: Any = None,
 72        files: FilesTypes = None,
 73        version: Union[str, HTTPVersion] = HTTPVersion.H11,
 74        timeout: Optional[float] = None,
 75        proxy: Optional[str] = None,
 76    ):
 77        # method
 78        self.method: str = (
 79            method.decode("ascii").upper()
 80            if isinstance(method, bytes)
 81            else method.upper()
 82        )
 83        # http version
 84        self.version: HTTPVersion = HTTPVersion(version)
 85        # timeout
 86        self.timeout: Optional[float] = timeout
 87        # proxy
 88        self.proxy: Optional[str] = proxy
 89
 90        # url
 91        if isinstance(url, tuple):
 92            scheme, host, port, path = url
 93            url = URL.build(
 94                scheme=scheme.decode("ascii"),
 95                host=host.decode("ascii"),
 96                port=port,
 97                path=path.decode("ascii"),
 98            )
 99        else:
100            url = URL(url)
101
102        if params is not None:
103            url = url.update_query(params)
104        self.url: URL = url
105
106        # headers
107        self.headers: CIMultiDict[str]
108        if headers is not None:
109            self.headers = CIMultiDict(headers)
110        else:
111            self.headers = CIMultiDict()
112
113        # cookies
114        self.cookies = Cookies(cookies)
115
116        # body
117        self.content: ContentTypes = content
118        self.data: DataTypes = data
119        self.json: Any = json
120        self.files: Optional[List[Tuple[str, FileType]]] = None
121        if files:
122            self.files = []
123            files_ = files.items() if isinstance(files, dict) else files
124            for name, file_info in files_:
125                if not isinstance(file_info, tuple):
126                    self.files.append((name, (None, file_info, None)))
127                elif len(file_info) == 2:
128                    self.files.append((name, (file_info[0], file_info[1], None)))
129                else:
130                    self.files.append((name, file_info))  # type: ignore
131
132    def __repr__(self) -> str:
133        class_name = self.__class__.__name__
134        url = str(self.url)
135        return f"<{class_name}({self.method!r}, {url!r})>"
136
137
138class Cookies(MutableMapping):
139    def __init__(self, cookies: CookieTypes = None) -> None:
140        self.jar: CookieJar = cookies if isinstance(cookies, CookieJar) else CookieJar()
141        if cookies is not None and not isinstance(cookies, CookieJar):
142            if isinstance(cookies, dict):
143                for key, value in cookies.items():
144                    self.set(key, value)
145            elif isinstance(cookies, list):
146                for key, value in cookies:
147                    self.set(key, value)
148            elif isinstance(cookies, Cookies):
149                for cookie in cookies.jar:
150                    self.jar.set_cookie(cookie)
151            else:
152                raise TypeError(f"Cookies must be dict or list, not {type(cookies)}")
153
154    def set(self, name: str, value: str, domain: str = "", path: str = "/") -> None:
155        cookie = Cookie(
156            version=0,
157            name=name,
158            value=value,
159            port=None,
160            port_specified=False,
161            domain=domain,
162            domain_specified=bool(domain),
163            domain_initial_dot=domain.startswith("."),
164            path=path,
165            path_specified=bool(path),
166            secure=False,
167            expires=None,
168            discard=True,
169            comment=None,
170            comment_url=None,
171            rest={},
172            rfc2109=False,
173        )
174        self.jar.set_cookie(cookie)
175
176    def get(
177        self,
178        name: str,
179        default: Optional[str] = None,
180        domain: str = None,
181        path: str = None,
182    ) -> Optional[str]:
183        value: Optional[str] = None
184        for cookie in self.jar:
185            if (
186                cookie.name == name
187                and (domain is None or cookie.domain == domain)
188                and (path is None or cookie.path == path)
189            ):
190                if value is not None:
191                    message = f"Multiple cookies exist with name={name}"
192                    raise ValueError(message)
193                value = cookie.value
194
195        return default if value is None else value
196
197    def delete(
198        self, name: str, domain: Optional[str] = None, path: Optional[str] = None
199    ) -> None:
200        if domain is not None and path is not None:
201            return self.jar.clear(domain, path, name)
202
203        remove = [
204            cookie
205            for cookie in self.jar
206            if cookie.name == name
207            and (domain is None or cookie.domain == domain)
208            and (path is None or cookie.path == path)
209        ]
210
211        for cookie in remove:
212            self.jar.clear(cookie.domain, cookie.path, cookie.name)
213
214    def clear(self, domain: Optional[str] = None, path: Optional[str] = None) -> None:
215        self.jar.clear(domain, path)
216
217    def update(self, cookies: CookieTypes = None) -> None:
218        cookies = Cookies(cookies)
219        for cookie in cookies.jar:
220            self.jar.set_cookie(cookie)
221
222    def __setitem__(self, name: str, value: str) -> None:
223        return self.set(name, value)
224
225    def __getitem__(self, name: str) -> str:
226        value = self.get(name)
227        if value is None:
228            raise KeyError(name)
229        return value
230
231    def __delitem__(self, name: str) -> None:
232        return self.delete(name)
233
234    def __len__(self) -> int:
235        return len(self.jar)
236
237    def __iter__(self) -> Iterator[Cookie]:
238        return (cookie for cookie in self.jar)
239
240    def __repr__(self) -> str:
241        cookies_repr = ", ".join(
242            [
243                f"<Cookie {cookie.name}={cookie.value} for {cookie.domain} />"
244                for cookie in self.jar
245            ]
246        )
247
248        return f"<Cookies [{cookies_repr}]>"
249
250
251@dataclasses.dataclass
252class WebSocketServerSetup:
253    """WebSocket 服务器路由配置。"""
254
255    path: URL  # path should not be absolute, check it by URL.is_absolute() == False
256    name: str
257    handle_func: Callable
258
259class DataclassEncoder(json.JSONEncoder):
260    """在JSON序列化 `Message` (List[Dataclass]) 时使用的 `JSONEncoder`"""
261
262    def default(self, o):
263        if dataclasses.is_dataclass(o):
264            return dataclasses.asdict(o)
265        return super().default(o)
class HTTPVersion(enum.Enum):
55class HTTPVersion(Enum):
56    H10 = "1.0"
57    H11 = "1.1"
58    H2 = "2"

An enumeration.

H10 = <HTTPVersion.H10: '1.0'>
H11 = <HTTPVersion.H11: '1.1'>
H2 = <HTTPVersion.H2: '2'>
Inherited Members
enum.Enum
name
value
class Request:
 61class Request:
 62    def __init__(
 63        self,
 64        method: Union[str, bytes],
 65        url: Union["URL", str, RawURL],
 66        *,
 67        params: QueryTypes = None,
 68        headers: HeaderTypes = None,
 69        cookies: CookieTypes = None,
 70        content: ContentTypes = None,
 71        data: DataTypes = None,
 72        json: Any = None,
 73        files: FilesTypes = None,
 74        version: Union[str, HTTPVersion] = HTTPVersion.H11,
 75        timeout: Optional[float] = None,
 76        proxy: Optional[str] = None,
 77    ):
 78        # method
 79        self.method: str = (
 80            method.decode("ascii").upper()
 81            if isinstance(method, bytes)
 82            else method.upper()
 83        )
 84        # http version
 85        self.version: HTTPVersion = HTTPVersion(version)
 86        # timeout
 87        self.timeout: Optional[float] = timeout
 88        # proxy
 89        self.proxy: Optional[str] = proxy
 90
 91        # url
 92        if isinstance(url, tuple):
 93            scheme, host, port, path = url
 94            url = URL.build(
 95                scheme=scheme.decode("ascii"),
 96                host=host.decode("ascii"),
 97                port=port,
 98                path=path.decode("ascii"),
 99            )
100        else:
101            url = URL(url)
102
103        if params is not None:
104            url = url.update_query(params)
105        self.url: URL = url
106
107        # headers
108        self.headers: CIMultiDict[str]
109        if headers is not None:
110            self.headers = CIMultiDict(headers)
111        else:
112            self.headers = CIMultiDict()
113
114        # cookies
115        self.cookies = Cookies(cookies)
116
117        # body
118        self.content: ContentTypes = content
119        self.data: DataTypes = data
120        self.json: Any = json
121        self.files: Optional[List[Tuple[str, FileType]]] = None
122        if files:
123            self.files = []
124            files_ = files.items() if isinstance(files, dict) else files
125            for name, file_info in files_:
126                if not isinstance(file_info, tuple):
127                    self.files.append((name, (None, file_info, None)))
128                elif len(file_info) == 2:
129                    self.files.append((name, (file_info[0], file_info[1], None)))
130                else:
131                    self.files.append((name, file_info))  # type: ignore
132
133    def __repr__(self) -> str:
134        class_name = self.__class__.__name__
135        url = str(self.url)
136        return f"<{class_name}({self.method!r}, {url!r})>"
Request( method: Union[str, bytes], url: Union[yarl.URL, str, Tuple[bytes, bytes, Union[int, NoneType], bytes]], *, params: Union[NoneType, str, Mapping[str, Union[str, int, float, List[Union[str, int, float]]]], List[Tuple[str, Union[str, int, float, List[Union[str, int, float]]]]]] = None, headers: Union[NoneType, multidict._multidict.CIMultiDict, Dict[str, str], List[Tuple[str, str]]] = None, cookies: Union[NoneType, ayaka.driver.ayakabot.model.Cookies, http.cookiejar.CookieJar, Dict[str, str], List[Tuple[str, str]]] = None, content: Union[str, bytes, NoneType] = None, data: Union[dict, NoneType] = None, json: Any = None, files: Union[Dict[str, Union[IO[bytes], bytes, Tuple[Union[str, NoneType], Union[IO[bytes], bytes]], Tuple[Union[str, NoneType], Union[IO[bytes], bytes], Union[str, NoneType]]]], List[Tuple[str, Union[IO[bytes], bytes, Tuple[Union[str, NoneType], Union[IO[bytes], bytes]], Tuple[Union[str, NoneType], Union[IO[bytes], bytes], Union[str, NoneType]]]]], NoneType] = None, version: Union[str, ayaka.driver.ayakabot.model.HTTPVersion] = <HTTPVersion.H11: '1.1'>, timeout: Union[float, NoneType] = None, proxy: Union[str, NoneType] = None)
 62    def __init__(
 63        self,
 64        method: Union[str, bytes],
 65        url: Union["URL", str, RawURL],
 66        *,
 67        params: QueryTypes = None,
 68        headers: HeaderTypes = None,
 69        cookies: CookieTypes = None,
 70        content: ContentTypes = None,
 71        data: DataTypes = None,
 72        json: Any = None,
 73        files: FilesTypes = None,
 74        version: Union[str, HTTPVersion] = HTTPVersion.H11,
 75        timeout: Optional[float] = None,
 76        proxy: Optional[str] = None,
 77    ):
 78        # method
 79        self.method: str = (
 80            method.decode("ascii").upper()
 81            if isinstance(method, bytes)
 82            else method.upper()
 83        )
 84        # http version
 85        self.version: HTTPVersion = HTTPVersion(version)
 86        # timeout
 87        self.timeout: Optional[float] = timeout
 88        # proxy
 89        self.proxy: Optional[str] = proxy
 90
 91        # url
 92        if isinstance(url, tuple):
 93            scheme, host, port, path = url
 94            url = URL.build(
 95                scheme=scheme.decode("ascii"),
 96                host=host.decode("ascii"),
 97                port=port,
 98                path=path.decode("ascii"),
 99            )
100        else:
101            url = URL(url)
102
103        if params is not None:
104            url = url.update_query(params)
105        self.url: URL = url
106
107        # headers
108        self.headers: CIMultiDict[str]
109        if headers is not None:
110            self.headers = CIMultiDict(headers)
111        else:
112            self.headers = CIMultiDict()
113
114        # cookies
115        self.cookies = Cookies(cookies)
116
117        # body
118        self.content: ContentTypes = content
119        self.data: DataTypes = data
120        self.json: Any = json
121        self.files: Optional[List[Tuple[str, FileType]]] = None
122        if files:
123            self.files = []
124            files_ = files.items() if isinstance(files, dict) else files
125            for name, file_info in files_:
126                if not isinstance(file_info, tuple):
127                    self.files.append((name, (None, file_info, None)))
128                elif len(file_info) == 2:
129                    self.files.append((name, (file_info[0], file_info[1], None)))
130                else:
131                    self.files.append((name, file_info))  # type: ignore
class Cookies(typing.MutableMapping):
139class Cookies(MutableMapping):
140    def __init__(self, cookies: CookieTypes = None) -> None:
141        self.jar: CookieJar = cookies if isinstance(cookies, CookieJar) else CookieJar()
142        if cookies is not None and not isinstance(cookies, CookieJar):
143            if isinstance(cookies, dict):
144                for key, value in cookies.items():
145                    self.set(key, value)
146            elif isinstance(cookies, list):
147                for key, value in cookies:
148                    self.set(key, value)
149            elif isinstance(cookies, Cookies):
150                for cookie in cookies.jar:
151                    self.jar.set_cookie(cookie)
152            else:
153                raise TypeError(f"Cookies must be dict or list, not {type(cookies)}")
154
155    def set(self, name: str, value: str, domain: str = "", path: str = "/") -> None:
156        cookie = Cookie(
157            version=0,
158            name=name,
159            value=value,
160            port=None,
161            port_specified=False,
162            domain=domain,
163            domain_specified=bool(domain),
164            domain_initial_dot=domain.startswith("."),
165            path=path,
166            path_specified=bool(path),
167            secure=False,
168            expires=None,
169            discard=True,
170            comment=None,
171            comment_url=None,
172            rest={},
173            rfc2109=False,
174        )
175        self.jar.set_cookie(cookie)
176
177    def get(
178        self,
179        name: str,
180        default: Optional[str] = None,
181        domain: str = None,
182        path: str = None,
183    ) -> Optional[str]:
184        value: Optional[str] = None
185        for cookie in self.jar:
186            if (
187                cookie.name == name
188                and (domain is None or cookie.domain == domain)
189                and (path is None or cookie.path == path)
190            ):
191                if value is not None:
192                    message = f"Multiple cookies exist with name={name}"
193                    raise ValueError(message)
194                value = cookie.value
195
196        return default if value is None else value
197
198    def delete(
199        self, name: str, domain: Optional[str] = None, path: Optional[str] = None
200    ) -> None:
201        if domain is not None and path is not None:
202            return self.jar.clear(domain, path, name)
203
204        remove = [
205            cookie
206            for cookie in self.jar
207            if cookie.name == name
208            and (domain is None or cookie.domain == domain)
209            and (path is None or cookie.path == path)
210        ]
211
212        for cookie in remove:
213            self.jar.clear(cookie.domain, cookie.path, cookie.name)
214
215    def clear(self, domain: Optional[str] = None, path: Optional[str] = None) -> None:
216        self.jar.clear(domain, path)
217
218    def update(self, cookies: CookieTypes = None) -> None:
219        cookies = Cookies(cookies)
220        for cookie in cookies.jar:
221            self.jar.set_cookie(cookie)
222
223    def __setitem__(self, name: str, value: str) -> None:
224        return self.set(name, value)
225
226    def __getitem__(self, name: str) -> str:
227        value = self.get(name)
228        if value is None:
229            raise KeyError(name)
230        return value
231
232    def __delitem__(self, name: str) -> None:
233        return self.delete(name)
234
235    def __len__(self) -> int:
236        return len(self.jar)
237
238    def __iter__(self) -> Iterator[Cookie]:
239        return (cookie for cookie in self.jar)
240
241    def __repr__(self) -> str:
242        cookies_repr = ", ".join(
243            [
244                f"<Cookie {cookie.name}={cookie.value} for {cookie.domain} />"
245                for cookie in self.jar
246            ]
247        )
248
249        return f"<Cookies [{cookies_repr}]>"

Abstract base class for generic types.

A generic type is typically declared by inheriting from this class parameterized with one or more type variables. For example, a generic mapping type might be defined as::

class Mapping(Generic[KT, VT]): def __getitem__(self, key: KT) -> VT: ... # Etc.

This class can then be used as follows::

def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: try: return mapping[key] except KeyError: return default

Cookies( cookies: Union[NoneType, ayaka.driver.ayakabot.model.Cookies, http.cookiejar.CookieJar, Dict[str, str], List[Tuple[str, str]]] = None)
140    def __init__(self, cookies: CookieTypes = None) -> None:
141        self.jar: CookieJar = cookies if isinstance(cookies, CookieJar) else CookieJar()
142        if cookies is not None and not isinstance(cookies, CookieJar):
143            if isinstance(cookies, dict):
144                for key, value in cookies.items():
145                    self.set(key, value)
146            elif isinstance(cookies, list):
147                for key, value in cookies:
148                    self.set(key, value)
149            elif isinstance(cookies, Cookies):
150                for cookie in cookies.jar:
151                    self.jar.set_cookie(cookie)
152            else:
153                raise TypeError(f"Cookies must be dict or list, not {type(cookies)}")
def set(self, name: str, value: str, domain: str = '', path: str = '/') -> None:
155    def set(self, name: str, value: str, domain: str = "", path: str = "/") -> None:
156        cookie = Cookie(
157            version=0,
158            name=name,
159            value=value,
160            port=None,
161            port_specified=False,
162            domain=domain,
163            domain_specified=bool(domain),
164            domain_initial_dot=domain.startswith("."),
165            path=path,
166            path_specified=bool(path),
167            secure=False,
168            expires=None,
169            discard=True,
170            comment=None,
171            comment_url=None,
172            rest={},
173            rfc2109=False,
174        )
175        self.jar.set_cookie(cookie)
def get( self, name: str, default: Union[str, NoneType] = None, domain: str = None, path: str = None) -> Union[str, NoneType]:
177    def get(
178        self,
179        name: str,
180        default: Optional[str] = None,
181        domain: str = None,
182        path: str = None,
183    ) -> Optional[str]:
184        value: Optional[str] = None
185        for cookie in self.jar:
186            if (
187                cookie.name == name
188                and (domain is None or cookie.domain == domain)
189                and (path is None or cookie.path == path)
190            ):
191                if value is not None:
192                    message = f"Multiple cookies exist with name={name}"
193                    raise ValueError(message)
194                value = cookie.value
195
196        return default if value is None else value

D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.

def delete( self, name: str, domain: Union[str, NoneType] = None, path: Union[str, NoneType] = None) -> None:
198    def delete(
199        self, name: str, domain: Optional[str] = None, path: Optional[str] = None
200    ) -> None:
201        if domain is not None and path is not None:
202            return self.jar.clear(domain, path, name)
203
204        remove = [
205            cookie
206            for cookie in self.jar
207            if cookie.name == name
208            and (domain is None or cookie.domain == domain)
209            and (path is None or cookie.path == path)
210        ]
211
212        for cookie in remove:
213            self.jar.clear(cookie.domain, cookie.path, cookie.name)
def clear( self, domain: Union[str, NoneType] = None, path: Union[str, NoneType] = None) -> None:
215    def clear(self, domain: Optional[str] = None, path: Optional[str] = None) -> None:
216        self.jar.clear(domain, path)

D.clear() -> None. Remove all items from D.

def update( self, cookies: Union[NoneType, ayaka.driver.ayakabot.model.Cookies, http.cookiejar.CookieJar, Dict[str, str], List[Tuple[str, str]]] = None) -> None:
218    def update(self, cookies: CookieTypes = None) -> None:
219        cookies = Cookies(cookies)
220        for cookie in cookies.jar:
221            self.jar.set_cookie(cookie)

D.update([E, ]**F) -> None. Update D from mapping/iterable E and F. If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

Inherited Members
collections.abc.MutableMapping
pop
popitem
setdefault
collections.abc.Mapping
keys
items
values
class WebSocketServerSetup:
253class WebSocketServerSetup:
254    """WebSocket 服务器路由配置。"""
255
256    path: URL  # path should not be absolute, check it by URL.is_absolute() == False
257    name: str
258    handle_func: Callable

WebSocket 服务器路由配置。

WebSocketServerSetup(path: yarl.URL, name: str, handle_func: Callable)
class DataclassEncoder(json.encoder.JSONEncoder):
260class DataclassEncoder(json.JSONEncoder):
261    """在JSON序列化 `Message` (List[Dataclass]) 时使用的 `JSONEncoder`"""
262
263    def default(self, o):
264        if dataclasses.is_dataclass(o):
265            return dataclasses.asdict(o)
266        return super().default(o)

在JSON序列化 Message (List[Dataclass]) 时使用的 JSONEncoder

def default(self, o):
263    def default(self, o):
264        if dataclasses.is_dataclass(o):
265            return dataclasses.asdict(o)
266        return super().default(o)

Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this::

def default(self, o):
    try:
        iterable = iter(o)
    except TypeError:
        pass
    else:
        return list(iterable)
    # Let the base class default method raise the TypeError
    return JSONEncoder.default(self, o)
Inherited Members
json.encoder.JSONEncoder
JSONEncoder
encode
iterencode