From 3a778dcf8287950d81c54ff83e87938032f2b6df Mon Sep 17 00:00:00 2001 From: "David L. Miller" <45697098+dlm6693@users.noreply.github.com> Date: Fri, 20 May 2022 13:52:56 -0400 Subject: [PATCH] add list of tuples functionality to AWSRequest params attribute (#2679) --- botocore/auth.py | 8 +++++--- botocore/awsrequest.py | 7 ++++++- tests/unit/test_awsrequest.py | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/botocore/auth.py b/botocore/auth.py index a0ca43159f..f38713e34d 100644 --- a/botocore/auth.py +++ b/botocore/auth.py @@ -19,6 +19,7 @@ import json import logging import time +from collections.abc import Mapping from email.utils import formatdate from hashlib import sha1, sha256 from operator import itemgetter @@ -245,10 +246,11 @@ def canonical_query_string(self, request): def _canonical_query_string_params(self, params): # [(key, value), (key2, value2)] key_val_pairs = [] - for key in params: - value = str(params[key]) + if isinstance(params, Mapping): + params = params.items() + for key, value in params: key_val_pairs.append( - (quote(key, safe='-_.~'), quote(value, safe='-_.~')) + (quote(key, safe='-_.~'), quote(str(value), safe='-_.~')) ) sorted_key_vals = [] # Sort by the URI-encoded key names, and in the case of diff --git a/botocore/awsrequest.py b/botocore/awsrequest.py index e9696a0b1c..8cbd83995c 100644 --- a/botocore/awsrequest.py +++ b/botocore/awsrequest.py @@ -13,6 +13,7 @@ # language governing permissions and limitations under the License. import functools import logging +from collections.abc import Mapping import urllib3.util from urllib3.connection import HTTPConnection, VerifiedHTTPSConnection @@ -367,7 +368,11 @@ def _prepare_url(self, original): if original.params: url_parts = urlparse(url) delim = '&' if url_parts.query else '?' - params = urlencode(list(original.params.items()), doseq=True) + 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 diff --git a/tests/unit/test_awsrequest.py b/tests/unit/test_awsrequest.py index 4787f6811d..12c738e57c 100644 --- a/tests/unit/test_awsrequest.py +++ b/tests/unit/test_awsrequest.py @@ -18,6 +18,7 @@ import socket import tempfile +import pytest from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool from botocore.awsrequest import ( @@ -268,6 +269,41 @@ def seek(self, where): self.assertTrue(looks_like_file.seek_called) +@pytest.mark.parametrize( + "test_input,expected_output", + [ + ([('foo', None)], 'http://example.com/?foo=None'), + ([(None, None)], 'http://example.com/?None=None'), + ( + [('foo', 'bar'), ('foo', 'baz'), ('fizz', 'buzz')], + 'http://example.com/?foo=bar&foo=baz&fizz=buzz' + ), + ([('foo', 'bar'), ('foo', None)], 'http://example.com/?foo=bar&foo=None'), + ([('foo', 'bar')], 'http://example.com/?foo=bar'), + ( + [('foo', 'bar'), ('foo', 'bar'), ('fizz', 'buzz')], + 'http://example.com/?foo=bar&foo=bar&fizz=buzz' + ), + ([('', 'bar')], 'http://example.com/?=bar'), + ([(1, 'bar')], 'http://example.com/?1=bar'), + ] +) +def test_can_use_list_tuples_for_params(test_input, expected_output): + request = AWSRequest( + url='http://example.com/', params=test_input + ) + prepared_request = request.prepare() + assert prepared_request.url == expected_output + + +def test_empty_list_tuples_value_error_for_params(): + request = AWSRequest( + url='http://example.com/', params=[()] + ) + with pytest.raises(ValueError): + request.prepare() + + class TestAWSResponse(unittest.TestCase): def setUp(self): self.response = AWSResponse('http://url.com', 200, HeadersDict(), None)