Merge branch 'master' into master

This commit is contained in:
2019-08-20 00:25:37 -04:00
committed by GitHub
28 changed files with 217 additions and 365 deletions
+1 -14
View File
@@ -3,31 +3,18 @@ language: python
install: "make"
# command to run tests
script:
- |
make test-readme
- make test-readme
- make ci
cache: pip
jobs:
include:
- stage: test
script:
- make test-readme
- make ci
python: '2.7'
- stage: test
script:
- make test-readme
- make ci
python: '3.5'
- stage: test
script:
- make test-readme
- make ci
python: '3.6'
- stage: test
script:
- make test-readme
- make ci
python: '3.7'
dist: xenial
- stage: coverage
+2
View File
@@ -191,3 +191,5 @@ Patches and Suggestions
- Antti Kaihola (`@akaihola <https://github.com/akaihola>`_)
- "Dull Bananas" <dull.bananas0@gmail.com> (`@dullbananas <https://github.com/dullbananas>`_)
- Alessio Izzo (`@aless10 <https://github.com/aless10>`_)
- Belavin Denis (`@luckydenis <https://github.com/luckydenis>`_)
- Dull Bananas <dull.bananas0@gmail.com> (`@dullbananas <https://github.com/dullbananas>`_)
+9 -9
View File
@@ -793,7 +793,7 @@ documentation](http://docs.python-requests.org/en/latest/community/release-proce
- Unicode URL improvements for Python 2.
- Re-order JSON param for backwards compat.
- Automatically defrag authentication schemes from host/pass URIs.
([\#2249](https://github.com/requests/requests/issues/2249))
([\#2249](https://github.com/psf/requests/issues/2249))
2.4.2 (2014-10-05)
------------------
@@ -801,26 +801,26 @@ documentation](http://docs.python-requests.org/en/latest/community/release-proce
**Improvements**
- FINALLY! Add json parameter for uploads!
([\#2258](https://github.com/requests/requests/pull/2258))
([\#2258](https://github.com/psf/requests/pull/2258))
- Support for bytestring URLs on Python 3.x
([\#2238](https://github.com/requests/requests/pull/2238))
([\#2238](https://github.com/psf/requests/pull/2238))
**Bugfixes**
- Avoid getting stuck in a loop
([\#2244](https://github.com/requests/requests/pull/2244))
([\#2244](https://github.com/psf/requests/pull/2244))
- Multiple calls to iter\* fail with unhelpful error.
([\#2240](https://github.com/requests/requests/issues/2240),
[\#2241](https://github.com/requests/requests/issues/2241))
([\#2240](https://github.com/psf/requests/issues/2240),
[\#2241](https://github.com/psf/requests/issues/2241))
**Documentation**
- Correct redirection introduction
([\#2245](https://github.com/requests/requests/pull/2245/))
([\#2245](https://github.com/psf/requests/pull/2245/))
- Added example of how to send multiple files in one request.
([\#2227](https://github.com/requests/requests/pull/2227/))
([\#2227](https://github.com/psf/requests/pull/2227/))
- Clarify how to pass a custom set of CAs
([\#2248](https://github.com/requests/requests/pull/2248/))
([\#2248](https://github.com/psf/requests/pull/2248/))
2.4.1 (2014-09-09)
------------------
+7 -7
View File
@@ -4,8 +4,8 @@ Requests: HTTP for Humans™
[![image](https://img.shields.io/pypi/v/requests.svg)](https://pypi.org/project/requests/)
[![image](https://img.shields.io/pypi/l/requests.svg)](https://pypi.org/project/requests/)
[![image](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests/)
[![codecov.io](https://codecov.io/github/requests/requests/coverage.svg?branch=master)](https://codecov.io/github/requests/requests)
[![image](https://img.shields.io/github/contributors/requests/requests.svg)](https://github.com/requests/requests/graphs/contributors)
[![codecov.io](https://codecov.io/github/psf/requests/coverage.svg?branch=master)](https://codecov.io/github/psf/requests)
[![image](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors)
[![image](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg)](https://saythanks.io/to/kennethreitz)
Requests is the only *Non-GMO* HTTP library for Python, safe for human
@@ -32,7 +32,7 @@ u'{"type":"User"...'
See [the similar code, sans Requests](https://gist.github.com/973705).
[![image](https://raw.githubusercontent.com/requests/requests/master/docs/_static/requests-logo-small.png)](http://docs.python-requests.org/)
[![image](https://raw.githubusercontent.com/psf/requests/master/docs/_static/requests-logo-small.png)](http://docs.python-requests.org/)
Requests allows you to send *organic, grass-fed* HTTP/1.1 requests,
without the need for manual labor. There's no need to manually add query
@@ -41,7 +41,7 @@ HTTP connection pooling are 100% automatic, thanks to
[urllib3](https://github.com/shazow/urllib3).
Besides, all the cool kids are doing it. Requests is one of the most
downloaded Python packages of all time, pulling in over 11,000,000
downloaded Python packages of all time, pulling in over 50,000,000
downloads every month. You don't want to be left out!
Feature Support
@@ -93,15 +93,15 @@ How to Contribute
1. Become more familiar with the project by reading our [Contributor's Guide](http://docs.python-requests.org/en/latest/dev/contributing/) and our [development philosophy](http://docs.python-requests.org/en/latest/dev/philosophy/).
2. Check for open issues or open a fresh issue to start a discussion
around a feature idea or a bug. There is a [Contributor
Friendly](https://github.com/requests/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open)
Friendly](https://github.com/psf/requests/issues?direction=desc&labels=Contributor+Friendly&page=1&sort=updated&state=open)
tag for issues that should be ideal for people who are not very
familiar with the codebase yet.
3. Fork [the repository](https://github.com/requests/requests) on
3. Fork [the repository](https://github.com/psf/requests) on
GitHub to start making your changes to the **master** branch (or
branch off of it).
4. Write a test which shows that the bug was fixed or that the feature
works as expected.
5. Send a [pull request](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork) and bug the maintainer until it gets merged and
published. :) Make sure to add yourself to
[AUTHORS](https://github.com/requests/requests/blob/master/AUTHORS.rst).
[AUTHORS](https://github.com/psf/requests/blob/master/AUTHORS.rst).
+20 -25
View File
@@ -50,29 +50,21 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
/* Native CPC by BuySellAds */
.native-js {
visibility: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial,
sans-serif;
opacity: 0;
#native-ribbon #_custom_ {
position: fixed;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0 -1px 4px 1px hsla(0, 0%, 0%, .15);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
transition: all .25s ease-in-out;
transform: translateY(calc(100% - 35px));
flex-flow: column nowrap;
}
.native-js[data-state=visible] {
position: fixed;
bottom: 0;
left: 0;
right: 0;
visibility: visible;
box-shadow: 0 -1px 4px 1px hsla(0, 0%, 0%, .15);
opacity: 1;
}
.native-js[data-state=visible]:hover {
#native-ribbon #_custom_:hover {
transform: translateY(0);
}
@@ -92,7 +84,7 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
transform-origin: left;
}
.native-js[data-state=visible]:hover .native-sponsor {
#native-ribbon #_custom_:hover .native-sponsor {
margin: 0 20px;
opacity: 0;
transform: scaleY(0);
@@ -100,17 +92,14 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
.native-flex {
display: flex;
padding: 0 20px 30px;
padding: 10px 20px 25px;
text-decoration: none;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
}
.native-flex:hover {
text-decoration: none;
}
.native-main {
display: flex;
@@ -135,6 +124,7 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
.native-desc {
letter-spacing: 1px;
font-weight: 300;
font-size: 14px;
line-height: 1.4;
}
@@ -158,6 +148,7 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
@media only screen and (min-width: 320px) and (max-width: 759px) {
.native-flex {
padding: 5px 5px 15px;
flex-direction: column;
flex-wrap: wrap;
@@ -165,6 +156,7 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
.native-img {
margin: 0;
display: none;
}
.native-details {
@@ -173,10 +165,13 @@ body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-ch
.native-main {
flex-direction: column;
margin-bottom: 20px;
text-align: center;
text-align: left;
flex-wrap: wrap;
align-content: center;
}
.native-cta {
display: none;
}
}
-131
View File
@@ -1,131 +0,0 @@
var _native = (function () {
var _options = {}
var _construct = function (e) {
var defaultOptions = {
carbonZoneKey: '',
fallback: '',
ignore: 'false',
placement: '',
prefix: 'native',
targetClass: 'native-ad'
}
if (typeof e === 'undefined') return defaultOptions
Object.keys(defaultOptions).forEach((key, index) => {
if (typeof e[key] === 'undefined') {
e[key] = defaultOptions[key]
}
})
return e
}
var init = function (zone, options) {
_options = _construct(options)
let jsonUrl = `https://srv.buysellads.com/ads/${zone}.json?callback=_native_go`
if (_options['placement'] !== '') {
jsonUrl += '&segment=placement:' + _options['placement']
}
if (_options['ignore'] === 'true') {
jsonUrl += '&ignore=yes'
}
let srv = document.createElement('script')
srv.src = jsonUrl
document.getElementsByTagName('head')[0].appendChild(srv)
}
var carbon = function (e) {
let srv = document.createElement('script')
srv.src = '//cdn.carbonads.com/carbon.js?serve=' + e['carbonZoneKey'] + '&placement=' + e['placement']
srv.id = '_carbonads_js'
return srv
}
var sanitize = function (ads) {
return ads
.filter(ad => {
return Object.keys(ad).length > 0
})
.filter(ad => {
return ad.hasOwnProperty('statlink')
})
}
var pixel = function (p, timestamp) {
let c = ''
if (p) {
p.split('||').forEach((pixel, index) => {
c += `<img src="${pixel.replace('[timestamp]', timestamp)}" style="display:none;" height="0" width="0" />`
})
}
return c
}
var options = function () {
return _options
}
return {
carbon: carbon,
init: init,
options: options,
pixel: pixel,
sanitize: sanitize
}
})({})
var _native_go = function (json) {
let options = _native.options()
let ads = _native.sanitize(json['ads'])
let selectedClass = document.querySelectorAll('.' + options['targetClass'])
if (ads.length < 1) {
selectedClass.forEach((className, index) => {
let selectedTarget = document.getElementsByClassName(options['targetClass'])[index]
if (options['fallback'] !== '' || options['carbonZoneKey'] !== '') selectedTarget.setAttribute('data-state', 'visible')
selectedTarget.innerHTML = options['fallback']
if (options['carbonZoneKey'] !== '') selectedTarget.appendChild(_native.carbon(options))
})
// End at this line if no ads are found, avoiding unnecessary steps
return
}
selectedClass.forEach((className, index) => {
let selectedTarget = document.getElementsByClassName(options['targetClass'])[index]
let adElement = selectedTarget.innerHTML
let prefix = options['prefix']
let ad = ads[index]
if (ad && className) {
let adInnerHtml = adElement
.replace(new RegExp('#' + prefix + '_bg_color#', 'g'), ad['backgroundColor'])
.replace(new RegExp('#' + prefix + '_bg_color_hover#', 'g'), ad['backgroundHoverColor'])
.replace(new RegExp('#' + prefix + '_company#', 'g'), ad['company'])
.replace(new RegExp('#' + prefix + '_cta#', 'g'), ad['callToAction'])
.replace(new RegExp('#' + prefix + '_cta_bg_color#', 'g'), ad['ctaBackgroundColor'])
.replace(new RegExp('#' + prefix + '_cta_bg_color_hover#', 'g'), ad['ctaBackgroundHoverColor'])
.replace(new RegExp('#' + prefix + '_cta_color#', 'g'), ad['ctaTextColor'])
.replace(new RegExp('#' + prefix + '_cta_color_hover#', 'g'), ad['ctaTextColorHover'])
.replace(new RegExp('#' + prefix + '_desc#', 'g'), ad['description'])
.replace(new RegExp('#' + prefix + '_index#', 'g'), prefix + '-' + ad['i'])
.replace(new RegExp('#' + prefix + '_img#', 'g'), ad['image'])
.replace(new RegExp('#' + prefix + '_small_img#', 'g'), ad['smallImage'])
.replace(new RegExp('#' + prefix + '_link#', 'g'), ad['statlink'])
.replace(new RegExp('#' + prefix + '_logo#', 'g'), ad['logo'])
.replace(new RegExp('#' + prefix + '_color#', 'g'), ad['textColor'])
.replace(new RegExp('#' + prefix + '_color_hover#', 'g'), ad['textColorHover'])
.replace(new RegExp('#' + prefix + '_title#', 'g'), ad['title'])
selectedTarget.innerHTML = null
selectedTarget.innerHTML += adInnerHtml + _native.pixel(ad['pixel'], ad['timestamp'])
selectedTarget.setAttribute('data-state', 'visible')
} else {
selectedTarget.innerHTML = null
selectedTarget.style.display = 'none'
}
})
}
+38 -31
View File
@@ -57,47 +57,54 @@
<!-- Native CPC by BuySellAds -->
<script src="{{ pathto('_static/', 1) }}/native.js"></script>
<script type="text/javascript" src="//m.servedby-buysellads.com/monetization.js"></script>
<div id="native-ribbon">
</div>
<script>
_native.init("CK7D62JU", {
targetClass: 'native-js'
});
</script>
<div class="native-js">
<div class="native-sponsor">Sponsored Ad by #native_company# — Learn More</div>
<a href="#native_link#" class="native-flex">
_bsa.init('custom', 'CK7D62JU', 'placement:pythonrequestsorg',
{
target: '#native-ribbon',
template: `
<div class="native-sponsor">Sponsored by ##company## — Learn More</div>
<a href="##link##" class="native-flex">
<style>
.native-js {
background: linear-gradient(-30deg, #native_bg_color#E5, #native_bg_color#E5 45%, #native_bg_color# 45%) #fff;
}
#native-ribbon #_custom_ {
background: linear-gradient(-30deg, ##backgroundColor##E5, ##backgroundColor##E5 45%, ##backgroundColor## 45%) #fff;
}
.native-details,
.native-sponsor {
color: #native_color# !important;
}
.native-details,
.native-sponsor,
.native-bsa {
color: ##textColor## !important;
}
.native-details:hover {
color: #native_color_hover# !important;
}
.native-details:hover {
color: ##textColorHover## !important;
}
.native-cta {
color: #native_cta_color#;
background-color: #native_cta_bg_color#;
}
.native-cta {
color: ##ctaTextColor##;
background-color: ##ctaBackgroundColor##;
}
.native-cta:hover {
color: #native_cta_color_hover;
background-color: #native_cta_bg_color_hover#;
}
.native-cta:hover {
color: ##ctaTextColorHover##;
background-color: ##ctaBackgroundColorHover##;
}
</style>
<div class="native-main">
<img class="native-img" src="#native_logo#">
<img class="native-img" src="##logo##">
<div class="native-details">
<span class="native-company">#native_title#</span>
<span class="native-desc">#native_desc#</span>
<span class="native-company">##title##</span>
<span class="native-desc">##description##</span>
</div>
<span class="native-cta">#native_cta#</span>
</div>
<span class="native-cta">##callToAction##</span>
</a>
</div>
`
}
);
</script>
+3 -39
View File
@@ -71,9 +71,9 @@
<p></p>
<li><a href="https://github.com/requests/requests">Requests @ GitHub</a></li>
<li><a href="https://github.com/psf/requests">Requests @ GitHub</a></li>
<li><a href="https://pypi.org/project/requests/">Requests @ PyPI</a></li>
<li><a href="https://github.com/requests/requests/issues">Issue Tracker</a></li>
<li><a href="https://github.com/psf/requests/issues">Issue Tracker</a></li>
<li><a href="http://docs.python-requests.org/en/latest/community/updates/#software-updates">Release History</a></li>
</ul>
@@ -91,41 +91,5 @@
<li><a href="http://es.python-requests.org/">Spanish</a></li>
</ul>
<div class="native-js">
<div class="native-sponsor">Sponsored by #native_company# — Learn More</div>
<a href="#native_link#" class="native-flex">
<style>
.native-js {
background: linear-gradient(-30deg, #native_bg_color#E5, #native_bg_color#E5 45%, #native_bg_color# 45%) #fff;
}
.native-details,
.native-sponsor,
.native-bsa {
color: #native_color# !important;
}
.native-details:hover {
color: #native_color_hover# !important;
}
.native-cta {
color: #native_cta_color#;
background-color: #native_cta_bg_color#;
}
.native-cta:hover {
color: #native_cta_color_hover;
background-color: #native_cta_bg_color_hover#;
}
</style>
<div class="native-main">
<img class="native-img" src="#native_logo#">
<div class="native-details">
<span class="native-company">#native_title#</span>
<span class="native-desc">#native_desc#</span>
</div>
</div>
<span class="native-cta">#native_cta#</span>
</a>
<div id="native-ribbon">
</div>
+2 -1
View File
@@ -21,7 +21,8 @@ Custom User-Agents?
-------------------
Requests allows you to easily override User-Agent strings, along with
any other HTTP Header.
any other HTTP Header. See `documentation about headers <http://docs.python-requests.org/en/master/user/quickstart/#custom-headers>`_.
Why not Httplib2?
+1 -1
View File
@@ -29,7 +29,7 @@ File an Issue
If you notice some unexpected behaviour in Requests, or want to see support
for a new feature,
`file an issue on GitHub <https://github.com/requests/requests/issues>`_.
`file an issue on GitHub <https://github.com/psf/requests/issues>`_.
E-mail
+1 -1
View File
@@ -14,7 +14,7 @@ GitHub
------
The best way to track the development of Requests is through
`the GitHub repo <https://github.com/requests/requests>`_.
`the GitHub repo <https://github.com/psf/requests>`_.
Twitter
-------
+1 -1
View File
@@ -197,7 +197,7 @@ through the `GitHub issues`_, **both open and closed**, to confirm that the bug
hasn't been reported before. Duplicate bug reports are a huge drain on the time
of other contributors, and should be avoided as much as possible.
.. _GitHub issues: https://github.com/requests/requests/issues
.. _GitHub issues: https://github.com/psf/requests/issues
Feature Requests
+2 -2
View File
@@ -8,11 +8,11 @@ Requests is under active development, and contributions are more than welcome!
#. Check for open issues or open a fresh issue to start a discussion around a bug.
There is a Contributor Friendly tag for issues that should be ideal for people who are not very
familiar with the codebase yet.
#. Fork `the repository <https://github.com/requests/requests>`_ on GitHub and start making your
#. Fork `the repository <https://github.com/psf/requests>`_ on GitHub and start making your
changes to a new branch.
#. Write a test which shows that the bug was fixed.
#. Send a pull request and bug the maintainer until it gets merged and published. :)
Make sure to add yourself to `AUTHORS <https://github.com/requests/requests/blob/master/AUTHORS.rst>`_.
Make sure to add yourself to `AUTHORS <https://github.com/psf/requests/blob/master/AUTHORS.rst>`_.
Feature Freeze
--------------
+2 -2
View File
@@ -17,8 +17,8 @@ Release v\ |version|. (:ref:`Installation <install>`)
.. image:: https://img.shields.io/pypi/pyversions/requests.svg
:target: https://pypi.org/project/requests/
.. image:: https://codecov.io/github/requests/requests/coverage.svg?branch=master
:target: https://codecov.io/github/requests/requests
.. image:: https://codecov.io/github/psf/requests/coverage.svg?branch=master
:target: https://codecov.io/github/psf/requests
:alt: codecov.io
.. image:: https://img.shields.io/badge/Say%20Thanks!-🦉-1EAEDB.svg
+6 -6
View File
@@ -300,7 +300,7 @@ immediately. You can override this behaviour and defer downloading the response
body until you access the :attr:`Response.content <requests.Response.content>`
attribute with the ``stream`` parameter::
tarball_url = 'https://github.com/requests/requests/tarball/master'
tarball_url = 'https://github.com/psf/requests/tarball/master'
r = requests.get(tarball_url, stream=True)
At this point only the response headers have been downloaded and the connection
@@ -680,7 +680,7 @@ from GitHub. Suppose we wanted commit ``a050faf`` on Requests. We would get it
like so::
>>> import requests
>>> r = requests.get('https://api.github.com/repos/requests/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
>>> r = requests.get('https://api.github.com/repos/psf/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
We should confirm that GitHub responded correctly. If it has, we want to work
out what type of content it is. Do this like so::
@@ -735,12 +735,12 @@ we should probably avoid making ham-handed POSTS to it. Instead, let's play
with the Issues feature of GitHub.
This documentation was added in response to
`Issue #482 <https://github.com/requests/requests/issues/482>`_. Given that
`Issue #482 <https://github.com/psf/requests/issues/482>`_. Given that
this issue already exists, we will use it as an example. Let's start by getting it.
::
>>> r = requests.get('https://api.github.com/repos/requests/requests/issues/482')
>>> r = requests.get('https://api.github.com/repos/psf/requests/issues/482')
>>> r.status_code
200
@@ -783,7 +783,7 @@ is to POST to the thread. Let's do it.
::
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it!"})
>>> url = u"https://api.github.com/repos/requests/requests/issues/482/comments"
>>> url = u"https://api.github.com/repos/psf/requests/issues/482/comments"
>>> r = requests.post(url=url, data=body)
>>> r.status_code
@@ -817,7 +817,7 @@ that.
5804413
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it once I feed my cat."})
>>> url = u"https://api.github.com/repos/requests/requests/issues/comments/5804413"
>>> url = u"https://api.github.com/repos/psf/requests/issues/comments/5804413"
>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
+4 -4
View File
@@ -25,15 +25,15 @@ Get the Source Code
-------------------
Requests is actively developed on GitHub, where the code is
`always available <https://github.com/requests/requests>`_.
`always available <https://github.com/psf/requests>`_.
You can either clone the public repository::
$ git clone git://github.com/requests/requests.git
$ git clone git://github.com/psf/requests.git
Or, download the `tarball <https://github.com/requests/requests/tarball/master>`_::
Or, download the `tarball <https://github.com/psf/requests/tarball/master>`_::
$ curl -OL https://github.com/requests/requests/tarball/master
$ curl -OL https://github.com/psf/requests/tarball/master
# optionally, zipball is also available (for Windows users).
Once you have a copy of the source, you can embed it in your own Python
+13 -58
View File
@@ -435,64 +435,19 @@ class HTTPAdapter(BaseAdapter):
timeout = TimeoutSauce(connect=timeout, read=timeout)
try:
if not chunked:
resp = conn.urlopen(
method=request.method,
url=url,
body=request.body,
headers=request.headers,
redirect=False,
assert_same_host=False,
preload_content=False,
decode_content=False,
retries=self.max_retries,
timeout=timeout
)
# Send the request.
else:
if hasattr(conn, 'proxy_pool'):
conn = conn.proxy_pool
low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT)
try:
low_conn.putrequest(request.method,
url,
skip_accept_encoding=True)
for header, value in request.headers.items():
low_conn.putheader(header, value)
low_conn.endheaders()
for i in request.body:
low_conn.send(hex(len(i))[2:].encode('utf-8'))
low_conn.send(b'\r\n')
low_conn.send(i)
low_conn.send(b'\r\n')
low_conn.send(b'0\r\n\r\n')
# Receive the response from the server
try:
# For Python 2.7, use buffering of HTTP responses
r = low_conn.getresponse(buffering=True)
except TypeError:
# For compatibility with Python 3.3+
r = low_conn.getresponse()
resp = HTTPResponse.from_httplib(
r,
pool=conn,
connection=low_conn,
preload_content=False,
decode_content=False
)
except:
# If we hit any problems here, clean up the connection.
# Then, reraise so that we can handle the actual exception.
low_conn.close()
raise
resp = conn.urlopen(
method=request.method,
url=url,
body=request.body,
headers=request.headers,
redirect=False,
assert_same_host=False,
preload_content=False,
decode_content=False,
retries=self.max_retries,
timeout=timeout,
chunked=chunked
)
except (ProtocolError, socket.error) as err:
raise ConnectionError(err, request=request)
+1 -1
View File
@@ -16,7 +16,7 @@ from . import sessions
def request(method, url, **kwargs):
"""Constructs and sends a :class:`Request <Request>`.
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS`, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
+1 -1
View File
@@ -239,7 +239,7 @@ class HTTPDigestAuth(AuthBase):
"""
# If response is not 4xx, do not auth
# See https://github.com/requests/requests/issues/3772
# See https://github.com/psf/requests/issues/3772
if not 400 <= r.status_code < 500:
self._thread_local.num_401_calls = 1
return r
+2
View File
@@ -43,6 +43,7 @@ if is_py2:
import cookielib
from Cookie import Morsel
from StringIO import StringIO
# Keep OrderedDict for backwards compatibility.
from collections import Callable, Mapping, MutableMapping, OrderedDict
@@ -59,6 +60,7 @@ elif is_py3:
from http import cookiejar as cookielib
from http.cookies import Morsel
from io import StringIO
# Keep OrderedDict for backwards compatibility.
from collections import OrderedDict
from collections.abc import Callable, Mapping, MutableMapping
+22 -5
View File
@@ -12,7 +12,7 @@ import sys
# Import encoding now, to avoid implicit import later.
# Implicit import within threads may cause LookupError when standard library is in a ZIP,
# such as in Embedded Python. See https://github.com/requests/requests/issues/3578.
# such as in Embedded Python. See https://github.com/psf/requests/issues/3578.
import encodings.idna
from urllib3.fields import RequestField
@@ -359,7 +359,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
#: We're unable to blindly call unicode/str functions
#: as this will include the bytestring indicator (b'')
#: on python 3.x.
#: https://github.com/requests/requests/pull/2238
#: https://github.com/psf/requests/pull/2238
if isinstance(url, bytes):
url = url.decode('utf8')
else:
@@ -641,6 +641,10 @@ class Response(object):
#: is a response.
self.request = None
#: If there was an error in the processing of content,
#: then save the error that would return the same error when you re-appeal.
self._error = None
def __enter__(self):
return self
@@ -750,12 +754,21 @@ class Response(object):
try:
for chunk in self.raw.stream(chunk_size, decode_content=True):
yield chunk
except ProtocolError as e:
raise ChunkedEncodingError(e)
self._error = ChunkedEncodingError(e)
except DecodeError as e:
raise ContentDecodingError(e)
self._error = ContentDecodingError(e)
except ReadTimeoutError as e:
raise ConnectionError(e)
self._error = ConnectionError(e)
finally:
# if we had an error - throw the saved error
if self._error:
raise self._error
else:
# Standard file-like object.
while True:
@@ -828,6 +841,10 @@ class Response(object):
else:
self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b''
# if we had an error - throw the saved error
if self._error is not None:
raise self._error
self._content_consumed = True
# don't need to release the connection; that's been handled by urllib3
# since we exhausted the data.
+16 -12
View File
@@ -11,9 +11,10 @@ import os
import sys
import time
from datetime import timedelta
from collections import OrderedDict
from .auth import _basic_auth_str
from .compat import cookielib, is_py3, OrderedDict, urljoin, urlparse, Mapping
from .compat import cookielib, is_py3, urljoin, urlparse, Mapping
from .cookies import (
cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies)
from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT
@@ -94,6 +95,10 @@ def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
class SessionRedirectMixin(object):
def __init__(self):
#: A list of domains that will be excluded from auth stripping
self.trusted_domains = []
def get_redirect_target(self, resp):
"""Receives a Response. Returns a redirect URI or ``None``"""
# Due to the nature of how requests processes redirects this method will
@@ -119,7 +124,8 @@ class SessionRedirectMixin(object):
"""Decide whether Authorization header should be removed when redirecting"""
old_parsed = urlparse(old_url)
new_parsed = urlparse(new_url)
if old_parsed.hostname != new_parsed.hostname:
if (old_parsed.hostname != new_parsed.hostname
and new_parsed.hostname not in self.trusted_domains):
return True
# Special case: allow http -> https redirect when using the standard
# ports. This isn't specified by RFC 7235, but is kept to avoid
@@ -162,7 +168,7 @@ class SessionRedirectMixin(object):
resp.raw.read(decode_content=False)
if len(resp.history) >= self.max_redirects:
raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp)
raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
# Release the connection back into the pool.
resp.close()
@@ -170,7 +176,7 @@ class SessionRedirectMixin(object):
# Handle redirection without scheme (see: RFC 1808 Section 4)
if url.startswith('//'):
parsed_rurl = urlparse(resp.url)
url = '%s:%s' % (to_native_string(parsed_rurl.scheme), url)
url = ':'.join([to_native_string(parsed_rurl.scheme), url])
# Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
parsed = urlparse(url)
@@ -192,19 +198,16 @@ class SessionRedirectMixin(object):
self.rebuild_method(prepared_request, resp)
# https://github.com/requests/requests/issues/1084
# https://github.com/psf/requests/issues/1084
if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect):
# https://github.com/requests/requests/issues/3490
# https://github.com/psf/requests/issues/3490
purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding')
for header in purged_headers:
prepared_request.headers.pop(header, None)
prepared_request.body = None
headers = prepared_request.headers
try:
del headers['Cookie']
except KeyError:
pass
headers.pop('Cookie', None)
# Extract any cookies sent on the response to the cookiejar
# in the new request. Because we've mutated our copied prepared
@@ -271,7 +274,6 @@ class SessionRedirectMixin(object):
if new_auth is not None:
prepared_request.prepare_auth(new_auth)
return
def rebuild_proxies(self, prepared_request, proxies):
"""This method re-evaluates the proxy configuration by considering the
@@ -417,6 +419,8 @@ class Session(SessionRedirectMixin):
self.mount('https://', HTTPAdapter())
self.mount('http://', HTTPAdapter())
super().__init__()
def __enter__(self):
return self
@@ -728,7 +732,7 @@ class Session(SessionRedirectMixin):
return adapter
# Nothing matches :-/
raise InvalidSchema("No connection adapters were found for '%s'" % url)
raise InvalidSchema("No connection adapters were found for {!r}".format(url))
def close(self):
"""Closes all adapters and as such the session"""
+3 -1
View File
@@ -7,7 +7,9 @@ requests.structures
Data structures that power Requests.
"""
from .compat import OrderedDict, Mapping, MutableMapping
from collections import OrderedDict
from .compat import Mapping, MutableMapping
class CaseInsensitiveDict(MutableMapping):
+3 -2
View File
@@ -19,6 +19,7 @@ import sys
import tempfile
import warnings
import zipfile
from collections import OrderedDict
from .__version__ import __version__
from . import certs
@@ -26,7 +27,7 @@ from . import certs
from ._internal_utils import to_native_string
from .compat import parse_http_list as _parse_list_header
from .compat import (
quote, urlparse, bytes, str, OrderedDict, unquote, getproxies,
quote, urlparse, bytes, str, unquote, getproxies,
proxy_bypass, urlunparse, basestring, integer_types, is_py3,
proxy_bypass_environment, getproxies_environment, Mapping)
from .cookies import cookiejar_from_dict
@@ -179,7 +180,7 @@ def get_netrc_auth(url, raise_errors=False):
except KeyError:
# os.path.expanduser can fail when $HOME is undefined and
# getpwuid fails. See https://bugs.python.org/issue20164 &
# https://github.com/requests/requests/issues/1846
# https://github.com/psf/requests/issues/1846
return
if os.path.exists(loc):
+5 -1
View File
@@ -54,7 +54,7 @@ test_requirements = [
'pytest-mock',
'pytest-xdist',
'PySocks>=1.5.6, !=1.5.7',
'pytest>=2.8.0'
'pytest>=3'
]
about = {}
@@ -105,4 +105,8 @@ setup(
'socks': ['PySocks>=1.5.6, !=1.5.7'],
'socks:sys_platform == "win32" and python_version == "2.7"': ['win_inet_pton'],
},
project_urls={
'Documentation': 'http://docs.python-requests.org',
'Source': 'https://github.com/kennethreitz/requests',
},
)
+43 -2
View File
@@ -3,6 +3,7 @@
import pytest
import threading
import requests
from requests.exceptions import ChunkedEncodingError
from tests.testserver.server import Server, consume_socket_content
@@ -28,7 +29,7 @@ def test_digestauth_401_count_reset_on_redirect():
"""Ensure we correctly reset num_401_calls after a successful digest auth,
followed by a 302 redirect to another digest auth prompt.
See https://github.com/requests/requests/issues/1979.
See https://github.com/psf/requests/issues/1979.
"""
text_401 = (b'HTTP/1.1 401 UNAUTHORIZED\r\n'
b'Content-Length: 0\r\n'
@@ -138,7 +139,7 @@ def test_digestauth_401_only_sent_once():
def test_digestauth_only_on_4xx():
"""Ensure we only send digestauth on 4xx challenges.
See https://github.com/requests/requests/issues/3772.
See https://github.com/psf/requests/issues/3772.
"""
text_200_chal = (b'HTTP/1.1 200 OK\r\n'
b'Content-Length: 0\r\n'
@@ -307,3 +308,43 @@ def test_fragment_update_on_redirect():
assert r.url == 'http://{}:{}/final-url/#relevant-section'.format(host, port)
close_server.set()
def test_response_content_retains_error():
"""Verify that accessing response.content retains an error.
See https://github.com/kennethreitz/requests/issues/4965
"""
data = "Some random stuff to read from remove server.\n"
def response_handler(sock):
req = consume_socket_content(sock, timeout=0.5)
# Send invalid chunked data (length mismatch)
sock.send(
b'HTTP/1.1 200 OK\r\n'
b'Transfer-Encoding: chunked\r\n'
b'\r\n2\r\n42\r\n8\r\n123\r\n' # 5 bytes missing
)
close_server = threading.Event()
server = Server(response_handler, wait_to_close_event=close_server)
with server as (host, port):
url = 'http://{}:{}/path'.format(host, port)
r = requests.post(url, stream=True)
with pytest.raises(ChunkedEncodingError):
r.content
# Access the bad response data again, I would expect the same
# error again.
try:
content = r.content
except ChunkedEncodingError:
pass # fine, same exception
else:
assert False, "error response has content: {0!r}".format(content)
close_server.set()
+6 -6
View File
@@ -18,7 +18,7 @@ from requests.adapters import HTTPAdapter
from requests.auth import HTTPDigestAuth, _basic_auth_str
from requests.compat import (
Morsel, cookielib, getproxies, str, urlparse,
builtin_str, OrderedDict)
builtin_str)
from requests.cookies import (
cookiejar_from_dict, morsel_to_cookie)
from requests.exceptions import (
@@ -130,7 +130,7 @@ class TestRequests:
assert request.url == expected
def test_params_original_order_is_preserved_by_default(self):
param_ordered_dict = OrderedDict((('z', 1), ('a', 1), ('k', 1), ('d', 1)))
param_ordered_dict = collections.OrderedDict((('z', 1), ('a', 1), ('k', 1), ('d', 1)))
session = requests.Session()
request = requests.Request('GET', 'http://example.com/', params=param_ordered_dict)
prep = session.prepare_request(request)
@@ -446,11 +446,11 @@ class TestRequests:
def test_headers_preserve_order(self, httpbin):
"""Preserve order when headers provided as OrderedDict."""
ses = requests.Session()
ses.headers = OrderedDict()
ses.headers = collections.OrderedDict()
ses.headers['Accept-Encoding'] = 'identity'
ses.headers['First'] = '1'
ses.headers['Second'] = '2'
headers = OrderedDict([('Third', '3'), ('Fourth', '4')])
headers = collections.OrderedDict([('Third', '3'), ('Fourth', '4')])
headers['Fifth'] = '5'
headers['Second'] = '222'
req = requests.Request('GET', httpbin('get'), headers=headers)
@@ -466,7 +466,7 @@ class TestRequests:
@pytest.mark.parametrize('key', ('User-agent', 'user-agent'))
def test_user_agent_transfers(self, httpbin, key):
heads = {key: 'Mozilla/5.0 (github.com/requests/requests)'}
heads = {key: 'Mozilla/5.0 (github.com/psf/requests)'}
r = requests.get(httpbin('user-agent'), headers=heads)
assert heads[key] in r.text
@@ -2212,7 +2212,7 @@ class TestTimeout:
pass
def test_encoded_methods(self, httpbin):
"""See: https://github.com/requests/requests/issues/2316"""
"""See: https://github.com/psf/requests/issues/2316"""
r = requests.request(b'GET', httpbin('get'))
assert r.ok
+3 -2
View File
@@ -33,7 +33,8 @@ class TestSuperLen:
'stream, value', (
(StringIO.StringIO, 'Test'),
(BytesIO, b'Test'),
pytest.mark.skipif('cStringIO is None')((cStringIO, 'Test')),
pytest.param(cStringIO, 'Test',
marks=pytest.mark.skipif('cStringIO is None')),
))
def test_io_streams(self, stream, value):
"""Ensures that we properly deal with different kinds of IO streams."""
@@ -397,7 +398,7 @@ def test_get_auth_from_url(url, auth):
),
))
def test_requote_uri_with_unquoted_percents(uri, expected):
"""See: https://github.com/requests/requests/issues/2356"""
"""See: https://github.com/psf/requests/issues/2356"""
assert requote_uri(uri) == expected