diff --git a/SOURCES/fix-leaking-test.patch b/SOURCES/fix-leaking-test.patch new file mode 100644 index 0000000..bef3b6e --- /dev/null +++ b/SOURCES/fix-leaking-test.patch @@ -0,0 +1,68 @@ +From 9b63f9cd37d19f2d4bbce42caec112ad0606d8dd Mon Sep 17 00:00:00 2001 +From: Cory Benfield +Date: Thu, 22 Oct 2015 12:22:28 +0100 +Subject: [PATCH] Make sure we unapply this patch. + +--- + test_requests.py | 47 ++++++++++++++++++++++++++--------------------- + 1 file changed, 26 insertions(+), 21 deletions(-) + +diff --git a/test_requests.py b/test_requests.py +index d1c6aa4d5..cb25555aa 100755 +--- a/test_requests.py ++++ b/test_requests.py +@@ -353,28 +353,33 @@ def test_basicauth_with_netrc(self): + wrong_auth = ('wronguser', 'wrongpass') + url = httpbin('basic-auth', 'user', 'pass') + +- def get_netrc_auth_mock(url): +- return auth +- requests.sessions.get_netrc_auth = get_netrc_auth_mock ++ old_auth = requests.sessions.get_netrc_auth + +- # Should use netrc and work. +- r = requests.get(url) +- assert r.status_code == 200 +- +- # Given auth should override and fail. +- r = requests.get(url, auth=wrong_auth) +- assert r.status_code == 401 +- +- s = requests.session() +- +- # Should use netrc and work. +- r = s.get(url) +- assert r.status_code == 200 +- +- # Given auth should override and fail. +- s.auth = wrong_auth +- r = s.get(url) +- assert r.status_code == 401 ++ try: ++ def get_netrc_auth_mock(url): ++ return auth ++ requests.sessions.get_netrc_auth = get_netrc_auth_mock ++ ++ # Should use netrc and work. ++ r = requests.get(url) ++ assert r.status_code == 200 ++ ++ # Given auth should override and fail. ++ r = requests.get(url, auth=wrong_auth) ++ assert r.status_code == 401 ++ ++ s = requests.session() ++ ++ # Should use netrc and work. ++ r = s.get(url) ++ assert r.status_code == 200 ++ ++ # Given auth should override and fail. ++ s.auth = wrong_auth ++ r = s.get(url) ++ assert r.status_code == 401 ++ finally: ++ requests.sessions.get_netrc_auth = old_auth + + def test_DIGEST_HTTP_200_OK_GET(self): + diff --git a/SOURCES/fix-requests.packages-aliasing.patch b/SOURCES/fix-requests.packages-aliasing.patch new file mode 100644 index 0000000..5d54514 --- /dev/null +++ b/SOURCES/fix-requests.packages-aliasing.patch @@ -0,0 +1,157 @@ +diff --git a/requests/__init__.py b/requests/__init__.py +index 8257b61..68443da 100644 +--- a/requests/__init__.py ++++ b/requests/__init__.py +@@ -64,6 +64,7 @@ from .exceptions import ( + RequestException, Timeout, URLRequired, + TooManyRedirects, HTTPError, ConnectionError + ) ++from . import packages + + # Set default logging handler to avoid "No handler found" warnings. + import logging +diff --git a/requests/packages/__init__.py b/requests/packages/__init__.py +index 4dcf870..385581e 100644 +--- a/requests/packages/__init__.py ++++ b/requests/packages/__init__.py +@@ -1,107 +1,35 @@ +-""" +-Copyright (c) Donald Stufft, pip, and individual contributors +- +-Permission is hereby granted, free of charge, to any person obtaining +-a copy of this software and associated documentation files (the +-"Software"), to deal in the Software without restriction, including +-without limitation the rights to use, copy, modify, merge, publish, +-distribute, sublicense, and/or sell copies of the Software, and to +-permit persons to whom the Software is furnished to do so, subject to +-the following conditions: +- +-The above copyright notice and this permission notice shall be +-included in all copies or substantial portions of the Software. +- +-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-""" +-from __future__ import absolute_import +- + import sys + +- +-class VendorAlias(object): +- +- def __init__(self, package_names): +- self._package_names = package_names +- self._vendor_name = __name__ +- self._vendor_pkg = self._vendor_name + "." +- self._vendor_pkgs = [ +- self._vendor_pkg + name for name in self._package_names +- ] +- +- def find_module(self, fullname, path=None): +- if fullname.startswith(self._vendor_pkg): +- return self +- +- def load_module(self, name): +- # Ensure that this only works for the vendored name +- if not name.startswith(self._vendor_pkg): +- raise ImportError( +- "Cannot import %s, must be a subpackage of '%s'." % ( +- name, self._vendor_name, +- ) +- ) +- +- if not (name == self._vendor_name or +- any(name.startswith(pkg) for pkg in self._vendor_pkgs)): +- raise ImportError( +- "Cannot import %s, must be one of %s." % ( +- name, self._vendor_pkgs +- ) +- ) +- +- # Check to see if we already have this item in sys.modules, if we do +- # then simply return that. +- if name in sys.modules: +- return sys.modules[name] +- +- # Check to see if we can import the vendor name +- try: +- # We do this dance here because we want to try and import this +- # module without hitting a recursion error because of a bunch of +- # VendorAlias instances on sys.meta_path +- real_meta_path = sys.meta_path[:] +- try: +- sys.meta_path = [ +- m for m in sys.meta_path +- if not isinstance(m, VendorAlias) +- ] +- __import__(name) +- module = sys.modules[name] +- finally: +- # Re-add any additions to sys.meta_path that were made while +- # during the import we just did, otherwise things like +- # requests.packages.urllib3.poolmanager will fail. +- for m in sys.meta_path: +- if m not in real_meta_path: +- real_meta_path.append(m) +- +- # Restore sys.meta_path with any new items. +- sys.meta_path = real_meta_path +- except ImportError: +- # We can't import the vendor name, so we'll try to import the +- # "real" name. +- real_name = name[len(self._vendor_pkg):] +- try: +- __import__(real_name) +- module = sys.modules[real_name] +- except ImportError: +- raise ImportError("No module named '%s'" % (name,)) +- +- # If we've gotten here we've found the module we're looking for, either +- # as part of our vendored package, or as the real name, so we'll add +- # it to sys.modules as the vendored name so that we don't have to do +- # the lookup again. +- sys.modules[name] = module +- +- # Finally, return the loaded module +- return module +- +- +-sys.meta_path.append(VendorAlias(["urllib3", "chardet"])) ++# This code exists for backwards compatibility reasons. ++ ++modules_list = [ ++ 'urllib3', 'chardet', 'chardet.big5prober', 'chardet.chardetect', ++ 'chardet.chardistribution', 'chardet.charsetgroupprober', 'chardet.charsetprober', ++ 'chardet.codingstatemachine', 'chardet.compat', 'chardet.constants', ++ 'chardet.cp949prober', 'chardet.escprober', 'chardet.escsm', 'chardet.eucjpprober', ++ 'chardet.euckrfreq', 'chardet.euckrprober', 'chardet.euctwfreq', 'chardet.euctwprober', ++ 'chardet.gb2312freq', 'chardet.gb2312prober', 'chardet.hebrewprober', ++ 'chardet.jisfreq', 'chardet.jpcntx', 'chardet.langbulgarianmodel', ++ 'chardet.langcyrillicmodel', 'chardet.langgreekmodel', 'chardet.langhebrewmodel', ++ 'chardet.langhungarianmodel', 'chardet.langthaimodel', 'chardet.latin1prober', ++ 'chardet.mbcharsetprober', 'chardet.mbcsgroupprober', 'chardet.mbcssm', ++ 'chardet.sbcharsetprober', 'chardet.sbcsgroupprober', 'chardet.sjisprober', ++ 'chardet.universaldetector', 'chardet.utf8prober', 'urllib3._collections', ++ 'urllib3.connectionpool', 'urllib3.connection', 'urllib3.contrib', 'urllib3.exceptions', ++ 'urllib3.fields', 'urllib3.filepost', 'urllib3.packages', 'urllib3.poolmanager', ++ 'urllib3.request', 'urllib3.response', 'urllib3.util', 'urllib3.contrib.ntlmpool', ++ 'urllib3.contrib.pyopenssl', 'urllib3.util.connection', 'urllib3.util.request', ++ 'urllib3.util.response', 'urllib3.util.retry', 'urllib3.util.ssl_', 'urllib3.util.timeout', ++ 'urllib3.util.url', 'urllib3.packages.ordered_dict', 'urllib3.packages.six', ++ 'urllib3.packages.ssl_match_hostname', 'urllib3.packages.ssl_match_hostname.implementation' ++ ] ++ ++ ++for package in modules_list: ++ try: ++ __import__(package) ++ except ImportError: ++ pass ++ else: ++ sys.modules['requests.packages.' + package] = sys.modules[package] ++ globals()[package] = sys.modules[package] diff --git a/SOURCES/import-urllib3-and-chardet-from-the-global-namespace.patch b/SOURCES/import-urllib3-and-chardet-from-the-global-namespace.patch new file mode 100644 index 0000000..fecebde --- /dev/null +++ b/SOURCES/import-urllib3-and-chardet-from-the-global-namespace.patch @@ -0,0 +1,123 @@ +diff --git a/requests/__init__.py b/requests/__init__.py +index 446500b..8257b61 100644 +--- a/requests/__init__.py ++++ b/requests/__init__.py +@@ -50,7 +50,7 @@ __copyright__ = 'Copyright 2015 Kenneth Reitz' + + # Attempt to enable urllib3's SNI support, if possible + try: +- from .packages.urllib3.contrib import pyopenssl ++ from urllib3.contrib import pyopenssl + pyopenssl.inject_into_urllib3() + except ImportError: + pass +diff --git a/requests/adapters.py b/requests/adapters.py +index 02e0dd1..daae42e 100644 +--- a/requests/adapters.py ++++ b/requests/adapters.py +@@ -11,22 +11,22 @@ and maintain connections. + import socket + + from .models import Response +-from .packages.urllib3.poolmanager import PoolManager, proxy_from_url +-from .packages.urllib3.response import HTTPResponse +-from .packages.urllib3.util import Timeout as TimeoutSauce +-from .packages.urllib3.util.retry import Retry ++from urllib3.poolmanager import PoolManager, proxy_from_url ++from urllib3.response import HTTPResponse ++from urllib3.util import Timeout as TimeoutSauce ++from urllib3.util.retry import Retry + from .compat import urlparse, basestring + from .utils import (DEFAULT_CA_BUNDLE_PATH, get_encoding_from_headers, + prepend_scheme_if_needed, get_auth_from_url, urldefragauth) + from .structures import CaseInsensitiveDict +-from .packages.urllib3.exceptions import ConnectTimeoutError +-from .packages.urllib3.exceptions import HTTPError as _HTTPError +-from .packages.urllib3.exceptions import MaxRetryError +-from .packages.urllib3.exceptions import ProxyError as _ProxyError +-from .packages.urllib3.exceptions import ProtocolError +-from .packages.urllib3.exceptions import ReadTimeoutError +-from .packages.urllib3.exceptions import SSLError as _SSLError +-from .packages.urllib3.exceptions import ResponseError ++from urllib3.exceptions import ConnectTimeoutError ++from urllib3.exceptions import HTTPError as _HTTPError ++from urllib3.exceptions import MaxRetryError ++from urllib3.exceptions import ProxyError as _ProxyError ++from urllib3.exceptions import ProtocolError ++from urllib3.exceptions import ReadTimeoutError ++from urllib3.exceptions import SSLError as _SSLError ++from urllib3.exceptions import ResponseError + from .cookies import extract_cookies_to_jar + from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, + ProxyError, RetryError) +diff --git a/requests/compat.py b/requests/compat.py +index 40bb6c5..4232288 100644 +--- a/requests/compat.py ++++ b/requests/compat.py +@@ -4,7 +4,7 @@ + pythoncompat + """ + +-from .packages import chardet ++import chardet + + import sys + +diff --git a/requests/exceptions.py b/requests/exceptions.py +index 89135a8..b70c525 100644 +--- a/requests/exceptions.py ++++ b/requests/exceptions.py +@@ -7,7 +7,7 @@ requests.exceptions + This module contains the set of Requests' exceptions. + + """ +-from .packages.urllib3.exceptions import HTTPError as BaseHTTPError ++from urllib3.exceptions import HTTPError as BaseHTTPError + + + class RequestException(IOError): +diff --git a/requests/models.py b/requests/models.py +index 419cf0a..131b88d 100644 +--- a/requests/models.py ++++ b/requests/models.py +@@ -16,10 +16,10 @@ from .structures import CaseInsensitiveDict + + from .auth import HTTPBasicAuth + from .cookies import cookiejar_from_dict, get_cookie_header +-from .packages.urllib3.fields import RequestField +-from .packages.urllib3.filepost import encode_multipart_formdata +-from .packages.urllib3.util import parse_url +-from .packages.urllib3.exceptions import ( ++from urllib3.fields import RequestField ++from urllib3.filepost import encode_multipart_formdata ++from urllib3.util import parse_url ++from urllib3.exceptions import ( + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) + from .exceptions import ( + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, +diff --git a/requests/sessions.py b/requests/sessions.py +index 9a51f33..22bd226 100644 +--- a/requests/sessions.py ++++ b/requests/sessions.py +@@ -21,7 +21,7 @@ from .hooks import default_hooks, dispatch_hook + from .utils import to_key_val_list, default_headers, to_native_string, DEFAULT_PORTS + from .exceptions import ( + TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) +-from .packages.urllib3._collections import RecentlyUsedContainer ++from urllib3._collections import RecentlyUsedContainer + from .structures import CaseInsensitiveDict + + from .adapters import HTTPAdapter +diff --git a/test_requests.py b/test_requests.py +index cf7c722..690b3ed 100755 +--- a/test_requests.py ++++ b/test_requests.py +@@ -1655,7 +1655,7 @@ def test_prepare_unicode_url(): + + + def test_urllib3_retries(): +- from requests.packages.urllib3.util import Retry ++ from urllib3.util import Retry + s = requests.Session() + s.mount('http://', HTTPAdapter(max_retries=Retry( + total=2, status_forcelist=[500] diff --git a/SOURCES/modify-tests-to-run-in-pytest.patch b/SOURCES/modify-tests-to-run-in-pytest.patch new file mode 100644 index 0000000..6dffdc3 --- /dev/null +++ b/SOURCES/modify-tests-to-run-in-pytest.patch @@ -0,0 +1,85 @@ +From cc0107b311f74c78bd653fa1a84e2dcedce9fc6f Mon Sep 17 00:00:00 2001 +From: Tomas Orsava +Date: Thu, 3 Oct 2019 16:17:37 +0200 +Subject: [PATCH] Modify tests to run in pytest + +--- + test_requests.py | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +diff --git a/test_requests.py b/test_requests.py +index 15406a2..74aa5b3 100755 +--- a/test_requests.py ++++ b/test_requests.py +@@ -7,7 +7,6 @@ from __future__ import division + import json + import os + import pickle +-import unittest + import collections + + import io +@@ -53,7 +52,7 @@ def httpbin(*suffix): + return urljoin(HTTPBIN, '/'.join(suffix)) + + +-class RequestsTestCase(unittest.TestCase): ++class TestCase(): + + _multiprocess_can_split_ = True + +@@ -1070,7 +1069,7 @@ class RequestsTestCase(unittest.TestCase): + assert len(list(r.iter_lines())) == 3 + + +-class TestContentEncodingDetection(unittest.TestCase): ++class TestContentEncodingDetection(): + + def test_none(self): + encodings = requests.utils.get_encodings_from_content('') +@@ -1114,7 +1113,7 @@ class TestContentEncodingDetection(unittest.TestCase): + assert encodings == ['HTML5', 'HTML4', 'XML'] + + +-class TestCaseInsensitiveDict(unittest.TestCase): ++class TestCaseInsensitiveDict(): + + def test_mapping_init(self): + cid = CaseInsensitiveDict({'Foo': 'foo', 'BAr': 'bar'}) +@@ -1252,7 +1251,7 @@ class TestCaseInsensitiveDict(unittest.TestCase): + assert frozenset(cid) == keyset + + +-class UtilsTestCase(unittest.TestCase): ++class UtilsTestCase(): + + def test_super_len_io_streams(self): + """ Ensures that we properly deal with different kinds of IO streams. """ +@@ -1376,7 +1375,7 @@ class UtilsTestCase(unittest.TestCase): + assert quoted == requote_uri(quoted) + + +-class TestMorselToCookieExpires(unittest.TestCase): ++class TestMorselToCookieExpires(): + + """Tests for morsel_to_cookie when morsel contains expires.""" + +@@ -1413,7 +1412,7 @@ class TestMorselToCookieExpires(unittest.TestCase): + assert cookie.expires is None + + +-class TestMorselToCookieMaxAge(unittest.TestCase): ++class TestMorselToCookieMaxAge(): + + """Tests for morsel_to_cookie when morsel contains max-age.""" + +@@ -1634,6 +1633,3 @@ def test_vendor_aliases(): + + with pytest.raises(ImportError): + from requests.packages import webbrowser +- +-if __name__ == '__main__': +- unittest.main() +-- +2.20.1 + diff --git a/SPECS/python-requests.spec b/SPECS/python-requests.spec index af6cd1e..50d9d3a 100644 --- a/SPECS/python-requests.spec +++ b/SPECS/python-requests.spec @@ -1,3 +1,6 @@ +# Tests need an internet connection, so they are disabled by default +%bcond_with online_tests + %if 0%{?fedora} %global _with_python3 1 %else @@ -6,7 +9,7 @@ Name: python-requests Version: 2.6.0 -Release: 6%{?dist} +Release: 10%{?dist} Summary: HTTP library, written in Python, for human beings License: ASL 2.0 @@ -19,19 +22,57 @@ Patch0: python-requests-system-cert-bundle.patch # Remove an unnecessary reference to a bundled compat lib in urllib3 Patch1: python-requests-remove-nested-bundling-dep.patch +# Modify tests to run in pytest so that we can run backported parametrized +# tests (Patch4: fix-default-port-handling.patch). +# We cannot launch the tests during build as they require internet, but QE +# launches them later. +# Minimal version of this upstream change: +# https://github.com/psf/requests/commit/6c2942b19865106a3ac65b3bfc1fc93aae2d346c +Patch2: modify-tests-to-run-in-pytest.patch + # Fix for CVE-2018-18074 # Resolved upstream: https://github.com/requests/requests/pull/4718 -Patch2: fix-CVE-2018-18074.patch +Patch3: fix-CVE-2018-18074.patch # Fix handling of default ports in auth stripping # Resolved upstream: https://github.com/psf/requests/pull/4851 -Patch3: fix-default-port-handling.patch +Patch4: fix-default-port-handling.patch + +# Fix a leaking test that was making another test (test_auth_is_stripped_on_redirect_off_host) fail +# Resolved upstream: https://github.com/psf/requests/commit/9b63f9cd37d19f2d4bbce42caec112ad0606d8dd +Patch5: fix-leaking-test.patch + +# The upstream requests library bundles urllib3 and chardet, and then +# manipulates the module namespace in order to alias the packages to +# the system ones if a distribution unbundles them. This however can lead +# to side effects, such as creating different instances of the modules +# in memory where changes can happen to a module but not reflected to another, +# leading to undefined behavior. +# This patch changes all the import statements for urllib3 and chardet to import +# from the global namespace, thus avoiding the instance duplication. +# Partial backport from https://github.com/psf/requests/pull/4067 +# Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1750839 +Patch6: import-urllib3-and-chardet-from-the-global-namespace.patch + +# While the requests.packages aliasing has been removed since +# it caused various issues while it's also an undocumented API, +# much code in the wild still relies on it. This fix ensures +# that while the naming scheme is there for backwards compatibility, +# the submodules contained in the requests.packages namespace +# are properly aliased to the system installed modules. +# Inspired by https://github.com/psf/requests/blob/v2.18.0/requests/packages.py +# Resolves: rhbz#1785607, rhbz#1785666, rhbz#1785696, rhbz#1787679 +Patch7: fix-requests.packages-aliasing.patch BuildArch: noarch BuildRequires: python2-devel BuildRequires: python-chardet >= 2.2.1-1 BuildRequires: python-urllib3 >= 1.10.2-1 +%if %{with online_tests} +BuildRequires: pytest +%endif + Requires: ca-certificates Requires: python-chardet >= 2.2.1-1 Requires: python-urllib3 >= 1.10.2-1 @@ -56,6 +97,9 @@ Summary: HTTP library, written in Python, for human beings BuildRequires: python3-devel BuildRequires: python3-chardet BuildRequires: python3-urllib3 +%if %{with online_tests} +BuildRequires: python3-pytest +%endif Requires: python3-chardet Requires: python3-urllib3 @@ -73,6 +117,10 @@ designed to make HTTP requests easy for developers. %patch1 -p1 %patch2 -p1 %patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 # Unbundle the certificate bundle from mozilla. rm -rf requests/cacert.pem @@ -110,15 +158,17 @@ popd %py2_install -## The tests succeed if run locally, but fail in koji. -## They require an active network connection to query httpbin.org -#%%check -#%%{__python} test_requests.py -#%%if 0%%{?_with_python3} -#pushd %%{py3dir} -#%%{__python3} test_requests.py -#popd -#%%endif +%if %{with online_tests} +# The tests succeed if run locally, but fail in koji. +# They require an active network connection to query httpbin.org +%check +%{__python} -m pytest test_requests.py +%if 0%{?_with_python3} +pushd %{py3dir} +%{__python3} -m pytest test_requests.py +popd +%endif +%endif #with online_tests %files %defattr(-,root,root,-) @@ -134,6 +184,23 @@ popd %endif %changelog +* Tue Mar 10 2020 Charalampos Stratakis - 2.6.0-10 +- Fix test_urllib3_retries by having it import urllib3 from the global namespace +Resolves: rhbz#1771567 + +* Wed Feb 19 2020 Charalampos Stratakis - 2.6.0-9 +- Bring back the requests.packages aliasing of its submodules +Resolves: rhbz#1785607, rhbz#1785666, rhbz#1785696, rhbz#1787679 + +* Thu Oct 24 2019 Charalampos Stratakis - 2.6.0-8 +- Import urllib3 and chardet from the global namespace +Resolves: rhbz#1750839 + +* Thu Oct 03 2019 Tomas Orsava - 2.6.0-7 +- Modify tests to run in pytest +- Add a bcond online_tests to enable the tests +Related: rhbz#1745417 + * Tue Aug 27 2019 Charalampos Stratakis - 2.6.0-6 - Fix handling of default ports in auth stripping Resolves: rhbz#1745417