From b473faf2bab0f3aaecc87fa7a17e83004c7a8ac3 Mon Sep 17 00:00:00 2001 From: Chunmei Xu Date: Mon, 18 Apr 2022 19:39:16 +0800 Subject: [PATCH] init from upstream Signed-off-by: Chunmei Xu --- ...-tests-and-updated-statud_code-build.patch | 22 ++++++ ...ding-code-3-to-retr4ieve-status_code.patch | 58 ++++++++++++++++ ...nterpreting-ftp-status-codes-minor-d.patch | 48 +++++++++++++ ...ve-logging-in-status-code-extraction.patch | 28 ++++++++ python-requests-ftp.spec | 64 ++++++++++++++++++ requests-ftp-0.3.1.tar.gz | Bin 0 -> 7850 bytes 6 files changed, 220 insertions(+) create mode 100644 PR28-01-Adding-2-tests-and-updated-statud_code-build.patch create mode 100644 PR28-02-Adding-code-3-to-retr4ieve-status_code.patch create mode 100644 PR28-03-fix-warning-in-interpreting-ftp-status-codes-minor-d.patch create mode 100644 PR28-05-Improve-logging-in-status-code-extraction.patch create mode 100644 python-requests-ftp.spec create mode 100644 requests-ftp-0.3.1.tar.gz diff --git a/PR28-01-Adding-2-tests-and-updated-statud_code-build.patch b/PR28-01-Adding-2-tests-and-updated-statud_code-build.patch new file mode 100644 index 0000000..c8da902 --- /dev/null +++ b/PR28-01-Adding-2-tests-and-updated-statud_code-build.patch @@ -0,0 +1,22 @@ +diff -purN requests-ftp-0.3.1.orig/requests_ftp/ftp.py requests-ftp-0.3.1/requests_ftp/ftp.py +--- requests-ftp-0.3.1.orig/requests_ftp/ftp.py 2015-08-13 16:57:27.000000000 -0400 ++++ requests-ftp-0.3.1/requests_ftp/ftp.py 2018-08-02 10:13:15.801731787 -0400 +@@ -99,7 +99,7 @@ def build_text_response(request, data, c + + def build_binary_response(request, data, code): + '''Build a response for data whose encoding is unknown.''' +- return build_response(request, data, code, None) ++ return build_response(request, data, code, None) + + + def build_response(request, data, code, encoding): +@@ -113,7 +113,8 @@ def build_response(request, data, code, + response.raw = data + response.url = request.url + response.request = request +- response.status_code = int(code.split()[0]) ++ last_valid_line_from_code = [line for line in code.split('\n') if line][-1] ++ response.status_code = int(last_valid_line_from_code.split()[0]) + if hasattr(data, "content_len"): + response.headers['Content-Length'] = str(data.content_len) + diff --git a/PR28-02-Adding-code-3-to-retr4ieve-status_code.patch b/PR28-02-Adding-code-3-to-retr4ieve-status_code.patch new file mode 100644 index 0000000..d746fcf --- /dev/null +++ b/PR28-02-Adding-code-3-to-retr4ieve-status_code.patch @@ -0,0 +1,58 @@ +diff -purN requests-ftp-0.3.1.orig/requests_ftp/ftp.py requests-ftp-0.3.1/requests_ftp/ftp.py +--- requests-ftp-0.3.1.orig/requests_ftp/ftp.py 2018-08-02 10:13:15.801731787 -0400 ++++ requests-ftp-0.3.1/requests_ftp/ftp.py 2018-08-02 10:14:20.633933095 -0400 +@@ -9,6 +9,7 @@ from io import BytesIO + import cgi + import os + import socket ++import logging + + from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout + from requests.exceptions import RequestException +@@ -102,6 +103,35 @@ def build_binary_response(request, data, + return build_response(request, data, code, None) + + ++def get_status_code_from_code_response(code): ++ ''' ++ The idea is to handle complicated code response (even multi lines). ++ We get the status code in two ways: ++ - extracting the code from the last valid line in the response ++ - getting it from the 3first digits in the code ++ After a comparaison between the two values, ++ we can safely set the code or raise a warning. ++ ++ Examples: ++ - get_code('200 Welcome') == 200 ++ ++ - multi_line_code = '226-File successfully transferred\n226 0.000 seconds' ++ get_code(multi_line_code) == 226 ++ ++ - multi_line_with_code_conflits = '200-File successfully transferred\n226 0.000 seconds' ++ get_code(multi_line_with_code_conflits) == 226 ++ ''' ++ last_valid_line_from_code = [line for line in code.split('\n') if line][-1] ++ status_code_from_last_line = int(last_valid_line_from_code.split()[0]) ++ status_code_from_first_digits = int(code[:3]) ++ if status_code_from_last_line != status_code_from_first_digits: ++ logging.warning( ++ 'Status code seems to be non consistant.\n' ++ 'Code received: %d, extracted: %d and %d' % ( ++ code, status_code_from_last_line, status_code_from_first_digits)) ++ return status_code_from_last_line ++ ++ + def build_response(request, data, code, encoding): + '''Builds a response object from the data returned by ftplib, using the + specified encoding.''' +@@ -113,8 +143,8 @@ def build_response(request, data, code, + response.raw = data + response.url = request.url + response.request = request +- last_valid_line_from_code = [line for line in code.split('\n') if line][-1] +- response.status_code = int(last_valid_line_from_code.split()[0]) ++ response.status_code = get_status_code_from_code_response(code) ++ + if hasattr(data, "content_len"): + response.headers['Content-Length'] = str(data.content_len) + diff --git a/PR28-03-fix-warning-in-interpreting-ftp-status-codes-minor-d.patch b/PR28-03-fix-warning-in-interpreting-ftp-status-codes-minor-d.patch new file mode 100644 index 0000000..5a2f8a6 --- /dev/null +++ b/PR28-03-fix-warning-in-interpreting-ftp-status-codes-minor-d.patch @@ -0,0 +1,48 @@ +diff -purN requests-ftp-0.3.1.orig/requests_ftp/ftp.py requests-ftp-0.3.1/requests_ftp/ftp.py +--- requests-ftp-0.3.1.orig/requests_ftp/ftp.py 2018-08-02 10:14:20.633933095 -0400 ++++ requests-ftp-0.3.1/requests_ftp/ftp.py 2018-08-02 10:15:20.491042199 -0400 +@@ -108,26 +108,36 @@ def get_status_code_from_code_response(c + The idea is to handle complicated code response (even multi lines). + We get the status code in two ways: + - extracting the code from the last valid line in the response +- - getting it from the 3first digits in the code +- After a comparaison between the two values, ++ - getting it from the 3 first digits in the code ++ After a comparison between the two values, + we can safely set the code or raise a warning. + + Examples: +- - get_code('200 Welcome') == 200 ++ - get_status_code_from_code_response('200 Welcome') == 200 + + - multi_line_code = '226-File successfully transferred\n226 0.000 seconds' +- get_code(multi_line_code) == 226 ++ get_status_code_from_code_response(multi_line_code) == 226 + +- - multi_line_with_code_conflits = '200-File successfully transferred\n226 0.000 seconds' +- get_code(multi_line_with_code_conflits) == 226 ++ - multi_line_with_code_conflicts = '200-File successfully transferred\n226 0.000 seconds' ++ get_status_code_from_code_response(multi_line_with_code_conflicts) == 226 ++ ++ For more detail see RFC 959, page 36, on multi-line responses: ++ https://www.ietf.org/rfc/rfc959.txt ++ ++ "Thus the format for multi-line replies is that the first line ++ will begin with the exact required reply code, followed ++ immediately by a Hyphen, "-" (also known as Minus), followed by ++ text. The last line will begin with the same code, followed ++ immediately by Space , optionally some text, and the Telnet ++ end-of-line code." + ''' + last_valid_line_from_code = [line for line in code.split('\n') if line][-1] + status_code_from_last_line = int(last_valid_line_from_code.split()[0]) + status_code_from_first_digits = int(code[:3]) + if status_code_from_last_line != status_code_from_first_digits: + logging.warning( +- 'Status code seems to be non consistant.\n' +- 'Code received: %d, extracted: %d and %d' % ( ++ 'FTP response status code seems to be inconsistent.\n' ++ 'Code received: {0}, extracted: {1} and {2}'.format( + code, status_code_from_last_line, status_code_from_first_digits)) + return status_code_from_last_line + diff --git a/PR28-05-Improve-logging-in-status-code-extraction.patch b/PR28-05-Improve-logging-in-status-code-extraction.patch new file mode 100644 index 0000000..6b2c817 --- /dev/null +++ b/PR28-05-Improve-logging-in-status-code-extraction.patch @@ -0,0 +1,28 @@ +diff -purN requests-ftp-0.3.1.orig/requests_ftp/ftp.py requests-ftp-0.3.1/requests_ftp/ftp.py +--- requests-ftp-0.3.1.orig/requests_ftp/ftp.py 2018-08-02 10:15:20.491042199 -0400 ++++ requests-ftp-0.3.1/requests_ftp/ftp.py 2018-08-02 10:16:16.588081626 -0400 +@@ -44,6 +44,7 @@ class FTPSession(requests.Session): + '''Sends an FTP SIZE. Returns a decimal number.''' + return self.request('SIZE', url, **kwargs) + ++log = logging.getLogger(__name__) + + def monkeypatch_session(): + '''Monkeypatch Requests Sessions to provide all the helper +@@ -135,10 +136,13 @@ def get_status_code_from_code_response(c + status_code_from_last_line = int(last_valid_line_from_code.split()[0]) + status_code_from_first_digits = int(code[:3]) + if status_code_from_last_line != status_code_from_first_digits: +- logging.warning( ++ log.warning( + 'FTP response status code seems to be inconsistent.\n' +- 'Code received: {0}, extracted: {1} and {2}'.format( +- code, status_code_from_last_line, status_code_from_first_digits)) ++ 'Code received: %s, extracted: %s and %s', ++ code, ++ status_code_from_last_line, ++ status_code_from_first_digits ++ ) + return status_code_from_last_line + + diff --git a/python-requests-ftp.spec b/python-requests-ftp.spec new file mode 100644 index 0000000..068e579 --- /dev/null +++ b/python-requests-ftp.spec @@ -0,0 +1,64 @@ +%define anolis_release 1 +%global srcname requests-ftp + +Name: python-%{srcname} +Version: 0.3.1 +Release: %{anolis_release}%{?dist} +Summary: FTP transport adapter for python-requests + +License: ASL 2.0 +URL: https://github.com/Lukasa/requests-ftp +Source0: https://pypi.python.org/packages/source/r/%{srcname}/%{srcname}-%{version}.tar.gz + +BuildArch: noarch + +# from https://github.com/Lukasa/requests-ftp/pull/28, handle multi-line responses +# from 4090846 +Patch1: PR28-01-Adding-2-tests-and-updated-statud_code-build.patch +# from 4f6a9f5 +Patch2: PR28-02-Adding-code-3-to-retr4ieve-status_code.patch +# from 3fb2700 +Patch3: PR28-03-fix-warning-in-interpreting-ftp-status-codes-minor-d.patch +# 2caa427 is only test updates, tests not in pypi tarball +# from 7321ab3 +Patch5: PR28-05-Improve-logging-in-status-code-extraction.patch + + +%description +Requests-FTP is an implementation of a very stupid FTP transport adapter for +use with the awesome Requests Python library. + +%package -n python3-%{srcname} +Summary: FTP transport adapter for python3-requests +%{?python_provide:%python_provide python3-%{srcname}} + +BuildRequires: python3-devel +BuildRequires: python3-setuptools + +Requires: python3-requests + +%description -n python3-requests-ftp +Requests-FTP is an implementation of a very stupid FTP transport adapter for +use with the awesome Requests Python library. + +This is the Python 3 version of the transport adapter module. + +%prep +%autosetup -n %{srcname}-%{version} -p1 +rm -rf requests_ftp.egg-info + +%build +%py3_build + +%install +%py3_install + +%files -n python3-%{srcname} +%doc README.rst +%license LICENSE +%{python3_sitelib}/requests_ftp/ +%{python3_sitelib}/requests_ftp*.egg-info* + +%changelog +* Mon Apr 18 2022 Chunmei Xu - 0.3.1-1 +- init from upstream diff --git a/requests-ftp-0.3.1.tar.gz b/requests-ftp-0.3.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f755d36ab404a226c1783fd4a46ed41550d10862 GIT binary patch literal 7850 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