update to 2.28.5 [release 2.28.5-1mamba;Fri Aug 08 2025]

This commit is contained in:
2025-08-12 12:41:15 +02:00
parent 97ed924b75
commit 85d52a86ac
6 changed files with 364 additions and 12 deletions

View File

@@ -0,0 +1,41 @@
diff --git a/tests/functional/eks/test_kubeconfig.py b/tests/functional/eks/test_kubeconfig.py
index d5dda2d..6c642aa 100644
--- a/tests/functional/eks/test_kubeconfig.py
+++ b/tests/functional/eks/test_kubeconfig.py
@@ -127,9 +127,9 @@ class TestKubeconfigLoader(unittest.TestCase):
)
loaded_config = self._loader.load_kubeconfig(simple_path)
self.assertEqual(loaded_config.content, content)
- self._validator.validate_config.assert_called_with(
- Kubeconfig(simple_path, content)
- )
+ validated_config = self._validator.validate_config.call_args.args[0]
+ self.assertEqual(validated_config.path, simple_path)
+ self.assertEqual(validated_config.content, content)
def test_load_noexist(self):
no_exist_path = os.path.join(
@@ -137,17 +137,17 @@ class TestKubeconfigLoader(unittest.TestCase):
)
loaded_config = self._loader.load_kubeconfig(no_exist_path)
self.assertEqual(loaded_config.content, _get_new_kubeconfig_content())
- self._validator.validate_config.assert_called_with(
- Kubeconfig(no_exist_path, _get_new_kubeconfig_content())
- )
+ validated_config = self._validator.validate_config.call_args.args[0]
+ self.assertEqual(validated_config.path, no_exist_path)
+ self.assertEqual(validated_config.content, _get_new_kubeconfig_content())
def test_load_empty(self):
empty_path = self._clone_config("valid_empty_existing")
loaded_config = self._loader.load_kubeconfig(empty_path)
self.assertEqual(loaded_config.content, _get_new_kubeconfig_content())
- self._validator.validate_config.assert_called_with(
- Kubeconfig(empty_path, _get_new_kubeconfig_content())
- )
+ validated_config = self._validator.validate_config.call_args.args[0]
+ self.assertEqual(validated_config.path, empty_path)
+ self.assertEqual(validated_config.content, _get_new_kubeconfig_content())
def test_load_directory(self):
current_directory = self._temp_directory

View File

@@ -0,0 +1,38 @@
From 4b5762bb17f172d9f9e058df8908651856ff4a69 Mon Sep 17 00:00:00 2001
From: Adam Williamson <awilliam@redhat.com>
Date: Thu, 13 Jun 2024 10:33:42 -0700
Subject: [PATCH] Bump the ceiling for botocore memory leak tests to 15 MiB
See https://github.com/boto/botocore/issues/3205 for the
background on this. In rebuilding awscli2 for Python 3.13 in
Fedora Rawhide, we found that two of these tests fail because
they now top out around 11MiB of memory usage, rather than
around 1.6MiB. We don't understand why this is yet, but it's not
a memory *leak*, so bumping the ceiling seems appropriate. I'm
sending this upstream so I have a reference for the downstream
package and to raise awareness of the issue, but the correct fix
may be something else.
Signed-off-by: Adam Williamson <awilliam@redhat.com>
---
tests/functional/botocore/leak/test_resource_leaks.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/functional/botocore/leak/test_resource_leaks.py b/tests/functional/botocore/leak/test_resource_leaks.py
index df1c4fe7f..73027725f 100644
--- a/tests/functional/botocore/leak/test_resource_leaks.py
+++ b/tests/functional/botocore/leak/test_resource_leaks.py
@@ -22,8 +22,8 @@ class TestDoesNotLeakMemory(BaseClientDriverTest):
# a substantial amount of time to the total test run time.
INJECT_DUMMY_CREDS = True
# We're making up numbers here, but let's say arbitrarily
- # that the memory can't increase by more than 10MB.
- MAX_GROWTH_BYTES = 10 * 1024 * 1024
+ # that the memory can't increase by more than 15MB.
+ MAX_GROWTH_BYTES = 15 * 1024 * 1024
def test_create_single_client_memory_constant(self):
self.cmd('create_client', 's3')
--
2.45.2

View File

