{"action":"created","issue":{"url":"https://api.github.com/repos/boto/botocore/issues/965","repository_url":"https://api.github.com/repos/boto/botocore","labels_url":"https://api.github.com/repos/boto/botocore/issues/965/labels{/name}","comments_url":"https://api.github.com/repos/boto/botocore/issues/965/comments","events_url":"https://api.github.com/repos/boto/botocore/issues/965/events","html_url":"https://github.com/boto/botocore/pull/965","id":161916231,"number":965,"title":"fixes #926 - expose retry information in exceptions","user":{"login":"jantman","id":273454,"avatar_url":"https://avatars.githubusercontent.com/u/273454?v=3","gravatar_id":"","url":"https://api.github.com/users/jantman","html_url":"https://github.com/jantman","followers_url":"https://api.github.com/users/jantman/followers","following_url":"https://api.github.com/users/jantman/following{/other_user}","gists_url":"https://api.github.com/users/jantman/gists{/gist_id}","starred_url":"https://api.github.com/users/jantman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jantman/subscriptions","organizations_url":"https://api.github.com/users/jantman/orgs","repos_url":"https://api.github.com/users/jantman/repos","events_url":"https://api.github.com/users/jantman/events{/privacy}","received_events_url":"https://api.github.com/users/jantman/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/boto/botocore/labels/needs-discussion","name":"needs-discussion","color":"e99695"},{"url":"https://api.github.com/repos/boto/botocore/labels/needs-review","name":"needs-review","color":"5319e7"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":8,"created_at":"2016-06-23T12:56:48Z","updated_at":"2016-06-30T10:03:42Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/boto/botocore/pulls/965","html_url":"https://github.com/boto/botocore/pull/965","diff_url":"https://github.com/boto/botocore/pull/965.diff","patch_url":"https://github.com/boto/botocore/pull/965.patch"},"body":"This is my attempt at fixing #926 by providing information on retries in ``ResponseMetadata``, which is returned as part of the exceptions that are raised to the user.\r\n\r\n* in botocore.retryhandler.MaxAttemptsDecorator.__call__(), add a MaxAttemptsReached=True element to ResponseMetadata if the maximum number of attempts is reached\r\n* in botocore.endpoint.Endpoint._send_request(), add a NumAttempts element to ResponseMetadata, containing the attempt number\r\n\r\nAn example of this in action:\r\n\r\n```python\r\n#!/usr/bin/env python\r\n\r\nimport boto3\r\nfrom mock import patch, Mock\r\nfrom botocore.vendored.requests.models import Response\r\n\r\nec2 = boto3.resource('ec2')\r\ni = ec2.Instance('i-0')\r\n\r\ndef mock_get_response(self, request, operation_model, attempts):\r\n    headers = {\r\n        'transfer-encoding': 'chunked',\r\n        'date': 'Thu, 23 Jun 2016 11:32:42 GMT',\r\n        'connection': 'close',\r\n        'server': 'AmazonEC2'\r\n    }\r\n    mock_resp = Mock(spec_set=Response)\r\n    type(mock_resp).content = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<Response><Errors><Error><Type>Sender</Type><Code>Throttling</Code><Message>Rate exceeded</Message></Error></Errors><RequestID>44c0f570-e338-48dd-9953-6684fa586dcb</RequestID></Response>'\r\n    type(mock_resp).headers = headers\r\n    type(mock_resp).status_code = 400\r\n    parsed = {\r\n        'ResponseMetadata': {\r\n            'HTTPStatusCode': 400,\r\n            'RequestId': '44c0f570-e338-48dd-9953-6684fa586dcb',\r\n            'HTTPHeaders': headers\r\n        },\r\n        'Error': {\r\n            'Message': \"Rate exceeded\",\r\n            'Code': 'Throttling'\r\n        }\r\n    }\r\n    return (mock_resp, parsed), None\r\n\r\n\r\nwith patch('botocore.endpoint.Endpoint._get_response', side_effect=mock_get_response, autospec=True):\r\n    try:\r\n        x = i.hypervisor\r\n    except Exception as ex:\r\n        print(ex.response)\r\n```\r\n\r\n```console\r\n(botocore)jantman@phoenix:pts/14:~/GIT/botocore (issue926_retry_info %=)$ time python ~/tmp/test.py \r\n{'ResponseMetadata': {'NumAttempts': 5, 'HTTPStatusCode': 400, 'MaxAttemptsReached': True, 'RequestId': '44c0f570-e338-48dd-9953-6684fa586dcb', 'HTTPHeaders': {'transfer-encoding': 'chunked', 'date': 'Thu, 23 Jun 2016 11:32:42 GMT', 'connection': 'close', 'server': 'AmazonEC2'}}, 'Error': {'Message': 'Rate exceeded', 'Code': 'Throttling'}}\r\n\r\nreal    0m13.479s\r\nuser    0m0.340s\r\nsys     0m0.043s\r\n```"},"comment":{"url":"https://api.github.com/repos/boto/botocore/issues/comments/229616611","html_url":"https://github.com/boto/botocore/pull/965#issuecomment-229616611","issue_url":"https://api.github.com/repos/boto/botocore/issues/965","id":229616611,"user":{"login":"jantman","id":273454,"avatar_url":"https://avatars.githubusercontent.com/u/273454?v=3","gravatar_id":"","url":"https://api.github.com/users/jantman","html_url":"https://github.com/jantman","followers_url":"https://api.github.com/users/jantman/followers","following_url":"https://api.github.com/users/jantman/following{/other_user}","gists_url":"https://api.github.com/users/jantman/gists{/gist_id}","starred_url":"https://api.github.com/users/jantman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jantman/subscriptions","organizations_url":"https://api.github.com/users/jantman/orgs","repos_url":"https://api.github.com/users/jantman/repos","events_url":"https://api.github.com/users/jantman/events{/privacy}","received_events_url":"https://api.github.com/users/jantman/received_events","type":"User","site_admin":false},"created_at":"2016-06-30T10:03:42Z","updated_at":"2016-06-30T10:03:42Z","body":"@JordonPhillips perms fixed. I'll re-push and squash once you're both OK with my changes - I really don't like rewriting or discarding history until I know I'm done with it. (Side note - not sure what workflow you use, but you know you _can_ squash merge through the PR UI now...)\r\n\r\n@jamesls Ok, I'll add back the boolean. Sorry for my confusion. I'll do my best on the tests, but frankly, I'm horribly confused by how the testing is done in botocore, and it seems that ``endpoint`` has very little existing coverage. I'll try my best."}}
{"actor":{"display_login":"jantman"}}