mirror of
https://github.com/kennethreitz/pipenv.git
synced 2026-06-05 22:50:18 +00:00
Update requirementslib, requests and vistir
Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
Vendored
+6
-11
@@ -22,7 +22,7 @@ usage:
|
||||
... or POST:
|
||||
|
||||
>>> payload = dict(key1='value1', key2='value2')
|
||||
>>> r = requests.post('http://httpbin.org/post', data=payload)
|
||||
>>> r = requests.post('https://httpbin.org/post', data=payload)
|
||||
>>> print(r.text)
|
||||
{
|
||||
...
|
||||
@@ -57,10 +57,10 @@ def check_compatibility(urllib3_version, chardet_version):
|
||||
# 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.23
|
||||
# urllib3 >= 1.21.1, <= 1.24
|
||||
assert major == 1
|
||||
assert minor >= 21
|
||||
assert minor <= 23
|
||||
assert minor <= 24
|
||||
|
||||
# Check chardet for compatibility.
|
||||
major, minor, patch = chardet_version.split('.')[:3]
|
||||
@@ -79,14 +79,14 @@ def _check_cryptography(cryptography_version):
|
||||
return
|
||||
|
||||
if cryptography_version < [1, 3, 4]:
|
||||
warning = 'Old version of cryptography ({0}) may cause slowdown.'.format(cryptography_version)
|
||||
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__)
|
||||
except (AssertionError, ValueError):
|
||||
warnings.warn("urllib3 ({0}) or chardet ({1}) doesn't match a supported "
|
||||
warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported "
|
||||
"version!".format(urllib3.__version__, chardet.__version__),
|
||||
RequestsDependencyWarning)
|
||||
|
||||
@@ -123,12 +123,7 @@ from .exceptions import (
|
||||
|
||||
# Set default logging handler to avoid "No handler found" warnings.
|
||||
import logging
|
||||
try: # Python 2.7+
|
||||
from logging import NullHandler
|
||||
except ImportError:
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
from logging import NullHandler
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
__title__ = 'requests'
|
||||
__description__ = 'Python HTTP for Humans.'
|
||||
__url__ = 'http://python-requests.org'
|
||||
__version__ = '2.19.1'
|
||||
__build__ = 0x021901
|
||||
__version__ = '2.20.0'
|
||||
__build__ = 0x022000
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__author_email__ = 'me@kennethreitz.org'
|
||||
__license__ = 'Apache 2.0'
|
||||
|
||||
Vendored
+15
-12
@@ -26,6 +26,7 @@ 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
|
||||
@@ -35,7 +36,8 @@ from .utils import (DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths,
|
||||
from .structures import CaseInsensitiveDict
|
||||
from .cookies import extract_cookies_to_jar
|
||||
from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError,
|
||||
ProxyError, RetryError, InvalidSchema, InvalidProxyURL)
|
||||
ProxyError, RetryError, InvalidSchema, InvalidProxyURL,
|
||||
InvalidURL)
|
||||
from .auth import _basic_auth_str
|
||||
|
||||
try:
|
||||
@@ -127,8 +129,7 @@ class HTTPAdapter(BaseAdapter):
|
||||
self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
|
||||
|
||||
def __getstate__(self):
|
||||
return dict((attr, getattr(self, attr, None)) for attr in
|
||||
self.__attrs__)
|
||||
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
|
||||
@@ -224,7 +225,7 @@ class HTTPAdapter(BaseAdapter):
|
||||
|
||||
if not cert_loc or not os.path.exists(cert_loc):
|
||||
raise IOError("Could not find a suitable TLS CA certificate bundle, "
|
||||
"invalid path: {0}".format(cert_loc))
|
||||
"invalid path: {}".format(cert_loc))
|
||||
|
||||
conn.cert_reqs = 'CERT_REQUIRED'
|
||||
|
||||
@@ -246,10 +247,10 @@ class HTTPAdapter(BaseAdapter):
|
||||
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: {0}".format(conn.cert_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: {0}".format(conn.key_file))
|
||||
"invalid path: {}".format(conn.key_file))
|
||||
|
||||
def build_response(self, req, resp):
|
||||
"""Builds a :class:`Response <requests.Response>` object from a urllib3
|
||||
@@ -378,7 +379,7 @@ class HTTPAdapter(BaseAdapter):
|
||||
when subclassing the
|
||||
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
||||
|
||||
:param proxies: The url of the proxy being used for this request.
|
||||
:param proxy: The url of the proxy being used for this request.
|
||||
:rtype: dict
|
||||
"""
|
||||
headers = {}
|
||||
@@ -407,7 +408,10 @@ class HTTPAdapter(BaseAdapter):
|
||||
:rtype: requests.Response
|
||||
"""
|
||||
|
||||
conn = self.get_connection(request.url, proxies)
|
||||
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)
|
||||
@@ -421,7 +425,7 @@ class HTTPAdapter(BaseAdapter):
|
||||
timeout = TimeoutSauce(connect=connect, read=read)
|
||||
except ValueError as e:
|
||||
# this may raise a string formatting error.
|
||||
err = ("Invalid timeout {0}. Pass a (connect, read) "
|
||||
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)
|
||||
@@ -471,11 +475,10 @@ class HTTPAdapter(BaseAdapter):
|
||||
|
||||
# Receive the response from the server
|
||||
try:
|
||||
# For Python 2.7+ versions, use buffering of HTTP
|
||||
# responses
|
||||
# For Python 2.7, use buffering of HTTP responses
|
||||
r = low_conn.getresponse(buffering=True)
|
||||
except TypeError:
|
||||
# For compatibility with Python 2.6 versions and back
|
||||
# For compatibility with Python 3.3+
|
||||
r = low_conn.getresponse()
|
||||
|
||||
resp = HTTPResponse.from_httplib(
|
||||
|
||||
Vendored
+13
-7
@@ -18,8 +18,10 @@ def request(method, url, **kwargs):
|
||||
|
||||
: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 or list of tuples ``[(key, value)]`` (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`.
|
||||
:param params: (optional) Dictionary, list of tuples or bytes to send
|
||||
in the body of 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`.
|
||||
@@ -47,7 +49,7 @@ def request(method, url, **kwargs):
|
||||
Usage::
|
||||
|
||||
>>> import requests
|
||||
>>> req = requests.request('GET', 'http://httpbin.org/get')
|
||||
>>> req = requests.request('GET', 'https://httpbin.org/get')
|
||||
<Response [200]>
|
||||
"""
|
||||
|
||||
@@ -62,7 +64,8 @@ def get(url, params=None, **kwargs):
|
||||
r"""Sends a GET request.
|
||||
|
||||
: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 params: (optional) Dictionary, list of tuples or bytes to send
|
||||
in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
:rtype: requests.Response
|
||||
@@ -102,7 +105,8 @@ 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 (will be form-encoded), bytes, or file-like object to send in the body of 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 data to send in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
@@ -116,7 +120,8 @@ def put(url, data=None, **kwargs):
|
||||
r"""Sends a PUT request.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary (will be form-encoded), bytes, or file-like object to send in the body of 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 data to send in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
@@ -130,7 +135,8 @@ def patch(url, data=None, **kwargs):
|
||||
r"""Sends a PATCH request.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary (will be form-encoded), bytes, or file-like object to send in the body of 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 data to send in the body of the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:return: :class:`Response <Response>` object
|
||||
|
||||
Vendored
+2
-2
@@ -38,7 +38,7 @@ def _basic_auth_str(username, password):
|
||||
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 ({0!r}) to "
|
||||
"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,
|
||||
@@ -48,7 +48,7 @@ def _basic_auth_str(username, password):
|
||||
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 ({0!r}) to "
|
||||
"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(password),
|
||||
category=DeprecationWarning,
|
||||
|
||||
Vendored
+1
-2
@@ -43,9 +43,8 @@ if is_py2:
|
||||
import cookielib
|
||||
from Cookie import Morsel
|
||||
from StringIO import StringIO
|
||||
from collections import Callable, Mapping, MutableMapping
|
||||
from collections import Callable, Mapping, MutableMapping, OrderedDict
|
||||
|
||||
from urllib3.packages.ordered_dict import OrderedDict
|
||||
|
||||
builtin_str = str
|
||||
bytes = str
|
||||
|
||||
Vendored
+17
-14
@@ -444,20 +444,21 @@ def create_cookie(name, value, **kwargs):
|
||||
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 = dict(
|
||||
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,)
|
||||
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:
|
||||
@@ -511,6 +512,7 @@ def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
|
||||
: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()
|
||||
@@ -529,6 +531,7 @@ def merge_cookies(cookiejar, cookies):
|
||||
|
||||
: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')
|
||||
|
||||
Vendored
+1
-2
@@ -89,8 +89,7 @@ def info():
|
||||
'version': getattr(idna, '__version__', ''),
|
||||
}
|
||||
|
||||
# OPENSSL_VERSION_NUMBER doesn't exist in the Python 2.6 ssl module.
|
||||
system_ssl = getattr(ssl, 'OPENSSL_VERSION_NUMBER', None)
|
||||
system_ssl = ssl.OPENSSL_VERSION_NUMBER
|
||||
system_ssl_info = {
|
||||
'version': '%x' % system_ssl if system_ssl is not None else ''
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -15,14 +15,14 @@ HOOKS = ['response']
|
||||
|
||||
|
||||
def default_hooks():
|
||||
return dict((event, []) for event in 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 dict()
|
||||
hooks = hooks or {}
|
||||
hooks = hooks.get(key)
|
||||
if hooks:
|
||||
if hasattr(hooks, '__call__'):
|
||||
|
||||
Vendored
+9
-8
@@ -204,9 +204,13 @@ class Request(RequestHooksMixin):
|
||||
: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 is provided, form-encoding will take place.
|
||||
: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: dictionary of URL parameters to append to the URL.
|
||||
: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.
|
||||
@@ -214,7 +218,7 @@ class Request(RequestHooksMixin):
|
||||
Usage::
|
||||
|
||||
>>> import requests
|
||||
>>> req = requests.Request('GET', 'http://httpbin.org/get')
|
||||
>>> req = requests.Request('GET', 'https://httpbin.org/get')
|
||||
>>> req.prepare()
|
||||
<PreparedRequest [GET]>
|
||||
"""
|
||||
@@ -274,7 +278,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
|
||||
Usage::
|
||||
|
||||
>>> import requests
|
||||
>>> req = requests.Request('GET', 'http://httpbin.org/get')
|
||||
>>> req = requests.Request('GET', 'https://httpbin.org/get')
|
||||
>>> r = req.prepare()
|
||||
<PreparedRequest [GET]>
|
||||
|
||||
@@ -648,10 +652,7 @@ class Response(object):
|
||||
if not self._content_consumed:
|
||||
self.content
|
||||
|
||||
return dict(
|
||||
(attr, getattr(self, attr, None))
|
||||
for attr in self.__attrs__
|
||||
)
|
||||
return {attr: getattr(self, attr, None) for attr in self.__attrs__}
|
||||
|
||||
def __setstate__(self, state):
|
||||
for name, value in state.items():
|
||||
|
||||
Vendored
+36
-16
@@ -115,6 +115,22 @@ class SessionRedirectMixin(object):
|
||||
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
|
||||
# Standard case: root URI must match
|
||||
return old_parsed.port != new_parsed.port or old_parsed.scheme != new_parsed.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."""
|
||||
@@ -236,14 +252,10 @@ class SessionRedirectMixin(object):
|
||||
headers = prepared_request.headers
|
||||
url = prepared_request.url
|
||||
|
||||
if 'Authorization' in headers:
|
||||
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.
|
||||
original_parsed = urlparse(response.request.url)
|
||||
redirect_parsed = urlparse(url)
|
||||
|
||||
if (original_parsed.hostname != redirect_parsed.hostname):
|
||||
del headers['Authorization']
|
||||
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
|
||||
@@ -299,7 +311,7 @@ class SessionRedirectMixin(object):
|
||||
"""
|
||||
method = prepared_request.method
|
||||
|
||||
# http://tools.ietf.org/html/rfc7231#section-6.4.4
|
||||
# https://tools.ietf.org/html/rfc7231#section-6.4.4
|
||||
if response.status_code == codes.see_other and method != 'HEAD':
|
||||
method = 'GET'
|
||||
|
||||
@@ -325,13 +337,13 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
>>> import requests
|
||||
>>> s = requests.Session()
|
||||
>>> s.get('http://httpbin.org/get')
|
||||
>>> s.get('https://httpbin.org/get')
|
||||
<Response [200]>
|
||||
|
||||
Or as a context manager::
|
||||
|
||||
>>> with requests.Session() as s:
|
||||
>>> s.get('http://httpbin.org/get')
|
||||
>>> s.get('https://httpbin.org/get')
|
||||
<Response [200]>
|
||||
"""
|
||||
|
||||
@@ -453,8 +465,8 @@ class Session(SessionRedirectMixin):
|
||||
: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, bytes, or file-like object to send
|
||||
in the body of 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
|
||||
@@ -550,7 +562,8 @@ class Session(SessionRedirectMixin):
|
||||
r"""Sends a POST request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of 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 \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:rtype: requests.Response
|
||||
@@ -562,7 +575,8 @@ class Session(SessionRedirectMixin):
|
||||
r"""Sends a PUT request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of 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 \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:rtype: requests.Response
|
||||
"""
|
||||
@@ -573,7 +587,8 @@ class Session(SessionRedirectMixin):
|
||||
r"""Sends a PATCH request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of 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 \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:rtype: requests.Response
|
||||
"""
|
||||
@@ -723,7 +738,7 @@ class Session(SessionRedirectMixin):
|
||||
self.adapters[key] = self.adapters.pop(key)
|
||||
|
||||
def __getstate__(self):
|
||||
state = dict((attr, getattr(self, attr, None)) for attr in self.__attrs__)
|
||||
state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
@@ -735,7 +750,12 @@ 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()
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# -*- 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.
|
||||
|
||||
Vendored
+9
-10
@@ -173,10 +173,10 @@ def get_netrc_auth(url, raise_errors=False):
|
||||
|
||||
for f in NETRC_FILES:
|
||||
try:
|
||||
loc = os.path.expanduser('~/{0}'.format(f))
|
||||
loc = os.path.expanduser('~/{}'.format(f))
|
||||
except KeyError:
|
||||
# os.path.expanduser can fail when $HOME is undefined and
|
||||
# getpwuid fails. See http://bugs.python.org/issue20164 &
|
||||
# getpwuid fails. See https://bugs.python.org/issue20164 &
|
||||
# https://github.com/requests/requests/issues/1846
|
||||
return
|
||||
|
||||
@@ -466,7 +466,7 @@ def _parse_content_type_header(header):
|
||||
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] = value
|
||||
params_dict[key.lower()] = value
|
||||
return content_type, params_dict
|
||||
|
||||
|
||||
@@ -706,6 +706,10 @@ def should_bypass_proxies(url, no_proxy):
|
||||
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.
|
||||
@@ -725,7 +729,7 @@ def should_bypass_proxies(url, no_proxy):
|
||||
else:
|
||||
host_with_port = parsed.hostname
|
||||
if parsed.port:
|
||||
host_with_port += ':{0}'.format(parsed.port)
|
||||
host_with_port += ':{}'.format(parsed.port)
|
||||
|
||||
for host in no_proxy:
|
||||
if parsed.hostname.endswith(host) or host_with_port.endswith(host):
|
||||
@@ -733,13 +737,8 @@ def should_bypass_proxies(url, no_proxy):
|
||||
# to apply the proxies on this URL.
|
||||
return True
|
||||
|
||||
# If the system proxy settings indicate that this URL should be bypassed,
|
||||
# don't proxy.
|
||||
# The proxy_bypass function is incredibly buggy on OS X in early versions
|
||||
# of Python 2.6, so allow this call to fail. Only catch the specific
|
||||
# exceptions we've seen, though: this call failing in other ways can reveal
|
||||
# legitimate problems.
|
||||
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):
|
||||
|
||||
+6
-5
@@ -17,9 +17,10 @@ from pip_shims import (
|
||||
FormatControl, InstallRequirement, PackageFinder, RequirementPreparer,
|
||||
RequirementSet, RequirementTracker, Resolver, WheelCache, pip_version
|
||||
)
|
||||
from vistir.compat import JSONDecodeError, TemporaryDirectory, fs_str
|
||||
from vistir.compat import JSONDecodeError, fs_str
|
||||
from vistir.contextmanagers import cd, temp_environ
|
||||
from vistir.misc import partialclass
|
||||
from vistir.path import create_tracked_tempdir
|
||||
|
||||
from ..utils import get_pip_command, prepare_pip_source_args, _ensure_dir
|
||||
from .cache import CACHE_DIR, DependencyCache
|
||||
@@ -580,12 +581,12 @@ def start_resolver(finder=None, wheel_cache=None):
|
||||
download_dir = PKGS_DOWNLOAD_DIR
|
||||
_ensure_dir(download_dir)
|
||||
|
||||
_build_dir = TemporaryDirectory(fs_str("build"))
|
||||
_source_dir = TemporaryDirectory(fs_str("source"))
|
||||
_build_dir = create_tracked_tempdir(fs_str("build"))
|
||||
_source_dir = create_tracked_tempdir(fs_str("source"))
|
||||
preparer = partialclass(
|
||||
RequirementPreparer,
|
||||
build_dir=_build_dir.name,
|
||||
src_dir=_source_dir.name,
|
||||
build_dir=_build_dir,
|
||||
src_dir=_source_dir,
|
||||
download_dir=download_dir,
|
||||
wheel_download_dir=WHEEL_DOWNLOAD_DIR,
|
||||
progress_bar="off",
|
||||
|
||||
+40
-9
@@ -14,6 +14,7 @@ from .project import ProjectFile
|
||||
from .requirements import Requirement
|
||||
|
||||
from .utils import optional_instance_of
|
||||
from ..utils import is_vcs, is_editable, merge_items
|
||||
|
||||
DEFAULT_NEWLINES = u"\n"
|
||||
|
||||
@@ -49,6 +50,27 @@ class Lockfile(object):
|
||||
def _get_lockfile(self):
|
||||
return self.projectfile.lockfile
|
||||
|
||||
def __getitem__(self, k, *args, **kwargs):
|
||||
retval = None
|
||||
lockfile = self._lockfile
|
||||
section = None
|
||||
pkg_type = None
|
||||
try:
|
||||
retval = lockfile[k]
|
||||
except KeyError:
|
||||
if "-" in k:
|
||||
section, _, pkg_type = k.rpartition("-")
|
||||
vals = getattr(lockfile.get(section, {}), "_data", {})
|
||||
if pkg_type == "vcs":
|
||||
retval = {k: v for k, v in vals.items() if is_vcs(v)}
|
||||
elif pkg_type == "editable":
|
||||
retval = {k: v for k, v in vals.items() if is_editable(v)}
|
||||
if retval is None:
|
||||
raise
|
||||
else:
|
||||
retval = getattr(retval, "_data", retval)
|
||||
return retval
|
||||
|
||||
def __getattr__(self, k, *args, **kwargs):
|
||||
retval = None
|
||||
lockfile = super(Lockfile, self).__getattribute__("_lockfile")
|
||||
@@ -56,9 +78,18 @@ class Lockfile(object):
|
||||
return super(Lockfile, self).__getattribute__(k)
|
||||
except AttributeError:
|
||||
retval = getattr(lockfile, k, None)
|
||||
if not retval:
|
||||
retval = super(Lockfile, self).__getattribute__(k, *args, **kwargs)
|
||||
return retval
|
||||
if retval is not None:
|
||||
return retval
|
||||
return super(Lockfile, self).__getattribute__(k, *args, **kwargs)
|
||||
|
||||
def get_deps(self, dev=False, only=True):
|
||||
deps = {}
|
||||
if dev:
|
||||
deps.update(self.develop._data)
|
||||
if only:
|
||||
return deps
|
||||
deps = merge_items([deps, self.default._data])
|
||||
return deps
|
||||
|
||||
@classmethod
|
||||
def read_projectfile(cls, path):
|
||||
@@ -135,7 +166,7 @@ class Lockfile(object):
|
||||
def default(self):
|
||||
return self._lockfile.default
|
||||
|
||||
def get_requirements(self, dev=False):
|
||||
def get_requirements(self, dev=True, only=False):
|
||||
"""Produces a generator which generates requirements from the desired section.
|
||||
|
||||
:param bool dev: Indicates whether to use dev requirements, defaults to False
|
||||
@@ -143,20 +174,20 @@ class Lockfile(object):
|
||||
:rtype: :class:`~requirementslib.models.requirements.Requirement`
|
||||
"""
|
||||
|
||||
section = self.develop if dev else self.default
|
||||
for k in section.keys():
|
||||
yield Requirement.from_pipfile(k, section[k]._data)
|
||||
deps = self.get_deps(dev=dev, only=only)
|
||||
for k, v in deps.items():
|
||||
yield Requirement.from_pipfile(k, v)
|
||||
|
||||
@property
|
||||
def dev_requirements(self):
|
||||
if not self._dev_requirements:
|
||||
self._dev_requirements = list(self.get_requirements(dev=True))
|
||||
self._dev_requirements = list(self.get_requirements(dev=True, only=True))
|
||||
return self._dev_requirements
|
||||
|
||||
@property
|
||||
def requirements(self):
|
||||
if not self._requirements:
|
||||
self._requirements = list(self.get_requirements(dev=False))
|
||||
self._requirements = list(self.get_requirements(dev=False, only=True))
|
||||
return self._requirements
|
||||
|
||||
@property
|
||||
|
||||
+70
-8
@@ -6,12 +6,15 @@ import attr
|
||||
import copy
|
||||
import os
|
||||
|
||||
import tomlkit
|
||||
|
||||
from vistir.compat import Path, FileNotFoundError
|
||||
|
||||
from .requirements import Requirement
|
||||
from .project import ProjectFile
|
||||
from .utils import optional_instance_of
|
||||
from ..exceptions import RequirementError
|
||||
from ..utils import is_vcs, is_editable, merge_items
|
||||
import plette.pipfiles
|
||||
|
||||
|
||||
@@ -20,6 +23,31 @@ is_path = optional_instance_of(Path)
|
||||
is_projectfile = optional_instance_of(ProjectFile)
|
||||
|
||||
|
||||
class PipfileLoader(plette.pipfiles.Pipfile):
|
||||
@classmethod
|
||||
def validate(cls, data):
|
||||
for key, klass in plette.pipfiles.PIPFILE_SECTIONS.items():
|
||||
if key not in data or key == "source":
|
||||
continue
|
||||
klass.validate(data[key])
|
||||
|
||||
@classmethod
|
||||
def load(cls, f, encoding=None):
|
||||
content = f.read()
|
||||
if encoding is not None:
|
||||
content = content.decode(encoding)
|
||||
_data = tomlkit.loads(content)
|
||||
if "source" not in _data:
|
||||
# HACK: There is no good way to prepend a section to an existing
|
||||
# TOML document, but there's no good way to copy non-structural
|
||||
# content from one TOML document to another either. Modify the
|
||||
# TOML content directly, and load the new in-memory document.
|
||||
sep = "" if content.startswith("\n") else "\n"
|
||||
content = plette.pipfiles.DEFAULT_SOURCE_TOML + sep + content
|
||||
data = tomlkit.loads(content)
|
||||
return cls(data)
|
||||
|
||||
|
||||
@attr.s(slots=True)
|
||||
class Pipfile(object):
|
||||
path = attr.ib(validator=is_path, type=Path)
|
||||
@@ -40,16 +68,50 @@ class Pipfile(object):
|
||||
def _get_pipfile(self):
|
||||
return self.projectfile.model
|
||||
|
||||
@property
|
||||
def pipfile(self):
|
||||
return self._pipfile
|
||||
|
||||
def get_deps(self, dev=False, only=True):
|
||||
deps = {}
|
||||
if dev:
|
||||
deps.update(self.pipfile["dev-packages"]._data)
|
||||
if only:
|
||||
return deps
|
||||
deps = merge_items([deps, self.pipfile["packages"]._data])
|
||||
return deps
|
||||
|
||||
def __getitem__(self, k, *args, **kwargs):
|
||||
retval = None
|
||||
pipfile = self._pipfile
|
||||
section = None
|
||||
pkg_type = None
|
||||
try:
|
||||
retval = pipfile[k]
|
||||
except KeyError:
|
||||
if "-" in k:
|
||||
section, _, pkg_type = k.rpartition("-")
|
||||
vals = getattr(pipfile.get(section, {}), "_data", {})
|
||||
if pkg_type == "vcs":
|
||||
retval = {k: v for k, v in vals.items() if is_vcs(v)}
|
||||
elif pkg_type == "editable":
|
||||
retval = {k: v for k, v in vals.items() if is_editable(v)}
|
||||
if retval is None:
|
||||
raise
|
||||
else:
|
||||
retval = getattr(retval, "_data", retval)
|
||||
return retval
|
||||
|
||||
def __getattr__(self, k, *args, **kwargs):
|
||||
retval = None
|
||||
pipfile = super(Pipfile, self).__getattribute__("_pipfile")
|
||||
try:
|
||||
return super(Pipfile, self).__getattribute__(k)
|
||||
retval = super(Pipfile, self).__getattribute__(k)
|
||||
except AttributeError:
|
||||
retval = getattr(pipfile, k, None)
|
||||
if not retval:
|
||||
retval = super(Pipfile, self).__getattribute__(k, *args, **kwargs)
|
||||
return retval
|
||||
if retval is not None:
|
||||
return retval
|
||||
return super(Pipfile, self).__getattribute__(k, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def requires_python(self):
|
||||
@@ -69,7 +131,7 @@ class Pipfile(object):
|
||||
"""
|
||||
pf = ProjectFile.read(
|
||||
path,
|
||||
plette.pipfiles.Pipfile,
|
||||
PipfileLoader,
|
||||
invalid_ok=True
|
||||
)
|
||||
return pf
|
||||
@@ -88,7 +150,7 @@ class Pipfile(object):
|
||||
if not path:
|
||||
raise RuntimeError("Must pass a path to classmethod 'Pipfile.load'")
|
||||
if not isinstance(path, Path):
|
||||
path = Path(path)
|
||||
path = Path(path).absolute()
|
||||
pipfile_path = path if path.name == "Pipfile" else path.joinpath("Pipfile")
|
||||
project_path = pipfile_path.parent
|
||||
if not project_path.exists():
|
||||
@@ -113,10 +175,10 @@ class Pipfile(object):
|
||||
projectfile = cls.load_projectfile(path, create=create)
|
||||
pipfile = projectfile.model
|
||||
dev_requirements = [
|
||||
Requirement.from_pipfile(k, v._data) for k, v in pipfile.get("dev-packages", {}).items()
|
||||
Requirement.from_pipfile(k, getattr(v, "_data", v)) for k, v in pipfile.get("dev-packages", {}).items()
|
||||
]
|
||||
requirements = [
|
||||
Requirement.from_pipfile(k, v._data) for k, v in pipfile.get("packages", {}).items()
|
||||
Requirement.from_pipfile(k, getattr(v, "_data", v)) for k, v in pipfile.get("packages", {}).items()
|
||||
]
|
||||
creation_args = {
|
||||
"projectfile": projectfile,
|
||||
|
||||
+2
-2
@@ -22,11 +22,11 @@ from pip_shims.shims import (
|
||||
)
|
||||
from six.moves.urllib import parse as urllib_parse
|
||||
from six.moves.urllib.parse import unquote
|
||||
from vistir.compat import FileNotFoundError, Path, TemporaryDirectory
|
||||
from vistir.compat import FileNotFoundError, Path
|
||||
from vistir.misc import dedup
|
||||
from vistir.path import (
|
||||
create_tracked_tempdir, get_converted_relative_path, is_file_url,
|
||||
is_valid_url, mkdir_p
|
||||
is_valid_url
|
||||
)
|
||||
|
||||
from ..exceptions import RequirementError
|
||||
|
||||
+106
@@ -5,9 +5,15 @@ import contextlib
|
||||
import logging
|
||||
import os
|
||||
|
||||
import boltons.iterutils
|
||||
import six
|
||||
import tomlkit
|
||||
|
||||
six.add_move(six.MovedAttribute("Mapping", "collections", "collections.abc"))
|
||||
six.add_move(six.MovedAttribute("Sequence", "collections", "collections.abc"))
|
||||
six.add_move(six.MovedAttribute("Set", "collections", "collections.abc"))
|
||||
six.add_move(six.MovedAttribute("ItemsView", "collections", "collections.abc"))
|
||||
from six.moves import Mapping, Sequence, Set, ItemsView
|
||||
from six.moves.urllib.parse import urlparse, urlsplit
|
||||
|
||||
from pip_shims.shims import (
|
||||
@@ -18,6 +24,8 @@ from vistir.compat import Path
|
||||
from vistir.path import is_valid_url, ensure_mkdir_p, create_tracked_tempdir
|
||||
|
||||
|
||||
|
||||
|
||||
VCS_LIST = ("git", "svn", "hg", "bzr")
|
||||
VCS_SCHEMES = []
|
||||
SCHEME_LIST = ("http://", "https://", "ftp://", "ftps://", "file://")
|
||||
@@ -66,6 +74,12 @@ def is_vcs(pipfile_entry):
|
||||
return False
|
||||
|
||||
|
||||
def is_editable(pipfile_entry):
|
||||
if isinstance(pipfile_entry, Mapping):
|
||||
return pipfile_entry.get("editable", False) is True
|
||||
return False
|
||||
|
||||
|
||||
def multi_split(s, split):
|
||||
"""Splits on multiple given separators."""
|
||||
for r in split:
|
||||
@@ -181,3 +195,95 @@ def ensure_setup_py(base_dir):
|
||||
finally:
|
||||
if is_new:
|
||||
setup_py.unlink()
|
||||
|
||||
|
||||
# Modified from https://github.com/mahmoud/boltons/blob/master/boltons/iterutils.py
|
||||
def dict_path_enter(path, key, value):
|
||||
if isinstance(value, six.string_types):
|
||||
return value, False
|
||||
elif isinstance(value, (Mapping, dict)):
|
||||
return value.__class__(), ItemsView(value)
|
||||
elif isinstance(value, tomlkit.items.Array):
|
||||
return value.__class__([], value.trivia), enumerate(value)
|
||||
elif isinstance(value, (Sequence, list)):
|
||||
return value.__class__(), enumerate(value)
|
||||
elif isinstance(value, (Set, set)):
|
||||
return value.__class__(), enumerate(value)
|
||||
else:
|
||||
return value, False
|
||||
|
||||
|
||||
def dict_path_exit(path, key, old_parent, new_parent, new_items):
|
||||
ret = new_parent
|
||||
if isinstance(new_parent, (Mapping, dict)):
|
||||
vals = dict(new_items)
|
||||
try:
|
||||
new_parent.update(new_items)
|
||||
except AttributeError:
|
||||
# Handle toml containers specifically
|
||||
try:
|
||||
new_parent.update(vals)
|
||||
# Now use default fallback if needed
|
||||
except AttributeError:
|
||||
ret = new_parent.__class__(vals)
|
||||
elif isinstance(new_parent, tomlkit.items.Array):
|
||||
vals = tomlkit.items.item([v for i, v in new_items])
|
||||
try:
|
||||
new_parent._value.extend(vals._value)
|
||||
except AttributeError:
|
||||
ret = tomlkit.items.item(vals)
|
||||
elif isinstance(new_parent, (Sequence, list)):
|
||||
vals = [v for i, v in new_items]
|
||||
try:
|
||||
new_parent.extend(vals)
|
||||
except AttributeError:
|
||||
ret = new_parent.__class__(vals) # tuples
|
||||
elif isinstance(new_parent, (Set, set)):
|
||||
vals = [v for i, v in new_items]
|
||||
try:
|
||||
new_parent.update(vals)
|
||||
except AttributeError:
|
||||
ret = new_parent.__class__(vals) # frozensets
|
||||
else:
|
||||
raise RuntimeError('unexpected iterable type: %r' % type(new_parent))
|
||||
return ret
|
||||
|
||||
|
||||
def merge_items(target_list, sourced=False):
|
||||
if not sourced:
|
||||
target_list = [(id(t), t) for t in target_list]
|
||||
|
||||
ret = None
|
||||
source_map = {}
|
||||
|
||||
def remerge_enter(path, key, value):
|
||||
new_parent, new_items = dict_path_enter(path, key, value)
|
||||
if ret and not path and key is None:
|
||||
new_parent = ret
|
||||
|
||||
try:
|
||||
cur_val = boltons.iterutils.get_path(ret, path + (key,))
|
||||
except KeyError as ke:
|
||||
pass
|
||||
else:
|
||||
new_parent = cur_val
|
||||
|
||||
return new_parent, new_items
|
||||
|
||||
def remerge_exit(path, key, old_parent, new_parent, new_items):
|
||||
return dict_path_exit(path, key, old_parent, new_parent, new_items)
|
||||
|
||||
for t_name, target in target_list:
|
||||
if sourced:
|
||||
def remerge_visit(path, key, value):
|
||||
source_map[path + (key,)] = t_name
|
||||
return True
|
||||
else:
|
||||
remerge_visit = boltons.iterutils.default_visit
|
||||
|
||||
ret = boltons.iterutils.remap(target, enter=remerge_enter, visit=remerge_visit,
|
||||
exit=remerge_exit)
|
||||
|
||||
if not sourced:
|
||||
return ret
|
||||
return ret, source_map
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ def spinner(spinner_name=None, start_text=None, handler_map=None, nospin=False):
|
||||
)
|
||||
else:
|
||||
spinner_name = None
|
||||
if not start_text:
|
||||
if not start_text and nospin is False:
|
||||
start_text = "Running..."
|
||||
with spinner_func(
|
||||
spinner_name=spinner_name,
|
||||
|
||||
Vendored
+54
-1
@@ -10,6 +10,7 @@ import sys
|
||||
|
||||
from collections import OrderedDict
|
||||
from functools import partial
|
||||
from itertools import islice
|
||||
|
||||
import six
|
||||
|
||||
@@ -32,6 +33,9 @@ __all__ = [
|
||||
"to_text",
|
||||
"to_bytes",
|
||||
"locale_encoding",
|
||||
"chunked",
|
||||
"take",
|
||||
"divide"
|
||||
]
|
||||
|
||||
|
||||
@@ -283,7 +287,10 @@ def run(
|
||||
cmd = Script.parse(cmd)
|
||||
if block or not return_object:
|
||||
combine_stderr = False
|
||||
with spinner(spinner_name=spinner_name, start_text="Running...", nospin=nospin) as sp:
|
||||
start_text = "Running..."
|
||||
if nospin:
|
||||
start_text = None
|
||||
with spinner(spinner_name=spinner_name, start_text=start_text, nospin=nospin) as sp:
|
||||
return _create_subprocess(
|
||||
cmd,
|
||||
env=_env,
|
||||
@@ -427,6 +434,52 @@ def to_text(string, encoding="utf-8", errors=None):
|
||||
return string
|
||||
|
||||
|
||||
def divide(n, iterable):
|
||||
"""
|
||||
split an iterable into n groups, per https://more-itertools.readthedocs.io/en/latest/api.html#grouping
|
||||
|
||||
:param int n: Number of unique groups
|
||||
:param iter iterable: An iterable to split up
|
||||
:return: a list of new iterables derived from the original iterable
|
||||
:rtype: list
|
||||
"""
|
||||
|
||||
seq = tuple(iterable)
|
||||
q, r = divmod(len(seq), n)
|
||||
|
||||
ret = []
|
||||
for i in range(n):
|
||||
start = (i * q) + (i if i < r else r)
|
||||
stop = ((i + 1) * q) + (i + 1 if i + 1 < r else r)
|
||||
ret.append(iter(seq[start:stop]))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def take(n, iterable):
|
||||
"""Take n elements from the supplied iterable without consuming it.
|
||||
|
||||
:param int n: Number of unique groups
|
||||
:param iter iterable: An iterable to split up
|
||||
|
||||
from https://github.com/erikrose/more-itertools/blob/master/more_itertools/recipes.py
|
||||
"""
|
||||
|
||||
return list(islice(iterable, n))
|
||||
|
||||
|
||||
def chunked(n, iterable):
|
||||
"""Split an iterable into lists of length *n*.
|
||||
|
||||
:param int n: Number of unique groups
|
||||
:param iter iterable: An iterable to split up
|
||||
|
||||
from https://github.com/erikrose/more-itertools/blob/master/more_itertools/more.py
|
||||
"""
|
||||
|
||||
return iter(partial(take, n, iter(iterable)), [])
|
||||
|
||||
|
||||
try:
|
||||
locale_encoding = locale.getdefaultencoding()[1] or "ascii"
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user