Repository: boto/botocore (python)

Issue:
Make clients case sensitive
Following up from https://github.com/boto/boto3/issues/171, this should *not* be allowed:


```
>>> import botocore.session
>>> session = botocore.session.get_session()
>>> session.create_client('S3')
<botocore.client.S3 object at 0x10368e2b0>
```

Our reference docs has all the services in lower case, and all our examples use lower case, so we should enforce that this is what you specify.  It's also what you get back from `session.get_available_services()`.

Pull request description:
Added validation to ensure that service names passed in conform to those exposed by the service model
Fixes #611 


Patch:
diff --git a/botocore/loaders.py b/botocore/loaders.py
index 5e0b63ad3..3ec069f9f 100644
--- a/botocore/loaders.py
+++ b/botocore/loaders.py
@@ -98,7 +98,7 @@ import os
 from botocore import BOTOCORE_ROOT
 from botocore.compat import json
 from botocore.compat import OrderedDict
-from botocore.exceptions import DataNotFoundError
+from botocore.exceptions import DataNotFoundError, ValidationError
 
 
 def instance_cache(func):
@@ -333,6 +333,9 @@ class Loader(object):
         """
         # Wrapper around the load_data.  This will calculate the path
         # to call load_data with.
+        if service_name not in self.list_available_services('service-2'):
+            raise ValidationError(value=service_name, param='service_name',
+                                  type_name='str')
         if api_version is None:
             api_version = self.determine_latest_version(
                 service_name, type_name)


Test patch:
diff --git a/tests/unit/test_loaders.py b/tests/unit/test_loaders.py
index deda4dd3b..6ec766bdb 100644
--- a/tests/unit/test_loaders.py
+++ b/tests/unit/test_loaders.py
@@ -24,7 +24,7 @@ import contextlib
 
 import mock
 
-from botocore.exceptions import DataNotFoundError
+from botocore.exceptions import DataNotFoundError, ValidationError
 from botocore.loaders import JSONFileLoader
 from botocore.loaders import Loader, create_loader
 
@@ -129,9 +129,25 @@ class TestLoader(BaseEnvVar):
                         file_loader=FakeLoader(),
                         include_default_search_paths=False)
         loader.determine_latest_version = mock.Mock(return_value='2015-03-01')
+        loader.list_available_services = mock.Mock(return_value=['baz'])
         loaded = loader.load_service_model('baz', type_name='service-2')
         self.assertEqual(loaded, ['loaded data'])
 
+    @mock.patch('os.path.isdir', mock.Mock(return_value=True))
+    def test_load_service_model_enforces_case(self):
+        class FakeLoader(object):
+            def load_file(self, name):
+                return ['loaded data']
+
+        loader = Loader(extra_search_paths=['foo'],
+                        file_loader=FakeLoader(),
+                        include_default_search_paths=False)
+        loader.determine_latest_version = mock.Mock(return_value='2015-03-01')
+        loader.list_available_services = mock.Mock(return_value=['baz'])
+
+        with self.assertRaises(ValidationError):
+            loader.load_service_model('BAZ', type_name='service-2')
+
     def test_create_loader_parses_data_path(self):
         search_path = os.pathsep.join(['foo', 'bar', 'baz'])
         loader = create_loader(search_path)
diff --git a/tests/unit/test_waiters.py b/tests/unit/test_waiters.py
index 26ed7f275..7dd29a525 100644
--- a/tests/unit/test_waiters.py
+++ b/tests/unit/test_waiters.py
@@ -565,16 +565,20 @@ class ServiceWaiterFunctionalTest(BaseEnvVar):
 
     def get_waiter_model(self, service, api_version=None):
         """Get the waiter model for the service."""
-        return WaiterModel(self.loader.load_service_model(
-            service, type_name='waiters-2', api_version=api_version))
+        with mock.patch('botocore.loaders.Loader.list_available_services',
+                        return_value=[service]):
+            return WaiterModel(self.loader.load_service_model(
+                service, type_name='waiters-2', api_version=api_version))
 
     def get_service_model(self, service, api_version=None):
         """Get the service model for the service."""
-        return ServiceModel(
-            self.loader.load_service_model(
-                service, type_name='service-2', api_version=api_version),
-            service_name=service
-        )
+        with mock.patch('botocore.loaders.Loader.list_available_services',
+                        return_value=[service]):
+            return ServiceModel(
+                self.loader.load_service_model(
+                    service, type_name='service-2', api_version=api_version),
+                service_name=service
+            )
 
 
 class CloudFrontWaitersTest(ServiceWaiterFunctionalTest):