Repository: boto/botocore
Pull request #747 (2015-12-28 22:39:53)

Improve error message when creating unknown service
Fixes boto/boto3#418

Error message before:

```
ValidationError: Invalid value ('ecr') for param service_name of type str
```

Error message now:

```
UnknownServiceError: Unknown service: 'asdf'. Valid service names are: apigateway, ..., swf, waf, workspaces
```

cc @kyleknap @mtdowling @rayluo @JordonPhillips 


Patch:
diff --git a/botocore/exceptions.py b/botocore/exceptions.py
index fb3cddf17..48cedc9dc 100644
--- a/botocore/exceptions.py
+++ b/botocore/exceptions.py
@@ -28,6 +28,17 @@ class BotoCoreError(Exception):
         self.kwargs = kwargs
 
 
+class UnknownServiceError(BotoCoreError):
+    """Raised when trying to load data for an unknown service.
+
+    :ivar service_name: The name of the unknown service.
+
+    """
+    fmt = (
+        "Unknown service: '{service_name}'. Valid service names are: "
+        "{known_service_names}")
+
+
 class DataNotFoundError(BotoCoreError):
     """
     The data associated with a particular path could not be loaded.
diff --git a/botocore/loaders.py b/botocore/loaders.py
index e8443eee5..ede3a0103 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, ValidationError
+from botocore.exceptions import DataNotFoundError, UnknownServiceError
 
 
 def instance_cache(func):
@@ -331,15 +331,21 @@ class Loader(object):
         :param api_version: The API version to load.  If this is not
             provided, then the latest API version will be used.
 
-        :return: The loaded data, or a DataNotFoundError if no data
-            could be found.
+        :raises: UnknownServiceError if there is no known service with
+            the provided service_name.
 
+        :raises: DataNotFoundError if no data could be found for the
+            service_name/type_name/api_version.
+
+        :return: The loaded data, as a python type (e.g. dict, list, etc).
         """
         # 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')
+        known_services = self.list_available_services('service-2')
+        if service_name not in known_services:
+            raise UnknownServiceError(
+                service_name=service_name,
+                known_service_names=', '.join(sorted(known_services)))
         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 b3368eb58..e526fe345 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, ValidationError
+from botocore.exceptions import DataNotFoundError, UnknownServiceError
 from botocore.loaders import JSONFileLoader
 from botocore.loaders import Loader, create_loader
 
@@ -152,7 +152,10 @@ class TestLoader(BaseEnvVar):
         loader.determine_latest_version = mock.Mock(return_value='2015-03-01')
         loader.list_available_services = mock.Mock(return_value=['baz'])
 
-        with self.assertRaises(ValidationError):
+        # Should have a) the unknown service name and b) list of valid
+        # service names.
+        with self.assertRaisesRegexp(UnknownServiceError,
+                                     'Unknown service.*BAZ.*baz'):
             loader.load_service_model('BAZ', type_name='service-2')
 
     def test_create_loader_parses_data_path(self):