blacksheep_client_request 0.0.1__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.
LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ChenyangGao <https://github.com/ChenyangGao>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env python3
2
+ # coding: utf-8
3
+
4
+ from __future__ import annotations
5
+
6
+ __author__ = "ChenyangGao <https://chenyanggao.github.io>"
7
+ __version__ = (0, 0, 1)
8
+ __all__ = ["request"]
9
+
10
+ from collections.abc import AsyncIterator, Callable, Iterable, Mapping
11
+ from gzip import decompress as decompress_gzip
12
+ from inspect import isawaitable
13
+ from json import loads
14
+ from re import compile as re_compile
15
+ from typing import Any, Literal
16
+ from zlib import compressobj, DEF_MEM_LEVEL, DEFLATED, MAX_WBITS
17
+
18
+ from asynctools import run_async
19
+ from argtools import argcount
20
+ from filewrap import bio_chunk_async_iter
21
+ from multidict import CIMultiDict
22
+ from blacksheep.client import ClientSession
23
+ from blacksheep.common.types import normalize_headers
24
+ from blacksheep.contents import Content, FormContent, JSONContent, StreamedContent, TextContent
25
+ from blacksheep.exceptions import HTTPException
26
+ from blacksheep.messages import Request, Response
27
+
28
+
29
+ CRE_search_charset = re_compile(r"\bcharset=(?P<charset>[^ ;]+)").search
30
+
31
+ if "__del__" not in ClientSession.__dict__:
32
+ def close(self, /):
33
+ try:
34
+ run_async(ClientSession.close(self))
35
+ except:
36
+ pass
37
+ setattr(ClientSession, "__del__", close)
38
+
39
+
40
+ def get_charset(content_type: str, default="utf-8") -> str:
41
+ match = CRE_search_charset(content_type)
42
+ if match is None:
43
+ return "utf-8"
44
+ return match["charset"]
45
+
46
+
47
+ def decompress_deflate(data: bytes, compresslevel: int = 9) -> bytes:
48
+ # Fork from: https://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations#answer-1089787
49
+ compress = compressobj(
50
+ compresslevel, # level: 0-9
51
+ DEFLATED, # method: must be DEFLATED
52
+ -MAX_WBITS, # window size in bits:
53
+ # -15..-8: negate, suppress header
54
+ # 8..15: normal
55
+ # 16..30: subtract 16, gzip header
56
+ DEF_MEM_LEVEL, # mem level: 1..8/9
57
+ 0 # strategy:
58
+ # 0 = Z_DEFAULT_STRATEGY
59
+ # 1 = Z_FILTERED
60
+ # 2 = Z_HUFFMAN_ONLY
61
+ # 3 = Z_RLE
62
+ # 4 = Z_FIXED
63
+ )
64
+ deflated = compress.compress(data)
65
+ deflated += compress.flush()
66
+ return deflated
67
+
68
+
69
+ async def decompress_response(resp: ResponseWrapper, /) -> bytes:
70
+ data = await resp.read()
71
+ content_encoding = resp.headers.get("Content-Encoding")
72
+ match content_encoding:
73
+ case "gzip":
74
+ data = decompress_gzip(data)
75
+ case "deflate":
76
+ data = decompress_deflate(data)
77
+ case "br":
78
+ from brotli import decompress as decompress_br # type: ignore
79
+ data = decompress_br(data)
80
+ case "zstd":
81
+ from zstandard import decompress as decompress_zstd
82
+ data = decompress_zstd(data)
83
+ return data
84
+
85
+
86
+ class ResponseWrapper:
87
+
88
+ def __init__(self, response: Response):
89
+ self.response = response
90
+ self.headers = CIMultiDict((str(k, "latin-1"), str(v, "latin-1")) for k, v in response.headers)
91
+
92
+ def __dir__(self, /) -> list[str]:
93
+ s = set(super().__dir__())
94
+ s.update(dir(self.response))
95
+ return sorted(s)
96
+
97
+ def __getattr__(self, attr, /):
98
+ return getattr(self.response, attr)
99
+
100
+ def __repr__(self, /):
101
+ return f"{type(self).__qualname__}({self.response!r})"
102
+
103
+
104
+ async def request(
105
+ url: str,
106
+ method: str = "GET",
107
+ params: None | Mapping | Iterable[tuple[Any, Any]] = None,
108
+ data: Any = None,
109
+ json: Any = None,
110
+ headers: None | Mapping | Iterable[tuple[Any, Any]] = None,
111
+ parse: Literal[None, ...] | bool | Callable = None, # type: ignore
112
+ raise_for_status: bool = True,
113
+ session: None | ClientSession = None,
114
+ ):
115
+ if session is None:
116
+ async with ClientSession() as session:
117
+ return await request(
118
+ url,
119
+ method,
120
+ params=params,
121
+ data=data,
122
+ headers=headers,
123
+ parse=parse,
124
+ raise_for_status=raise_for_status,
125
+ session=session,
126
+ )
127
+ headers = normalize_headers(headers) if headers else None
128
+ req = Request(method.upper(), session.get_url(url, params), headers)
129
+ if json is not None and not isinstance(json, JSONContent):
130
+ data = JSONContent(json)
131
+ if data:
132
+ if isinstance(data, Content):
133
+ content = data
134
+ elif isinstance(data, str):
135
+ content = TextContent(data)
136
+ elif isinstance(data, (bytes, bytearray, memoryview)):
137
+ content = Content(b"application/octet-stream", data)
138
+ elif hasattr(data, "read"):
139
+ async def gen():
140
+ async for chunk in bio_chunk_async_iter(data):
141
+ yield chunk
142
+ content = StreamedContent(b"application/octet-stream", gen)
143
+ elif isinstance(data, AsyncIterator):
144
+ async def gen():
145
+ async for chunk in data:
146
+ yield chunk
147
+ content = StreamedContent(b"application/octet-stream", gen)
148
+ else:
149
+ content = FormContent(data)
150
+ req = req.with_content(content)
151
+ resp = ResponseWrapper(await session.send(req))
152
+ if resp.status >= 400:
153
+ raise HTTPException(resp.status, resp.reason)
154
+ if parse is None or parse is ...:
155
+ return resp
156
+ elif parse is False:
157
+ return await decompress_response(resp)
158
+ elif parse is True:
159
+ data = await decompress_response(resp)
160
+ content_type = resp.headers.get("Content-Type", "")
161
+ if content_type == "application/json":
162
+ return loads(data)
163
+ elif content_type.startswith("application/json;"):
164
+ return loads(data.decode(get_charset(content_type)))
165
+ elif content_type.startswith("text/"):
166
+ return data.decode(get_charset(content_type))
167
+ return data
168
+ else:
169
+ ac = argcount(parse)
170
+ if ac == 1:
171
+ ret = parse(resp)
172
+ else:
173
+ ret = parse(resp, await decompress_response(resp))
174
+ if isawaitable(ret):
175
+ ret = await ret
176
+ return ret
177
+
File without changes
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ChenyangGao <https://github.com/ChenyangGao>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.1
2
+ Name: blacksheep_client_request
3
+ Version: 0.0.1
4
+ Summary: blacksheep request extension.
5
+ Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/blacksheep_client_request
6
+ License: MIT
7
+ Keywords: blacksheep,request
8
+ Author: ChenyangGao
9
+ Author-email: wosiwujm@gmail.com
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Topic :: Software Development
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Dist: blacksheep
25
+ Requires-Dist: brotli
26
+ Requires-Dist: http_response
27
+ Requires-Dist: multidict
28
+ Requires-Dist: python-argtools
29
+ Requires-Dist: python-asynctools (>=0.0.4)
30
+ Requires-Dist: python-filewrap
31
+ Requires-Dist: zstandard
32
+ Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/blacksheep_client_request
33
+ Description-Content-Type: text/markdown
34
+
35
+ # blacksheep request extension.
36
+
37
+ ## Installation
38
+
39
+ You can install via [pypi](https://pypi.org/project/blacksheep_client_request/)
40
+
41
+ ```console
42
+ pip install -U blacksheep_client_request
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```python
48
+ from blacksheep_client_request import request
49
+ ```
50
+
@@ -0,0 +1,7 @@
1
+ LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
+ blacksheep_client_request/__init__.py,sha256=SMSx7ciVMfENvq5vG1lXwLC1rwVjqSNjhJ3E9WhsgU8,6246
3
+ blacksheep_client_request/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ blacksheep_client_request-0.0.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
+ blacksheep_client_request-0.0.1.dist-info/METADATA,sha256=dqbT-AqIFeizOkggVCbCBWDv_5puZ_99rHNcpj5894A,1662
6
+ blacksheep_client_request-0.0.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
7
+ blacksheep_client_request-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.8.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any