Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-swiftclient for openSUSE:Factory checked in at 2024-06-05 17:42:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-swiftclient (Old) and /work/SRC/openSUSE:Factory/.python-swiftclient.new.24587 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-swiftclient" Wed Jun 5 17:42:10 2024 rev:35 rq:1178611 version:4.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-swiftclient/python-swiftclient.changes 2024-01-15 22:19:28.383382146 +0100 +++ /work/SRC/openSUSE:Factory/.python-swiftclient.new.24587/python-swiftclient.changes 2024-06-05 17:42:48.134983980 +0200 @@ -1,0 +2,27 @@ +Tue Jun 4 20:08:29 UTC 2024 - cloud-de...@suse.de + +- update to version 4.6.0 + - make setup dependencies discoverable + - tests: Fix call assertion + - Fix swiftclient output regression + - Add transaction id to errors + - reno: Update master for unmaintained/zed + - reno: Update master for unmaintained/victoria + - CI: constrain deps for tests + - Mark py312 job as voting and update classifiers + - Update master for stable/2023.2 + - CI: skip func tests on irrelevant changes + - CI: add py39 and py310 to experimental pipeline + - Bring back (experimental) py38 job + - tests: Skip keystoneauth tests if not available + - Update master for stable/2024.1 + - shell: Print friendly account byte quotas + - Authors / changelog for 4.5.0 + - reno: Update master for unmaintained/yoga + - lint: Up-rev hacking + - CI: Fix py36 and py37 jobs + - Remove duplicate script entry leading to broken wheel build + - reno: Update master for unmaintained/xena + - reno: Update master for unmaintained/wallaby + +------------------------------------------------------------------- Old: ---- python-swiftclient-4.4.0.tar.gz New: ---- python-swiftclient-4.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-swiftclient.spec ++++++ --- /var/tmp/diff_new_pack.SzPt7N/_old 2024-06-05 17:42:50.543071676 +0200 +++ /var/tmp/diff_new_pack.SzPt7N/_new 2024-06-05 17:42:50.547071823 +0200 @@ -17,13 +17,13 @@ Name: python-swiftclient -Version: 4.4.0 +Version: 4.6.0 Release: 0 Summary: OpenStack Object Storage API Client Library License: Apache-2.0 Group: Development/Languages/Python URL: https://docs.openstack.org/python-swiftclient -Source0: https://files.pythonhosted.org/packages/source/p/python-swiftclient/python-swiftclient-4.4.0.tar.gz +Source0: https://files.pythonhosted.org/packages/source/p/python-swiftclient/python-swiftclient-4.6.0.tar.gz BuildRequires: openstack-macros BuildRequires: python3-keystoneclient BuildRequires: python3-pbr @@ -37,7 +37,7 @@ %package -n python3-swiftclient Summary: OpenStack Object Storage API Client Library -Requires: python3-requests >= 1.1.0 +Requires: python3-requests >= 2.4.0 %if 0%{?suse_version} Obsoletes: python2-swiftclient < 3.9.0 %endif @@ -61,7 +61,7 @@ This package contains documentation files for %{name}. %prep -%autosetup -p1 -n python-swiftclient-4.4.0 +%autosetup -p1 -n python-swiftclient-4.6.0 %py_req_cleanup %build ++++++ _service ++++++ --- /var/tmp/diff_new_pack.SzPt7N/_old 2024-06-05 17:42:50.591073424 +0200 +++ /var/tmp/diff_new_pack.SzPt7N/_new 2024-06-05 17:42:50.595073571 +0200 @@ -2,7 +2,7 @@ <service mode="manual" name="renderspec"> <param name="input-template">https://opendev.org/openstack/rpm-packaging/raw/master/openstack/python-swiftclient/python-swiftclient.spec.j2</param> <param name="output-name">python-swiftclient.spec</param> - <param name="requirements">https://opendev.org/openstack/python-swiftclient/raw/branch/stable/xena/requirements.txt</param> + <param name="requirements">https://opendev.org/openstack/python-swiftclient/raw/master/requirements.txt</param> <param name="changelog-email">cloud-de...@suse.de</param> <param name="changelog-provider">gh,openstack,python-swiftclient</param> </service> ++++++ python-swiftclient-4.4.0.tar.gz -> python-swiftclient-4.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/.mailmap new/python-swiftclient-4.6.0/.mailmap --- old/python-swiftclient-4.4.0/.mailmap 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/.mailmap 2024-05-23 10:01:23.000000000 +0200 @@ -97,3 +97,4 @@ Flavio Percoco <flape...@gmail.com> Timur Alperovich <timur...@swiftstack.com> <ti...@timuralp.com> Thiago da Silva <thiagodasi...@gmail.com> <thi...@redhat.com> +DavHau <hsngr...@gmail.com> <hsngrmpf+git...@gmail.com> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/.zuul.yaml new/python-swiftclient-4.6.0/.zuul.yaml --- old/python-swiftclient-4.4.0/.zuul.yaml 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/.zuul.yaml 2024-05-23 10:01:23.000000000 +0200 @@ -37,21 +37,33 @@ - publish-openstack-docs-pti - release-notes-jobs-python3 experimental: - # on-demand pipeline used to test older (but still supported) versions of python + # on-demand pipeline used to test older (but still supported) versions of python, + # as well as intermediate releases that the openstack-python3-jobs might skip jobs: - openstack-tox-py36 - openstack-tox-py37 + - openstack-tox-py38 + - openstack-tox-py310 check: jobs: - - swiftclient-swift-functional - - swiftclient-functional - - openstack-tox-py311: + - swiftclient-functional: + irrelevant-files: &functest-irrelevant-files + - ^(doc|releasenotes)/.*$ + - ^test/unit/.*$ + - ^(.gitreview|.mailmap|AUTHORS|ChangeLog|.*\.rst)$ + - swiftclient-swift-functional: + irrelevant-files: *functest-irrelevant-files + - tempest-full-py3: + irrelevant-files: *functest-irrelevant-files + - openstack-tox-py311 + - openstack-tox-py312: voting: true gate: jobs: - swiftclient-swift-functional - swiftclient-functional - - openstack-tox-py311: + - openstack-tox-py311 + - openstack-tox-py312: voting: true post: jobs: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/AUTHORS new/python-swiftclient-4.6.0/AUTHORS --- old/python-swiftclient-4.4.0/AUTHORS 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/AUTHORS 2024-05-23 10:01:23.000000000 +0200 @@ -30,6 +30,7 @@ Dan Prince (dpri...@redhat.com) Daniel Wakefield (daniel.wakefi...@hp.com) Darrell Bishop (darr...@swiftstack.com) +DavHau (hsngr...@gmail.com) David Goetz (david.go...@rackspace.com) David Kranz (david.kr...@qrclab.com) David Shrewsbury (shrewsbury.d...@gmail.com) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/ChangeLog new/python-swiftclient-4.6.0/ChangeLog --- old/python-swiftclient-4.4.0/ChangeLog 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/ChangeLog 2024-05-23 10:01:23.000000000 +0200 @@ -1,3 +1,15 @@ +4.5.0 +----- + +* `swift stat --lh` now prints account quotas (including per-policy quotas) + in human-readable units, similar to account usage values. + +* Modernized some aspects of packaging, allowing wheels to be built with more + (and more recent) tools. + +* Various other minor bug fixes and improvements. + + 4.4.0 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/PKG-INFO new/python-swiftclient-4.6.0/PKG-INFO --- old/python-swiftclient-4.4.0/PKG-INFO 2023-09-01 15:39:15.668769100 +0200 +++ new/python-swiftclient-4.6.0/PKG-INFO 2024-05-23 10:01:55.497597500 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: python-swiftclient -Version: 4.4.0 +Version: 4.6.0 Summary: OpenStack Object Storage API Client Library Home-page: https://docs.openstack.org/python-swiftclient/latest/ Author: OpenStack @@ -76,6 +76,7 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.6 Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/pyproject.toml new/python-swiftclient-4.6.0/pyproject.toml --- old/python-swiftclient-4.4.0/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-swiftclient-4.6.0/pyproject.toml 2024-05-23 10:01:23.000000000 +0200 @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "pbr"] +build-backend = "setuptools.build_meta" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/python_swiftclient.egg-info/PKG-INFO new/python-swiftclient-4.6.0/python_swiftclient.egg-info/PKG-INFO --- old/python-swiftclient-4.4.0/python_swiftclient.egg-info/PKG-INFO 2023-09-01 15:39:15.000000000 +0200 +++ new/python-swiftclient-4.6.0/python_swiftclient.egg-info/PKG-INFO 2024-05-23 10:01:55.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: python-swiftclient -Version: 4.4.0 +Version: 4.6.0 Summary: OpenStack Object Storage API Client Library Home-page: https://docs.openstack.org/python-swiftclient/latest/ Author: OpenStack @@ -76,6 +76,7 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.6 Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/python_swiftclient.egg-info/SOURCES.txt new/python-swiftclient-4.6.0/python_swiftclient.egg-info/SOURCES.txt --- old/python-swiftclient-4.4.0/python_swiftclient.egg-info/SOURCES.txt 2023-09-01 15:39:15.000000000 +0200 +++ new/python-swiftclient-4.6.0/python_swiftclient.egg-info/SOURCES.txt 2024-05-23 10:01:55.000000000 +0200 @@ -11,6 +11,7 @@ LICENSE MANIFEST.in README.rst +pyproject.toml requirements.txt run_tests.sh setup.cfg @@ -58,7 +59,10 @@ releasenotes/notes/3_9_0_release-3c293d277f14ec22.yaml releasenotes/notes/4_3_0_release.yaml releasenotes/notes/4_4_0_release-d731bab5982c160b.yaml +releasenotes/notes/4_5_0_release-b315d25b889293f2.yaml releasenotes/source/2023.1.rst +releasenotes/source/2023.2.rst +releasenotes/source/2024.1.rst releasenotes/source/conf.py releasenotes/source/current.rst releasenotes/source/index.rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/python_swiftclient.egg-info/pbr.json new/python-swiftclient-4.6.0/python_swiftclient.egg-info/pbr.json --- old/python-swiftclient-4.4.0/python_swiftclient.egg-info/pbr.json 2023-09-01 15:39:15.000000000 +0200 +++ new/python-swiftclient-4.6.0/python_swiftclient.egg-info/pbr.json 2024-05-23 10:01:55.000000000 +0200 @@ -1 +1 @@ -{"git_version": "54fbfa8", "is_release": true} \ No newline at end of file +{"git_version": "e7061db", "is_release": true} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/python_swiftclient.egg-info/requires.txt new/python-swiftclient-4.6.0/python_swiftclient.egg-info/requires.txt --- old/python-swiftclient-4.4.0/python_swiftclient.egg-info/requires.txt 2023-09-01 15:39:15.000000000 +0200 +++ new/python-swiftclient-4.6.0/python_swiftclient.egg-info/requires.txt 2024-05-23 10:01:55.000000000 +0200 @@ -5,7 +5,8 @@ [test] coverage!=4.4,>=4.0 -hacking<3.3.0,>=3.2.0 +hacking<6.2.0,>=3.2.0 keystoneauth1>=3.4.0 openstacksdk>=0.11.0 +python-keystoneclient>=0.7.0 stestr!=3.0.0,>=2.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/notes/4_5_0_release-b315d25b889293f2.yaml new/python-swiftclient-4.6.0/releasenotes/notes/4_5_0_release-b315d25b889293f2.yaml --- old/python-swiftclient-4.4.0/releasenotes/notes/4_5_0_release-b315d25b889293f2.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/python-swiftclient-4.6.0/releasenotes/notes/4_5_0_release-b315d25b889293f2.yaml 2024-05-23 10:01:23.000000000 +0200 @@ -0,0 +1,12 @@ +--- +features: + - | + ``swift stat --lh`` now prints account quotas (including per-policy quotas) + in human-readable units, similar to account usage values. + + - | + Modernized some aspects of packaging, allowing wheels to be built with more + (and more recent) tools. +fixes: + - | + Various other minor bug fixes and improvements. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/2023.2.rst new/python-swiftclient-4.6.0/releasenotes/source/2023.2.rst --- old/python-swiftclient-4.4.0/releasenotes/source/2023.2.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/python-swiftclient-4.6.0/releasenotes/source/2023.2.rst 2024-05-23 10:01:23.000000000 +0200 @@ -0,0 +1,6 @@ +=========================== +2023.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2023.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/2024.1.rst new/python-swiftclient-4.6.0/releasenotes/source/2024.1.rst --- old/python-swiftclient-4.4.0/releasenotes/source/2024.1.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/python-swiftclient-4.6.0/releasenotes/source/2024.1.rst 2024-05-23 10:01:23.000000000 +0200 @@ -0,0 +1,6 @@ +=========================== +2024.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/index.rst new/python-swiftclient-4.6.0/releasenotes/source/index.rst --- old/python-swiftclient-4.4.0/releasenotes/source/index.rst 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/releasenotes/source/index.rst 2024-05-23 10:01:23.000000000 +0200 @@ -6,6 +6,8 @@ :maxdepth: 1 current + 2024.1 + 2023.2 2023.1 zed yoga diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/victoria.rst new/python-swiftclient-4.6.0/releasenotes/source/victoria.rst --- old/python-swiftclient-4.4.0/releasenotes/source/victoria.rst 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/releasenotes/source/victoria.rst 2024-05-23 10:01:23.000000000 +0200 @@ -3,4 +3,4 @@ ============================= .. release-notes:: - :branch: stable/victoria + :branch: unmaintained/victoria diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/wallaby.rst new/python-swiftclient-4.6.0/releasenotes/source/wallaby.rst --- old/python-swiftclient-4.4.0/releasenotes/source/wallaby.rst 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/releasenotes/source/wallaby.rst 2024-05-23 10:01:23.000000000 +0200 @@ -3,4 +3,4 @@ ============================ .. release-notes:: - :branch: stable/wallaby + :branch: unmaintained/wallaby diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/xena.rst new/python-swiftclient-4.6.0/releasenotes/source/xena.rst --- old/python-swiftclient-4.4.0/releasenotes/source/xena.rst 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/releasenotes/source/xena.rst 2024-05-23 10:01:23.000000000 +0200 @@ -3,4 +3,4 @@ ========================= .. release-notes:: - :branch: stable/xena + :branch: unmaintained/xena diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/yoga.rst new/python-swiftclient-4.6.0/releasenotes/source/yoga.rst --- old/python-swiftclient-4.4.0/releasenotes/source/yoga.rst 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/releasenotes/source/yoga.rst 2024-05-23 10:01:23.000000000 +0200 @@ -3,4 +3,4 @@ ========================= .. release-notes:: - :branch: stable/yoga + :branch: unmaintained/yoga diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/releasenotes/source/zed.rst new/python-swiftclient-4.6.0/releasenotes/source/zed.rst --- old/python-swiftclient-4.4.0/releasenotes/source/zed.rst 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/releasenotes/source/zed.rst 2024-05-23 10:01:23.000000000 +0200 @@ -3,4 +3,4 @@ ======================== .. release-notes:: - :branch: stable/zed + :branch: unmaintained/zed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/setup.cfg new/python-swiftclient-4.6.0/setup.cfg --- old/python-swiftclient-4.4.0/setup.cfg 2023-09-01 15:39:15.668769100 +0200 +++ new/python-swiftclient-4.6.0/setup.cfg 2024-05-23 10:01:55.497597500 +0200 @@ -29,13 +29,12 @@ Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Programming Language :: Python :: 3 :: Only [files] packages = swiftclient -scripts = - bin/swift data_files = share/man/man1 = doc/manpages/swift.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/swiftclient/client.py new/python-swiftclient-4.6.0/swiftclient/client.py --- old/python-swiftclient-4.4.0/swiftclient/client.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/swiftclient/client.py 2024-05-23 10:01:23.000000000 +0200 @@ -19,10 +19,13 @@ import socket import re import logging +from urllib3.exceptions import HTTPError as urllib_http_error + import warnings from requests.exceptions import RequestException, SSLError import http.client as http_client +from requests.structures import CaseInsensitiveDict from urllib.parse import quote, unquote from urllib.parse import urljoin, urlparse, urlunparse from time import sleep, time @@ -209,6 +212,15 @@ return ret +class LowerKeyCaseInsensitiveDict(CaseInsensitiveDict): + """ + CaseInsensitiveDict returning lower case keys for items() + """ + + def __iter__(self): + return iter(self._store.keys()) + + class _ObjectBody: """ Readable and iterable object body response wrapper. @@ -287,7 +299,7 @@ try: buf = self.resp.read(length) self.bytes_read += len(buf) - except (socket.error, RequestException): + except (socket.error, urllib_http_error, RequestException): if self.conn.attempts > self.conn.retries: raise if (not buf and self.bytes_read < self.expected_length and @@ -735,9 +747,9 @@ def resp_header_dict(resp): - resp_headers = {} + resp_headers = LowerKeyCaseInsensitiveDict() for header, value in resp.getheaders(): - header = parse_header_string(header).lower() + header = parse_header_string(header) resp_headers[header] = parse_header_string(value) return resp_headers @@ -1926,6 +1938,7 @@ is_not_range_request and resp_chunk_size and self.attempts <= self.retries and rheaders.get('transfer-encoding') is None) + if retry_is_possible: body = _RetryBody(body.resp, self, container, obj, resp_chunk_size=resp_chunk_size, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/swiftclient/command_helpers.py new/python-swiftclient-4.6.0/swiftclient/command_helpers.py --- old/python-swiftclient-4.4.0/swiftclient/command_helpers.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/swiftclient/command_helpers.py 2024-05-23 10:01:23.000000000 +0200 @@ -15,6 +15,7 @@ POLICY_HEADER_PREFIX = 'x-account-storage-policy-' +PER_POLICY_QUOTA_HEADER_PREFIX = 'x-account-quota-bytes-policy-' def stat_account(conn, options): @@ -38,6 +39,10 @@ ('Objects', object_count), ('Bytes', bytes_used), ]) + if headers.get('x-account-meta-quota-bytes'): + quota_bytes = prt_bytes(headers.get('x-account-meta-quota-bytes'), + options['human']).lstrip() + items.append(('Quota Bytes', quota_bytes)) policies = set() for header_key, header_value in headers.items(): @@ -68,6 +73,10 @@ options['human'] ).lstrip()), )) + policy_quota = headers.get(PER_POLICY_QUOTA_HEADER_PREFIX + policy) + if policy_quota: + items.append(('Quota Bytes for policy "' + policy + '"', + prt_bytes(policy_quota, options['human']).lstrip())) return items, headers @@ -75,7 +84,10 @@ def print_account_stats(items, headers, output_manager): exclude_policy_headers = [] for header_key, header_value in headers.items(): - if header_key.lower().startswith(POLICY_HEADER_PREFIX): + if header_key.lower().startswith(( + POLICY_HEADER_PREFIX, + PER_POLICY_QUOTA_HEADER_PREFIX, + )): exclude_policy_headers.append(header_key) items.extend(headers_to_items( @@ -84,7 +96,8 @@ 'content-length', 'date', 'x-account-container-count', 'x-account-object-count', - 'x-account-bytes-used'] + exclude_policy_headers))) + 'x-account-bytes-used', + 'x-account-meta-quota-bytes'] + exclude_policy_headers))) # line up the items nicely offset = max(len(item) for item, value in items) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/swiftclient/multithreading.py new/python-swiftclient-4.6.0/swiftclient/multithreading.py --- old/python-swiftclient-4.4.0/swiftclient/multithreading.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/swiftclient/multithreading.py 2024-05-23 10:01:23.000000000 +0200 @@ -89,6 +89,10 @@ msg = msg % fmt_args self.error_print_pool.submit(self._print_error, msg) + def error_with_txn_id(self, swift_err): + self.error("{}\nFailed Transaction ID: {}".format( + swift_err.value, swift_err.transaction_id or 'unknown')) + def get_error_count(self): return self.error_count diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/swiftclient/service.py new/python-swiftclient-4.6.0/swiftclient/service.py --- old/python-swiftclient-4.4.0/swiftclient/service.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/swiftclient/service.py 2024-05-23 10:01:23.000000000 +0200 @@ -32,6 +32,9 @@ from threading import Thread from queue import Queue from queue import Empty as QueueEmpty +from requests.exceptions import RequestException +from socket import error as socket_error +from urllib3.exceptions import HTTPError as urllib_http_error from urllib.parse import quote import json @@ -68,12 +71,16 @@ class SwiftError(Exception): def __init__(self, value, container=None, obj=None, - segment=None, exc=None): + segment=None, exc=None, transaction_id=None): self.value = value self.container = container self.obj = obj self.segment = segment self.exception = exc + if transaction_id is None: + self.transaction_id = getattr(exc, 'transaction_id', None) + else: + self.transaction_id = transaction_id def __str__(self): value = repr(self.value) @@ -459,7 +466,9 @@ try: self._content_length = int(headers.get('content-length')) except ValueError: - raise SwiftError('content-length header must be an integer') + raise SwiftError( + 'content-length header must be an integer', + transaction_id=self._txn_id) def __iter__(self): for chunk in self._body: @@ -1306,9 +1315,15 @@ else: pseudodir = True - for chunk in obj_body: - if fp is not None: - fp.write(chunk) + try: + for chunk in obj_body: + if fp is not None: + fp.write(chunk) + except (socket_error, + urllib_http_error, + RequestException) as err: + raise ClientException( + str(err), http_response_headers=headers) finish_time = time() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/swiftclient/shell.py new/python-swiftclient-4.6.0/swiftclient/shell.py --- old/python-swiftclient-4.4.0/swiftclient/shell.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/swiftclient/shell.py 2024-05-23 10:01:23.000000000 +0200 @@ -227,7 +227,7 @@ output_manager.error('Error Deleting: {0}: {1}' .format(p, r['error'])) except SwiftError as err: - output_manager.error(err.value) + output_manager.error_with_txn_id(err) st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>] @@ -484,11 +484,13 @@ "Object '%s/%s' not found", container, obj) continue output_manager.error( - "Error downloading object '%s/%s': %s", - container, obj, error) + "Error downloading object '%s/%s': %s\n" + "Failed Transaction ID: %s", + container, obj, error, + getattr(error, 'transaction_id', 'unknown')) except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) except Exception as e: output_manager.error(e) @@ -670,7 +672,7 @@ prt_bytes(totals['bytes'], human)) except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) st_stat_options = '''[--lh] [--header <header:value>] @@ -754,21 +756,21 @@ items, headers, output_manager ) else: - raise(stat_result["error"]) + raise stat_result["error"] else: output_manager.error( 'Usage: %s stat %s\n%s', BASENAME, st_stat_options, st_stat_help) except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) st_post_options = '''[--read-acl <acl>] [--write-acl <acl>] [--sync-to <sync-to>] [--sync-key <sync-key>] [--meta <name:value>] [--header <header>] [<container> [<object>]] -''' +''' # noqa st_post_help = ''' Updates meta information for the account, container, or object. @@ -864,10 +866,10 @@ else: result = swift.post(container=container) if not result["success"]: - raise(result["error"]) + raise result["error"] except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) st_copy_options = '''[--destination </container/object>] [--fresh-metadata] @@ -972,7 +974,7 @@ return except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) st_upload_options = '''[--changed] [--skip-identical] [--segment-size <size>] @@ -1270,7 +1272,7 @@ "to chunk the object") except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) st_capabilities_options = '''[--json] [<proxy_url>] @@ -1332,7 +1334,7 @@ del capabilities['swift'] _print_compo_cap('Additional middleware', capabilities) except SwiftError as e: - output_manager.error(e.value) + output_manager.error_with_txn_id(e) st_info = st_capabilities @@ -1520,7 +1522,7 @@ Optional positional arguments: <command> Swift client command to filter the flags by. -'''.strip('\n') +'''.strip('\n') # noqa st_bash_completion_options = '''[command] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/test/unit/test_authv1.py new/python-swiftclient-4.6.0/test/unit/test_authv1.py --- old/python-swiftclient-4.4.0/test/unit/test_authv1.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/test/unit/test_authv1.py 2024-05-23 10:01:23.000000000 +0200 @@ -16,10 +16,13 @@ import json import unittest from unittest import mock -from keystoneauth1 import plugin -from keystoneauth1 import loading -from keystoneauth1 import exceptions -from swiftclient import authv1 +try: + from keystoneauth1 import plugin + from keystoneauth1 import loading + from keystoneauth1 import exceptions + from swiftclient import authv1 +except ImportError: + plugin = loading = exceptions = authv1 = None class TestDataNoAccount: @@ -44,6 +47,10 @@ class TestPluginLoading(TestDataNoAccount, unittest.TestCase): + def setUp(self): + if authv1 is None: + raise unittest.SkipTest('keystoneauth1 is not available') + def test_can_load(self): loader = loading.get_plugin_loader('v1password') self.assertIsInstance(loader, authv1.PasswordLoader) @@ -120,6 +127,8 @@ class TestPlugin(TestDataNoAccount, unittest.TestCase): def setUp(self): + if authv1 is None: + raise unittest.SkipTest('keystoneauth1 is not available') self.mock_session = mock.MagicMock() self.mock_response = self.mock_session.get.return_value self.mock_response.status_code = 200 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/test/unit/test_command_helpers.py new/python-swiftclient-4.6.0/test/unit/test_command_helpers.py --- old/python-swiftclient-4.4.0/test/unit/test_command_helpers.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/test/unit/test_command_helpers.py 2024-05-23 10:01:23.000000000 +0200 @@ -18,6 +18,7 @@ from unittest import mock from swiftclient import command_helpers as h +from swiftclient.client import LowerKeyCaseInsensitiveDict from swiftclient.multithreading import OutputManager @@ -233,6 +234,33 @@ self.conn.head_object.return_value = stub_headers args = ('c', 'o') with self.output_manager as output_manager: + items, headers = h.stat_object(self.conn, self.options, *args) + h.print_object_stats(items, headers, output_manager) + expected = """ + URL: http://storage/v1/a/c/o + Auth Token: tk12345 + Account: a + Container: c + Object: o + Content Length: 1048576 + ETag: 68b329da9893e34099c7d8ad5cb9c940 + Meta Color: blue +Content-Encoding: gzip +""" + self.assertOut(expected) + + def test_stat_object_case_insensitive_headers(self): + self.options['verbose'] += 1 + # stub head object request + stub_headers = LowerKeyCaseInsensitiveDict({ + 'content-length': 2 ** 20, + 'x-object-meta-color': 'blue', + 'ETag': '68b329da9893e34099c7d8ad5cb9c940', + 'content-encoding': 'gzip', + }) + self.conn.head_object.return_value = stub_headers + args = ('c', 'o') + with self.output_manager as output_manager: items, headers = h.stat_object(self.conn, self.options, *args) h.print_object_stats(items, headers, output_manager) expected = """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/test/unit/test_service.py new/python-swiftclient-4.6.0/test/unit/test_service.py --- old/python-swiftclient-4.4.0/test/unit/test_service.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/test/unit/test_service.py 2024-05-23 10:01:23.000000000 +0200 @@ -31,7 +31,9 @@ import swiftclient import swiftclient.utils as utils -from swiftclient.client import Connection, ClientException +from swiftclient.client import ( + Connection, ClientException, LowerKeyCaseInsensitiveDict +) from swiftclient.service import ( SwiftService, SwiftError, SwiftUploadObject, SwiftDeleteObject ) @@ -167,8 +169,10 @@ self.assertIsNone(sr._actual_md5) # Check Contentlength raises error if it isn't an integer - self.assertRaises(SwiftError, self.sr, 'path', 'body', - {'content-length': 'notanint'}) + with self.assertRaises(SwiftError) as cm: + self.sr('path', 'body', {'content-length': 'notanint'}) + self.assertEqual("'content-length header must be an integer'", + str(cm.exception)) def test_iterator_usage(self): def _consume(sr): @@ -239,6 +243,26 @@ self.assertEqual(sr._actual_md5.hexdigest(), md5('abc'.encode() * 3).hexdigest()) + def test_swift_reader_knows_slo_etag_is_not_md5(self): + segment_bodies = [b'abc', b'def', b'ghi'] + # slo etag is md5 of the sum of md5 of segments + slo_etag = md5(b''.join( + md5(b).hexdigest().encode() + for b in segment_bodies + )).hexdigest() + headers = LowerKeyCaseInsensitiveDict({ + 'Content-Length': len(b''.join(segment_bodies)), + 'X-Static-Large-Object': 'true', + 'ETag': '"%s"' % slo_etag + }) + sr = self.sr('path', segment_bodies, headers) + # x-static-large-object; so no exception is raised! + actual_md5 = md5(b''.join(sr)).hexdigest() + self.assertEqual(sr._actual_read, 9) + self.assertIsNone(sr._actual_md5) + self.assertEqual(actual_md5, + md5(b''.join(segment_bodies)).hexdigest()) + class _TestServiceBase(unittest.TestCase): def _get_mock_connection(self, attempts=2): @@ -653,6 +677,7 @@ self.assertIsNone(se.exception) self.assertEqual(str(se), '5') + self.assertNotIn(str(se), 'Transaction ID') def test_swifterror_creation(self): test_exc = Exception('test exc') @@ -665,6 +690,25 @@ self.assertEqual(se.exception, test_exc) self.assertEqual(str(se), '5 container:con object:obj segment:seg') + self.assertNotIn(str(se), 'Transaction ID') + + def test_swifterror_clientexception_creation(self): + test_exc = ClientException( + Exception('test exc'), + http_response_headers=LowerKeyCaseInsensitiveDict({ + 'x-trans-id': 'someTransId'}) + ) + se = SwiftError(5, 'con', 'obj', 'seg', test_exc) + + self.assertEqual(se.value, 5) + self.assertEqual(se.container, 'con') + self.assertEqual(se.obj, 'obj') + self.assertEqual(se.segment, 'seg') + self.assertEqual(se.exception, test_exc) + + self.assertEqual('someTransId', se.transaction_id) + self.assertNotIn('someTransId', str(se)) + self.assertIn('5 container:con object:obj segment:seg', str(se)) class TestServiceUtils(unittest.TestCase): @@ -1330,7 +1374,7 @@ options) responses = [x for x in resp_iter] for resp in responses: - self.assertFalse('error' in resp) + self.assertNotIn('error', resp) self.assertTrue(resp['success']) self.assertEqual(5, len(responses)) container_resp, segment_container_resp = responses[0:2] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/test/unit/test_shell.py new/python-swiftclient-4.6.0/test/unit/test_shell.py --- old/python-swiftclient-4.4.0/test/unit/test_shell.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/test/unit/test_shell.py 2024-05-23 10:01:23.000000000 +0200 @@ -15,6 +15,7 @@ import io import contextlib +import socket from genericpath import getmtime import getpass import hashlib @@ -26,8 +27,11 @@ from unittest import mock import textwrap from time import localtime, mktime, strftime, strptime +from requests.exceptions import RequestException +from urllib3.exceptions import HTTPError import swiftclient +from swiftclient.client import LowerKeyCaseInsensitiveDict from swiftclient.service import SwiftError import swiftclient.shell import swiftclient.utils @@ -160,6 +164,34 @@ ' Bytes: 3\n') @mock.patch('swiftclient.service.Connection') + def test_stat_account_with_quota(self, connection): + argv = ["", "stat", "--lh"] + return_headers = { + 'x-account-container-count': '2000', + 'x-account-object-count': '3000', + 'x-account-bytes-used': '4000000', + 'x-account-storage-policy-gold-bytes-used': '4000', + 'x-account-meta-quota-bytes': '5000000', + 'x-account-quota-bytes-policy-gold': '5000', + 'content-length': 0, + 'date': ''} + connection.return_value.head_account.return_value = return_headers + connection.return_value.url = 'http://127.0.0.1/v1/AUTH_account' + with CaptureOutput() as output: + swiftclient.shell.main(argv) + + self.assertEqual( + output.out, + ' Account: AUTH_account\n' + ' Containers: 2000\n' + ' Objects: 2.9K\n' + ' Bytes: 3.8M\n' + ' Quota Bytes: 4.8M\n' + ' Objects in policy "gold": 0\n' + ' Bytes in policy "gold": 3.9K\n' + 'Quota Bytes for policy "gold": 4.9K\n') + + @mock.patch('swiftclient.service.Connection') def test_stat_account_with_headers(self, connection): argv = ["", "stat", "-H", "Skip-Middleware: Test"] return_headers = { @@ -208,6 +240,28 @@ ' Sync Key: secret\n') @mock.patch('swiftclient.service.Connection') + def test_stat_container_not_found(self, connection): + connection.return_value.head_container.side_effect = \ + swiftclient.ClientException( + 'test', + http_status=404, + http_response_headers=LowerKeyCaseInsensitiveDict({ + 'x-trans-id': 'someTransId'}) + ) + argv = ["", "stat", "container"] + + with CaptureOutput() as output: + with self.assertRaises(SystemExit): + swiftclient.shell.main(argv) + connection.return_value.head_container.assert_called_with( + 'container', headers={}, resp_chunk_size=65536, + response_dict={}) + + self.assertIn('Container \'container\' not found\n' + 'Failed Transaction ID: someTransId', + output.err) + + @mock.patch('swiftclient.service.Connection') def test_stat_container_with_headers(self, connection): return_headers = { 'x-container-object-count': '1', @@ -286,6 +340,27 @@ ' Manifest: manifest\n') @mock.patch('swiftclient.service.Connection') + def test_stat_object_not_found(self, connection): + connection.return_value.head_object.side_effect = \ + swiftclient.ClientException( + 'test', http_status=404, + http_response_headers=LowerKeyCaseInsensitiveDict({ + 'x-trans-id': 'someTransId'}) + ) + argv = ["", "stat", "container", "object"] + + with CaptureOutput() as output: + with self.assertRaises(SystemExit): + swiftclient.shell.main(argv) + connection.return_value.head_object.assert_called_with( + 'container', 'object', headers={}, resp_chunk_size=65536, + response_dict={}) + + self.assertIn('test: 404\n' + 'Failed Transaction ID: someTransId', + output.err) + + @mock.patch('swiftclient.service.Connection') def test_stat_object_with_headers(self, connection): return_headers = { 'x-object-manifest': 'manifest', @@ -707,11 +782,122 @@ swiftclient.shell.main(argv) self.assertEqual('objcontent', output.out) + def _do_test_download_clientexception(self, exc): + retry_calls = [] + + def _fake_retry(conn, *args, **kwargs): + retry_calls.append((args, kwargs)) + conn.attempts += 1 + + body = mock.MagicMock() + body.resp.read.side_effect = RequestException('test_exc') + return (LowerKeyCaseInsensitiveDict({ + 'content-type': 'text/plain', + 'etag': '2cbbfe139a744d6abbe695e17f3c1991', + 'x-trans-id': 'someTransId'}), + body) + + argv = ["", "download", "container", "object", "--retries", "1"] + with CaptureOutput() as output: + with mock.patch(BUILTIN_OPEN) as mock_open: + with mock.patch("swiftclient.service.Connection._retry", + _fake_retry): + with self.assertRaises(SystemExit): + swiftclient.shell.main(argv) + mock_open.assert_called_with('object', 'wb', 65536) + self.assertEqual([ + ((None, swiftclient.client.get_object, 'container', 'object'), + {'headers': {}, + 'query_string': None, + 'resp_chunk_size': 65536, + 'response_dict': {}}), + ((None, swiftclient.client.get_object, 'container', 'object'), + {'attempts': 1, + 'headers': {'If-Match': mock.ANY, 'Range': 'bytes=0-'}, + 'query_string': None, + 'resp_chunk_size': 65536, + 'response_dict': {}})], + retry_calls) + self.assertIn('Error downloading object \'container/object\': ' + 'test_exc', + str(output.err)) + self.assertIn('someTransId', str(output.err)) + + def test_download_request_exception(self): + self._do_test_download_clientexception(RequestException('text_exc')) + + def test_download_socket_error(self): + self._do_test_download_clientexception(socket.error()) + + def test_download_http_error(self): + self._do_test_download_clientexception(HTTPError) + + def test_download_request_exception_retries_0(self): + retry_calls = [] + + def _fake_retry(conn, *args, **kwargs): + retry_calls.append((args, kwargs)) + conn.attempts += 1 + + body = mock.MagicMock() + body.__iter__.side_effect = RequestException('test_exc') + return (LowerKeyCaseInsensitiveDict({ + 'content-type': 'text/plain', + 'etag': '2cbbfe139a744d6abbe695e17f3c1991', + 'x-trans-id': 'someTransId'}), + body) + + argv = ["", "download", "container", "object", "--retries", "0"] + with CaptureOutput() as output: + with mock.patch(BUILTIN_OPEN) as mock_open: + with mock.patch("swiftclient.service.Connection._retry", + _fake_retry): + with self.assertRaises(SystemExit): + swiftclient.shell.main(argv) + mock_open.assert_called_with('object', 'wb', 65536) + self.assertEqual([ + ((None, swiftclient.client.get_object, 'container', 'object'), + {'headers': {}, + 'query_string': None, + 'resp_chunk_size': 65536, + 'response_dict': {}}), ], + retry_calls) + self.assertIn('Error downloading object \'container/object\': ' + 'test_exc', + str(output.err)) + self.assertIn('someTransId', str(output.err)) + + @mock.patch('swiftclient.service.Connection') + def test_download_bad_content_length(self, connection): + objcontent = io.BytesIO(b'objcontent') + connection.return_value.get_object.side_effect = [ + (LowerKeyCaseInsensitiveDict({ + 'content-type': 'text/plain', + 'content-length': 'BAD', + 'etag': '2cbbfe139a744d6abbe695e17f3c1991', + 'x-trans-id': 'someTransId'}), + objcontent) + ] + with CaptureOutput() as output: + with self.assertRaises(SystemExit): + with mock.patch(BUILTIN_OPEN) as mock_open: + argv = ["", "download", "container", "object"] + swiftclient.shell.main(argv) + connection.return_value.get_object.assert_called_with( + 'container', 'object', headers={}, resp_chunk_size=65536, + response_dict={}) + mock_open.assert_called_with('object', 'wb', 65536) + + self.assertIn("Error downloading object \'container/object\': " + "'content-length header must be an integer'" + "\nFailed Transaction ID: someTransId", + str(output.err)) + @mock.patch('swiftclient.service.shuffle') @mock.patch('swiftclient.service.Connection') def test_download_shuffle(self, connection, mock_shuffle): # Test that the container and object lists are shuffled - mock_shuffle.side_effect = lambda l: l + mock_shuffle.side_effect = lambda to_shuffle: to_shuffle connection.return_value.get_object.return_value = [ {'content-type': 'text/plain', 'etag': EMPTY_ETAG}, @@ -867,12 +1053,13 @@ fh.write(b'12345678901234567890') swiftclient.shell.main(argv) expected_calls = [mock.call('container', - {'X-Storage-Policy': mock.ANY}, + {'X-Storage-Policy': 'one'}, response_dict={}), mock.call('container_segments', - {'X-Storage-Policy': mock.ANY}, + {'X-Storage-Policy': 'one'}, response_dict={})] - connection.return_value.put_container.has_calls(expected_calls) + connection.return_value.put_container.assert_has_calls(expected_calls, + any_order=True) connection.return_value.put_object.assert_called_with( 'container', self.tmpfile.lstrip('/'), @@ -1901,7 +2088,9 @@ with self.assertRaises(SystemExit): swiftclient.shell.main(argv) - self.assertEqual(output.err, 'Account not found\n') + self.assertEqual( + output.err, + 'Account not found\nFailed Transaction ID: unknown\n') @mock.patch('swiftclient.service.Connection') def test_post_container(self, connection): @@ -2093,7 +2282,8 @@ self.assertEqual( output.err, 'Combination of multiple objects and destination ' - 'including object is invalid\n') + 'including object is invalid\n' + 'Failed Transaction ID: unknown\n') @mock.patch('swiftclient.service.Connection') def test_copy_object_bad_auth(self, connection): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/test/unit/test_swiftclient.py new/python-swiftclient-4.6.0/test/unit/test_swiftclient.py --- old/python-swiftclient-4.4.0/test/unit/test_swiftclient.py 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/test/unit/test_swiftclient.py 2024-05-23 10:01:23.000000000 +0200 @@ -579,7 +579,10 @@ self.assertTrue(token) def test_auth_v3applicationcredential(self): - from keystoneauth1 import exceptions as ksauthexceptions + try: + from keystoneauth1 import exceptions as ksauthexceptions + except ImportError: + raise unittest.SkipTest('keystoneauth1 is not available') os_options = { "auth_type": "v3applicationcredential", @@ -1114,6 +1117,25 @@ self.assertEqual('t\xe9st', headers.get('x-utf-8-header', '')) self.assertEqual('%ff', headers.get('x-non-utf-8-header', '')) self.assertEqual('%FF', headers.get('x-binary-header', '')) + for k, v in headers.items(): + # N.B. k is always lower case! + self.assertTrue(k.islower()) + for k in headers.keys(): + # N.B. k is always lower case! + self.assertTrue(k.islower()) + self.assertTrue(set([ + 'x-utf-8-header', + 'x-non-utf-8-header', + 'x-binary-header', + ]).intersection(headers)) + + self.assertEqual('t\xe9st', headers.get('X-Utf-8-Header', '')) + self.assertEqual('%ff', headers.get('X-Non-Utf-8-Header', '')) + self.assertEqual('%FF', headers.get('X-Binary-Header', '')) + + self.assertEqual('t\xe9st', headers.get('X-UTF-8-HEADER', '')) + self.assertEqual('%ff', headers.get('X-NON-UTF-8-HEADER', '')) + self.assertEqual('%FF', headers.get('X-BINARY-HEADER', '')) def test_chunk_size_read_method(self): conn = c.Connection('http://auth.url/', 'some_user', 'some_key') @@ -1997,7 +2019,7 @@ 'authurl': 'http://www.test.com', 'tenant_name': 'atenant'} conn = c.Connection(**args) - self.assertEqual(type(conn), c.Connection) + self.assertIsInstance(conn, c.Connection) def test_instance_kwargs_token(self): args = {'preauthtoken': 'atoken123', @@ -3053,7 +3075,7 @@ conn = c.Connection(url, 'asdf', 'asdf') self.assertIsNone(conn.http_conn) conn.http_conn = c.http_connection(url) - self.assertEqual(type(conn.http_conn), tuple) + self.assertIsInstance(conn.http_conn, tuple) self.assertEqual(len(conn.http_conn), 2) http_conn_obj = conn.http_conn[1] self.assertIsInstance(http_conn_obj, c.HTTPConnection) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/test-requirements.txt new/python-swiftclient-4.6.0/test-requirements.txt --- old/python-swiftclient-4.4.0/test-requirements.txt 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/test-requirements.txt 2024-05-23 10:01:23.000000000 +0200 @@ -1,6 +1,7 @@ -hacking>=3.2.0,<3.3.0 # Apache-2.0 +hacking>=3.2.0,<6.2.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 +python-keystoneclient>=0.7.0 keystoneauth1>=3.4.0 # Apache-2.0 stestr>=2.0.0,!=3.0.0 # Apache-2.0 openstacksdk>=0.11.0 # Apache-2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-swiftclient-4.4.0/tox.ini new/python-swiftclient-4.6.0/tox.ini --- old/python-swiftclient-4.4.0/tox.ini 2023-09-01 15:38:49.000000000 +0200 +++ new/python-swiftclient-4.6.0/tox.ini 2024-05-23 10:01:23.000000000 +0200 @@ -1,18 +1,18 @@ [tox] envlist = py3,pep8 minversion = 3.18.0 -skipsdist = True [testenv] +skipsdist = True usedevelop = True list_dependencies_command = python -m pip freeze setenv = LANG=en_US.utf-8 VIRTUAL_ENV={envdir} -deps = -r{toxinidir}/requirements.txt +deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - .[keystone] commands = sh -c '(find . -not \( -type d -name .?\* -prune \) \ \( -type d -name "__pycache__" -or -type f -name "*.py[co]" \) \ -print0) | xargs -0 rm -rf' @@ -24,6 +24,12 @@ commands = python -m flake8 swiftclient test +[testenv:{py36,py37}] +# Drop the use of constraints; most dependencies have dropped support for +# these versions already, and have updated their metadata to reflect that +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + [testenv:venv] commands = {posargs} @@ -69,7 +75,8 @@ # H404: multi line docstring should start without a leading new line # H405: multi line docstring summary not separated with an empty line # W504: line break after binary operator -ignore = H101,H301,H306,H401,H403,H404,H405,W504 +# F811: Redefinition of unused name from line n +ignore = H101,H301,H306,H401,H403,H404,H405,W504,F811 # H106: Donât put vim configuration in source files # H203: Use assertIs(Not)None to check for None enable-extensions=H106,H203 @@ -87,8 +94,7 @@ [testenv:releasenotes] usedevelop = False -deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/doc/requirements.txt +deps = {[testenv:docs]deps} commands = sphinx-build -a -W -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pdf-docs]