diff --git a/routingpy/client_base.py b/routingpy/client_base.py new file mode 100644 index 0000000..087f585 --- /dev/null +++ b/routingpy/client_base.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2021 GIS OPS UG +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +# +""" +Core client functionality, common across all routers. +""" + + +from routingpy.__version__ import __version__ + +from abc import ABCMeta, abstractmethod +from datetime import timedelta +import requests +from urllib.parse import urlencode + + +_DEFAULT_USER_AGENT = "routingpy/v{}".format(__version__) +_RETRIABLE_STATUSES = set([503]) + + +class options(object): + """ + Contains default configuration options for all routers, e.g. `timeout` and `proxies`. Each router can take the same + configuration values, which will override the values set in `options` object. + + Example for overriding default values for `user_agent` and proxies: + + >>> from routingpy.routers import options + >>> from routingpy.routers import MapboxValhalla + >>> options.default_user_agent = 'amazing_routing_app' + >>> options.default_proxies = {'https': '129.125.12.0'} + >>> router = MapboxValhalla(my_key) + >>> print(router.headers) + {'User-Agent': 'amazing_routing_app', 'Content-Type': 'application/json'} + >>> print(router.proxies) + {'https': '129.125.12.0'} + + Attributes: + self.default_timeout: + Combined connect and read timeout for HTTP requests, in + seconds. Specify "None" for no timeout. Integer. + + self.default_retry_timeout: + Timeout across multiple retriable requests, in + seconds. Integer. + + self.default_retry_over_query_limit: + If True, client will not raise an exception + on HTTP 429, but instead jitter a sleeping timer to pause between + requests until HTTP 200 or retry_timeout is reached. Boolean. + + self.default_skip_api_error: + Continue with batch processing if a :class:`routingpy.exceptions.RouterApiError` is + encountered (e.g. no route found). If False, processing will discontinue and raise an error. Boolean. + + self.default_user_agent: + User-Agent to send with the requests to routing API. String. + + self.default_proxies: + Proxies passed to the requests library. Dictionary. + """ + + default_timeout = 60 + default_retry_timeout = 60 + default_retry_over_query_limit = True + default_skip_api_error = False + default_user_agent = _DEFAULT_USER_AGENT + default_proxies = None + + +# To avoid trouble when respecting timeout for individual routers (i.e. can't be None, since that's no timeout) +DEFAULT = type("object", (object,), {"__repr__": lambda self: "DEFAULT"})() + + +class BaseClient(metaclass=ABCMeta): + """Abstract base class every client inherits from. Authentication is handled in each subclass.""" + + def __init__( + self, + base_url, + user_agent=None, + timeout=DEFAULT, + retry_timeout=None, + retry_over_query_limit=None, + skip_api_error=None, + **kwargs + ): + """ + :param base_url: The base URL for the request. All routers must provide a default. + Should not have a trailing slash. + :type base_url: string + + :param user_agent: User-Agent to send with the requests to routing API. + Overrides ``options.default_user_agent``. + :type user_agent: string + + :param timeout: Combined connect and read timeout for HTTP requests, in + seconds. Specify "None" for no timeout. + :type timeout: int + + :param retry_timeout: Timeout across multiple retriable requests, in + seconds. + :type retry_timeout: int + + :param retry_over_query_limit: If True, client will not raise an exception + on HTTP 429, but instead jitter a sleeping timer to pause between + requests until HTTP 200 or retry_timeout is reached. + :type retry_over_query_limit: bool + + :param skip_api_error: Continue with batch processing if a :class:`routingpy.exceptions.RouterApiError` is + encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default False. + :type skip_api_error: bool + + :param **kwargs: Additional keyword arguments for the `requests library `_ + :type **kwargs: dict + """ + self.base_url = base_url + + self.retry_over_query_limit = ( + retry_over_query_limit + if retry_over_query_limit is False + else options.default_retry_over_query_limit + ) + self.retry_timeout = timedelta(seconds=retry_timeout or options.default_retry_timeout) + + self.skip_api_error = skip_api_error or options.default_skip_api_error + + self.headers = { + "User-Agent": user_agent or options.default_user_agent, + "Content-Type": "application/json", + } + + self.timeout = timeout if timeout != DEFAULT else options.default_timeout + + self.kwargs = kwargs + + self._req = None + + @abstractmethod + def _request( + self, + url, + get_params={}, + post_params=None, + first_request_time=None, + retry_counter=0, + dry_run=None, + ): + """Performs HTTP GET/POST with credentials, returning the body as + JSON. + + :param url: URL path for the request. Should begin with a slash. + :type url: string + + :param get_params: HTTP GET parameters. + :type get_params: dict or list of tuples + + :param post_params: HTTP POST parameters. Only specified by calling method. + :type post_params: dict + + :param first_request_time: The time of the first request (None if no + retries have occurred). + :type first_request_time: :class:`datetime.datetime` + + :param retry_counter: The number of this retry, or zero for first attempt. + :type retry_counter: int + + :param dry_run: If 'true', only prints URL and parameters. 'true' or 'false'. + :type dry_run: string + + :raises routingpy.exceptions.RouterApiError: when the API returns an error due to faulty configuration. + :raises routingpy.exceptions.RouterServerError: when the API returns a server error. + :raises routingpy.exceptions.RouterError: when anything else happened while requesting. + :raises routingpy.exceptions.JSONParseError: when the JSON response can't be parsed. + :raises routingpy.exceptions.Timeout: when the request timed out. + :raises routingpy.exceptions.TransportError: when something went wrong while trying to + execute a request. + + :returns: raw JSON response. + :rtype: dict + """ + pass + + @staticmethod + def _generate_auth_url(path, params): + """Returns the path and query string portion of the request URL, first + adding any necessary parameters. + + :param path: The path portion of the URL. + :type path: string + + :param params: URL parameters. + :type params: dict or list of key/value tuples + + :rtype: string + + """ + + if isinstance(params, dict): + params = sorted(dict(**params).items()) + elif isinstance(params, (list, tuple)): + params = params + + return path + "?" + requests.utils.unquote_unreserved(urlencode(params)) diff --git a/routingpy/routers/base.py b/routingpy/client_default.py similarity index 55% rename from routingpy/routers/base.py rename to routingpy/client_default.py index b3618b7..5dcb2d0 100644 --- a/routingpy/routers/base.py +++ b/routingpy/client_default.py @@ -14,84 +14,21 @@ # License for the specific language governing permissions and limitations under # the License. # -""" -Core client functionality, common across all routers. -""" +from .client_base import BaseClient, DEFAULT, _RETRIABLE_STATUSES, options from routingpy import exceptions from routingpy.utils import get_ordinal -from ..__version__ import __version__ -from abc import ABCMeta, abstractmethod from datetime import datetime -from datetime import timedelta -import warnings -import requests -from urllib.parse import urlencode import json import random +import requests import time - -_DEFAULT_USER_AGENT = "routingpy/v{}".format(__version__) -_RETRIABLE_STATUSES = set([503]) - - -class options(object): - """ - Contains default configuration options for all routers, e.g. `timeout` and `proxies`. Each router can take the same - configuration values, which will override the values set in `options` object. - - Example for overriding default values for `user_agent` and proxies: - - >>> from routingpy.routers import options - >>> from routingpy.routers import MapboxValhalla - >>> options.default_user_agent = 'amazing_routing_app' - >>> options.default_proxies = {'https': '129.125.12.0'} - >>> router = MapboxValhalla(my_key) - >>> print(router.headers) - {'User-Agent': 'amazing_routing_app', 'Content-Type': 'application/json'} - >>> print(router.proxies) - {'https': '129.125.12.0'} - - Attributes: - self.default_timeout: - Combined connect and read timeout for HTTP requests, in - seconds. Specify "None" for no timeout. Integer. - - self.default_retry_timeout: - Timeout across multiple retriable requests, in - seconds. Integer. - - self.default_retry_over_query_limit: - If True, client will not raise an exception - on HTTP 429, but instead jitter a sleeping timer to pause between - requests until HTTP 200 or retry_timeout is reached. Boolean. - - self.default_skip_api_error: - Continue with batch processing if a :class:`routingpy.exceptions.RouterApiError` is - encountered (e.g. no route found). If False, processing will discontinue and raise an error. Boolean. - - self.default_user_agent: - User-Agent to send with the requests to routing API. String. - - self.default_proxies: - Proxies passed to the requests library. Dictionary. - """ - - default_timeout = 60 - default_retry_timeout = 60 - default_retry_over_query_limit = True - default_skip_api_error = False - default_user_agent = _DEFAULT_USER_AGENT - default_proxies = None - - -# To avoid trouble when respecting timeout for individual routers (i.e. can't be None, since that's no timeout) -DEFAULT = type("object", (object,), {"__repr__": lambda self: "DEFAULT"})() +import warnings -class Router(metaclass=ABCMeta): - """Abstract base class every router inherits from. Authentication is handled in each subclass.""" +class Client(BaseClient): + """Default client class for requests handling. Uses the requests package.""" def __init__( self, @@ -99,9 +36,9 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=None, skip_api_error=None, + **kwargs ): """ :param base_url: The base URL for the request. All routers must provide a default. @@ -120,19 +57,6 @@ def __init__( seconds. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. - - Example: - - >>> from routingpy.routers import ORS - >>> router = ORS(my_key, requests_kwargs={'proxies': {'https': '129.125.12.0'}}) - >>> print(router.proxies) - {'https': '129.125.12.0'} - - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -141,40 +65,34 @@ def __init__( :param skip_api_error: Continue with batch processing if a :class:`routingpy.exceptions.RouterApiError` is encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default False. :type skip_api_error: bool + + :param **kwargs: Additional arguments, such as headers or proxies. + :type **kwargs: dict """ self._session = requests.Session() - self.base_url = base_url - - self.retry_over_query_limit = ( - retry_over_query_limit - if retry_over_query_limit is False - else options.default_retry_over_query_limit + super(Client, self).__init__( + base_url, + user_agent=user_agent, + timeout=timeout, + retry_timeout=retry_timeout, + retry_over_query_limit=retry_over_query_limit, + skip_api_error=skip_api_error, + **kwargs ) - self.retry_timeout = timedelta(seconds=retry_timeout or options.default_retry_timeout) - - self.skip_api_error = skip_api_error or options.default_skip_api_error - - self.requests_kwargs = requests_kwargs or {} - self.headers = { - "User-Agent": user_agent or options.default_user_agent, - "Content-Type": "application/json", - } + self.kwargs = kwargs or {} try: - self.headers.update(self.requests_kwargs["headers"]) + self.headers.update(self.kwargs["headers"]) except KeyError: pass - self.requests_kwargs["headers"] = self.headers - self.timeout = timeout if timeout != DEFAULT else options.default_timeout - self.requests_kwargs["timeout"] = self.timeout + self.kwargs["headers"] = self.headers + self.kwargs["timeout"] = self.timeout - self.proxies = self.requests_kwargs.get("proxies") or options.default_proxies + self.proxies = self.kwargs.get("proxies") or options.default_proxies if self.proxies: - self.requests_kwargs["proxies"] = self.proxies - - self._req = None + self.kwargs["proxies"] = self.proxies def _request( self, @@ -183,7 +101,6 @@ def _request( post_params=None, first_request_time=None, retry_counter=0, - requests_kwargs=None, dry_run=None, ): """Performs HTTP GET/POST with credentials, returning the body as @@ -205,11 +122,6 @@ def _request( :param retry_counter: The number of this retry, or zero for first attempt. :type retry_counter: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. - :type requests_kwargs: dict - :param dry_run: If 'true', only prints URL and parameters. 'true' or 'false'. :type dry_run: string @@ -243,10 +155,7 @@ def _request( authed_url = self._generate_auth_url(url, get_params) - # Default to the client-level self.requests_kwargs, with method-level - # requests_kwargs arg overriding. - requests_kwargs = requests_kwargs or {} - final_requests_kwargs = dict(self.requests_kwargs, **requests_kwargs) + final_requests_kwargs = self.kwargs # Determine GET/POST. requests_method = self._session.get @@ -283,9 +192,7 @@ def _request( UserWarning, ) - return self._request( - url, get_params, post_params, first_request_time, retry_counter + 1, requests_kwargs - ) + return self._request(url, get_params, post_params, first_request_time, retry_counter + 1) try: result = self._get_body(response) @@ -311,9 +218,7 @@ def _request( UserWarning, ) # Retry request. - return self._request( - url, get_params, post_params, first_request_time, retry_counter + 1, requests_kwargs - ) + return self._request(url, get_params, post_params, first_request_time, retry_counter + 1) @property def req(self): @@ -342,42 +247,3 @@ def _get_body(response): raise exceptions.RouterError(status_code, body) return body - - @staticmethod - def _generate_auth_url(path, params): - """Returns the path and query string portion of the request URL, first - adding any necessary parameters. - - :param path: The path portion of the URL. - :type path: string - - :param params: URL parameters. - :type params: dict or list of key/value tuples - - :rtype: string - - """ - - if isinstance(params, dict): - params = sorted(dict(**params).items()) - elif isinstance(params, (list, tuple)): - params = params - - return path + "?" + requests.utils.unquote_unreserved(urlencode(params)) - - @abstractmethod - def directions(self): - """Implement this method for the router's directions endpoint or raise NotImplementedError""" - pass - - @abstractmethod - def isochrones(self): - """Implement this method for the router's isochrones endpoint or raise NotImplementedError""" - pass - - @abstractmethod - def matrix(self): - """Implement this method for the router's matrix endpoint or raise NotImplementedError""" - pass - - # Other endpoints are allowed and encouraged! diff --git a/routingpy/routers/__init__.py b/routingpy/routers/__init__.py index 3034bd8..0aeb765 100644 --- a/routingpy/routers/__init__.py +++ b/routingpy/routers/__init__.py @@ -13,7 +13,7 @@ .. _`contribution guidelines`: https://github.com/gis-ops/routing-py/blob/master/CONTRIBUTING.md .. _here: https://github.com/gis-ops/routing-py#api """ -from .base import options # noqa: F401 +from routingpy.client_base import options # noqa: F401 from routingpy.exceptions import RouterNotFound from .openrouteservice import ORS diff --git a/routingpy/routers/google.py b/routingpy/routers/google.py index a1d7a3e..1ac8ca9 100644 --- a/routingpy/routers/google.py +++ b/routingpy/routers/google.py @@ -15,7 +15,8 @@ # the License. # -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import convert, utils from routingpy.direction import Directions, Direction from routingpy.matrix import Matrix @@ -23,7 +24,7 @@ from operator import itemgetter -class Google(Router): +class Google: """Performs requests to the Google API services.""" _base_url = "https://maps.googleapis.com/maps/api" @@ -34,9 +35,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs={}, retry_over_query_limit=True, skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes a Google client. @@ -56,21 +58,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import Google - >>> router = Google(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -81,18 +68,24 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ self.key = api_key - super(Google, self).__init__( + self.client = client( self._base_url, user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + **client_kwargs ) class WayPoint(object): @@ -218,7 +211,7 @@ def directions( # noqa: C901 :type transit_routing_preference: str :param dry_run: Print URL and parameters without sending the request. - :param dry_run: bool + :type dry_run: bool :returns: One or multiple route(s) from provided coordinates and restrictions. :rtype: :class:`routingpy.direction.Direction` or :class:`routingpy.direction.Directions` @@ -287,7 +280,7 @@ def directions( # noqa: C901 params["transit_routing_preference"] = transit_routing_preference return self._parse_direction_json( - self._request("/directions/json", get_params=params, dry_run=dry_run), alternatives + self.client._request("/directions/json", get_params=params, dry_run=dry_run), alternatives ) @staticmethod @@ -468,7 +461,7 @@ def matrix( # noqa: C901 params["transit_routing_preference"] = transit_routing_preference return self._parse_matrix_json( - self._request("/distancematrix/json", get_params=params, dry_run=dry_run) + self.client._request("/distancematrix/json", get_params=params, dry_run=dry_run) ) @staticmethod diff --git a/routingpy/routers/graphhopper.py b/routingpy/routers/graphhopper.py index d079389..02e7e28 100644 --- a/routingpy/routers/graphhopper.py +++ b/routingpy/routers/graphhopper.py @@ -17,7 +17,8 @@ from typing import List, Tuple # noqa: F401 -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import convert from routingpy import utils from routingpy.direction import Direction, Directions @@ -25,7 +26,7 @@ from routingpy.matrix import Matrix -class Graphhopper(Router): +class Graphhopper: """Performs requests to the Graphhopper API services.""" _DEFAULT_BASE_URL = "https://graphhopper.com/api/1" @@ -37,9 +38,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs={}, retry_over_query_limit=False, skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes an graphhopper client. @@ -63,21 +65,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import Graphhopper - >>> router = Graphhopper(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -88,20 +75,27 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict + """ if base_url == self._DEFAULT_BASE_URL and api_key is None: raise KeyError("API key must be specified.") self.key = api_key - super(Graphhopper, self).__init__( + self.client = client( base_url, user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + **client_kwargs ) def directions( # noqa: C901 @@ -380,7 +374,7 @@ def directions( # noqa: C901 ) return self._parse_directions_json( - self._request("/route", get_params=params, dry_run=dry_run), algorithm, elevation + self.client._request("/route", get_params=params, dry_run=dry_run), algorithm, elevation ) @staticmethod @@ -497,7 +491,7 @@ def isochrones( params.append(("debug", convert._convert_bool(debug))) return self._parse_isochrone_json( - self._request("/isochrone", get_params=params, dry_run=dry_run), + self.client._request("/isochrone", get_params=params, dry_run=dry_run), type, intervals[0], buckets, @@ -620,7 +614,7 @@ def matrix( params.append(("debug", convert._convert_bool(debug))) return self._parse_matrix_json( - self._request("/matrix", get_params=params, dry_run=dry_run), + self.client._request("/matrix", get_params=params, dry_run=dry_run), ) @staticmethod diff --git a/routingpy/routers/heremaps.py b/routingpy/routers/heremaps.py index 9c94d4f..e02514e 100644 --- a/routingpy/routers/heremaps.py +++ b/routingpy/routers/heremaps.py @@ -15,7 +15,8 @@ # the License. # -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import convert from routingpy.direction import Direction, Directions from routingpy.isochrone import Isochrones, Isochrone @@ -24,7 +25,7 @@ from operator import itemgetter -class HereMaps(Router): +class HereMaps: """Performs requests to the HERE Maps API services.""" _DEFAULT_BASE_URL = "https://route.api.here.com/routing/7.2" @@ -37,10 +38,11 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=False, skip_api_error=None, api_key=None, + client=Client, + **client_kwargs ): """ Initializes a HERE Maps client. @@ -63,21 +65,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import HereMaps - >>> router = HereMaps(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -88,6 +75,12 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ if app_id is None and app_code is None and api_key is None: @@ -108,14 +101,14 @@ def __init__( self.base_url = self._DEFAULT_BASE_URL self.auth = {"app_id": self.app_id, "app_code": self.app_code} - super(HereMaps, self).__init__( + self.client = client( self.base_url, user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + **client_kwargs ) class Waypoint(object): @@ -779,7 +772,7 @@ def directions( # noqa: C901 params["speedProfile"] = speed_profile return self._parse_direction_json( - self._request( + self.client._request( convert._delimit_list(["/calculateroute", format], "."), get_params=params, dry_run=dry_run, @@ -1076,7 +1069,7 @@ def isochrones( # noqa: C901 params["speedProfile"] = speed_profile return self._parse_isochrone_json( - self._request( + self.client._request( convert._delimit_list(["/calculateisoline", format], "."), get_params=params, dry_run=dry_run, @@ -1365,7 +1358,7 @@ def matrix( # noqa: C901 params["speedProfile"] = speed_profile return self._parse_matrix_json( - self._request( + self.client._request( convert._delimit_list(["/calculatematrix", format], "."), get_params=params, dry_run=dry_run, diff --git a/routingpy/routers/mapbox_osrm.py b/routingpy/routers/mapbox_osrm.py index 1e42bc2..245e181 100644 --- a/routingpy/routers/mapbox_osrm.py +++ b/routingpy/routers/mapbox_osrm.py @@ -18,17 +18,18 @@ Core client functionality, common across all API requests. """ -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import convert, utils from routingpy.direction import Direction, Directions from routingpy.isochrone import Isochrone, Isochrones from routingpy.matrix import Matrix -class MapboxOSRM(Router): +class MapboxOSRM: """Performs requests to the OSRM API services.""" - _base_url = 'https://api.mapbox.com' + _base_url = "https://api.mapbox.com" def __init__( self, @@ -36,9 +37,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=False, - skip_api_error=None + skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes a Mapbox OSRM client. @@ -58,21 +60,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import MapboxOSRM - >>> router = MapboxOSRM(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -83,13 +70,26 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ self.api_key = api_key - super(MapboxOSRM, self).__init__( - self._base_url, user_agent, timeout, retry_timeout, requests_kwargs, retry_over_query_limit, - skip_api_error + client_kwargs.update({"headers": {"Content-Type": "application/x-www-form-urlencoded"}}) + + self.client = client( + self._base_url, + user_agent, + timeout, + retry_timeout, + retry_over_query_limit, + skip_api_error, + **client_kwargs ) def directions( # noqa: C901 @@ -113,7 +113,7 @@ def directions( # noqa: C901 voice_units=None, waypoint_names=None, waypoint_targets=None, - dry_run=None + dry_run=None, ): """Get directions between an origin point and a destination point. @@ -222,17 +222,17 @@ def directions( # noqa: C901 """ coords = convert._delimit_list( - [convert._delimit_list([convert._format_float(f) for f in pair]) for pair in locations], ';' + [convert._delimit_list([convert._format_float(f) for f in pair]) for pair in locations], ";" ) - params = {'coordinates': coords} + params = {"coordinates": coords} if radiuses: - params["radiuses"] = convert._delimit_list(radiuses, ';') + params["radiuses"] = convert._delimit_list(radiuses, ";") if bearings: params["bearings"] = convert._delimit_list( - [convert._delimit_list(pair) for pair in bearings], ';' + [convert._delimit_list(pair) for pair in bearings], ";" ) if alternatives is not None: @@ -254,50 +254,49 @@ def directions( # noqa: C901 params["overview"] = convert._convert_bool(overview) if exclude is not None: - params['exclude'] = exclude + params["exclude"] = exclude if approaches: - params['approaches'] = ';' + convert._delimit_list(approaches, ';') + params["approaches"] = ";" + convert._delimit_list(approaches, ";") if banner_instructions: - params['banner_instuctions'] = convert._convert_bool(banner_instructions) + params["banner_instuctions"] = convert._convert_bool(banner_instructions) if language: - params['language'] = language + params["language"] = language if roundabout_exits: - params['roundabout_exits'] = convert._convert_bool(roundabout_exits) + params["roundabout_exits"] = convert._convert_bool(roundabout_exits) if voice_instructions: - params['voide_instructions'] = convert._convert_bool(voice_instructions) + params["voide_instructions"] = convert._convert_bool(voice_instructions) if voice_units: - params['voice_units'] = voice_units + params["voice_units"] = voice_units if waypoint_names: - params['waypoint_names'] = convert._delimit_list(waypoint_names, ';') + params["waypoint_names"] = convert._delimit_list(waypoint_names, ";") if waypoint_targets: - params['waypoint_targets'] = ';' + convert._delimit_list( + params["waypoint_targets"] = ";" + convert._delimit_list( [ - convert._delimit_list([convert._format_float(f) - for f in pair]) + convert._delimit_list([convert._format_float(f) for f in pair]) for pair in waypoint_targets - ], ';' + ], + ";", ) - get_params = {'access_token': self.api_key} if self.api_key else {} + get_params = {"access_token": self.api_key} if self.api_key else {} return self._parse_direction_json( - self._request( + self.client._request( "/directions/v5/mapbox/" + profile, get_params=get_params, post_params=params, dry_run=dry_run, - requests_kwargs={"headers": { - "Content-Type": 'application/x-www-form-urlencoded' - }}, - ), alternatives, geometries + ), + alternatives, + geometries, ) @staticmethod @@ -309,18 +308,16 @@ def _parse_direction_json(response, alternatives, geometry_format): return Direction() def _parse_geometry(route_geometry): - if geometry_format in (None, 'polyline'): + if geometry_format in (None, "polyline"): geometry = [ - list(reversed(coord)) - for coord in utils.decode_polyline5(route_geometry, is3d=False) + list(reversed(coord)) for coord in utils.decode_polyline5(route_geometry, is3d=False) ] - elif geometry_format == 'polyline6': + elif geometry_format == "polyline6": geometry = [ - list(reversed(coord)) - for coord in utils.decode_polyline6(route_geometry, is3d=False) + list(reversed(coord)) for coord in utils.decode_polyline6(route_geometry, is3d=False) ] - elif geometry_format == 'geojson': - geometry = route_geometry['coordinates'] + elif geometry_format == "geojson": + geometry = route_geometry["coordinates"] else: raise ValueError( "OSRM: parameter geometries needs one of ['polyline', 'polyline6', 'geojson" @@ -329,22 +326,22 @@ def _parse_geometry(route_geometry): if alternatives: routes = [] - for route in response['routes']: + for route in response["routes"]: routes.append( Direction( - geometry=_parse_geometry(route['geometry']), - duration=int(route['duration']), - distance=int(route['distance']), - raw=route + geometry=_parse_geometry(route["geometry"]), + duration=int(route["duration"]), + distance=int(route["distance"]), + raw=route, ) ) return Directions(routes, response) else: return Direction( - geometry=_parse_geometry(response['routes'][0]['geometry']), - duration=int(response['routes'][0]['duration']), - distance=int(response['routes'][0]['distance']), - raw=response + geometry=_parse_geometry(response["routes"][0]["geometry"]), + duration=int(response["routes"][0]["duration"]), + distance=int(response["routes"][0]["distance"]), + raw=response, ) def isochrones( @@ -356,7 +353,7 @@ def isochrones( polygons=None, denoise=None, generalize=None, - dry_run=None + dry_run=None, ): """Gets isochrones or equidistants for a range of time values around a given set of coordinates. @@ -396,29 +393,31 @@ def isochrones( """ params = { - "contours_minutes": convert._delimit_list([int(x / 60) for x in sorted(intervals)], ','), - 'access_token': self.api_key, - 'costing': profile + "contours_minutes": convert._delimit_list([int(x / 60) for x in sorted(intervals)], ","), + "access_token": self.api_key, + "costing": profile, } - locations_string = convert._delimit_list(locations, ',') + locations_string = convert._delimit_list(locations, ",") if contours_colors: - params["contours_colors"] = convert._delimit_list(contours_colors, ',') + params["contours_colors"] = convert._delimit_list(contours_colors, ",") if polygons is not None: - params['polygons'] = polygons + params["polygons"] = polygons if denoise: - params['denoise'] = denoise + params["denoise"] = denoise if generalize: - params['generalize'] = generalize + params["generalize"] = generalize return self._parse_isochrone_json( - self._request( - "/isochrone/v1/" + profile + '/' + locations_string, get_params=params, dry_run=dry_run - ), intervals, locations + self.client._request( + "/isochrone/v1/" + profile + "/" + locations_string, get_params=params, dry_run=dry_run + ), + intervals, + locations, ) @staticmethod @@ -428,11 +427,13 @@ def _parse_isochrone_json(response, intervals, locations): return Isochrones( [ Isochrone( - geometry=isochrone['geometry']['coordinates'], + geometry=isochrone["geometry"]["coordinates"], interval=intervals[idx], - center=locations - ) for idx, isochrone in enumerate(list(reversed(response['features']))) - ], response + center=locations, + ) + for idx, isochrone in enumerate(list(reversed(response["features"]))) + ], + response, ) def matrix( @@ -443,7 +444,7 @@ def matrix( destinations=None, annotations=None, fallback_speed=None, - dry_run=None + dry_run=None, ): """ Gets travel distance and time for a matrix of origins and destinations. @@ -484,28 +485,28 @@ def matrix( """ coords = convert._delimit_list( - [convert._delimit_list([convert._format_float(f) for f in pair]) for pair in locations], ';' + [convert._delimit_list([convert._format_float(f) for f in pair]) for pair in locations], ";" ) - params = {'access_token': self.api_key} + params = {"access_token": self.api_key} if sources: - params['sources'] = convert._delimit_list(sources, ';') + params["sources"] = convert._delimit_list(sources, ";") if destinations: - params['destinations'] = convert._delimit_list(destinations, ';') + params["destinations"] = convert._delimit_list(destinations, ";") if annotations: - params['annotations'] = convert._delimit_list(annotations) + params["annotations"] = convert._delimit_list(annotations) if fallback_speed: - params['fallback_speed'] = str(fallback_speed) + params["fallback_speed"] = str(fallback_speed) return self._parse_matrix_json( - self._request( - "/directions-matrix/v1/mapbox/" + profile + '/' + coords, + self.client._request( + "/directions-matrix/v1/mapbox/" + profile + "/" + coords, get_params=params, - dry_run=dry_run + dry_run=dry_run, ) ) @@ -515,5 +516,5 @@ def _parse_matrix_json(response): return Matrix() return Matrix( - durations=response.get('durations'), distances=response.get('distances'), raw=response + durations=response.get("durations"), distances=response.get("distances"), raw=response ) diff --git a/routingpy/routers/mapbox_valhalla.py b/routingpy/routers/mapbox_valhalla.py index f9b74f8..6bea1e8 100644 --- a/routingpy/routers/mapbox_valhalla.py +++ b/routingpy/routers/mapbox_valhalla.py @@ -15,7 +15,8 @@ # the License. from .valhalla import Valhalla -from .base import DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client class MapboxValhalla(Valhalla): @@ -29,9 +30,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=False, skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes a Valhalla client. @@ -51,21 +53,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import MapboxValhalla - >>> router = MapboxValhalla(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -76,6 +63,12 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ super(MapboxValhalla, self).__init__( @@ -84,7 +77,8 @@ def __init__( user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + client=client, + **client_kwargs ) diff --git a/routingpy/routers/openrouteservice.py b/routingpy/routers/openrouteservice.py index a4efdf4..1737162 100644 --- a/routingpy/routers/openrouteservice.py +++ b/routingpy/routers/openrouteservice.py @@ -15,14 +15,15 @@ # the License. # -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import utils from routingpy.direction import Direction, Directions from routingpy.isochrone import Isochrone, Isochrones from routingpy.matrix import Matrix -class ORS(Router): +class ORS: """Performs requests to the ORS API services.""" _DEFAULT_BASE_URL = "https://api.openrouteservice.org" @@ -34,9 +35,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=False, skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes an openrouteservice client. @@ -60,21 +62,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import ORS - >>> router = ORS(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -85,24 +72,30 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ if base_url == self._DEFAULT_BASE_URL and api_key is None: raise KeyError("API key must be specified.") - requests_kwargs = requests_kwargs or {} - headers = requests_kwargs.get("headers") or {} + client_kwargs = client_kwargs or {} + headers = client_kwargs.get("headers") or {} headers.update({"Authorization": api_key}) - requests_kwargs.update({"headers": headers}) + client_kwargs.update({"headers": headers}) - super(ORS, self).__init__( + self.client = client( base_url, user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + **client_kwargs ) def directions( # noqa: C901 @@ -314,7 +307,7 @@ def directions( # noqa: C901 params["options"] = options return self._parse_direction_json( - self._request( + self.client._request( "/v2/directions/" + profile + "/" + format, get_params={}, post_params=params, @@ -465,7 +458,7 @@ def isochrones( params["intersections"] = intersections return self._parse_isochrone_json( - self._request( + self.client._request( "/v2/isochrones/" + profile + "/geojson", get_params={}, post_params=params, @@ -559,7 +552,7 @@ def matrix( params["units"] = units return self._parse_matrix_json( - self._request( + self.client._request( "/v2/matrix/" + profile + "/json", get_params={}, post_params=params, dry_run=dry_run ) ) diff --git a/routingpy/routers/osrm.py b/routingpy/routers/osrm.py index a5b8307..f41b09c 100644 --- a/routingpy/routers/osrm.py +++ b/routingpy/routers/osrm.py @@ -17,13 +17,14 @@ from typing import List # noqa: F401 -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import convert, utils from routingpy.direction import Directions, Direction from routingpy.matrix import Matrix -class OSRM(Router): +class OSRM: """Performs requests to the OSRM API services.""" _DEFAULT_BASE_URL = "https://router.project-osrm.org" @@ -34,9 +35,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=False, skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes an OSRM client. @@ -57,21 +59,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import OSRM - >>> router = OSRM(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -82,16 +69,22 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ - super(OSRM, self).__init__( + self.client = client( base_url, user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + **client_kwargs ) def directions( @@ -199,7 +192,9 @@ def directions( params["overview"] = convert._convert_bool(overview) return self._parse_direction_json( - self._request("/route/v1/" + profile + "/" + coords, get_params=params, dry_run=dry_run), + self.client._request( + "/route/v1/" + profile + "/" + coords, get_params=params, dry_run=dry_run + ), alternatives, geometries, ) @@ -327,7 +322,9 @@ def matrix( params["annotations"] = convert._delimit_list(annotations) return self._parse_matrix_json( - self._request("/table/v1/" + profile + "/" + coords, get_params=params, dry_run=dry_run) + self.client._request( + "/table/v1/" + profile + "/" + coords, get_params=params, dry_run=dry_run + ) ) @staticmethod diff --git a/routingpy/routers/valhalla.py b/routingpy/routers/valhalla.py index 08ce257..4701fb4 100644 --- a/routingpy/routers/valhalla.py +++ b/routingpy/routers/valhalla.py @@ -20,7 +20,8 @@ from typing import List, Union # noqa: F401 -from .base import Router, DEFAULT +from routingpy.client_base import DEFAULT +from routingpy.client_default import Client from routingpy import utils from routingpy.direction import Direction from routingpy.isochrone import Isochrone, Isochrones @@ -29,7 +30,7 @@ from operator import itemgetter -class Valhalla(Router): +class Valhalla: """Performs requests to a Valhalla instance.""" def __init__( @@ -39,9 +40,10 @@ def __init__( user_agent=None, timeout=DEFAULT, retry_timeout=None, - requests_kwargs=None, retry_over_query_limit=False, skip_api_error=None, + client=Client, + **client_kwargs ): """ Initializes a Valhalla client. @@ -65,21 +67,6 @@ def __init__( seconds. Default :attr:`routingpy.routers.options.default_retry_timeout`. :type retry_timeout: int - :param requests_kwargs: Extra keyword arguments for the requests - library, which among other things allow for proxy auth to be - implemented. **Note**, that ``proxies`` can be set globally - in :attr:`routingpy.routers.options.default_proxies`. - - Example: - - >>> from routingpy.routers import Valhalla - >>> router = Valhalla(my_key, requests_kwargs={ - >>> 'proxies': {'https': '129.125.12.0'} - >>> }) - >>> print(router.proxies) - {'https': '129.125.12.0'} - :type requests_kwargs: dict - :param retry_over_query_limit: If True, client will not raise an exception on HTTP 429, but instead jitter a sleeping timer to pause between requests until HTTP 200 or retry_timeout is reached. @@ -90,18 +77,24 @@ def __init__( encountered (e.g. no route found). If False, processing will discontinue and raise an error. Default :attr:`routingpy.routers.options.default_skip_api_error`. :type skip_api_error: bool + + :param client: A client class for request handling. Needs to be derived from :class:`routingpy.base.BaseClient` + :type client: abc.ABCMeta + + :param **client_kwargs: Additional arguments passed to the client, such as headers or proxies. + :type **client_kwargs: dict """ self.api_key = api_key - super(Valhalla, self).__init__( + self.client = client( base_url, user_agent, timeout, retry_timeout, - requests_kwargs, retry_over_query_limit, skip_api_error, + **client_kwargs ) class Waypoint(object): @@ -236,7 +229,8 @@ def directions( get_params = {"access_token": self.api_key} if self.api_key else {} return self._parse_direction_json( - self._request("/route", get_params=get_params, post_params=params, dry_run=dry_run), units + self.client._request("/route", get_params=get_params, post_params=params, dry_run=dry_run), + units, ) @staticmethod @@ -415,7 +409,9 @@ def isochrones( # noqa: C901 get_params = {"access_token": self.api_key} if self.api_key else {} return self._parse_isochrone_json( - self._request("/isochrone", get_params=get_params, post_params=params, dry_run=dry_run), + self.client._request( + "/isochrone", get_params=get_params, post_params=params, dry_run=dry_run + ), intervals, locations, ) @@ -551,7 +547,7 @@ def matrix( get_params = {"access_token": self.api_key} if self.api_key else {} return self._parse_matrix_json( - self._request( + self.client._request( "/sources_to_targets", get_params=get_params, post_params=params, dry_run=dry_run ), units, diff --git a/tests/test_base.py b/tests/test_base.py index 213c48f..48653ec 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -21,13 +21,14 @@ import time import routingpy -from routingpy.routers import options, base +from routingpy.routers import options +from routingpy import client_default import tests as _test -class RouterMock(base.Router): +class ClientMock(client_default.Client): def __init__(self, *args, **kwargs): - super(RouterMock, self).__init__(*args, **kwargs) + super(ClientMock, self).__init__(*args, **kwargs) def directions(self, *args, **kwargs): return self._request(*args, **kwargs) @@ -41,7 +42,7 @@ def matrix(self): class BaseTest(_test.TestCase): def setUp(self): - self.router = RouterMock("https://httpbin.org/") + self.client = ClientMock("https://httpbin.org/") self.params = {"c": "d", "a": "b", "1": "2"} def test_router_by_name(self): @@ -57,17 +58,17 @@ def test_options(self): options.default_retry_timeout = 10 options.default_retry_over_query_limit = False options.default_proxies = {"https": "192.103.10.102"} - new_router = RouterMock("https://foo.bar") + new_client = ClientMock("https://foo.bar") req_kwargs = { "timeout": options.default_timeout, "headers": {"User-Agent": options.default_user_agent, "Content-Type": "application/json"}, "proxies": options.default_proxies, } - self.assertEqual(req_kwargs, new_router.requests_kwargs) - self.assertEqual(new_router.retry_over_query_limit, options.default_retry_over_query_limit) + self.assertEqual(req_kwargs, new_client.kwargs) + self.assertEqual(new_client.retry_over_query_limit, options.default_retry_over_query_limit) def test_urlencode(self): - encoded_params = self.router._generate_auth_url("directions", self.params) + encoded_params = self.client._generate_auth_url("directions", self.params) self.assertEqual("directions?1=2&a=b&c=d", encoded_params) @responses.activate @@ -81,12 +82,12 @@ def test_skip_api_error(self): content_type="application/json", ) - client = RouterMock(base_url="https://httpbin.org", skip_api_error=False) + client = ClientMock(base_url="https://httpbin.org", skip_api_error=False) print(client.skip_api_error) with self.assertRaises(routingpy.exceptions.RouterApiError): client.directions(url="/post", post_params=self.params) - client = RouterMock(base_url="https://httpbin.org", skip_api_error=True) + client = ClientMock(base_url="https://httpbin.org", skip_api_error=True) client.directions(url="/post", post_params=self.params) self.assertEqual(responses.calls[1].response.json(), query) @@ -101,7 +102,7 @@ def test_retry_timeout(self): content_type="application/json", ) - client = RouterMock(base_url="https://httpbin.org", retry_over_query_limit=True, retry_timeout=3) + client = ClientMock(base_url="https://httpbin.org", retry_over_query_limit=True, retry_timeout=3) with self.assertRaises(routingpy.exceptions.OverQueryLimit): client.directions(url="/post", post_params=query) @@ -117,7 +118,7 @@ def test_raise_over_query_limit(self): ) with self.assertRaises(routingpy.exceptions.OverQueryLimit): - client = RouterMock(base_url="https://httpbin.org", retry_over_query_limit=False) + client = ClientMock(base_url="https://httpbin.org", retry_over_query_limit=False) client.directions(url="/post", post_params=query) @responses.activate @@ -134,7 +135,7 @@ def test_raise_timeout_retriable_requests(self): content_type="application/json", ) - client = RouterMock(base_url="https://httpbin.org", retry_timeout=retry_timeout) + client = ClientMock(base_url="https://httpbin.org", retry_timeout=retry_timeout) start = time.time() with self.assertRaises(routingpy.exceptions.Timeout): @@ -154,7 +155,7 @@ def test_dry_run(self): content_type="application/json", ) - self.router.directions(get_params={"format_out": "geojson"}, url="directions/", dry_run="true") + self.client.directions(get_params={"format_out": "geojson"}, url="directions/", dry_run="true") self.assertEqual(0, len(responses.calls)) @@ -164,10 +165,10 @@ def test_headers(self): timeout = {"holaDieWaldFee": 600} headers = {"headers": {"X-Rate-Limit": "50"}} - client = RouterMock("https://httpbin.org", requests_kwargs=dict(timeout, **headers)) + client = ClientMock("https://httpbin.org", **dict(timeout, **headers)) - self.assertDictContainsSubset(timeout, client.requests_kwargs) - self.assertDictContainsSubset(headers["headers"], client.requests_kwargs["headers"]) + self.assertDictContainsSubset(timeout, client.kwargs) + self.assertDictContainsSubset(headers["headers"], client.kwargs["headers"]) @responses.activate def test_req_property(self): @@ -181,7 +182,7 @@ def test_req_property(self): content_type="application/json", ) - self.router.directions(url="routes", get_params={"a": "b"}) + self.client.directions(url="routes", get_params={"a": "b"}) - assert isinstance(self.router.req, requests.PreparedRequest) - self.assertEqual("https://httpbin.org/routes?a=b", self.router.req.url) + assert isinstance(self.client.req, requests.PreparedRequest) + self.assertEqual("https://httpbin.org/routes?a=b", self.client.req.url) diff --git a/tests/test_heremaps.py b/tests/test_heremaps.py index 8687949..f3446bf 100644 --- a/tests/test_heremaps.py +++ b/tests/test_heremaps.py @@ -148,7 +148,7 @@ def test_full_isochrones_response_object(self): responses.add( responses.GET, - "https://isoline.route.api.here.com/routing/7.2/calculateisoline.json", + "https://route.api.here.com/routing/7.2/calculateisoline.json", status=200, json=ENDPOINTS_RESPONSES[self.name]["isochrones"], content_type="application/json", @@ -158,7 +158,7 @@ def test_full_isochrones_response_object(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - "https://isoline.route.api.here.com/routing/7.2/calculateisoline.json?app_code=sample_app_code&" + "https://route.api.here.com/routing/7.2/calculateisoline.json?app_code=sample_app_code&" "app_id=sample_app_id&mode=fastest%3Bcar&quality=1&range=1000%2C2000%2C3000&rangeType=distance&" "singleComponent=false&start=geo%2148.23424%2C8.34234", responses.calls[0].request.url, @@ -178,7 +178,7 @@ def test_full_matrix(self): responses.add( responses.GET, - "https://matrix.route.api.here.com/routing/7.2/calculatematrix.json", + "https://route.api.here.com/routing/7.2/calculatematrix.json", status=200, json=ENDPOINTS_RESPONSES[self.name]["matrix"], content_type="application/json", @@ -188,7 +188,7 @@ def test_full_matrix(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - "https://matrix.route.api.here.com/routing/7.2/calculatematrix.json?app_code=sample_app_code&" + "https://route.api.here.com/routing/7.2/calculatematrix.json?app_code=sample_app_code&" "app_id=sample_app_id&destination0=geo%2149.445776%2C8.780916&height=20&length=10&limitedWeight=10&" "mode=fastest%3Bcar&shippedHazardousGoods=gas%2Cflammable&start0=geo%2149.420577%2C8.688641&" "start1=geo%2149.415776%2C8.680916&summaryAttributes=traveltime%2Ccostfactor&trailersCount=3&truckType=truck&" @@ -333,7 +333,7 @@ def test_full_isochrones_response_object(self): responses.add( responses.GET, - "https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json", + "https://route.ls.hereapi.com/routing/7.2/calculateisoline.json", status=200, json=ENDPOINTS_RESPONSES[self.name]["isochrones"], content_type="application/json", @@ -343,7 +343,7 @@ def test_full_isochrones_response_object(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - "https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json?apikey=sample_api_key&" + "https://route.ls.hereapi.com/routing/7.2/calculateisoline.json?apikey=sample_api_key&" "mode=fastest%3Bcar&quality=1&range=1000%2C2000%2C3000&rangeType=distance&" "singleComponent=false&start=geo%2148.23424%2C8.34234", responses.calls[0].request.url, @@ -363,7 +363,7 @@ def test_full_matrix(self): responses.add( responses.GET, - "https://matrix.route.ls.hereapi.com/routing/7.2/calculatematrix.json", + "https://route.ls.hereapi.com/routing/7.2/calculatematrix.json", status=200, json=ENDPOINTS_RESPONSES[self.name]["matrix"], content_type="application/json", @@ -373,7 +373,7 @@ def test_full_matrix(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - "https://matrix.route.ls.hereapi.com/routing/7.2/calculatematrix.json?apikey=sample_api_key&" + "https://route.ls.hereapi.com/routing/7.2/calculatematrix.json?apikey=sample_api_key&" "destination0=geo%2149.445776%2C8.780916&height=20&length=10&limitedWeight=10&" "mode=fastest%3Bcar&shippedHazardousGoods=gas%2Cflammable&start0=geo%2149.420577%2C8.688641&" "start1=geo%2149.415776%2C8.680916&summaryAttributes=traveltime%2Ccostfactor&trailersCount=3&truckType=truck&"