Compare commits

...

140 Commits

Author SHA1 Message Date
Kenneth Reitz
2d98ca7477 Merge branch 'release/0.5.0' 2011-06-21 22:44:02 -04:00
Kenneth Reitz
e2d6a92150 Timeouts are normal errors now.
--
FIX ALL TEH TESTS!
2011-06-21 22:43:15 -04:00
Kenneth Reitz
162b751f6d Timeouts are normal errors now.
--
BREAK ALL TEH TESTS!
2011-06-21 22:40:27 -04:00
Kenneth Reitz
20aed5488b Release the v0.5.0! 2011-06-21 22:26:06 -04:00
Kenneth Reitz
2374d0fd7a so many paramatersssss 2011-06-21 22:22:46 -04:00
Kenneth Reitz
483d00736c readme update 2011-06-21 22:22:02 -04:00
Kenneth Reitz
15de7d9dba add PATCH to docs 2011-06-21 22:21:54 -04:00
Kenneth Reitz
34a583b821 PATCH in Docs 2011-06-21 22:06:31 -04:00
Kenneth Reitz
2da86daa3a update history 2011-06-21 22:03:56 -04:00
Kenneth Reitz
b4eac4c993 redirect fix 2011-06-21 22:00:04 -04:00
Kenneth Reitz
b4a4f38cce verbose output stream support 2011-06-21 21:59:57 -04:00
Kenneth Reitz
8c81cf64de proper PATCH testing 2011-06-21 18:24:28 -04:00
Kenneth Reitz
00f066a467 PATCH support 2011-06-21 18:03:06 -04:00
Kenneth Reitz
bdf9f90c8a omnijson for tests 2011-06-21 17:59:24 -04:00
Kenneth Reitz
428678b2e4 Added Alberto Paro to AUTHORS 2011-06-21 17:35:29 -04:00
Alberto Paro
bcd30f05ab Added setuptools management to setup.py to allow to install in develop mode 2011-06-21 22:48:31 +02:00
Kenneth Reitz
bd49da067f updated history 2011-06-21 00:07:41 -04:00
moliware
3e7c682e66 Support for proxies 2011-06-19 22:36:50 +02:00
Kenneth Reitz
bbd1e40bd5 no pypy default 2011-06-17 14:09:33 -04:00
Kenneth Reitz
a25f5ff1dd that works 2011-06-17 13:51:29 -04:00
Richard Boulton
80d860d2d4 Allow POST and PUT requests to take both querystring params and request body
data.
2011-06-16 15:48:43 +01:00
Richard Boulton
f31ade335d Change documentation comments for "params" and "data" parameters to refer to
the request body or the query string, as appropriate.  "GET parameters", "POST
data", etc aren't the ideal terminology to use - they come from common usage
with web browsers, rather than the HTTP specification, so are off-putting in
other contexts.
2011-06-16 15:40:14 +01:00
Richard Boulton
023ab755e5 Move the code to encode the request data into a static helper method - I think this makes the code clearer, but it also prepares the way for reusing the encoding code 2011-06-16 13:35:33 +01:00
Richard Boulton
4320f81bf4 Actually use the HTTPSBIN variable for test https requests 2011-06-16 13:06:22 +01:00
Kenneth Reitz
47f1f28385 + Richard Boulton to AUTHOURS 2011-06-16 06:27:13 -04:00
Richard Boulton
3d1444b829 Update documentation of the "data" parameter for requests.post() to indicate that it allows bytes. Update documentation of requests.put() to refer to the "data" parameter rather than the (hidden, but functional) "params" parameter, and note that that also allows both bytes and dictionaries. 2011-06-16 03:18:47 -07:00
Zbigniew Siciarz
4d3a9defd2 Porting cookie test to httpbin.org. 2011-06-16 09:08:32 +02:00
Zbigniew Siciarz
1f1211747b Merge remote-tracking branch 'upstream/develop' into develop 2011-06-16 08:56:38 +02:00
Kenneth Reitz
45e30048f0 port request tests to httpbin.org 2011-06-16 00:11:47 -04:00
Kenneth Reitz
245c6edec3 todo 2011-06-15 22:56:14 -04:00
Kenneth Reitz
cb0a96fbf3 Redirect chain flag default off. 2011-06-15 22:43:26 -04:00
Zbigniew Siciarz
fade9e36b6 Merge branch 'develop' of git://github.com/kennethreitz/requests into develop 2011-06-13 12:41:40 +02:00
Zbigniew Siciarz
24e2a05605 Merge branch 'master' of git://github.com/kennethreitz/requests into develop 2011-06-13 12:41:33 +02:00
Kenneth Reitz
1749260c97 update tests 2011-06-12 22:02:35 -04:00
Kenneth Reitz
3b86c3c0e3 httpbin 2011-06-12 22:00:17 -04:00
Kenneth Reitz
8f3da7b72c added Daniele Tricoli 'Eriol' to authors 2011-06-12 13:14:56 -04:00
Kenneth Reitz
d6cde6a5df add Zbigniew Siciarz to authors 2011-06-09 18:09:39 -04:00
Zbigniew Siciarz
54fd24b757 Testing that cookies actually work. 2011-06-09 12:30:43 +02:00
Kenneth Reitz
2153b21152 less hostility 2011-06-08 19:22:19 -04:00
Kenneth Reitz
74013d751c rackspace cloud's net connection is insane 2011-06-08 19:18:11 -04:00
Kenneth Reitz
1fa1cdd2a3 WHITESPACE 2011-06-08 19:11:08 -04:00
Kenneth Reitz
cd0d0a2b17 whitespace 2011-06-05 21:22:35 -04:00
Kenneth Reitz
e63569041a Update AUTHORS
Refs #46
2011-05-27 08:02:57 -04:00
Kenneth Reitz
54449483b6 Merge branch 'patch-1' of https://github.com/JNRowe/requests into develop 2011-05-27 08:00:22 -04:00
Kenneth Reitz
52bedf4c0b default post data to empty byte string
Refs #45
2011-05-27 07:59:28 -04:00
James Rowe
5962c0ea86 Subclass dict for CaseInsensitiveDict. 2011-05-27 00:17:44 -07:00
Johannes
9b2d7382bf Conform api.* arguments to api.request arguments.
Fixes #40.
Fixes #41.
2011-05-27 08:36:03 +02:00
Kenneth Reitz
7e3d265b0f gauge.es 2011-05-24 18:38:45 -04:00
Kenneth Reitz
f990a7b453 Merge branch 'develop' of github.com:kennethreitz/requests into develop 2011-05-24 17:18:28 -04:00
Kenneth Reitz
ef02d02b1f testimonial cleanup 2011-05-24 17:18:19 -04:00
Kenneth Reitz
04c169ecfe Merge pull request #39 from mrtazz/patch-1
e.g. should be i.e. I guess
2011-05-23 08:42:52 -07:00
Daniel Schauenberg
5593f12347 e.g. should be i.e. I guess 2011-05-23 08:39:15 -07:00
Kenneth Reitz
3189d8690e refactor^2 2011-05-22 18:01:21 -04:00
Kenneth Reitz
b51e042e73 Merge pull request #38 from jgorset/requests
---

Ive refactored `api.request`. I think. The new version is more robust in that it no longer accepts arbitrary arguments and raises an exception if one attempts to call it with both `params` and `data` (that should probably be supported, though, but thats a topic for another issue).

Conflicts:
	requests/api.py
2011-05-22 17:54:10 -04:00
Johannes
82f1320ed5 Refactor api.request 2011-05-22 23:49:29 +02:00
Kenneth Reitz
add6feab02 Merge branch 'release/0.4.1' 2011-05-22 14:16:48 -04:00
Kenneth Reitz
670c4aecdd version bump (v0.4.1) 2011-05-22 14:13:49 -04:00
Kenneth Reitz
215d3984cc setup cleanup 2011-05-22 13:58:37 -04:00
Kenneth Reitz
0b36d925f6 remove debug print 2011-05-22 13:56:57 -04:00
Kenneth Reitz
bc290f62d7 defaults 2011-05-22 13:55:13 -04:00
Kenneth Reitz
c0faa9e79c new settings location 2011-05-22 13:31:17 -04:00
Kenneth Reitz
f887906ec3 cleanup — comments 2011-05-22 13:31:09 -04:00
Kenneth Reitz
982fd9d803 new config module 2011-05-22 13:30:55 -04:00
Kenneth Reitz
d64bf501d0 no import packages 2011-05-22 13:30:22 -04:00
Kenneth Reitz
49f1ec1efb settings -> config
reimpliment settings as singleton
2011-05-22 13:27:17 -04:00
Kenneth Reitz
0741448798 pypy binary moved 2011-05-22 12:14:50 -04:00
Kenneth Reitz
8a4d5c4683 useragent 2011-05-22 12:10:56 -04:00
Kenneth Reitz
3cdb777c23 restore allow_redirect behavior 2011-05-22 11:35:01 -04:00
Kenneth Reitz
ddc5452978 Merge pull request #37 from jgorset/requests
---

