nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: boto/botocore
Pull request #1217 (2017-06-11 23:33:32)

Fix SigV2Auth for GET requests
The add_auth() method references request.param rather than
request.params which causes it to thow an AttributeError when invoked on
a GET request.

This fix is required to access the Alexa Top Sites API using botocore.

```python
from botocore.auth import SigV2Auth
from botocore.awsrequest import AWSRequest
from botocore.endpoint import BotocoreHTTPSession
from botocore.session import get_session

request = AWSRequest()
request.method = 'GET'
request.url = 'https://ats.amazonaws.com'
request.params['Action'] = 'TopSites'
request.params['ResponseGroup'] = 'Country'

credentials = get_session().get_credentials()
auth = SigV2Auth(credentials)
auth.add_auth(request)

session = BotocoreHTTPSession()
session.send(request.prepare())
```


Patch:
diff --git a/botocore/auth.py b/botocore/auth.py
index d31253ab6..c9fd5d601 100644
--- a/botocore/auth.py
+++ b/botocore/auth.py
@@ -109,7 +109,7 @@ class SigV2Auth(BaseSigner):
             params = request.data
         else:
             # GET
-            params = request.param
+            params = request.params
         params['AWSAccessKeyId'] = self.credentials.access_key
         params['SignatureVersion'] = '2'
         params['SignatureMethod'] = 'HmacSHA256'


Test patch:
diff --git a/tests/unit/auth/test_signers.py b/tests/unit/auth/test_signers.py
index 281a9bc0f..735cbfb29 100644
--- a/tests/unit/auth/test_signers.py
+++ b/tests/unit/auth/test_signers.py
@@ -226,6 +226,20 @@ class TestSigV2(unittest.TestCase):
             result, ('Foo=%E2%9C%93',
                      u'VCtWuwaOL0yMffAT8W4y0AFW3W4KUykBqah9S40rB+Q='))
 
+    def test_get(self):
+        request = Request()
+        request.url = '/'
+        request.method = 'GET'
+        request.params = {'Foo': u'\u2713'}
+        self.signer.add_auth(request)
+        self.assertEqual(request.params['AWSAccessKeyId'], 'foo')
+        self.assertEqual(request.params['Foo'], u'\u2713')
+        self.assertEqual(request.params['Timestamp'], '2014-06-20T08:40:23Z')
+        self.assertEqual(request.params['Signature'],
+                         u'Un97klqZCONP65bA1+Iv4H3AcB2I40I4DBvw5ZERFPw=')
+        self.assertEqual(request.params['SignatureMethod'], 'HmacSHA256')
+        self.assertEqual(request.params['SignatureVersion'], '2')
+
 
 class TestSigV3(unittest.TestCase):
Repository: boto/botocore
Pull request #1230 (2017-06-23 18:05:44)

Make copying UNSIGNED return the same instance
botocore.UNSIGNED is meant as a sentinal value like None, so copying
it should always return the same instance. This implements that
behavior.

Fixes boto/boto3#1144

cc @kyleknap @jamesls @dstufft @stealthycoin


Patch:
diff --git a/.changes/next-release/bugfix-Config-76988.json b/.changes/next-release/bugfix-Config-76988.json
new file mode 100644
index 000000000..07fcb75c6
--- /dev/null
+++ b/.changes/next-release/bugfix-Config-76988.json
@@ -0,0 +1,5 @@
+{
+  "description": "Fixes a bug resulting from copy/deepcopy not returning the same object for botocore.UNSIGNED. Fixes boto/boto3`#1144 <https://github.com/boto/botocore/issues/1144>`__",
+  "category": "Config",
+  "type": "bugfix"
+}
diff --git a/botocore/__init__.py b/botocore/__init__.py
index c09240742..fcff885bf 100644
--- a/botocore/__init__.py
+++ b/botocore/__init__.py
@@ -60,8 +60,17 @@ ScalarTypes = ('string', 'integer', 'boolean', 'timestamp', 'float', 'double')
 
 BOTOCORE_ROOT = os.path.dirname(os.path.abspath(__file__))
 
+
 # Used to specify anonymous (unsigned) request signature