@@ -0,0 +1,113 @@
diff --git a/awscli/customizations/cloudformation/yamlhelper.py b/awscli/customizations/cloudformation/yamlhelper.py
index 8e3b291..a926d53 100644
--- a/awscli/customizations/cloudformation/yamlhelper.py
+++ b/awscli/customizations/cloudformation/yamlhelper.py
@@ -91,8 +91,14 @@ def yaml_dump(dict_to_dump):
yaml.Representer = FlattenAliasRepresenter
_add_yaml_1_1_boolean_resolvers(yaml.Resolver)
yaml.Representer.add_representer(OrderedDict, _dict_representer)
+ yaml.Representer.add_representer(dict, _dict_representer)
- return dump_yaml_to_str(yaml, dict_to_dump)
+ result = dump_yaml_to_str(yaml, dict_to_dump)
+
+ # let other YAML instances use the default dict representer
+ yaml.Representer.add_representer(dict, ruamel.yaml.representer.SafeRepresenter.represent_dict)
+
+ return result
def _dict_constructor(loader, node):
diff --git a/awscli/customizations/eks/kubeconfig.py b/awscli/customizations/eks/kubeconfig.py
index 2d302d8..d00c4a8 100644
--- a/awscli/customizations/eks/kubeconfig.py
+++ b/awscli/customizations/eks/kubeconfig.py
@@ -45,7 +45,7 @@ def _get_new_kubeconfig_content():
("contexts", []),
("current-context", ""),
("kind", "Config"),
- ("preferences", OrderedDict()),
+ ("preferences", {}),
("users", []),
]
)
@@ -135,7 +135,7 @@ class KubeconfigValidator(object):
for key, value in self._validation_content.items():
if key in config.content and type(config.content[key]) == list:
for element in config.content[key]:
- if not isinstance(element, OrderedDict):
+ if not isinstance(element, dict):
raise KubeconfigCorruptedError(
f"Entry in {key} not a {dict}. "
)
diff --git a/awscli/customizations/eks/ordered_yaml.py b/awscli/customizations/eks/ordered_yaml.py
index f9b1a11..0cce12e 100644
--- a/awscli/customizations/eks/ordered_yaml.py
+++ b/awscli/customizations/eks/ordered_yaml.py
@@ -50,10 +50,18 @@ def ordered_yaml_dump(to_dump, stream=None):
:type stream: file
"""
yaml = ruamel.yaml.YAML(typ="safe", pure=True)
+ yaml.width = 99999
yaml.default_flow_style = False
yaml.Representer.add_representer(OrderedDict, _ordered_representer)
+ yaml.Representer.add_representer(dict, _ordered_representer)
if stream is None:
- return dump_yaml_to_str(yaml, to_dump)
+ result = dump_yaml_to_str(yaml, to_dump)
+ else:
+ result = None
+ yaml.dump(to_dump, stream)
- yaml.dump(to_dump, stream)
+ # let other YAML instances use the default dict representer
+ yaml.Representer.add_representer(dict, ruamel.yaml.representer.SafeRepresenter.represent_dict)
+
+ return result
diff --git a/tests/unit/customizations/cloudformation/test_yamlhelper.py b/tests/unit/customizations/cloudformation/test_yamlhelper.py
index e7ac758..b9632e9 100644
--- a/tests/unit/customizations/cloudformation/test_yamlhelper.py
+++ b/tests/unit/customizations/cloudformation/test_yamlhelper.py
@@ -130,28 +130,10 @@ class TestYaml(BaseYAMLTest):
' Name: name1\n'
)
output_dict = yaml_parse(input_template)
- expected_dict = OrderedDict(
- [
- (
- 'B_Resource',
- OrderedDict(
- [
- ('Key2', {'Name': 'name2'}),
- ('Key1', {'Name': 'name1'}),
- ]
- ),
- ),
- (
- 'A_Resource',
- OrderedDict(
- [
- ('Key2', {'Name': 'name2'}),
- ('Key1', {'Name': 'name1'}),
- ]
- ),
- ),
- ]
- )
+ expected_dict = {
+ 'B_Resource': {'Key2': {'Name': 'name2'}, 'Key1': {'Name': 'name1'}},
+ 'A_Resource': {'Key2': {'Name': 'name2'}, 'Key1': {'Name': 'name1'}}
+ }
self.assertEqual(expected_dict, output_dict)
output_template = yaml_dump(output_dict)
@@ -165,7 +147,7 @@ class TestYaml(BaseYAMLTest):
<<: *base
"""
output = yaml_parse(test_yaml)
- self.assertTrue(isinstance(output, OrderedDict))
+ self.assertTrue(isinstance(output, dict))
self.assertEqual(output.get('test').get('property'), 'value')
def test_unroll_yaml_anchors(self):

View File

