Source code for walkscore.api

# -*- coding: utf-8 -*-

# The lack of a module docstring for this module is **INTENTIONAL**.
# The module is imported into the documentation using Sphinx's autodoc
# extension, and its member function documentation is automatically incorporated
# there as needed.

import os

from validator_collection import validators

from walkscore.http_client import default_http_client
from walkscore.locationscore import LocationScore
from walkscore.utilities import check_for_errors
from walkscore.errors import AuthenticationError, InvalidCoordinatesError


[docs]class WalkScoreAPI(object): """The Python object which exposes the WalkScore API's functionality.""" _BASE_URL = 'http://api.walkscore.com' _SCORE_ENDPOINT = '/score' def __init__(self, api_key = None, http_client = None, proxy = None, max_retries = None): """ :param api_key: The API key provided by WalkScore used to authenticate your application. If :obj:`None <python:None>` or not specified will default to the ``WALKSCORE_API_KEY`` environment variable if present, and :obj:`None <python:None>` if not. :type api_key: :class:`str <python:str>` / :obj:`None <python:None>` :param http_client: The HTTP client instance to use for the execution of requests. If not overridden, will default to `urlfetch <https://github.com/ifduyue/urlfetch>`_, `requests <https://github.com/kennethreitz/requests>`_, `pycurl <https://github.com/pycurl/pycurl>`_, :doc:`urllib2 <python:urllib2>` in order based on whether they are available in the environment. .. tip:: You can override the HTTP client by supplying a :class:`HTTPClient <walkscore.http_client.HTTPClient>` instance to the method. :type http_client: :class:`HTTPClient <walkscore.http_client.HTTPClient>` :param proxy: The URL to use as an HTTP proxy. Defaults to :obj:`None <python:None>`. :type proxy: :class:`str <python:str>` / :obj:`None <python:None>` :param max_retries: Determines the maximum number of HTTP request attempts to make on network failure before giving up. If not specified, defaults to environment variable ``BACKOFF_DEFAULT_TRIES`` or ``3`` if not available. :type max_retries: :class:`int <python:int>` """ self._api_key = None self._http_client = None self._proxy = None self._max_retries = None if not api_key: api_key = os.getenv('WALKSCORE_API_KEY', None) self.api_key = api_key self.http_client = http_client self.proxy = proxy self.max_retries = max_retries @property def api_key(self): """The API key used to sign requests made against the API. :rtype: :class:`str <python:str>` / :obj:`None <python:None>` """ return self._api_key @api_key.setter def api_key(self, value): self._api_key = validators.string(value, allow_empty = True) @property def http_client(self): """The object instance to use as the HTTP client to make HTTP requests against the WalkScore API. :rtype: :class:`HTTPClient <walkscore.http_client.HTTPClient>` """ if not self._http_client: return default_http_client(proxy = self.proxy) return self._http_client @http_client.setter def http_client(self, value): if value and not checkers.is_type(value, 'HTTPClient'): raise ValueError('http_client must be of type "HTTPClient", was "%s"' % str(type(value))) self._http_client = value @property def proxy(self): """The URL to use as a proxy for requests made to the WalkScore API. :rtype: :class:`str <python:str>` / :obj:`None <python:None>` """ return self._proxy @proxy.setter def proxy(self, value): self._proxy = validators.url(value, allow_empty = True) self.http_client.proxy = self._proxy @property def max_retries(self): """The number of attempts to make on network connectivity-related API failures. :rtype: :class:`int <python:int>` """ if not self._max_retries: return validators.integer(os.getenv('BACKOFF_DEFAULT_TRIES', '3')) return self._max_retries @max_retries.setter def max_retries(self, value): self._max_retries = validators.integer(value, allow_empty = True) @property def _API_URL(self): """The full URL to use when requesting scores from the WalkScore API. :rtype: :class:`str <python:str>` """ return self._BASE_URL + self._SCORE_ENDPOINT
[docs] def get_score(self, latitude, longitude, address = None, return_transit_score = True, return_bike_score = True, max_retries = None): """Retrieve the :term:`WalkScore`, :term:`TransitScore`, and/or :term:`BikeScore` for a given location from the WalkScore API. :param latitude: The latitude of the location whose score(s) should be retrieved. :type latitude: numeric :param longitude: The longitude of the location whose score(s) should be retrieved. :type longitude: numeric :param address: The address whose score(s) should be retrieved. Defaults to :obj:`None <python:None>`. :type address: :class:`str <python:str>` / :obj:`None <python:None>` :param return_transit_score: If ``True``, will return the location's :term:`TransitScore`. Defaults to ``True``. :type return_transit_score: :class:`bool <python:bool>` :param return_bike_score: If ``True``, will return the location's :term:`BikeScore`. Defaults to ``True``. :type return_bike_score: :class:`bool <python:bool>` :param max_retries: The maximum number of retries to attempt if the WalkScore API times out or otherwise fails to return a response. If :obj:`None <python:None>`, will apply the default the configured when initializing the WalkScore API object. To suppress all retries, set to 0. Defaults to :obj:`None <python:None>`. :type max_retries: :obj:`None <python:None>` / :class:`int <python:int>` :returns: The location's :term:`WalkScore`, :term:`TransitScore`, and :term:`BikeScore` with meta-data. :rtype: :class:`LocationScore <walkscore.locationscore.LocationScore>` :raises AuthenticationError: if the API key is invalid :raises ScoreInProgressError: if the score is being calculated and is not currently available :raises WalkScoreError: if an internal WalkScore API error occurred :raises QuotaError: if your daily quota has been exceeded :raises BlockedIPError: if your IP address has been blocked :raises InvalidCoordinatesError: if your latitude/longitude coordinates are not valid """ if not self.api_key: raise AuthenticationError('No API key supplied.') if not (latitude and longitude): raise InvalidCoordinatesError('No coordinates supplied.') if latitude: latitude = validators.numeric(latitude, allow_empty = False) latitude = str(latitude) if longitude: longitude = validators.numeric(longitude, allow_empty = False) longitude = str(longitude) if max_retries is None: max_retries = self.max_retries method = 'GET' parameters = { 'address': address, 'lat': latitude, 'lon': longitude, 'format': 'json', 'transit': 1, 'bike': 1, 'wsapikey': self.api_key } if not return_bike_score: parameters['bike'] = None if not return_transit_score: parameters['transit'] = None if max_retries: response = self.http_client.request_with_retries(method, self._API_URL, parameters = parameters, request_body = None) else: response = self.http_client.request(method, self._API_URL, parameters = parameters, request_body = None) result_set = check_for_errors(*response) result = LocationScore.from_json(result_set[0], api_compatible = True) result.address = address result.original_latitude = latitude result.original_longitude = longitude return result