Merge branch 'develop'

This commit is contained in:
Kenneth Reitz
2011-10-22 22:14:15 -04:00
15 changed files with 378 additions and 303 deletions
+14 -5
View File
@@ -1,10 +1,19 @@
History
-------
0.7.0 (2011-10-22)
++++++++++++++++++
* Sessions are now the primary interface.
* Deprecated InvalidMethodException.
* PATCH fix.
* New config system (no more global settings).
0.6.6 (2011-10-19)
++++++++++++++++++
* Session parameter bugfix (params merging)
* Session parameter bugfix (params merging).
0.6.5 (2011-10-18)
@@ -18,10 +27,10 @@ History
++++++++++++++++++
* Automatic decoding of unicode, based on HTTP Headers.
* New ``decode_unicode`` setting
* Removal of ``r.read/close`` methods
* New ``decode_unicode`` setting.
* Removal of ``r.read/close`` methods.
* New ``r.faw`` interface for advanced response usage.*
* Automatic expansion of parameterized headers
* Automatic expansion of parameterized headers.
0.6.3 (2011-10-13)
@@ -33,7 +42,7 @@ History
0.6.2 (2011-10-09)
++++++++++++++++++
* GET/HEAD obeys allow_redirects=False
* GET/HEAD obeys allow_redirects=False.
0.6.1 (2011-08-20)
+1 -1
View File
@@ -2,7 +2,7 @@ init:
pip install -r reqs.txt
test:
nosetests test_requests.py --with-color
nosetests test_requests.py
ci: init
nosetests test_requests.py --with-xunit --xunit-file=junit-report.xml
+1 -1
View File
@@ -6,7 +6,7 @@ Modules
- `robotframework-requests <https://github.com/bulkan/robotframework-requests>`_, a Robot Framework API wrapper.
- `fullerene <https://github.com/bitprophet/fullerene>`_, a Graphite Dashboard.
- `urbanairship-python <https://github.com/benjaminws/urbanairship-python>`_, a fork of the Urban Airship API wrapper.
- `WhitespaceBot <https://github.com/Gunio/WhitespaceBot/>`_, a project that automatically forks repos, strips trailing whitespace, and sends a pull request.
Articles & Talks
================
+7 -7
View File
@@ -17,13 +17,13 @@ A session object has all the methods of the main Requests API.
Let's persist some cookies across requests::
with requests.session() as s:
s = requests.session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
print r.content
# '{"cookies": {"sessioncookie": "123456789"}}'
print r.content
# '{"cookies": {"sessioncookie": "123456789"}}'
Sessions can also be used to provide default data to the request methods::
@@ -39,8 +39,8 @@ Sessions can also be used to provide default data to the request methods::
.. admonition:: Global Settings
Certain parameters are best set at the ``request.config`` level
(e.g.. a global proxy, user agent header).
Certain parameters are best set in the ``config`` dictionary
(e.g. user agent header).
Asynchronous Requests
+1 -1
View File
@@ -18,7 +18,7 @@ or, with `easy_install <http://pypi.python.org/pypi/setuptools>`_::
$ easy_install requests
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/index.html#pip-compared-to-easy-install>`_.
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/other-tools.html#pip-compared-to-easy-install>`_.
+31 -2
View File
@@ -1,4 +1,33 @@
# -*- coding: utf-8 -*-
from core import *
from core import __version__
# __
# /__) _ _ _ _ _/ _
# / ( (- (/ (/ (- _) / _)
# /
"""
requests
~~~~~~~~
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
"""
__title__ = 'requests'
__version__ = '0.7.0'
__build__ = 0x000700
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2011 Kenneth Reitz'
from . import utils
from .models import HTTPError, Request, Response
from .api import request, get, head, post, patch, put, delete
from .sessions import session
from .status_codes import codes
from .exceptions import (
RequestException, AuthenticationError, Timeout, URLRequired,
TooManyRedirects
)
+20 -75
View File
@@ -11,96 +11,41 @@ This module implements the Requests API.
"""
import config
from .models import Request, Response, AuthObject
from .status_codes import codes
from .hooks import dispatch_hook
from .utils import cookiejar_from_dict, header_expand
from .sessions import session
__all__ = ('request', 'get', 'head', 'post', 'patch', 'put', 'delete')
def request(method, url,
params=None, data=None, headers=None, cookies=None, files=None, auth=None,
timeout=None, allow_redirects=False, proxies=None, hooks=None, return_response=True):
params=None,
data=None,
headers=None,
cookies=None,
files=None,
auth=None,
timeout=None,
allow_redirects=False,
proxies=None,
hooks=None,
return_response=True,
config=None):
"""Constructs and sends a :class:`Request <Request>`.
Returns :class:`Response <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 or bytes 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) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
:param return_response: (optional) If False, an un-sent Request object will returned.
"""
method = str(method).upper()
if cookies is None:
cookies = {}
cookies = cookiejar_from_dict(cookies)
# Expand header values
if headers:
for k, v in headers.items() or {}:
headers[k] = header_expand(v)
args = dict(
method = method,
url = url,
data = data,
params = params,
headers = headers,
cookiejar = cookies,
files = files,
auth = auth,
hooks = hooks,
timeout = timeout or config.settings.timeout,
allow_redirects = allow_redirects,
proxies = proxies or config.settings.proxies,
s = session()
return s.request(
method, url, params, data, headers, cookies, files, auth,
timeout, allow_redirects, proxies, hooks, return_response,
config
)
# Arguments manipulation hook.
args = dispatch_hook('args', hooks, args)
r = Request(**args)
# Pre-request hook.
r = dispatch_hook('pre_request', hooks, r)
# Don't send if asked nicely.
if not return_response:
return r
# Send the HTTP Request.
r.send()
# Post-request hook.
r = dispatch_hook('post_request', hooks, r)
# Response manipulation hook.
r.response = dispatch_hook('response', hooks, r.response)
return r.response
def get(url, **kwargs):
"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param **kwargs: Optional arguments that ``request`` takes.
"""
kwargs.setdefault('allow_redirects', True)
return request('GET', url, **kwargs)
@@ -146,7 +91,7 @@ def patch(url, data='', **kwargs):
:param **kwargs: Optional arguments that ``request`` takes.
"""
return request('patch', url, **kwargs)
return request('patch', url, data='', **kwargs)
def delete(url, **kwargs):
-68
View File
@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.config
~~~~~~~~~~~~~~~
This module provides the Requests settings feature set.
"""
class Settings(object):
_singleton = {}
# attributes with defaults
__attrs__ = []
def __init__(self, **kwargs):
super(Settings, self).__init__()
self.__dict__ = self._singleton
def __call__(self, *args, **kwargs):
# new instance of class to call
r = self.__class__()
# cache previous settings for __exit__
r.__cache = self.__dict__.copy()
map(self.__cache.setdefault, self.__attrs__)
# set new settings
self.__dict__.update(*args, **kwargs)
return r
def __enter__(self):
pass
def __exit__(self, *args):
# restore cached copy
self.__dict__.update(self.__cache.copy())
del self.__cache
def __getattribute__(self, key):
if key in object.__getattribute__(self, '__attrs__'):
try:
return object.__getattribute__(self, key)
except AttributeError:
return None
return object.__getattribute__(self, key)
settings = Settings()
settings.base_headers = {'User-Agent': 'python-requests.org'}
settings.accept_gzip = True
settings.proxies = None
settings.verbose = None
settings.timeout = None
settings.max_redirects = 30
settings.decode_unicode = True
#: Use socket.setdefaulttimeout() as fallback?
settings.timeout_fallback = True
-29
View File
@@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.core
~~~~~~~~~~~~~
This module implements the main Requests system.
:copyright: (c) 2011 by Kenneth Reitz.
:license: ISC, see LICENSE for more details.
"""
__title__ = 'requests'
__version__ = '0.6.6'
__build__ = 0x000606
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2011 Kenneth Reitz'
from models import HTTPError, Request, Response
from api import *
from exceptions import *
from sessions import session
from status_codes import codes
from config import settings
import utils
+38
View File
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
requests.defaults
~~~~~~~~~~~~~~~~~
This module provides the Requests configuration defaults.
settings parameters:
- :base_headers: - Sets default User-Agent to `python-requests.org`
- :accept_gzip: - Whether or not to accept gzip-compressed data
- :proxies: - http proxies?
- :verbose: - display verbose information?
- :timeout: - timeout time until request terminates
- :max_redirects: - maximum number of allowed redirects?
- :decode_unicode: - whether or not to accept unicode?
"""
from . import __version__
defaults = dict()
defaults['base_headers'] = {
'User-Agent': 'python-requests/%s' % __version__,
'Accept-Encoding': ', '.join([ 'identity', 'deflate', 'compress', 'gzip' ]),
}
defaults['proxies'] = {}
defaults['verbose'] = None
defaults['timeout'] = None
defaults['max_redirects'] = 30
defaults['decode_unicode'] = True
defaults['timeout_fallback'] = True
# defaults['keep_alive'] = True
# defaults['max_connections'] = 10
+4 -5
View File
@@ -2,7 +2,9 @@
"""
requests.exceptions
~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~
This module contains the set of Requests' exceptions.
"""
@@ -12,15 +14,12 @@ class RequestException(Exception):
class AuthenticationError(RequestException):
"""The authentication credentials provided were invalid."""
class Timeout(RequestException):
"""The request timed out."""
class URLRequired(RequestException):
"""A valid URL is required to make a request."""
class InvalidMethod(RequestException):
"""An inappropriate method was attempted."""
class TooManyRedirects(RequestException):
"""Too many redirects."""
+73 -54
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
@@ -9,22 +10,22 @@ requests.models
import urllib
import urllib2
import socket
import codecs
import zlib
from urllib2 import HTTPError
from urlparse import urlparse, urlunparse, urljoin
from datetime import datetime
from .config import settings
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPForcedBasicAuthHandler, HTTPDigestAuthHandler, HTTPRedirectHandler
from .structures import CaseInsensitiveDict
from .packages.poster.encode import multipart_encode
from .packages.poster.streaminghttp import register_openers, get_handlers
from .utils import dict_from_cookiejar, get_unicode_from_response, stream_decode_response_unicode, decode_gzip, stream_decode_gzip
from .utils import (dict_from_cookiejar, get_unicode_from_response, stream_decode_response_unicode, decode_gzip, stream_decode_gzip)
from .status_codes import codes
from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod, TooManyRedirects
from .exceptions import Timeout, URLRequired, TooManyRedirects
from .monkeys import Request as _Request
from .monkeys import (
HTTPBasicAuthHandler, HTTPForcedBasicAuthHandler,
HTTPDigestAuthHandler, HTTPRedirectHandler)
REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
@@ -37,9 +38,20 @@ class Request(object):
"""
def __init__(self,
url=None, headers=dict(), files=None, method=None, data=dict(),
params=dict(), auth=None, cookiejar=None, timeout=None, redirect=False,
allow_redirects=False, proxies=None, hooks=None):
url=None,
headers=dict(),
files=None,
method=None,
data=dict(),
params=dict(),
auth=None,
cookies=None,
timeout=None,
redirect=False,
allow_redirects=False,
proxies=None,
hooks=None,
config=None):
#: Float describes the timeout of the request.
# (Use socket.setdefaulttimeout() as fallback)
@@ -91,7 +103,10 @@ class Request(object):
self.auth = auth
#: CookieJar to attach to :class:`Request <Request>`.
self.cookiejar = cookiejar
self.cookies = cookies
#: Dictionary of configurations for this request.
self.config = config
#: True if Request has been sent.
self.sent = False
@@ -99,17 +114,12 @@ class Request(object):
#: Event-handling hooks.
self.hooks = hooks
# Header manipulation and defaults.
if settings.accept_gzip:
settings.base_headers.update({'Accept-Encoding': 'gzip'})
if headers:
headers = CaseInsensitiveDict(self.headers)
else:
headers = CaseInsensitiveDict()
for (k, v) in settings.base_headers.items():
for (k, v) in self.config.get('base_headers', {}).items():
if k not in headers:
headers[k] = v
@@ -120,20 +130,13 @@ class Request(object):
return '<Request [%s]>' % (self.method)
def _checks(self):
"""Deterministic checks for consistency."""
if not self.url:
raise URLRequired
def _get_opener(self):
"""Creates appropriate opener object for urllib2."""
_handlers = []
if self.cookiejar is not None:
_handlers.append(urllib2.HTTPCookieProcessor(self.cookiejar))
if self.cookies is not None:
_handlers.append(urllib2.HTTPCookieProcessor(self.cookies))
if self.auth:
if not isinstance(self.auth.handler,
@@ -185,14 +188,15 @@ class Request(object):
def build(resp):
response = Response()
response.config = self.config
response.status_code = getattr(resp, 'code', None)
try:
response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None))
response.raw = resp
if self.cookiejar:
response.cookies = dict_from_cookiejar(self.cookiejar)
if self.cookies:
response.cookies = dict_from_cookiejar(self.cookies)
except AttributeError:
@@ -219,7 +223,7 @@ class Request(object):
r.raw.close()
if not len(history) < settings.max_redirects:
if not len(history) < self.config.get('max_redirects'):
raise TooManyRedirects()
history.append(r)
@@ -243,9 +247,16 @@ class Request(object):
method = self.method
request = Request(
url, self.headers, self.files, method,
self.data, self.params, self.auth, self.cookiejar,
redirect=True
url=url,
headers=self.headers,
files=self.files,
method=method,
# data=self.data,
# params=self.params,
auth=self.auth,
cookies=self.cookies,
redirect=True,
config=self.config
)
request.send()
r = request.response
@@ -310,32 +321,37 @@ class Request(object):
already been sent.
"""
self._checks()
# Some people...
if not self.url:
raise URLRequired
# Logging
if settings.verbose:
settings.verbose.write('%s %s %s\n' % (
if self.config.get('verbose'):
self.config.get('verbose').write('%s %s %s\n' % (
datetime.now().isoformat(), self.method, self.url
))
# Build the URL
url = self._build_url()
if self.method in ('GET', 'HEAD', 'DELETE'):
req = _Request(url, method=self.method)
# Attach uploaded files.
if self.files:
register_openers()
# Add form-data to the multipart.
if self.data:
self.files.update(self.data)
data, headers = multipart_encode(self.files)
else:
data = self._enc_data
headers = {}
if self.files:
register_openers()
if self.data:
self.files.update(self.data)
datagen, headers = multipart_encode(self.files)
req = _Request(url, data=datagen, headers=headers, method=self.method)
else:
req = _Request(url, data=self._enc_data, method=self.method)
# Build the Urllib2 Request.
req = _Request(url, data=data, headers=headers, method=self.method)
# Add the headers to the request.
if self.headers:
for k,v in self.headers.iteritems():
req.add_header(k, v)
@@ -353,19 +369,19 @@ class Request(object):
if not 'timeout' in str(err):
raise
if settings.timeout_fallback:
if self.config.get('timeout_fallback'):
# fall-back and use global socket timeout (This is not thread-safe!)
old_timeout = socket.getdefaulttimeout()
socket.setdefaulttimeout(self.timeout)
resp = opener(req)
if settings.timeout_fallback:
if self.config.get('timeout_fallback'):
# restore global timeout
socket.setdefaulttimeout(old_timeout)
if self.cookiejar is not None:
self.cookiejar.extract_cookies(resp, req)
if self.cookies is not None:
self.cookies.extract_cookies(resp, req)
except (urllib2.HTTPError, urllib2.URLError), why:
if hasattr(why, 'reason'):
@@ -429,6 +445,9 @@ class Response(object):
#: A dictionary of Cookies the server sent back.
self.cookies = None
#: Dictionary of configurations for this request.
self.config = None
def __repr__(self):
return '<Response [%s]>' % (self.status_code)
@@ -460,7 +479,7 @@ class Response(object):
if 'gzip' in self.headers.get('content-encoding', ''):
gen = stream_decode_gzip(gen)
if decode_unicode is None:
decode_unicode = settings.decode_unicode
decode_unicode = self.config.get('decode_unicode')
if decode_unicode:
gen = stream_decode_response_unicode(gen, self)
return gen
@@ -489,7 +508,7 @@ class Response(object):
pass
# Decode unicode content.
if settings.decode_unicode:
if self.config.get('decode_unicode'):
self._content = get_unicode_from_response(self)
self._content_consumed = True
+160 -42
View File
@@ -11,9 +11,10 @@ requests (cookies, auth, proxies).
import cookielib
from . import api
from .utils import add_dict_to_cookiejar
from .defaults import defaults
from .models import Request
from .hooks import dispatch_hook
from .utils import add_dict_to_cookiejar, cookiejar_from_dict, header_expand
def merge_kwargs(local_kwarg, default_kwarg):
@@ -25,6 +26,9 @@ def merge_kwargs(local_kwarg, default_kwarg):
if default_kwarg is None:
return local_kwarg
if isinstance(local_kwarg, basestring):
return local_kwarg
if local_kwarg is None:
return default_kwarg
@@ -32,8 +36,6 @@ def merge_kwargs(local_kwarg, default_kwarg):
if not hasattr(default_kwarg, 'items'):
return local_kwarg
# Update new values.
kwargs = default_kwarg.copy()
kwargs.update(local_kwarg)
@@ -49,7 +51,7 @@ def merge_kwargs(local_kwarg, default_kwarg):
class Session(object):
"""A Requests session."""
__attrs__ = ['headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks', 'params']
__attrs__ = ['headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks', 'params', 'config']
def __init__(self,
@@ -59,7 +61,8 @@ class Session(object):
timeout=None,
proxies=None,
hooks=None,
params=None):
params=None,
config=None):
self.headers = headers or {}
self.cookies = cookies or {}
@@ -68,14 +71,14 @@ class Session(object):
self.proxies = proxies or {}
self.hooks = hooks or {}
self.params = params or {}
self.config = config or {}
for (k, v) in defaults.items():
self.config.setdefault(k, v)
# Set up a CookieJar to be used by default
self.cookies = cookielib.FileCookieJar()
# Map and wrap requests.api methods
self._map_api_methods()
def __repr__(self):
return '<requests-client at 0x%x>' % (id(self))
@@ -85,48 +88,163 @@ class Session(object):
def __exit__(self, *args):
pass
def _map_api_methods(self):
"""Reads each available method from requests.api and decorates
them with a wrapper, which inserts any instance-local attributes
(from __attrs__) that have been set, combining them with **kwargs.
def request(self, method, url,
params=None,
data=None,
headers=None,
cookies=None,
files=None,
auth=None,
timeout=None,
allow_redirects=False,
proxies=None,
hooks=None,
return_response=True,
config=None):
"""Constructs and sends a :class:`Request <Request>`.
Returns :class:`Response <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 or bytes 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) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
:param return_response: (optional) If False, an un-sent Request object will returned.
:param config: (optional) A configuration dictionary.
"""
def pass_args(func):
def wrapper_func(*args, **kwargs):
method = str(method).upper()
# Argument collector.
_kwargs = {}
if cookies is None:
cookies = {}
# If a session request has a cookie_dict, inject the
# values into the existing CookieJar instead.
if isinstance(kwargs.get('cookies', None), dict):
kwargs['cookies'] = add_dict_to_cookiejar(
self.cookies, kwargs['cookies']
)
if isinstance(cookies, dict):
cookies = add_dict_to_cookiejar(self.cookies, cookies)
for attr in self.__attrs__:
# for attr in ['headers',]:
s_val = self.__dict__.get(attr)
r_val = kwargs.get(attr)
cookies = cookiejar_from_dict(cookies)
new_attr = merge_kwargs(r_val, s_val)
# Expand header values
if headers:
for k, v in headers.items() or {}:
headers[k] = header_expand(v)
# Skip attributes that were set to None.
if new_attr is not None:
_kwargs[attr] = new_attr
args = dict(
method=method,
url=url,
data=data,
params=params,
headers=headers,
cookies=cookies,
files=files,
auth=auth,
hooks=hooks,
timeout=timeout,
allow_redirects=allow_redirects,
proxies=proxies,
config=config
)
# Make sure we didn't miss anything.
for (k, v) in kwargs.items():
if k not in _kwargs:
_kwargs[k] = v
for attr in self.__attrs__:
session_val = getattr(self, attr, None)
local_val = args.get(attr)
return func(*args, **_kwargs)
args[attr] = merge_kwargs(local_val, session_val)
return wrapper_func
# Arguments manipulation hook.
args = dispatch_hook('args', hooks, args)
r = Request(**args)
# Pre-request hook.
r = dispatch_hook('pre_request', hooks, r)
# Don't send if asked nicely.
if not return_response:
return r
# Send the HTTP Request.
r.send()
# Post-request hook.
r = dispatch_hook('post_request', hooks, r)
# Response manipulation hook.
r.response = dispatch_hook('response', hooks, r.response)
return r.response
def get(self, url, **kwargs):
"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param **kwargs: Optional arguments that ``request`` takes.
"""
kwargs.setdefault('allow_redirects', True)
return self.request('GET', url, **kwargs)
def head(self, url, **kwargs):
"""Sends a HEAD request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param **kwargs: Optional arguments that ``request`` takes.
"""
kwargs.setdefault('allow_redirects', True)
return self.request('HEAD', url, **kwargs)
def post(self, url, data='', **kwargs):
"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param **kwargs: Optional arguments that ``request`` takes.
"""
return self.request('post', url, data=data, **kwargs)
def put(self, url, data='', **kwargs):
"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param **kwargs: Optional arguments that ``request`` takes.
"""
return self.request('put', url, data=data, **kwargs)
def patch(self, url, data='', **kwargs):
"""Sends a PATCH request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
:param **kwargs: Optional arguments that ``request`` takes.
"""
return self.request('patch', url, data='', **kwargs)
def delete(self, url, **kwargs):
"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param **kwargs: Optional arguments that ``request`` takes.
"""
return self.request('delete', url, **kwargs)
# Map and decorate each function available in requests.api
map(lambda fn: setattr(self, fn, pass_args(getattr(api, fn))),
api.__all__)
def session(**kwargs):
+3
View File
@@ -56,6 +56,9 @@ _codes = {
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'),
+25 -13
View File
@@ -8,6 +8,7 @@ import cookielib
import os
import unittest
import requests
import envoy
try:
@@ -15,11 +16,9 @@ try:
except ImportError:
import json
import requests
from requests.sessions import Session
PORT = os.environ.get('HTTPBIN_PORT', '7045')
# TODO: Detect an open port.
PORT = os.environ.get('HTTPBIN_PORT', '7077')
HTTPBIN_URL = 'http://0.0.0.0:%s/' % (PORT)
# HTTPBIN_URL = 'http://127.0.0.1:8000/'
@@ -47,8 +46,9 @@ class RequestsTestSuite(unittest.TestCase):
if not _httpbin:
self.httpbin = envoy.connect('gunicorn httpbin:app --bind=0.0.0.0:%s' % (PORT))
c = envoy.connect('gunicorn httpbin:app --bind=0.0.0.0:%s' % (PORT))
self.httpbin = c
_httpbin = True
time.sleep(1)
@@ -58,22 +58,34 @@ class RequestsTestSuite(unittest.TestCase):
"""Teardown."""
# self.httpbin.kill()
def test_entry_points(self):
import requests
requests.session
requests.session().get
requests.session().head
requests.get
requests.head
requests.put
requests.patch
requests.post
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_GET(self):
r = requests.get(httpbin('/get'))
self.assertEqual(r.status_code, 200)
def test_HTTP_302_ALLOW_REDIRECT_GET(self):
r = requests.get(httpbin('redirect', '1'))
self.assertEqual(r.status_code, 200)
r = requests.get(httpbin('redirect', '1'))
self.assertEqual(r.status_code, 200)
def test_HTTP_302_GET(self):
r = requests.get(httpbin('redirect', '1'), allow_redirects=False)
self.assertEqual(r.status_code, 302)
r = requests.get(httpbin('redirect', '1'), allow_redirects=False)
self.assertEqual(r.status_code, 302)
def test_HTTP_200_OK_GET_WITH_PARAMS(self):
@@ -442,7 +454,7 @@ class RequestsTestSuite(unittest.TestCase):
def test_session_HTTP_200_OK_GET(self):
s = Session()
s = requests.session()
r = s.get(httpbin('/get'))
self.assertEqual(r.status_code, 200)
@@ -451,7 +463,7 @@ class RequestsTestSuite(unittest.TestCase):
heads = {'User-agent': 'Mozilla/5.0'}
s = Session()
s = requests.session()
s.headers = heads
# Make 2 requests from Session object, should send header both times
@@ -472,7 +484,7 @@ class RequestsTestSuite(unittest.TestCase):
params = {'a': 'a_test'}
s = Session()
s = requests.session()
s.params = params
# Make 2 requests from Session object, should send header both times