Repository: boto/botocore
Pull request #1172 (2017-03-31 20:32:51)

Fix mturk method name
Fix naming on `list_hits_for_qualification_type` for mturk. The old method name will still be supported.

This will need to be merged along with aws/aws-cli#2522

codecov-io: # [Codecov](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=h1) Report
> Merging [#1172](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=desc) into [develop](https://codecov.io/gh/boto/botocore/commit/d35b645882d7f9de411a54f7a22914aba87d7f51?src=pr&el=desc) will **decrease** coverage by `1.45%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/boto/botocore/pull/1172/graphs/tree.svg?height=150&token=QOLXw6XSza&width=650&src=pr)](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           develop    #1172      +/-   ##
===========================================
- Coverage    98.02%   96.56%   -1.46%     
===========================================
  Files           40       40              
  Lines         7029     7040      +11     
===========================================
- Hits          6890     6798      -92     
- Misses         139      242     +103
```


| [Impacted Files](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [botocore/\_\_init\_\_.py](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree#diff-Ym90b2NvcmUvX19pbml0X18ucHk=) | `100% <ø> (ø)` | :arrow_up: |
| [botocore/client.py](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree#diff-Ym90b2NvcmUvY2xpZW50LnB5) | `99.73% <100%> (ø)` | :arrow_up: |
| [botocore/handlers.py](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree#diff-Ym90b2NvcmUvaGFuZGxlcnMucHk=) | `95.68% <100%> (-0.46%)` | :arrow_down: |
| [botocore/compat.py](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree#diff-Ym90b2NvcmUvY29tcGF0LnB5) | `57.77% <0%> (-34.08%)` | :arrow_down: |
| [botocore/awsrequest.py](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree#diff-Ym90b2NvcmUvYXdzcmVxdWVzdC5weQ==) | `74.09% <0%> (-24.55%)` | :arrow_down: |
| [botocore/docs/utils.py](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=tree#diff-Ym90b2NvcmUvZG9jcy91dGlscy5weQ==) | `97.09% <0%> (-0.2%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=footer). Last update [d35b645...954cf9c](https://codecov.io/gh/boto/botocore/pull/1172?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
JordonPhillips: Good to look at again

Patch:
diff --git a/.changes/next-release/bugfix-mturk-31799.json b/.changes/next-release/bugfix-mturk-31799.json
new file mode 100644
index 000000000..58bbcdfa3
--- /dev/null
+++ b/.changes/next-release/bugfix-mturk-31799.json
@@ -0,0 +1,5 @@
+{
+  "type": "bugfix",
+  "description": "Fix naming on list_hits_for_qualification_type for mturk. The old method name will still be supported.",
+  "category": "mturk"
+}
diff --git a/botocore/__init__.py b/botocore/__init__.py
index bdc5cbe52..1cebee8da 100644
--- a/botocore/__init__.py
+++ b/botocore/__init__.py
@@ -45,6 +45,8 @@ _xform_cache = {
     ('DescribeStorediSCSIVolumes', '-'): 'describe-stored-iscsi-volumes',
     ('CreateStorediSCSIVolume', '_'): 'create_stored_iscsi_volume',
     ('CreateStorediSCSIVolume', '-'): 'create-stored-iscsi-volume',
+    ('ListHITsForQualificationType', '_'): 'list_hits_for_qualification_type',
+    ('ListHITsForQualificationType', '-'): 'list-hits-for-qualification-type',
 }
 # The items in this dict represent partial renames to apply globally to all
 # services which might have a matching argument or operation. This way a
diff --git a/botocore/client.py b/botocore/client.py
index 2e3394549..821f6fd6d 100644
--- a/botocore/client.py
+++ b/botocore/client.py
@@ -496,6 +496,19 @@ class BaseClient(object):
         self._exceptions = None
         self._register_handlers()
 
+    def __getattr__(self, item):
+        event_name = 'getattr.%s.%s' % (self._service_model.service_name, item)
+        handler, event_response = self.meta.events.emit_until_response(
+            event_name, client=self)
+
+        if event_response is not None:
+            return event_response
+
+        raise AttributeError(
+            "'%s' object has no attribute '%s'" % (
+                self.__class__.__name__, item)
+        )
+
     def _register_handlers(self):
         # Register the handler required to sign requests.
         self.meta.events.register('request-created.%s' %
diff --git a/botocore/handlers.py b/botocore/handlers.py
index 5c7711de6..05211b098 100644
--- a/botocore/handlers.py
+++ b/botocore/handlers.py
@@ -814,11 +814,25 @@ class ParameterAlias(object):
         section.write(updated_content)
 
 
+class ClientMethodAlias(object):
+    def __init__(self, actual_name):
+        """ Aliases a non-extant method to an existing method.
+
+        :param actual_name: The name of the method that actually exists on
+            the client.
+        """
+        self._actual = actual_name
+
+    def __call__(self, client, **kwargs):
+        return getattr(client, self._actual)
+
 # This is a list of (event_name, handler).
 # When a Session is created, everything in this list will be
 # automatically registered with that Session.
 
 BUILTIN_HANDLERS = [
+    ('getattr.mturk.list_hi_ts_for_qualification_type',
+     ClientMethodAlias('list_hits_for_qualification_type')),
     ('before-parameter-build.s3.UploadPart',
      convert_body_to_file_like_object, REGISTER_LAST),
     ('before-parameter-build.s3.PutObject',


Test patch:
diff --git a/tests/functional/test_mturk.py b/tests/functional/test_mturk.py
new file mode 100644
index 000000000..c87567abe
--- /dev/null
+++ b/tests/functional/test_mturk.py
@@ -0,0 +1,40 @@
+# Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import mock
+
+from botocore.stub import Stubber
+from tests import BaseSessionTest
+
+
+class TestMturk(BaseSessionTest):
+    def setUp(self):
+        super(TestMturk, self).setUp()
+        self.region = 'us-west-2'
+        self.client = self.session.create_client(
+            'mturk', self.region)
+        self.stubber = Stubber(self.client)
+        self.stubber.activate()
+
+    def tearDown(self):
+        self.stubber.deactivate()
+
+    def test_list_hits_aliased(self):
+        self.stubber.add_response('list_hits_for_qualification_type', {})
+        self.stubber.add_response('list_hits_for_qualification_type', {})
+
+        params = {'QualificationTypeId': 'foo'}
+
+        self.client.list_hi_ts_for_qualification_type(**params)
+        self.client.list_hits_for_qualification_type(**params)
+
+        self.stubber.assert_no_pending_responses()
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index c76089541..d3aecdcaa 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -1368,6 +1368,35 @@ class TestAutoGeneratedClient(unittest.TestCase):
         service_client.test_operation(Foo='one')
         self.assertFalse(self.endpoint.make_request.called)
 
+    def test_getattr_emits_event(self):
+        emitter = mock.Mock()
+        emitter.emit_until_response.return_value = (None, None)
+
+        creator = self.create_client_creator(event_emitter=emitter)
+        service_client = creator.create_client('myservice', 'us-west-2')
+
+        # Assert that the event hasn't fired yet
+        emitter.emit_until_response.assert_not_called()
+
+        with self.assertRaises(AttributeError):
+            service_client.attribute_that_does_not_exist
+
+        emitter.emit_until_response.assert_called_once_with(
+            'getattr.myservice.attribute_that_does_not_exist',
+            client=service_client
+        )
+
+    def test_getattr_event_returns_response(self):
+        emitter = mock.Mock()
+        emitter.emit_until_response.return_value = (None, 'success')
+
+        creator = self.create_client_creator(event_emitter=emitter)
+        service_client = creator.create_client('myservice', 'us-west-2')
+
+        value = service_client.attribute_that_does_not_exist
+
+        self.assertEqual(value, 'success')
+
 
 class TestClientErrors(TestAutoGeneratedClient):
     def add_error_response(self, error_response):
diff --git a/tests/unit/test_handlers.py b/tests/unit/test_handlers.py
index 0602b336e..d6164cef0 100644
--- a/tests/unit/test_handlers.py
+++ b/tests/unit/test_handlers.py
@@ -1221,3 +1221,13 @@ class TestParameterAlias(unittest.TestCase):
         contents = self.sample_section.flush_structure().decode('utf-8')
         self.assertIn(self.alias_name + '=',  contents)
         self.assertNotIn(self.original_name + '=', contents)
+
+
+class TestCommandAlias(unittest.TestCase):
+    def test_command_alias(self):
+        alias = handlers.ClientMethodAlias('foo')
+        client = mock.Mock()
+        client.foo.return_value = 'bar'
+
+        response = alias(client=client)()
+        self.assertEqual(response, 'bar')