-UNSIGNED = object()
+class UNSIGNED(object):
+    def __copy__(self):
+        return self
+
+    def __deepcopy__(self, memodict):
+        return self
+
+
+UNSIGNED = UNSIGNED()
 
 
 def xform_name(name, sep='_', _xform_cache=_xform_cache,


Test patch:
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 4a9312b52..ebd515117 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -15,9 +15,11 @@ from tests import unittest
 from dateutil.tz import tzutc, tzoffset
 import datetime
 from botocore.compat import six
+import copy
 
 import mock
 
+import botocore
 from botocore import xform_name
 from botocore.compat import OrderedDict, json
 from botocore.awsrequest import AWSRequest
@@ -1635,5 +1637,13 @@ class TestContainerMetadataFetcher(unittest.TestCase):
     def test_external_host_not_allowed_if_https(self):
         self.assert_host_is_not_allowed('https://somewhere.com/foo')
 
+
+class TestUnsigned(unittest.TestCase):
+    def test_copy_returns_same_object(self):
+        self.assertIs(botocore.UNSIGNED, copy.copy(botocore.UNSIGNED))
+
+    def test_deepcopy_returns_same_object(self):
+        self.assertIs(botocore.UNSIGNED, copy.deepcopy(botocore.UNSIGNED))
+
 if __name__ == '__main__':
     unittest.main()
Repository: boto/botocore
Pull request #1239 (2017-07-11 17:30:05)

Generate example enum
This causes generated skeletons to populate an example value for
enums that matches known possible values.

cc @stealthycoin @dstufft


Patch:
diff --git a/botocore/utils.py b/botocore/utils.py
index 1d9185c37..59cf34925 100644
--- a/botocore/utils.py
+++ b/botocore/utils.py
@@ -18,6 +18,7 @@ import hashlib
 import binascii
 import functools
 import weakref
+import random
 
 import dateutil.parser
 from dateutil.tz import tzlocal, tzutc
@@ -578,6 +579,8 @@ class ArgumentGenerator(object):
             elif shape.type_name == 'string':
                 if self._use_member_names:
                     return name
+                if shape.enum:
+                    return random.choice(shape.enum)
                 return ''
             elif shape.type_name in ['integer', 'long']:
                 return 0


Test patch:
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index ebd515117..c1cc1b9c3 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -416,6 +416,17 @@ class TestArgumentGenerator(unittest.TestCase):
             }
         )
 