@@ -0,0 +1,124 @@
diff --git a/awscli/botocore/awsrequest.py b/awscli/botocore/awsrequest.py
index 4ce0449..2b14009 100644
--- a/awscli/botocore/awsrequest.py
+++ b/awscli/botocore/awsrequest.py
@@ -14,6 +14,7 @@
import functools
import io
import logging
+from collections.abc import Mapping
import socket
import sys
@@ -24,6 +25,7 @@ from botocore.compat import (
HTTPResponse,
MutableMapping,
urlencode,
+ urlparse,
urlsplit,
urlunsplit,
)
@@ -66,32 +68,34 @@ class AWSConnection:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._original_response_cls = self.response_class
- # We'd ideally hook into httplib's states, but they're all
- # __mangled_vars so we use our own state var. This variable is set
- # when we receive an early response from the server. If this value is
- # set to True, any calls to send() are noops. This value is reset to
- # false every time _send_request is called. This is to workaround the
- # fact that py2.6 (and only py2.6) has a separate send() call for the
- # body in _send_request, as opposed to endheaders(), which is where the
- # body is sent in all versions > 2.6.
+ # This variable is set when we receive an early response from the
+ # server. If this value is set to True, any calls to send() are noops.
+ # This value is reset to false every time _send_request is called.
+ # This is to workaround changes in urllib3 2.0 which uses separate
+ # send() calls in request() instead of delegating to endheaders(),
+ # which is where the body is sent in CPython's HTTPConnection.
self._response_received = False
self._expect_header_set = False
+ self._send_called = False
def close(self):
super().close()
# Reset all of our instance state we were tracking.
self._response_received = False
self._expect_header_set = False
+ self._send_called = False
self.response_class = self._original_response_cls
- def _send_request(self, method, url, body, headers, *args, **kwargs):
+ def request(self, method, url, body=None, headers=None, *args, **kwargs):
+ if headers is None:
+ headers = {}
self._response_received = False
if headers.get('Expect', b'') == b'100-continue':
self._expect_header_set = True
else:
self._expect_header_set = False
self.response_class = self._original_response_cls
- rval = super()._send_request(
+ rval = super().request(
method, url, body, headers, *args, **kwargs
)
self._expect_header_set = False
@@ -210,10 +214,13 @@ class AWSConnection:
def send(self, str):
if self._response_received:
- logger.debug(
- "send() called, but reseponse already received. "
- "Not sending data."
- )
+ if not self._send_called:
+ # urllib3 2.0 chunks and calls send potentially
+ # thousands of times inside `request` unlike the
+ # standard library. Only log this once for sanity.
+ logger.debug("send() called, but reseponse already received. "
+ "Not sending data.")
+ self._send_called = True
return
return super().send(str)
@@ -370,8 +377,14 @@ class AWSRequestPreparer:
def _prepare_url(self, original):
url = original.url
if original.params:
- params = urlencode(list(original.params.items()), doseq=True)
- url = f'{url}?{params}'
+ url_parts = urlparse(url)
+ delim = '&' if url_parts.query else '?'
+ if isinstance(original.params, Mapping):
+ params_to_encode = list(original.params.items())
+ else:
+ params_to_encode = original.params
+ params = urlencode(params_to_encode, doseq=True)
+ url = delim.join((url, params))
return url
def _prepare_headers(self, original, prepared_body=None):
diff --git a/awscli/botocore/httpsession.py b/awscli/botocore/httpsession.py
index 516bb3f..25f982d 100644
--- a/awscli/botocore/httpsession.py
+++ b/awscli/botocore/httpsession.py
@@ -328,7 +328,6 @@ class URLLib3Session:
def _get_pool_manager_kwargs(self, **extra_kwargs):
pool_manager_kwargs = {
- 'strict': True,
'timeout': self._timeout,
'maxsize': self._max_pool_connections,
'ssl_context': self._get_ssl_context(),
diff --git a/tests/unit/botocore/test_http_session.py b/tests/unit/botocore/test_http_session.py
index 90ed7d4..0a10c15 100644
--- a/tests/unit/botocore/test_http_session.py
+++ b/tests/unit/botocore/test_http_session.py
@@ -148,7 +148,6 @@ class TestURLLib3Session(unittest.TestCase):
def _assert_manager_call(self, manager, *assert_args, **assert_kwargs):
call_kwargs = {
- 'strict': True,
'maxsize': mock.ANY,
'timeout': mock.ANY,
'ssl_context': mock.ANY,

View File

@@ -0,0 +1,16 @@
The raw YAML output fields are sometimes printed in non-alphabetical
order, causing the raw text comparison to sometimes fail. The parsed
output is subsequently compared, and that should be all that matters.
diff --git a/tests/unit/output/test_yaml_output.py b/tests/unit/output/test_yaml_output.py
index b5e3a0a..88132f7 100644
--- a/tests/unit/output/test_yaml_output.py
+++ b/tests/unit/output/test_yaml_output.py
@@ -62,7 +62,6 @@ class TestYAMLOutput(BaseAWSCommandParamsTest):
" UserId: EXAMPLEUSERID\n"
" UserName: testuser-51\n"
)
- self.assertEqual(stdout, expected)
parsed_output = self.yaml.load(stdout)
self.assertEqual(self.parsed_response, parsed_output)

View File

@@ -1,6 +1,6 @@
%define pkgname %(echo %name | cut -d- -f2- | tr - _)
Name: python-awscli
Version: 1.36.15
Version: 2.28.5
Release: 1mamba
Summary: A unified command line interface to Amazon Web Services
Group: System/Libraries
@@ -8,16 +8,26 @@ Vendor: openmamba
Distribution: openmamba
Packager: Silvan Calarco <silvan.calarco@mambasoft.it>
URL: https://aws.amazon.com/cli/
Source: https://pypi.debian.net/awscli/awscli-%{version}.tar.gz
Source: https://github.com/aws/aws-cli.git/%{version}/aws-cli-%{version}.tar.bz2
Patch0: python-awscli-2.28.5-python-ruamel-yaml-0.18.14.patch
Patch1: python-awscli-2.28.5-python-urllib3-2.5.patch
Patch2: python-awscli-2.28.5-assertions.patch
Patch3: python-awscli-2.28.5-bump-the-ceiling-for-botocore-memory-leak-tests-to-1.patch
Patch4: python-awscli-2.28.5-test-yaml.patch
License: Apache License 2.0
BuildArch: noarch
## AUTOBUILDREQ-BEGIN
BuildRequires: libpython311-devel
BuildRequires: python3.11dist(botocore)
BuildRequires: python3.11dist(awscrt)
BuildRequires: python3.11dist(colorama)
BuildRequires: python3.11dist(distro)
BuildRequires: python3.11dist(docutils)
BuildRequires: python3.11dist(pyyaml)
BuildRequires: python3.11dist(rsa)
BuildRequires: python3.11dist(s3transfer)
BuildRequires: python3.11dist(jmespath)
BuildRequires: python3.11dist(prompt-toolkit)
BuildRequires: python3.11dist(python-dateutil)
BuildRequires: python3.11dist(ruamel.yaml)
BuildRequires: python3.11dist(ruamel.yaml.clib)
BuildRequires: python3.11dist(urllib3)
## AUTOBUILDREQ-END
%if "0%{?with_pyver}" == "03"
@@ -33,13 +43,20 @@ Obsoletes: python-awscli < 1.31.5
%{summary}.
%prep
%setup -q -n awscli-%{version}
sed -i "s|botocore==|botocore>=|" setup.cfg setup.py awscli.egg-info/requires.txt
sed -i "s|\(docutils[^,]*\)[,]*<0.17[,]*|\1|" setup.cfg setup.py awscli.egg-info/requires.txt
sed -i "s|\(s3transfer[^,]*\)[,]*<0.9.0[,]*|\1|" setup.cfg setup.py awscli.egg-info/requires.txt
sed -i "s|\(colorama[^,]*\)[,]*<0.4.5[,]*|\1|" setup.cfg setup.py awscli.egg-info/requires.txt
sed -i "s|\(rsa[^,]*\)[,]*<4.8[,]*|\1|" setup.cfg setup.py awscli.egg-info/requires.txt
%setup -q -n aws-cli-%{version}
%patch 0 -p1 -b .python-ruamel-yaml-0.18.14
%patch 1 -p1 -b .python-urllib3-2.5
%patch 2 -p1 -b .assertions
%patch 3 -p1 -b .bump-the-ceiling-for-botocore-memory-leak-tests-to-1
%patch 4 -p1 -b .test-yaml
sed -i "s|\(docutils\)>=.*[0-9]|\1|" pyproject.toml
sed -i "s|\(distro\)>=.*[0-9]|\1|" pyproject.toml
sed -i "s|\(python-dateutil\)>=.*[0-9]|\1|" pyproject.toml
sed -i "s|\(flit_core\)>=.*[0-9]|\1|" pyproject.toml
sed -i "s|\(urllib3\)>=.*[0-9]|\1|" pyproject.toml
sed -i "s|\(ruamel.yaml\)>=.*[0-9]|\1|" pyproject.toml
sed -i "s|\(ruamel.yaml.clib\)>=.*[0-9]|\1|" pyproject.toml
%build
CFLAGS="%{optflags}" %{__python} -m build --no-isolation --wheel
@@ -64,6 +81,9 @@ CFLAGS="%{optflags}" %{__python} -m build --no-isolation --wheel
%{python_sitelib}/%{pkgname}/*
%changelog
* Fri Aug 08 2025 Silvan Calarco <silvan.calarco@mambasoft.it> 2.28.5-1mamba
- update to 2.28.5
* Wed Dec 04 2024 Silvan Calarco <silvan.calarco@mambasoft.it> 1.36.15-1mamba
- update to 1.36.15