back to Claude Sonnet 3.5 - Fill-in summary
Claude Sonnet 3.5 - Fill-in: requests
Failed to run pytests for test tests
/testbed/.venv/lib/python3.12/site-packages/httpbin/helpers.py:32: SyntaxWarning: invalid escape sequence '\_'
ASCII_ART = """
/testbed/.venv/lib/python3.12/site-packages/httpbin/helpers.py:77: SyntaxWarning: invalid escape sequence '\ '
ANGRY_ASCII ="""
/testbed/.venv/lib/python3.12/site-packages/httpbin/helpers.py:444: SyntaxWarning: invalid escape sequence '\s'
match = re.search('\s*(W/)?\"?([^"]*)\"?\s*', part)
ImportError while loading conftest '/testbed/tests/conftest.py'.
tests/conftest.py:12: in <module>
from requests.compat import urljoin
src/requests/__init__.py:164: in <module>
from .api import delete, get, head, options, patch, post, put, request
src/requests/api.py:10: in <module>
from . import sessions
src/requests/sessions.py:14: in <module>
from .adapters import HTTPAdapter
E File "/testbed/src/requests/adapters.py", line 34
E if typing.TYPE_CHECKING:
E ^^
E IndentationError: expected an indented block after 'except' statement on line 33
Patch diff
diff --git a/src/requests/_internal_utils.py b/src/requests/_internal_utils.py
index 4342cd14..1712c808 100644
--- a/src/requests/_internal_utils.py
+++ b/src/requests/_internal_utils.py
@@ -23,7 +23,11 @@ def to_native_string(string, encoding='ascii'):
that string in the native string type, encoding and decoding where
necessary. This assumes ASCII unless told otherwise.
"""
- pass
+ if isinstance(string, builtin_str):
+ return string
+ if isinstance(string, bytes):
+ return string.decode(encoding)
+ return string.encode(encoding).decode(encoding)
def unicode_is_ascii(u_string):
@@ -33,4 +37,4 @@ def unicode_is_ascii(u_string):
and not Python 2 `str`.
:rtype: bool
"""
- pass
+ return all(ord(c) < 128 for c in u_string)
diff --git a/src/requests/adapters.py b/src/requests/adapters.py
index 57d9ddf8..85f70894 100644
--- a/src/requests/adapters.py
+++ b/src/requests/adapters.py
@@ -143,7 +143,13 @@ class HTTPAdapter(BaseAdapter):
:param block: Block when no free connections are available.
:param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
"""
- pass
+ 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.
@@ -157,7 +163,36 @@ class HTTPAdapter(BaseAdapter):
:returns: ProxyManager
:rtype: urllib3.ProxyManager
"""
- pass
+ if proxy in self.proxy_manager:
+ return self.proxy_manager[proxy]
+
+ proxy_headers = self.proxy_headers(proxy)
+ if proxy_headers:
+ proxy_kwargs['proxy_headers'] = proxy_kwargs.get('proxy_headers', {})
+ proxy_kwargs['proxy_headers'].update(proxy_headers)
+
+ if proxy.lower().startswith('socks'):
+ username, password = get_auth_from_url(proxy)
+ manager = SOCKSProxyManager(
+ proxy,
+ username=username,
+ password=password,
+ num_pools=self._pool_connections,
+ maxsize=self._pool_maxsize,
+ block=self._pool_block,
+ **proxy_kwargs
+ )
+ else:
+ manager = proxy_from_url(
+ proxy,
+ num_pools=self._pool_connections,
+ maxsize=self._pool_maxsize,
+ block=self._pool_block,
+ **proxy_kwargs
+ )
+
+ self.proxy_manager[proxy] = manager
+ return manager
def cert_verify(self, conn, url, verify, cert):
"""Verify a SSL certificate. This method should not be called from user
@@ -171,7 +206,22 @@ class HTTPAdapter(BaseAdapter):
to a CA bundle to use
:param cert: The SSL certificate to verify.
"""
- pass
+ if isinstance(verify, str):
+ conn.cert_reqs = 'CERT_REQUIRED'
+ conn.ca_certs = verify
+ elif verify is False:
+ conn.cert_reqs = 'CERT_NONE'
+ conn.ca_certs = None
+ else:
+ conn.cert_reqs = 'CERT_REQUIRED'
+ conn.ca_certs = DEFAULT_CA_BUNDLE_PATH
+
+ if cert:
+ if isinstance(cert, str):
+ conn.cert_file = cert
+ else:
+ conn.cert_file = cert[0]
+ conn.key_file = cert[1]
def build_response(self, req, resp):
"""Builds a :class:`Response <requests.Response>` object from a urllib3
@@ -183,7 +233,32 @@ class HTTPAdapter(BaseAdapter):
:param resp: The urllib3 response object.
:rtype: requests.Response
"""
- pass
+ 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 build_connection_pool_key_attributes(self, request, verify, cert=None):
"""Build the PoolKey attributes used by urllib3 to return a connection.
@@ -270,7 +345,18 @@ class HTTPAdapter(BaseAdapter):
:param proxies: (optional) A Requests-style dictionary of proxies used on this request.
:rtype: urllib3.ConnectionPool
"""
- pass
+ warnings.warn(
+ "get_connection is deprecated and will be removed in a future version. "
+ "Please use get_connection_with_tls_context instead.",
+ DeprecationWarning
+ )
+ proxy = select_proxy(url, proxies)
+
+ if proxy:
+ proxy = prepend_scheme_if_needed(proxy, 'http')
+ return self.proxy_manager_for(proxy)
+
+ return self.poolmanager.connection_from_url(url)
def close(self):
"""Disposes of any internal state.
@@ -278,7 +364,10 @@ class HTTPAdapter(BaseAdapter):
Currently, this closes the PoolManager and any active ProxyManager,
which closes any pooled connections.
"""
- pass
+ self.poolmanager.clear()
+ for proxy in self.proxy_manager.values():
+ proxy.clear()
+ self.proxy_manager.clear()
def request_url(self, request, proxies):
"""Obtain the url to use when making the final request.
@@ -294,7 +383,13 @@ class HTTPAdapter(BaseAdapter):
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
:rtype: str
"""
- pass
+ proxy = select_proxy(request.url, proxies)
+ scheme = urlparse(request.url).scheme
+
+ if proxy and scheme != 'https':
+ return request.url
+ else:
+ return request.path_url
def add_headers(self, request, **kwargs):
"""Add any headers needed by the connection. As of v2.0 this does
@@ -323,7 +418,13 @@ class HTTPAdapter(BaseAdapter):
:param proxy: The url of the proxy being used for this request.
:rtype: dict
"""
- pass
+ headers = {}
+ username, password = get_auth_from_url(proxy)
+
+ if username and password:
+ headers['Proxy-Authorization'] = _basic_auth_str(username, password)
+
+ return headers
def send(self, request, stream=False, timeout=None, verify=True, cert=
None, proxies=None):
diff --git a/src/requests/auth.py b/src/requests/auth.py
index f08aecf6..c7fddca4 100644
--- a/src/requests/auth.py
+++ b/src/requests/auth.py
@@ -21,7 +21,8 @@ CONTENT_TYPE_MULTI_PART = 'multipart/form-data'
def _basic_auth_str(username, password):
"""Returns a Basic Auth string."""
- pass
+ auth = f"{username}:{password}"
+ return f"Basic {b64encode(auth.encode('utf-8')).decode('ascii')}"
class AuthBase:
@@ -72,11 +73,92 @@ class HTTPDigestAuth(AuthBase):
"""
:rtype: str
"""
- pass
+ 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')
+
+ if algorithm is None:
+ _algorithm = 'MD5'
+ else:
+ _algorithm = algorithm.upper()
+
+ 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
+ else:
+ raise ValueError(f"Unsupported algorithm: {_algorithm}")
+
+ KD = lambda s, d: hash_utf8(f"{s}:{d}")
+
+ if hash_utf8 is None:
+ return None
+
+ entdig = None
+ p_parsed = urlparse(url)
+ path = p_parsed.path or "/"
+ if p_parsed.query:
+ path += f"?{p_parsed.query}"
+
+ A1 = f"{self.username}:{realm}:{self.password}"
+ A2 = f"{method}:{path}"
+
+ HA1 = hash_utf8(A1)
+ HA2 = hash_utf8(A2)
+
+ if qop is None:
+ respdig = KD(HA1, f"{nonce}:{HA2}")
+ elif qop == 'auth' or 'auth' in qop.split(','):
+ nc = f"{self._thread_local.nonce_count:08x}"
+ cnonce = self._thread_local.cnonce
+ noncebit = f"{nonce}:{nc}:{cnonce}:{qop}"
+ respdig = KD(HA1, f"{noncebit}:{HA2}")
+ else:
+ return None
+
+ self._thread_local.nonce_count += 1
+
+ base = (f'username="{self.username}", realm="{realm}", nonce="{nonce}", '
+ f'uri="{path}", response="{respdig}"')
+ if opaque:
+ base += f', opaque="{opaque}"'
+ if algorithm:
+ base += f', algorithm="{algorithm}"'
+ if entdig:
+ base += f', digest="{entdig}"'
+ if qop:
+ base += f', qop="{qop}"'
+ if 'auth' in qop.split(','):
+ base += f', nc={nc}, cnonce="{cnonce}"'
+
+ return f'Digest {base}'
def handle_redirect(self, r, **kwargs):
"""Reset num_401_calls counter on redirects."""
- pass
+ if r.is_redirect:
+ self._thread_local.num_401_calls = 1
def handle_401(self, r, **kwargs):
"""
@@ -84,7 +166,35 @@ class HTTPDigestAuth(AuthBase):
:rtype: requests.Response
"""
- pass
+ if r.status_code != 401:
+ self._thread_local.num_401_calls = 1
+ return r
+
+ 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):
self.init_per_thread_state()
diff --git a/src/requests/compat.py b/src/requests/compat.py
index d4d04060..f792e4a5 100644
--- a/src/requests/compat.py
+++ b/src/requests/compat.py
@@ -12,7 +12,15 @@ import sys
def _resolve_char_detection():
"""Find supported character detection libraries."""
- pass
+ try:
+ import charset_normalizer
+ return charset_normalizer
+ except ImportError:
+ try:
+ import chardet
+ return chardet
+ except ImportError:
+ return None
chardet = _resolve_char_detection()
diff --git a/src/requests/cookies.py b/src/requests/cookies.py
index 6392b7bc..4079fe6d 100644
--- a/src/requests/cookies.py
+++ b/src/requests/cookies.py
@@ -61,7 +61,13 @@ def extract_cookies_to_jar(jar, request, response):
:param request: our own requests.Request object
:param response: urllib3.HTTPResponse object
"""
- pass
+ if not (hasattr(response, '_original_response') and
+ hasattr(response._original_response, 'msg')):
+ return
+ req = MockRequest(request)
+ # the _original_response field is the wrapped httplib.HTTPResponse object
+ res = MockResponse(response._original_response.msg)
+ jar.extract_cookies(res, req)
def get_cookie_header(jar, request):
@@ -70,7 +76,9 @@ def get_cookie_header(jar, request):
:rtype: str
"""
- pass
+ 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):
@@ -78,7 +86,18 @@ def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
Wraps CookieJar.clear(), is O(n).
"""
- pass
+ 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):
@@ -112,14 +131,22 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. warning:: operation is O(n), not O(1).
"""
- pass
+ 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.
"""
- pass
+ 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
@@ -127,7 +154,8 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. seealso:: itervalues() and iteritems().
"""
- pass
+ for cookie in iter(self):
+ yield cookie.name
def keys(self):
"""Dict-like keys() that returns a list of names of cookies from the
@@ -135,7 +163,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. seealso:: values() and items().
"""
- pass
+ return list(self.iterkeys())
def itervalues(self):
"""Dict-like itervalues() that returns an iterator of values of cookies
@@ -143,7 +171,8 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. seealso:: iterkeys() and iteritems().
"""
- pass
+ for cookie in iter(self):
+ yield cookie.value
def values(self):
"""Dict-like values() that returns a list of values of cookies from the
@@ -151,7 +180,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. seealso:: keys() and items().
"""
- pass
+ return list(self.itervalues())
def iteritems(self):
"""Dict-like iteritems() that returns an iterator of name-value tuples
@@ -159,7 +188,8 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. seealso:: iterkeys() and itervalues().
"""
- pass
+ 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
@@ -168,15 +198,23 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
.. seealso:: keys() and values().
"""
- pass
+ return list(self.iteritems())
def list_domains(self):
"""Utility method to list all the domains in the jar."""
- pass
+ 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."""
- pass
+ 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.
@@ -184,7 +222,8 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
:rtype: bool
"""
- pass
+ domains = self.list_domains()
+ return len(domains) > 1
def get_dict(self, domain=None, path=None):
"""Takes as an argument an optional domain and path and returns a plain
@@ -193,7 +232,12 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
:rtype: dict
"""
- pass
+ 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:
@@ -282,12 +326,68 @@ 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").
"""
- pass
+ 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."""
- pass
+ expires = None
+ if morsel['max-age']:
+ try:
+ expires = int(time.time() + int(morsel['max-age']))
+ except ValueError:
+ pass
+ 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(
+ name=morsel.key,
+ value=morsel.value,
+ version=0,
+ port=None,
+ port_specified=False,
+ domain=morsel['domain'],
+ domain_specified=bool(morsel['domain']),
+ domain_initial_dot=morsel['domain'].startswith('.'),
+ path=morsel['path'],
+ path_specified=bool(morsel['path']),
+ secure=bool(morsel['secure']),
+ expires=expires,
+ discard=False,
+ comment=morsel['comment'],
+ comment_url=bool(morsel['comment']),
+ rest={'HttpOnly': morsel['httponly']},
+ rfc2109=False,
+ )
def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
@@ -299,7 +399,16 @@ def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
already in the jar with new ones.
:rtype: CookieJar
"""
- pass
+ 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):
@@ -309,4 +418,14 @@ def merge_cookies(cookiejar, cookies):
:param cookies: Dictionary or CookieJar object to be added.
:rtype: CookieJar
"""
- pass
+ 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):
+ for cookie in cookies:
+ cookiejar.set_cookie(cookie)
+
+ return cookiejar
diff --git a/src/requests/help.py b/src/requests/help.py
index 8057ee09..91dae62e 100644
--- a/src/requests/help.py
+++ b/src/requests/help.py
@@ -36,17 +36,83 @@ def _implementation():
doesn't work for Jython or IronPython. Future investigation should be done
to work out the correct shape of the code for those platforms.
"""
- pass
+ implementation = platform.python_implementation()
+ version = platform.python_version()
+ return {'name': implementation, 'version': version}
def info():
"""Generate information for a bug report."""
- pass
+ try:
+ import urllib3
+ except ImportError:
+ urllib3 = None
+
+ try:
+ import chardet
+ except ImportError:
+ chardet = None
+
+ try:
+ import charset_normalizer
+ except ImportError:
+ charset_normalizer = None
+
+ try:
+ from urllib3.contrib import pyopenssl
+ except ImportError:
+ pyopenssl = None
+ OpenSSL = None
+ cryptography = None
+ else:
+ import cryptography
+ import OpenSSL
+
+ system_ssl = ssl.OPENSSL_VERSION_INFO
+ system_ssl_info = {'name': 'OpenSSL', 'version': f'{system_ssl[0]}.{system_ssl[1]}.{system_ssl[2]}'}
+
+ return {
+ 'platform': {
+ 'system': platform.system(),
+ 'release': platform.release(),
+ },
+ 'implementation': _implementation(),
+ 'python': {
+ 'version': platform.python_version(),
+ 'executable': sys.executable,
+ 'build': platform.python_build(),
+ },
+ 'requests': {
+ 'version': requests_version,
+ },
+ 'urllib3': {
+ 'version': urllib3.__version__ if urllib3 else None,
+ },
+ 'chardet': {
+ 'version': chardet.__version__ if chardet else None,
+ },
+ 'charset_normalizer': {
+ 'version': charset_normalizer.__version__ if charset_normalizer else None,
+ },
+ 'idna': {
+ 'version': idna.__version__,
+ },
+ 'system_ssl': system_ssl_info,
+ 'pyopenssl': {
+ 'version': OpenSSL.version.__version__ if OpenSSL else None,
+ 'openssl_version': OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION).decode('utf-8') if OpenSSL else None,
+ },
+ 'cryptography': {
+ 'version': cryptography.__version__ if cryptography else None,
+ },
+ 'pyopenssl_subject': pyopenssl.get_subject() if pyopenssl else None,
+ 'pyopenssl_issuer': pyopenssl.get_issuer() if pyopenssl else None,
+ }
def main():
"""Pretty-print the bug information as JSON."""
- pass
+ print(json.dumps(info(), sort_keys=True, indent=2))
if __name__ == '__main__':
diff --git a/src/requests/hooks.py b/src/requests/hooks.py
index 8dfca96f..6de12ac2 100644
--- a/src/requests/hooks.py
+++ b/src/requests/hooks.py
@@ -14,4 +14,16 @@ HOOKS = ['response']
def dispatch_hook(key, hooks, hook_data, **kwargs):
"""Dispatches a hook dictionary on a given piece of data."""
- pass
+ hooks = hooks or {}
+ if key not in hooks:
+ return hook_data
+
+ if hasattr(hooks[key], '__call__'):
+ hooks[key] = [hooks[key]]
+
+ for hook in hooks[key]:
+ _hook_data = hook(hook_data, **kwargs)
+ if _hook_data is not None:
+ hook_data = _hook_data
+
+ return hook_data
diff --git a/src/requests/structures.py b/src/requests/structures.py
index b389ca26..d3d96e7d 100644
--- a/src/requests/structures.py
+++ b/src/requests/structures.py
@@ -58,7 +58,7 @@ class CaseInsensitiveDict(MutableMapping):
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
- pass
+ return ((key.lower(), value) for key, value in self._store.values())
def __eq__(self, other):
if isinstance(other, Mapping):
diff --git a/src/requests/utils.py b/src/requests/utils.py
index 504e22fc..536e053d 100644
--- a/src/requests/utils.py
+++ b/src/requests/utils.py
@@ -40,22 +40,71 @@ if sys.platform == 'win32':
Checks proxy settings gathered from the environment, if specified,
or the registry.
"""
- pass
+ try:
+ return proxy_bypass_environment(host)
+ except KeyError:
+ return False
def dict_to_sequence(d):
"""Returns an internal sequence dictionary update."""
- pass
+ if isinstance(d, Mapping):
+ return d.items()
+ return d
def get_netrc_auth(url, raise_errors=False):
"""Returns the Requests tuple auth for a given url from netrc."""
- pass
+ try:
+ from netrc import netrc, NetrcParseError
+
+ netrc_path = None
+ for f in NETRC_FILES:
+ try:
+ loc = os.path.expanduser('~/{}'.format(f))
+ except KeyError:
+ # os.path.expanduser can fail when $HOME is undefined and
+ # getpwuid fails. See https://bugs.python.org/issue20164
+ continue
+
+ if os.path.exists(loc):
+ netrc_path = loc
+ break
+
+ # Abort early if there isn't one.
+ if netrc_path is None:
+ return None
+
+ ri = urlparse(url)
+
+ # Strip port numbers from netloc
+ host = ri.netloc.split(':')[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 the file doesn't exist
+ if raise_errors:
+ raise
+
+ except (ImportError, AttributeError):
+ # netrc module not available or not supported
+ pass
+
+ return None
def guess_filename(obj):
"""Tries to guess the filename of the given object."""
- pass
+ name = getattr(obj, 'name', None)
+ if name and isinstance(name, basestring) and name[0] != '<' and name[-1] != '>':
+ return os.path.basename(name)
+
+ return None
def extract_zipped_paths(path):
@@ -63,13 +112,42 @@ def extract_zipped_paths(path):
archive with the location of an extracted copy of the target, or else
just return the provided path unchanged.
"""
- pass
+ if os.path.exists(path):
+ return path
+
+ 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
+
+ try:
+ zip_file = zipfile.ZipFile(archive)
+ if member not in zip_file.namelist():
+ return path
+
+ extracted_path = os.path.join(tempfile.gettempdir(), 'requests-temp', member.split('/')[-1])
+ if not os.path.exists(extracted_path):
+ extracted_path = zip_file.extract(member, path=os.path.dirname(extracted_path))
+ zip_file.close()
+ return extracted_path
+ except (zipfile.BadZipFile, OSError):
+ return path
@contextlib.contextmanager
def atomic_open(filename):
"""Write a file to the disk in an atomic fashion"""
- pass
+ tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename))
+ try:
+ with os.fdopen(tmp_descriptor, 'wb') as tmp_handler:
+ yield tmp_handler
+ os.replace(tmp_name, filename)
+ except BaseException:
+ os.remove(tmp_name)
+ raise
def from_key_val_list(value):
@@ -90,7 +168,13 @@ def from_key_val_list(value):
:rtype: OrderedDict
"""
- pass
+ 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):
@@ -110,7 +194,16 @@ def to_key_val_list(value):
:rtype: list
"""
- pass
+ 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):
+ return list(value.items())
+
+ return value
def parse_list_header(value):
@@ -136,7 +229,7 @@ def parse_list_header(value):
:return: :class:`list`
:rtype: list
"""
- pass
+ return _parse_list_header(value)
def parse_dict_header(value):
@@ -161,7 +254,14 @@ def parse_dict_header(value):
:return: :class:`dict`
:rtype: dict
"""
- pass
+ result = {}
+ for item in _parse_list_header(value):
+ if '=' not in item:
+ result[item] = None
+ else:
+ name, value = item.split('=', 1)
+ result[name] = unquote_header_value(value)
+ return result
def unquote_header_value(value, is_filename=False):
@@ -172,7 +272,11 @@ def unquote_header_value(value, is_filename=False):
:param value: the header value to unquote.
:rtype: str
"""
- pass
+ if value and value[0] == value[-1] == '"':
+ value = value[1:-1]
+ if not is_filename or value[:2] != '\\\\':
+ return value.replace('\\\\', '\\').replace('\\"', '"')
+ return value
def dict_from_cookiejar(cj):
@@ -181,7 +285,12 @@ def dict_from_cookiejar(cj):
:param cj: CookieJar object to extract cookies from.
:rtype: dict
"""
- pass
+ cookie_dict = {}
+
+ for cookie in cj:
+ cookie_dict[cookie.name] = cookie.value
+
+ return cookie_dict
def add_dict_to_cookiejar(cj, cookie_dict):