![Meme](http://i.imgur.com/kXzcW.jpg)
2011-05-22 11:25:31 -04:00
Johannes
8aa6fabd4d Support relative redirects. Fixes #36 2011-05-22 11:41:15 +02:00
Kenneth Reitz
398f5b501f not tablib.. 2011-05-21 16:24:28 -04:00
Kenneth Reitz
8d9bcefa9d Merge pull request #35 from jgorset/requests
---

Ive completed the migration of the requests settings to the new `settings` module, but my attempt to facilitate for manipulating settings outside of the context manager (eg. `requests.settings.timeout = 5`) has been thwarted by the namespace collision of the context manager and the module.

Im probably just being stupid. Youll figure it out.

Conflicts:
	requests/api.py
2011-05-21 13:04:32 -04:00
Kenneth Reitz
0b29964bf8 updated history 2011-05-21 13:00:21 -04:00
Kenneth Reitz
06e662be86 python2.5 with 2011-05-21 12:47:29 -04:00
Kenneth Reitz
e153b94190 Improved redirection behavior for 301/303 2011-05-21 12:44:58 -04:00
Johannes
7d0374df2f Slight refactor 2011-05-20 22:50:50 +02:00
Johannes
2f60748c73 Stop importing inspect 2011-05-20 20:45:51 +02:00
Johannes
bbd58b922b Migrate settings to new module 2011-05-20 20:32:39 +02:00
Kenneth Reitz
21e51b504e no toys 2011-05-20 12:55:36 -04:00
Kenneth Reitz
ba79a2eb97 new settings module 2011-05-20 12:54:42 -04:00
Johannes
ef81c16611 Fix typos 2011-05-20 18:34:43 +02:00
Johannes
da26f1f377 Adjust tests 2011-05-20 18:31:23 +02:00
Johannes
c8cc2d0421 Relocate exceptions 2011-05-20 18:20:17 +02:00
Johannes
a00dc1abf3 Raise Timeout on request timeouts 2011-05-20 18:17:46 +02:00
Johannes
59c747b793 Fix typos 2011-05-19 23:32:27 +02:00
Johannes Gorset
baddc5b6ad Refactor Request#__init__ 2011-05-19 10:16:54 +02:00
Johannes Gorset
637d0330c2 Add test for non-urlencoded post data 2011-05-19 10:14:15 +02:00
Johannes Gorset
21e2d0c34a Add tests for settings 2011-05-19 10:13:42 +02:00
Johannes Gorset
ec5f227381 Refactor settings 2011-05-19 10:13:37 +02:00
Kenneth Reitz
8ffb2462f9 home page url change 2011-05-18 10:11:07 -04:00
Kenneth Reitz
12187155b4 urllib2 2011-05-16 01:38:48 -04:00
Kenneth Reitz
d7f49f3aa5 no quickstart till done 2011-05-16 01:33:33 -04:00
Kenneth Reitz
461497f5be "a kennethreitz project" 2011-05-16 01:31:54 -04:00
Kenneth Reitz
7929bb91be api update 2011-05-16 01:18:09 -04:00
Kenneth Reitz
6e13e7343d Big API update 2011-05-16 01:13:05 -04:00
Kenneth Reitz
f9f3b46ef6 not tablib 2011-05-16 00:10:30 -04:00
Kenneth Reitz
cc851818c2 doc fixes (pretty) 2011-05-15 23:24:25 -04:00
Kenneth Reitz
ba45b9d890 Major docs cleanup 2011-05-15 23:19:19 -04:00
Kenneth Reitz
9fd70c209b GA and CR 2011-05-15 23:00:15 -04:00
Kenneth Reitz
b7970c2a46 config update 2011-05-15 22:58:17 -04:00
Kenneth Reitz
a7d072a7c0 features philosophy 2011-05-15 21:29:49 -04:00
Kenneth Reitz
054891ab5e can/cant 2011-05-15 21:22:40 -04:00
Kenneth Reitz
d18ea48b95 docs update 2011-05-15 21:14:51 -04:00
Kenneth Reitz
d39cde206b installation 2011-05-15 21:01:48 -04:00
Kenneth Reitz
58d5e0318e api.py -> api.rst 2011-05-15 21:01:44 -04:00
Kenneth Reitz
3e23e48239 docs intro 2011-05-15 20:46:36 -04:00
Kenneth Reitz
f1f5788066 docs refactor 2011-05-15 20:46:28 -04:00
Kenneth Reitz
928facfcd9 better links 2011-05-15 20:34:40 -04:00
Kenneth Reitz
70857f3097 example udpate 2011-05-15 20:25:01 -04:00
Kenneth Reitz
da726fc030 webkit scrollbars 2011-05-15 20:24:55 -04:00
Kenneth Reitz
874a874ea6 better index 2011-05-15 17:52:09 -04:00
Kenneth Reitz
0f76e17258 index cleanup 2011-05-15 17:49:59 -04:00
Kenneth Reitz
6b36911260 testimonials 2011-05-15 17:28:16 -04:00
Kenneth Reitz
1ebf3afb24 orgs 2011-05-15 13:23:05 -04:00
Kenneth Reitz
84544b1d01 link fix 2011-05-15 13:14:22 -04:00
Kenneth Reitz
319b570f82 much better index 2011-05-15 13:13:41 -04:00
Kenneth Reitz
f7481adf45 better verbage 2011-05-15 13:05:22 -04:00
Kenneth Reitz
9b165629f9 better title 2011-05-15 13:01:58 -04:00
Kenneth Reitz
79b43b2b6e better 2011-05-15 12:49:19 -04:00
Kenneth Reitz
80b71a03ce initial doc index 2011-05-15 12:43:34 -04:00
Kenneth Reitz
2546bafdde name fix 2011-05-15 11:35:06 -04:00
Kenneth Reitz
d27e4bff1e ignore builds 2011-05-15 00:41:26 -04:00
Kenneth Reitz
27dd96cf3d config update 2011-05-15 00:41:00 -04:00
Kenneth Reitz
3e45e4ba51 added krthemes 2011-05-15 00:38:59 -04:00
Kenneth Reitz
0ee249247a updated history for v0.4.0 2011-05-15 00:00:44 -04:00
Kenneth Reitz
0f520a8c69 Merge branch 'release/0.4.0' into develop 2011-05-14 23:58:07 -04:00
Kenneth Reitz
aa33194606 Merge branch 'release/0.4.0' 2011-05-14 23:58:00 -04:00
Kenneth Reitz
51360e1ede version bump 2011-05-14 23:57:53 -04:00
Kenneth Reitz
af0febe8ad history update 2011-05-14 23:57:19 -04:00
Kenneth Reitz
155863bf71 readme update 2011-05-14 23:55:15 -04:00
Kenneth Reitz
3cec61481c ignore http errors in redirect loops 2011-05-14 23:22:26 -04:00
Kenneth Reitz
5708fe9565 python 2.5 fix 2011-05-14 23:21:58 -04:00
Kenneth Reitz
c40deefb8a cleanup mess 2011-05-14 23:03:31 -04:00
Kenneth Reitz
cd8ce63a3b Custom RedirectHandler and .history support.
Closes #23
2011-05-14 23:01:13 -04:00
Kenneth Reitz
35beda21b8 pass things into the dict 2011-05-14 22:02:14 -04:00
Kenneth Reitz
85b8b2bd44 CaseInsensitiveDict for Headers 2011-05-14 22:02:07 -04:00
Kenneth Reitz
29aa7736ca Headers: Default to None 2011-05-14 21:52:33 -04:00
Kenneth Reitz
e8ff405fce remote async __all__ 2011-05-14 21:50:41 -04:00
Kenneth Reitz
5172d24e66 New CaseInsensitiveDict 2011-05-14 21:50:25 -04:00
Kenneth Reitz
dfe7d877eb date header on history 2011-05-14 14:46:47 -04:00
38 changed files with 2155 additions and 584 deletions

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ coverage.xml
nosetests.xml
pylint.txt
*.pyc
docs/_build
toy.py

View File

@@ -18,4 +18,11 @@ Patches and Suggestions
- Rob Madole
- Aram Dulyan
- Johannes Gorset
- 村山めがね (Megan Emurayama)
- 村山めがね (Megane Murayama)
- James Rowe
- Daniel Schauenberg
- Zbigniew Siciarz
- Daniele Tricoli 'Eriol'
- Richard Boulton
- Miguel Olivares <miguel@moliware.com>
- Alberto Paro

View File

@@ -1,8 +1,37 @@
History
-------
0.3.4
+++++
0.5.0 (2011-06-21)
++++++++++++++++++
* PATCH Support
* Support for Proxies
* HTTPBin Test Suite
* Redirect Fixes
* settings.verbose stream writing
* Querystrings for all methods
* URLErrors (Connection Refused, Timeout, Invalid URLs) are treated as explicity raised
``r.requests.get('hwe://blah'); r.raise_for_status()``
0.4.1 (2011-05-22)
++++++++++++++++++
* Improved Redirection Handling
* New 'allow_redirects' param for following non-GET/HEAD Redirects
* Settings module refactoring
0.4.0 (2011-05-15)
++++++++++++++++++
* Response.history: list of redirected responses
* Case-Insensitive Header Dictionaries!
* Unicode URLs
0.3.4 (2011-05-14)
++++++++++++++++++
* Urllib2 HTTPAuthentication Recursion fix (Basic/Digest)
* Internal Refactor

View File

@@ -1,5 +1,5 @@
Requests: The Simple (e.g. usable) HTTP Module
==============================================
Requests: HTTP for Humans
=========================
Most existing Python modules for dealing HTTP requests are insane. I have to look up *everything* that I want to do. Most of my worst Python experiences are a result of the various built-in HTTP libraries (yes, even worse than Logging).
@@ -10,11 +10,16 @@ Really simple.
Features
--------
- Extremely simple GET, HEAD, POST, PUT, DELETE Requests
- Extremely simple HEAD, GET, POST, PUT, PATCH, DELETE Requests
+ Simple HTTP Header Request Attachment
+ Simple Data/Params Request Attachment
+ Simple Multipart File Uploads
+ CookieJar Support
+ Redirection History
+ Proxy Support
+ Redirection Recursion Urllib Fix
+ Auto Decompression of GZipped Content
+ Unicode URL Support
- Simple Authentication
+ Simple URL + HTTP Auth Registry
@@ -31,15 +36,14 @@ It couldn't be simpler. ::
HTTPS? Basic Authentication? ::
>>> r = requests.get('https://convore.com/api/account/verify.json')
>>> r = requests.get('https://httpbin.ep.ip/basic-auth/user/pass')
>>> r.status_code
401
Uh oh, we're not authorized! Let's add authentication. ::
>>> conv_auth = ('requeststest', 'requeststest')
>>> r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
>>> r = requests.get(https://httpbin.ep.ip/basic-auth/user/pass', auth=('user', 'pass'))
>>> r.status_code
200
@@ -48,7 +52,7 @@ Uh oh, we're not authorized! Let's add authentication. ::
'application/json'
>>> r.content
'{"username": "requeststest", "url": "/users/requeststest/", "id": "9408", "img": "censored-long-url"}'
'{"authenticated": true, "user": "user"}'
@@ -62,24 +66,28 @@ All request functions return a Response object (see below).
If a {filename: fileobject} dictionary is passed in (files=...), a multipart_encode upload will be performed.
If CookieJar object is is passed in (cookies=...), the cookies will be sent with the request.
GET Requests
>>> requests.get(url, params={}, headers={}, cookies=None, auth=None)
<Response [200]>
HEAD Requests
>>> requests.head(url, params={}, headers={}, cookies=None, auth=None)
>>> requests.head(url, params={}, headers={}, cookies=None, auth=None, timeout=None, proxies={})
<Response [200]>
PUT Requests
>>> requests.put(url, data='', headers={}, files={}, cookies=None, auth=None)
GET Requests
>>> requests.get(url, params={}, headers={}, cookies=None, auth=None, timeout=None, proxies={})
<Response [200]>
POST Requests
>>> requests.post(url, data={}, headers={}, files={}, cookies=None, auth=None)
>>> requests.post(url, data={}, headers={}, files={}, cookies=None, auth=None, timeout=None, allow_redirects=False, params{}, proxies={})
<Response [200]>
PUT Requests
>>> requests.put(url, data={}, headers={}, files={}, cookies=None, auth=None, timeout=None, allow_redirects=False, params{}, proxies={})
<Response [200]>
PATCH Requests
>>> requests.post(url, data={}, headers={}, files={}, cookies=None, auth=None, timeout=None, allow_redirects=False, params{}, proxies={})
<Response [200]>
DELETE Requests
>>> requests.delete(url, params={}, headers={}, cookies=None, auth=None)
>>> requests.delete(url, params={}, headers={}, cookies=None, auth=None, timeout=None, allow_redirects=False, params{}, proxies={})
<Response [200]>
@@ -89,10 +97,13 @@ If CookieJar object is is passed in (cookies=...), the cookies will be sent with
(Integer) Received HTTP Status Code Response
Response.headers
(Dictionary) Received HTTP Response Headers
((CaseInsensitive) Dictionary) Received HTTP Response Headers.
Response.content
(Bytes) Received Content
(Bytes) Received Content.
Response.history
(List of Responses) Redirection History.
Response.url
(String) URL of response. Useful for detecting redirects.
@@ -143,10 +154,7 @@ If you'd like to contribute, simply fork `the repository`_, commit your changes
Roadmap
-------
- Sphinx Documentation (http://code.kennethreitz.com/requests/)
- Exhaustive unit tests
- Get rid of Poster (gets really nasty in py3.x)
- Python 3.x Support
- Sphinx Documentation
.. _`the repository`: http://github.com/kennethreitz/requests
.. _AUTHORS: http://github.com/kennethreitz/requests/blob/master/AUTHORS

22
docs/_templates/sidebarintro.html vendored Normal file
View File

@@ -0,0 +1,22 @@
<h3>About Requests</h3>
<p>
Requests is an elegant and simple HTTP library for Python, built for
human beings. You are currently looking at the documentation of the
development release.
</p>
<h3>Feedback</h3>
<p>
Feedback is greatly appreciated. If you have any questions, comments,
random praise, or anonymous threats, <a href="mailto:me@kennethreitz.com">
shoot me an email</a>.
</p>
<h3>Useful Links</h3>
<ul>
<li><a href="http://python-requests.org/">The Requests Website</a></li>
<li><a href="http://pypi.python.org/pypi/requests">Requests @ PyPI</a></li>
<li><a href="http://github.com/kennethreitz/requests">Requests @ GitHub</a></li>
<li><a href="http://github.com/kennethreitz/requests/issues">Issue Tracker</a></li>
</ul>

6
docs/_templates/sidebarlogo.html vendored Normal file
View File

@@ -0,0 +1,6 @@
<h3><a href="http://python-requests.org">About Requests</a></h3>
<p>
Requests is an elegant and simple HTTP library for Python, built for
human beings. You are currently looking at the documentation of the
development release.
</p>

3
docs/_themes/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.pyc
*.pyo
.DS_Store

45
docs/_themes/LICENSE vendored Normal file
View File

@@ -0,0 +1,45 @@
Modifications:
Copyright (c) 2011 Kenneth Reitz.
Original Project:
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

25
docs/_themes/README.rst vendored Normal file
View File

@@ -0,0 +1,25 @@
krTheme Sphinx Style
====================
This repository contains sphinx styles Kenneth Reitz uses in most of
his projects. It is a drivative of Mitsuhiko's themes for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py: ::
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
**kr**
the standard flask documentation theme for large projects
**kr_small**
small one-page theme. Intended to be used by very small addon libraries.

86
docs/_themes/flask_theme_support.py vendored Normal file
View File

@@ -0,0 +1,86 @@
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}

47
docs/_themes/kr/layout.html vendored Normal file
View File

@@ -0,0 +1,47 @@
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<link media="only screen and (max-device-width: 480px)" href="{{
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
{% endblock %}
{%- block relbar2 %}{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
</div>
<a href="https://github.com/kennethreitz/requests">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" />
</a>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-8742933-11']);
_gaq.push(['_setDomainName', 'none']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript">
(function() {
var t = document.createElement('script');
t.type = 'text/javascript';
t.async = true;
t.id = 'gauges-tracker';
t.setAttribute('data-site-id',
'4ddc27f6613f5d186d000007');
t.src = '//secure.gaug.es/track.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(t, s);
})();
</script>
{%- endblock %}

19
docs/_themes/kr/relations.html vendored Normal file
View File

@@ -0,0 +1,19 @@
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>

463
docs/_themes/kr/static/flasky.css_t vendored Normal file
View File

@@ -0,0 +1,463 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro';
font-size: 17px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}
@media screen and (max-width: 600px) {
div.sphinxsidebar {
display: none;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
.document {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
}
/* scrollbars */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-button:start:decrement,
::-webkit-scrollbar-button:end:increment {
display: block;
height: 10px;
}
::-webkit-scrollbar-button:vertical:increment {
background-color: #fff;
}
::-webkit-scrollbar-track-piece {
background-color: #eee;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:vertical {
height: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:horizontal {
width: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}

70
docs/_themes/kr/static/small_flask.css vendored Normal file
View File

@@ -0,0 +1,70 @@
/*
* small_flask.css_t
* ~~~~~~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}

7
docs/_themes/kr/theme.conf vendored Normal file
View File

@@ -0,0 +1,7 @@
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
touch_icon =

22
docs/_themes/kr_small/layout.html vendored Normal file
View File

@@ -0,0 +1,22 @@
{% extends "basic/layout.html" %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{% block footer %}
{% if pagename == 'index' %}
</div>
{% endif %}
{% endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}
{% if theme_github_fork %}
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
{% endif %}
{% endblock %}
{% block sidebar1 %}{% endblock %}
{% block sidebar2 %}{% endblock %}

View File

@@ -0,0 +1,287 @@
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- flasky theme based on nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
color: #000;
background: white;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 40px auto 0 auto;
width: 700px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
text-align: right;
color: #888;
padding: 10px;
font-size: 14px;
width: 650px;
margin: 0 auto 40px auto;
}
div.footer a {
color: #888;
text-decoration: underline;
}
div.related {
line-height: 32px;
color: #888;
}
div.related ul {
padding: 0 0 0 10px;
}
div.related a {
color: #444;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body {
padding-bottom: 40px; /* saved for footer */
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: white;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight{
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.85em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td {
padding: 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
pre {
padding: 0;
margin: 15px -30px;
padding: 8px;
line-height: 1.3em;
padding: 7px 30px;
background: #eee;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
dl pre {
margin-left: -60px;
padding-left: 60px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
}
a:hover tt {
background: #EEE;
}

10
docs/_themes/kr_small/theme.conf vendored Normal file
View File

@@ -0,0 +1,10 @@
[theme]
inherit = basic
stylesheet = flasky.css
nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
github_fork = ''

68
docs/api.rst Normal file
View File

@@ -0,0 +1,68 @@
.. _api:
API
===
.. module:: requests
This part of the documentation covers all the interfaces of Requests. For
parts where Requests depends on external libraries, we document the most
important right here and provide links to the canonical documentation.
Main Interface
--------------
All of Request's functionality can be accessed by these 5 methods. They
all return a :class:`Response <models.Response>` object.
.. autofunction:: head
.. autofunction:: get
.. autofunction:: post
.. autofunction:: put
.. autofunction:: patch
.. autofunction:: delete
-----------
.. autoclass:: requests.models.Response
:inherited-members:
Exceptions
----------
.. autoexception:: HTTPError
.. autoexception:: RequestException
.. autoexception:: requests.models.AuthenticationError
.. autoexception:: requests.models.URLRequired
.. autoexception:: requests.models.InvalidMethod
Internals
---------
These items are an internal component to Requests, and should never be
seen by the end user (developer). This part of the API documentation
exists for those who are extending the functionality of Requests.
Functions
~~~~~~~~~
.. autofunction:: request
Classes
~~~~~~~
.. autoclass:: requests.models.Request
:inherited-members:
Structures
~~~~~~~~~~
.. autoclass:: requests.structures.CaseInsensitiveDict
:inherited-members:

View File

@@ -16,7 +16,9 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))
import requests
from requests import __version__
# -- General configuration -----------------------------------------------------
@@ -41,14 +43,14 @@ master_doc = 'index'
# General information about the project.
project = u'Requests'
copyright = u'2011, Kenneth Reitz'
copyright = u'2011. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.3.3'
version = __version__
# The full version, including alpha/beta/rc tags.
release = version
@@ -81,7 +83,7 @@ exclude_patterns = ['_build']
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = 'flask_theme_support.FlaskyStyle'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
@@ -112,6 +114,7 @@ html_theme = 'default'
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
@@ -131,7 +134,12 @@ html_static_path = ['_static']
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
@@ -147,10 +155,10 @@ html_static_path = ['_static']
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
html_show_sphinx = False
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
@@ -230,3 +238,7 @@ texinfo_documents = [
# Documents to append as an appendix to all manuals.
texinfo_appendices = []
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'kr'

0
docs/dev/internals.rst Normal file
View File

View File

@@ -3,18 +3,87 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Requests's documentation!
====================================
Requests: HTTP for Humans
=========================
Contents:
Release v\ |version|. (:ref:`Installation <install>`)
Requests is an :ref:`ISC Licensed <isc>` HTTP library, written in Python, for human beings.
Most existing Python modules for sending HTTP requests are extremely verbose
and cumbersome. Python's builtin **urllib2** module provides most of
the HTTP capabilities you should need, but the api is thoroughly **broken**.
It requires an *enormous* amount of work (even method overrides) to perform the simplest of tasks.
Things shouldnt be this way. Not in Python.
::
>>> r = requests.get('https://api.github.com', auth=('user', 'pass'))
>>> r.status_code
200
>>> r.headers['content-type']
'application/json'
See `the same code, without Requests <https://gist.github.com/973705>`_.
Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
**PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
multipart files, and parameters with simple Python dictionaries, and access the
response data in the same way. It's powered by :py:class:`urllib2`, but it does
all the hard work and crazy hacks for you.
Testimonals
-----------
`Twitter, Inc <http://twitter.com>`_ uses Requests internally.
**Daniel Greenfeld**
Nuked a 1200 LOC spaghetti code library with 10 lines of code thanks to @kennethreitz's request library. Today has been AWESOME.
**Kenny Meyers**
Python HTTP: When in doubt, or when not in doubt, use Requests. Beautiful, simple, Pythonic.
**Rich Leland**
Requests is awesome. That is all.
**Steve Pike**
I can never remember how to do it the regular way. ``import requests; requests.get()`` is just so easy!
User Guide
----------
This part of the documentation, which is mostly prose, begins with some background information about Requests, then focuses on step-by-step instructions for getting the most out of Requests.
.. toctree::
:maxdepth: 2
Indices and tables
==================
user/intro
user/install
.. user/quickstart
user/advanced
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
API Documentation
-----------------
If you are looking for information on a specific function, class or method,
this part of the documentation is for you.
.. toctree::
:maxdepth: 2
api
Developer Guide
---------------
If you want to contribute to the project, this part of the documentation is for
you.
.. toctree::
:maxdepth: 2
dev/internals

0
docs/user/advanced.rst Normal file
View File

53
docs/user/install.rst Normal file
View File

@@ -0,0 +1,53 @@
.. _install:
Installation
============
This part of the documentation covers the installation of Requests. The first step to using any software package is getting it properly installed.
Distribute & Pip
----------------
Installing requests is simple with `pip <http://www.pip-installer.org/>`_::
$ pip install requests
or, with `easy_install <http://pypi.python.org/pypi/setuptools>`_::
$ easy_install install requests
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/index.html#pip-compared-to-easy-install>`_.
Cheeseshop Mirror
-----------------
If the Cheeseshop is down, you can also install Requests from Kenneth Reitz's personal `Cheeseshop mirror <pip.kreitz.co/>`_::
$ pip install -i http://pip.kreitz.co/simple requests
Get the Code
------------
Requsts is actively developed on GitHub, where the code is
`always available <https://github.com/kennethreitz/requests>`_.
You can either clone the public repository::
git clone git://github.com/kennethreitz/requests.git
Download the `tarball <https://github.com/kennethreitz/requests/tarball/master>`_::
$ curl -O https://github.com/kennethreitz/requests/tarball/master
Or, download the `zipball <https://github.com/kennethreitz/requests/zipball/master>`_::
$ curl -O https://github.com/kennethreitz/requests/zipball/master
Once you have a copy of the source, you can embed it in your Python package, or install it into your site-packages easily::
$ python setup.py install

55
docs/user/intro.rst Normal file
View File

@@ -0,0 +1,55 @@
Introduction
============
Philosophy
----------
Requests was developed with a few :pep:`20` idioms in mind.
#. Beautiful is better than ugly.
#. Explicit is better than implicit.
#. Simple is better than complex.
#. Complex is better than complicated.
#. Readability counts.
All contributions to Requests should keep these important rules in mind.
ISC License
-----------
A large number of open source projects you find today are `GPL Licensed`_. While the GPL has its time and place, it should most certainly not be your go-to license for your next open source project.
A project that is released as GPL cannot be used in any commercial product without the product itself also being offered as open source. The MIT, BSD, ISC, and Apache2 licenses are great alternatives to the GPL that allow your open-source software to be used freely in proprietary, closed-source software.
Requests is released under terms of `The ISC License`_.
.. _`GPL Licensed`: http://www.opensource.org/licenses/gpl-license.php
.. _`The ISC License`: http://www.opensource.org/licenses/isc-license
Requests License
----------------
Copyright (c) 2011, Kenneth Reitz
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Python Interpreters
-------------------
At this time, the following Python platforms are officially supported:
* cPython 2.5
* cPython 2.6
* cPython 2.7
* PyPy-c 1.4
* PyPy-c 1.5
Support for Python 3.x is planned.

84
docs/user/quickstart.rst Normal file
View File

@@ -0,0 +1,84 @@
Feature Overview
================
Requests is designed to solve a 90% use case — making simple requests. While most
HTTP libraries are extremely extensible, they often attempt to support the entire HTTP Spec.
This often leads to extremely messy and cumbersome APIs, as is the case with urllib2. Requests abandons support for edge-cases, and focuses on the essentials.
.. _features:
Requests Can:
-------------
- Make **GET**, **POST**, **PUT**, **DELETE**, and **HEAD** requests.
- Handle HTTP and HTTPS Requests
- Add Request headers (with a simple dictionary)
- URLEncode your Form Data (with a simple dictionary)
- Add Multi-part File Uploads (with a simple dictionary)
- Handle CookieJars (with a single parameter)
- Add HTTP Authentication (with a single parameter)
- Handle redirects (with history)
- Automatically decompress GZip'd responses
- Support Unicode URLs
- Gracefully timeout
- Interface with Eventlet & Gevent
Requests Can't:
---------------
- Handle Caching
- Handle Keep-Alives
Quickstart
==========
GET Request
-----------
Adding Parameters
-----------------
Adding Headers
--------------
HTTP Basic Auth
---------------
Tracking Redirects
------------------
HTTP POST (Form Data)
---------------------
HTTP POST (Binary Data)
-----------------------
HTTP POST (Multipart Files)
---------------------------
HTTP PUT
--------
HTTP DELETE
-----------
HTTP HEAD
---------

View File

@@ -1,31 +1,4 @@
# -*- coding: utf-8 -*-
import inspect
import packages
from core import *
from core import __version__
timeout = None
class settings:
"""Context manager for settings."""
cache = {}
def __init__(self, timeout):
self.module = inspect.getmodule(self)
# Cache settings
self.cache['timeout'] = self.module.timeout
self.module.timeout = timeout
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
# Restore settings
for key in self.cache:
setattr(self.module, key, self.cache[key])

View File

@@ -11,105 +11,174 @@ This module impliments the Requests API.
"""
import requests
import config
from .models import Request, Response, AuthManager, AuthObject, auth_manager
__all__ = ('request', 'get', 'head', 'post', 'put', 'delete')
__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):
def request(method, url, **kwargs):
"""Sends a `method` request. Returns :class:`Response` object.
"""Constructs and sends a :class:`Request <models.Request>`. Returns :class:`Response <models.Response>` object.
:param method: method for the new :class:`Request` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET/HEAD/DELETE Parameters to send with the :class:`Request`.
:param data: (optional) Bytes/Dictionary of PUT/POST Data to send with the :class:`Request`.
: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) 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.
"""
data = kwargs.pop('data', dict()) or kwargs.pop('params', dict())
r = Request(method=method, url=url, data=data, headers=kwargs.pop('headers', {}),
cookiejar=kwargs.pop('cookies', None), files=kwargs.pop('files', None),
auth=kwargs.pop('auth', auth_manager.get_auth(url)),
timeout=kwargs.pop('timeout', requests.timeout))
r = Request(
method = method,
url = url,
data = data,
params = params,
headers = headers,
cookiejar = cookies,
files = files,
auth = auth or auth_manager.get_auth(url),
timeout = timeout or config.settings.timeout,
allow_redirects = allow_redirects,
proxies = proxies
)
r.send()
return r.response
def get(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
def get(url,
params=None, headers=None, cookies=None, auth=None, timeout=None,
proxies=None):
"""Sends a GET request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('GET', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
return request('GET', url,
params=params, headers=headers, cookies=cookies, auth=auth,
timeout=timeout, proxies=proxies)
def head(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
def head(url,
params=None, headers=None, cookies=None, auth=None, timeout=None,
proxies=None):
"""Sends a HEAD request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of GET Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
:param auth: (optional) AuthObject to enable Basic HTTP Auth.
:param timeout: (optional) Float describing the timeout of the request.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('HEAD', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
return request('HEAD', url,
params=params, headers=headers, cookies=cookies, auth=auth,
timeout=timeout, proxies=proxies)
def post(url, data={}, headers={}, files=None, cookies=None, auth=None, **kwargs):
def post(url,
data='', headers=None, files=None, cookies=None, auth=None, timeout=None,
allow_redirects=False, params=None, proxies=None):
"""Sends a POST request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary of POST data to send with 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 sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
: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 redirect following is allowed.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('POST', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth, **kwargs)
return request('POST', url,
params=params, data=data, headers=headers, files=files,
cookies=cookies, auth=auth, timeout=timeout,
allow_redirects=allow_redirects, proxies=proxies)
def put(url, data='', headers={}, files={}, cookies=None, auth=None, **kwargs):
def put(url, data='', headers=None, files=None, cookies=None, auth=None,
timeout=None, allow_redirects=False, params=None, proxies=None):
"""Sends a PUT request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Bytes of PUT Data to send with 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 sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
: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 redirect following is allowed.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('PUT', url, data=data, headers=headers, files=files, cookies=cookies, auth=auth, **kwargs)
return request('PUT', url,
params=params, data=data, headers=headers, files=files,
cookies=cookies, auth=auth, timeout=timeout,
allow_redirects=allow_redirects, proxies=proxies)
def delete(url, params={}, headers={}, cookies=None, auth=None, **kwargs):
def patch(url, data='', headers=None, files=None, cookies=None, auth=None,
timeout=None, allow_redirects=False, params=None, proxies=None):
"""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 headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
: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 redirect following is allowed.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('PATCH', url,
params=params, data=data, headers=headers, files=files,
cookies=cookies, auth=auth, timeout=timeout,
allow_redirects=allow_redirects, proxies=proxies)
def delete(url,
params=None, headers=None, cookies=None, auth=None, timeout=None,
allow_redirects=False, proxies=None):
"""Sends a DELETE request. Returns :class:`Response` object.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary of DELETE Parameters to send with the :class:`Request`.
:param params: (optional) Dictionary of parameters, or bytes, to be sent in the query string for the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to sent with the :class:`Request`.
:param cookies: (optional) CookieJar object to send with the :class:`Request`.
: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 redirect following is allowed.
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
"""
return request('DELETE', url, params=params, headers=headers, cookies=cookies, auth=auth, **kwargs)
return request('DELETE', url,
params=params, headers=headers, cookies=cookies, auth=auth,
timeout=timeout, allow_redirects=allow_redirects, proxies=proxies)

View File

@@ -39,10 +39,3 @@ if not 'eventlet' in locals():
from .core import *
__all__ = [
'Request', 'Response', 'request', 'get', 'head', 'post', 'put', 'delete',
'auth_manager', 'AuthObject','RequestException', 'AuthenticationError',
'URLRequired', 'InvalidMethod', 'HTTPError'
]

56
requests/config.py Normal file
View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
"""
requests.config
~~~~~~~~~~~~~~~
This module provides the Requests settings feature set.
"""
class Settings(object):
_singleton = {}
# attributes with defaults
__attrs__ = ('timeout', 'verbose')
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()

View File

@@ -12,12 +12,14 @@ This module implements the main Requests system.
"""
__title__ = 'requests'
__version__ = '0.3.4'
__build__ = 0x000304
__version__ = '0.5.0'
__build__ = 0x000500
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2011 Kenneth Reitz'
from .models import HTTPError, auth_manager
from .api import *
from models import HTTPError, auth_manager
from api import *
from exceptions import *
from config import settings

23
requests/exceptions.py Normal file
View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
"""
requests.exceptions
~~~~~~~~~~~~~~~
"""
class RequestException(Exception):
"""There was an ambiguous exception that occured while handling your
request."""
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."""

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
"""
requests.system
requests.models
~~~~~~~~~~~~~~~
"""
import requests
import urllib
import urllib2
import socket
@@ -14,55 +13,71 @@ import zlib
from urllib2 import HTTPError
from urlparse import urlparse
from datetime import datetime
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPDigestAuthHandler
from .config import settings
from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPDigestAuthHandler, HTTPRedirectHandler
from .structures import CaseInsensitiveDict
from .packages.poster.encode import multipart_encode
from .packages.poster.streaminghttp import register_openers, get_handlers
from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod
REDIRECT_STATI = (301, 302, 303, 307)
class Request(object):
"""The :class:`Request` object. It carries out all functionality of
"""The :class:`Request <models.Request>` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""
_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')
_METHODS = ('GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH')
def __init__(self, url=None, headers=dict(), files=None, method=None,
data=dict(), auth=None, cookiejar=None, timeout=None):
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):
socket.setdefaulttimeout(timeout)
#: Request URL.
self.url = url
#: Dictonary of HTTP Headers to attach to the :class:`Request <models.Request>`.
self.headers = headers
#: Dictionary of files to multipart upload (``{filename: content}``).
self.files = files
#: HTTP Method to use. Available: GET, HEAD, PUT, POST, DELETE.
self.method = method
self.data = {}
# self.data = {}
if hasattr(data, 'items'):
for (k, v) in data.items():
self.data.update({
k.encode('utf-8') if isinstance(k, unicode) else k:
v.encode('utf-8') if isinstance(v, unicode) else v
})
# url encode data if it's a dict
if hasattr(data, 'items'):
self._enc_data = urllib.urlencode(self.data)
else:
self._enc_data = data
#: Dictionary or byte of request body data to attach to the
#: :class:`Request <models.Request>`.
self.data = None
#: Dictionary or byte of querystring data to attach to the
#: :class:`Request <models.Request>`.
self.params = None
#: True if :class:`Request <models.Request>` is part of a redirect chain (disables history
#: and HTTPError storage).
self.redirect = redirect
#: Set to True if full redirects are allowed (e.g. re-POST-ing of data at new ``Location``)
self.allow_redirects = allow_redirects
# Dictionary mapping protocol to the URL of the proxy (e.g. {'http': 'foo.bar:3128'})
self.proxies = proxies
self.data, self._enc_data = self._encode_params(data)
self.params, self._enc_params = self._encode_params(params)
#: :class:`Response <models.Response>` instance, containing
#: content and metadata of HTTP Response, once :attr:`sent <send>`.
self.response = Response()
if isinstance(auth, (list, tuple)):
auth = AuthObject(*auth)
if not auth:
auth = auth_manager.get_auth(self.url)
#: :class:`AuthObject` to attach to :class:`Request <models.Request>`.
self.auth = auth
#: CookieJar to attach to :class:`Request <models.Request>`.
self.cookiejar = cookiejar
#: True if Request has been sent.
self.sent = False
@@ -101,10 +116,17 @@ class Request(object):
_handlers.append(self.auth.handler)
if self.proxies:
_handlers.append(urllib2.ProxyHandler(self.proxies))
_handlers.append(HTTPRedirectHandler)
if not _handlers:
return urllib2.urlopen
_handlers.extend(get_handlers())
if self.data or self.files:
_handlers.extend(get_handlers())
opener = urllib2.build_opener(*_handlers)
if self.headers:
@@ -118,40 +140,111 @@ class Request(object):
return opener.open
def _build_response(self, resp):
"""Build internal Response object from given response."""
if isinstance(resp, HTTPError):
# print resp.__dict__
pass
"""Build internal :class:`Response <models.Response>` object from given response."""
self.response.status_code = getattr(resp, 'code', None)
def build(resp):
try:
self.response.headers = getattr(resp.info(), 'dict', None)
self.response.content = resp.read()
except AttributeError, why:
pass
response = Response()
response.status_code = getattr(resp, 'code', None)
if self.response.headers.get('content-encoding', None) == 'gzip':
try:
self.response.content = zlib.decompress(self.response.content, 16+zlib.MAX_WBITS)
except zlib.error:
response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None))
response.content = resp.read()
except AttributeError:
pass
self.response.url = getattr(resp, 'url', None)
if response.headers['content-encoding'] == 'gzip':
try:
response.content = zlib.decompress(response.content, 16+zlib.MAX_WBITS)
except zlib.error:
pass
# TODO: Support deflate
response.url = getattr(resp, 'url', None)
return response
history = []
r = build(resp)
if r.status_code in REDIRECT_STATI:
self.redirect = True
if self.redirect:
while (
('location' in r.headers) and
((self.method in ('GET', 'HEAD')) or
(r.status_code is 303) or
(self.allow_redirects))
):
history.append(r)
url = r.headers['location']
# Facilitate non-RFC2616-compliant 'location' headers
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
if not urlparse(url).netloc:
parent_url_components = urlparse(self.url)
url = '%s://%s/%s' % (parent_url_components.scheme, parent_url_components.netloc, url)
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
if r.status_code is 303:
method = 'GET'
else:
method = self.method
request = Request(
url, self.headers, self.files, method,
self.data, self.params, self.auth, self.cookiejar,
redirect=True
)
request.send()
r = request.response
r.history = history
self.response = r
@staticmethod
def _build_url(url, data=None):
"""Build URLs."""
def _encode_params(data):
"""Encode parameters in a piece of data.
if urlparse(url).query:
return '%s&%s' % (url, data)
If the data supplied is a dictionary, encodes each parameter in it, and
returns the dictionary of encoded parameters, and a urlencoded version
of that.
Otherwise, assumes the data is already encoded appropriately, and
returns it twice.
"""
if hasattr(data, 'items'):
result = {}
for (k, v) in data.items():
result[k.encode('utf-8') if isinstance(k, unicode) else k] \
= v.encode('utf-8') if isinstance(v, unicode) else v
return result, urllib.urlencode(result)
else:
if data:
return '%s?%s' % (url, data)
return data, data
def _build_url(self):
"""Build the actual URL to use"""
if self._enc_params:
if urlparse(self.url).query:
return '%s&%s' % (self.url, self._enc_params)
else:
return url
return '%s?%s' % (self.url, self._enc_params)
else:
return self.url
def send(self, anyway=False):
@@ -167,8 +260,16 @@ class Request(object):
self._checks()
success = False
# Logging
if settings.verbose:
settings.verbose.write('%s %s %s\n' % (
datetime.now().isoformat(), self.method, self.url
))
url = self._build_url()
if self.method in ('GET', 'HEAD', 'DELETE'):
req = _Request(self._build_url(self.url, self._enc_data), method=self.method)
req = _Request(url, method=self.method)
else:
if self.files:
@@ -178,10 +279,10 @@ class Request(object):
self.files.update(self.data)
datagen, headers = multipart_encode(self.files)
req = _Request(self.url, data=datagen, headers=headers, method=self.method)
req = _Request(url, data=datagen, headers=headers, method=self.method)
else:
req = _Request(self.url, data=self._enc_data, method=self.method)
req = _Request(url, data=self._enc_data, method=self.method)
if self.headers:
req.headers.update(self.headers)
@@ -195,9 +296,15 @@ class Request(object):
if self.cookiejar is not None:
self.cookiejar.extract_cookies(resp, req)
except urllib2.HTTPError, why:
except (urllib2.HTTPError, urllib2.URLError), why:
if hasattr(why, 'reason'):
if isinstance(why.reason, socket.timeout):
why = Timeout(why)
self._build_response(why)
self.response.error = why
if not self.redirect:
self.response.error = why
else:
self._build_response(resp)
self.response.ok = True
@@ -208,6 +315,7 @@ class Request(object):
self.sent = self.response.ok
return self.sent
@@ -217,19 +325,35 @@ class Request(object):
class Response(object):
"""The :class:`Request` object. All :class:`Request` objects contain a
:class:`Request.response <response>` attribute, which is an instance of
this class.
"""The core :class:`Response <models.Response>` object. All
:class:`Request <models.Request>` objects contain a
:class:`response <models.Response>` attribute, which is an instance
of this class.
"""
def __init__(self):
#: Raw content of the response, in bytes.
#: If ``content-encoding`` of response was set to ``gzip``, the
#: response data will be automatically deflated.
self.content = None
#: Integer Code of responded HTTP Status.
self.status_code = None
self.headers = dict()
#: Case-insensitive Dictionary of Response Headers.
#: For example, ``headers['content-encoding']`` will return the
#: value of a ``'Content-Encoding'`` response header.
self.headers = CaseInsensitiveDict()
#: Final URL location of Response.
self.url = None
#: True if no :attr:`error` occured.
self.ok = False
#: Resulting :class:`HTTPError` of request, if one occured.
self.error = None
#: True, if the response :attr:`content` is cached locally.
self.cached = False
#: A list of :class:`Response <models.Response>` objects from
#: the history of the Request. Any redirect responses will end
#: up here.
self.history = []
def __repr__(self):
@@ -237,22 +361,24 @@ class Response(object):
def __nonzero__(self):
"""Returns true if status_code is 'OK'."""
"""Returns true if :attr:`status_code` is 'OK'."""
return not self.error
def raise_for_status(self):
"""Raises stored HTTPError if one exists."""
"""Raises stored :class:`HTTPError`, if one occured."""
if self.error:
raise self.error
def read(self, *args):
"""Returns :attr:`content`. Used for file-like object compatiblity."""
return self.content
class AuthManager(object):
"""Authentication Manager."""
"""Requests Authentication Manager."""
def __new__(cls):
singleton = cls.__dict__.get('__singleton__')
@@ -420,16 +546,3 @@ class AuthObject(object):
self.handler = self._handlers.get(handler.lower(), urllib2.HTTPBasicAuthHandler)
else:
self.handler = handler
class RequestException(Exception):
"""There was an ambiguous exception that occured while handling your
request."""
class AuthenticationError(RequestException):
"""The authentication credentials provided were invalid."""
class URLRequired(RequestException):
"""A valid URL is required to make a request."""
class InvalidMethod(RequestException):
"""An inappropriate method was attempted."""

View File

@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
#-*- coding: utf-8 -*-
"""
requests.monkeys
@@ -26,9 +26,16 @@ class Request(urllib2.Request):
return urllib2.Request.get_method(self)
class HTTPRedirectHandler(urllib2.HTTPRedirectHandler):
def http_error_301(self, req, fp, code, msg, headers):
pass
http_error_302 = http_error_303 = http_error_307 = http_error_301
class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
# from mercurial
def __init__(self, *args, **kwargs):
urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
@@ -45,8 +52,10 @@ class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
if req is not self.retried_req:
self.retried_req = req
self.retried = 0
return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
self, auth_header, host, req, headers)
self, auth_header, host, req, headers
)

View File

@@ -1,360 +1,28 @@
# -*- coding: utf-8 -*-
# from: werkzeug
"""
requests.structures
~~~~~~~~~~~~~~~~~~~
class TypeConversionDict(dict):
"""Works like a regular dict but the :meth:`get` method can perform
type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
are subclasses of this class and provide the same feature.
"""
Datastructures that power Requests.
def get(self, key, default=None, type=None):
"""Return the default value if the requested data doesn't exist.
If `type` is provided and is a callable it should convert the value,
return it or raise a :exc:`ValueError` if that is not possible. In
this case the function will return the default as if the value was not
found:
"""
>>> d = TypeConversionDict(foo='42', bar='blub')
>>> d.get('foo', type=int)
42
>>> d.get('bar', -1, type=int)
-1
class CaseInsensitiveDict(dict):
"""Case-insensitive Dictionary for :class:`Response <models.Response>` Headers.
:param key: The key to be looked up.
:param default: The default value to be returned if the key can't
be looked up. If not further specified `None` is
returned.
:param type: A callable that is used to cast the value in the
:class:`MultiDict`. If a :exc:`ValueError` is raised
by this callable the default value is returned.
"""
try:
rv = self[key]
if type is not None:
rv = type(rv)
except (KeyError, ValueError):
rv = default
return rv
For example, ``headers['content-encoding']`` will return the
value of a ``'Content-Encoding'`` response header."""
def _lower_keys(self):
return map(str.lower, self.keys())
def __contains__(self, key):
return key.lower() in self._lower_keys()
# from: werkzeug
class MultiDict(TypeConversionDict):
"""A :class:`MultiDict` is a dictionary subclass customized to deal with
multiple values for the same key which is for example used by the parsing
functions in the wrappers. This is necessary because some HTML form
elements pass multiple values for the same key.
:class:`MultiDict` implements all standard dictionary methods.
Internally, it saves all values for a key as a list, but the standard dict
access methods will only return the first value for a key. If you want to
gain access to the other values, too, you have to use the `list` methods as
explained below.
Basic Usage:
>>> d = MultiDict([('a', 'b'), ('a', 'c')])
>>> d
MultiDict([('a', 'b'), ('a', 'c')])
>>> d['a']
'b'
>>> d.getlist('a')
['b', 'c']
>>> 'a' in d
True
It behaves like a normal dict thus all dict functions will only return the
first value when multiple values for one key are found.
From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
exceptions.
A :class:`MultiDict` can be constructed from an iterable of
``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
onwards some keyword parameters.
:param mapping: the initial value for the :class:`MultiDict`. Either a
regular dict, an iterable of ``(key, value)`` tuples
or `None`.
"""
# the key error this class raises. Because of circular dependencies
# with the http exception module this class is created at the end of
# this module.
KeyError = None
def __init__(self, mapping=None):
if isinstance(mapping, MultiDict):
dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
elif isinstance(mapping, dict):
tmp = {}
for key, value in mapping.iteritems():
if isinstance(value, (tuple, list)):
value = list(value)
else:
value = [value]
tmp[key] = value
dict.__init__(self, tmp)
else:
tmp = {}
for key, value in mapping or ():
tmp.setdefault(key, []).append(value)
dict.__init__(self, tmp)
def __getstate__(self):
return dict(self.lists())
def __setstate__(self, value):
dict.clear(self)
dict.update(self, value)
def __iter__(self):
return self.iterkeys()
def __getitem__(self, key):
"""Return the first data value for this key;
raises KeyError if not found.
:param key: The key to be looked up.
:raise KeyError: if the key does not exist.
"""
# We allow fall-through here, so values default to None
if key in self:
return dict.__getitem__(self, key)[0]
raise self.KeyError(key)
def __setitem__(self, key, value):
"""Like :meth:`add` but removes an existing key first.
:param key: the key for the value.
:param value: the value to set.
"""
dict.__setitem__(self, key, [value])
def add(self, key, value):
"""Adds a new value for the key.
.. versionadded:: 0.6
:param key: the key for the value.
:param value: the value to add.
"""
dict.setdefault(self, key, []).append(value)
def getlist(self, key, type=None):
"""Return the list of items for a given key. If that key is not in the
`MultiDict`, the return value will be an empty list. Just as `get`
`getlist` accepts a `type` parameter. All items will be converted
with the callable defined there.
:param key: The key to be looked up.
:param type: A callable that is used to cast the value in the
:class:`MultiDict`. If a :exc:`ValueError` is raised
by this callable the value will be removed from the list.
:return: a :class:`list` of all the values for the key.
"""
try:
rv = dict.__getitem__(self, key)
except KeyError:
return []
if type is None:
return list(rv)
result = []
for item in rv:
try:
result.append(type(item))
except ValueError:
pass
return result
def setlist(self, key, new_list):
"""Remove the old values for a key and add new ones. Note that the list
you pass the values in will be shallow-copied before it is inserted in
the dictionary.
>>> d = MultiDict()
>>> d.setlist('foo', ['1', '2'])
>>> d['foo']
'1'
>>> d.getlist('foo')
['1', '2']
:param key: The key for which the values are set.
:param new_list: An iterable with the new values for the key. Old values
are removed first.
"""
dict.__setitem__(self, key, list(new_list))
def setdefault(self, key, default=None):
"""Returns the value for the key if it is in the dict, otherwise it
returns `default` and sets that value for `key`.
:param key: The key to be looked up.
:param default: The default value to be returned if the key is not
in the dict. If not further specified it's `None`.
"""
if key not in self:
self[key] = default
else:
default = self[key]
return default
def setlistdefault(self, key, default_list=None):
"""Like `setdefault` but sets multiple values. The list returned
is not a copy, but the list that is actually used internally. This
means that you can put new values into the dict by appending items
to the list:
>>> d = MultiDict({"foo": 1})
>>> d.setlistdefault("foo").extend([2, 3])
>>> d.getlist("foo")
[1, 2, 3]
:param key: The key to be looked up.
:param default: An iterable of default values. It is either copied
(in case it was a list) or converted into a list
before returned.
:return: a :class:`list`
"""
if key not in self:
default_list = list(default_list or ())
dict.__setitem__(self, key, default_list)
else:
default_list = dict.__getitem__(self, key)
return default_list
def items(self, multi=False):
"""Return a list of ``(key, value)`` pairs.
:param multi: If set to `True` the list returned will have a
pair for each value of each key. Otherwise it
will only contain pairs for the first value of
each key.
:return: a :class:`list`
"""
return list(self.iteritems(multi))
def lists(self):
"""Return a list of ``(key, values)`` pairs, where values is the list of
all values associated with the key.
:return: a :class:`list`
"""
return list(self.iterlists())
def values(self):
"""Returns a list of the first value on every key's value list.
:return: a :class:`list`.
"""
return [self[key] for key in self.iterkeys()]
def listvalues(self):
"""Return a list of all values associated with a key. Zipping
:meth:`keys` and this is the same as calling :meth:`lists`:
>>> d = MultiDict({"foo": [1, 2, 3]})
>>> zip(d.keys(), d.listvalues()) == d.lists()
True
:return: a :class:`list`
"""
return list(self.iterlistvalues())
def iteritems(self, multi=False):
"""Like :meth:`items` but returns an iterator."""
for key, values in dict.iteritems(self):
if multi:
for value in values:
yield key, value
else:
yield key, values[0]
def iterlists(self):
"""Like :meth:`items` but returns an iterator."""
for key, values in dict.iteritems(self):
yield key, list(values)
def itervalues(self):
"""Like :meth:`values` but returns an iterator."""
for values in dict.itervalues(self):
yield values[0]
def iterlistvalues(self):
"""Like :meth:`listvalues` but returns an iterator."""
return dict.itervalues(self)
def copy(self):
"""Return a shallow copy of this object."""
return self.__class__(self)
def to_dict(self, flat=True):
"""Return the contents as regular dict. If `flat` is `True` the
returned dict will only have the first item present, if `flat` is
`False` all values will be returned as lists.
:param flat: If set to `False` the dict returned will have lists
with all the values in it. Otherwise it will only
contain the first value for each key.
:return: a :class:`dict`
"""
if flat:
return dict(self.iteritems())
return dict(self.lists())
def update(self, other_dict):
"""update() extends rather than replaces existing key lists."""
for key, value in iter_multi_items(other_dict):
MultiDict.add(self, key, value)
def pop(self, key, default=_missing):
"""Pop the first item for a list on the dict. Afterwards the
key is removed from the dict, so additional values are discarded:
>>> d = MultiDict({"foo": [1, 2, 3]})
>>> d.pop("foo")
1
>>> "foo" in d
False
:param key: the key to pop.
:param default: if provided the value to return if the key was
not in the dictionary.
"""
try:
return dict.pop(self, key)[0]
except KeyError, e:
if default is not _missing:
return default
raise self.KeyError(str(e))
def popitem(self):
"""Pop an item from the dict."""
try:
item = dict.popitem(self)
return (item[0], item[1][0])
except KeyError, e:
raise self.KeyError(str(e))
def poplist(self, key):
"""Pop the list for a key from the dict. If the key is not in the dict
an empty list is returned.
.. versionchanged:: 0.5
If the key does no longer exist a list is returned instead of
raising an error.
"""
return dict.pop(self, key, [])
def popitemlist(self):
"""Pop a ``(key, list)`` tuple from the dict."""
try:
return dict.popitem(self)
except KeyError, e:
raise self.KeyError(str(e))
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
return self.items()[self._lower_keys().index(key.lower())][1]

7
setup.py Normal file → Executable file
View File

@@ -5,7 +5,10 @@ import os
import sys
import requests
from distutils.core import setup
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
@@ -30,7 +33,7 @@ setup(
open('HISTORY.rst').read(),
author='Kenneth Reitz',
author_email='me@kennethreitz.com',
url='https://github.com/kennethreitz/requests',
url='http://python-requests.org',
packages= [
'requests',
'requests.packages',

View File

@@ -1,64 +1,133 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import with_statement
import unittest
import cookielib
import omnijson as json
import requests
HTTPBIN_URL = 'http://httpbin.org/'
HTTPSBIN_URL = 'https://httpbin.ep.io/'
# HTTPBIN_URL = 'http://staging.httpbin.org/'
# HTTPSBIN_URL = 'https://httpbin-staging.ep.io/'
def httpbin(*suffix):
"""Returns url for HTTPBIN resource."""
return HTTPBIN_URL + '/'.join(suffix)
def httpsbin(*suffix):
"""Returns url for HTTPSBIN resource."""
return HTTPSBIN_URL + '/'.join(suffix)
class RequestsTestSuite(unittest.TestCase):
"""Requests test cases."""
def setUp(self):
pass
def tearDown(self):
"""Teardown."""
pass
def test_invalid_url(self):
self.assertRaises(ValueError, requests.get, 'hiwpefhipowhefopw')
def test_HTTP_200_OK_GET(self):
r = requests.get('http://google.com')
r = requests.get(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_GET(self):
r = requests.get('https://google.com')
r = requests.get(httpsbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get('http://www.google.com/search', params={'q': 'test'}, headers=heads)
r = requests.get(httpbin('user-agent'), headers=heads)
assert heads['User-agent'] in r.content
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
heads = {'User-agent': 'Mozilla/5.0'}
r = requests.get('http://google.com/search?test=true', params={'q': 'test'}, headers=heads)
r = requests.get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
self.assertEqual(r.status_code, 200)
def test_user_agent_transfers(self):
"""Issue XX"""
heads = {'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'}
r = requests.get('http://whatsmyua.com', headers=heads);
heads = {
'User-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads);
self.assertTrue(heads['User-agent'] in r.content)
heads = {
'user-agent':
'Mozilla/5.0 (github.com/kennethreitz/requests)'
}
r = requests.get(httpbin('user-agent'), headers=heads);
self.assertTrue(heads['user-agent'] in r.content)
def test_HTTP_200_OK_HEAD(self):
r = requests.head('http://google.com')
r = requests.head(httpbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_HEAD(self):
r = requests.head('https://google.com')
r = requests.head(httpsbin('/'))
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_PUT(self):
r = requests.put(httpbin('put'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_PUT(self):
r = requests.put(httpsbin('put'))
self.assertEqual(r.status_code, 200)
def test_HTTP_200_OK_PATCH(self):
r = requests.patch(httpbin('patch'))
self.assertEqual(r.status_code, 200)
def test_HTTPS_200_OK_PATCH(self):
r = requests.patch(httpsbin('patch'))
self.assertEqual(r.status_code, 200)
def test_AUTH_HTTPS_200_OK_GET(self):
auth = ('requeststest', 'requeststest')
url = 'https://convore.com/api/account/verify.json'
auth = ('user', 'pass')
url = httpsbin('basic-auth', 'user', 'pass')
r = requests.get(url, auth=auth)
self.assertEqual(r.status_code, 200)
@@ -69,93 +138,190 @@ class RequestsTestSuite(unittest.TestCase):
# reset auto authentication
requests.auth_manager.empty()
def test_POSTBIN_GET_POST_FILES(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
url = httpbin('post')
post = requests.post(url).raise_for_status()
post = requests.post(bin.url, data={'some': 'data'})
self.assertEqual(post.status_code, 201)
post = requests.post(url, data={'some': 'data'})
self.assertEqual(post.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')})
self.assertEqual(post2.status_code, 201)
post2 = requests.post(url, files={'some': open('test_requests.py')})
self.assertEqual(post2.status_code, 200)
post3 = requests.post(url, data='[{"some": "json"}]')
self.assertEqual(post3.status_code, 200)
post3 = requests.post(bin.url, data='[{"some": "json"}]')
self.assertEqual(post.status_code, 201)
def test_POSTBIN_GET_POST_FILES_WITH_PARAMS(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')}, data={'some': 'data'})
self.assertEqual(post2.status_code, 201)
url = httpbin('post')
post = requests.post(url, files={'some': open('test_requests.py')}, data={'some': 'data'})
self.assertEqual(post.status_code, 200)
def test_POSTBIN_GET_POST_FILES_WITH_HEADERS(self):
bin = requests.post('http://www.postbin.org/')
self.assertEqual(bin.status_code, 200)
post2 = requests.post(bin.url, files={'some': open('test_requests.py')},
headers={'User-Agent': 'requests-tests'})
url = httpbin('post')
post2 = requests.post(url, files={'some': open('test_requests.py')},
headers = {'User-Agent': 'requests-tests'})
self.assertEqual(post2.status_code, 200)
self.assertEqual(post2.status_code, 201)
def test_nonzero_evaluation(self):
r = requests.get('http://google.com/some-404-url')
r = requests.get(httpbin('status', '500'))
self.assertEqual(bool(r), False)
r = requests.get('http://google.com/')
r = requests.get(httpbin('/'))
self.assertEqual(bool(r), True)
def test_request_ok_set(self):
r = requests.get('http://google.com/some-404-url')
r = requests.get(httpbin('status', '404'))
self.assertEqual(r.ok, False)
def test_status_raising(self):
r = requests.get('http://google.com/some-404-url')
r = requests.get(httpbin('status', '404'))
self.assertRaises(requests.HTTPError, r.raise_for_status)
r = requests.get('http://google.com/')
r = requests.get(httpbin('status', '200'))
self.assertFalse(r.error)
r.raise_for_status()
def test_cookie_jar(self):
"""
.. todo:: This really doesn't test to make sure the cookie is working
"""
jar = cookielib.CookieJar()
self.assertFalse(jar)
requests.get('http://google.com', cookies=jar)
url = httpbin('cookies', 'set', 'requests_cookie', 'awesome')
r = requests.get(url, cookies=jar)
self.assertTrue(jar)
cookie_found = False
for cookie in jar:
if cookie.name == 'requests_cookie':
self.assertEquals(cookie.value, 'awesome')
cookie_found = True
self.assertTrue(cookie_found)
r = requests.get(httpbin('cookies'), cookies=jar)
self.assertTrue('awesome' in r.content)
def test_decompress_gzip(self):
r = requests.get('http://api.stackoverflow.com/1.1/users/495995/top-answer-tags')
r = requests.get(httpbin('gzip'))
r.content.decode('ascii')
def test_autoauth(self):
conv_auth = ('requeststest', 'requeststest')
requests.auth_manager.add_auth('convore.com', conv_auth)
http_auth = ('user', 'pass')
requests.auth_manager.add_auth('httpbin.org', http_auth)
r = requests.get('https://convore.com/api/account/verify.json')
r = requests.get(httpbin('basic-auth', 'user', 'pass'))
self.assertEquals(r.status_code, 200)
def test_unicode_get(self):
requests.get('http://google.com', params={'foo': u'føø'})
requests.get('http://google.com', params={u'føø': u'føø'})
requests.get('http://google.com', params={'føø': 'føø'})
requests.get('http://google.com', params={'foo': u'foo'})
requests.get('http://google.com/ø', params={'foo': u'foo'})
url = httpbin('/')
requests.get(url, params={'foo': u'føø'})
requests.get(url, params={u'føø': u'føø'})
requests.get(url, params={'føø': 'føø'})
requests.get(url, params={'foo': u'foo'})
requests.get(httpbin('ø'), params={'foo': u'foo'})
def test_httpauth_recursion(self):
conv_auth = ('requeststest', 'bad_password')
http_auth = ('user', 'BADpass')
r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
r = requests.get(httpbin('basic-auth', 'user', 'pass'), auth=http_auth)
self.assertEquals(r.status_code, 401)
def test_settings(self):
def test():
r = requests.get(httpbin(''))
r.raise_for_status()
with requests.settings(timeout=0.0000001):
self.assertRaises(requests.Timeout, test)
with requests.settings(timeout=100):
requests.get(httpbin(''))
def test_urlencoded_post_data(self):
r = requests.post(httpbin('post'), data=dict(test='fooaowpeuf'))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, httpbin('post'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), dict(test='fooaowpeuf'))
self.assertEquals(rbody.get('data'), '')
def test_nonurlencoded_post_data(self):
r = requests.post(httpbin('post'), data='fooaowpeuf')
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, httpbin('post'))
rbody = json.loads(r.content)
# Body wasn't valid url encoded data, so the server returns None as
# "form" and the raw body as "data".
self.assertEquals(rbody.get('form'), None)
self.assertEquals(rbody.get('data'), 'fooaowpeuf')
def test_urlencoded_post_querystring(self):
r = requests.post(httpbin('post'), params=dict(test='fooaowpeuf'))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, httpbin('post?test=fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), {}) # No form supplied
self.assertEquals(rbody.get('data'), '')
def test_nonurlencoded_post_querystring(self):
r = requests.post(httpbin('post'), params='fooaowpeuf')
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, httpbin('post?fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), {}) # No form supplied
self.assertEquals(rbody.get('data'), '')
def test_urlencoded_post_query_and_data(self):
r = requests.post(httpbin('post'), params=dict(test='fooaowpeuf'),
data=dict(test2="foobar"))
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, httpbin('post?test=fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), dict(test2='foobar'))
self.assertEquals(rbody.get('data'), '')
def test_nonurlencoded_post_query_and_data(self):
r = requests.post(httpbin('post'), params='fooaowpeuf',
data="foobar")
self.assertEquals(r.status_code, 200)
self.assertEquals(r.headers['content-type'], 'application/json')
self.assertEquals(r.url, httpbin('post?fooaowpeuf'))
rbody = json.loads(r.content)
self.assertEquals(rbody.get('form'), None)
self.assertEquals(rbody.get('data'), 'foobar')
if __name__ == '__main__':
unittest.main()

View File

@@ -1,11 +1,8 @@
[tox]
envlist = py25,py26,py27,pypy
envlist = py25,py26,py27
[testenv]
commands=py.test --junitxml=junit-{envname}.xml
deps =
deps =
pytest
[testenv:pypy]
basepython=/usr/bin/pypy-c
omnijson