+    def test_generate_string_enum(self):
+        enum_values = ['A', 'B', 'C']
+        model = {
+            'A': {'type': 'string', 'enum': enum_values}
+        }
+        shape = DenormalizedStructureBuilder().with_members(
+            model).build_model()
+        actual = self.arg_generator.generate_skeleton(shape)
+
+        self.assertIn(actual['A'], enum_values)
+
     def test_generate_scalars(self):
         self.assert_skeleton_from_model_is(
             model={
Repository: boto/botocore
Pull request #1317 (2017-11-22 00:32:22)

Enforce ISO8601 format in cached refresher
This moves the cached credential fetcher to enforcing iso format
rather than relying on a hand-rolled format string. This is important
on Windows, where our format string can produce strings that we
can't parse.


Patch:
diff --git a/botocore/credentials.py b/botocore/credentials.py
index b53707dda..eb2011298 100644
--- a/botocore/credentials.py
+++ b/botocore/credentials.py
@@ -132,8 +132,10 @@ def _parse_if_needed(value):
     return parse(value)
 
 
-def _serialize_if_needed(value):
+def _serialize_if_needed(value, iso=False):
     if isinstance(value, datetime.datetime):
+        if iso:
+            return value.isoformat()
         return value.strftime('%Y-%m-%dT%H:%M:%S%Z')
     return value
 
@@ -509,7 +511,7 @@ class CachedCredentialFetcher(object):
             logger.debug("Credentials for role retrieved from cache.")
 
         creds = response['Credentials']
-        expiration = _serialize_if_needed(creds['Expiration'])
+        expiration = _serialize_if_needed(creds['Expiration'], iso=True)
         return {
             'access_key': creds['AccessKeyId'],
             'secret_key': creds['SecretAccessKey'],


Test patch:
diff --git a/tests/unit/test_credentials.py b/tests/unit/test_credentials.py
index c8951d6f3..07f582ec7 100644
--- a/tests/unit/test_credentials.py
+++ b/tests/unit/test_credentials.py
@@ -197,7 +197,7 @@ class TestAssumeRoleCredentialRefresher(BaseEnvVar):
     def get_expected_creds_from_response(self, response):
         expiration = response['Credentials']['Expiration']
         if isinstance(expiration, datetime):
-            expiration = expiration.strftime('%Y-%m-%dT%H:%M:%S%Z')
+            expiration = expiration.isoformat()
         return {
             'access_key': response['Credentials']['AccessKeyId'],
             'secret_key': response['Credentials']['SecretAccessKey'],
Repository: boto/botocore
Pull request #446 (2015-01-30 19:27:05)

Handle capitalized keys in error responses
For rest-json, we should handle the case where
the keys can come back as either code/Code and
message/Message.

cc @kyleknap @danielgtaylor 


Patch:
diff --git a/botocore/parsers.py b/botocore/parsers.py
index 35be11b83..0902740d7 100644
--- a/botocore/parsers.py
+++ b/botocore/parsers.py
@@ -635,15 +635,17 @@ class RestJSONParser(BaseRestParser, BaseJSONParser):
     def _do_error_parse(self, response, shape):
         body = self._initial_body_parse(response['body'])
         error = {'Error': {}, 'ResponseMetadata': {}}
-        error['Error']['Message'] = body.get('message', '')
+        error['Error']['Message'] = body.get('message',
+                                             body.get('Message', ''))
         if 'x-amzn-errortype' in response['headers']:
             code = response['headers']['x-amzn-errortype']
             # Could be:
             # x-amzn-errortype: ValidationException:
             code = code.split(':')[0]
             error['Error']['Code'] = code
-        elif 'code' in body:
-            error['Error']['Code'] = body['code']
+        elif 'code' in body or 'Code' in body:
+            error['Error']['Code'] = body.get(
+                'code', body.get('Code', ''))
         return error
 
 


Test patch:
diff --git a/tests/unit/test_parsers.py b/tests/unit/test_parsers.py
index 257868c84..32e68a0b6 100644
--- a/tests/unit/test_parsers.py
+++ b/tests/unit/test_parsers.py
@@ -269,6 +269,18 @@ class TestResponseMetadataParsed(unittest.TestCase):
         self.assertEqual(parsed['Error'], {'Message': 'Access denied',
                                            'Code': 'AccessDeniedException'})
 
+    def test_can_parse_with_case_insensitive_keys(self):
+        body = (b'{"Code":"AccessDeniedException","type":"Client","Message":'
+                b'"Access denied"}')
+        headers = {
+             'x-amzn-requestid': 'request-id'
+        }
+        parser = parsers.RestJSONParser()
+        parsed = parser.parse(
+            {'body': body, 'headers': headers, 'status_code': 400}, None)
+        self.assertEqual(parsed['Error'], {'Message': 'Access denied',
+                                           'Code': 'AccessDeniedException'})
+
 
 class TestResponseParsingDatetimes(unittest.TestCase):
     def test_can_parse_float_timestamps(self):
Repository: boto/botocore
Pull request #463 (2015-02-14 00:33:13)

Fix error with parsing iam policies response
We were making an assumption that the member names were always
in the response, but in the case of ListPolicyVersions the
'Document' key is _not_ in the response.

Before you'd just get:

```
$ aws iam list-policy-versions --policy-arn 'arn:aws:iam::asdf:policy/AdministratorAccess-asdf'

u'Document'
```
cc @kyleknap @danielgtaylor 


Patch:
diff --git a/botocore/handlers.py b/botocore/handlers.py
index 436b8f08f..38d9bdfc5 100644
--- a/botocore/handlers.py
+++ b/botocore/handlers.py
@@ -340,7 +340,8 @@ def _decode_policy_types(parsed, shape):
     if shape.type_name == 'structure':
         for member_name, member_shape in shape.members.items():
             if member_shape.type_name == 'string' and \
-                    member_shape.name == shape_name:
+                    member_shape.name == shape_name and \
+                    member_name in parsed:
                 parsed[member_name] = decode_quoted_jsondoc(parsed[member_name])
             elif member_name in parsed:
                 _decode_policy_types(parsed[member_name], member_shape)


Test patch:
diff --git a/tests/unit/test_handlers.py b/tests/unit/test_handlers.py
index e88ee08f7..1c33edeaf 100644
--- a/tests/unit/test_handlers.py
+++ b/tests/unit/test_handlers.py
@@ -348,6 +348,46 @@ class TestHandlers(BaseSessionTest):
         # an error response.
         self.assertEqual(original, handler_input)
 
+    def test_decode_json_policy(self):
+        parsed = {
+            'Document': '{"foo": "foobarbaz"}',
+            'Other': 'bar',
+        }
+        service_def = {
+            'operations': {
+                'Foo': {
+                    'output': {'shape': 'PolicyOutput'},
+                }
+            },
+            'shapes': {
+                'PolicyOutput': {
+                    'type': 'structure',
+                    'members': {
+                        'Document': {
+                            'shape': 'policyDocumentType'
+                        },
+                        'Other': {
+                            'shape': 'stringType'
+                        }
+                    }
+                },
+                'policyDocumentType': {
+                    'type': 'string'
+                },
+                'stringType': {
+                    'type': 'string'
+                },
+            }
+        }
+        model = ServiceModel(service_def)
+        op_model = model.operation_model('Foo')
+        handlers.json_decode_policies(parsed, op_model)
+        self.assertEqual(parsed['Document'], {'foo': 'foobarbaz'})
+
+        no_document = {'Other': 'bar'}
+        handlers.json_decode_policies(no_document, op_model)
+        self.assertEqual(no_document, {'Other': 'bar'})
+
     def test_inject_account_id(self):
         params = {}
         handlers.inject_account_id(params)
Repository: boto/botocore
Pull request #477 (2015-03-03 22:45:24)

Remove _path key when parsing INI config files
See https://github.com/boto/botocore/pull/461#issuecomment-77001875 for more info.

cc @kyleknap @danielgtaylor 


Patch:
diff --git a/botocore/config.py b/botocore/config.py
index 20bad02b4..dc62ea6bb 100644
--- a/botocore/config.py
+++ b/botocore/config.py
@@ -110,8 +110,7 @@ def load_config(config_filename):
 def raw_config_parse(config_filename):
     """Returns the parsed INI config contents.
 
-    Each section name is a top level key, and a _path key is inserted whose
-    value is the ``config_filename``.
+    Each section name is a top level key.
 
     :returns: A dict with keys for each profile found in the config
         file and the value of each key being a dict containing name
@@ -132,7 +131,6 @@ def raw_config_parse(config_filename):
         except configparser.Error:
             raise botocore.exceptions.ConfigParseError(path=path)
         else:
-            config['_path'] = path
             for section in cp.sections():
                 config[section] = {}
                 for option in cp.options(section):


Test patch:
diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
index 8dc7f49e5..e4cfafb34 100644
--- a/tests/unit/test_config.py
+++ b/tests/unit/test_config.py
@@ -37,10 +37,6 @@ class TestConfig(BaseEnvVar):
         loaded_config = raw_config_parse(path('aws_config'))
         self.assertIn('default', loaded_config)
         self.assertIn('profile "personal"', loaded_config)
-        # A _path key is inserted that is the full path of the
-        # config file.
-        self.assertEqual(loaded_config['_path'], path('aws_config'))
-
 
     def test_profile_map_conversion(self):
         loaded_config = load_config(path('aws_config'))
diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py
index 2cb4298c7..c92fd06a1 100644
--- a/tests/unit/test_session.py
+++ b/tests/unit/test_session.py
@@ -155,6 +155,19 @@ class SessionTest(BaseSessionTest):
                              {'aws_access_key_id': 'FROM_CREDS_FILE_1',
                               'aws_secret_access_key': 'FROM_CREDS_FILE_2'})
 
+    def test_path_not_in_available_profiles(self):
+        with temporary_file('w') as f:
+            self.session.set_config_variable('credentials_file', f.name)
+            f.write('[newprofile]\n')
+            f.write('aws_access_key_id=FROM_CREDS_FILE_1\n')
+            f.write('aws_secret_access_key=FROM_CREDS_FILE_2\n')
+            f.flush()
+
+            profiles = self.session.available_profiles
+            self.assertEqual(
+                set(profiles),
+                set(['foo', 'default', 'newprofile']))
+
     def test_register_unregister(self):
         calls = []
         handler = lambda **kwargs: calls.append(kwargs)
Repository: boto/botocore
Pull request #488 (2015-03-11 21:45:13)

Add service_name when using get_service_model
Clients did this, but not session.get_service_model.

Need this in the the CLI refactor, otherwise the model doesn't know the "service name" to use to create the client when endpoint_prefix != service_name.

cc @kyleknap @danielgtaylor 


Patch:
diff --git a/botocore/session.py b/botocore/session.py
index 8f57c0472..f7d473ffe 100644
--- a/botocore/session.py
+++ b/botocore/session.py
@@ -500,7 +500,7 @@ class Session(object):
 
         """
         service_description = self.get_service_data(service_name, api_version)
-        return ServiceModel(service_description)
+        return ServiceModel(service_description, service_name=service_name)
 
     def get_waiter_model(self, service_name, api_version=None):
         loader = self.get_component('data_loader')


Test patch:
diff --git a/tests/integration/test_session.py b/tests/integration/test_session.py
index 6edfd9797..5252f497e 100644
--- a/tests/integration/test_session.py
+++ b/tests/integration/test_session.py
@@ -47,3 +47,17 @@ class TestCanChangeParsing(unittest.TestCase):
         dates = [bucket['CreationDate'] for bucket in parsed['Buckets']]
         self.assertTrue(all(isinstance(date, str) for date in dates),
                         "Expected all str types but instead got: %s" % dates)
+
+    def test_maps_service_name_when_overriden(self):
+        ses = self.session.get_service_model('ses')
+        self.assertEqual(ses.endpoint_prefix, 'email')
+        # But we should map the service_name to be the same name
+        # used when calling get_service_model which is different
+        # than the endpoint_prefix.
+        self.assertEqual(ses.service_name, 'ses')
+
+    def test_maps_service_name_from_client(self):
+        # Same thing as test_maps_service_name_from_client,
+        # except through the client interface.
+        client = self.session.create_client('ses')
+        self.assertEqual(client.meta.service_model.service_name, 'ses')
diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py
index c92fd06a1..0bd620272 100644
--- a/tests/unit/test_session.py
+++ b/tests/unit/test_session.py
@@ -343,6 +343,7 @@ class TestGetServiceModel(BaseSessionTest):
         self.session.register_component('data_loader', loader)
         model = self.session.get_service_model('made_up')
         self.assertIsInstance(model, ServiceModel)
+        self.assertEqual(model.service_name, 'made_up')
 
 
 class TestGetWaiterModel(BaseSessionTest):
@@ -389,6 +390,7 @@ class TestCreateClient(BaseSessionTest):
             mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY,
             scoped_config=mock.ANY, client_config=config)
 
+
 class TestPerformOperation(BaseSessionTest):
     def test_s3(self):
         service = self.session.get_service('s3')
Repository: boto/botocore
Pull request #490 (2015-03-12 17:48:13)

Add documentation properties to service/op models
cc @kyleknap @danielgtaylor


Patch:
diff --git a/botocore/model.py b/botocore/model.py
index 855e9a75b..329434f5a 100644
--- a/botocore/model.py
+++ b/botocore/model.py
@@ -235,6 +235,10 @@ class ServiceModel(object):
             raise OperationNotFoundError(operation_name)
         return OperationModel(model, self)
 
+    @CachedProperty
+    def documentation(self):
+        return self._service_description.get('documentation', '')
+
     @CachedProperty
     def operation_names(self):
         return list(self._service_description.get('operations', []))
@@ -328,6 +332,10 @@ class OperationModel(object):
     def service_model(self):
         return self._service_model
 
+    @CachedProperty
+    def documentation(self):
+        return self._operation_model.get('documentation', '')
+
     @CachedProperty
     def input_shape(self):
         if 'input' not in self._operation_model:


Test patch:
diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py
index ac12b826c..d169e29cd 100644
--- a/tests/unit/test_model.py
+++ b/tests/unit/test_model.py
@@ -35,7 +35,7 @@ class TestServiceModel(unittest.TestCase):
         self.model = {
             'metadata': {'protocol': 'query',
                          'endpointPrefix': 'endpoint-prefix'},
-            'documentation': '',
+            'documentation': 'Documentation value',
             'operations': {},
             'shapes': {}
         }
@@ -61,6 +61,10 @@ class TestServiceModel(unittest.TestCase):
     def test_signing_name_defaults_to_endpoint_prefix(self):
         self.assertEqual(self.service_model.signing_name, 'endpoint-prefix')
 
+    def test_documentation_exposed_as_property(self):
+        self.assertEqual(self.service_model.documentation,
+                         'Documentation value')
+
 
 class TestOperationModelFromService(unittest.TestCase):
     def setUp(self):
@@ -81,6 +85,7 @@ class TestOperationModelFromService(unittest.TestCase):
                         'shape': 'OperationNameResponse',
                     },
                     'errors': [{'shape': 'NoSuchResourceException'}],
+                    'documentation': 'Docs for OperationName',
                 }
             },
             'shapes': {
@@ -122,6 +127,11 @@ class TestOperationModelFromService(unittest.TestCase):
         self.assertEqual(shape.name, 'OperationNameRequest')
         self.assertEqual(list(sorted(shape.members)), ['Arg1', 'Arg2'])
 
+    def test_has_documentation_property(self):
+        service_model = model.ServiceModel(self.model)
+        operation = service_model.operation_model('OperationName')
+        self.assertEqual(operation.documentation, 'Docs for OperationName')
+
     def test_service_model_available_from_operation_model(self):
         service_model = model.ServiceModel(self.model)
         operation = service_model.operation_model('OperationName')