Repository: mapbox/mapbox-sdk-py

Issue:
Add api_name and api_version Service class attributes
These can be used in constructing the the base URI and in debugging.

Discussion:
sgillies: 👋  @amishas157 @perrygeo can you review this for me?

Patch:
diff --git a/.travis.yml b/.travis.yml
index b8e7592..ff98592 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,7 @@ python:
   - '3.4'
   - '3.5'
   - '3.6'
-  - pypy
+  - pypy-5.3.1
 cache:
   directories:
     - $HOME/.pip-cache/
diff --git a/CHANGES b/CHANGES
index ee8f2dd..9a18eff 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+Next (YYYY-MM-DD)
+-----------------
+- Add api_name and api_version class attributes (#207).
+
+
 0.14.0 (2017-07-10)
 -------------------
 - Permit images of size 1 and 1280 (#196)
diff --git a/README.rst b/README.rst
index d5e4771..fc69108 100644
--- a/README.rst
+++ b/README.rst
@@ -15,56 +15,57 @@ The Mapbox Python SDK is a low-level client API, not a Resource API such as the
 Services
 ========
 
-- **Analytics** `examples <./docs/analytics.md>`__, `website <https://www.mapbox.com/api-documentation/#analytics>`__
+- **Analytics V1** `examples <./docs/analytics.md>`__, `website <https://www.mapbox.com/api-documentation/#analytics>`__
 
   - API usage for services by resource. 
   - available for premium and enterprise plans.
 
-- **Directions** `examples <./docs/directions.md#directions>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#directions>`__
+- **Directions V4** `examples <./docs/directions.md#directions>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#directions>`__
 
   - Profiles for driving, walking, and cycling
   - GeoJSON & Polyline formatting
   - Instructions as text or HTML
 
-- **Distance** `examples <./docs/distance.md#distance>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#directions-matrix>`__
+- **Distance V1** `examples <./docs/distance.md#distance>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#directions-matrix>`__
 
   - Travel-time tables between up to 100 points
   - Profiles for driving, walking and cycling
 
-- **Geocoding** `examples <./docs/geocoding.md#geocoding>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#geocoding>`__
+- **Geocoding V5** `examples <./docs/geocoding.md#geocoding>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#geocoding>`__
 
   - Forward (place names ⇢ longitude, latitude)
   - Reverse (longitude, latitude ⇢ place names)
 
-- **Map Matching** `examples <./docs/mapmatching.md#map-matching>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#map-matching>`__
+- **Map Matching V4** `examples <./docs/mapmatching.md#map-matching>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#map-matching>`__
 
   - Snap GPS traces to OpenStreetMap data
 
-- **Static Maps** `examples <./docs/static.md#static-maps>`__, `website <https://www.mapbox.com/api-documentation/pages/static_classic.html>`__
+- **Static Maps V4** `examples <./docs/static.md#static-maps>`__, `website <https://www.mapbox.com/api-documentation/pages/static_classic.html>`__
 
   - Generate standalone images from existing Mapbox *mapids* (tilesets)
   - Render with GeoJSON overlays
   
-- **Static Styles** `examples <./docs/static.md#static-maps>`__, `website <https://www.mapbox.com/api-documentation/#static>`__
+- **Static Styles V1** `examples <./docs/static.md#static-maps>`__, `website <https://www.mapbox.com/api-documentation/#static>`__
 
   - Generate standalone images from existing Mapbox *styles*
   - Render with GeoJSON overlays
   - Adjust pitch and bearing, decimal zoom levels
   
-- **Surface** `examples <./docs/surface.md#surface>`__, `website <https://www.mapbox.com/developers/api/surface/>`__
+- **Surface V4** `examples <./docs/surface.md#surface>`__, `website <https://www.mapbox.com/developers/api/surface/>`__
 
   - Interpolates values along lines. Useful for elevation traces.
 
-- **Uploads** `examples <./docs/uploads.md#uploads>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#uploads>`__
+- **Uploads V1** `examples <./docs/uploads.md#uploads>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#uploads>`__
 
   - Upload data to be processed and hosted by Mapbox.
 
-- **Datasets** `examples <./docs/datasets.md#datasets>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#datasets>`__
+- **Datasets V1** `examples <./docs/datasets.md#datasets>`__, `website <https://www.mapbox.com/api-documentation/?language=Python#datasets>`__
 
   - Manage editable collections of GeoJSON features
   - Persistent storage for custom geographic data
 
-Other services coming soon.
+Please note that there may be some lag between the release of new Mapbox web
+services and releases of this package.
 
 Installation
 ============
diff --git a/mapbox/services/analytics.py b/mapbox/services/analytics.py
index dafb3c5..5428b70 100644
--- a/mapbox/services/analytics.py
+++ b/mapbox/services/analytics.py
@@ -5,15 +5,14 @@ from uritemplate import URITemplate
 from mapbox.services.base import Service
 from mapbox import errors
 
+
 class Analytics(Service):
-    """Access to Analytics API"""
+    """Access to Analytics API V1"""
 
+    api_name = 'analytics'
+    api_version = 'v1'
     valid_resource_types = ['tokens', 'styles', 'accounts', 'tilesets']
 
-    @property
-    def baseuri(self):
-        return 'https://{0}/analytics/v1'.format(self.host)
-
     def _validate_resource_type(self, resource_type):
         if resource_type not in self.valid_resource_types:
             raise errors.InvalidResourceTypeError(
diff --git a/mapbox/services/base.py b/mapbox/services/base.py
index 9bae0a4..c8cae87 100644
--- a/mapbox/services/base.py
+++ b/mapbox/services/base.py
@@ -42,6 +42,8 @@ class Service(object):
     """Service base class."""
 
     default_host = 'api.mapbox.com'
+    api_name = 'hors service'
+    api_version = 'v0'
 
     def __init__(self, access_token=None, host=None, cache=None):
         """Constructs a Service object.
@@ -55,6 +57,11 @@ class Service(object):
         if cache:
             self.session = CacheControl(self.session, cache=cache)
 
+    @property
+    def baseuri(self):
+        return 'https://{0}/{1}/{2}'.format(
+            self.host, self.api_name, self.api_version)
+
     @property
     def username(self):
         """Get username from access token.
diff --git a/mapbox/services/datasets.py b/mapbox/services/datasets.py
index 921897b..064f27e 100644
--- a/mapbox/services/datasets.py
+++ b/mapbox/services/datasets.py
@@ -8,11 +8,10 @@ from mapbox.services.base import Service
 
 
 class Datasets(Service):
-    """Access to the Datasets API."""
+    """Access to the Datasets API V1"""
 
-    @property
-    def baseuri(self):
-        return 'https://{0}/datasets/v1'.format(self.host)
+    api_name = 'datasets'
+    api_version = 'v1'
 
     def _attribs(self, name=None, description=None):
         """Form an attributes dictionary from keyword args."""
diff --git a/mapbox/services/directions.py b/mapbox/services/directions.py
index d6f6af3..a782c33 100644
--- a/mapbox/services/directions.py
+++ b/mapbox/services/directions.py
@@ -6,7 +6,10 @@ from mapbox import errors
 
 
 class Directions(Service):
-    """Access to the Directions API."""
+    """Access to the Directions API V4"""
+
+    api_name = 'directions'
+    api_version = 'v4'
 
     valid_profiles = ['mapbox.driving',
                       'mapbox.cycling',
@@ -16,7 +19,8 @@ class Directions(Service):
 
     @property
     def baseuri(self):
-        return 'https://{0}/v4/directions'.format(self.host)
+        return 'https://{0}/{2}/{1}'.format(
+            self.host, self.api_name, self.api_version)
 
     def _validate_profile(self, profile):
         if profile not in self.valid_profiles:
diff --git a/mapbox/services/distance.py b/mapbox/services/distance.py
index be27a34..9362532 100644
--- a/mapbox/services/distance.py
+++ b/mapbox/services/distance.py
@@ -6,13 +6,17 @@ from mapbox.services.base import Service
 
 
 class Distance(Service):
-    """Access to the Distance API."""
+    """Access to the Distance API V1"""
+
+    api_name = 'distances'
+    api_version = 'v1'
 
     valid_profiles = ['driving', 'cycling', 'walking']
 
     @property
     def baseuri(self):
-        return 'https://{0}/distances/v1/mapbox'.format(self.host)
+        return 'https://{0}/{1}/{2}/mapbox'.format(
+            self.host, self.api_name, self.api_version)
 
     def _validate_profile(self, profile):
         if profile not in self.valid_profiles:
diff --git a/mapbox/services/geocoding.py b/mapbox/services/geocoding.py
index e22b91e..bad7dd3 100644
--- a/mapbox/services/geocoding.py
+++ b/mapbox/services/geocoding.py
@@ -7,14 +7,12 @@ from mapbox.services.base import Service
 
 
 class Geocoder(Service):
-    """Access to the Geocoding API."""
+    """Access to the Geocoding API V5"""
 
+    api_name = 'geocoding'
+    api_version = 'v5'
     precision = {'reverse': 5, 'proximity': 3}
 
-    @property
-    def baseuri(self):
-        return 'https://{0}/geocoding/v5'.format(self.host)
-
     def __init__(self, name='mapbox.places', access_token=None, cache=None,
                  host=None):
         """Constructs a Geocoding Service object.
diff --git a/mapbox/services/mapmatching.py b/mapbox/services/mapmatching.py
index cec6acc..66f6c8f 100644
--- a/mapbox/services/mapmatching.py
+++ b/mapbox/services/mapmatching.py
@@ -7,14 +7,12 @@ from mapbox.services.base import Service
 
 
 class MapMatcher(Service):
-    """Access to the Map Matching API."""
+    """Access to the Map Matching API V4"""
 
+    api_name = 'matching'
+    api_version = 'v4'
     valid_profiles = ['mapbox.driving', 'mapbox.cycling', 'mapbox.walking']
 
-    @property
-    def baseuri(self):
-        return 'https://{0}/matching/v4'.format(self.host)
-
     def _validate_profile(self, profile):
         if profile not in self.valid_profiles:
             raise errors.InvalidProfileError(
diff --git a/mapbox/services/static.py b/mapbox/services/static.py
index a532ac4..4aadee7 100644
--- a/mapbox/services/static.py
+++ b/mapbox/services/static.py
@@ -8,11 +8,14 @@ from mapbox.utils import normalize_geojson_featurecollection
 
 
 class Static(Service):
-    """Access to the Static Map API."""
+    """Access to the Static Map API V4"""
+
+    api_name = None
+    api_version = 'v4'
 
     @property
     def baseuri(self):
-        return 'https://{0}/v4'.format(self.host)
+        return 'https://{0}/{1}'.format(self.host, self.api_version)
 
     def _validate_lat(self, val):
         if val < -85.0511 or val > 85.0511:
diff --git a/mapbox/services/static_style.py b/mapbox/services/static_style.py
index b0cef3a..750f0fa 100644
--- a/mapbox/services/static_style.py
+++ b/mapbox/services/static_style.py
@@ -50,11 +50,10 @@ def validate_bearing(val):
 
 
 class StaticStyle(Service):
-    """Access to the Static Map API."""
+    """Access to the Static Map API V1"""
 
-    @property
-    def baseuri(self):
-        return 'https://{0}/styles/v1'.format(self.host)
+    api_name = 'styles'
+    api_version = 'v1'
 
     def tile(self, username, style_id, z, x, y, tile_size=512, retina=False):
         "/styles/v1/{username}/{style_id}/tiles/{tileSize}/{z}/{x}/{y}{@2x}"
diff --git a/mapbox/services/surface.py b/mapbox/services/surface.py
index 8c69073..2dd3dcc 100644
--- a/mapbox/services/surface.py
+++ b/mapbox/services/surface.py
@@ -5,11 +5,15 @@ from mapbox.services.base import Service
 
 
 class Surface(Service):
-    """Access to the Surface API."""
+    """Access to the Surface API V4"""
+
+    api_name = 'surface'
+    api_version = 'v4'
 
     @property
     def baseuri(self):
-        return 'https://{0}/v4/surface'.format(self.host)
+        return 'https://{0}/{2}/{1}'.format(
+            self.host, self.api_name, self.api_version)
 
     def surface(self,
                 features,
diff --git a/mapbox/services/uploads.py b/mapbox/services/uploads.py
index 8c4a34d..94dcd75 100644
--- a/mapbox/services/uploads.py
+++ b/mapbox/services/uploads.py
@@ -10,7 +10,7 @@ from mapbox.services.base import Service
 
 
 class Uploader(Service):
-    """Access to the Upload API.
+    """Access to the Upload API V1
 
     Example usage:
 
@@ -29,9 +29,8 @@ class Uploader(Service):
         assert job not in u.list().json()
     """
 
-    @property
-    def baseuri(self):
-        return 'https://{0}/uploads/v1'.format(self.host)
+    api_name = 'uploads'
+    api_version = 'v1'
 
     def _get_credentials(self):
         """Gets temporary S3 credentials to stage user-uploaded files
diff --git a/tox.ini b/tox.ini
index bdce644..bceb578 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,4 +7,4 @@ deps =
     pytest-cov
     responses
 commands = 
-    py.test --cov {envsitepackagesdir}/mapbox --cov-report term-missing
+    py.test --cov mapbox --cov-report term-missing


Test patch:
diff --git a/tests/test_analytics.py b/tests/test_analytics.py
index 0940e41..1ddd84e 100644
--- a/tests/test_analytics.py
+++ b/tests/test_analytics.py
@@ -5,25 +5,37 @@ import responses
 import mapbox
 from mapbox import errors
 
+
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Analytics()
+    assert serv.api_name == 'analytics'
+    assert serv.api_version == 'v1'
+
+
 def test_resource_type_invalid():
     """'random' is not a valid resource type."""
     with pytest.raises(errors.InvalidResourceTypeError):
         mapbox.Analytics(access_token='pk.test')._validate_resource_type('random')
 
+
 @pytest.mark.parametrize('resource_type', ['tokens', 'styles', 'accounts', 'tilesets'])
 def test_profile_valid(resource_type):
     """Resource types are valid."""
     assert resource_type == mapbox.Analytics(
         access_token='pk.test')._validate_resource_type(resource_type)
 
+
 @pytest.mark.parametrize(
-    'start, end',[('2016-03-22T00:00:00.000Z', None),
-                ('2016-03-22T00:00:00.000Z', '2016-03-20T00:00:00.000Z'),
-                ('2016-03-22T00:00:00.000Z', '2017-04-20T00:00:00.000Z')])
+    'start, end', [
+        ('2016-03-22T00:00:00.000Z', None),
+        ('2016-03-22T00:00:00.000Z', '2016-03-20T00:00:00.000Z'),
+        ('2016-03-22T00:00:00.000Z', '2017-04-20T00:00:00.000Z')])
 def test_period_invalid(start, end):
     with pytest.raises(errors.InvalidPeriodError):
         mapbox.Analytics(access_token='pk.test')._validate_period(start, end)
 
+
 @pytest.mark.parametrize(
     'start, end', [('2016-03-22T00:00:00.000Z', '2016-03-24T00:00:00.000Z'),
                    (None, None)])
@@ -31,16 +43,19 @@ def test_period_valid(start, end):
     period = start, end
     assert period == mapbox.Analytics(access_token='pk.test')._validate_period(start, end)
 
+
 def test_username_invalid():
     """Username is requird."""
     with pytest.raises(errors.InvalidUsernameError):
         mapbox.Analytics(access_token='pk.test')._validate_username(None)
 
+
 def test_username_valid():
     """Providing valid username"""
     user = 'abc'
     assert user == mapbox.Analytics(access_token='pk.test')._validate_username(user)
 
+
 def test_id_invalid():
     """id is required when resource type is other than accounts"""
     resource_type = 'tokens'
@@ -48,13 +63,14 @@ def test_id_invalid():
     with pytest.raises(errors.InvalidId):
         mapbox.Analytics(access_token='pk.test')._validate_id(resource_type, id)
 
+
 @pytest.mark.parametrize(
-    'resource_type, id', [('accounts', None),
-                   ('tokens', 'abc')])
+    'resource_type, id', [('accounts', None), ('tokens', 'abc')])
 def test_id_valid(resource_type, id):
     """id is not required when resource type is accounts"""
     assert id == mapbox.Analytics(access_token='pk.test')._validate_id(resource_type, id)
 
+
 @responses.activate
 def test_analytics():
     responses.add(
diff --git a/tests/test_base.py b/tests/test_base.py
index 2025043..da442ad 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -10,6 +10,14 @@ from mapbox.errors import TokenError
 from mapbox.services import base
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = base.Service()
+    assert serv.api_name == 'hors service'
+    assert serv.api_version == 'v0'
+    assert serv.baseuri == 'https://api.mapbox.com/hors service/v0'
+
+
 def test_service_session():
     """Get a session using a token"""
     session = base.Session('pk.test')
diff --git a/tests/test_datasets.py b/tests/test_datasets.py
index b35f517..53a4e0c 100644
--- a/tests/test_datasets.py
+++ b/tests/test_datasets.py
@@ -11,6 +11,13 @@ access_token = 'pk.{0}.test'.format(
     base64.b64encode(b'{"u":"testuser"}').decode('utf-8'))
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = Datasets()
+    assert serv.api_name == 'datasets'
+    assert serv.api_version == 'v1'
+
+
 def test_datasets_service_properties():
     """Get expected username and baseuri."""
     datasets = Datasets(access_token=access_token)
@@ -240,7 +247,7 @@ def test_update_feature():
         callback=request_callback)
 
     response = Datasets(access_token=access_token).update_feature(
-            'test', '1', {'type': 'Feature'})
+        'test', '1', {'type': 'Feature'})
     assert response.status_code == 200
 
 
diff --git a/tests/test_directions.py b/tests/test_directions.py
index f4160b1..2628d26 100644
--- a/tests/test_directions.py
+++ b/tests/test_directions.py
@@ -21,6 +21,13 @@ points = [{
             36.92217534275667]}}]
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Directions()
+    assert serv.api_name == 'directions'
+    assert serv.api_version == 'v4'
+
+
 @responses.activate
 @pytest.mark.parametrize("cache", [None, DictCache()])
 def test_directions(cache):
@@ -41,7 +48,6 @@ def test_directions(cache):
 
 
 @responses.activate
-
 def test_directions_geojson():
     with open('tests/moors.json') as fh:
         body = fh.read()
diff --git a/tests/test_distances.py b/tests/test_distances.py
index 7abacd7..4c4be8b 100644
--- a/tests/test_distances.py
+++ b/tests/test_distances.py
@@ -29,6 +29,13 @@ points = [{
             36.922175]}}]
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Distance()
+    assert serv.api_name == 'distances'
+    assert serv.api_version == 'v1'
+
+
 def test_profile_invalid():
     """'jetpack' is not a valid profile."""
     with pytest.raises(ValueError):
@@ -74,21 +81,3 @@ def test_distances_matrix():
     # 3x3 list
     assert len(matrix) == 3
     assert len(matrix[0]) == 3
-
-
-@responses.activate
-def test_distances_matrix():
-
-    responses.add(
-        responses.POST,
-        'https://api.mapbox.com/distances/v1/mapbox/driving?access_token=pk.test',
-        match_querystring=True,
-        body='{"durations":[[0,4977,5951],[4963,0,9349],[5881,9317,0]]}',
-        status=200,
-        content_type='application/json')
-
-    res = mapbox.Distance(access_token='pk.test').distances(points)
-    matrix = res.json()['durations']
-    # 3x3 list
-    assert len(matrix) == 3
-    assert len(matrix[0]) == 3
diff --git a/tests/test_geocoder.py b/tests/test_geocoder.py
index 46efa78..7f17b54 100644
--- a/tests/test_geocoder.py
+++ b/tests/test_geocoder.py
@@ -8,6 +8,13 @@ import pytest
 import mapbox
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Geocoder()
+    assert serv.api_name == 'geocoding'
+    assert serv.api_version == 'v5'
+
+
 def test_geocoder_default_name():
     """Default name is set"""
     geocoder = mapbox.Geocoder()
@@ -121,7 +128,7 @@ def test_validate_country_codes_err():
 
 def test_validate_country():
     assert mapbox.Geocoder()._validate_country_codes(
-        ('us', 'br')) ==  {'country': 'us,br'}
+        ('us', 'br')) == {'country': 'us,br'}
 
 
 def test_validate_place_types_err():
@@ -229,7 +236,7 @@ def test_geocoder_forward_bbox():
 
     response = mapbox.Geocoder(
         access_token='pk.test').forward(
-            'washington', bbox=(-78.3284,38.6039,-78.0428,38.7841))
+            'washington', bbox=(-78.3284, 38.6039, -78.0428, 38.7841))
     assert response.status_code == 200
     assert response.json()['query'] == ["washington"]
 
diff --git a/tests/test_mapmatching.py b/tests/test_mapmatching.py
index 0791a88..53743bd 100644
--- a/tests/test_mapmatching.py
+++ b/tests/test_mapmatching.py
@@ -24,6 +24,13 @@ def line_feature():
                 [13.420631289482117, 52.50294888790448]]}}
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.MapMatcher()
+    assert serv.api_name == 'matching'
+    assert serv.api_version == 'v4'
+
+
 @responses.activate
 def test_matching(line_feature):
 
diff --git a/tests/test_staticmaps.py b/tests/test_staticmaps.py
index cb325bf..6f1c62d 100644
--- a/tests/test_staticmaps.py
+++ b/tests/test_staticmaps.py
@@ -18,6 +18,13 @@ import responses
 import mapbox
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Static()
+    assert serv.api_name is None
+    assert serv.api_version == 'v4'
+
+
 @pytest.fixture
 def points():
     points = [
diff --git a/tests/test_staticstyle.py b/tests/test_staticstyle.py
index 6cf9941..33ca666 100644
--- a/tests/test_staticstyle.py
+++ b/tests/test_staticstyle.py
@@ -33,6 +33,13 @@ def points():
                 coordinates=[-61.6, 12.0]))]
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.StaticStyle()
+    assert serv.api_name == 'styles'
+    assert serv.api_version == 'v1'
+
+
 @responses.activate
 def test_staticmap_lonlatzpitchbearing():
 
diff --git a/tests/test_surface.py b/tests/test_surface.py
index 562f9c7..c85b656 100644
--- a/tests/test_surface.py
+++ b/tests/test_surface.py
@@ -20,6 +20,13 @@ points = [{
         "coordinates": [-112.083965, 36.053845]}}]
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Surface()
+    assert serv.api_name == 'surface'
+    assert serv.api_version == 'v4'
+
+
 @responses.activate
 def test_surface():
     body = """{"results":[{"id":0,"latlng":{"lat":36.05322,"lng":-112.084004},"ele":2186.361304424316},{"id":1,"latlng":{"lat":36.053573,"lng":-112.083914},"ele":2187.6233827411997},{"id":2,"latlng":{"lat":36.053845,"lng":-112.083965},"ele":2163.921475128245}],"attribution":"&lt;a href='https://www.mapbox.com/about/maps/' target='_blank'&gt;&amp;copy; Mapbox&lt;/a&gt;"}"""
diff --git a/tests/test_upload.py b/tests/test_upload.py
index 1afcfc3..9b2ec99 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -25,6 +25,13 @@ upload_response_body = """
     "name": null}}""".format(username=username)
 
 
+def test_class_attrs():
+    """Get expected class attr values"""
+    serv = mapbox.Uploader()
+    assert serv.api_name == 'uploads'
+    assert serv.api_version == 'v1'
+
+
 @responses.activate
 def test_get_credentials():
     query_body = """