Skip to content

Commit ece1718

Browse files
nilsnoldenilsnolde
authored andcommitted
Ors v5 (#34)
* update validator with new restrictions * All necessary changes implemented * added self.req as a property
1 parent ec75b92 commit ece1718

24 files changed

+1656
-1086
lines changed

CHANGELOG

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# 2.0.0
2+
3+
- implement all backend changes from moving to openrouteservice v5
4+
- now all parameters are named like their backend equivalents, while keeping the old ors-py parameter names for backwards compatibility, but with deprecation warnings
5+
- validator validates ALL parameters
6+
- added a Client.req property, returning the actual `requests` request
7+
18
### v1.1.8
29

310
- make dependencies more sensible (#32)

openrouteservice/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# the License.
1818
#
1919

20-
__version__ = "1.1.7"
20+
__version__ = "2.0.0"
2121

2222
# Make sure QGIS plugin can import openrouteservice-py
2323

openrouteservice/client.py

Lines changed: 76 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
from datetime import timedelta
2626
import functools
2727
import requests
28+
import json
2829
import random
2930
import time
30-
import collections
31+
import warnings
3132

3233
from openrouteservice import exceptions, __version__
3334

@@ -45,15 +46,15 @@
4546
class Client(object):
4647
"""Performs requests to the ORS API services."""
4748

48-
def __init__(self, key=None,
49-
base_url=_DEFAULT_BASE_URL,
49+
def __init__(self,
50+
key=None,
51+
base_url=_DEFAULT_BASE_URL,
5052
timeout=60,
5153
retry_timeout=60,
5254
requests_kwargs=None,
53-
queries_per_minute=40,
54-
retry_over_query_limit=False):
55+
retry_over_query_limit=True):
5556
"""
56-
:param key: ORS API key. Required.
57+
:param key: ORS API key.
5758
:type key: string
5859
5960
:param base_url: The base URL for the request. Defaults to the ORS API
@@ -68,52 +69,59 @@ def __init__(self, key=None,
6869
seconds.
6970
:type retry_timeout: int
7071
72+
:param requests_kwargs: Extra keyword arguments for the requests
73+
library, which among other things allow for proxy auth to be
74+
implemented. See the official requests docs for more info:
75+
http://docs.python-requests.org/en/latest/api/#main-interface
76+
:type requests_kwargs: dict
77+
7178
:param queries_per_minute: Number of queries per second permitted.
7279
If the rate limit is reached, the client will sleep for the
7380
appropriate amount of time before it runs the current query.
7481
Note, it won't help to initiate another client. This saves you the
7582
trouble of raised exceptions.
7683
:type queries_per_second: int
7784
78-
:param requests_kwargs: Extra keyword arguments for the requests
79-
library, which among other things allow for proxy auth to be
80-
implemented. See the official requests docs for more info:
81-
http://docs.python-requests.org/en/latest/api/#main-interface
82-
:type requests_kwargs: dict
85+
:param retry_over_query_limit: If True, the client will retry when query
86+
limit is reached (HTTP 429). Default False.
8387
"""
8488

85-
self.session = requests.Session()
86-
self.key = key
87-
self.base_url = base_url
88-
89-
self.timeout = timeout
90-
self.retry_over_query_limit = retry_over_query_limit
91-
self.retry_timeout = timedelta(seconds=retry_timeout)
92-
self.requests_kwargs = requests_kwargs or {}
93-
self.requests_kwargs.update({
89+
self._session = requests.Session()
90+
self._key = key
91+
self._base_url = base_url
92+
93+
if self._base_url == _DEFAULT_BASE_URL and key is None:
94+
raise ValueError("No API key was specified. Please visit https://openrouteservice.org/sign-up to create one.")
95+
96+
self._timeout = timeout
97+
self._retry_over_query_limit = retry_over_query_limit
98+
self._retry_timeout = timedelta(seconds=retry_timeout)
99+
self._requests_kwargs = requests_kwargs or {}
100+
self._requests_kwargs.update({
94101
"headers": {"User-Agent": _USER_AGENT,
95-
'Content-type': 'application/json'},
96-
"timeout": self.timeout
102+
'Content-type': 'application/json',
103+
"Authorization": self._key},
104+
"timeout": self._timeout,
97105
})
98106

99-
self.queries_per_minute = queries_per_minute
100-
self.sent_times = collections.deque("", queries_per_minute)
107+
self._req = None
101108

102-
def request(self,
103-
url, params,
104-
first_request_time=None,
105-
retry_counter=0,
106-
requests_kwargs=None,
107-
post_json=None,
108-
dry_run=None):
109+
def request(self,
110+
url,
111+
get_params=None,
112+
first_request_time=None,
113+
retry_counter=0,
114+
requests_kwargs=None,
115+
post_json=None,
116+
dry_run=None):
109117
"""Performs HTTP GET/POST with credentials, returning the body as
110118
JSON.
111119
112120
:param url: URL path for the request. Should begin with a slash.
113121
:type url: string
114122
115-
:param params: HTTP GET parameters.
116-
:type params: dict or list of key/value tuples
123+
:param get_params: HTTP GET parameters.
124+
:type get_params: dict or list of key/value tuples
117125
118126
:param first_request_time: The time of the first request (None if no
119127
retries have occurred).
@@ -135,8 +143,6 @@ def request(self,
135143
136144
:raises ApiError: when the API returns an error.
137145
:raises Timeout: if the request timed out.
138-
:raises TransportError: when something went wrong while trying to
139-
execute a request.
140146
141147
:rtype: dict from JSON response.
142148
"""
@@ -145,7 +151,7 @@ def request(self,
145151
first_request_time = datetime.now()
146152

147153
elapsed = datetime.now() - first_request_time
148-
if elapsed > self.retry_timeout:
154+
if elapsed > self._retry_timeout:
149155
raise exceptions.Timeout()
150156

151157
if retry_counter > 0:
@@ -158,69 +164,64 @@ def request(self,
158164
time.sleep(delay_seconds * (random.random() + 0.5))
159165

160166
authed_url = self._generate_auth_url(url,
161-
params,
167+
get_params,
162168
)
163169

164170
# Default to the client-level self.requests_kwargs, with method-level
165171
# requests_kwargs arg overriding.
166172
requests_kwargs = requests_kwargs or {}
167-
final_requests_kwargs = dict(self.requests_kwargs, **requests_kwargs)
168-
169-
# Check if the time of the nth previous query (where n is
170-
# queries_per_second) is under a second ago - if so, sleep for
171-
# the difference.
172-
if self.sent_times and len(self.sent_times) == self.queries_per_minute:
173-
elapsed_since_earliest = time.time() - self.sent_times[0]
174-
if elapsed_since_earliest < 60:
175-
print("Request limit of {} per minute exceeded. Wait for {} seconds".format(self.queries_per_minute,
176-
60 - elapsed_since_earliest))
177-
time.sleep(60 - elapsed_since_earliest)
173+
final_requests_kwargs = dict(self._requests_kwargs, **requests_kwargs)
178174

179175
# Determine GET/POST.
180-
# post_json is so far only sent from matrix call
181-
requests_method = self.session.get
176+
requests_method = self._session.get
182177

183178
if post_json is not None:
184-
requests_method = self.session.post
179+
requests_method = self._session.post
185180
final_requests_kwargs["json"] = post_json
186181

187182
# Only print URL and parameters for dry_run
188183
if dry_run:
189-
print("url:\n{}\nParameters:\n{}".format(self.base_url+authed_url,
190-
final_requests_kwargs))
184+
print("url:\n{}\nHeaders:\n{}".format(self._base_url + authed_url,
185+
json.dumps(final_requests_kwargs, indent=2)))
191186
return
192187

193188
try:
194-
response = requests_method(self.base_url + authed_url,
189+
response = requests_method(self._base_url + authed_url,
195190
**final_requests_kwargs)
191+
self._req = response.request
192+
196193
except requests.exceptions.Timeout:
197194
raise exceptions.Timeout()
198-
except Exception as e:
199-
raise exceptions.TransportError(e)
200195

201196
if response.status_code in _RETRIABLE_STATUSES:
202197
# Retry request.
203-
print('Server down.\nRetrying for the {}th time.'.format(retry_counter + 1))
198+
warnings.warn('Server down.\nRetrying for the {}th time.'.format(retry_counter + 1),
199+
UserWarning,
200+
stacklevel=1)
204201

205-
return self.request(url, params, first_request_time,
206-
retry_counter + 1, requests_kwargs, post_json)
202+
return self.request(url, get_params, first_request_time,
203+
retry_counter + 1, requests_kwargs, post_json)
207204

208205
try:
209206
result = self._get_body(response)
210-
self.sent_times.append(time.time())
207+
211208
return result
212209
except exceptions._RetriableRequest as e:
213-
if isinstance(e, exceptions._OverQueryLimit) and not self.retry_over_query_limit:
210+
if isinstance(e, exceptions._OverQueryLimit) and not self._retry_over_query_limit:
214211
raise
215212

216-
print('Rate limit exceeded.\nRetrying for the {}th time.'.format(retry_counter + 1))
213+
warnings.warn('Rate limit exceeded.\nRetrying for the {}th time.'.format(retry_counter + 1),
214+
UserWarning,
215+
stacklevel=1)
217216
# Retry request.
218-
return self.request(url, params, first_request_time,
219-
retry_counter + 1, requests_kwargs,
220-
post_json)
221-
except:
222-
raise
217+
return self.request(url, get_params, first_request_time,
218+
retry_counter + 1, requests_kwargs,
219+
post_json)
223220

221+
@property
222+
def req(self):
223+
"""Returns request object. Can be used in case of request failure."""
224+
return self._req
224225

225226
def _get_body(self, response):
226227
body = response.json()
@@ -229,14 +230,17 @@ def _get_body(self, response):
229230

230231
if status_code == 429:
231232
raise exceptions._OverQueryLimit(
232-
str(status_code), body)
233+
status_code,
234+
body
235+
)
233236
if status_code != 200:
234-
raise exceptions.ApiError(status_code,
235-
body)
237+
raise exceptions.ApiError(
238+
status_code,
239+
body
240+
)
236241

237242
return body
238243

239-
240244
def _generate_auth_url(self, path, params):
241245
"""Returns the path and query string portion of the request URL, first
242246
adding any necessary parameters.
@@ -254,17 +258,7 @@ def _generate_auth_url(self, path, params):
254258
if type(params) is dict:
255259
params = sorted(dict(**params).items())
256260

257-
# Only auto-add API key when using ORS. If own instance, API key must
258-
# be explicitly added to params
259-
if self.key:
260-
params.append(("api_key", self.key))
261-
return path + "?" + _urlencode_params(params)
262-
elif self.base_url != _DEFAULT_BASE_URL:
263-
return path + "?" + _urlencode_params(params)
264-
265-
raise ValueError("No API key specified. "
266-
"Visit https://go.openrouteservice.org/dev-dashboard/ "
267-
"to create one.")
261+
return path + "?" + _urlencode_params(params)
268262

269263

270264
from openrouteservice.directions import directions

openrouteservice/convert.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def _has_method(arg, method):
129129

130130

131131
def decode_polyline(polyline, is3d=False):
132-
"""Decodes a Polyline string into a GeoJSON structure.
132+
"""Decodes a Polyline string into a GeoJSON geometry.
133133
134134
:param polyline: An encoded polyline, only the geometry.
135135
:type polyline: string

openrouteservice/deprecation.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (C) 2018 HeiGIT, University of Heidelberg.
3+
#
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6+
# use this file except in compliance with the License. You may obtain a copy of
7+
# the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations under
15+
# the License.
16+
#
17+
18+
import warnings
19+
20+
def warning(old_name, new_name):
21+
warnings.warn('{} will be deprecated in v2.0. Please use {} instead'.format(old_name, new_name),
22+
DeprecationWarning,
23+
stacklevel=2)

0 commit comments

Comments
 (0)