From 55cf9bdb93e0b6a3ac3f3e6c8dc83a3ae3e45e3a Mon Sep 17 00:00:00 2001 From: Mauricio Baeza Date: Sat, 17 Jul 2021 14:51:06 -0500 Subject: [PATCH] Add custom path for import modules --- README.md | 8 +- oxt/custom_path.oxt | Bin 0 -> 12261 bytes oxt/example_001.oxt | Bin 0 -> 11807 bytes oxt/others_libraries.oxt | Bin 0 -> 67073 bytes source/custom_path/Addons.xcu | 33 + source/custom_path/META-INF/manifest.xml | 5 + source/custom_path/README.md | 6 + source/custom_path/description.xml | 19 + source/custom_path/description/desc_en.txt | 1 + source/custom_path/description/desc_es.txt | 1 + source/custom_path/images/logo.png | Bin 0 -> 9189 bytes source/custom_path/main.py | 31 + source/custom_path/src/__init__.py | 2 + source/custom_path/src/mymodule.py | 28 + source/example_001/Addons.xcu | 33 + source/example_001/META-INF/manifest.xml | 5 + source/example_001/README.md | 4 + source/example_001/description.xml | 19 + source/example_001/description/desc_en.txt | 1 + source/example_001/description/desc_es.txt | 1 + source/example_001/images/logo.png | Bin 0 -> 9189 bytes source/example_001/main.py | 49 + source/my_first_extension/README.md | 4 + source/others_libraries/Addons.xcu | 33 + source/others_libraries/META-INF/manifest.xml | 5 + source/others_libraries/README.md | 6 + source/others_libraries/description.xml | 19 + .../others_libraries/description/desc_en.txt | 1 + .../others_libraries/description/desc_es.txt | 1 + source/others_libraries/images/logo.png | Bin 0 -> 9189 bytes source/others_libraries/main.py | 53 + .../pythonpath/requests/__init__.py | 152 +++ .../pythonpath/requests/__version__.py | 14 + .../pythonpath/requests/_internal_utils.py | 42 + .../pythonpath/requests/adapters.py | 533 +++++++++ .../pythonpath/requests/api.py | 159 +++ .../pythonpath/requests/auth.py | 305 +++++ .../pythonpath/requests/certs.py | 18 + .../pythonpath/requests/compat.py | 75 ++ .../pythonpath/requests/cookies.py | 549 +++++++++ .../pythonpath/requests/exceptions.py | 127 +++ .../pythonpath/requests/help.py | 135 +++ .../pythonpath/requests/hooks.py | 34 + .../pythonpath/requests/models.py | 966 ++++++++++++++++ .../pythonpath/requests/packages.py | 26 + .../pythonpath/requests/sessions.py | 781 +++++++++++++ .../pythonpath/requests/status_codes.py | 123 ++ .../pythonpath/requests/structures.py | 105 ++ .../pythonpath/requests/utils.py | 1013 +++++++++++++++++ 49 files changed, 5524 insertions(+), 1 deletion(-) create mode 100644 oxt/custom_path.oxt create mode 100644 oxt/example_001.oxt create mode 100644 oxt/others_libraries.oxt create mode 100644 source/custom_path/Addons.xcu create mode 100644 source/custom_path/META-INF/manifest.xml create mode 100644 source/custom_path/README.md create mode 100644 source/custom_path/description.xml create mode 100644 source/custom_path/description/desc_en.txt create mode 100644 source/custom_path/description/desc_es.txt create mode 100644 source/custom_path/images/logo.png create mode 100644 source/custom_path/main.py create mode 100644 source/custom_path/src/__init__.py create mode 100644 source/custom_path/src/mymodule.py create mode 100644 source/example_001/Addons.xcu create mode 100644 source/example_001/META-INF/manifest.xml create mode 100644 source/example_001/README.md create mode 100644 source/example_001/description.xml create mode 100644 source/example_001/description/desc_en.txt create mode 100644 source/example_001/description/desc_es.txt create mode 100644 source/example_001/images/logo.png create mode 100644 source/example_001/main.py create mode 100644 source/my_first_extension/README.md create mode 100644 source/others_libraries/Addons.xcu create mode 100644 source/others_libraries/META-INF/manifest.xml create mode 100644 source/others_libraries/README.md create mode 100644 source/others_libraries/description.xml create mode 100644 source/others_libraries/description/desc_en.txt create mode 100644 source/others_libraries/description/desc_es.txt create mode 100644 source/others_libraries/images/logo.png create mode 100644 source/others_libraries/main.py create mode 100644 source/others_libraries/pythonpath/requests/__init__.py create mode 100644 source/others_libraries/pythonpath/requests/__version__.py create mode 100644 source/others_libraries/pythonpath/requests/_internal_utils.py create mode 100644 source/others_libraries/pythonpath/requests/adapters.py create mode 100644 source/others_libraries/pythonpath/requests/api.py create mode 100644 source/others_libraries/pythonpath/requests/auth.py create mode 100644 source/others_libraries/pythonpath/requests/certs.py create mode 100644 source/others_libraries/pythonpath/requests/compat.py create mode 100644 source/others_libraries/pythonpath/requests/cookies.py create mode 100644 source/others_libraries/pythonpath/requests/exceptions.py create mode 100644 source/others_libraries/pythonpath/requests/help.py create mode 100644 source/others_libraries/pythonpath/requests/hooks.py create mode 100644 source/others_libraries/pythonpath/requests/models.py create mode 100644 source/others_libraries/pythonpath/requests/packages.py create mode 100644 source/others_libraries/pythonpath/requests/sessions.py create mode 100644 source/others_libraries/pythonpath/requests/status_codes.py create mode 100644 source/others_libraries/pythonpath/requests/structures.py create mode 100644 source/others_libraries/pythonpath/requests/utils.py diff --git a/README.md b/README.md index f2f000d..7d4fadc 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ ### Basic examples for extensions develop in Python for LibreOffice. +* Test + +./zaz.py -mi my_first_extension + + # Ejemplos de extensiones para LibreOffice -### Ejemplos basicos de desarrollo de extensiones con Python para LibreOffice \ No newline at end of file +### Ejemplos basicos de desarrollo de extensiones con Python para LibreOffice + diff --git a/oxt/custom_path.oxt b/oxt/custom_path.oxt new file mode 100644 index 0000000000000000000000000000000000000000..1efeb173072a0a6617c6fb9f564787bb4ac09dcd GIT binary patch literal 12261 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 N_jz@{|0n?PKLFK#s)+yq literal 0 HcmV?d00001 diff --git a/oxt/example_001.oxt b/oxt/example_001.oxt new file mode 100644 index 0000000000000000000000000000000000000000..c1515d814ff3cd3379171d743ef71ac011b5ee70 GIT binary patch literal 11807 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 + + + + + + My extension + Mi extensión + + + _self + + + + + Custom path + Ruta personalizada + + + + + + service:org.examples.zaz.custompath?argument + + + _self + + + + + + + diff --git a/source/custom_path/META-INF/manifest.xml b/source/custom_path/META-INF/manifest.xml new file mode 100644 index 0000000..5c5cd28 --- /dev/null +++ b/source/custom_path/META-INF/manifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/source/custom_path/README.md b/source/custom_path/README.md new file mode 100644 index 0000000..0a2d998 --- /dev/null +++ b/source/custom_path/README.md @@ -0,0 +1,6 @@ +# example_001 + +Include other libraries in this example: + +https://docs.python-requests.org/en/master + diff --git a/source/custom_path/description.xml b/source/custom_path/description.xml new file mode 100644 index 0000000..e3bc748 --- /dev/null +++ b/source/custom_path/description.xml @@ -0,0 +1,19 @@ + + + + + + Include custom path + Incluir ruta personalizada + + + + + + + + + + El Mau + + diff --git a/source/custom_path/description/desc_en.txt b/source/custom_path/description/desc_en.txt new file mode 100644 index 0000000..eef9a6a --- /dev/null +++ b/source/custom_path/description/desc_en.txt @@ -0,0 +1 @@ +ZAZ - Add custom path for import modules diff --git a/source/custom_path/description/desc_es.txt b/source/custom_path/description/desc_es.txt new file mode 100644 index 0000000..ba27787 --- /dev/null +++ b/source/custom_path/description/desc_es.txt @@ -0,0 +1 @@ +ZAZ - Agregar ruta personalizada para modulos diff --git a/source/custom_path/images/logo.png b/source/custom_path/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0d53fa22bdba5af0aab3dc7896af161d54932f GIT binary patch literal 9189 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 + + + + + + My extension + Mi extensión + + + _self + + + + + Say hello + Di hola + + + + + + service:org.examples.zaz.001?argument + + + _self + + + + + + + diff --git a/source/example_001/META-INF/manifest.xml b/source/example_001/META-INF/manifest.xml new file mode 100644 index 0000000..14f491b --- /dev/null +++ b/source/example_001/META-INF/manifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/source/example_001/README.md b/source/example_001/README.md new file mode 100644 index 0000000..0de9e44 --- /dev/null +++ b/source/example_001/README.md @@ -0,0 +1,4 @@ +# example_001 + +Add icon and description + diff --git a/source/example_001/description.xml b/source/example_001/description.xml new file mode 100644 index 0000000..194b302 --- /dev/null +++ b/source/example_001/description.xml @@ -0,0 +1,19 @@ + + + + + + Example extension 1 + Extensión de ejemplo 1 + + + + + + + + + + El Mau + + diff --git a/source/example_001/description/desc_en.txt b/source/example_001/description/desc_en.txt new file mode 100644 index 0000000..877fefd --- /dev/null +++ b/source/example_001/description/desc_en.txt @@ -0,0 +1 @@ +ZAZ - Extension example 1 diff --git a/source/example_001/description/desc_es.txt b/source/example_001/description/desc_es.txt new file mode 100644 index 0000000..6f59701 --- /dev/null +++ b/source/example_001/description/desc_es.txt @@ -0,0 +1 @@ +ZAZ - Extensión de ejemplo 1 diff --git a/source/example_001/images/logo.png b/source/example_001/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0d53fa22bdba5af0aab3dc7896af161d54932f GIT binary patch literal 9189 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 + + + + + + My extension + Mi extensión + + + _self + + + + + Other libraries + Otras librerias + + + + + + service:org.examples.zaz.other?argument + + + _self + + + + + + + diff --git a/source/others_libraries/META-INF/manifest.xml b/source/others_libraries/META-INF/manifest.xml new file mode 100644 index 0000000..14f491b --- /dev/null +++ b/source/others_libraries/META-INF/manifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/source/others_libraries/README.md b/source/others_libraries/README.md new file mode 100644 index 0000000..0a2d998 --- /dev/null +++ b/source/others_libraries/README.md @@ -0,0 +1,6 @@ +# example_001 + +Include other libraries in this example: + +https://docs.python-requests.org/en/master + diff --git a/source/others_libraries/description.xml b/source/others_libraries/description.xml new file mode 100644 index 0000000..156b3dd --- /dev/null +++ b/source/others_libraries/description.xml @@ -0,0 +1,19 @@ + + + + + + Include other libraries + Incluir otras librerías + + + + + + + + + + El Mau + + diff --git a/source/others_libraries/description/desc_en.txt b/source/others_libraries/description/desc_en.txt new file mode 100644 index 0000000..5b911ba --- /dev/null +++ b/source/others_libraries/description/desc_en.txt @@ -0,0 +1 @@ +ZAZ - Include other libraries diff --git a/source/others_libraries/description/desc_es.txt b/source/others_libraries/description/desc_es.txt new file mode 100644 index 0000000..e042de4 --- /dev/null +++ b/source/others_libraries/description/desc_es.txt @@ -0,0 +1 @@ +ZAZ - Incluir otras librerías diff --git a/source/others_libraries/images/logo.png b/source/others_libraries/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0d53fa22bdba5af0aab3dc7896af161d54932f GIT binary patch literal 9189 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 import requests + >>> r = requests.get('https://www.python.org') + >>> r.status_code + 200 + >>> b'Python is a programming language' in r.content + True + +... or POST: + + >>> payload = dict(key1='value1', key2='value2') + >>> r = requests.post('https://httpbin.org/post', data=payload) + >>> print(r.text) + { + ... + "form": { + "key1": "value1", + "key2": "value2" + }, + ... + } + +The other HTTP methods are supported - see `requests.api`. Full documentation +is at . + +:copyright: (c) 2017 by Kenneth Reitz. +:license: Apache 2.0, see LICENSE for more details. +""" + +import urllib3 +import warnings +from .exceptions import RequestsDependencyWarning + +try: + from charset_normalizer import __version__ as charset_normalizer_version +except ImportError: + charset_normalizer_version = None + +try: + from chardet import __version__ as chardet_version +except ImportError: + chardet_version = None + +def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): + urllib3_version = urllib3_version.split('.') + assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version) == 2: + urllib3_version.append('0') + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1, <= 1.26 + assert major == 1 + assert minor >= 21 + assert minor <= 26 + + # Check charset_normalizer for compatibility. + if chardet_version: + major, minor, patch = chardet_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet_version >= 3.0.2, < 5.0.0 + assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0) + elif charset_normalizer_version: + major, minor, patch = charset_normalizer_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # charset_normalizer >= 2.0.0 < 3.0.0 + assert (2, 0, 0) <= (major, minor, patch) < (3, 0, 0) + else: + raise Exception("You need either charset_normalizer or chardet installed") + +def _check_cryptography(cryptography_version): + # cryptography < 1.3.4 + try: + cryptography_version = list(map(int, cryptography_version.split('.'))) + except ValueError: + return + + if cryptography_version < [1, 3, 4]: + warning = 'Old version of cryptography ({}) may cause slowdown.'.format(cryptography_version) + warnings.warn(warning, RequestsDependencyWarning) + +# Check imported dependencies for compatibility. +try: + check_compatibility(urllib3.__version__, chardet_version, charset_normalizer_version) +except (AssertionError, ValueError): + warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported " + "version!".format(urllib3.__version__, chardet_version, charset_normalizer_version), + RequestsDependencyWarning) + +# Attempt to enable urllib3's fallback for SNI support +# if the standard library doesn't support SNI or the +# 'ssl' library isn't available. +try: + try: + import ssl + except ImportError: + ssl = None + + if not getattr(ssl, "HAS_SNI", False): + from urllib3.contrib import pyopenssl + pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import __version__ as cryptography_version + _check_cryptography(cryptography_version) +except ImportError: + pass + +# urllib3's DependencyWarnings should be silenced. +from urllib3.exceptions import DependencyWarning +warnings.simplefilter('ignore', DependencyWarning) + +from .__version__ import __title__, __description__, __url__, __version__ +from .__version__ import __build__, __author__, __author_email__, __license__ +from .__version__ import __copyright__, __cake__ + +from . import utils +from . import packages +from .models import Request, Response, PreparedRequest +from .api import request, get, head, post, patch, put, delete, options +from .sessions import session, Session +from .status_codes import codes +from .exceptions import ( + RequestException, Timeout, URLRequired, + TooManyRedirects, HTTPError, ConnectionError, + FileModeWarning, ConnectTimeout, ReadTimeout +) + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +logging.getLogger(__name__).addHandler(NullHandler()) + +# FileModeWarnings go off per the default. +warnings.simplefilter('default', FileModeWarning, append=True) diff --git a/source/others_libraries/pythonpath/requests/__version__.py b/source/others_libraries/pythonpath/requests/__version__.py new file mode 100644 index 0000000..0d7cde1 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/__version__.py @@ -0,0 +1,14 @@ +# .-. .-. .-. . . .-. .-. .-. .-. +# |( |- |.| | | |- `-. | `-. +# ' ' `-' `-`.`-' `-' `-' ' `-' + +__title__ = 'requests' +__description__ = 'Python HTTP for Humans.' +__url__ = 'https://requests.readthedocs.io' +__version__ = '2.26.0' +__build__ = 0x022600 +__author__ = 'Kenneth Reitz' +__author_email__ = 'me@kennethreitz.org' +__license__ = 'Apache 2.0' +__copyright__ = 'Copyright 2020 Kenneth Reitz' +__cake__ = u'\u2728 \U0001f370 \u2728' diff --git a/source/others_libraries/pythonpath/requests/_internal_utils.py b/source/others_libraries/pythonpath/requests/_internal_utils.py new file mode 100644 index 0000000..759d9a5 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/_internal_utils.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +""" +requests._internal_utils +~~~~~~~~~~~~~~ + +Provides utility functions that are consumed internally by Requests +which depend on extremely few external helpers (such as compat) +""" + +from .compat import is_py2, builtin_str, str + + +def to_native_string(string, encoding='ascii'): + """Given a string object, regardless of type, returns a representation of + that string in the native string type, encoding and decoding where + necessary. This assumes ASCII unless told otherwise. + """ + if isinstance(string, builtin_str): + out = string + else: + if is_py2: + out = string.encode(encoding) + else: + out = string.decode(encoding) + + return out + + +def unicode_is_ascii(u_string): + """Determine if unicode string only contains ASCII characters. + + :param str u_string: unicode string to check. Must be unicode + and not Python 2 `str`. + :rtype: bool + """ + assert isinstance(u_string, str) + try: + u_string.encode('ascii') + return True + except UnicodeEncodeError: + return False diff --git a/source/others_libraries/pythonpath/requests/adapters.py b/source/others_libraries/pythonpath/requests/adapters.py new file mode 100644 index 0000000..fa4d9b3 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/adapters.py @@ -0,0 +1,533 @@ +# -*- coding: utf-8 -*- + +""" +requests.adapters +~~~~~~~~~~~~~~~~~ + +This module contains the transport adapters that Requests uses to define +and maintain connections. +""" + +import os.path +import socket + +from urllib3.poolmanager import PoolManager, proxy_from_url +from urllib3.response import HTTPResponse +from urllib3.util import parse_url +from urllib3.util import Timeout as TimeoutSauce +from urllib3.util.retry import Retry +from urllib3.exceptions import ClosedPoolError +from urllib3.exceptions import ConnectTimeoutError +from urllib3.exceptions import HTTPError as _HTTPError +from urllib3.exceptions import MaxRetryError +from urllib3.exceptions import NewConnectionError +from urllib3.exceptions import ProxyError as _ProxyError +from urllib3.exceptions import ProtocolError +from urllib3.exceptions import ReadTimeoutError +from urllib3.exceptions import SSLError as _SSLError +from urllib3.exceptions import ResponseError +from urllib3.exceptions import LocationValueError + +from .models import Response +from .compat import urlparse, basestring +from .utils import (DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths, + get_encoding_from_headers, prepend_scheme_if_needed, + get_auth_from_url, urldefragauth, select_proxy) +from .structures import CaseInsensitiveDict +from .cookies import extract_cookies_to_jar +from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, + ProxyError, RetryError, InvalidSchema, InvalidProxyURL, + InvalidURL) +from .auth import _basic_auth_str + +try: + from urllib3.contrib.socks import SOCKSProxyManager +except ImportError: + def SOCKSProxyManager(*args, **kwargs): + raise InvalidSchema("Missing dependencies for SOCKS support.") + +DEFAULT_POOLBLOCK = False +DEFAULT_POOLSIZE = 10 +DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None + + +class BaseAdapter(object): + """The Base Transport Adapter""" + + def __init__(self): + super(BaseAdapter, self).__init__() + + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + """ + raise NotImplementedError + + def close(self): + """Cleans up adapter specific items.""" + raise NotImplementedError + + +class HTTPAdapter(BaseAdapter): + """The built-in HTTP Adapter for urllib3. + + Provides a general-case interface for Requests sessions to contact HTTP and + HTTPS urls by implementing the Transport Adapter interface. This class will + usually be created by the :class:`Session ` class under the + covers. + + :param pool_connections: The number of urllib3 connection pools to cache. + :param pool_maxsize: The maximum number of connections to save in the pool. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. + :param pool_block: Whether the connection pool should block for connections. + + Usage:: + + >>> import requests + >>> s = requests.Session() + >>> a = requests.adapters.HTTPAdapter(max_retries=3) + >>> s.mount('http://', a) + """ + __attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize', + '_pool_block'] + + def __init__(self, pool_connections=DEFAULT_POOLSIZE, + pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES, + pool_block=DEFAULT_POOLBLOCK): + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) + self.config = {} + self.proxy_manager = {} + + super(HTTPAdapter, self).__init__() + + self._pool_connections = pool_connections + self._pool_maxsize = pool_maxsize + self._pool_block = pool_block + + self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) + + def __getstate__(self): + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state): + # Can't handle by adding 'proxy_manager' to self.__attrs__ because + # self.poolmanager uses a lambda function, which isn't pickleable. + self.proxy_manager = {} + self.config = {} + + for attr, value in state.items(): + setattr(self, attr, value) + + self.init_poolmanager(self._pool_connections, self._pool_maxsize, + block=self._pool_block) + + def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param connections: The number of urllib3 connection pools to cache. + :param maxsize: The maximum number of connections to save in the pool. + :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. + """ + # save these values for pickling + self._pool_connections = connections + self._pool_maxsize = maxsize + self._pool_block = block + + self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize, + block=block, strict=True, **pool_kwargs) + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + :rtype: urllib3.ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith('socks'): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs) + + return manager + + def cert_verify(self, conn, url, verify, cert): + """Verify a SSL certificate. This method should not be called from user + code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param conn: The urllib3 connection object associated with the cert. + :param url: The requested URL. + :param verify: Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: The SSL certificate to verify. + """ + if url.lower().startswith('https') and verify: + + cert_loc = None + + # Allow self-specified cert location. + if verify is not True: + cert_loc = verify + + if not cert_loc: + cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) + + if not cert_loc or not os.path.exists(cert_loc): + raise IOError("Could not find a suitable TLS CA certificate bundle, " + "invalid path: {}".format(cert_loc)) + + conn.cert_reqs = 'CERT_REQUIRED' + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + conn.ca_cert_dir = None + + if cert: + if not isinstance(cert, basestring): + conn.cert_file = cert[0] + conn.key_file = cert[1] + else: + conn.cert_file = cert + conn.key_file = None + if conn.cert_file and not os.path.exists(conn.cert_file): + raise IOError("Could not find the TLS certificate file, " + "invalid path: {}".format(conn.cert_file)) + if conn.key_file and not os.path.exists(conn.key_file): + raise IOError("Could not find the TLS key file, " + "invalid path: {}".format(conn.key_file)) + + def build_response(self, req, resp): + """Builds a :class:`Response ` object from a urllib3 + response. This should not be called from user code, and is only exposed + for use when subclassing the + :class:`HTTPAdapter ` + + :param req: The :class:`PreparedRequest ` used to generate the response. + :param resp: The urllib3 response object. + :rtype: requests.Response + """ + response = Response() + + # Fallback to None if there's no status_code, for whatever reason. + response.status_code = getattr(resp, 'status', None) + + # Make headers case-insensitive. + response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {})) + + # Set encoding. + response.encoding = get_encoding_from_headers(response.headers) + response.raw = resp + response.reason = response.raw.reason + + if isinstance(req.url, bytes): + response.url = req.url.decode('utf-8') + else: + response.url = req.url + + # Add new cookies from the server. + extract_cookies_to_jar(response.cookies, req, resp) + + # Give the Response some context. + response.request = req + response.connection = self + + return response + + def get_connection(self, url, proxies=None): + """Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter `. + + :param url: The URL to connect to. + :param proxies: (optional) A Requests-style dictionary of proxies used on this request. + :rtype: urllib3.ConnectionPool + """ + proxy = select_proxy(url, proxies) + + if proxy: + proxy = prepend_scheme_if_needed(proxy, 'http') + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL("Please check proxy URL. It is malformed" + " and could be missing the host.") + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) + else: + # Only scheme should be lower case + parsed = urlparse(url) + url = parsed.geturl() + conn = self.poolmanager.connection_from_url(url) + + return conn + + def close(self): + """Disposes of any internal state. + + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. + """ + self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() + + def request_url(self, request, proxies): + """Obtain the url to use when making the final request. + + If the message is being sent through a HTTP proxy, the full URL has to + be used. Otherwise, we should only use the path portion of the URL. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param request: The :class:`PreparedRequest ` being sent. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. + :rtype: str + """ + proxy = select_proxy(request.url, proxies) + scheme = urlparse(request.url).scheme + + is_proxied_http_request = (proxy and scheme != 'https') + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith('socks') + + url = request.path_url + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) + + return url + + def add_headers(self, request, **kwargs): + """Add any headers needed by the connection. As of v2.0 this does + nothing by default, but is left for overriding by users that subclass + the :class:`HTTPAdapter `. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param request: The :class:`PreparedRequest ` to add headers to. + :param kwargs: The keyword arguments from the call to send(). + """ + pass + + def proxy_headers(self, proxy): + """Returns a dictionary of the headers to add to any request sent + through a proxy. This works with urllib3 magic to ensure that they are + correctly sent to the proxy, rather than in a tunnelled request if + CONNECT is being used. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter `. + + :param proxy: The url of the proxy being used for this request. + :rtype: dict + """ + headers = {} + username, password = get_auth_from_url(proxy) + + if username: + headers['Proxy-Authorization'] = _basic_auth_str(username, + password) + + return headers + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection(request.url, proxies) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers(request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies) + + chunked = not (request.body is None or 'Content-Length' in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError as e: + # this may raise a string formatting error. + err = ("Invalid timeout {}. Pass a (connect, read) " + "timeout tuple, or a single float to set " + "both timeouts to the same value".format(timeout)) + raise ValueError(err) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + if not chunked: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout + ) + + # Send the request. + else: + if hasattr(conn, 'proxy_pool'): + conn = conn.proxy_pool + + low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) + + try: + low_conn.putrequest(request.method, + url, + skip_accept_encoding=True) + + for header, value in request.headers.items(): + low_conn.putheader(header, value) + + low_conn.endheaders() + + for i in request.body: + low_conn.send(hex(len(i))[2:].encode('utf-8')) + low_conn.send(b'\r\n') + low_conn.send(i) + low_conn.send(b'\r\n') + low_conn.send(b'0\r\n\r\n') + + # Receive the response from the server + try: + # For Python 2.7, use buffering of HTTP responses + r = low_conn.getresponse(buffering=True) + except TypeError: + # For compatibility with Python 3.3+ + r = low_conn.getresponse() + + resp = HTTPResponse.from_httplib( + r, + pool=conn, + connection=low_conn, + preload_content=False, + decode_content=False + ) + except: + # If we hit any problems here, clean up the connection. + # Then, reraise so that we can handle the actual exception. + low_conn.close() + raise + + except (ProtocolError, socket.error) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: + raise ConnectionError(e, request=request) + + except _ProxyError as e: + raise ProxyError(e) + + except (_SSLError, _HTTPError) as e: + if isinstance(e, _SSLError): + # This branch is for urllib3 versions earlier than v1.22 + raise SSLError(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) + else: + raise + + return self.build_response(request, resp) diff --git a/source/others_libraries/pythonpath/requests/api.py b/source/others_libraries/pythonpath/requests/api.py new file mode 100644 index 0000000..4cba90e --- /dev/null +++ b/source/others_libraries/pythonpath/requests/api.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- + +""" +requests.api +~~~~~~~~~~~~ + +This module implements the Requests API. + +:copyright: (c) 2012 by Kenneth Reitz. +:license: Apache2, see LICENSE for more details. +""" + +from . import sessions + + +def request(method, url, **kwargs): + """Constructs and sends a :class:`Request `. + + :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. + :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How many seconds to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param stream: (optional) if ``False``, the response content will be immediately downloaded. + :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response ` object + :rtype: requests.Response + + Usage:: + + >>> import requests + >>> req = requests.request('GET', 'https://httpbin.org/get') + >>> req + + """ + + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) + + +def get(url, params=None, **kwargs): + r"""Sends a GET request. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the query string for the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request('get', url, params=params, **kwargs) + + +def options(url, **kwargs): + r"""Sends an OPTIONS request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request('options', url, **kwargs) + + +def head(url, **kwargs): + r"""Sends a HEAD request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. If + `allow_redirects` is not provided, it will be set to `False` (as + opposed to the default :meth:`request` behavior). + :return: :class:`Response ` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return request('head', url, **kwargs) + + +def post(url, data=None, json=None, **kwargs): + r"""Sends a POST request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request('post', url, data=data, json=json, **kwargs) + + +def put(url, data=None, **kwargs): + r"""Sends a PUT request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request('put', url, data=data, **kwargs) + + +def patch(url, data=None, **kwargs): + r"""Sends a PATCH request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request('patch', url, data=data, **kwargs) + + +def delete(url, **kwargs): + r"""Sends a DELETE request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response ` object + :rtype: requests.Response + """ + + return request('delete', url, **kwargs) diff --git a/source/others_libraries/pythonpath/requests/auth.py b/source/others_libraries/pythonpath/requests/auth.py new file mode 100644 index 0000000..eeface3 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/auth.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- + +""" +requests.auth +~~~~~~~~~~~~~ + +This module contains the authentication handlers for Requests. +""" + +import os +import re +import time +import hashlib +import threading +import warnings + +from base64 import b64encode + +from .compat import urlparse, str, basestring +from .cookies import extract_cookies_to_jar +from ._internal_utils import to_native_string +from .utils import parse_dict_header + +CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded' +CONTENT_TYPE_MULTI_PART = 'multipart/form-data' + + +def _basic_auth_str(username, password): + """Returns a Basic Auth string.""" + + # "I want us to put a big-ol' comment on top of it that + # says that this behaviour is dumb but we need to preserve + # it because people are relying on it." + # - Lukasa + # + # These are here solely to maintain backwards compatibility + # for things like ints. This will be removed in 3.0.0. + if not isinstance(username, basestring): + warnings.warn( + "Non-string usernames will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(username), + category=DeprecationWarning, + ) + username = str(username) + + if not isinstance(password, basestring): + warnings.warn( + "Non-string passwords will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(type(password)), + category=DeprecationWarning, + ) + password = str(password) + # -- End Removal -- + + if isinstance(username, str): + username = username.encode('latin1') + + if isinstance(password, str): + password = password.encode('latin1') + + authstr = 'Basic ' + to_native_string( + b64encode(b':'.join((username, password))).strip() + ) + + return authstr + + +class AuthBase(object): + """Base class that all auth implementations derive from""" + + def __call__(self, r): + raise NotImplementedError('Auth hooks must be callable.') + + +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other + + def __call__(self, r): + r.headers['Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, r): + r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self): + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, 'init'): + self._thread_local.init = True + self._thread_local.last_nonce = '' + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None + + def build_digest_header(self, method, url): + """ + :rtype: str + """ + + realm = self._thread_local.chal['realm'] + nonce = self._thread_local.chal['nonce'] + qop = self._thread_local.chal.get('qop') + algorithm = self._thread_local.chal.get('algorithm') + opaque = self._thread_local.chal.get('opaque') + hash_utf8 = None + + if algorithm is None: + _algorithm = 'MD5' + else: + _algorithm = algorithm.upper() + # lambdas assume digest modules are imported at the top level + if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': + def md5_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.md5(x).hexdigest() + hash_utf8 = md5_utf8 + elif _algorithm == 'SHA': + def sha_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha1(x).hexdigest() + hash_utf8 = sha_utf8 + elif _algorithm == 'SHA-256': + def sha256_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha256(x).hexdigest() + hash_utf8 = sha256_utf8 + elif _algorithm == 'SHA-512': + def sha512_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha512(x).hexdigest() + hash_utf8 = sha512_utf8 + + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) + + if hash_utf8 is None: + return None + + # XXX not implemented yet + entdig = None + p_parsed = urlparse(url) + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" + if p_parsed.query: + path += '?' + p_parsed.query + + A1 = '%s:%s:%s' % (self.username, realm, self.password) + A2 = '%s:%s' % (method, path) + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 + else: + self._thread_local.nonce_count = 1 + ncvalue = '%08x' % self._thread_local.nonce_count + s = str(self._thread_local.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + if _algorithm == 'MD5-SESS': + HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) + + if not qop: + respdig = KD(HA1, "%s:%s" % (nonce, HA2)) + elif qop == 'auth' or 'auth' in qop.split(','): + noncebit = "%s:%s:%s:%s:%s" % ( + nonce, ncvalue, cnonce, 'auth', HA2 + ) + respdig = KD(HA1, noncebit) + else: + # XXX handle auth-int. + return None + + self._thread_local.last_nonce = nonce + + # XXX should the partial digests be encoded too? + base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ + 'response="%s"' % (self.username, realm, nonce, path, respdig) + if opaque: + base += ', opaque="%s"' % opaque + if algorithm: + base += ', algorithm="%s"' % algorithm + if entdig: + base += ', digest="%s"' % entdig + if qop: + base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) + + return 'Digest %s' % (base) + + def handle_redirect(self, r, **kwargs): + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + + def handle_401(self, r, **kwargs): + """ + Takes the given response and tries digest-auth, if needed. + + :rtype: requests.Response + """ + + # If response is not 4xx, do not auth + # See https://github.com/psf/requests/issues/3772 + if not 400 <= r.status_code < 500: + self._thread_local.num_401_calls = 1 + return r + + if self._thread_local.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + r.request.body.seek(self._thread_local.pos) + s_auth = r.headers.get('www-authenticate', '') + + if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2: + + self._thread_local.num_401_calls += 1 + pat = re.compile(r'digest ', flags=re.IGNORECASE) + self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1)) + + # Consume content and release the original connection + # to allow our new request to reuse the same one. + r.content + r.close() + prep = r.request.copy() + extract_cookies_to_jar(prep._cookies, r.request, r.raw) + prep.prepare_cookies(prep._cookies) + + prep.headers['Authorization'] = self.build_digest_header( + prep.method, prep.url) + _r = r.connection.send(prep, **kwargs) + _r.history.append(r) + _r.request = prep + + return _r + + self._thread_local.num_401_calls = 1 + return r + + def __call__(self, r): + # Initialize per-thread state, if needed + self.init_per_thread_state() + # If we have a saved nonce, skip the 401 + if self._thread_local.last_nonce: + r.headers['Authorization'] = self.build_digest_header(r.method, r.url) + try: + self._thread_local.pos = r.body.tell() + except AttributeError: + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None + r.register_hook('response', self.handle_401) + r.register_hook('response', self.handle_redirect) + self._thread_local.num_401_calls = 1 + + return r + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other diff --git a/source/others_libraries/pythonpath/requests/certs.py b/source/others_libraries/pythonpath/requests/certs.py new file mode 100644 index 0000000..d1a378d --- /dev/null +++ b/source/others_libraries/pythonpath/requests/certs.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +requests.certs +~~~~~~~~~~~~~~ + +This module returns the preferred default CA certificate bundle. There is +only one — the one from the certifi package. + +If you are packaging Requests, e.g., for a Linux distribution or a managed +environment, you can change the definition of where() to return a separately +packaged CA bundle. +""" +from certifi import where + +if __name__ == '__main__': + print(where()) diff --git a/source/others_libraries/pythonpath/requests/compat.py b/source/others_libraries/pythonpath/requests/compat.py new file mode 100644 index 0000000..0b14f50 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/compat.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +""" +requests.compat +~~~~~~~~~~~~~~~ + +This module handles import compatibility issues between Python 2 and +Python 3. +""" + +try: + import chardet +except ImportError: + import charset_normalizer as chardet + +import sys + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = (_ver[0] == 2) + +#: Python 3.x? +is_py3 = (_ver[0] == 3) + +try: + import simplejson as json +except ImportError: + import json + +# --------- +# Specifics +# --------- + +if is_py2: + from urllib import ( + quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, + proxy_bypass, proxy_bypass_environment, getproxies_environment) + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag + from urllib2 import parse_http_list + import cookielib + from Cookie import Morsel + from StringIO import StringIO + # Keep OrderedDict for backwards compatibility. + from collections import Callable, Mapping, MutableMapping, OrderedDict + + + builtin_str = str + bytes = str + str = unicode + basestring = basestring + numeric_types = (int, long, float) + integer_types = (int, long) + +elif is_py3: + from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag + from urllib.request import parse_http_list, getproxies, proxy_bypass, proxy_bypass_environment, getproxies_environment + from http import cookiejar as cookielib + from http.cookies import Morsel + from io import StringIO + # Keep OrderedDict for backwards compatibility. + from collections import OrderedDict + from collections.abc import Callable, Mapping, MutableMapping + + builtin_str = str + str = str + bytes = bytes + basestring = (str, bytes) + numeric_types = (int, float) + integer_types = (int,) diff --git a/source/others_libraries/pythonpath/requests/cookies.py b/source/others_libraries/pythonpath/requests/cookies.py new file mode 100644 index 0000000..56fccd9 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/cookies.py @@ -0,0 +1,549 @@ +# -*- coding: utf-8 -*- + +""" +requests.cookies +~~~~~~~~~~~~~~~~ + +Compatibility code to be able to use `cookielib.CookieJar` with requests. + +requests.utils imports from here, so be careful with imports. +""" + +import copy +import time +import calendar + +from ._internal_utils import to_native_string +from .compat import cookielib, urlparse, urlunparse, Morsel, MutableMapping + +try: + import threading +except ImportError: + import dummy_threading as threading + + +class MockRequest(object): + """Wraps a `requests.Request` to mimic a `urllib2.Request`. + + The code in `cookielib.CookieJar` expects this interface in order to correctly + manage cookie policies, i.e., determine whether a cookie can be set, given the + domains of the request and the cookie. + + The original request object is read-only. The client is responsible for collecting + the new headers via `get_new_headers()` and interpreting them appropriately. You + probably want `get_cookie_header`, defined below. + """ + + def __init__(self, request): + self._r = request + self._new_headers = {} + self.type = urlparse(self._r.url).scheme + + def get_type(self): + return self.type + + def get_host(self): + return urlparse(self._r.url).netloc + + def get_origin_req_host(self): + return self.get_host() + + def get_full_url(self): + # Only return the response's URL if the user hadn't set the Host + # header + if not self._r.headers.get('Host'): + return self._r.url + # If they did set it, retrieve it and reconstruct the expected domain + host = to_native_string(self._r.headers['Host'], encoding='utf-8') + parsed = urlparse(self._r.url) + # Reconstruct the URL as we expect it + return urlunparse([ + parsed.scheme, host, parsed.path, parsed.params, parsed.query, + parsed.fragment + ]) + + def is_unverifiable(self): + return True + + def has_header(self, name): + return name in self._r.headers or name in self._new_headers + + def get_header(self, name, default=None): + return self._r.headers.get(name, self._new_headers.get(name, default)) + + def add_header(self, key, val): + """cookielib has no legitimate use for this method; add it back if you find one.""" + raise NotImplementedError("Cookie headers should be added with add_unredirected_header()") + + def add_unredirected_header(self, name, value): + self._new_headers[name] = value + + def get_new_headers(self): + return self._new_headers + + @property + def unverifiable(self): + return self.is_unverifiable() + + @property + def origin_req_host(self): + return self.get_origin_req_host() + + @property + def host(self): + return self.get_host() + + +class MockResponse(object): + """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. + + ...what? Basically, expose the parsed HTTP headers from the server response + the way `cookielib` expects to see them. + """ + + def __init__(self, headers): + """Make a MockResponse for `cookielib` to read. + + :param headers: a httplib.HTTPMessage or analogous carrying the headers + """ + self._headers = headers + + def info(self): + return self._headers + + def getheaders(self, name): + self._headers.getheaders(name) + + +def extract_cookies_to_jar(jar, request, response): + """Extract the cookies from the response into a CookieJar. + + :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) + :param request: our own requests.Request object + :param response: urllib3.HTTPResponse object + """ + if not (hasattr(response, '_original_response') and + response._original_response): + return + # the _original_response field is the wrapped httplib.HTTPResponse object, + req = MockRequest(request) + # pull out the HTTPMessage with the headers and put it in the mock: + res = MockResponse(response._original_response.msg) + jar.extract_cookies(res, req) + + +def get_cookie_header(jar, request): + """ + Produce an appropriate Cookie header string to be sent with `request`, or None. + + :rtype: str + """ + r = MockRequest(request) + jar.add_cookie_header(r) + return r.get_new_headers().get('Cookie') + + +def remove_cookie_by_name(cookiejar, name, domain=None, path=None): + """Unsets a cookie by name, by default over all domains and paths. + + Wraps CookieJar.clear(), is O(n). + """ + clearables = [] + for cookie in cookiejar: + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) + + for domain, path, name in clearables: + cookiejar.clear(domain, path, name) + + +class CookieConflictError(RuntimeError): + """There are two cookies that meet the criteria specified in the cookie jar. + Use .get and .set and include domain and path args in order to be more specific. + """ + + +class RequestsCookieJar(cookielib.CookieJar, MutableMapping): + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. + + This is the CookieJar we create by default for requests and sessions that + don't specify one, since some clients may expect response.cookies and + session.cookies to support dict operations. + + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. + + Unlike a regular CookieJar, this class is pickleable. + + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ + + def get(self, name, default=None, domain=None, path=None): + """Dict-like get() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + + .. warning:: operation is O(n), not O(1). + """ + try: + return self._find_no_duplicates(name, domain, path) + except KeyError: + return default + + def set(self, name, value, **kwargs): + """Dict-like set() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + """ + # support client code that unsets cookies by assignment of a None value: + if value is None: + remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path')) + return + + if isinstance(value, Morsel): + c = morsel_to_cookie(value) + else: + c = create_cookie(name, value, **kwargs) + self.set_cookie(c) + return c + + def iterkeys(self): + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. + + .. seealso:: itervalues() and iteritems(). + """ + for cookie in iter(self): + yield cookie.name + + def keys(self): + """Dict-like keys() that returns a list of names of cookies from the + jar. + + .. seealso:: values() and items(). + """ + return list(self.iterkeys()) + + def itervalues(self): + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. + + .. seealso:: iterkeys() and iteritems(). + """ + for cookie in iter(self): + yield cookie.value + + def values(self): + """Dict-like values() that returns a list of values of cookies from the + jar. + + .. seealso:: keys() and items(). + """ + return list(self.itervalues()) + + def iteritems(self): + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. + + .. seealso:: iterkeys() and itervalues(). + """ + for cookie in iter(self): + yield cookie.name, cookie.value + + def items(self): + """Dict-like items() that returns a list of name-value tuples from the + jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a + vanilla python dict of key value pairs. + + .. seealso:: keys() and values(). + """ + return list(self.iteritems()) + + def list_domains(self): + """Utility method to list all the domains in the jar.""" + domains = [] + for cookie in iter(self): + if cookie.domain not in domains: + domains.append(cookie.domain) + return domains + + def list_paths(self): + """Utility method to list all the paths in the jar.""" + paths = [] + for cookie in iter(self): + if cookie.path not in paths: + paths.append(cookie.path) + return paths + + def multiple_domains(self): + """Returns True if there are multiple domains in the jar. + Returns False otherwise. + + :rtype: bool + """ + domains = [] + for cookie in iter(self): + if cookie.domain is not None and cookie.domain in domains: + return True + domains.append(cookie.domain) + return False # there is only one domain in jar + + def get_dict(self, domain=None, path=None): + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements. + + :rtype: dict + """ + dictionary = {} + for cookie in iter(self): + if ( + (domain is None or cookie.domain == domain) and + (path is None or cookie.path == path) + ): + dictionary[cookie.name] = cookie.value + return dictionary + + def __contains__(self, name): + try: + return super(RequestsCookieJar, self).__contains__(name) + except CookieConflictError: + return True + + def __getitem__(self, name): + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1). + """ + return self._find_no_duplicates(name) + + def __setitem__(self, name, value): + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead. + """ + self.set(name, value) + + def __delitem__(self, name): + """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + ``remove_cookie_by_name()``. + """ + remove_cookie_by_name(self, name) + + def set_cookie(self, cookie, *args, **kwargs): + if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'): + cookie.value = cookie.value.replace('\\"', '') + return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) + + def update(self, other): + """Updates this jar with cookies from another CookieJar or dict-like""" + if isinstance(other, cookielib.CookieJar): + for cookie in other: + self.set_cookie(copy.copy(cookie)) + else: + super(RequestsCookieJar, self).update(other) + + def _find(self, name, domain=None, path=None): + """Requests uses this method internally to get cookie values. + + If there are conflicting cookies, _find arbitrarily chooses one. + See _find_no_duplicates if you want an exception thrown if there are + conflicting cookies. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :return: cookie.value + """ + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + return cookie.value + + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def _find_no_duplicates(self, name, domain=None, path=None): + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :raises KeyError: if cookie is not found + :raises CookieConflictError: if there are multiple cookies + that match name and optionally domain and path + :return: cookie.value + """ + toReturn = None + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if toReturn is not None: # if there are multiple cookies that meet passed in criteria + raise CookieConflictError('There are multiple cookies with name, %r' % (name)) + toReturn = cookie.value # we will eventually return this as long as no cookie conflict + + if toReturn: + return toReturn + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def __getstate__(self): + """Unlike a normal CookieJar, this class is pickleable.""" + state = self.__dict__.copy() + # remove the unpickleable RLock object + state.pop('_cookies_lock') + return state + + def __setstate__(self, state): + """Unlike a normal CookieJar, this class is pickleable.""" + self.__dict__.update(state) + if '_cookies_lock' not in self.__dict__: + self._cookies_lock = threading.RLock() + + def copy(self): + """Return a copy of this RequestsCookieJar.""" + new_cj = RequestsCookieJar() + new_cj.set_policy(self.get_policy()) + new_cj.update(self) + return new_cj + + def get_policy(self): + """Return the CookiePolicy instance used.""" + return self._policy + + +def _copy_cookie_jar(jar): + if jar is None: + return None + + if hasattr(jar, 'copy'): + # We're dealing with an instance of RequestsCookieJar + return jar.copy() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + +def create_cookie(name, value, **kwargs): + """Make a cookie from underspecified parameters. + + By default, the pair of `name` and `value` will be set for the domain '' + and sent on every request (this is sometimes called a "supercookie"). + """ + result = { + 'version': 0, + 'name': name, + 'value': value, + 'port': None, + 'domain': '', + 'path': '/', + 'secure': False, + 'expires': None, + 'discard': True, + 'comment': None, + 'comment_url': None, + 'rest': {'HttpOnly': None}, + 'rfc2109': False, + } + + badargs = set(kwargs) - set(result) + if badargs: + err = 'create_cookie() got unexpected keyword arguments: %s' + raise TypeError(err % list(badargs)) + + result.update(kwargs) + result['port_specified'] = bool(result['port']) + result['domain_specified'] = bool(result['domain']) + result['domain_initial_dot'] = result['domain'].startswith('.') + result['path_specified'] = bool(result['path']) + + return cookielib.Cookie(**result) + + +def morsel_to_cookie(morsel): + """Convert a Morsel object into a Cookie containing the one k/v pair.""" + + expires = None + if morsel['max-age']: + try: + expires = int(time.time() + int(morsel['max-age'])) + except ValueError: + raise TypeError('max-age: %s must be integer' % morsel['max-age']) + elif morsel['expires']: + time_template = '%a, %d-%b-%Y %H:%M:%S GMT' + expires = calendar.timegm( + time.strptime(morsel['expires'], time_template) + ) + return create_cookie( + comment=morsel['comment'], + comment_url=bool(morsel['comment']), + discard=False, + domain=morsel['domain'], + expires=expires, + name=morsel.key, + path=morsel['path'], + port=None, + rest={'HttpOnly': morsel['httponly']}, + rfc2109=False, + secure=bool(morsel['secure']), + value=morsel.value, + version=morsel['version'] or 0, + ) + + +def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): + """Returns a CookieJar from a key/value dictionary. + + :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. + :rtype: CookieJar + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] + for name in cookie_dict: + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + + return cookiejar + + +def merge_cookies(cookiejar, cookies): + """Add cookies to cookiejar and returns a merged CookieJar. + + :param cookiejar: CookieJar object to add the cookies to. + :param cookies: Dictionary or CookieJar object to be added. + :rtype: CookieJar + """ + if not isinstance(cookiejar, cookielib.CookieJar): + raise ValueError('You can only merge into CookieJar') + + if isinstance(cookies, dict): + cookiejar = cookiejar_from_dict( + cookies, cookiejar=cookiejar, overwrite=False) + elif isinstance(cookies, cookielib.CookieJar): + try: + cookiejar.update(cookies) + except AttributeError: + for cookie_in_jar in cookies: + cookiejar.set_cookie(cookie_in_jar) + + return cookiejar diff --git a/source/others_libraries/pythonpath/requests/exceptions.py b/source/others_libraries/pythonpath/requests/exceptions.py new file mode 100644 index 0000000..c412ec9 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/exceptions.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- + +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. +""" +from urllib3.exceptions import HTTPError as BaseHTTPError + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request. + """ + + def __init__(self, *args, **kwargs): + """Initialize RequestException with `request` and `response` objects.""" + response = kwargs.pop('response', None) + self.response = response + self.request = kwargs.pop('request', None) + if (response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(RequestException, self).__init__(*args, **kwargs) + + +class InvalidJSONError(RequestException): + """A JSON error occurred.""" + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL schema (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """See defaults.py for valid schemas.""" + + +class InvalidURL(RequestException, ValueError): + """The URL provided was somehow invalid.""" + + +class InvalidHeader(RequestException, ValueError): + """The header value provided was somehow invalid.""" + + +class InvalidProxyURL(InvalidURL): + """The proxy URL provided is invalid.""" + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content.""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed.""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +class UnrewindableBodyError(RequestException): + """Requests encountered an error when trying to rewind a body.""" + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """A file was opened in text mode, but Requests determined its binary length.""" + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" diff --git a/source/others_libraries/pythonpath/requests/help.py b/source/others_libraries/pythonpath/requests/help.py new file mode 100644 index 0000000..4cd6389 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/help.py @@ -0,0 +1,135 @@ +"""Module containing bug report helper(s).""" +from __future__ import print_function + +import json +import platform +import sys +import ssl + +import idna +import urllib3 + +from . import __version__ as requests_version + +try: + import charset_normalizer +except ImportError: + charset_normalizer = None + +try: + import chardet +except ImportError: + chardet = None + +try: + from urllib3.contrib import pyopenssl +except ImportError: + pyopenssl = None + OpenSSL = None + cryptography = None +else: + import OpenSSL + import cryptography + + +def _implementation(): + """Return a dict with the Python implementation and version. + + Provide both the name and the version of the Python implementation + currently running. For example, on CPython 2.7.5 it will return + {'name': 'CPython', 'version': '2.7.5'}. + + This function works best on CPython and PyPy: in particular, it probably + doesn't work for Jython or IronPython. Future investigation should be done + to work out the correct shape of the code for those platforms. + """ + implementation = platform.python_implementation() + + if implementation == 'CPython': + implementation_version = platform.python_version() + elif implementation == 'PyPy': + implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major, + sys.pypy_version_info.minor, + sys.pypy_version_info.micro) + if sys.pypy_version_info.releaselevel != 'final': + implementation_version = ''.join([ + implementation_version, sys.pypy_version_info.releaselevel + ]) + elif implementation == 'Jython': + implementation_version = platform.python_version() # Complete Guess + elif implementation == 'IronPython': + implementation_version = platform.python_version() # Complete Guess + else: + implementation_version = 'Unknown' + + return {'name': implementation, 'version': implementation_version} + + +def info(): + """Generate information for a bug report.""" + try: + platform_info = { + 'system': platform.system(), + 'release': platform.release(), + } + except IOError: + platform_info = { + 'system': 'Unknown', + 'release': 'Unknown', + } + + implementation_info = _implementation() + urllib3_info = {'version': urllib3.__version__} + charset_normalizer_info = {'version': None} + chardet_info = {'version': None} + if charset_normalizer: + charset_normalizer_info = {'version': charset_normalizer.__version__} + if chardet: + chardet_info = {'version': chardet.__version__} + + pyopenssl_info = { + 'version': None, + 'openssl_version': '', + } + if OpenSSL: + pyopenssl_info = { + 'version': OpenSSL.__version__, + 'openssl_version': '%x' % OpenSSL.SSL.OPENSSL_VERSION_NUMBER, + } + cryptography_info = { + 'version': getattr(cryptography, '__version__', ''), + } + idna_info = { + 'version': getattr(idna, '__version__', ''), + } + + system_ssl = ssl.OPENSSL_VERSION_NUMBER + system_ssl_info = { + 'version': '%x' % system_ssl if system_ssl is not None else '' + } + + return { + 'platform': platform_info, + 'implementation': implementation_info, + 'system_ssl': system_ssl_info, + 'using_pyopenssl': pyopenssl is not None, + 'using_charset_normalizer': chardet is None, + 'pyOpenSSL': pyopenssl_info, + 'urllib3': urllib3_info, + 'chardet': chardet_info, + 'charset_normalizer': charset_normalizer_info, + 'cryptography': cryptography_info, + 'idna': idna_info, + 'requests': { + 'version': requests_version, + }, + } + + +def main(): + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/source/others_libraries/pythonpath/requests/hooks.py b/source/others_libraries/pythonpath/requests/hooks.py new file mode 100644 index 0000000..7a51f21 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/hooks.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +""" +requests.hooks +~~~~~~~~~~~~~~ + +This module provides the capabilities for the Requests hooks system. + +Available hooks: + +``response``: + The response generated from a Request. +""" +HOOKS = ['response'] + + +def default_hooks(): + return {event: [] for event in HOOKS} + +# TODO: response is the only one + + +def dispatch_hook(key, hooks, hook_data, **kwargs): + """Dispatches a hook dictionary on a given piece of data.""" + hooks = hooks or {} + hooks = hooks.get(key) + if hooks: + if hasattr(hooks, '__call__'): + hooks = [hooks] + for hook in hooks: + _hook_data = hook(hook_data, **kwargs) + if _hook_data is not None: + hook_data = _hook_data + return hook_data diff --git a/source/others_libraries/pythonpath/requests/models.py b/source/others_libraries/pythonpath/requests/models.py new file mode 100644 index 0000000..aa6fb86 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/models.py @@ -0,0 +1,966 @@ +# -*- coding: utf-8 -*- + +""" +requests.models +~~~~~~~~~~~~~~~ + +This module contains the primary objects that power Requests. +""" + +import datetime +import sys + +# Import encoding now, to avoid implicit import later. +# Implicit import within threads may cause LookupError when standard library is in a ZIP, +# such as in Embedded Python. See https://github.com/psf/requests/issues/3578. +import encodings.idna + +from urllib3.fields import RequestField +from urllib3.filepost import encode_multipart_formdata +from urllib3.util import parse_url +from urllib3.exceptions import ( + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) + +from io import UnsupportedOperation +from .hooks import default_hooks +from .structures import CaseInsensitiveDict + +from .auth import HTTPBasicAuth +from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar +from .exceptions import ( + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, + ContentDecodingError, ConnectionError, StreamConsumedError, InvalidJSONError) +from ._internal_utils import to_native_string, unicode_is_ascii +from .utils import ( + guess_filename, get_auth_from_url, requote_uri, + stream_decode_response_unicode, to_key_val_list, parse_header_links, + iter_slices, guess_json_utf, super_len, check_header_validity) +from .compat import ( + Callable, Mapping, + cookielib, urlunparse, urlsplit, urlencode, str, bytes, + is_py2, chardet, builtin_str, basestring) +from .compat import json as complexjson +from .status_codes import codes + +#: The set of HTTP status codes that indicate an automatically +#: processable redirect. +REDIRECT_STATI = ( + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 +) + +DEFAULT_REDIRECT_LIMIT = 30 +CONTENT_CHUNK_SIZE = 10 * 1024 +ITER_CHUNK_SIZE = 512 + + +class RequestEncodingMixin(object): + @property + def path_url(self): + """Build the path URL to use.""" + + url = [] + + p = urlsplit(self.url) + + path = p.path + if not path: + path = '/' + + url.append(path) + + query = p.query + if query: + url.append('?') + url.append(query) + + return ''.join(url) + + @staticmethod + def _encode_params(data): + """Encode parameters in a piece of data. + + Will successfully encode parameters when passed as a dict or a list of + 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + if parameters are supplied as a dict. + """ + + if isinstance(data, (str, bytes)): + return data + elif hasattr(data, 'read'): + return data + elif hasattr(data, '__iter__'): + result = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): + vs = [vs] + for v in vs: + if v is not None: + result.append( + (k.encode('utf-8') if isinstance(k, str) else k, + v.encode('utf-8') if isinstance(v, str) else v)) + return urlencode(result, doseq=True) + else: + return data + + @staticmethod + def _encode_files(files, data): + """Build the body for a multipart/form-data request. + + Will successfully encode files when passed as a dict or a list of + tuples. Order is retained if data is a list of tuples but arbitrary + if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). + """ + if (not files): + raise ValueError("Files must be provided.") + elif isinstance(data, basestring): + raise ValueError("Data must not be a string.") + + new_fields = [] + fields = to_key_val_list(data or {}) + files = to_key_val_list(files or {}) + + for field, val in fields: + if isinstance(val, basestring) or not hasattr(val, '__iter__'): + val = [val] + for v in val: + if v is not None: + # Don't call str() on bytestrings: in Py3 it all goes wrong. + if not isinstance(v, bytes): + v = str(v) + + new_fields.append( + (field.decode('utf-8') if isinstance(field, bytes) else field, + v.encode('utf-8') if isinstance(v, str) else v)) + + for (k, v) in files: + # support for explicit filename + ft = None + fh = None + if isinstance(v, (tuple, list)): + if len(v) == 2: + fn, fp = v + elif len(v) == 3: + fn, fp, ft = v + else: + fn, fp, ft, fh = v + else: + fn = guess_filename(v) or k + fp = v + + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + elif hasattr(fp, 'read'): + fdata = fp.read() + elif fp is None: + continue + else: + fdata = fp + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) + rf.make_multipart(content_type=ft) + new_fields.append(rf) + + body, content_type = encode_multipart_formdata(new_fields) + + return body, content_type + + +class RequestHooksMixin(object): + def register_hook(self, event, hook): + """Properly register a hook.""" + + if event not in self.hooks: + raise ValueError('Unsupported event specified, with event name "%s"' % (event)) + + if isinstance(hook, Callable): + self.hooks[event].append(hook) + elif hasattr(hook, '__iter__'): + self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) + + def deregister_hook(self, event, hook): + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False + + +class Request(RequestHooksMixin): + """A user-created :class:`Request ` object. + + Used to prepare a :class:`PreparedRequest `, which is sent to the server. + + :param method: HTTP method to use. + :param url: URL to send. + :param headers: dictionary of headers to send. + :param files: dictionary of {filename: fileobject} files to multipart upload. + :param data: the body to attach to the request. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param json: json for the body to attach to the request (if files or data is not specified). + :param params: URL parameters to append to the URL. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param auth: Auth handler or (user, pass) tuple. + :param cookies: dictionary or CookieJar of cookies to attach to this request. + :param hooks: dictionary of callback hooks, for internal usage. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> req.prepare() + + """ + + def __init__(self, + method=None, url=None, headers=None, files=None, data=None, + params=None, auth=None, cookies=None, hooks=None, json=None): + + # Default empty dicts for dict params. + data = [] if data is None else data + files = [] if files is None else files + headers = {} if headers is None else headers + params = {} if params is None else params + hooks = {} if hooks is None else hooks + + self.hooks = default_hooks() + for (k, v) in list(hooks.items()): + self.register_hook(event=k, hook=v) + + self.method = method + self.url = url + self.headers = headers + self.files = files + self.data = data + self.json = json + self.params = params + self.auth = auth + self.cookies = cookies + + def __repr__(self): + return '' % (self.method) + + def prepare(self): + """Constructs a :class:`PreparedRequest ` for transmission and returns it.""" + p = PreparedRequest() + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + json=self.json, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) + return p + + +class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): + """The fully mutable :class:`PreparedRequest ` object, + containing the exact bytes that will be sent to the server. + + Instances are generated from a :class:`Request ` object, and + should not be instantiated manually; doing so may produce undesirable + effects. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> r = req.prepare() + >>> r + + + >>> s = requests.Session() + >>> s.send(r) + + """ + + def __init__(self): + #: HTTP verb to send to the server. + self.method = None + #: HTTP URL to send the request to. + self.url = None + #: dictionary of HTTP headers. + self.headers = None + # The `CookieJar` used to create the Cookie header will be stored here + # after prepare_cookies is called + self._cookies = None + #: request body to send to the server. + self.body = None + #: dictionary of callback hooks, for internal usage. + self.hooks = default_hooks() + #: integer denoting starting position of a readable file-like body. + self._body_position = None + + def prepare(self, + method=None, url=None, headers=None, files=None, data=None, + params=None, auth=None, cookies=None, hooks=None, json=None): + """Prepares the entire request with the given parameters.""" + + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files, json) + self.prepare_auth(auth, url) + + # Note that prepare_auth must be last to enable authentication schemes + # such as OAuth to work on a fully prepared request. + + # This MUST go after prepare_auth. Authenticators could add a hook + self.prepare_hooks(hooks) + + def __repr__(self): + return '' % (self.method) + + def copy(self): + p = PreparedRequest() + p.method = self.method + p.url = self.url + p.headers = self.headers.copy() if self.headers is not None else None + p._cookies = _copy_cookie_jar(self._cookies) + p.body = self.body + p.hooks = self.hooks + p._body_position = self._body_position + return p + + def prepare_method(self, method): + """Prepares the given HTTP method.""" + self.method = method + if self.method is not None: + self.method = to_native_string(self.method.upper()) + + @staticmethod + def _get_idna_encoded_host(host): + import idna + + try: + host = idna.encode(host, uts46=True).decode('utf-8') + except idna.IDNAError: + raise UnicodeError + return host + + def prepare_url(self, url, params): + """Prepares the given HTTP URL.""" + #: Accept objects that have string representations. + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/psf/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode('utf8') + else: + url = unicode(url) if is_py2 else str(url) + + # Remove leading whitespaces from url + url = url.lstrip() + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. + if ':' in url and not url.lower().startswith('http'): + self.url = url + return + + # Support for unicode domain names and paths. + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) + + if not scheme: + error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?") + error = error.format(to_native_string(url, 'utf8')) + + raise MissingSchema(error) + + if not host: + raise InvalidURL("Invalid URL %r: No host supplied" % url) + + # In general, we want to try IDNA encoding the hostname if the string contains + # non-ASCII characters. This allows users to automatically get the correct IDNA + # behaviour. For strings containing only ASCII characters, we need to also verify + # it doesn't start with a wildcard (*), before allowing the unencoded hostname. + if not unicode_is_ascii(host): + try: + host = self._get_idna_encoded_host(host) + except UnicodeError: + raise InvalidURL('URL has an invalid label.') + elif host.startswith(u'*'): + raise InvalidURL('URL has an invalid label.') + + # Carefully reconstruct the network location + netloc = auth or '' + if netloc: + netloc += '@' + netloc += host + if port: + netloc += ':' + str(port) + + # Bare domains aren't valid URLs. + if not path: + path = '/' + + if is_py2: + if isinstance(scheme, str): + scheme = scheme.encode('utf-8') + if isinstance(netloc, str): + netloc = netloc.encode('utf-8') + if isinstance(path, str): + path = path.encode('utf-8') + if isinstance(query, str): + query = query.encode('utf-8') + if isinstance(fragment, str): + fragment = fragment.encode('utf-8') + + if isinstance(params, (str, bytes)): + params = to_native_string(params) + + enc_params = self._encode_params(params) + if enc_params: + if query: + query = '%s&%s' % (query, enc_params) + else: + query = enc_params + + url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) + self.url = url + + def prepare_headers(self, headers): + """Prepares the given HTTP headers.""" + + self.headers = CaseInsensitiveDict() + if headers: + for header in headers.items(): + # Raise exception on invalid header value. + check_header_validity(header) + name, value = header + self.headers[to_native_string(name)] = value + + def prepare_body(self, data, files, json=None): + """Prepares the given HTTP body data.""" + + # Check if file, fo, generator, iterator. + # If not, run through normal process. + + # Nottin' on you. + body = None + content_type = None + + if not data and json is not None: + # urllib3 requires a bytes-like body. Python 2's json.dumps + # provides this natively, but Python 3 gives a Unicode string. + content_type = 'application/json' + + try: + body = complexjson.dumps(json, allow_nan=False) + except ValueError as ve: + raise InvalidJSONError(ve, request=self) + + if not isinstance(body, bytes): + body = body.encode('utf-8') + + is_stream = all([ + hasattr(data, '__iter__'), + not isinstance(data, (basestring, list, tuple, Mapping)) + ]) + + if is_stream: + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + + body = data + + if getattr(body, 'tell', None) is not None: + # Record the current file position before reading. + # This will allow us to rewind a file in the event + # of a redirect. + try: + self._body_position = body.tell() + except (IOError, OSError): + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body + self._body_position = object() + + if files: + raise NotImplementedError('Streamed bodies and files are mutually exclusive.') + + if length: + self.headers['Content-Length'] = builtin_str(length) + else: + self.headers['Transfer-Encoding'] = 'chunked' + else: + # Multi-part file uploads. + if files: + (body, content_type) = self._encode_files(files, data) + else: + if data: + body = self._encode_params(data) + if isinstance(data, basestring) or hasattr(data, 'read'): + content_type = None + else: + content_type = 'application/x-www-form-urlencoded' + + self.prepare_content_length(body) + + # Add content-type if it wasn't explicitly provided. + if content_type and ('content-type' not in self.headers): + self.headers['Content-Type'] = content_type + + self.body = body + + def prepare_content_length(self, body): + """Prepare Content-Length header based on request method and body""" + if body is not None: + length = super_len(body) + if length: + # If length exists, set it. Otherwise, we fallback + # to Transfer-Encoding: chunked. + self.headers['Content-Length'] = builtin_str(length) + elif self.method not in ('GET', 'HEAD') and self.headers.get('Content-Length') is None: + # Set Content-Length to 0 for methods that can have a body + # but don't provide one. (i.e. not GET or HEAD) + self.headers['Content-Length'] = '0' + + def prepare_auth(self, auth, url=''): + """Prepares the given HTTP auth data.""" + + # If no Auth is explicitly provided, extract it from the URL first. + if auth is None: + url_auth = get_auth_from_url(self.url) + auth = url_auth if any(url_auth) else None + + if auth: + if isinstance(auth, tuple) and len(auth) == 2: + # special-case basic HTTP auth + auth = HTTPBasicAuth(*auth) + + # Allow auth to make its changes. + r = auth(self) + + # Update self to reflect the auth changes. + self.__dict__.update(r.__dict__) + + # Recompute Content-Length + self.prepare_content_length(self.body) + + def prepare_cookies(self, cookies): + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest ` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand. + """ + if isinstance(cookies, cookielib.CookieJar): + self._cookies = cookies + else: + self._cookies = cookiejar_from_dict(cookies) + + cookie_header = get_cookie_header(self._cookies, self) + if cookie_header is not None: + self.headers['Cookie'] = cookie_header + + def prepare_hooks(self, hooks): + """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or [] + for event in hooks: + self.register_hook(event, hooks[event]) + + +class Response(object): + """The :class:`Response ` object, which contains a + server's response to an HTTP request. + """ + + __attrs__ = [ + '_content', 'status_code', 'headers', 'url', 'history', + 'encoding', 'reason', 'cookies', 'elapsed', 'request' + ] + + def __init__(self): + self._content = False + self._content_consumed = False + self._next = None + + #: Integer Code of responded HTTP Status, e.g. 404 or 200. + self.status_code = None + + #: Case-insensitive Dictionary of Response Headers. + #: For example, ``headers['content-encoding']`` will return the + #: value of a ``'Content-Encoding'`` response header. + self.headers = CaseInsensitiveDict() + + #: File-like object representation of response (for advanced usage). + #: Use of ``raw`` requires that ``stream=True`` be set on the request. + #: This requirement does not apply for use internally to Requests. + self.raw = None + + #: Final URL location of Response. + self.url = None + + #: Encoding to decode with when accessing r.text. + self.encoding = None + + #: A list of :class:`Response ` objects from + #: the history of the Request. Any redirect responses will end + #: up here. The list is sorted from the oldest to the most recent request. + self.history = [] + + #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". + self.reason = None + + #: A CookieJar of Cookies the server sent back. + self.cookies = cookiejar_from_dict({}) + + #: The amount of time elapsed between sending the request + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. + self.elapsed = datetime.timedelta(0) + + #: The :class:`PreparedRequest ` object to which this + #: is a response. + self.request = None + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def __getstate__(self): + # Consume everything; accessing the content attribute makes + # sure the content has been fully read. + if not self._content_consumed: + self.content + + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + # pickled objects do not have .raw + setattr(self, '_content_consumed', True) + setattr(self, 'raw', None) + + def __repr__(self): + return '' % (self.status_code) + + def __bool__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __nonzero__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __iter__(self): + """Allows you to use a response as an iterator.""" + return self.iter_content(128) + + @property + def ok(self): + """Returns True if :attr:`status_code` is less than 400, False if not. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + try: + self.raise_for_status() + except HTTPError: + return False + return True + + @property + def is_redirect(self): + """True if this Response is a well-formed HTTP redirect that could have + been processed automatically (by :meth:`Session.resolve_redirects`). + """ + return ('location' in self.headers and self.status_code in REDIRECT_STATI) + + @property + def is_permanent_redirect(self): + """True if this Response one of the permanent versions of redirect.""" + return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect)) + + @property + def next(self): + """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" + return self._next + + @property + def apparent_encoding(self): + """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" + return chardet.detect(self.content)['encoding'] + + def iter_content(self, chunk_size=1, decode_unicode=False): + """Iterates over the response data. When stream=True is set on the + request, this avoids reading the content at once into memory for + large responses. The chunk size is the number of bytes it should + read into memory. This is not necessarily the length of each item + returned as decoding can take place. + + chunk_size must be of type int or None. A value of None will + function differently depending on the value of `stream`. + stream=True will read data as it arrives in whatever size the + chunks are received. If stream=False, data is returned as + a single chunk. + + If decode_unicode is True, content will be decoded using the best + available encoding based on the response. + """ + + def generate(): + # Special case for urllib3. + if hasattr(self.raw, 'stream'): + try: + for chunk in self.raw.stream(chunk_size, decode_content=True): + yield chunk + except ProtocolError as e: + raise ChunkedEncodingError(e) + except DecodeError as e: + raise ContentDecodingError(e) + except ReadTimeoutError as e: + raise ConnectionError(e) + else: + # Standard file-like object. + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + yield chunk + + self._content_consumed = True + + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() + elif chunk_size is not None and not isinstance(chunk_size, int): + raise TypeError("chunk_size must be an int, it is instead a %s." % type(chunk_size)) + # simulate reading small chunks of the content + reused_chunks = iter_slices(self._content, chunk_size) + + stream_chunks = generate() + + chunks = reused_chunks if self._content_consumed else stream_chunks + + if decode_unicode: + chunks = stream_decode_response_unicode(chunks, self) + + return chunks + + def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None): + """Iterates over the response data, one line at a time. When + stream=True is set on the request, this avoids reading the + content at once into memory for large responses. + + .. note:: This method is not reentrant safe. + """ + + pending = None + + for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode): + + if pending is not None: + chunk = pending + chunk + + if delimiter: + lines = chunk.split(delimiter) + else: + lines = chunk.splitlines() + + if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: + pending = lines.pop() + else: + pending = None + + for line in lines: + yield line + + if pending is not None: + yield pending + + @property + def content(self): + """Content of the response, in bytes.""" + + if self._content is False: + # Read the contents. + if self._content_consumed: + raise RuntimeError( + 'The content for this response was already consumed') + + if self.status_code == 0 or self.raw is None: + self._content = None + else: + self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b'' + + self._content_consumed = True + # don't need to release the connection; that's been handled by urllib3 + # since we exhausted the data. + return self._content + + @property + def text(self): + """Content of the response, in unicode. + + If Response.encoding is None, encoding will be guessed using + ``charset_normalizer`` or ``chardet``. + + The encoding of the response content is determined based solely on HTTP + headers, following RFC 2616 to the letter. If you can take advantage of + non-HTTP knowledge to make a better guess at the encoding, you should + set ``r.encoding`` appropriately before accessing this property. + """ + + # Try charset from content-type + content = None + encoding = self.encoding + + if not self.content: + return str('') + + # Fallback to auto-detected encoding. + if self.encoding is None: + encoding = self.apparent_encoding + + # Decode unicode from given encoding. + try: + content = str(self.content, encoding, errors='replace') + except (LookupError, TypeError): + # A LookupError is raised if the encoding was not found which could + # indicate a misspelling or similar mistake. + # + # A TypeError can be raised if encoding is None + # + # So we try blindly encoding. + content = str(self.content, errors='replace') + + return content + + def json(self, **kwargs): + r"""Returns the json-encoded content of a response, if any. + + :param \*\*kwargs: Optional arguments that ``json.loads`` takes. + :raises simplejson.JSONDecodeError: If the response body does not + contain valid json and simplejson is installed. + :raises json.JSONDecodeError: If the response body does not contain + valid json and simplejson is not installed on Python 3. + :raises ValueError: If the response body does not contain valid + json and simplejson is not installed on Python 2. + """ + + if not self.encoding and self.content and len(self.content) > 3: + # No encoding set. JSON RFC 4627 section 3 states we should expect + # UTF-8, -16 or -32. Detect which one to use; If the detection or + # decoding fails, fall back to `self.text` (using charset_normalizer to make + # a best guess). + encoding = guess_json_utf(self.content) + if encoding is not None: + try: + return complexjson.loads( + self.content.decode(encoding), **kwargs + ) + except UnicodeDecodeError: + # Wrong UTF codec detected; usually because it's not UTF-8 + # but some other 8-bit codec. This is an RFC violation, + # and the server didn't bother to tell us what codec *was* + # used. + pass + return complexjson.loads(self.text, **kwargs) + + @property + def links(self): + """Returns the parsed header links of the response, if any.""" + + header = self.headers.get('link') + + # l = MultiDict() + l = {} + + if header: + links = parse_header_links(header) + + for link in links: + key = link.get('rel') or link.get('url') + l[key] = link + + return l + + def raise_for_status(self): + """Raises :class:`HTTPError`, if one occurred.""" + + http_error_msg = '' + if isinstance(self.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. (See PR #3538) + try: + reason = self.reason.decode('utf-8') + except UnicodeDecodeError: + reason = self.reason.decode('iso-8859-1') + else: + reason = self.reason + + if 400 <= self.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url) + + elif 500 <= self.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url) + + if http_error_msg: + raise HTTPError(http_error_msg, response=self) + + def close(self): + """Releases the connection back to the pool. Once this method has been + called the underlying ``raw`` object must not be accessed again. + + *Note: Should not normally need to be called explicitly.* + """ + if not self._content_consumed: + self.raw.close() + + release_conn = getattr(self.raw, 'release_conn', None) + if release_conn is not None: + release_conn() diff --git a/source/others_libraries/pythonpath/requests/packages.py b/source/others_libraries/pythonpath/requests/packages.py new file mode 100644 index 0000000..00196bf --- /dev/null +++ b/source/others_libraries/pythonpath/requests/packages.py @@ -0,0 +1,26 @@ +import sys + +try: + import chardet +except ImportError: + import charset_normalizer as chardet + import warnings + + warnings.filterwarnings('ignore', 'Trying to detect', module='charset_normalizer') + +# This code exists for backwards compatibility reasons. +# I don't like it either. Just look the other way. :) + +for package in ('urllib3', 'idna'): + locals()[package] = __import__(package) + # This traversal is apparently necessary such that the identities are + # preserved (requests.packages.urllib3.* is urllib3.*) + for mod in list(sys.modules): + if mod == package or mod.startswith(package + '.'): + sys.modules['requests.packages.' + mod] = sys.modules[mod] + +target = chardet.__name__ +for mod in list(sys.modules): + if mod == target or mod.startswith(target + '.'): + sys.modules['requests.packages.' + target.replace(target, 'chardet')] = sys.modules[mod] +# Kinda cool, though, right? diff --git a/source/others_libraries/pythonpath/requests/sessions.py b/source/others_libraries/pythonpath/requests/sessions.py new file mode 100644 index 0000000..ae4bcc8 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/sessions.py @@ -0,0 +1,781 @@ +# -*- coding: utf-8 -*- + +""" +requests.sessions +~~~~~~~~~~~~~~~~~ + +This module provides a Session object to manage and persist settings across +requests (cookies, auth, proxies). +""" +import os +import sys +import time +from datetime import timedelta +from collections import OrderedDict + +from .auth import _basic_auth_str +from .compat import cookielib, is_py3, urljoin, urlparse, Mapping +from .cookies import ( + cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) +from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT +from .hooks import default_hooks, dispatch_hook +from ._internal_utils import to_native_string +from .utils import to_key_val_list, default_headers, DEFAULT_PORTS +from .exceptions import ( + TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) + +from .structures import CaseInsensitiveDict +from .adapters import HTTPAdapter + +from .utils import ( + requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, + get_auth_from_url, rewind_body +) + +from .status_codes import codes + +# formerly defined here, reexposed here for backward compatibility +from .models import REDIRECT_STATI + +# Preferred clock, based on which one is more accurate on a given system. +if sys.platform == 'win32': + try: # Python 3.4+ + preferred_clock = time.perf_counter + except AttributeError: # Earlier than Python 3. + preferred_clock = time.clock +else: + preferred_clock = time.time + + +def merge_setting(request_setting, session_setting, dict_class=OrderedDict): + """Determines appropriate setting for a given request, taking into account + the explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` + """ + + if session_setting is None: + return request_setting + + if request_setting is None: + return session_setting + + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and + isinstance(request_setting, Mapping) + ): + return request_setting + + merged_setting = dict_class(to_key_val_list(session_setting)) + merged_setting.update(to_key_val_list(request_setting)) + + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] + + return merged_setting + + +def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): + """Properly merges both requests and session hooks. + + This is necessary because when request_hooks == {'response': []}, the + merge breaks Session hooks entirely. + """ + if session_hooks is None or session_hooks.get('response') == []: + return request_hooks + + if request_hooks is None or request_hooks.get('response') == []: + return session_hooks + + return merge_setting(request_hooks, session_hooks, dict_class) + + +class SessionRedirectMixin(object): + + def get_redirect_target(self, resp): + """Receives a Response. Returns a redirect URI or ``None``""" + # Due to the nature of how requests processes redirects this method will + # be called at least once upon the original response and at least twice + # on each subsequent redirect response (if any). + # If a custom mixin is used to handle this logic, it may be advantageous + # to cache the redirect location onto the response object as a private + # attribute. + if resp.is_redirect: + location = resp.headers['location'] + # Currently the underlying http module on py3 decode headers + # in latin1, but empirical evidence suggests that latin1 is very + # rarely used with non-ASCII characters in HTTP headers. + # It is more likely to get UTF8 header rather than latin1. + # This causes incorrect handling of UTF8 encoded location headers. + # To solve this, we re-encode the location in latin1. + if is_py3: + location = location.encode('latin1') + return to_native_string(location, 'utf8') + return None + + def should_strip_auth(self, old_url, new_url): + """Decide whether Authorization header should be removed when redirecting""" + old_parsed = urlparse(old_url) + new_parsed = urlparse(new_url) + if old_parsed.hostname != new_parsed.hostname: + return True + # Special case: allow http -> https redirect when using the standard + # ports. This isn't specified by RFC 7235, but is kept to avoid + # breaking backwards compatibility with older versions of requests + # that allowed any redirects on the same host. + if (old_parsed.scheme == 'http' and old_parsed.port in (80, None) + and new_parsed.scheme == 'https' and new_parsed.port in (443, None)): + return False + + # Handle default port usage corresponding to scheme. + changed_port = old_parsed.port != new_parsed.port + changed_scheme = old_parsed.scheme != new_parsed.scheme + default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) + if (not changed_scheme and old_parsed.port in default_port + and new_parsed.port in default_port): + return False + + # Standard case: root URI must match + return changed_port or changed_scheme + + def resolve_redirects(self, resp, req, stream=False, timeout=None, + verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): + """Receives a Response. Returns a generator of Responses or Requests.""" + + hist = [] # keep track of history + + url = self.get_redirect_target(resp) + previous_fragment = urlparse(req.url).fragment + while url: + prepared_request = req.copy() + + # Update history and keep track of redirects. + # resp.history must ignore the original request in this loop + hist.append(resp) + resp.history = hist[1:] + + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) + + if len(resp.history) >= self.max_redirects: + raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp) + + # Release the connection back into the pool. + resp.close() + + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith('//'): + parsed_rurl = urlparse(resp.url) + url = ':'.join([to_native_string(parsed_rurl.scheme), url]) + + # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) + parsed = urlparse(url) + if parsed.fragment == '' and previous_fragment: + parsed = parsed._replace(fragment=previous_fragment) + elif parsed.fragment: + previous_fragment = parsed.fragment + url = parsed.geturl() + + # Facilitate relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # Compliant with RFC3986, we percent encode the url. + if not parsed.netloc: + url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) + + prepared_request.url = to_native_string(url) + + self.rebuild_method(prepared_request, resp) + + # https://github.com/psf/requests/issues/1084 + if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): + # https://github.com/psf/requests/issues/3490 + purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') + for header in purged_headers: + prepared_request.headers.pop(header, None) + prepared_request.body = None + + headers = prepared_request.headers + headers.pop('Cookie', None) + + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) + merge_cookies(prepared_request._cookies, self.cookies) + prepared_request.prepare_cookies(prepared_request._cookies) + + # Rebuild auth and proxy information. + proxies = self.rebuild_proxies(prepared_request, proxies) + self.rebuild_auth(prepared_request, resp) + + # A failed tell() sets `_body_position` to `object()`. This non-None + # value ensures `rewindable` will be True, allowing us to raise an + # UnrewindableBodyError, instead of hanging the connection. + rewindable = ( + prepared_request._body_position is not None and + ('Content-Length' in headers or 'Transfer-Encoding' in headers) + ) + + # Attempt to rewind consumed file-like object. + if rewindable: + rewind_body(prepared_request) + + # Override the original request. + req = prepared_request + + if yield_requests: + yield req + else: + + resp = self.send( + req, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + allow_redirects=False, + **adapter_kwargs + ) + + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + + # extract redirect url, if any, for the next loop + url = self.get_redirect_target(resp) + yield resp + + def rebuild_auth(self, prepared_request, response): + """When being redirected we may want to strip authentication from the + request to avoid leaking credentials. This method intelligently removes + and reapplies authentication where possible to avoid credential loss. + """ + headers = prepared_request.headers + url = prepared_request.url + + if 'Authorization' in headers and self.should_strip_auth(response.request.url, url): + # If we get redirected to a new host, we should strip out any + # authentication headers. + del headers['Authorization'] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None + if new_auth is not None: + prepared_request.prepare_auth(new_auth) + + + def rebuild_proxies(self, prepared_request, proxies): + """This method re-evaluates the proxy configuration by considering the + environment variables. If we are redirected to a URL covered by + NO_PROXY, we strip the proxy configuration. Otherwise, we set missing + proxy keys for this URL (in case they were stripped by a previous + redirect). + + This method also replaces the Proxy-Authorization header where + necessary. + + :rtype: dict + """ + proxies = proxies if proxies is not None else {} + headers = prepared_request.headers + url = prepared_request.url + scheme = urlparse(url).scheme + new_proxies = proxies.copy() + no_proxy = proxies.get('no_proxy') + + bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) + if self.trust_env and not bypass_proxy: + environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) + + proxy = environ_proxies.get(scheme, environ_proxies.get('all')) + + if proxy: + new_proxies.setdefault(scheme, proxy) + + if 'Proxy-Authorization' in headers: + del headers['Proxy-Authorization'] + + try: + username, password = get_auth_from_url(new_proxies[scheme]) + except KeyError: + username, password = None, None + + if username and password: + headers['Proxy-Authorization'] = _basic_auth_str(username, password) + + return new_proxies + + def rebuild_method(self, prepared_request, response): + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != 'HEAD': + method = 'GET' + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != 'HEAD': + method = 'GET' + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == 'POST': + method = 'GET' + + prepared_request.method = method + + +class Session(SessionRedirectMixin): + """A Requests session. + + Provides cookie persistence, connection-pooling, and configuration. + + Basic Usage:: + + >>> import requests + >>> s = requests.Session() + >>> s.get('https://httpbin.org/get') + + + Or as a context manager:: + + >>> with requests.Session() as s: + ... s.get('https://httpbin.org/get') + + """ + + __attrs__ = [ + 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', + 'cert', 'adapters', 'stream', 'trust_env', + 'max_redirects', + ] + + def __init__(self): + + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request ` sent from this + #: :class:`Session `. + self.headers = default_headers() + + #: Default Authentication tuple or object to attach to + #: :class:`Request `. + self.auth = None + + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request `. + self.proxies = {} + + #: Event-handling hooks. + self.hooks = default_hooks() + + #: Dictionary of querystring data to attach to each + #: :class:`Request `. The dictionary values may be lists for + #: representing multivalued query parameters. + self.params = {} + + #: Stream response content default. + self.stream = False + + #: SSL Verification default. + #: Defaults to `True`, requiring requests to verify the TLS certificate at the + #: remote end. + #: If verify is set to `False`, requests will accept any TLS certificate + #: presented by the server, and will ignore hostname mismatches and/or + #: expired certificates, which will make your application vulnerable to + #: man-in-the-middle (MitM) attacks. + #: Only set this to `False` for testing. + self.verify = True + + #: SSL client certificate default, if String, path to ssl client + #: cert file (.pem). If Tuple, ('cert', 'key') pair. + self.cert = None + + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. + #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is + #: 30. + self.max_redirects = DEFAULT_REDIRECT_LIMIT + + #: Trust environment settings for proxy configuration, default + #: authentication and similar. + self.trust_env = True + + #: A CookieJar containing all currently outstanding cookies set on this + #: session. By default it is a + #: :class:`RequestsCookieJar `, but + #: may be any other ``cookielib.CookieJar`` compatible object. + self.cookies = cookiejar_from_dict({}) + + # Default connection adapters. + self.adapters = OrderedDict() + self.mount('https://', HTTPAdapter()) + self.mount('http://', HTTPAdapter()) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def prepare_request(self, request): + """Constructs a :class:`PreparedRequest ` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request ` instance and those of the + :class:`Session`. + + :param request: :class:`Request` instance to prepare with this + session's settings. + :rtype: requests.PreparedRequest + """ + cookies = request.cookies or {} + + # Bootstrap CookieJar. + if not isinstance(cookies, cookielib.CookieJar): + cookies = cookiejar_from_dict(cookies) + + # Merge with session cookies + merged_cookies = merge_cookies( + merge_cookies(RequestsCookieJar(), self.cookies), cookies) + + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(request.url) + + p = PreparedRequest() + p.prepare( + method=request.method.upper(), + url=request.url, + files=request.files, + data=request.data, + json=request.json, + headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_hooks(request.hooks, self.hooks), + ) + return p + + def request(self, method, url, + params=None, data=None, headers=None, cookies=None, files=None, + auth=None, timeout=None, allow_redirects=True, proxies=None, + hooks=None, stream=None, verify=None, cert=None, json=None): + """Constructs a :class:`Request `, prepares it and sends it. + Returns :class:`Response ` object. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query + string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the + :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the + :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the + :class:`Request`. + :param files: (optional) Dictionary of ``'filename': file-like-objects`` + for multipart encoding upload. + :param auth: (optional) Auth tuple or callable to enable + Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. When set to + ``False``, requests will accept any TLS certificate presented by + the server, and will ignore hostname mismatches and/or expired + certificates, which will make your application vulnerable to + man-in-the-middle (MitM) attacks. Setting verify to ``False`` + may be useful during local development or testing. + :param cert: (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :rtype: requests.Response + """ + # Create the Request. + req = Request( + method=method.upper(), + url=url, + headers=headers, + files=files, + data=data or {}, + json=json, + params=params or {}, + auth=auth, + cookies=cookies, + hooks=hooks, + ) + prep = self.prepare_request(req) + + proxies = proxies or {} + + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) + + # Send the request. + send_kwargs = { + 'timeout': timeout, + 'allow_redirects': allow_redirects, + } + send_kwargs.update(settings) + resp = self.send(prep, **send_kwargs) + + return resp + + def get(self, url, **kwargs): + r"""Sends a GET request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('GET', url, **kwargs) + + def options(self, url, **kwargs): + r"""Sends a OPTIONS request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('OPTIONS', url, **kwargs) + + def head(self, url, **kwargs): + r"""Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return self.request('HEAD', url, **kwargs) + + def post(self, url, data=None, json=None, **kwargs): + r"""Sends a POST request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('POST', url, data=data, json=json, **kwargs) + + def put(self, url, data=None, **kwargs): + r"""Sends a PUT request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PUT', url, data=data, **kwargs) + + def patch(self, url, data=None, **kwargs): + r"""Sends a PATCH request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PATCH', url, data=data, **kwargs) + + def delete(self, url, **kwargs): + r"""Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('DELETE', url, **kwargs) + + def send(self, request, **kwargs): + """Send a given PreparedRequest. + + :rtype: requests.Response + """ + # Set defaults that the hooks can utilize to ensure they always have + # the correct parameters to reproduce the previous request. + kwargs.setdefault('stream', self.stream) + kwargs.setdefault('verify', self.verify) + kwargs.setdefault('cert', self.cert) + kwargs.setdefault('proxies', self.rebuild_proxies(request, self.proxies)) + + # It's possible that users might accidentally send a Request object. + # Guard against that specific failure case. + if isinstance(request, Request): + raise ValueError('You can only send PreparedRequests.') + + # Set up variables needed for resolve_redirects and dispatching of hooks + allow_redirects = kwargs.pop('allow_redirects', True) + stream = kwargs.get('stream') + hooks = request.hooks + + # Get the appropriate adapter to use + adapter = self.get_adapter(url=request.url) + + # Start time (approximately) of the request + start = preferred_clock() + + # Send the request + r = adapter.send(request, **kwargs) + + # Total elapsed time of the request (approximately) + elapsed = preferred_clock() - start + r.elapsed = timedelta(seconds=elapsed) + + # Response manipulation hooks + r = dispatch_hook('response', hooks, r, **kwargs) + + # Persist cookies + if r.history: + + # If the hooks create history then we want those cookies too + for resp in r.history: + extract_cookies_to_jar(self.cookies, resp.request, resp.raw) + + extract_cookies_to_jar(self.cookies, request, r.raw) + + # Resolve redirects if allowed. + if allow_redirects: + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + history = [resp for resp in gen] + else: + history = [] + + # Shuffle things around if there's history. + if history: + # Insert the first (original) request at the start + history.insert(0, r) + # Get the last request made + r = history.pop() + r.history = history + + # If redirects aren't being followed, store the response on the Request for Response.next(). + if not allow_redirects: + try: + r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) + except StopIteration: + pass + + if not stream: + r.content + + return r + + def merge_environment_settings(self, url, proxies, stream, verify, cert): + """ + Check the environment and merge it with some settings. + + :rtype: dict + """ + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + no_proxy = proxies.get('no_proxy') if proxies is not None else None + env_proxies = get_environ_proxies(url, no_proxy=no_proxy) + for (k, v) in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration and be compatible + # with cURL. + if verify is True or verify is None: + verify = (os.environ.get('REQUESTS_CA_BUNDLE') or + os.environ.get('CURL_CA_BUNDLE')) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {'verify': verify, 'proxies': proxies, 'stream': stream, + 'cert': cert} + + def get_adapter(self, url): + """ + Returns the appropriate connection adapter for the given URL. + + :rtype: requests.adapters.BaseAdapter + """ + for (prefix, adapter) in self.adapters.items(): + + if url.lower().startswith(prefix.lower()): + return adapter + + # Nothing matches :-/ + raise InvalidSchema("No connection adapters were found for {!r}".format(url)) + + def close(self): + """Closes all adapters and as such the session""" + for v in self.adapters.values(): + v.close() + + def mount(self, prefix, adapter): + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by prefix length. + """ + self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) + + def __getstate__(self): + state = {attr: getattr(self, attr, None) for attr in self.__attrs__} + return state + + def __setstate__(self, state): + for attr, value in state.items(): + setattr(self, attr, value) + + +def session(): + """ + Returns a :class:`Session` for context-management. + + .. deprecated:: 1.0.0 + + This method has been deprecated since version 1.0.0 and is only kept for + backwards compatibility. New code should use :class:`~requests.sessions.Session` + to create a session. This may be removed at a future date. + + :rtype: Session + """ + return Session() diff --git a/source/others_libraries/pythonpath/requests/status_codes.py b/source/others_libraries/pythonpath/requests/status_codes.py new file mode 100644 index 0000000..d80a7cd --- /dev/null +++ b/source/others_libraries/pythonpath/requests/status_codes.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- + +r""" +The ``codes`` object defines a mapping from common names for HTTP statuses +to their numerical codes, accessible either as attributes or as dictionary +items. + +Example:: + + >>> import requests + >>> requests.codes['temporary_redirect'] + 307 + >>> requests.codes.teapot + 418 + >>> requests.codes['\o/'] + 200 + +Some codes have multiple names, and both upper- and lower-case versions of +the names are allowed. For example, ``codes.ok``, ``codes.OK``, and +``codes.okay`` all correspond to the HTTP status code 200. +""" + +from .structures import LookupDict + +_codes = { + + # Informational. + 100: ('continue',), + 101: ('switching_protocols',), + 102: ('processing',), + 103: ('checkpoint',), + 122: ('uri_too_long', 'request_uri_too_long'), + 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'), + 201: ('created',), + 202: ('accepted',), + 203: ('non_authoritative_info', 'non_authoritative_information'), + 204: ('no_content',), + 205: ('reset_content', 'reset'), + 206: ('partial_content', 'partial'), + 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'), + 208: ('already_reported',), + 226: ('im_used',), + + # Redirection. + 300: ('multiple_choices',), + 301: ('moved_permanently', 'moved', '\\o-'), + 302: ('found',), + 303: ('see_other', 'other'), + 304: ('not_modified',), + 305: ('use_proxy',), + 306: ('switch_proxy',), + 307: ('temporary_redirect', 'temporary_moved', 'temporary'), + 308: ('permanent_redirect', + 'resume_incomplete', 'resume',), # These 2 to be removed in 3.0 + + # Client Error. + 400: ('bad_request', 'bad'), + 401: ('unauthorized',), + 402: ('payment_required', 'payment'), + 403: ('forbidden',), + 404: ('not_found', '-o-'), + 405: ('method_not_allowed', 'not_allowed'), + 406: ('not_acceptable',), + 407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'), + 408: ('request_timeout', 'timeout'), + 409: ('conflict',), + 410: ('gone',), + 411: ('length_required',), + 412: ('precondition_failed', 'precondition'), + 413: ('request_entity_too_large',), + 414: ('request_uri_too_large',), + 415: ('unsupported_media_type', 'unsupported_media', 'media_type'), + 416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'), + 417: ('expectation_failed',), + 418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'), + 421: ('misdirected_request',), + 422: ('unprocessable_entity', 'unprocessable'), + 423: ('locked',), + 424: ('failed_dependency', 'dependency'), + 425: ('unordered_collection', 'unordered'), + 426: ('upgrade_required', 'upgrade'), + 428: ('precondition_required', 'precondition'), + 429: ('too_many_requests', 'too_many'), + 431: ('header_fields_too_large', 'fields_too_large'), + 444: ('no_response', 'none'), + 449: ('retry_with', 'retry'), + 450: ('blocked_by_windows_parental_controls', 'parental_controls'), + 451: ('unavailable_for_legal_reasons', 'legal_reasons'), + 499: ('client_closed_request',), + + # Server Error. + 500: ('internal_server_error', 'server_error', '/o\\', '✗'), + 501: ('not_implemented',), + 502: ('bad_gateway',), + 503: ('service_unavailable', 'unavailable'), + 504: ('gateway_timeout',), + 505: ('http_version_not_supported', 'http_version'), + 506: ('variant_also_negotiates',), + 507: ('insufficient_storage',), + 509: ('bandwidth_limit_exceeded', 'bandwidth'), + 510: ('not_extended',), + 511: ('network_authentication_required', 'network_auth', 'network_authentication'), +} + +codes = LookupDict(name='status_codes') + +def _init(): + for code, titles in _codes.items(): + for title in titles: + setattr(codes, title, code) + if not title.startswith(('\\', '/')): + setattr(codes, title.upper(), code) + + def doc(code): + names = ', '.join('``%s``' % n for n in _codes[code]) + return '* %d: %s' % (code, names) + + global __doc__ + __doc__ = (__doc__ + '\n' + + '\n'.join(doc(code) for code in sorted(_codes)) + if __doc__ is not None else None) + +_init() diff --git a/source/others_libraries/pythonpath/requests/structures.py b/source/others_libraries/pythonpath/requests/structures.py new file mode 100644 index 0000000..8ee0ba7 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/structures.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ + +Data structures that power Requests. +""" + +from collections import OrderedDict + +from .compat import Mapping, MutableMapping + + +class CaseInsensitiveDict(MutableMapping): + """A case-insensitive ``dict``-like object. + + Implements all methods and operations of + ``MutableMapping`` as well as dict's ``copy``. Also + provides ``lower_items``. + + All keys are expected to be strings. The structure remembers the + case of the last key to be set, and ``iter(instance)``, + ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` + will contain case-sensitive keys. However, querying and contains + testing is case insensitive:: + + cid = CaseInsensitiveDict() + cid['Accept'] = 'application/json' + cid['aCCEPT'] == 'application/json' # True + list(cid) == ['Accept'] # True + + For example, ``headers['content-encoding']`` will return the + value of a ``'Content-Encoding'`` response header, regardless + of how the header name was originally stored. + + If the constructor, ``.update``, or equality comparison + operations are given keys that have equal ``.lower()``s, the + behavior is undefined. + """ + + def __init__(self, data=None, **kwargs): + self._store = OrderedDict() + if data is None: + data = {} + self.update(data, **kwargs) + + def __setitem__(self, key, value): + # Use the lowercased key for lookups, but store the actual + # key alongside the value. + self._store[key.lower()] = (key, value) + + def __getitem__(self, key): + return self._store[key.lower()][1] + + def __delitem__(self, key): + del self._store[key.lower()] + + def __iter__(self): + return (casedkey for casedkey, mappedvalue in self._store.values()) + + def __len__(self): + return len(self._store) + + def lower_items(self): + """Like iteritems(), but with all lowercase keys.""" + return ( + (lowerkey, keyval[1]) + for (lowerkey, keyval) + in self._store.items() + ) + + def __eq__(self, other): + if isinstance(other, Mapping): + other = CaseInsensitiveDict(other) + else: + return NotImplemented + # Compare insensitively + return dict(self.lower_items()) == dict(other.lower_items()) + + # Copy is required + def copy(self): + return CaseInsensitiveDict(self._store.values()) + + def __repr__(self): + return str(dict(self.items())) + + +class LookupDict(dict): + """Dictionary lookup object.""" + + def __init__(self, name=None): + self.name = name + super(LookupDict, self).__init__() + + def __repr__(self): + return '' % (self.name) + + def __getitem__(self, key): + # We allow fall-through here, so values default to None + + return self.__dict__.get(key, None) + + def get(self, key, default=None): + return self.__dict__.get(key, default) diff --git a/source/others_libraries/pythonpath/requests/utils.py b/source/others_libraries/pythonpath/requests/utils.py new file mode 100644 index 0000000..dbb02a0 --- /dev/null +++ b/source/others_libraries/pythonpath/requests/utils.py @@ -0,0 +1,1013 @@ +# -*- coding: utf-8 -*- + +""" +requests.utils +~~~~~~~~~~~~~~ + +This module provides utility functions that are used within Requests +that are also useful for external consumption. +""" + +import codecs +import contextlib +import io +import os +import re +import socket +import struct +import sys +import tempfile +import warnings +import zipfile +from collections import OrderedDict +from urllib3.util import make_headers + +from .__version__ import __version__ +from . import certs +# to_native_string is unused here, but imported here for backwards compatibility +from ._internal_utils import to_native_string +from .compat import parse_http_list as _parse_list_header +from .compat import ( + quote, urlparse, bytes, str, unquote, getproxies, + proxy_bypass, urlunparse, basestring, integer_types, is_py3, + proxy_bypass_environment, getproxies_environment, Mapping) +from .cookies import cookiejar_from_dict +from .structures import CaseInsensitiveDict +from .exceptions import ( + InvalidURL, InvalidHeader, FileModeWarning, UnrewindableBodyError) + +NETRC_FILES = ('.netrc', '_netrc') + +DEFAULT_CA_BUNDLE_PATH = certs.where() + +DEFAULT_PORTS = {'http': 80, 'https': 443} + +# Ensure that ', ' is used to preserve previous delimiter behavior. +DEFAULT_ACCEPT_ENCODING = ", ".join( + re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"]) +) + + +if sys.platform == 'win32': + # provide a proxy_bypass version on Windows without DNS lookups + + def proxy_bypass_registry(host): + try: + if is_py3: + import winreg + else: + import _winreg as winreg + except ImportError: + return False + + try: + internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') + # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it + proxyEnable = int(winreg.QueryValueEx(internetSettings, + 'ProxyEnable')[0]) + # ProxyOverride is almost always a string + proxyOverride = winreg.QueryValueEx(internetSettings, + 'ProxyOverride')[0] + except OSError: + return False + if not proxyEnable or not proxyOverride: + return False + + # make a check value list from the registry entry: replace the + # '' string by the localhost entry and the corresponding + # canonical entry. + proxyOverride = proxyOverride.split(';') + # now check if we match one of the registry values. + for test in proxyOverride: + if test == '': + if '.' not in host: + return True + test = test.replace(".", r"\.") # mask dots + test = test.replace("*", r".*") # change glob sequence + test = test.replace("?", r".") # change glob char + if re.match(test, host, re.I): + return True + return False + + def proxy_bypass(host): # noqa + """Return True, if the host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or the registry. + """ + if getproxies_environment(): + return proxy_bypass_environment(host) + else: + return proxy_bypass_registry(host) + + +def dict_to_sequence(d): + """Returns an internal sequence dictionary update.""" + + if hasattr(d, 'items'): + d = d.items() + + return d + + +def super_len(o): + total_length = None + current_position = 0 + + if hasattr(o, '__len__'): + total_length = len(o) + + elif hasattr(o, 'len'): + total_length = o.len + + elif hasattr(o, 'fileno'): + try: + fileno = o.fileno() + except io.UnsupportedOperation: + pass + else: + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if 'b' not in o.mode: + warnings.warn(( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode."), + FileModeWarning + ) + + if hasattr(o, 'tell'): + try: + current_position = o.tell() + except (OSError, IOError): + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + if total_length is not None: + current_position = total_length + else: + if hasattr(o, 'seek') and total_length is None: + # StringIO and BytesIO have seek but no useable fileno + try: + # seek to end of file + o.seek(0, 2) + total_length = o.tell() + + # seek back to current position to support + # partially read file-like objects + o.seek(current_position or 0) + except (OSError, IOError): + total_length = 0 + + if total_length is None: + total_length = 0 + + return max(0, total_length - current_position) + + +def get_netrc_auth(url, raise_errors=False): + """Returns the Requests tuple auth for a given url from netrc.""" + + netrc_file = os.environ.get('NETRC') + if netrc_file is not None: + netrc_locations = (netrc_file,) + else: + netrc_locations = ('~/{}'.format(f) for f in NETRC_FILES) + + try: + from netrc import netrc, NetrcParseError + + netrc_path = None + + for f in netrc_locations: + try: + loc = os.path.expanduser(f) + except KeyError: + # os.path.expanduser can fail when $HOME is undefined and + # getpwuid fails. See https://bugs.python.org/issue20164 & + # https://github.com/psf/requests/issues/1846 + return + + if os.path.exists(loc): + netrc_path = loc + break + + # Abort early if there isn't one. + if netrc_path is None: + return + + ri = urlparse(url) + + # Strip port numbers from netloc. This weird `if...encode`` dance is + # used for Python 3.2, which doesn't support unicode literals. + splitstr = b':' + if isinstance(url, str): + splitstr = splitstr.decode('ascii') + host = ri.netloc.split(splitstr)[0] + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = (0 if _netrc[0] else 1) + return (_netrc[login_i], _netrc[2]) + except (NetrcParseError, IOError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise + + # App Engine hackiness. + except (ImportError, AttributeError): + pass + + +def guess_filename(obj): + """Tries to guess the filename of the given object.""" + name = getattr(obj, 'name', None) + if (name and isinstance(name, basestring) and name[0] != '<' and + name[-1] != '>'): + return os.path.basename(name) + + +def extract_zipped_paths(path): + """Replace nonexistent paths that look like they refer to a member of a zip + archive with the location of an extracted copy of the target, or else + just return the provided path unchanged. + """ + if os.path.exists(path): + # this is already a valid path, no need to do anything further + return path + + # find the first valid part of the provided path and treat that as a zip archive + # assume the rest of the path is the name of a member in the archive + archive, member = os.path.split(path) + while archive and not os.path.exists(archive): + archive, prefix = os.path.split(archive) + member = '/'.join([prefix, member]) + + if not zipfile.is_zipfile(archive): + return path + + zip_file = zipfile.ZipFile(archive) + if member not in zip_file.namelist(): + return path + + # we have a valid zip archive and a valid member of that archive + tmp = tempfile.gettempdir() + extracted_path = os.path.join(tmp, member.split('/')[-1]) + if not os.path.exists(extracted_path): + # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition + with atomic_open(extracted_path) as file_handler: + file_handler.write(zip_file.read(member)) + return extracted_path + + +@contextlib.contextmanager +def atomic_open(filename): + """Write a file to the disk in an atomic fashion""" + replacer = os.rename if sys.version_info[0] == 2 else os.replace + tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename)) + try: + with os.fdopen(tmp_descriptor, 'wb') as tmp_handler: + yield tmp_handler + replacer(tmp_name, filename) + except BaseException: + os.remove(tmp_name) + raise + + +def from_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + + :rtype: OrderedDict + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + return OrderedDict(value) + + +def to_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + Traceback (most recent call last): + ... + ValueError: cannot encode objects that are not 2-tuples + + :rtype: list + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + if isinstance(value, Mapping): + value = value.items() + + return list(value) + + +# From mitsuhiko/werkzeug (used with permission). +def parse_list_header(value): + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + :rtype: list + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +# From mitsuhiko/werkzeug (used with permission). +def parse_dict_header(value): + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict: + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + :param value: a string with a dict header. + :return: :class:`dict` + :rtype: dict + """ + result = {} + for item in _parse_list_header(value): + if '=' not in item: + result[item] = None + continue + name, value = item.split('=', 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +# From mitsuhiko/werkzeug (used with permission). +def unquote_header_value(value, is_filename=False): + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + :param value: the header value to unquote. + :rtype: str + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != '\\\\': + return value.replace('\\\\', '\\').replace('\\"', '"') + return value + + +def dict_from_cookiejar(cj): + """Returns a key/value dictionary from a CookieJar. + + :param cj: CookieJar object to extract cookies from. + :rtype: dict + """ + + cookie_dict = {} + + for cookie in cj: + cookie_dict[cookie.name] = cookie.value + + return cookie_dict + + +def add_dict_to_cookiejar(cj, cookie_dict): + """Returns a CookieJar from a key/value dictionary. + + :param cj: CookieJar to insert cookies into. + :param cookie_dict: Dict of key/values to insert into CookieJar. + :rtype: CookieJar + """ + + return cookiejar_from_dict(cookie_dict, cj) + + +def get_encodings_from_content(content): + """Returns encodings from given content string. + + :param content: bytestring to extract encodings from. + """ + warnings.warn(( + 'In requests 3.0, get_encodings_from_content will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + charset_re = re.compile(r']', flags=re.I) + pragma_re = re.compile(r']', flags=re.I) + xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') + + return (charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content)) + + +def _parse_content_type_header(header): + """Returns content type and parameters from given header + + :param header: string + :return: tuple containing content type and dictionary of + parameters + """ + + tokens = header.split(';') + content_type, params = tokens[0].strip(), tokens[1:] + params_dict = {} + items_to_strip = "\"' " + + for param in params: + param = param.strip() + if param: + key, value = param, True + index_of_equals = param.find("=") + if index_of_equals != -1: + key = param[:index_of_equals].strip(items_to_strip) + value = param[index_of_equals + 1:].strip(items_to_strip) + params_dict[key.lower()] = value + return content_type, params_dict + + +def get_encoding_from_headers(headers): + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :rtype: str + """ + + content_type = headers.get('content-type') + + if not content_type: + return None + + content_type, params = _parse_content_type_header(content_type) + + if 'charset' in params: + return params['charset'].strip("'\"") + + if 'text' in content_type: + return 'ISO-8859-1' + + if 'application/json' in content_type: + # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset + return 'utf-8' + + +def stream_decode_response_unicode(iterator, r): + """Stream decodes a iterator.""" + + if r.encoding is None: + for item in iterator: + yield item + return + + decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace') + for chunk in iterator: + rv = decoder.decode(chunk) + if rv: + yield rv + rv = decoder.decode(b'', final=True) + if rv: + yield rv + + +def iter_slices(string, slice_length): + """Iterate over slices of a string.""" + pos = 0 + if slice_length is None or slice_length <= 0: + slice_length = len(string) + while pos < len(string): + yield string[pos:pos + slice_length] + pos += slice_length + + +def get_unicode_from_response(r): + """Returns the requested content back in unicode. + + :param r: Response object to get unicode content from. + + Tried: + + 1. charset from content-type + 2. fall back and replace all unicode characters + + :rtype: str + """ + warnings.warn(( + 'In requests 3.0, get_unicode_from_response will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + tried_encodings = [] + + # Try charset from content-type + encoding = get_encoding_from_headers(r.headers) + + if encoding: + try: + return str(r.content, encoding) + except UnicodeError: + tried_encodings.append(encoding) + + # Fall back: + try: + return str(r.content, encoding, errors='replace') + except TypeError: + return r.content + + +# The unreserved URI characters (RFC 3986) +UNRESERVED_SET = frozenset( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~") + + +def unquote_unreserved(uri): + """Un-escape any percent-escape sequences in a URI that are unreserved + characters. This leaves all reserved, illegal and non-ASCII bytes encoded. + + :rtype: str + """ + parts = uri.split('%') + for i in range(1, len(parts)): + h = parts[i][0:2] + if len(h) == 2 and h.isalnum(): + try: + c = chr(int(h, 16)) + except ValueError: + raise InvalidURL("Invalid percent-escape sequence: '%s'" % h) + + if c in UNRESERVED_SET: + parts[i] = c + parts[i][2:] + else: + parts[i] = '%' + parts[i] + else: + parts[i] = '%' + parts[i] + return ''.join(parts) + + +def requote_uri(uri): + """Re-quote the given URI. + + This function passes the given URI through an unquote/quote cycle to + ensure that it is fully and consistently quoted. + + :rtype: str + """ + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) + + +def address_in_network(ip, net): + """This function allows you to check if an IP belongs to a network subnet + + Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 + returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 + + :rtype: bool + """ + ipaddr = struct.unpack('=L', socket.inet_aton(ip))[0] + netaddr, bits = net.split('/') + netmask = struct.unpack('=L', socket.inet_aton(dotted_netmask(int(bits))))[0] + network = struct.unpack('=L', socket.inet_aton(netaddr))[0] & netmask + return (ipaddr & netmask) == (network & netmask) + + +def dotted_netmask(mask): + """Converts mask from /xx format to xxx.xxx.xxx.xxx + + Example: if mask is 24 function returns 255.255.255.0 + + :rtype: str + """ + bits = 0xffffffff ^ (1 << 32 - mask) - 1 + return socket.inet_ntoa(struct.pack('>I', bits)) + + +def is_ipv4_address(string_ip): + """ + :rtype: bool + """ + try: + socket.inet_aton(string_ip) + except socket.error: + return False + return True + + +def is_valid_cidr(string_network): + """ + Very simple check of the cidr format in no_proxy variable. + + :rtype: bool + """ + if string_network.count('/') == 1: + try: + mask = int(string_network.split('/')[1]) + except ValueError: + return False + + if mask < 1 or mask > 32: + return False + + try: + socket.inet_aton(string_network.split('/')[0]) + except socket.error: + return False + else: + return False + return True + + +@contextlib.contextmanager +def set_environ(env_name, value): + """Set the environment variable 'env_name' to 'value' + + Save previous value, yield, and then restore the previous value stored in + the environment variable 'env_name'. + + If 'value' is None, do nothing""" + value_changed = value is not None + if value_changed: + old_value = os.environ.get(env_name) + os.environ[env_name] = value + try: + yield + finally: + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value + + +def should_bypass_proxies(url, no_proxy): + """ + Returns whether we should bypass proxies or not. + + :rtype: bool + """ + # Prioritize lowercase environment variables over uppercase + # to keep a consistent behaviour with other http projects (curl, wget). + get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper()) + + # First check whether no_proxy is defined. If it is, check that the URL + # we're getting isn't in the no_proxy list. + no_proxy_arg = no_proxy + if no_proxy is None: + no_proxy = get_proxy('no_proxy') + parsed = urlparse(url) + + if parsed.hostname is None: + # URLs don't always have hostnames, e.g. file:/// urls. + return True + + if no_proxy: + # We need to check whether we match here. We need to see if we match + # the end of the hostname, both with and without the port. + no_proxy = ( + host for host in no_proxy.replace(' ', '').split(',') if host + ) + + if is_ipv4_address(parsed.hostname): + for proxy_ip in no_proxy: + if is_valid_cidr(proxy_ip): + if address_in_network(parsed.hostname, proxy_ip): + return True + elif parsed.hostname == proxy_ip: + # If no_proxy ip was defined in plain IP notation instead of cidr notation & + # matches the IP of the index + return True + else: + host_with_port = parsed.hostname + if parsed.port: + host_with_port += ':{}'.format(parsed.port) + + for host in no_proxy: + if parsed.hostname.endswith(host) or host_with_port.endswith(host): + # The URL does match something in no_proxy, so we don't want + # to apply the proxies on this URL. + return True + + with set_environ('no_proxy', no_proxy_arg): + # parsed.hostname can be `None` in cases such as a file URI. + try: + bypass = proxy_bypass(parsed.hostname) + except (TypeError, socket.gaierror): + bypass = False + + if bypass: + return True + + return False + + +def get_environ_proxies(url, no_proxy=None): + """ + Return a dict of environment proxies. + + :rtype: dict + """ + if should_bypass_proxies(url, no_proxy=no_proxy): + return {} + else: + return getproxies() + + +def select_proxy(url, proxies): + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + return proxies.get(urlparts.scheme, proxies.get('all')) + + proxy_keys = [ + urlparts.scheme + '://' + urlparts.hostname, + urlparts.scheme, + 'all://' + urlparts.hostname, + 'all', + ] + proxy = None + for proxy_key in proxy_keys: + if proxy_key in proxies: + proxy = proxies[proxy_key] + break + + return proxy + + +def default_user_agent(name="python-requests"): + """ + Return a string representing the default user agent. + + :rtype: str + """ + return '%s/%s' % (name, __version__) + + +def default_headers(): + """ + :rtype: requests.structures.CaseInsensitiveDict + """ + return CaseInsensitiveDict({ + 'User-Agent': default_user_agent(), + 'Accept-Encoding': DEFAULT_ACCEPT_ENCODING, + 'Accept': '*/*', + 'Connection': 'keep-alive', + }) + + +def parse_header_links(value): + """Return a list of parsed link headers proxies. + + i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" + + :rtype: list + """ + + links = [] + + replace_chars = ' \'"' + + value = value.strip(replace_chars) + if not value: + return links + + for val in re.split(', *<', value): + try: + url, params = val.split(';', 1) + except ValueError: + url, params = val, '' + + link = {'url': url.strip('<> \'"')} + + for param in params.split(';'): + try: + key, value = param.split('=') + except ValueError: + break + + link[key.strip(replace_chars)] = value.strip(replace_chars) + + links.append(link) + + return links + + +# Null bytes; no need to recreate these on each call to guess_json_utf +_null = '\x00'.encode('ascii') # encoding to ASCII for Python 3 +_null2 = _null * 2 +_null3 = _null * 3 + + +def guess_json_utf(data): + """ + :rtype: str + """ + # JSON always starts with two ASCII characters, so detection is as + # easy as counting the nulls and from their location and count + # determine the encoding. Also detect a BOM, if present. + sample = data[:4] + if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): + return 'utf-32' # BOM included + if sample[:3] == codecs.BOM_UTF8: + return 'utf-8-sig' # BOM included, MS style (discouraged) + if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): + return 'utf-16' # BOM included + nullcount = sample.count(_null) + if nullcount == 0: + return 'utf-8' + if nullcount == 2: + if sample[::2] == _null2: # 1st and 3rd are null + return 'utf-16-be' + if sample[1::2] == _null2: # 2nd and 4th are null + return 'utf-16-le' + # Did not detect 2 valid UTF-16 ascii-range characters + if nullcount == 3: + if sample[:3] == _null3: + return 'utf-32-be' + if sample[1:] == _null3: + return 'utf-32-le' + # Did not detect a valid UTF-32 ascii-range character + return None + + +def prepend_scheme_if_needed(url, new_scheme): + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme) + + # urlparse is a finicky beast, and sometimes decides that there isn't a + # netloc present. Assume that it's being over-cautious, and switch netloc + # and path if urlparse decided there was no netloc. + if not netloc: + netloc, path = path, netloc + + return urlunparse((scheme, netloc, path, params, query, fragment)) + + +def get_auth_from_url(url): + """Given a url with authentication components, extract them into a tuple of + username,password. + + :rtype: (str,str) + """ + parsed = urlparse(url) + + try: + auth = (unquote(parsed.username), unquote(parsed.password)) + except (AttributeError, TypeError): + auth = ('', '') + + return auth + + +# Moved outside of function to avoid recompile every call +_CLEAN_HEADER_REGEX_BYTE = re.compile(b'^\\S[^\\r\\n]*$|^$') +_CLEAN_HEADER_REGEX_STR = re.compile(r'^\S[^\r\n]*$|^$') + + +def check_header_validity(header): + """Verifies that header value is a string which doesn't contain + leading whitespace or return characters. This prevents unintended + header injection. + + :param header: tuple, in the format (name, value). + """ + name, value = header + + if isinstance(value, bytes): + pat = _CLEAN_HEADER_REGEX_BYTE + else: + pat = _CLEAN_HEADER_REGEX_STR + try: + if not pat.match(value): + raise InvalidHeader("Invalid return character or leading space in header: %s" % name) + except TypeError: + raise InvalidHeader("Value for header {%s: %s} must be of type str or " + "bytes, not %s" % (name, value, type(value))) + + +def urldefragauth(url): + """ + Given a url remove the fragment and the authentication part. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit('@', 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, '')) + + +def rewind_body(prepared_request): + """Move file pointer back to its recorded starting position + so it can be read again on redirect. + """ + body_seek = getattr(prepared_request.body, 'seek', None) + if body_seek is not None and isinstance(prepared_request._body_position, integer_types): + try: + body_seek(prepared_request._body_position) + except (IOError, OSError): + raise UnrewindableBodyError("An error occurred when rewinding request " + "body for redirect.") + else: + raise UnrewindableBodyError("Unable to rewind request body for redirect.")