From 8aefd81ad93db5c7b5a8e37ac5cb41e8d23622f5 Mon Sep 17 00:00:00 2001 From: Roman Miroshnychenko Date: Wed, 23 Oct 2024 15:33:51 +0300 Subject: [PATCH] [script.module.simple-requests] 1.0.0 (#2665) --- script.module.simple-requests/LICENSE.txt | 19 + script.module.simple-requests/addon.xml | 22 + script.module.simple-requests/icon.png | Bin 0 -> 37881 bytes .../libs/simple_requests.py | 413 ++++++++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 script.module.simple-requests/LICENSE.txt create mode 100644 script.module.simple-requests/addon.xml create mode 100644 script.module.simple-requests/icon.png create mode 100644 script.module.simple-requests/libs/simple_requests.py diff --git a/script.module.simple-requests/LICENSE.txt b/script.module.simple-requests/LICENSE.txt new file mode 100644 index 000000000..4708d298f --- /dev/null +++ b/script.module.simple-requests/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2021, Roman Miroshnychenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/script.module.simple-requests/addon.xml b/script.module.simple-requests/addon.xml new file mode 100644 index 000000000..02e846199 --- /dev/null +++ b/script.module.simple-requests/addon.xml @@ -0,0 +1,22 @@ + + + + + + + + all + simple-requests Python library + simple-requests is a simple library for making HTTP requests with API similar to the popular "requests" library + MIT + https://github.com/romanvm/kodi.simple-requests + 1.0.0: +- Initial public release + + icon.png + + + diff --git a/script.module.simple-requests/icon.png b/script.module.simple-requests/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3eaf29550cc446a8afb2326faff75dcf2ac66274 GIT binary patch literal 37881 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 VA31@hAL{@B002ovPDHLkV1l>ofrkJ9 literal 0 HcmV?d00001 diff --git a/script.module.simple-requests/libs/simple_requests.py b/script.module.simple-requests/libs/simple_requests.py new file mode 100644 index 000000000..5a30d848e --- /dev/null +++ b/script.module.simple-requests/libs/simple_requests.py @@ -0,0 +1,413 @@ +# Copyright (c) 2021, Roman Miroshnychenko +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +""" +A simple library for making HTTP requests with API similar to the popular "requests" library + +It depends only on the Python standard library and uses **urllib.request** module internally. + +Supported: + +* HTTP methods: GET, POST +* HTTP and HTTPS. +* Disabling SSL certificates validation. +* Request payload as form data, JSON and raw binary data. +* Custom headers. +* Cookies. +* Basic authentication. +* Gzipped response content. + +Not supported: + +* File upload. +* Persistent Session objects. Since Python's built-in **urllib.request** does not support keep-alive + connections, persistent sessions do not make much sense in this case. +* Streaming requests and responses. simple-requests is not suitable for sending and receiving + large volumes of data. + +Example:: + + from pprint import pprint + + import simple_requests as requests + + response = requests.get('https://httpbin.org/html') + if not response.ok: + response.raise_for_status() + print(response.text) + + response = requests.post('https://httpbin.org/post', + data={'username': 'foo', 'password': 'bar'}) + if not response.ok: + response.raise_for_status() + pprint(response.json()) + +""" +import gzip +import io +import json as _json +import ssl +import threading +from base64 import b64encode +from email.message import Message +from http.cookiejar import CookieJar, Cookie +from typing import Optional, Dict, Any, Tuple, Union, List, BinaryIO, Iterable +from urllib import request as url_request +from urllib.error import HTTPError as _HTTPError +from urllib.parse import urlparse, urlencode + +__all__ = [ + 'RequestException', + 'ConnectionError', + 'HTTPError', + 'RequestsCookieJar', + 'get', + 'post', +] + + +class RequestException(IOError): + + def __repr__(self) -> str: + return self.__str__() + + +class ConnectionError(RequestException): + + def __init__(self, message: str, url: str): + super().__init__(message) + self.message = message + self.url = url + + def __str__(self) -> str: + return f'ConnectionError for url {self.url}: {self.message}' + + +class HTTPError(RequestException): + + def __init__(self, response: 'Response'): + self.response = response + + def __str__(self) -> str: + return f'HTTPError: {self.response.status_code} for url: {self.response.url}' + + +class HTTPMessage(Message): + + def update(self, dct: Dict[str, str]) -> None: + for key, value in dct.items(): + self[key] = value + + +class RequestsCookieJar(CookieJar): + """A picklable CookieJar class with dictionary-like interface""" + + def __setitem__(self, name: str, value: str) -> None: + """Set a cookie like in a dictionary.""" + cookie = Cookie( + version=0, + name=name, + value=value, + port=None, + port_specified=False, + domain="", + domain_specified=False, + domain_initial_dot=False, + path="/", + path_specified=True, + secure=False, + expires=None, + discard=True, + comment=None, + comment_url=None, + rest={'HttpOnly': None}, + rfc2109=False + ) + self.set_cookie(cookie) + + def __getitem__(self, name: str) -> str: + """Retrieve a cookie's value by name.""" + for cookie in self: + if cookie.name == name: + return cookie.value + raise KeyError(f"Cookie with name {name} not found.") + + def __delitem__(self, name: str) -> None: + """Delete a cookie by name.""" + cookies_to_keep = [cookie for cookie in self if cookie.name != name] + self.clear() # Remove all cookies + for cookie in cookies_to_keep: + self.set_cookie(cookie) + + def __contains__(self, name) -> bool: + """Check if a cookie with the given name exists.""" + for cookie in self: + if cookie.name == name: + return True + return False + + def items(self) -> Iterable[Tuple[str, str]]: + for cookie in self: + yield cookie.name, cookie.value + + def keys(self) -> Iterable[str]: + """Return the names of all cookies.""" + for cookie in self: + yield cookie.name + + def values(self) -> Iterable[str]: + for cookie in self: + yield cookie.value + + def get_dict(self) -> Dict[str, str]: + return dict(self.items()) + + def update(self, cookies: Union[Dict[str, str], CookieJar]): + if isinstance(cookies, dict): + for key, value in cookies.items(): + self[key] = value + return + for cookie in cookies: + self.set_cookie(cookie) + + def get(self, key: str, default: Optional[Any] = None) -> Any: + try: + return self[key] + except KeyError: + return default + + def __getstate__(self): + """Return the state for pickling.""" + state = self.__dict__.copy() + # Get the list of cookies for pickling + state['cookies'] = list(self) + state['_cookies_lock'] = None + return state + + def __setstate__(self, state): + """Restore the state from pickling.""" + state['_cookies_lock'] = threading.RLock() + self.__dict__.update(state) + # Re-set cookies from pickled state + cookies = state.get('cookies', []) + self.clear() + for cookie in cookies: + self.set_cookie(cookie) + + +class Response: + NULL = object() + + def __init__(self): + self.encoding: str = 'utf-8' + self.status_code: int = -1 + self._headers: Optional[HTTPMessage] = None + self.url: str = '' + self.content: bytes = b'' + self._text = None + self._json = self.NULL + self.cookies: CookieJar = RequestsCookieJar() + + def __str__(self) -> str: + return f'' + + def __repr__(self) -> str: + return self.__str__() + + @property + def headers(self) -> HTTPMessage: + return self._headers + + @headers.setter + def headers(self, value: HTTPMessage): + charset = value.get_content_charset() + if charset: + self.encoding = charset + self._headers = value + + @property + def ok(self) -> bool: + return self.status_code < 400 + + @property + def text(self) -> str: + """ + :return: Response payload as decoded text + """ + if self._text is None: + try: + self._text = self.content.decode(self.encoding) + except (UnicodeDecodeError, LookupError): + self._text = self.content.decode('utf-8', 'replace') + return self._text + + def json(self) -> Optional[Union[Dict[str, Any], List[Any]]]: + try: + if self._json is self.NULL: + self._json = _json.loads(self.content) + return self._json + except ValueError as exc: + raise ValueError('Response content is not a valid JSON') from exc + + def raise_for_status(self) -> None: + if not self.ok: + raise HTTPError(self) + + +def _create_request(url_structure, params=None, data=None, headers=None, auth=None, json=None): + query = url_structure.query + if params is not None: + separator = '&' if query else '' + query += separator + urlencode(params, doseq=True) + full_url = url_structure.scheme + '://' + url_structure.netloc + url_structure.path + if query: + full_url += '?' + query + prepared_headers = HTTPMessage() + if headers is not None: + prepared_headers.update(headers) + body = None + if json is not None: + body = _json.dumps(json).encode('utf-8') + prepared_headers['Content-Type'] = 'application/json' + if body is None and isinstance(data, dict): + body = urlencode(data, doseq=True).encode('ascii') + prepared_headers['Content-Type'] = 'application/x-www-form-urlencoded' + if body is None and isinstance(data, bytes): + body = data + if body is None and isinstance(data, str): + body = data.encode('utf-8') + if body is None and hasattr(data, 'read'): + body = data.read() + if body is not None and 'Content-Type' not in prepared_headers: + prepared_headers['Content-Type'] = 'application/octet-stream' + if auth is not None: + encoded_credentials = b64encode((auth[0] + ':' + auth[1]).encode('utf-8')).decode('ascii') + prepared_headers['Authorization'] = f'Basic {encoded_credentials}' + if 'Accept-Encoding' not in prepared_headers: + prepared_headers['Accept-Encoding'] = 'gzip' + return url_request.Request(full_url, body, prepared_headers) + + +def _get_cookie_jar(cookies): + if isinstance(cookies, CookieJar): + return cookies + cookie_jar = RequestsCookieJar() + if cookies is not None: + cookie_jar.update(cookies) + return cookie_jar + + +def post(url: str, + params: Optional[Dict[str, Any]] = None, + data: Optional[Union[Dict[str, Any], str, bytes, BinaryIO]] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Union[Dict[str, str], CookieJar] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + verify: bool = True, + json: Optional[Dict[str, Any]] = None) -> Response: + """ + POST request + + This function assumes that a request body should be encoded with UTF-8 + and by default sends Accept-Encoding: gzip header to receive response content compressed. + + :param url: URL + :param params: URL query params + :param data: request payload as dict, str, bytes or a binary file object. + If "data" or "json" is passed then a POST request is sent. + For str, bytes or file object it's caller's responsibility to provide a proper + 'Content-Type' header. + :param headers: additional headers + :param cookies: cookies as a dict or a CookieJar object. If a CookieJar object is provided + the same object will be attached to a response object with the updated set of cookies. + :param auth: a tuple of (login, password) for Basic authentication + :param timeout: request timeout in seconds + :param verify: verify SSL certificates + :param json: request payload as JSON. This parameter has precedence over "data", that is, + if it's present then "data" is ignored. + :raises: ConnectionError + :return: Response object + """ + url_structure = urlparse(url) + request = _create_request(url_structure, params, data, headers, auth, json) + context = None + if url_structure.scheme == 'https': + context = ssl.SSLContext() + if not verify: + context.verify_mode = ssl.CERT_NONE + context.check_hostname = False + cookie_jar = _get_cookie_jar(cookies) + opener_director = url_request.build_opener( + url_request.HTTPSHandler(context=context), + url_request.HTTPCookieProcessor(cookie_jar) + ) + resp = None + try: + resp = opener_director.open(request, timeout=timeout) + content = resp.read() + except _HTTPError as exc: + resp = exc + content = resp.read() + except Exception as exc: + raise ConnectionError(str(exc), request.full_url) from exc + finally: + if resp is not None: + resp.close() + response = Response() + response.status_code = resp.status if hasattr(resp, 'status') else resp.getstatus() + response.headers = resp.headers if hasattr(resp, 'headers') else resp.info() + response.url = resp.url if hasattr(resp, 'url') else resp.geturl() + if resp.headers.get('Content-Encoding') == 'gzip': + temp_fo = io.BytesIO(content) + gzip_file = gzip.GzipFile(fileobj=temp_fo) + content = gzip_file.read() + response.content = content + if isinstance(cookies, CookieJar): + response.cookies = cookies + response.cookies.extract_cookies(resp, request) + return response + + +def get(url: str, + params: Optional[Dict[str, Any]] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Union[Dict[str, str], CookieJar] = None, + auth: Optional[Tuple[str, str]] = None, + timeout: Optional[float] = None, + verify: bool = True) -> Response: + """ + GET request + + This function by default sends Accept-Encoding: gzip header + to receive response content compressed. + + :param url: URL + :param params: URL query params + :param headers: additional headers + :param cookies: cookies as a dict or a CookieJar object. If a CookieJar object is provided + the same object will be attached to a response object with the updated set of cookies. + :param auth: a tuple of (login, password) for Basic authentication + :param timeout: request timeout in seconds + :param verify: verify SSL certificates + :raises: ConnectionError + :return: Response object + """ + return post(url=url, params=params, headers=headers, cookies=cookies, + auth=auth, timeout=timeout, verify=verify)