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]

Reply via email to