From c8053b61a83ee94410e0fb024a0ed5037a2f68fd Mon Sep 17 00:00:00 2001 From: Matthew Laney Date: Mon, 27 Nov 2017 14:58:42 -0800 Subject: [PATCH 001/166] Implement region parameter in distance matrix to fix issue #209 --- googlemaps/distance_matrix.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) mode change 100644 => 100755 googlemaps/distance_matrix.py diff --git a/googlemaps/distance_matrix.py b/googlemaps/distance_matrix.py old mode 100644 new mode 100755 index 6c1f0626..f6a85e8c --- a/googlemaps/distance_matrix.py +++ b/googlemaps/distance_matrix.py @@ -24,7 +24,7 @@ def distance_matrix(client, origins, destinations, mode=None, language=None, avoid=None, units=None, departure_time=None, arrival_time=None, transit_mode=None, - transit_routing_preference=None, traffic_model=None): + transit_routing_preference=None, traffic_model=None, region=None): """ Gets travel distance and time for a matrix of origins and destinations. :param origins: One or more locations and/or latitude/longitude values, @@ -81,6 +81,11 @@ def distance_matrix(client, origins, destinations, the travel mode is driving, and where the request includes a departure_time. + :param region: Specifies the prefered region the geocoder should search + first, but it will not restrict the results to only this region. Valid + values are a ccTLD code. + :type region: string + :rtype: matrix of distances. Results are returned in rows, each row containing one origin paired with each destination. """ @@ -127,4 +132,7 @@ def distance_matrix(client, origins, destinations, if traffic_model: params["traffic_model"] = traffic_model + if region: + params["region"] = region + return client._request("/maps/api/distancematrix/json", params) From 8671ecd2bb301f8f82d5574f027204ceb6dfe9d5 Mon Sep 17 00:00:00 2001 From: Kerrick Staley Date: Thu, 4 Jan 2018 16:36:51 -0800 Subject: [PATCH 002/166] Add option to not retry when over query limit --- googlemaps/client.py | 16 +++++++++++++--- googlemaps/exceptions.py | 8 ++++++++ googlemaps/geolocation.py | 2 +- googlemaps/roads.py | 2 +- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 72b59ed0..bcef125e 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -51,7 +51,8 @@ class Client(object): def __init__(self, key=None, client_id=None, client_secret=None, timeout=None, connect_timeout=None, read_timeout=None, retry_timeout=60, requests_kwargs=None, - queries_per_second=10, channel=None): + queries_per_second=10, channel=None, + retry_over_query_limit=True): """ :param key: Maps API key. Required, unless "client_id" and "client_secret" are set. @@ -93,6 +94,11 @@ def __init__(self, key=None, client_id=None, client_secret=None, appropriate amount of time before it runs the current query. :type queries_per_second: int + :param retry_over_query_limit: If True, requests that result in a + response indicating the query rate limit was exceeded will be + retried. Defaults to True. + :type retry_over_query_limit: bool + :raises ValueError: when either credentials are missing, incomplete or invalid. :raises NotImplementedError: if connect_timeout and read_timeout are @@ -150,6 +156,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, }) self.queries_per_second = queries_per_second + self.retry_over_query_limit = retry_over_query_limit self.sent_times = collections.deque("", queries_per_second) def _request(self, url, params, first_request_time=None, retry_counter=0, @@ -253,7 +260,10 @@ def _request(self, url, params, first_request_time=None, retry_counter=0, result = self._get_body(response) self.sent_times.append(time.time()) return result - except googlemaps.exceptions._RetriableRequest: + except googlemaps.exceptions._RetriableRequest as e: + if isinstance(e, googlemaps.exceptions._OverQueryLimit) and not self.retry_over_query_limit: + raise + # Retry request. return self._request(url, params, first_request_time, retry_counter + 1, base_url, accepts_clientid, @@ -273,7 +283,7 @@ def _get_body(self, response): return body if api_status == "OVER_QUERY_LIMIT": - raise googlemaps.exceptions._RetriableRequest() + raise googlemaps.exceptions._OverQueryLimit() if "error_message" in body: raise googlemaps.exceptions.ApiError(api_status, diff --git a/googlemaps/exceptions.py b/googlemaps/exceptions.py index 4197a650..8aa73e77 100644 --- a/googlemaps/exceptions.py +++ b/googlemaps/exceptions.py @@ -58,3 +58,11 @@ class Timeout(Exception): class _RetriableRequest(Exception): """Signifies that the request can be retried.""" pass + +class _OverQueryLimit(_RetriableRequest): + """Signifies that the request failed because the client exceeded its query rate limit. + + Normally we treat this as a retriable condition, but we allow the calling code to specify that these requests should + not be retried. + """ + pass diff --git a/googlemaps/geolocation.py b/googlemaps/geolocation.py index 9cbfb6b5..e4b57082 100644 --- a/googlemaps/geolocation.py +++ b/googlemaps/geolocation.py @@ -31,7 +31,7 @@ def _geolocation_extract(response): if response.status_code in (200, 404): return body elif response.status_code == 403: - raise exceptions._RetriableRequest() + raise exceptions._OverQueryLimit() else: try: error = body["error"]["errors"][0]["reason"] diff --git a/googlemaps/roads.py b/googlemaps/roads.py index 5bf2276d..138dc391 100644 --- a/googlemaps/roads.py +++ b/googlemaps/roads.py @@ -133,7 +133,7 @@ def _roads_extract(resp): status = error["status"] if status == "RESOURCE_EXHAUSTED": - raise googlemaps.exceptions._RetriableRequest() + raise googlemaps.exceptions._OverQueryLimit() if "message" in error: raise googlemaps.exceptions.ApiError(status, error["message"]) From 20d1b7b4dba2942b09750b534b2652d6d52cb10c Mon Sep 17 00:00:00 2001 From: Kerrick Staley Date: Thu, 4 Jan 2018 17:00:16 -0800 Subject: [PATCH 003/166] Make it subclass ApiError --- googlemaps/client.py | 10 ++++------ googlemaps/exceptions.py | 2 +- googlemaps/geolocation.py | 14 ++++++++------ googlemaps/roads.py | 8 +++----- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index bcef125e..a1cac991 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -283,13 +283,11 @@ def _get_body(self, response): return body if api_status == "OVER_QUERY_LIMIT": - raise googlemaps.exceptions._OverQueryLimit() + raise googlemaps.exceptions._OverQueryLimit( + api_status, body.get("error_message")) - if "error_message" in body: - raise googlemaps.exceptions.ApiError(api_status, - body["error_message"]) - else: - raise googlemaps.exceptions.ApiError(api_status) + raise googlemaps.exceptions.ApiError(api_status, + body.get("error_message")) def _generate_auth_url(self, path, params, accepts_clientid): """Returns the path and query string portion of the request URL, first diff --git a/googlemaps/exceptions.py b/googlemaps/exceptions.py index 8aa73e77..679b26c3 100644 --- a/googlemaps/exceptions.py +++ b/googlemaps/exceptions.py @@ -59,7 +59,7 @@ class _RetriableRequest(Exception): """Signifies that the request can be retried.""" pass -class _OverQueryLimit(_RetriableRequest): +class _OverQueryLimit(ApiError, _RetriableRequest): """Signifies that the request failed because the client exceeded its query rate limit. Normally we treat this as a retriable condition, but we allow the calling code to specify that these requests should diff --git a/googlemaps/geolocation.py b/googlemaps/geolocation.py index e4b57082..c8db15ec 100644 --- a/googlemaps/geolocation.py +++ b/googlemaps/geolocation.py @@ -30,13 +30,15 @@ def _geolocation_extract(response): body = response.json() if response.status_code in (200, 404): return body - elif response.status_code == 403: - raise exceptions._OverQueryLimit() + + try: + error = body["error"]["errors"][0]["reason"] + except KeyError: + error = None + + if response.status_code == 403: + raise exceptions._OverQueryLimit(response.status_code, error) else: - try: - error = body["error"]["errors"][0]["reason"] - except KeyError: - error = None raise exceptions.ApiError(response.status_code, error) diff --git a/googlemaps/roads.py b/googlemaps/roads.py index 138dc391..edfb8ecb 100644 --- a/googlemaps/roads.py +++ b/googlemaps/roads.py @@ -133,12 +133,10 @@ def _roads_extract(resp): status = error["status"] if status == "RESOURCE_EXHAUSTED": - raise googlemaps.exceptions._OverQueryLimit() + raise googlemaps.exceptions._OverQueryLimit(status, + error.get("message")) - if "message" in error: - raise googlemaps.exceptions.ApiError(status, error["message"]) - else: - raise googlemaps.exceptions.ApiError(status) + raise googlemaps.exceptions.ApiError(status, error.get("message")) if resp.status_code != 200: raise googlemaps.exceptions.HTTPError(resp.status_code) From 91e6fb99d2902946b706327336948c6d008e9cc4 Mon Sep 17 00:00:00 2001 From: Kerrick Staley Date: Thu, 4 Jan 2018 17:02:12 -0800 Subject: [PATCH 004/166] Add unit test --- test/test_client.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_client.py b/test/test_client.py index f2d424a4..79019d36 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -288,3 +288,19 @@ def test_requests_version(self): requests.__version__ = '2.4.0' googlemaps.Client(**client_args_timeout) googlemaps.Client(**client_args) + + @responses.activate + def test_no_retry_over_query_limit(self): + responses.add(responses.GET, + "https://maps.googleapis.com/foo", + body='{"status":"OVER_QUERY_LIMIT"}', + status=200, + content_type="application/json") + + client = googlemaps.Client(key="AIzaasdf", + retry_over_query_limit=False) + + with self.assertRaises(googlemaps.exceptions.ApiError): + client._request("/foo", {}) + + self.assertEqual(1, len(responses.calls)) From ed23c0451e1140ea0f6312650ce3d56cca8ed772 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Tue, 9 Jan 2018 11:01:10 +1100 Subject: [PATCH 005/166] Increase default QPS from 10 to 50 Change-Id: I14979c04d1c1c314e007a9eb5858ee1db52a74e7 --- googlemaps/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 72b59ed0..d6820ab5 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -51,7 +51,7 @@ class Client(object): def __init__(self, key=None, client_id=None, client_secret=None, timeout=None, connect_timeout=None, read_timeout=None, retry_timeout=60, requests_kwargs=None, - queries_per_second=10, channel=None): + queries_per_second=50, channel=None): """ :param key: Maps API key. Required, unless "client_id" and "client_secret" are set. From 6a7be65017073f2593ae0679f3eabcdb36b10b03 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 12 Feb 2018 12:24:24 +1100 Subject: [PATCH 006/166] Validate country components in places autocomplete. Closes #216. Change-Id: Ie88f23b7b0a22d1ed8e07b74d027f16252a8e80d --- googlemaps/places.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 3eb5cbea..8cdacf27 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -332,9 +332,9 @@ def places_autocomplete(client, input_text, offset=None, location=None, https://developers.google.com/places/web-service/autocomplete#place_types :type type: string - :param components: A component filter for which you wish to obtain a geocode, - for example: - ``{'administrative_area': 'TX','country': 'US'}`` + :param components: A component filter for which you wish to obtain a geocode. + Currently, you can use components to filter by up to 5 countries for + example: ``{'country': ['US', 'AU']}`` :type components: dict :param strict_bounds: Returns only those places that are strictly within @@ -401,6 +401,8 @@ def _autocomplete(client, url_part, input_text, offset=None, location=None, if types: params["types"] = types if components: + if len(components) != 1 or components.keys()[0] != "country": + raise ValueError("Only country components are supported") params["components"] = convert.components(components) if strict_bounds: params["strictbounds"] = "true" From a91c1753ef6c81755c05b9e8d778d0ccc0488e74 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 12 Feb 2018 12:32:31 +1100 Subject: [PATCH 007/166] Python3 fix Change-Id: I687ddf1045a4e68445172212d83c016bc31443df --- googlemaps/places.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 8cdacf27..a5cf8dac 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -401,7 +401,7 @@ def _autocomplete(client, url_part, input_text, offset=None, location=None, if types: params["types"] = types if components: - if len(components) != 1 or components.keys()[0] != "country": + if len(components) != 1 or list(components.keys())[0] != "country": raise ValueError("Only country components are supported") params["components"] = convert.components(components) if strict_bounds: From 881bfdf0bc17176c82c9631b381836022b277744 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 12 Feb 2018 12:41:29 +1100 Subject: [PATCH 008/166] Allow string version of bounds. Closes #188. Change-Id: I648d10cd69be1f7069b5f0391a0ca68d337f467d --- googlemaps/convert.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/googlemaps/convert.py b/googlemaps/convert.py index 6206cfa8..a20e3a32 100644 --- a/googlemaps/convert.py +++ b/googlemaps/convert.py @@ -265,7 +265,9 @@ def bounds(arg): :type arg: dict """ - if isinstance(arg, dict): + if is_string(arg) and arg.count("|") == 1 and arg.count(",") == 2: + return arg + elif isinstance(arg, dict): if "southwest" in arg and "northeast" in arg: return "%s|%s" % (latlng(arg["southwest"]), latlng(arg["northeast"])) From c1c6affdc15a6be7ea264d7aca06a07fd64d74be Mon Sep 17 00:00:00 2001 From: Stvan Date: Mon, 26 Mar 2018 01:59:25 +0200 Subject: [PATCH 009/166] Places service region parameter appending (#228) --- googlemaps/places.py | 16 +++++++++++++--- test/test_places.py | 5 +++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index a5cf8dac..6b02d2ec 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -21,7 +21,7 @@ def places(client, query, location=None, radius=None, language=None, - min_price=None, max_price=None, open_now=False, type=None, + min_price=None, max_price=None, open_now=False, type=None, region=None, page_token=None): """ Places search. @@ -57,6 +57,10 @@ def places(client, query, location=None, radius=None, language=None, The full list of supported types is available here: https://developers.google.com/places/supported_types :type type: string + + :param region: The region code, optional parameter. + See more @ https://developers.google.com/places/web-service/search + :type region: string :param page_token: Token from a previous search that when provided will returns the next page of results for the same search. @@ -69,7 +73,7 @@ def places(client, query, location=None, radius=None, language=None, """ return _places(client, "text", query=query, location=location, radius=radius, language=language, min_price=min_price, - max_price=max_price, open_now=open_now, type=type, + max_price=max_price, open_now=open_now, type=type, region=region, page_token=page_token) @@ -85,6 +89,10 @@ def places_nearby(client, location, radius=None, keyword=None, language=None, :param radius: Distance in meters within which to bias results. :type radius: int + + :param region: The region code, optional parameter. + See more @ https://developers.google.com/places/web-service/search + :type region: string :param keyword: A term to be matched against all content that Google has indexed for this place. @@ -202,7 +210,7 @@ def places_radar(client, location, radius, keyword=None, min_price=None, def _places(client, url_part, query=None, location=None, radius=None, keyword=None, language=None, min_price=0, max_price=4, name=None, - open_now=False, rank_by=None, type=None, page_token=None): + open_now=False, rank_by=None, type=None, region=None, page_token=None): """ Internal handler for ``places``, ``places_nearby``, and ``places_radar``. See each method's docs for arg details. @@ -228,6 +236,8 @@ def _places(client, url_part, query=None, location=None, radius=None, params["rankby"] = rank_by if type: params["type"] = type + if region: + params["region"] = region if page_token: params["pagetoken"] = page_token diff --git a/test/test_places.py b/test/test_places.py index 71e3a546..f9e033c8 100644 --- a/test/test_places.py +++ b/test/test_places.py @@ -34,6 +34,7 @@ def setUp(self): self.location = (-33.86746, 151.207090) self.type = 'liquor_store' self.language = 'en-AU' + self.region = 'AU' self.radius = 100 @responses.activate @@ -44,14 +45,14 @@ def test_places_text_search(self): status=200, content_type='application/json') self.client.places('restaurant', location=self.location, - radius=self.radius, language=self.language, + radius=self.radius, region=self.region, language=self.language, min_price=1, max_price=4, open_now=True, type=self.type) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&location=-33.86746%%2C151.20709&' 'maxprice=4&minprice=1&opennow=true&query=restaurant&' - 'radius=100&type=liquor_store&key=%s' + 'radius=100®ion=AU&type=liquor_store&key=%s' % (url, self.key), responses.calls[0].request.url) @responses.activate From 86cfa452a07a039f942df5e51a410756797b2ac3 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 16 Apr 2018 15:49:37 +1000 Subject: [PATCH 010/166] location param for places nearby is not required if pagetoken provided Change-Id: I70b63549ba8850694425a743f747952db6964d2d --- googlemaps/places.py | 16 +++++++++------- test/test_convert.py | 3 +-- test/test_places.py | 6 ++++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index a5cf8dac..b5a59d6d 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -73,9 +73,9 @@ def places(client, query, location=None, radius=None, language=None, page_token=page_token) -def places_nearby(client, location, radius=None, keyword=None, language=None, - min_price=None, max_price=None, name=None, open_now=False, - rank_by=None, type=None, page_token=None): +def places_nearby(client, location=None, radius=None, keyword=None, + language=None, min_price=None, max_price=None, name=None, + open_now=False, rank_by=None, type=None, page_token=None): """ Performs nearby search for places. @@ -130,13 +130,15 @@ def places_nearby(client, location, radius=None, keyword=None, language=None, next_page_token: token for retrieving the next page of results """ + if not location and not page_token: + raise ValueError("either a location or page_token arg is required") if rank_by == "distance": if not (keyword or name or type): - raise ValueError("either a keyword, name, or type arg is required " - "when rank_by is set to distance") + raise ValueError("either a keyword, name, or type arg is required " + "when rank_by is set to distance") elif radius is not None: - raise ValueError("radius cannot be specified when rank_by is set to " - "distance") + raise ValueError("radius cannot be specified when rank_by is set to " + "distance") return _places(client, "nearby", location=location, radius=radius, keyword=keyword, language=language, min_price=min_price, diff --git a/test/test_convert.py b/test/test_convert.py index 090a95fc..ed08c84b 100644 --- a/test/test_convert.py +++ b/test/test_convert.py @@ -107,8 +107,7 @@ def test_bounds(self): ne = {"lat": 1, "lng": 2} sw = (3, 4) b = {"northeast": ne, "southwest": sw} - self.assertEqual("3,4|1,2", - convert.bounds(b)) + self.assertEqual("3,4|1,2", convert.bounds(b)) with self.assertRaises(TypeError): convert.bounds("test") diff --git a/test/test_places.py b/test/test_places.py index 71e3a546..49418d42 100644 --- a/test/test_places.py +++ b/test/test_places.py @@ -61,7 +61,7 @@ def test_places_nearby_search(self): body='{"status": "OK", "results": [], "html_attributions": []}', status=200, content_type='application/json') - self.client.places_nearby(self.location, keyword='foo', + self.client.places_nearby(location=self.location, keyword='foo', language=self.language, min_price=1, max_price=4, name='bar', open_now=True, rank_by='distance', type=self.type) @@ -72,11 +72,13 @@ def test_places_nearby_search(self): 'type=liquor_store&key=%s' % (url, self.key), responses.calls[0].request.url) + with self.assertRaises(ValueError): + self.client.places_nearby(radius=self.radius) with self.assertRaises(ValueError): self.client.places_nearby(self.location, rank_by="distance") with self.assertRaises(ValueError): - self.client.places_nearby(self.location, rank_by="distance", + self.client.places_nearby(location=self.location, rank_by="distance", keyword='foo', radius=self.radius) @responses.activate From 42e7e99d46b3b8aec7d5994e674f366d36fcc5e1 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Mon, 21 May 2018 03:08:17 +0200 Subject: [PATCH 011/166] Fix type annotation in places_autocomplete() docs (#231) This was broken by the rename of the parameter from `type` to `types` in commit 85b81274a5db1bf877aa391bcccfd1456ccce2f0. --- googlemaps/places.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 0b035bdb..4c95dcbd 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -342,7 +342,7 @@ def places_autocomplete(client, input_text, offset=None, location=None, :param types: Restricts the results to places matching the specified type. The full list of supported types is available here: https://developers.google.com/places/web-service/autocomplete#place_types - :type type: string + :type types: string :param components: A component filter for which you wish to obtain a geocode. Currently, you can use components to filter by up to 5 countries for From bca4a9bfd187fbc2324d328633ed682f88bd56c0 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 21 May 2018 11:13:38 +1000 Subject: [PATCH 012/166] Add manifest Change-Id: Ib37b4d808c60d103a9852943b7e1ba58171d9426 --- MANIFEST.in | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..4e833047 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE README.md +global-exclude __pycache__ +global-exclude *.py[co] From 82f71d81e5da3511a539879336401e570974bd11 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Thu, 31 May 2018 11:19:05 +1000 Subject: [PATCH 013/166] Move test module into package. Closes #238. Change-Id: Ic32abf0f4c734c2ec8f582d3318b271c7c4df653 --- {test => googlemaps/test}/__init__.py | 0 {test => googlemaps/test}/test_client.py | 10 ++++++---- {test => googlemaps/test}/test_convert.py | 0 {test => googlemaps/test}/test_directions.py | 6 ++++-- {test => googlemaps/test}/test_distance_matrix.py | 6 ++++-- {test => googlemaps/test}/test_elevation.py | 4 +++- {test => googlemaps/test}/test_geocoding.py | 4 +++- {test => googlemaps/test}/test_geolocation.py | 3 ++- {test => googlemaps/test}/test_places.py | 2 +- {test => googlemaps/test}/test_roads.py | 5 +++-- {test => googlemaps/test}/test_timezone.py | 5 +++-- setup.py | 1 + tox.ini | 2 +- 13 files changed, 31 insertions(+), 17 deletions(-) rename {test => googlemaps/test}/__init__.py (100%) rename {test => googlemaps/test}/test_client.py (99%) rename {test => googlemaps/test}/test_convert.py (100%) rename {test => googlemaps/test}/test_directions.py (99%) rename {test => googlemaps/test}/test_distance_matrix.py (99%) rename {test => googlemaps/test}/test_elevation.py (99%) rename {test => googlemaps/test}/test_geocoding.py (99%) rename {test => googlemaps/test}/test_geolocation.py (97%) rename {test => googlemaps/test}/test_places.py (99%) rename {test => googlemaps/test}/test_roads.py (99%) rename {test => googlemaps/test}/test_timezone.py (98%) diff --git a/test/__init__.py b/googlemaps/test/__init__.py similarity index 100% rename from test/__init__.py rename to googlemaps/test/__init__.py diff --git a/test/test_client.py b/googlemaps/test/test_client.py similarity index 99% rename from test/test_client.py rename to googlemaps/test/test_client.py index 79019d36..90d83b3e 100644 --- a/test/test_client.py +++ b/googlemaps/test/test_client.py @@ -18,14 +18,16 @@ """Tests for client module.""" -import responses import time -import googlemaps -from googlemaps import client as _client -import test as _test +import responses import requests +import googlemaps +import googlemaps.client as _client +import googlemaps.test as _test + + class ClientTest(_test.TestCase): def test_no_api_key(self): diff --git a/test/test_convert.py b/googlemaps/test/test_convert.py similarity index 100% rename from test/test_convert.py rename to googlemaps/test/test_convert.py diff --git a/test/test_directions.py b/googlemaps/test/test_directions.py similarity index 99% rename from test/test_directions.py rename to googlemaps/test/test_directions.py index d7d8eb42..de21dc1e 100644 --- a/test/test_directions.py +++ b/googlemaps/test/test_directions.py @@ -19,11 +19,13 @@ from datetime import datetime from datetime import timedelta -import responses import time +import responses + import googlemaps -import test as _test +import googlemaps.test as _test + class DirectionsTest(_test.TestCase): diff --git a/test/test_distance_matrix.py b/googlemaps/test/test_distance_matrix.py similarity index 99% rename from test/test_distance_matrix.py rename to googlemaps/test/test_distance_matrix.py index 966abef8..5e929b24 100644 --- a/test/test_distance_matrix.py +++ b/googlemaps/test/test_distance_matrix.py @@ -20,9 +20,11 @@ from datetime import datetime import time -import googlemaps import responses -import test as _test + +import googlemaps +import googlemaps.test as _test + class DistanceMatrixTest(_test.TestCase): diff --git a/test/test_elevation.py b/googlemaps/test/test_elevation.py similarity index 99% rename from test/test_elevation.py rename to googlemaps/test/test_elevation.py index 6fc990ba..41146939 100644 --- a/test/test_elevation.py +++ b/googlemaps/test/test_elevation.py @@ -18,10 +18,12 @@ """Tests for the elevation module.""" import datetime + import responses import googlemaps -import test as _test +import googlemaps.test as _test + class ElevationTest(_test.TestCase): diff --git a/test/test_geocoding.py b/googlemaps/test/test_geocoding.py similarity index 99% rename from test/test_geocoding.py rename to googlemaps/test/test_geocoding.py index e074c034..511e6d8f 100644 --- a/test/test_geocoding.py +++ b/googlemaps/test/test_geocoding.py @@ -19,10 +19,12 @@ """Tests for the geocoding module.""" import datetime + import responses -import test as _test import googlemaps +import googlemaps.test as _test + class GeocodingTest(_test.TestCase): diff --git a/test/test_geolocation.py b/googlemaps/test/test_geolocation.py similarity index 97% rename from test/test_geolocation.py rename to googlemaps/test/test_geolocation.py index 8b6e1918..64b6f36a 100644 --- a/test/test_geolocation.py +++ b/googlemaps/test/test_geolocation.py @@ -20,8 +20,9 @@ import responses -import test as _test import googlemaps +import googlemaps.test as _test + class GeolocationTest(_test.TestCase): diff --git a/test/test_places.py b/googlemaps/test/test_places.py similarity index 99% rename from test/test_places.py rename to googlemaps/test/test_places.py index 8ae5f221..6bb4aa1e 100644 --- a/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -22,8 +22,8 @@ import responses -import test as _test import googlemaps +import googlemaps.test as _test class PlacesTest(_test.TestCase): diff --git a/test/test_roads.py b/googlemaps/test/test_roads.py similarity index 99% rename from test/test_roads.py rename to googlemaps/test/test_roads.py index 2d0b9c65..6e11b604 100644 --- a/test/test_roads.py +++ b/googlemaps/test/test_roads.py @@ -18,10 +18,11 @@ """Tests for the roads module.""" +import responses + import googlemaps +import googlemaps.test as _test -import responses -import test as _test class RoadsTest(_test.TestCase): diff --git a/test/test_timezone.py b/googlemaps/test/test_timezone.py similarity index 98% rename from test/test_timezone.py rename to googlemaps/test/test_timezone.py index 11127478..23daeca3 100644 --- a/test/test_timezone.py +++ b/googlemaps/test/test_timezone.py @@ -18,12 +18,13 @@ """Tests for the timezone module.""" +import datetime + import responses import mock -import datetime import googlemaps -import test as _test +import googlemaps.test as _test class TimezoneTest(_test.TestCase): diff --git a/setup.py b/setup.py index ec7b2e38..6ab70806 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ platforms='Posix; MacOS X; Windows', setup_requires=requirements, install_requires=requirements, + test_suite='googlemaps.test', classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', diff --git a/tox.ini b/tox.ini index 67b23a7f..77ca5b7e 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ envlist = [testenv] commands = - nosetests + nosetests googlemaps/test deps = -rtest_requirements.txt [testenv:docs] From bf153940b56a4cfb34a4fcd9c9b40767c4715c5c Mon Sep 17 00:00:00 2001 From: Nicola Cammillini Date: Wed, 20 Jun 2018 01:26:44 +0200 Subject: [PATCH 014/166] Broken test link (#242) After moving the directory containing tests inside package, link in README is broken. Link now points one directory down, to googlemaps/test. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80d9f468..f71721a4 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ directions_result = gmaps.directions("Sydney Town Hall", departure_time=now) ``` -For more usage examples, check out [the tests](test/). +For more usage examples, check out [the tests](googlemaps/test/). ## Features From ce498cbba072d7acb3b480d73d96ebea02627715 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 25 Jun 2018 16:11:35 +1000 Subject: [PATCH 015/166] Add findplacebytext method and fields params (#234) * Add findplacebytext method and fields params Change-Id: I76c3393a57b0286336cc5c871e24a9f5297ce21d * Add autocomplete session token Change-Id: I70dd571a4442b0c7e4959aaa27094932d7c9ea8d * Add locationbias field to findplacefromtext Change-Id: I8ce8f08acdb7437ee75e7c44a28cb477e84d01ec * Remove alt_id Change-Id: I192d5d6e74977734e406acb0590b426ee227f52e * find_places -> find_place Change-Id: Ibcd2fb904c08f9d3eee8926dac3b4906f4fe241d --- googlemaps/client.py | 2 + googlemaps/places.py | 117 ++++++++++++++++++++++++++++++--- googlemaps/test/test_places.py | 47 +++++++++++-- 3 files changed, 153 insertions(+), 13 deletions(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 11d15071..89ad179c 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -339,6 +339,7 @@ def _generate_auth_url(self, path, params, accepts_clientid): from googlemaps.roads import nearest_roads from googlemaps.roads import speed_limits from googlemaps.roads import snapped_speed_limits +from googlemaps.places import find_place from googlemaps.places import places from googlemaps.places import places_nearby from googlemaps.places import places_radar @@ -382,6 +383,7 @@ def wrapper(*args, **kwargs): Client.nearest_roads = make_api_method(nearest_roads) Client.speed_limits = make_api_method(speed_limits) Client.snapped_speed_limits = make_api_method(snapped_speed_limits) +Client.find_place = make_api_method(find_place) Client.places = make_api_method(places) Client.places_nearby = make_api_method(places_nearby) Client.places_radar = make_api_method(places_radar) diff --git a/googlemaps/places.py b/googlemaps/places.py index 4c95dcbd..70e17c10 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -17,9 +17,87 @@ """Performs requests to the Google Places API.""" +from uuid import uuid4 as places_autocomplete_session_token from googlemaps import convert +PLACES_FIND_FIELDS = set([ + "formatted_address", "geometry", "icon", "id", "name", + "permanently_closed", "photos", "place_id", "scope", "types", + "vicinity", "opening_hours", "price_level", "rating", +]) + +PLACES_DETAIL_FIELDS = set([ + "address_component", "adr_address", "alt_id", "formatted_address", + "geometry", "icon", "id", "name", "permanently_closed", "photo", + "place_id", "scope", "type", "url", "utc_offset", "vicinity", + "formatted_phone_number", "international_phone_number", "opening_hours", + "website", "price_level", "rating", "review", +]) + + +def find_place(client, input, input_type, fields=None, location_bias=None, + language=None): + """ + A Find Place request takes a text input, and returns a place. + The text input can be any kind of Places data, for example, + a name, address, or phone number. + + :param input: The text input specifying which place to search for (for + example, a name, address, or phone number). + :type input: string + + :param input_type: The type of input. This can be one of either 'textquery' + or 'phonenumber'. + :type input_type: string + + :param fields: The fields specifying the types of place data to return, + separated by a comma. For full details see: + https://developers.google.com/places/web-service/search#FindPlaceRequests + :type input: list + + :param location_bias: Prefer results in a specified area, by specifying + either a radius plus lat/lng, or two lat/lng pairs + representing the points of a rectangle. See: + https://developers.google.com/places/web-service/search#FindPlaceRequests + :type location_bias: string + + :param language: The language in which to return results. + :type langauge: string + + :rtype: result dict with the following keys: + status: status code + candidates: list of places + """ + params = {"input": input, "inputtype": input_type} + + if input_type != "textquery" and input_type != "phonenumber": + raise ValueError("Valid values for the `input_type` param for " + "`find_place` are 'textquery' or 'phonenumber', " + "the given value is invalid: '%s'" % input_type) + + if fields: + invalid_fields = set(fields) - PLACES_FIND_FIELDS + if invalid_fields: + raise ValueError("Valid values for the `fields` param for " + "`find_place` are '%s', these given field(s) " + "are invalid: '%s'" % ( + "', '".join(PLACES_FIND_FIELDS), + "', '".join(invalid_fields))) + params["fields"] = convert.join_list(",", fields) + + if location_bias: + valid = ["ipbias", "point", "circle", "rectangle"] + if location_bias.split(":")[0] not in valid: + raise ValueError("location_bias should be prefixed with one of: %s" + % valid) + params["locationbias"] = location_bias + if language: + params["language"] = language + + return client._request("/maps/api/place/findplacefromtext/json", params) + + def places(client, query, location=None, radius=None, language=None, min_price=None, max_price=None, open_now=False, type=None, region=None, page_token=None): @@ -57,7 +135,7 @@ def places(client, query, location=None, radius=None, language=None, The full list of supported types is available here: https://developers.google.com/places/supported_types :type type: string - + :param region: The region code, optional parameter. See more @ https://developers.google.com/places/web-service/search :type region: string @@ -89,7 +167,7 @@ def places_nearby(client, location=None, radius=None, keyword=None, :param radius: Distance in meters within which to bias results. :type radius: int - + :param region: The region code, optional parameter. See more @ https://developers.google.com/places/web-service/search :type region: string @@ -247,7 +325,7 @@ def _places(client, url_part, query=None, location=None, radius=None, return client._request(url, params) -def place(client, place_id, language=None): +def place(client, place_id, fields=None, language=None): """ Comprehensive details for an individual place. @@ -255,6 +333,11 @@ def place(client, place_id, language=None): returned from a Places search. :type place_id: string + :param fields: The fields specifying the types of place data to return, + separated by a comma. For full details see: + https://cloud.google.com/maps-platform/user-guide/product-changes/#places + :type input: list + :param language: The language in which to return results. :type langauge: string @@ -263,8 +346,20 @@ def place(client, place_id, language=None): html_attributions: set of attributions which must be displayed """ params = {"placeid": place_id} + + if fields: + invalid_fields = set(fields) - PLACES_DETAIL_FIELDS + if invalid_fields: + raise ValueError("Valid values for the `fields` param for " + "`place` are '%s', these given field(s) " + "are invalid: '%s'" % ( + "', '".join(PLACES_DETAIL_FIELDS), + "', '".join(invalid_fields))) + params["fields"] = convert.join_list(",", fields) + if language: params["language"] = language + return client._request("/maps/api/place/details/json", params) @@ -313,8 +408,8 @@ def places_photo(client, photo_reference, max_width=None, max_height=None): return response.iter_content() -def places_autocomplete(client, input_text, offset=None, location=None, - radius=None, language=None, types=None, +def places_autocomplete(client, input_text, session_token, offset=None, + location=None, radius=None, language=None, types=None, components=None, strict_bounds=False): """ Returns Place predictions given a textual search string and optional @@ -323,6 +418,10 @@ def places_autocomplete(client, input_text, offset=None, location=None, :param input_text: The text string on which to search. :type input_text: string + :param session_token: A random string which identifies an autocomplete + session for billing purposes. + :type session_token: string + :param offset: The position, in the input term, of the last character that the service uses to match predictions. For example, if the input is 'Google' and the offset is 3, the @@ -392,9 +491,9 @@ def places_autocomplete_query(client, input_text, offset=None, location=None, location=location, radius=radius, language=language) -def _autocomplete(client, url_part, input_text, offset=None, location=None, - radius=None, language=None, types=None, components=None, - strict_bounds=False): +def _autocomplete(client, url_part, input_text, session_token=None, + offset=None, location=None, radius=None, language=None, + types=None, components=None, strict_bounds=False): """ Internal handler for ``autocomplete`` and ``autocomplete_query``. See each method's docs for arg details. @@ -402,6 +501,8 @@ def _autocomplete(client, url_part, input_text, offset=None, location=None, params = {"input": input_text} + if session_token: + params["sessiontoken"] = session_token if offset: params["offset"] = offset if location: diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index 6bb4aa1e..37d1e76f 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -23,6 +23,7 @@ import responses import googlemaps +from googlemaps.places import places_autocomplete_session_token import googlemaps.test as _test @@ -37,6 +38,33 @@ def setUp(self): self.region = 'AU' self.radius = 100 + @responses.activate + def test_places_find(self): + url = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json' + responses.add(responses.GET, url, + body='{"status": "OK", "candidates": []}', + status=200, content_type='application/json') + + self.client.find_place('restaurant', 'textquery', + fields=['geometry', 'id'], + location_bias='point:90,90', + language=self.language) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual('%s?language=en-AU&inputtype=textquery&' + 'locationbias=point:90,90&input=restaurant' + '&fields=geometry,id&key=%s' + % (url, self.key), responses.calls[0].request.url) + + with self.assertRaises(ValueError): + self.client.find_place('restaurant', 'invalid') + with self.assertRaises(ValueError): + self.client.find_place('restaurant', 'textquery', + fields=['geometry', 'invalid']) + with self.assertRaises(ValueError): + self.client.find_place('restaurant', 'textquery', + location_bias='invalid') + @responses.activate def test_places_text_search(self): url = 'https://maps.googleapis.com/maps/api/place/textsearch/json' @@ -109,12 +137,18 @@ def test_place_detail(self): body='{"status": "OK", "result": {}, "html_attributions": []}', status=200, content_type='application/json') - self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', language=self.language) + self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', + fields=['geometry', 'id'], language=self.language) self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4&key=%s' + self.assertURLEqual('%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4' + '&key=%s&fields=geometry,id' % (url, self.key), responses.calls[0].request.url) + with self.assertRaises(ValueError): + self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', + fields=['geometry', 'invalid']) + @responses.activate def test_photo(self): url = 'https://maps.googleapis.com/maps/api/place/photo' @@ -135,7 +169,9 @@ def test_autocomplete(self): body='{"status": "OK", "predictions": []}', status=200, content_type='application/json') - self.client.places_autocomplete('Google', offset=3, + session_token = places_autocomplete_session_token() + + self.client.places_autocomplete('Google', session_token, offset=3, location=self.location, radius=self.radius, language=self.language, @@ -146,8 +182,9 @@ def test_autocomplete(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?components=country%%3Aau&input=Google&language=en-AU&' 'location=-33.86746%%2C151.20709&offset=3&radius=100&' - 'strictbounds=true&types=geocode&key=%s' % - (url, self.key), responses.calls[0].request.url) + 'strictbounds=true&types=geocode&key=%s&sessiontoken=' % + (url, self.key), responses.calls[0].request.url, + session_token) @responses.activate def test_autocomplete_query(self): From d0d00eaf0b6656e4ead15c4c011436cb60660ca6 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 25 Jun 2018 16:19:46 +1000 Subject: [PATCH 016/166] Version 3.0.0 Change-Id: I3b31dba8aa1f8f5ebffb9a35cf4b8d3ede041616 --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index e9a0b779..e658de7a 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "2.5.1-dev" +__version__ = "3.0.0" from googlemaps.client import Client import googlemaps.exceptions diff --git a/setup.py b/setup.py index 6ab70806..ee98ba4a 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ ] setup(name='googlemaps', - version='2.5.1-dev', + version='3.0.0', description='Python client library for Google Maps API Web Services', scripts=[], url='https://github.com/googlemaps/google-maps-services-python', From 864f1184a8cec56698f4f1019699eabda0f4a158 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 25 Jun 2018 16:26:29 +1000 Subject: [PATCH 017/166] Dev version Change-Id: Ibbe5633812db7e881c91e8fc52385e0161521db9 --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index e658de7a..51286c07 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "3.0.0" +__version__ = "3.0.0-dev" from googlemaps.client import Client import googlemaps.exceptions diff --git a/setup.py b/setup.py index ee98ba4a..d1bd6fad 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ ] setup(name='googlemaps', - version='3.0.0', + version='3.0.0-dev', description='Python client library for Google Maps API Web Services', scripts=[], url='https://github.com/googlemaps/google-maps-services-python', From 93d47cb23b317a25d68708b29c08c20b74dc8c43 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Wed, 27 Jun 2018 09:54:15 +1000 Subject: [PATCH 018/166] Fix autocomplete sessiontoken. Change-Id: I3de2df5783b3b43b7e11bb02ffb7ec75065a197c --- googlemaps/places.py | 6 +++--- googlemaps/test/test_places.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 70e17c10..e2c38c56 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -455,9 +455,9 @@ def places_autocomplete(client, input_text, session_token, offset=None, :rtype: list of predictions """ - return _autocomplete(client, "", input_text, offset=offset, - location=location, radius=radius, language=language, - types=types, components=components, + return _autocomplete(client, "", input_text, session_token=session_token, + offset=offset, location=location, radius=radius, + language=language, types=types, components=components, strict_bounds=strict_bounds) diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index 37d1e76f..77d05afd 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -182,9 +182,8 @@ def test_autocomplete(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?components=country%%3Aau&input=Google&language=en-AU&' 'location=-33.86746%%2C151.20709&offset=3&radius=100&' - 'strictbounds=true&types=geocode&key=%s&sessiontoken=' % - (url, self.key), responses.calls[0].request.url, - session_token) + 'strictbounds=true&types=geocode&key=%s&sessiontoken=%s' % + (url, self.key, session_token), responses.calls[0].request.url) @responses.activate def test_autocomplete_query(self): From fe7cb74b317de29f46d95fd3ddaf456ae9794d6f Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Wed, 27 Jun 2018 11:32:01 +1000 Subject: [PATCH 019/166] Add constants for places fields param by category. Change-Id: Ifa9d792eb7f04083d5f398d0522b33f9bd2b9170 --- googlemaps/places.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index e2c38c56..5fe22815 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -21,20 +21,37 @@ from googlemaps import convert -PLACES_FIND_FIELDS = set([ +PLACES_FIND_FIELDS_BASIC = set([ "formatted_address", "geometry", "icon", "id", "name", - "permanently_closed", "photos", "place_id", "scope", "types", - "vicinity", "opening_hours", "price_level", "rating", + "permanently_closed", "photos", "place_id", "plus_code", "scope", + "types", ]) -PLACES_DETAIL_FIELDS = set([ +PLACES_FIND_FIELDS_CONTACT = set(["opening_hours",]) + +PLACES_FIND_FIELDS_ATMOSPHERE = set(["price_level", "rating"]) + +PLACES_FIND_FIELDS = (PLACES_FIND_FIELDS_BASIC ^ + PLACES_FIND_FIELDS_CONTACT ^ + PLACES_FIND_FIELDS_ATMOSPHERE) + +PLACES_DETAIL_FIELDS_BASIC = set([ "address_component", "adr_address", "alt_id", "formatted_address", "geometry", "icon", "id", "name", "permanently_closed", "photo", - "place_id", "scope", "type", "url", "utc_offset", "vicinity", + "place_id", "plus_code", "scope", "type", "url", "utc_offset", "vicinity", +]) + +PLACES_DETAIL_FIELDS_CONTACT = set([ "formatted_phone_number", "international_phone_number", "opening_hours", - "website", "price_level", "rating", "review", + "website", ]) +PLACES_DETAIL_FIELDS_ATMOSPHERE = set(["price_level", "rating", "review",]) + +PLACES_DETAIL_FIELDS = (PLACES_DETAIL_FIELDS_BASIC ^ + PLACES_DETAIL_FIELDS_CONTACT ^ + PLACES_DETAIL_FIELDS_ATMOSPHERE) + def find_place(client, input, input_type, fields=None, location_bias=None, language=None): From 49f834c4a4f606d3f4a9b0285e7ffd9c90e03785 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Wed, 27 Jun 2018 11:34:30 +1000 Subject: [PATCH 020/166] Version 3.0.1 Change-Id: I36de197ecbf52927d409417d2ba7748f97f6b448 --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 51286c07..80330067 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "3.0.0-dev" +__version__ = "3.0.1" from googlemaps.client import Client import googlemaps.exceptions diff --git a/setup.py b/setup.py index d1bd6fad..974fecef 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ ] setup(name='googlemaps', - version='3.0.0-dev', + version='3.0.1', description='Python client library for Google Maps API Web Services', scripts=[], url='https://github.com/googlemaps/google-maps-services-python', From 216588d10512e720a7ced134ada5f4075dff51a2 Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Wed, 27 Jun 2018 11:38:16 +1000 Subject: [PATCH 021/166] Version 3.0.1-dev Change-Id: Idf1369704fdf3e78abb29c1cdaabed48582bccca --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 80330067..fe54f896 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "3.0.1" +__version__ = "3.0.11-dev" from googlemaps.client import Client import googlemaps.exceptions diff --git a/setup.py b/setup.py index 974fecef..08530b45 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ ] setup(name='googlemaps', - version='3.0.1', + version='3.0.1-dev', description='Python client library for Google Maps API Web Services', scripts=[], url='https://github.com/googlemaps/google-maps-services-python', From aa7dc9ce7427dbbb76fc1107a309087e020d2f38 Mon Sep 17 00:00:00 2001 From: Pyglouthon Date: Mon, 2 Jul 2018 01:48:59 +0200 Subject: [PATCH 022/166] Add sessiontoken to place details request to get free request in an autocomplete session (#244) --- googlemaps/places.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 5fe22815..7324c253 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -342,7 +342,7 @@ def _places(client, url_part, query=None, location=None, radius=None, return client._request(url, params) -def place(client, place_id, fields=None, language=None): +def place(client, place_id, session_token=None, fields=None, language=None): """ Comprehensive details for an individual place. @@ -350,13 +350,17 @@ def place(client, place_id, fields=None, language=None): returned from a Places search. :type place_id: string + :param session_token: A random string which identifies an autocomplete + session for billing purposes. + :type session_token: string + :param fields: The fields specifying the types of place data to return, separated by a comma. For full details see: https://cloud.google.com/maps-platform/user-guide/product-changes/#places :type input: list :param language: The language in which to return results. - :type langauge: string + :type language: string :rtype: result dict with the following keys: result: dict containing place details @@ -376,6 +380,8 @@ def place(client, place_id, fields=None, language=None): if language: params["language"] = language + if session_token: + params["sessiontoken"] = session_token return client._request("/maps/api/place/details/json", params) From afdec6575592d7808594473206107060467b437a Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 2 Jul 2018 10:53:39 +1000 Subject: [PATCH 023/166] Version 3.0.2 Change-Id: Ie14e3489fa932fa6ba4f90a79776a8a73b3a1f19 --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index fe54f896..63f904c8 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "3.0.11-dev" +__version__ = "3.0.2" from googlemaps.client import Client import googlemaps.exceptions diff --git a/setup.py b/setup.py index 08530b45..2cad3805 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ ] setup(name='googlemaps', - version='3.0.1-dev', + version='3.0.2', description='Python client library for Google Maps API Web Services', scripts=[], url='https://github.com/googlemaps/google-maps-services-python', From 8c4c0c2253e635d2db52bf8762073d029024e17d Mon Sep 17 00:00:00 2001 From: Stephen McDonald Date: Mon, 20 Aug 2018 09:52:16 +1000 Subject: [PATCH 024/166] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f71721a4..e9a2e9c7 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ instead of an API key. [apikey]: https://developers.google.com/maps/faq#keysystem [clientid]: https://developers.google.com/maps/documentation/business/webservices/auth -[Google Maps API Web Services]: https://developers.google.com/maps/documentation/webservices/ +[Google Maps API Web Services]: https://developers.google.com/maps/apis-by-platform#web_service_apis [Directions API]: https://developers.google.com/maps/documentation/directions/ [directions-key]: https://developers.google.com/maps/documentation/directions/get-api-key#key [directions-client-id]: https://developers.google.com/maps/documentation/directions/get-api-key#client-id From a3d79ab523c5f1a92bde255ca38ea822307886d2 Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 10 Sep 2018 16:36:12 -0400 Subject: [PATCH 025/166] Do not override headers passed in request_kwargs If headers are passed in as part of the request_kwargs, then make sure they are not overridden as part of the load. This is required if you are limiting your API by using referer. --- googlemaps/client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 89ad179c..c4e1d5c1 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -149,8 +149,10 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.channel = channel self.retry_timeout = timedelta(seconds=retry_timeout) self.requests_kwargs = requests_kwargs or {} + headers = self.request_kwargs.pop('headers', {}) + headers.update({"User-Agent": _USER_AGENT}) self.requests_kwargs.update({ - "headers": {"User-Agent": _USER_AGENT}, + "headers": headers, "timeout": self.timeout, "verify": True, # NOTE(cbro): verify SSL certs. }) From f6e02a5bfa2971b0cb56cf7c12f7d9f9e5dc03ca Mon Sep 17 00:00:00 2001 From: Craig Date: Mon, 10 Sep 2018 16:50:26 -0400 Subject: [PATCH 026/166] Update client.py --- googlemaps/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index c4e1d5c1..6c695f31 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -149,7 +149,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.channel = channel self.retry_timeout = timedelta(seconds=retry_timeout) self.requests_kwargs = requests_kwargs or {} - headers = self.request_kwargs.pop('headers', {}) + headers = self.requests_kwargs.pop('headers', {}) headers.update({"User-Agent": _USER_AGENT}) self.requests_kwargs.update({ "headers": headers, From 2b580ca70672c6003125ec926003dafc74f516be Mon Sep 17 00:00:00 2001 From: Yevhen Amelin Date: Sat, 27 Oct 2018 12:07:20 +0300 Subject: [PATCH 027/166] Fix typos in docstrings --- googlemaps/geocoding.py | 4 ++-- googlemaps/places.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/googlemaps/geocoding.py b/googlemaps/geocoding.py index a2913cf9..b665d776 100644 --- a/googlemaps/geocoding.py +++ b/googlemaps/geocoding.py @@ -43,7 +43,7 @@ def geocode(client, address=None, components=None, bounds=None, region=None, :type region: string :param language: The language in which to return results. - :type langauge: string + :type language: string :rtype: list of geocoding results. """ @@ -85,7 +85,7 @@ def reverse_geocode(client, latlng, result_type=None, location_type=None, :type location_type: list of strings :param language: The language in which to return results. - :type langauge: string + :type language: string :rtype: list of reverse geocoding results. """ diff --git a/googlemaps/places.py b/googlemaps/places.py index 7324c253..7c2277a9 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -80,7 +80,7 @@ def find_place(client, input, input_type, fields=None, location_bias=None, :type location_bias: string :param language: The language in which to return results. - :type langauge: string + :type language: string :rtype: result dict with the following keys: status: status code @@ -132,7 +132,7 @@ def places(client, query, location=None, radius=None, language=None, :type radius: int :param language: The language in which to return results. - :type langauge: string + :type language: string :param min_price: Restricts results to only those places with no less than this price level. Valid values are in the range from 0 (most affordable) @@ -194,7 +194,7 @@ def places_nearby(client, location=None, radius=None, keyword=None, :type keyword: string :param language: The language in which to return results. - :type langauge: string + :type language: string :param min_price: Restricts results to only those places with no less than this price level. Valid values are in the range from 0 @@ -459,7 +459,7 @@ def places_autocomplete(client, input_text, session_token, offset=None, :type radius: int :param language: The language in which to return results. - :type langauge: string + :type language: string :param types: Restricts the results to places matching the specified type. The full list of supported types is available here: @@ -506,7 +506,7 @@ def places_autocomplete_query(client, input_text, offset=None, location=None, :type radius: number :param language: The language in which to return results. - :type langauge: string + :type language: string :rtype: list of predictions """ From d6c514d6282415d243b990375e2582ed8530be04 Mon Sep 17 00:00:00 2001 From: engstrom Date: Mon, 12 Nov 2018 14:19:14 -0700 Subject: [PATCH 028/166] Increase the required version of requests. Versions of the requests library prior to 2.20.0 have a known security vulnerability (CVE-2018-18074). --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2cad3805..e3bf4286 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ requirements = [ - 'requests>=2.11.1,<3.0', + 'requests>=2.20.0,<3.0', ] setup(name='googlemaps', From 7f7d302b97e05dd12c4a9a687b03781c136101b6 Mon Sep 17 00:00:00 2001 From: Alexander Polekha Date: Thu, 30 May 2019 15:56:56 +0300 Subject: [PATCH 029/166] user_ratings_total field added to place atmosphere fields --- googlemaps/places.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 7c2277a9..534319dd 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -29,7 +29,9 @@ PLACES_FIND_FIELDS_CONTACT = set(["opening_hours",]) -PLACES_FIND_FIELDS_ATMOSPHERE = set(["price_level", "rating"]) +PLACES_FIND_FIELDS_ATMOSPHERE = set([ + "price_level", "rating", "user_ratings_total", +]) PLACES_FIND_FIELDS = (PLACES_FIND_FIELDS_BASIC ^ PLACES_FIND_FIELDS_CONTACT ^ @@ -46,7 +48,9 @@ "website", ]) -PLACES_DETAIL_FIELDS_ATMOSPHERE = set(["price_level", "rating", "review",]) +PLACES_DETAIL_FIELDS_ATMOSPHERE = set([ + "price_level", "rating", "review", "user_ratings_total", +]) PLACES_DETAIL_FIELDS = (PLACES_DETAIL_FIELDS_BASIC ^ PLACES_DETAIL_FIELDS_CONTACT ^ From 6e60b3c0a3e794403b8a7f94bbd52d1f29da15f0 Mon Sep 17 00:00:00 2001 From: David Robles Date: Wed, 7 Aug 2019 13:04:30 -0700 Subject: [PATCH 030/166] Parameter session_token in places_autocomplete should be optional The official docs of the Places API has the session_token parameter as optional. This change makes it optional too in the library to keep it in sync. --- googlemaps/places.py | 2 +- googlemaps/test/test_places.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 534319dd..7f43b13c 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -435,7 +435,7 @@ def places_photo(client, photo_reference, max_width=None, max_height=None): return response.iter_content() -def places_autocomplete(client, input_text, session_token, offset=None, +def places_autocomplete(client, input_text, session_token=None, offset=None, location=None, radius=None, language=None, types=None, components=None, strict_bounds=False): """ diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index 77d05afd..4080b55f 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -171,7 +171,7 @@ def test_autocomplete(self): session_token = places_autocomplete_session_token() - self.client.places_autocomplete('Google', session_token, offset=3, + self.client.places_autocomplete('Google', session_token=session_token, offset=3, location=self.location, radius=self.radius, language=self.language, From 5d8d0061be6bcfef162d4b7bc8caff8b37bf2695 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 19 Aug 2019 15:07:07 -0700 Subject: [PATCH 031/166] remove deprecated places radar (#288) --- googlemaps/client.py | 2 -- googlemaps/places.py | 58 +--------------------------------- googlemaps/test/test_places.py | 20 ------------ 3 files changed, 1 insertion(+), 79 deletions(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 6c695f31..9a195c3c 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -344,7 +344,6 @@ def _generate_auth_url(self, path, params, accepts_clientid): from googlemaps.places import find_place from googlemaps.places import places from googlemaps.places import places_nearby -from googlemaps.places import places_radar from googlemaps.places import place from googlemaps.places import places_photo from googlemaps.places import places_autocomplete @@ -388,7 +387,6 @@ def wrapper(*args, **kwargs): Client.find_place = make_api_method(find_place) Client.places = make_api_method(places) Client.places_nearby = make_api_method(places_nearby) -Client.places_radar = make_api_method(places_radar) Client.place = make_api_method(place) Client.places_photo = make_api_method(places_photo) Client.places_autocomplete = make_api_method(places_autocomplete) diff --git a/googlemaps/places.py b/googlemaps/places.py index 7f43b13c..2daf61aa 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -253,67 +253,11 @@ def places_nearby(client, location=None, radius=None, keyword=None, rank_by=rank_by, type=type, page_token=page_token) -def places_radar(client, location, radius, keyword=None, min_price=None, - max_price=None, name=None, open_now=False, type=None): - """ - Performs radar search for places. - - :param location: The latitude/longitude value for which you wish to obtain the - closest, human-readable address. - :type location: string, dict, list, or tuple - - :param radius: Distance in meters within which to bias results. - :type radius: int - - :param keyword: A term to be matched against all content that Google has - indexed for this place. - :type keyword: string - - :param min_price: Restricts results to only those places with no less than - this price level. Valid values are in the range from 0 - (most affordable) to 4 (most expensive). - :type min_price: int - - :param max_price: Restricts results to only those places with no greater - than this price level. Valid values are in the range - from 0 (most affordable) to 4 (most expensive). - :type max_price: int - - :param name: One or more terms to be matched against the names of places. - :type name: string or list of strings - - :param open_now: Return only those places that are open for business at - the time the query is sent. - :type open_now: bool - - :param type: Restricts the results to places matching the specified type. - The full list of supported types is available here: - https://developers.google.com/places/supported_types - :type type: string - - :rtype: result dict with the following keys: - status: status code - results: list of places - html_attributions: set of attributions which must be displayed - - """ - if not (keyword or name or type): - raise ValueError("either a keyword, name, or type arg is required") - - from warnings import warn - warn("places_radar is deprecated, see http://goo.gl/BGiumE", - DeprecationWarning) - - return _places(client, "radar", location=location, radius=radius, - keyword=keyword, min_price=min_price, max_price=max_price, - name=name, open_now=open_now, type=type) - - def _places(client, url_part, query=None, location=None, radius=None, keyword=None, language=None, min_price=0, max_price=4, name=None, open_now=False, rank_by=None, type=None, region=None, page_token=None): """ - Internal handler for ``places``, ``places_nearby``, and ``places_radar``. + Internal handler for ``places`` and ``places_nearby``. See each method's docs for arg details. """ diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index 4080b55f..4a32000a 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -110,26 +110,6 @@ def test_places_nearby_search(self): self.client.places_nearby(location=self.location, rank_by="distance", keyword='foo', radius=self.radius) - @responses.activate - def test_places_radar_search(self): - url = 'https://maps.googleapis.com/maps/api/place/radarsearch/json' - responses.add(responses.GET, url, - body='{"status": "OK", "results": [], "html_attributions": []}', - status=200, content_type='application/json') - - self.client.places_radar(self.location, self.radius, keyword='foo', - min_price=1, max_price=4, name='bar', - open_now=True, type=self.type) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?keyword=foo&location=-33.86746%%2C151.20709&' - 'maxprice=4&minprice=1&name=bar&opennow=true&radius=100&' - 'type=liquor_store&key=%s' - % (url, self.key), responses.calls[0].request.url) - - with self.assertRaises(ValueError): - self.client.places_radar(self.location, self.radius) - @responses.activate def test_place_detail(self): url = 'https://maps.googleapis.com/maps/api/place/details/json' From 681dabe23ab44c91025b140603e81ef61a57e2c0 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 20 Aug 2019 10:22:17 -0700 Subject: [PATCH 032/166] fix top level import and remove unused imports (#289) * fix imports and remove unused * use uuid4 directly for session token in test --- googlemaps/__init__.py | 6 +++--- googlemaps/distance_matrix.py | 1 - googlemaps/places.py | 1 - googlemaps/test/test_places.py | 5 +++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 63f904c8..1c94fae5 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -18,7 +18,7 @@ __version__ = "3.0.2" from googlemaps.client import Client -import googlemaps.exceptions +from googlemaps import exceptions -# Allow sphinx to pick up these symbols for the documentation. -__all__ = ["Client"] + +__all__ = ["Client", "exceptions"] diff --git a/googlemaps/distance_matrix.py b/googlemaps/distance_matrix.py index f6a85e8c..1d848253 100755 --- a/googlemaps/distance_matrix.py +++ b/googlemaps/distance_matrix.py @@ -18,7 +18,6 @@ """Performs requests to the Google Maps Distance Matrix API.""" from googlemaps import convert -from googlemaps.convert import as_list def distance_matrix(client, origins, destinations, diff --git a/googlemaps/places.py b/googlemaps/places.py index 2daf61aa..53ca3889 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -17,7 +17,6 @@ """Performs requests to the Google Places API.""" -from uuid import uuid4 as places_autocomplete_session_token from googlemaps import convert diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index 4a32000a..17bf03e8 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -18,12 +18,13 @@ """Tests for the places module.""" +import uuid + from types import GeneratorType import responses import googlemaps -from googlemaps.places import places_autocomplete_session_token import googlemaps.test as _test @@ -149,7 +150,7 @@ def test_autocomplete(self): body='{"status": "OK", "predictions": []}', status=200, content_type='application/json') - session_token = places_autocomplete_session_token() + session_token = uuid.uuid4().hex self.client.places_autocomplete('Google', session_token=session_token, offset=3, location=self.location, From eef6f7cade6325b35e44cd0045c45c29d75b7091 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 22 Aug 2019 09:42:29 -0700 Subject: [PATCH 033/166] add badges for pypi and number of contributors and download (#293) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e9a2e9c7..e17ccbad 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ Python Client for Google Maps Services ==================================== [![Build Status](https://travis-ci.org/googlemaps/google-maps-services-python.svg?branch=master)](https://travis-ci.org/googlemaps/google-maps-services-python) +[![PyPI version](https://badge.fury.io/py/googlemaps.svg)](https://badge.fury.io/py/googlemaps) +![PyPI - Downloads](https://img.shields.io/pypi/dd/googlemaps) +![GitHub contributors](https://img.shields.io/github/contributors/googlemaps/google-maps-services-python) ## Description From e063ee077160ceba93c15e7e718a50bb764195ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Thu, 22 Aug 2019 19:00:52 +0200 Subject: [PATCH 034/166] Include tests in sdist pypi tarball (#273) --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 4e833047..20b3a1ad 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE README.md +recursive-include googlemaps/test *.py global-exclude __pycache__ global-exclude *.py[co] From 8297fd9f1dfd221cb1dd22187bcf410b5c8475cc Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 22 Aug 2019 10:36:23 -0700 Subject: [PATCH 035/166] add long description to setup.py (#292) Add `long_description` to setup.py from the readme and set the `long_description_content_type` so that pypi renders it correctly. --- setup.py | 71 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/setup.py b/setup.py index e3bf4286..31e3edab 100644 --- a/setup.py +++ b/setup.py @@ -1,42 +1,43 @@ import sys - - -try: - from setuptools import setup -except ImportError: - from distutils.core import setup +import io +from setuptools import setup if sys.version_info <= (2, 4): - error = 'Requires Python Version 2.5 or above... exiting.' - print >> sys.stderr, error - sys.exit(1) + error = "Requires Python Version 2.5 or above... exiting." + print >>sys.stderr, error + sys.exit(1) + +requirements = ["requests>=2.20.0,<3.0"] -requirements = [ - 'requests>=2.20.0,<3.0', -] +# use io.open until python2.7 support is dropped +with io.open("README.md", encoding="utf8") as f: + readme = f.read() -setup(name='googlemaps', - version='3.0.2', - description='Python client library for Google Maps API Web Services', - scripts=[], - url='https://github.com/googlemaps/google-maps-services-python', - packages=['googlemaps'], - license='Apache 2.0', - platforms='Posix; MacOS X; Windows', - setup_requires=requirements, - install_requires=requirements, - test_suite='googlemaps.test', - classifiers=['Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Topic :: Internet', - ] - ) +setup( + name="googlemaps", + version="3.0.2", + description="Python client library for Google Maps Platform", + long_description=readme, + long_description_content_type="text/markdown", + scripts=[], + url="https://github.com/googlemaps/google-maps-services-python", + packages=["googlemaps"], + license="Apache 2.0", + platforms="Posix; MacOS X; Windows", + setup_requires=requirements, + install_requires=requirements, + test_suite="googlemaps.test", + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Topic :: Internet", + ], +) From e2e92382e7ae166112ef80a57c9b76198e821e7d Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 22 Aug 2019 10:38:22 -0700 Subject: [PATCH 036/166] upgrade to pytest and nox with coverage reporting to codecov (#291) - pytest replaces the unmaintained nose - nox replaces tox - coverage reported to codecov.io --- .gitignore | 5 ++-- .travis.yml | 22 +++++++++++------ .travis/install.sh | 11 +++++++++ README.md | 13 +++++----- noxfile.py | 57 +++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 12 +++++++++ test_requirements.txt | 6 +++-- tox.ini | 16 ------------ 8 files changed, 107 insertions(+), 35 deletions(-) create mode 100755 .travis/install.sh create mode 100644 noxfile.py create mode 100644 setup.cfg delete mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 78c58e12..0d2efbbc 100644 --- a/.gitignore +++ b/.gitignore @@ -28,9 +28,8 @@ dist/ # python testing things etc .coverage -.tox +.nox env -nosetests.xml googlemaps.egg-info *.egg - +.vscode/ diff --git a/.travis.yml b/.travis.yml index ea1fa63d..87a0fece 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,22 @@ language: python +dist: xenial matrix: include: - - { python: '2.7', env: TOXENV=py27 } - - { python: '3.4', env: TOXENV=py34 } - - { python: '3.5', env: TOXENV=py35 } - - { python: '3.6', env: TOXENV=py36 } - - { python: '3.6', env: TOXENV=docs } + - python: '2.7' + env: NOXSESSION="tests-2.7" + - python: '3.5' + env: NOXSESSION="tests-3.5" + - python: '3.6' + env: NOXSESSION="tests-3.6" + - python: '3.7' + env: NOXSESSION="tests-3.7" + sudo: required # required for Python 3.7 (github.com/travis-ci/travis-ci#9069) + - python: '2.7' + env: NOXSESSION="docs" install: - - pip install requests - - pip install tox +- ./.travis/install.sh script: - - tox +- python3 -m nox --session "$NOXSESSION" diff --git a/.travis/install.sh b/.travis/install.sh new file mode 100755 index 00000000..9dd84bde --- /dev/null +++ b/.travis/install.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -exo pipefail + +if ! python3 -m pip --version; then + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + sudo python3 get-pip.py + sudo python3 -m pip install nox +else + python3 -m pip install nox +fi diff --git a/README.md b/README.md index e17ccbad..45e2de6d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Python Client for Google Maps Services ==================================== [![Build Status](https://travis-ci.org/googlemaps/google-maps-services-python.svg?branch=master)](https://travis-ci.org/googlemaps/google-maps-services-python) +[![codecov](https://codecov.io/gh/googlemaps/google-maps-services-python/branch/master/graph/badge.svg)](https://codecov.io/gh/googlemaps/google-maps-services-python) [![PyPI version](https://badge.fury.io/py/googlemaps.svg)](https://badge.fury.io/py/googlemaps) ![PyPI - Downloads](https://img.shields.io/pypi/dd/googlemaps) ![GitHub contributors](https://img.shields.io/github/contributors/googlemaps/google-maps-services-python) @@ -165,14 +166,14 @@ instead of an API key. ## Building the Project - # Installing tox - $ pip install tox + # Installing nox + $ pip install nox # Running tests - $ tox + $ nox # Generating documentation - $ tox -e docs + $ nox -e docs # Uploading a new release $ easy_install wheel twine @@ -180,13 +181,13 @@ instead of an API key. $ twine upload dist/* # Copy docs to gh-pages - $ tox -e docs && mv docs/_build/html generated_docs && git clean -Xdi && git checkout gh-pages + $ nox -e docs && mv docs/_build/html generated_docs && git clean -Xdi && git checkout gh-pages [apikey]: https://developers.google.com/maps/faq#keysystem [clientid]: https://developers.google.com/maps/documentation/business/webservices/auth -[Google Maps API Web Services]: https://developers.google.com/maps/apis-by-platform#web_service_apis +[Google Maps Platform web services]: https://developers.google.com/maps/apis-by-platform#web_service_apis [Directions API]: https://developers.google.com/maps/documentation/directions/ [directions-key]: https://developers.google.com/maps/documentation/directions/get-api-key#key [directions-client-id]: https://developers.google.com/maps/documentation/directions/get-api-key#client-id diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000..f1170918 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,57 @@ +import nox + + +def _install_dev_packages(session): + session.install("-e", ".") + + +def _install_test_dependencies(session): + session.install("-r", "test_requirements.txt") + + +def _install_doc_dependencies(session): + session.install("sphinx") + + +@nox.session(python=["2.7", "3.5", "3.6", "3.7"]) +def tests(session): + _install_dev_packages(session) + _install_test_dependencies(session) + + session.install("pytest") + session.run("pytest") + + session.notify("cover") + + +@nox.session +def cover(session): + """Coverage analysis.""" + session.install("coverage") + session.install("codecov") + session.run("coverage", "report", "--show-missing") + session.run("codecov") + session.run("coverage", "erase") + + +@nox.session(python="2.7") +def docs(session): + _install_dev_packages(session) + _install_doc_dependencies(session) + + session.run("rm", "-rf", "docs/_build", external=True) + + sphinx_args = [ + "-a", + "-E", + "-b", + "html", + "-d", + "docs/_build/doctrees", + "docs", + "docs/_build/html", + ] + + sphinx_cmd = "sphinx-build" + + session.run(sphinx_cmd, *sphinx_args) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..399af724 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,12 @@ +[tool:pytest] +addopts = -rsxX --cov=googlemaps --cov-report= + +[coverage:run] +omit = + googlemaps/test/* + +[coverage:report] +exclude_lines = + pragma: no cover + def __repr__ + raise NotImplementedError \ No newline at end of file diff --git a/test_requirements.txt b/test_requirements.txt index 5646081a..e5a18030 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,2 +1,4 @@ -nose -responses==0.3 +pytest +pytest-cov +responses +mock diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 77ca5b7e..00000000 --- a/tox.ini +++ /dev/null @@ -1,16 +0,0 @@ -[tox] -envlist = - py27,py32,py34,py35,py36,docs - -[testenv] -commands = - nosetests googlemaps/test -deps = -rtest_requirements.txt - -[testenv:docs] -basepython = - python2.7 -commands = - sphinx-build -a -E -b html -d docs/_build/doctrees docs docs/_build/html -deps = - Sphinx From 102dae3936d4678224f3221345be2f7e2b8554e3 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 23 Aug 2019 13:35:47 -0700 Subject: [PATCH 037/166] update readme and direct to documentation for api key (#297) --- README.md | 116 +++++++++++++++++++----------------------------------- 1 file changed, 40 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 45e2de6d..10f9d50f 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,22 @@ Python Client for Google Maps Services ## Description -Use Python? Want to [geocode][Geocoding API] something? Looking for [directions][Directions API]? -Maybe [matrices of directions][Distance Matrix API]? This library brings the [Google Maps API Web -Services] to your Python application. +Use Python? Want to geocode something? Looking for directions? +Maybe matrices of directions? This library brings the Google Maps Platform Web +Services to your Python application. ![Analytics](https://maps-ga-beacon.appspot.com/UA-12846745-20/google-maps-services-python/readme?pixel) The Python Client for Google Maps Services is a Python Client library for the following Google Maps APIs: - - [Directions API] - - [Distance Matrix API] - - [Elevation API] - - [Geocoding API] - - [Geolocation API] - - [Time Zone API] - - [Roads API] - - [Places API] + - Directions API + - Distance Matrix API + - Elevation API + - Geocoding API + - Geolocation API + - Time Zone API + - Roads API + - Places API Keep in mind that the same [terms and conditions](https://developers.google.com/maps/terms) apply to usage of the APIs when they're accessed through this library. @@ -39,42 +39,20 @@ to make backwards-incompatible changes. If we do remove some functionality (typi better functionality exists or if the feature proved infeasible), our intention is to deprecate and give developers a year to update their code. -If you find a bug, or have a feature suggestion, please [log an issue][issues]. If you'd like to -contribute, please read [How to Contribute][contrib]. +If you find a bug, or have a feature suggestion, please log an issue. If you'd like to +contribute, please read contribute. ## Requirements - Python 2.7 or later. - A Google Maps API key. -### API keys +## API Keys Each Google Maps Web Service request requires an API key or client ID. API keys -are freely available with a Google Account at -https://developers.google.com/console. The type of API key you need is a -**Server key**. - -To get an API key: - - 1. Visit https://developers.google.com/console and log in with - a Google Account. - 1. Select one of your existing projects, or create a new project. - 1. Enable the API(s) you want to use. The Python Client for Google Maps Services - accesses the following APIs: - * Directions API - * Distance Matrix API - * Elevation API - * Geocoding API - * Geolocation API - * Places API - * Roads API - * Time Zone API - 1. Create a new **Server key**. - 1. If you'd like to restrict requests to a specific IP address, do so now. - -For guided help, follow the instructions for the [Directions API][directions-key]. -You only need one API key, but remember to enable all the APIs you need. -For even more information, see the guide to [API keys][apikey]. +are generated in the 'Credentials' page of the 'APIs & Services' tab of [Google Cloud console](https://console.cloud.google.com/apis/credentials). + +For even more information on getting started with Google Maps Platform and generating/restricting an API key, see [Get Started with Google Maps Platform](https://developers.google.com/maps/gmp-get-started) in our docs. **Important:** This key should be kept secret on your server. @@ -84,25 +62,9 @@ For even more information, see the guide to [API keys][apikey]. Note that you will need requests 2.4.0 or higher if you want to specify connect/read timeouts. -## Developer Documentation - -View the [reference documentation](https://googlemaps.github.io/google-maps-services-python/docs/) - -Additional documentation for the included web services is available at -https://developers.google.com/maps/. - - - [Directions API] - - [Distance Matrix API] - - [Elevation API] - - [Geocoding API] - - [Geolocation API] - - [Time Zone API] - - [Roads API] - - [Places API] - ## Usage -This example uses the [Geocoding API] and the [Directions API] with an API key: +This example uses the Geocoding API and the Directions API with an API key: ```python import googlemaps @@ -130,7 +92,7 @@ and `client_secret` variables with appropriate values. For a guide on how to generate the `client_secret` (digital signature), see the documentation for the API you're using. For example, see the guide for the -[Directions API][directions-client-id]. +[Directions API](https://developers.google.com/maps/documentation/directions/get-api-key#client-id). ```python gmaps = googlemaps.Client(client_id=client_id, client_secret=client_secret) @@ -160,7 +122,7 @@ are returned from the API. ### Client IDs -Google Maps APIs Premium Plan customers can use their [client ID and secret][clientid] to authenticate, +Google Maps APIs Premium Plan customers can use their client ID and secret to authenticate, instead of an API key. ## Building the Project @@ -183,21 +145,23 @@ instead of an API key. # Copy docs to gh-pages $ nox -e docs && mv docs/_build/html generated_docs && git clean -Xdi && git checkout gh-pages - -[apikey]: https://developers.google.com/maps/faq#keysystem -[clientid]: https://developers.google.com/maps/documentation/business/webservices/auth - -[Google Maps Platform web services]: https://developers.google.com/maps/apis-by-platform#web_service_apis -[Directions API]: https://developers.google.com/maps/documentation/directions/ -[directions-key]: https://developers.google.com/maps/documentation/directions/get-api-key#key -[directions-client-id]: https://developers.google.com/maps/documentation/directions/get-api-key#client-id -[Distance Matrix API]: https://developers.google.com/maps/documentation/distancematrix/ -[Elevation API]: https://developers.google.com/maps/documentation/elevation/ -[Geocoding API]: https://developers.google.com/maps/documentation/geocoding/ -[Geolocation API]: https://developers.google.com/maps/documentation/geolocation/ -[Time Zone API]: https://developers.google.com/maps/documentation/timezone/ -[Roads API]: https://developers.google.com/maps/documentation/roads/ -[Places API]: https://developers.google.com/places/ - -[issues]: https://github.com/googlemaps/google-maps-services-python/issues -[contrib]: https://github.com/googlemaps/google-maps-services-python/blob/master/CONTRIB.md +## Documentation & resources +### Getting started +- [Get Started with Google Maps Platform](https://developers.google.com/maps/gmp-get-started) +- [Generating/restricting an API key](https://developers.google.com/maps/gmp-get-started#api-key) +- [Authenticating with a client ID](https://developers.google.com/maps/documentation/directions/get-api-key#client-id) + +### API docs +- [Google Maps Platform web services](https://developers.google.com/maps/apis-by-platform#web_service_apis) +- [Directions API](https://developers.google.com/maps/documentation/directions/) +- [Distance Matrix API](https://developers.google.com/maps/documentation/distancematrix/) +- [Elevation API](https://developers.google.com/maps/documentation/elevation/) +- [Geocoding API](https://developers.google.com/maps/documentation/geocoding/) +- [Geolocation API](https://developers.google.com/maps/documentation/geolocation/) +- [Time Zone API](https://developers.google.com/maps/documentation/timezone/) +- [Roads API](https://developers.google.com/maps/documentation/roads/) +- [Places API](https://developers.google.com/places/) + +### Support +- [Report an issue](https://github.com/googlemaps/google-maps-services-python/issues) +- [Contribute](https://github.com/googlemaps/google-maps-services-python/blob/master/CONTRIB.md) From 35a0f10d1e43f7de11612f5f954df0546a47fab0 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 23 Aug 2019 13:47:01 -0700 Subject: [PATCH 038/166] automate upload to pypi with travis deploy (#296) --- .travis.yml | 12 ++++++++++++ README.md | 7 +------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 87a0fece..0b7d50fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,3 +20,15 @@ install: script: - python3 -m nox --session "$NOXSESSION" + +deploy: + on: + repo: googlemaps/google-maps-services-python + tag: true + python: '3.6' # only run this deploy once with python 3.6 + provider: pypi + distributions: 'sdist bdist_wheel' + user: __token__ # api token encrypted within travis + +notifications: + email: false diff --git a/README.md b/README.md index 10f9d50f..b4669df7 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ directions_result = gmaps.directions("Sydney Town Hall", departure_time=now) ``` -For more usage examples, check out [the tests](googlemaps/test/). +For more usage examples, check out [the tests](https://github.com/googlemaps/google-maps-services-python/tree/master/googlemaps/test). ## Features @@ -137,11 +137,6 @@ instead of an API key. # Generating documentation $ nox -e docs - # Uploading a new release - $ easy_install wheel twine - $ python setup.py sdist bdist_wheel - $ twine upload dist/* - # Copy docs to gh-pages $ nox -e docs && mv docs/_build/html generated_docs && git clean -Xdi && git checkout gh-pages From 5439986a94b8f372fd1f697d5dafa9eef7dfe61e Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 26 Aug 2019 11:43:57 -0700 Subject: [PATCH 039/166] explicitly truncate float precision in format_float to 8 decimals, closes #277 (#301) --- googlemaps/convert.py | 11 +++++++---- googlemaps/test/test_convert.py | 16 ++++++++++++++++ googlemaps/test/test_distance_matrix.py | 4 ++-- googlemaps/test/test_geocoding.py | 2 +- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/googlemaps/convert.py b/googlemaps/convert.py index a20e3a32..b5823f67 100644 --- a/googlemaps/convert.py +++ b/googlemaps/convert.py @@ -34,9 +34,10 @@ def format_float(arg): """Formats a float value to be as short as possible. - Trims extraneous trailing zeros and period to give API - args the best possible chance of fitting within 2000 char - URL length restrictions. + Truncates float to 8 decimal places and trims extraneous + trailing zeros and period to give API args the best + possible chance of fitting within 2000 char URL length + restrictions. For example: @@ -45,13 +46,15 @@ def format_float(arg): format_float(40.1) -> "40.1" format_float(40.001) -> "40.001" format_float(40.0010) -> "40.001" + format_float(40.000000001) -> "40" + format_float(40.000000009) -> "40.00000001" :param arg: The lat or lng float. :type arg: float :rtype: string """ - return ("%f" % float(arg)).rstrip("0").rstrip(".") + return ("%.8f" % float(arg)).rstrip("0").rstrip(".") def latlng(arg): diff --git a/googlemaps/test/test_convert.py b/googlemaps/test/test_convert.py index ed08c84b..9cbde9ea 100644 --- a/googlemaps/test/test_convert.py +++ b/googlemaps/test/test_convert.py @@ -19,6 +19,7 @@ import datetime import unittest +import pytest from googlemaps import convert @@ -152,3 +153,18 @@ def test_polyline_round_trip(self): points = convert.decode_polyline(test_polyline) actual_polyline = convert.encode_polyline(points) self.assertEqual(test_polyline, actual_polyline) + + +@pytest.mark.parametrize( + "value, expected", + [ + (40, "40"), + (40.0, "40"), + (40.1, "40.1"), + (40.00000001, "40.00000001"), + (40.000000009, "40.00000001"), + (40.000000001, "40"), + ], +) +def test_format_float(value, expected): + assert convert.format_float(value) == expected diff --git a/googlemaps/test/test_distance_matrix.py b/googlemaps/test/test_distance_matrix.py index 5e929b24..83ecfaf3 100644 --- a/googlemaps/test/test_distance_matrix.py +++ b/googlemaps/test/test_distance_matrix.py @@ -80,8 +80,8 @@ def test_mixed_params(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual('https://maps.googleapis.com/maps/api/distancematrix/json?' 'key=%s&origins=Bobcaygeon+ON%%7C41.43206%%2C-81.38992&' - 'destinations=43.012486%%2C-83.696415%%7C42.886386%%2C' - '-78.878163' % self.key, + 'destinations=43.012486%%2C-83.6964149%%7C42.8863855%%2C' + '-78.8781627' % self.key, responses.calls[0].request.url) @responses.activate diff --git a/googlemaps/test/test_geocoding.py b/googlemaps/test/test_geocoding.py index 511e6d8f..0f946850 100644 --- a/googlemaps/test/test_geocoding.py +++ b/googlemaps/test/test_geocoding.py @@ -59,7 +59,7 @@ def test_reverse_geocode(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'latlng=-33.867487%%2C151.20699&key=%s' % self.key, + 'latlng=-33.8674869,151.2069902&key=%s' % self.key, responses.calls[0].request.url) @responses.activate From 393536c4e66a64d1b451c50770ec8a77749c3c24 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 27 Aug 2019 12:03:11 -0600 Subject: [PATCH 040/166] release version 3.1.0 (#298) --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ setup.py | 8 ++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..a70f1b86 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog +All notable changes to this project will be documented in this file. + +## [Unreleased] +### Changed +### Added +### Removed + +## [v3.1.0] +### Changed +- Switched build system to use [nox](https://nox.thea.codes/en/stable/), pytest, and codecov. Added Python 3.7 to test framework. +- Set precision of truncated latitude and longitude floats [to 8 decimals](https://github.com/googlemaps/google-maps-services-python/pull/301) instead of 6. +- Minimum version of requests increased. +- Session token parameter [added](https://github.com/googlemaps/google-maps-services-python/pull/244) to `place()`. +- Fixed issue where headers in `request_kwargs` were being overridden. +### Added +- Automation for PyPi uploads. +- Long description to package. +- Added tests to manifest and tarball. +### Removed +- Removed places `places_autocomplete_session_token` which can be replaced with `uuid.uuid4().hex`. +- Removed deprecated `places_radar`. + + +**Note:** Start of changelog is 2019-08-27, [v3.0.2]. + +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.0...HEAD +[v3.1.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.2...3.1.0 +[v3.0.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.1...3.0.2 +[v3.0.1]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.0...3.0.1 +[v3.0.0]: https://github.com/googlemaps/google-maps-services-python/compare/2.5.1...3.0.0 diff --git a/setup.py b/setup.py index 31e3edab..5d51adab 100644 --- a/setup.py +++ b/setup.py @@ -15,11 +15,15 @@ with io.open("README.md", encoding="utf8") as f: readme = f.read() +with io.open("CHANGELOG.md", encoding="utf8") as f: + changelog = f.read() + + setup( name="googlemaps", - version="3.0.2", + version="3.1.0", description="Python client library for Google Maps Platform", - long_description=readme, + long_description=readme + changelog, long_description_content_type="text/markdown", scripts=[], url="https://github.com/googlemaps/google-maps-services-python", From 20da29d6d29328ca6c3391268d68d119f70aeabf Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 27 Aug 2019 13:37:11 -0600 Subject: [PATCH 041/166] fix travis deploy (#305) --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0b7d50fa..2dd6c9be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,9 +26,11 @@ deploy: repo: googlemaps/google-maps-services-python tag: true python: '3.6' # only run this deploy once with python 3.6 + branch: env(tag) # branch is equal to tag name provider: pypi distributions: 'sdist bdist_wheel' user: __token__ # api token encrypted within travis + skip_existing: true notifications: email: false From 5c4440c637010f50a912f205c91906e2e298c42a Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Wed, 28 Aug 2019 20:07:36 +0530 Subject: [PATCH 042/166] fix(manifest): include changelog in distribution (#308) currently manifest.in does not include changelog.md, which causes pip install to fail --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 20b3a1ad..fdf9f786 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include LICENSE README.md +include CHANGELOG.md LICENSE README.md recursive-include googlemaps/test *.py global-exclude __pycache__ global-exclude *.py[co] From 15491753130d1eb3911de34bf025433c30339286 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 28 Aug 2019 10:01:58 -0600 Subject: [PATCH 043/166] set version to 3.1.1 (#310) --- CHANGELOG.md | 7 ++++++- setup.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a70f1b86..8f955e0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ All notable changes to this project will be documented in this file. ### Added ### Removed +## [v3.1.1] +### Changed +- Added changelog to manifest + ## [v3.1.0] ### Changed - Switched build system to use [nox](https://nox.thea.codes/en/stable/), pytest, and codecov. Added Python 3.7 to test framework. @@ -24,7 +28,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.0...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.1...HEAD +[v3.1.1]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.0...3.1.1 [v3.1.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.2...3.1.0 [v3.0.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.1...3.0.2 [v3.0.1]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.0...3.0.1 diff --git a/setup.py b/setup.py index 5d51adab..76ffcc9f 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ setup( name="googlemaps", - version="3.1.0", + version="3.1.1", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From f79a1eea6ed4bc2414e068336137e5d013f00de5 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 29 Aug 2019 11:46:37 -0600 Subject: [PATCH 044/166] test distribution tar as part of ci (#311) --- .travis.yml | 1 + .travis/distribution.sh | 6 ++++++ CHANGELOG.md | 1 + noxfile.py | 10 +++++++++- 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100755 .travis/distribution.sh diff --git a/.travis.yml b/.travis.yml index 2dd6c9be..e1c65e95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ install: script: - python3 -m nox --session "$NOXSESSION" +- python3 -m nox -e distribution deploy: on: diff --git a/.travis/distribution.sh b/.travis/distribution.sh new file mode 100755 index 00000000..f7c08690 --- /dev/null +++ b/.travis/distribution.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +rm -rf dist + +python setup.py sdist +pip install $(find dist -name googlemaps-*.tar.gz) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f955e0f..05ea6a96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Changed ### Added +- Tests for distribution tar as part of CI ### Removed ## [v3.1.1] diff --git a/noxfile.py b/noxfile.py index f1170918..e6fa230a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,5 +1,7 @@ import nox +SUPPORTED_PY_VERSIONS = ["2.7", "3.5", "3.6", "3.7"] + def _install_dev_packages(session): session.install("-e", ".") @@ -13,7 +15,7 @@ def _install_doc_dependencies(session): session.install("sphinx") -@nox.session(python=["2.7", "3.5", "3.6", "3.7"]) +@nox.session(python=SUPPORTED_PY_VERSIONS) def tests(session): _install_dev_packages(session) _install_test_dependencies(session) @@ -55,3 +57,9 @@ def docs(session): sphinx_cmd = "sphinx-build" session.run(sphinx_cmd, *sphinx_args) + + +@nox.session() +def distribution(session): + session.run("bash", ".travis/distribution.sh", external=True) + session.run("python", "-c", "import googlemaps") From d3cd4c07247fcd71d9c1f29c37138aba1f8ddae8 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 30 Aug 2019 10:46:57 -0600 Subject: [PATCH 045/166] add github issue templates (#306) --- .github/ISSUE_TEMPLATE/bug_report.md | 47 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 32 +++++++++++++++ .github/ISSUE_TEMPLATE/support_request.md | 16 ++++++++ README.md | 1 + 4 files changed, 96 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/support_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..b1a36c32 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,47 @@ +--- +name: Bug report +about: Create a report to help us improve +label: type: bug, triage me +--- + +Thanks for stopping by to let us know something could be better! + +--- +**PLEASE READ** + +If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. + +Discover additional support services for the Google Maps Platform, including developer communities, technical guidance, and expert support at the Google Maps Platform [support resources page](https://developers.google.com/maps/support/). + +If your bug or feature request is not related to this particular library, please visit the Google Maps Platform [issue trackers](https://developers.google.com/maps/support/#issue_tracker). + +Check for answers on StackOverflow with the [google-maps](http://stackoverflow.com/questions/tagged/google-maps) tag. + +--- + +Please be sure to include as much information as possible: + +#### Environment details + +1. Specify the API at the beginning of the title (for example, "Places: ...") +2. OS type and version +3. Library version and other environment information + +#### Steps to reproduce + + 1. ? + +#### Code example + +```python +# example +``` + +#### Stack trace +``` +# example +``` + +Following these steps will guarantee the quickest resolution possible. + +Thanks! diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..54fcee5e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,32 @@ +--- +name: Feature request +about: Suggest an idea for this library +label: type: feature request, triage me +--- + +Thanks for stopping by to let us know something could be better! + +--- +**PLEASE READ** + +If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. + +Discover additional support services for the Google Maps Platform, including developer communities, technical guidance, and expert support at the Google Maps Platform [support resources page](https://developers.google.com/maps/support/). + +If your bug or feature request is not related to this particular library, please visit the Google Maps Platform [issue trackers](https://developers.google.com/maps/support/#issue_tracker). + +Check for answers on StackOverflow with the [google-maps](http://stackoverflow.com/questions/tagged/google-maps) tag. + +--- + + **Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + + **Describe the solution you'd like** +A clear and concise description of what you want to happen. + + **Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + + **Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md new file mode 100644 index 00000000..4eb13391 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -0,0 +1,16 @@ +--- +name: Support request +about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. +label: triage me, type: question +--- +**PLEASE READ** + +If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. + +Discover additional support services for the Google Maps Platform, including developer communities, technical guidance, and expert support at the Google Maps Platform [support resources page](https://developers.google.com/maps/support/). + +If your bug or feature request is not related to this particular library, please visit the Google Maps Platform [issue trackers](https://developers.google.com/maps/support/#issue_tracker). + +Check for answers on StackOverflow with the [google-maps](http://stackoverflow.com/questions/tagged/google-maps) tag. + +--- diff --git a/README.md b/README.md index b4669df7..fdc7d9b2 100644 --- a/README.md +++ b/README.md @@ -160,3 +160,4 @@ instead of an API key. ### Support - [Report an issue](https://github.com/googlemaps/google-maps-services-python/issues) - [Contribute](https://github.com/googlemaps/google-maps-services-python/blob/master/CONTRIB.md) +- [StackOverflow](http://stackoverflow.com/questions/tagged/google-maps) From 91efdd11fc98d36566ec2a32c17c72c305b1b3a1 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 30 Aug 2019 11:07:19 -0600 Subject: [PATCH 046/166] add missing line to fix github templates (#312) --- .github/ISSUE_TEMPLATE/bug_report.md | 1 + .github/ISSUE_TEMPLATE/feature_request.md | 1 + .github/ISSUE_TEMPLATE/support_request.md | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b1a36c32..ad65bce3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,6 +2,7 @@ name: Bug report about: Create a report to help us improve label: type: bug, triage me + --- Thanks for stopping by to let us know something could be better! diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 54fcee5e..8b40f036 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,6 +2,7 @@ name: Feature request about: Suggest an idea for this library label: type: feature request, triage me + --- Thanks for stopping by to let us know something could be better! diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index 4eb13391..81602d23 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -2,6 +2,7 @@ name: Support request about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. label: triage me, type: question + --- **PLEASE READ** From 3afe6586698523ff96272d01c4845e150abe0e53 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 30 Aug 2019 11:26:48 -0600 Subject: [PATCH 047/166] escape github template labels (#313) --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/ISSUE_TEMPLATE/support_request.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ad65bce3..1539cc67 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,7 @@ --- name: Bug report about: Create a report to help us improve -label: type: bug, triage me +label: 'type: bug, triage me' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 8b40f036..557f2315 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,7 @@ --- name: Feature request about: Suggest an idea for this library -label: type: feature request, triage me +label: 'type: feature request, triage me' --- diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index 81602d23..f8cade51 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -1,7 +1,7 @@ --- name: Support request about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. -label: triage me, type: question +label: 'triage me, type: question' --- **PLEASE READ** From b70730aa0490214d8eefec11630463582ee00fb2 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 9 Sep 2019 10:35:12 -0700 Subject: [PATCH 048/166] add support for subfields in mask (#302) --- googlemaps/places.py | 349 +++++++++++++++++++++++---------- googlemaps/test/test_places.py | 8 +- 2 files changed, 253 insertions(+), 104 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 53ca3889..a77f2d85 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -20,44 +20,92 @@ from googlemaps import convert -PLACES_FIND_FIELDS_BASIC = set([ - "formatted_address", "geometry", "icon", "id", "name", - "permanently_closed", "photos", "place_id", "plus_code", "scope", - "types", -]) - -PLACES_FIND_FIELDS_CONTACT = set(["opening_hours",]) - -PLACES_FIND_FIELDS_ATMOSPHERE = set([ - "price_level", "rating", "user_ratings_total", -]) - -PLACES_FIND_FIELDS = (PLACES_FIND_FIELDS_BASIC ^ - PLACES_FIND_FIELDS_CONTACT ^ - PLACES_FIND_FIELDS_ATMOSPHERE) - -PLACES_DETAIL_FIELDS_BASIC = set([ - "address_component", "adr_address", "alt_id", "formatted_address", - "geometry", "icon", "id", "name", "permanently_closed", "photo", - "place_id", "plus_code", "scope", "type", "url", "utc_offset", "vicinity", -]) - -PLACES_DETAIL_FIELDS_CONTACT = set([ - "formatted_phone_number", "international_phone_number", "opening_hours", - "website", -]) - -PLACES_DETAIL_FIELDS_ATMOSPHERE = set([ - "price_level", "rating", "review", "user_ratings_total", -]) - -PLACES_DETAIL_FIELDS = (PLACES_DETAIL_FIELDS_BASIC ^ - PLACES_DETAIL_FIELDS_CONTACT ^ - PLACES_DETAIL_FIELDS_ATMOSPHERE) - - -def find_place(client, input, input_type, fields=None, location_bias=None, - language=None): +PLACES_FIND_FIELDS_BASIC = set( + [ + "formatted_address", + "geometry", + "geometry/location", + "geometry/location/lat", + "geometry/location/lng", + "geometry/viewport", + "geometry/viewport/northeast", + "geometry/viewport/northeast/lat", + "geometry/viewport/northeast/lng", + "geometry/viewport/southwest", + "geometry/viewport/southwest/lat", + "geometry/viewport/southwest/lng", + "icon", + "id", + "name", + "permanently_closed", + "photos", + "place_id", + "plus_code", + "scope", + "types", + ] +) + +PLACES_FIND_FIELDS_CONTACT = set(["opening_hours"]) + +PLACES_FIND_FIELDS_ATMOSPHERE = set(["price_level", "rating", "user_ratings_total"]) + +PLACES_FIND_FIELDS = ( + PLACES_FIND_FIELDS_BASIC + ^ PLACES_FIND_FIELDS_CONTACT + ^ PLACES_FIND_FIELDS_ATMOSPHERE +) + +PLACES_DETAIL_FIELDS_BASIC = set( + [ + "address_component", + "adr_address", + "alt_id", + "formatted_address", + "geometry", + "geometry/location", + "geometry/location/lat", + "geometry/location/lng", + "geometry/viewport", + "geometry/viewport/northeast", + "geometry/viewport/northeast/lat", + "geometry/viewport/northeast/lng", + "geometry/viewport/southwest", + "geometry/viewport/southwest/lat", + "geometry/viewport/southwest/lng", + "icon", + "id", + "name", + "permanently_closed", + "photo", + "place_id", + "plus_code", + "scope", + "type", + "url", + "utc_offset", + "vicinity", + ] +) + +PLACES_DETAIL_FIELDS_CONTACT = set( + ["formatted_phone_number", "international_phone_number", "opening_hours", "website"] +) + +PLACES_DETAIL_FIELDS_ATMOSPHERE = set( + ["price_level", "rating", "review", "user_ratings_total"] +) + +PLACES_DETAIL_FIELDS = ( + PLACES_DETAIL_FIELDS_BASIC + ^ PLACES_DETAIL_FIELDS_CONTACT + ^ PLACES_DETAIL_FIELDS_ATMOSPHERE +) + + +def find_place( + client, input, input_type, fields=None, location_bias=None, language=None +): """ A Find Place request takes a text input, and returns a place. The text input can be any kind of Places data, for example, @@ -92,25 +140,27 @@ def find_place(client, input, input_type, fields=None, location_bias=None, params = {"input": input, "inputtype": input_type} if input_type != "textquery" and input_type != "phonenumber": - raise ValueError("Valid values for the `input_type` param for " - "`find_place` are 'textquery' or 'phonenumber', " - "the given value is invalid: '%s'" % input_type) + raise ValueError( + "Valid values for the `input_type` param for " + "`find_place` are 'textquery' or 'phonenumber', " + "the given value is invalid: '%s'" % input_type + ) if fields: invalid_fields = set(fields) - PLACES_FIND_FIELDS if invalid_fields: - raise ValueError("Valid values for the `fields` param for " - "`find_place` are '%s', these given field(s) " - "are invalid: '%s'" % ( - "', '".join(PLACES_FIND_FIELDS), - "', '".join(invalid_fields))) + raise ValueError( + "Valid values for the `fields` param for " + "`find_place` are '%s', these given field(s) " + "are invalid: '%s'" + % ("', '".join(PLACES_FIND_FIELDS), "', '".join(invalid_fields)) + ) params["fields"] = convert.join_list(",", fields) if location_bias: valid = ["ipbias", "point", "circle", "rectangle"] if location_bias.split(":")[0] not in valid: - raise ValueError("location_bias should be prefixed with one of: %s" - % valid) + raise ValueError("location_bias should be prefixed with one of: %s" % valid) params["locationbias"] = location_bias if language: params["language"] = language @@ -118,9 +168,19 @@ def find_place(client, input, input_type, fields=None, location_bias=None, return client._request("/maps/api/place/findplacefromtext/json", params) -def places(client, query, location=None, radius=None, language=None, - min_price=None, max_price=None, open_now=False, type=None, region=None, - page_token=None): +def places( + client, + query, + location=None, + radius=None, + language=None, + min_price=None, + max_price=None, + open_now=False, + type=None, + region=None, + page_token=None, +): """ Places search. @@ -169,15 +229,36 @@ def places(client, query, location=None, radius=None, language=None, html_attributions: set of attributions which must be displayed next_page_token: token for retrieving the next page of results """ - return _places(client, "text", query=query, location=location, - radius=radius, language=language, min_price=min_price, - max_price=max_price, open_now=open_now, type=type, region=region, - page_token=page_token) - - -def places_nearby(client, location=None, radius=None, keyword=None, - language=None, min_price=None, max_price=None, name=None, - open_now=False, rank_by=None, type=None, page_token=None): + return _places( + client, + "text", + query=query, + location=location, + radius=radius, + language=language, + min_price=min_price, + max_price=max_price, + open_now=open_now, + type=type, + region=region, + page_token=page_token, + ) + + +def places_nearby( + client, + location=None, + radius=None, + keyword=None, + language=None, + min_price=None, + max_price=None, + name=None, + open_now=False, + rank_by=None, + type=None, + page_token=None, +): """ Performs nearby search for places. @@ -240,21 +321,49 @@ def places_nearby(client, location=None, radius=None, keyword=None, raise ValueError("either a location or page_token arg is required") if rank_by == "distance": if not (keyword or name or type): - raise ValueError("either a keyword, name, or type arg is required " - "when rank_by is set to distance") + raise ValueError( + "either a keyword, name, or type arg is required " + "when rank_by is set to distance" + ) elif radius is not None: - raise ValueError("radius cannot be specified when rank_by is set to " - "distance") - - return _places(client, "nearby", location=location, radius=radius, - keyword=keyword, language=language, min_price=min_price, - max_price=max_price, name=name, open_now=open_now, - rank_by=rank_by, type=type, page_token=page_token) - - -def _places(client, url_part, query=None, location=None, radius=None, - keyword=None, language=None, min_price=0, max_price=4, name=None, - open_now=False, rank_by=None, type=None, region=None, page_token=None): + raise ValueError( + "radius cannot be specified when rank_by is set to " "distance" + ) + + return _places( + client, + "nearby", + location=location, + radius=radius, + keyword=keyword, + language=language, + min_price=min_price, + max_price=max_price, + name=name, + open_now=open_now, + rank_by=rank_by, + type=type, + page_token=page_token, + ) + + +def _places( + client, + url_part, + query=None, + location=None, + radius=None, + keyword=None, + language=None, + min_price=0, + max_price=4, + name=None, + open_now=False, + rank_by=None, + type=None, + region=None, + page_token=None, +): """ Internal handler for ``places`` and ``places_nearby``. See each method's docs for arg details. @@ -318,11 +427,12 @@ def place(client, place_id, session_token=None, fields=None, language=None): if fields: invalid_fields = set(fields) - PLACES_DETAIL_FIELDS if invalid_fields: - raise ValueError("Valid values for the `fields` param for " - "`place` are '%s', these given field(s) " - "are invalid: '%s'" % ( - "', '".join(PLACES_DETAIL_FIELDS), - "', '".join(invalid_fields))) + raise ValueError( + "Valid values for the `fields` param for " + "`place` are '%s', these given field(s) " + "are invalid: '%s'" + % ("', '".join(PLACES_DETAIL_FIELDS), "', '".join(invalid_fields)) + ) params["fields"] = convert.join_list(",", fields) if language: @@ -372,15 +482,27 @@ def places_photo(client, photo_reference, max_width=None, max_height=None): # "extract_body" and "stream" args here are used to return an iterable # response containing the image file data, rather than converting from # json. - response = client._request("/maps/api/place/photo", params, - extract_body=lambda response: response, - requests_kwargs={"stream": True}) + response = client._request( + "/maps/api/place/photo", + params, + extract_body=lambda response: response, + requests_kwargs={"stream": True}, + ) return response.iter_content() -def places_autocomplete(client, input_text, session_token=None, offset=None, - location=None, radius=None, language=None, types=None, - components=None, strict_bounds=False): +def places_autocomplete( + client, + input_text, + session_token=None, + offset=None, + location=None, + radius=None, + language=None, + types=None, + components=None, + strict_bounds=False, +): """ Returns Place predictions given a textual search string and optional geographic bounds. @@ -425,14 +547,24 @@ def places_autocomplete(client, input_text, session_token=None, offset=None, :rtype: list of predictions """ - return _autocomplete(client, "", input_text, session_token=session_token, - offset=offset, location=location, radius=radius, - language=language, types=types, components=components, - strict_bounds=strict_bounds) - - -def places_autocomplete_query(client, input_text, offset=None, location=None, - radius=None, language=None): + return _autocomplete( + client, + "", + input_text, + session_token=session_token, + offset=offset, + location=location, + radius=radius, + language=language, + types=types, + components=components, + strict_bounds=strict_bounds, + ) + + +def places_autocomplete_query( + client, input_text, offset=None, location=None, radius=None, language=None +): """ Returns Place predictions given a textual search query, such as "pizza near New York", and optional geographic bounds. @@ -457,13 +589,30 @@ def places_autocomplete_query(client, input_text, offset=None, location=None, :rtype: list of predictions """ - return _autocomplete(client, "query", input_text, offset=offset, - location=location, radius=radius, language=language) - - -def _autocomplete(client, url_part, input_text, session_token=None, - offset=None, location=None, radius=None, language=None, - types=None, components=None, strict_bounds=False): + return _autocomplete( + client, + "query", + input_text, + offset=offset, + location=location, + radius=radius, + language=language, + ) + + +def _autocomplete( + client, + url_part, + input_text, + session_token=None, + offset=None, + location=None, + radius=None, + language=None, + types=None, + components=None, + strict_bounds=False, +): """ Internal handler for ``autocomplete`` and ``autocomplete_query``. See each method's docs for arg details. diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index 17bf03e8..f6fa9f8a 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -47,14 +47,14 @@ def test_places_find(self): status=200, content_type='application/json') self.client.find_place('restaurant', 'textquery', - fields=['geometry', 'id'], + fields=['geometry/location', 'id'], location_bias='point:90,90', language=self.language) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&inputtype=textquery&' 'locationbias=point:90,90&input=restaurant' - '&fields=geometry,id&key=%s' + '&fields=geometry/location,id&key=%s' % (url, self.key), responses.calls[0].request.url) with self.assertRaises(ValueError): @@ -119,11 +119,11 @@ def test_place_detail(self): status=200, content_type='application/json') self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', - fields=['geometry', 'id'], language=self.language) + fields=['geometry/location', 'id'], language=self.language) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4' - '&key=%s&fields=geometry,id' + '&key=%s&fields=geometry/location,id' % (url, self.key), responses.calls[0].request.url) with self.assertRaises(ValueError): From 5817524ca256066e0d1eb5f48855b9f36d309874 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 9 Sep 2019 11:12:06 -0700 Subject: [PATCH 049/166] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ea6a96..0223b95b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. ### Changed ### Added - Tests for distribution tar as part of CI +- Support for subfields such as `geometry/location` or `geometry/viewport` in Places. + ### Removed ## [v3.1.1] From 32a6a9846f860f0d69fabab7a66afdf7265750be Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 11 Sep 2019 14:15:47 -0700 Subject: [PATCH 050/166] set version to 3.1.2 (#315) --- CHANGELOG.md | 11 +++++++---- setup.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0223b95b..92a9f4f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Changed ### Added -- Tests for distribution tar as part of CI -- Support for subfields such as `geometry/location` or `geometry/viewport` in Places. - ### Removed +## [v3.1.2] +### Added +- Tests for distribution tar as part of CI +- Support for subfields such as `geometry/location` and `geometry/viewport` in Places. + ## [v3.1.1] ### Changed - Added changelog to manifest @@ -31,7 +33,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.1...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.2...HEAD +[v3.1.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.1...3.1.2 [v3.1.1]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.0...3.1.1 [v3.1.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.2...3.1.0 [v3.0.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.1...3.0.2 diff --git a/setup.py b/setup.py index 76ffcc9f..812e43f2 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ setup( name="googlemaps", - version="3.1.1", + version="3.1.2", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From e10f05d8e1b73ddd920b4eeba74d471039c04d30 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 11 Sep 2019 15:16:50 -0700 Subject: [PATCH 051/166] travis conditional: tag to tags (#316) --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e1c65e95..75d8e4c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,10 +24,8 @@ script: deploy: on: - repo: googlemaps/google-maps-services-python - tag: true + tags: true python: '3.6' # only run this deploy once with python 3.6 - branch: env(tag) # branch is equal to tag name provider: pypi distributions: 'sdist bdist_wheel' user: __token__ # api token encrypted within travis From cdf75c73f7a5d21b25049e4de7a99ea69b03be57 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 20 Sep 2019 09:42:57 -0700 Subject: [PATCH 052/166] add deprecation warning for alt_id, id, reference, and scope (#319) --- CHANGELOG.md | 5 ++++- googlemaps/places.py | 23 ++++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92a9f4f3..6ae61f5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,11 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Changed +- deprecation warning for place fields: `alt_id`, `id`, `reference`, and `scope`. Read more about this at https://developers.google.com/maps/deprecations. + +## [v3.1.2] ### Added -### Removed +- Tests for distribution tar as part of CI ## [v3.1.2] ### Added diff --git a/googlemaps/places.py b/googlemaps/places.py index a77f2d85..87110cc1 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -16,6 +16,7 @@ # """Performs requests to the Google Places API.""" +import warnings from googlemaps import convert @@ -35,13 +36,13 @@ "geometry/viewport/southwest/lat", "geometry/viewport/southwest/lng", "icon", - "id", + "id", # deprecated: https://developers.google.com/maps/deprecations "name", "permanently_closed", "photos", "place_id", "plus_code", - "scope", + "scope", # deprecated: https://developers.google.com/maps/deprecations "types", ] ) @@ -60,7 +61,7 @@ [ "address_component", "adr_address", - "alt_id", + "alt_id", # deprecated: https://developers.google.com/maps/deprecations "formatted_address", "geometry", "geometry/location", @@ -74,13 +75,13 @@ "geometry/viewport/southwest/lat", "geometry/viewport/southwest/lng", "icon", - "id", + "id", # deprecated: https://developers.google.com/maps/deprecations "name", "permanently_closed", "photo", "place_id", "plus_code", - "scope", + "scope", # deprecated: https://developers.google.com/maps/deprecations "type", "url", "utc_offset", @@ -102,6 +103,11 @@ ^ PLACES_DETAIL_FIELDS_ATMOSPHERE ) +DEPRECATED_FIELDS = {"alt_id", "id", "reference", "scope"} +DEPRECATED_FIELDS_MESSAGE = ( + "Fields, %s, are deprecated. " + "Read more at https://developers.google.com/maps/deprecations." +) def find_place( client, input, input_type, fields=None, location_bias=None, language=None @@ -147,6 +153,13 @@ def find_place( ) if fields: + deprecated_fields = set(fields) & DEPRECATED_FIELDS + if deprecated_fields: + warnings.warn( + DEPRECATED_FIELDS_MESSAGE % str(list(deprecated_fields)), + DeprecationWarning + ) + invalid_fields = set(fields) - PLACES_FIND_FIELDS if invalid_fields: raise ValueError( From c5e450bc1fc38e0fd607ff6befec7c51812c8ace Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 20 Sep 2019 09:57:30 -0700 Subject: [PATCH 053/166] release 3.1.3 (#320) --- CHANGELOG.md | 8 +++----- setup.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ae61f5f..4c322577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [v3.1.3] ### Changed - deprecation warning for place fields: `alt_id`, `id`, `reference`, and `scope`. Read more about this at https://developers.google.com/maps/deprecations. -## [v3.1.2] -### Added -- Tests for distribution tar as part of CI - ## [v3.1.2] ### Added - Tests for distribution tar as part of CI @@ -36,7 +33,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.2...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.3...HEAD +[v3.1.3]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.2...3.1.3 [v3.1.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.1...3.1.2 [v3.1.1]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.0...3.1.1 [v3.1.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.0.2...3.1.0 diff --git a/setup.py b/setup.py index 812e43f2..9a901068 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ setup( name="googlemaps", - version="3.1.2", + version="3.1.3", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 4b1721f09bbf1bc6cd10610306e017bb995bd416 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 2 Oct 2019 23:01:22 -0700 Subject: [PATCH 054/166] add stale config --- .github/stale.yml | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..3adea285 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,59 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 30 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 30 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: [] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - pinned + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: "status: will not fix" + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > +# Your comment here. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 10 + +# Limit to only `issues` or `pulls` +# only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed From 704cbb4a65fc61a0dfcd010d7e42df7ad3de2949 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 2 Oct 2019 23:17:48 -0700 Subject: [PATCH 055/166] modify stale config --- .github/stale.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 3adea285..aadf0f18 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -24,21 +24,21 @@ exemptMilestones: false exemptAssignees: false # Label to use when marking as stale -staleLabel: "status: will not fix" +staleLabel: "stale" # Comment to post when marking as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. + recent activity. Please comment here if it is still valid so that we can + reprioritize. Thank you! # Comment to post when removing the stale label. # unmarkComment: > # Your comment here. # Comment to post when closing a stale Issue or Pull Request. -# closeComment: > -# Your comment here. +closeComment: > + Closing this. Please reopen if you believe it should be addressed. Thank you for your contribution. # Limit the number of actions per hour, from 1-30. Default is 30 limitPerRun: 10 From 866bb21c2dda761bb54aed115d82b01951f08d0f Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 2 Oct 2019 23:34:49 -0700 Subject: [PATCH 056/166] modify stale config --- .github/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index aadf0f18..876e12a7 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 30 +daysUntilStale: 90 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. From a3fe2deb7823671e8b48a065637014fb94fee1e0 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 2 Oct 2019 23:53:43 -0700 Subject: [PATCH 057/166] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 4 +++- .github/ISSUE_TEMPLATE/feature_request.md | 4 +++- .github/ISSUE_TEMPLATE/support_request.md | 8 ++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1539cc67..4782add5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,9 @@ --- name: Bug report about: Create a report to help us improve -label: 'type: bug, triage me' +title: '' +labels: 'type: bug, triage me' +assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 557f2315..39c3c5ab 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,9 @@ --- name: Feature request about: Suggest an idea for this library -label: 'type: feature request, triage me' +title: '' +labels: 'type: feature request, triage me' +assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index f8cade51..495a5c99 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -1,9 +1,13 @@ --- name: Support request -about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. -label: 'triage me, type: question' +about: If you have a support contract with Google, please create an issue in the Google + Cloud Support console. +title: '' +labels: 'triage me, type: question' +assignees: '' --- + **PLEASE READ** If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/). This will ensure a timely response. From 8cff8e5b1ca9724c72c955345524552c48eca3a6 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 14 Oct 2019 11:22:02 -0600 Subject: [PATCH 058/166] add pr templates --- .github/PULL_REQUEST_TEMPLATE/pull_request_template.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 00000000..2bbfe499 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,6 @@ +--- +name: Pull request +about: Create a pull request +label: 'triage me' + +--- From 2947d6123fa94cae9cd27ba931c1ac600c974e6f Mon Sep 17 00:00:00 2001 From: David Robles Date: Fri, 15 Nov 2019 15:31:17 -0800 Subject: [PATCH 059/166] fix: APIError.__str__ should always return a str (#328) --- googlemaps/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/exceptions.py b/googlemaps/exceptions.py index 679b26c3..0a0f116a 100644 --- a/googlemaps/exceptions.py +++ b/googlemaps/exceptions.py @@ -27,7 +27,7 @@ def __init__(self, status, message=None): def __str__(self): if self.message is None: - return self.status + return str(self.status) else: return "%s (%s)" % (self.status, self.message) From 147e7dc5f2fe4ca530b5be4e9abb86f65421e38a Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 15 Nov 2019 16:39:04 -0700 Subject: [PATCH 060/166] chore: release v3.1.4 (#329) --- CHANGELOG.md | 7 ++++++- setup.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c322577..3f4d9f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [v3.1.4] +### Changed +- `APIError.__str__` should always return a str (#328) + ## [v3.1.3] ### Changed - deprecation warning for place fields: `alt_id`, `id`, `reference`, and `scope`. Read more about this at https://developers.google.com/maps/deprecations. @@ -33,7 +37,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.3...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.4...HEAD +[v3.1.4]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.3...3.1.4 [v3.1.3]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.2...3.1.3 [v3.1.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.1...3.1.2 [v3.1.1]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.0...3.1.1 diff --git a/setup.py b/setup.py index 9a901068..4fc840b2 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ setup( name="googlemaps", - version="3.1.3", + version="3.1.4", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 81640b0a76fb741f228996f260a05c6e4a2cb27c Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 19 Dec 2019 14:22:58 -0600 Subject: [PATCH 061/166] fix: remove deprecated place fields (#332) --- CHANGELOG.md | 3 +++ googlemaps/places.py | 20 +------------------- googlemaps/test/test_places.py | 8 ++++---- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f4d9f0e..8e3e5558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Removed +- Removed place fields: `alt_id`, `id`, `reference`, and `scope`. Read more about this at https://developers.google.com/maps/deprecations. + ## [v3.1.4] ### Changed - `APIError.__str__` should always return a str (#328) diff --git a/googlemaps/places.py b/googlemaps/places.py index 87110cc1..eb748b35 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -36,13 +36,11 @@ "geometry/viewport/southwest/lat", "geometry/viewport/southwest/lng", "icon", - "id", # deprecated: https://developers.google.com/maps/deprecations "name", "permanently_closed", "photos", "place_id", "plus_code", - "scope", # deprecated: https://developers.google.com/maps/deprecations "types", ] ) @@ -61,7 +59,6 @@ [ "address_component", "adr_address", - "alt_id", # deprecated: https://developers.google.com/maps/deprecations "formatted_address", "geometry", "geometry/location", @@ -75,13 +72,11 @@ "geometry/viewport/southwest/lat", "geometry/viewport/southwest/lng", "icon", - "id", # deprecated: https://developers.google.com/maps/deprecations "name", "permanently_closed", "photo", "place_id", "plus_code", - "scope", # deprecated: https://developers.google.com/maps/deprecations "type", "url", "utc_offset", @@ -103,12 +98,6 @@ ^ PLACES_DETAIL_FIELDS_ATMOSPHERE ) -DEPRECATED_FIELDS = {"alt_id", "id", "reference", "scope"} -DEPRECATED_FIELDS_MESSAGE = ( - "Fields, %s, are deprecated. " - "Read more at https://developers.google.com/maps/deprecations." -) - def find_place( client, input, input_type, fields=None, location_bias=None, language=None ): @@ -152,14 +141,7 @@ def find_place( "the given value is invalid: '%s'" % input_type ) - if fields: - deprecated_fields = set(fields) & DEPRECATED_FIELDS - if deprecated_fields: - warnings.warn( - DEPRECATED_FIELDS_MESSAGE % str(list(deprecated_fields)), - DeprecationWarning - ) - + if fields: invalid_fields = set(fields) - PLACES_FIND_FIELDS if invalid_fields: raise ValueError( diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index f6fa9f8a..a21cd8ee 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -47,14 +47,14 @@ def test_places_find(self): status=200, content_type='application/json') self.client.find_place('restaurant', 'textquery', - fields=['geometry/location', 'id'], + fields=['geometry/location', 'place_id'], location_bias='point:90,90', language=self.language) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&inputtype=textquery&' 'locationbias=point:90,90&input=restaurant' - '&fields=geometry/location,id&key=%s' + '&fields=geometry/location,place_id&key=%s' % (url, self.key), responses.calls[0].request.url) with self.assertRaises(ValueError): @@ -119,11 +119,11 @@ def test_place_detail(self): status=200, content_type='application/json') self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', - fields=['geometry/location', 'id'], language=self.language) + fields=['geometry/location', 'place_id'], language=self.language) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4' - '&key=%s&fields=geometry/location,id' + '&key=%s&fields=geometry/location,place_id' % (url, self.key), responses.calls[0].request.url) with self.assertRaises(ValueError): From 19f6e53dc2ede5852bcb1ae3df9941b6d1e81136 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 19 Dec 2019 14:37:26 -0600 Subject: [PATCH 062/166] build: test and build for python 3 only (#333) --- .travis.yml | 6 +++--- CHANGELOG.md | 3 ++- noxfile.py | 2 +- setup.py | 8 +------- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75d8e4c4..dc90fb60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,6 @@ dist: xenial matrix: include: - - python: '2.7' - env: NOXSESSION="tests-2.7" - python: '3.5' env: NOXSESSION="tests-3.5" - python: '3.6' @@ -12,7 +10,9 @@ matrix: - python: '3.7' env: NOXSESSION="tests-3.7" sudo: required # required for Python 3.7 (github.com/travis-ci/travis-ci#9069) - - python: '2.7' + - python: '3.8' + env: NOXSESSION="tests-3.8" + - python: '3.6' env: NOXSESSION="docs" install: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e3e5558..40a8a3c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -### Removed +### Changed +- Python 2 is no longer supported - Removed place fields: `alt_id`, `id`, `reference`, and `scope`. Read more about this at https://developers.google.com/maps/deprecations. ## [v3.1.4] diff --git a/noxfile.py b/noxfile.py index e6fa230a..088ce82c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,6 +1,6 @@ import nox -SUPPORTED_PY_VERSIONS = ["2.7", "3.5", "3.6", "3.7"] +SUPPORTED_PY_VERSIONS = ["3.5", "3.6", "3.7", "3.8"] def _install_dev_packages(session): diff --git a/setup.py b/setup.py index 4fc840b2..d5880608 100644 --- a/setup.py +++ b/setup.py @@ -3,12 +3,6 @@ from setuptools import setup -if sys.version_info <= (2, 4): - error = "Requires Python Version 2.5 or above... exiting." - print >>sys.stderr, error - sys.exit(1) - - requirements = ["requests>=2.20.0,<3.0"] # use io.open until python2.7 support is dropped @@ -38,10 +32,10 @@ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Topic :: Internet", ], ) From 06f3a1473f6345bf953d7e0e3e7ed9b4ad30675f Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 19 Dec 2019 16:29:15 -0600 Subject: [PATCH 063/166] chore(release): 4.0.0 (#334) --- CHANGELOG.md | 2 +- googlemaps/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a8a3c7..2ace6be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog All notable changes to this project will be documented in this file. -## [Unreleased] +## [v4.0.0] ### Changed - Python 2 is no longer supported - Removed place fields: `alt_id`, `id`, `reference`, and `scope`. Read more about this at https://developers.google.com/maps/deprecations. diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 1c94fae5..89660007 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "3.0.2" +__version__ = "4.0.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index d5880608..3f17dd13 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="googlemaps", - version="3.1.4", + version="4.0.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 3a871b06126ed23b13ae710ad63da499b4e55287 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 19 Dec 2019 15:30:47 -0700 Subject: [PATCH 064/166] docs: fix changelog for v4.0.0 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ace6be4..b2ca9736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.4...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/4.0.0...HEAD +[v4.0.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.4...4.0.0 [v3.1.4]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.3...3.1.4 [v3.1.3]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.2...3.1.3 [v3.1.2]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.1...3.1.2 From 94c757e28e631fbbe198eb838a8ab55ea8d9dc37 Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Mon, 23 Dec 2019 17:11:12 -0800 Subject: [PATCH 065/166] style: Update pull request template. (#335) * style: Update pull request template. * Add back name, about and label. --- .github/PULL_REQUEST_TEMPLATE/pull_request_template.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md index 2bbfe499..4d7d59e9 100644 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -2,5 +2,11 @@ name: Pull request about: Create a pull request label: 'triage me' - --- +Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: +- [ ] Make sure to open a GitHub issue as a bug/feature request before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea +- [ ] Ensure the tests and linter pass +- [ ] Code coverage does not decrease (if any source code was changed) +- [ ] Appropriate docs were updated (if necessary) + +Fixes # 🦕 From e3dcfc3076ca41dc86f1c916cf467aa9bc3df408 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 8 Jan 2020 14:10:20 -0800 Subject: [PATCH 066/166] fix: increase stale bot window --- .github/stale.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 876e12a7..8ed0e080 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,11 +1,11 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 90 +daysUntilStale: 120 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 30 +daysUntilClose: 180 # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) onlyLabels: [] @@ -13,6 +13,7 @@ onlyLabels: [] # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable exemptLabels: - pinned + - "type: bug" # Set to true to ignore issues in a project (defaults to false) exemptProjects: false @@ -44,7 +45,7 @@ closeComment: > limitPerRun: 10 # Limit to only `issues` or `pulls` -# only: issues +only: issues # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': # pulls: From 2d6d4a9b1733860daacf421e71e894a1123e8e3a Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Tue, 21 Jan 2020 09:52:02 -0800 Subject: [PATCH 067/166] feat: Adding experience_id support to Client class. (#338) * feat: Adding experience_id support to Client class. * Writing tests. * Adding sample tags. * Use underscore. --- .gitignore | 1 + googlemaps/client.py | 47 +++++++++++++++++-- googlemaps/test/test_client.py | 83 ++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0d2efbbc..c4477ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ env googlemaps.egg-info *.egg .vscode/ +.idea/ diff --git a/googlemaps/client.py b/googlemaps/client.py index 9a195c3c..ac054917 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -39,12 +39,13 @@ except ImportError: # Python 2 from urllib import urlencode - +_X_GOOG_MAPS_EXPERIENCE_ID = "X-Goog-Maps-Experience-ID" _USER_AGENT = "GoogleGeoApiClientPython/%s" % googlemaps.__version__ _DEFAULT_BASE_URL = "https://maps.googleapis.com" _RETRIABLE_STATUSES = set([500, 503, 504]) + class Client(object): """Performs requests to the Google Maps API web services.""" @@ -52,7 +53,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, timeout=None, connect_timeout=None, read_timeout=None, retry_timeout=60, requests_kwargs=None, queries_per_second=50, channel=None, - retry_over_query_limit=True): + retry_over_query_limit=True, experience_id=None): """ :param key: Maps API key. Required, unless "client_id" and "client_secret" are set. @@ -99,6 +100,10 @@ def __init__(self, key=None, client_id=None, client_secret=None, retried. Defaults to True. :type retry_over_query_limit: bool + :param experience_id: The value for the HTTP header field name + 'X-Goog-Maps-Experience-ID'. + :type experience_id: str + :raises ValueError: when either credentials are missing, incomplete or invalid. :raises NotImplementedError: if connect_timeout and read_timeout are @@ -150,7 +155,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.retry_timeout = timedelta(seconds=retry_timeout) self.requests_kwargs = requests_kwargs or {} headers = self.requests_kwargs.pop('headers', {}) - headers.update({"User-Agent": _USER_AGENT}) + headers.update({"User-Agent": _USER_AGENT}) self.requests_kwargs.update({ "headers": headers, "timeout": self.timeout, @@ -160,6 +165,42 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.queries_per_second = queries_per_second self.retry_over_query_limit = retry_over_query_limit self.sent_times = collections.deque("", queries_per_second) + self.set_experience_id(experience_id) + + def set_experience_id(self, *experience_id_args): + """Sets the value for the HTTP header field name + 'X-Goog-Maps-Experience-ID' to be used on subsequent API calls. + + :param experience_id_args: the experience ID + :type experience_id_args: string varargs + """ + if len(experience_id_args) == 0 or experience_id_args[0] is None: + self.clear_experience_id() + return + + headers = self.requests_kwargs.pop("headers", {}) + headers[_X_GOOG_MAPS_EXPERIENCE_ID] = ",".join(experience_id_args) + self.requests_kwargs["headers"] = headers + + def get_experience_id(self): + """Gets the experience ID for the HTTP header field name + 'X-Goog-Maps-Experience-ID' + + :return: The experience ID if set + :rtype: str + """ + headers = self.requests_kwargs.get("headers", {}) + return headers.get(_X_GOOG_MAPS_EXPERIENCE_ID, None) + + def clear_experience_id(self): + """Clears the experience ID for the HTTP header field name + 'X-Goog-Maps-Experience-ID' if set. + """ + headers = self.requests_kwargs.get("headers") + if headers is None: + return + headers.pop(_X_GOOG_MAPS_EXPERIENCE_ID, {}) + self.requests_kwargs["headers"] = headers def _request(self, url, params, first_request_time=None, retry_counter=0, base_url=_DEFAULT_BASE_URL, accepts_clientid=True, diff --git a/googlemaps/test/test_client.py b/googlemaps/test/test_client.py index 90d83b3e..9c689b67 100644 --- a/googlemaps/test/test_client.py +++ b/googlemaps/test/test_client.py @@ -22,10 +22,12 @@ import responses import requests +import uuid import googlemaps import googlemaps.client as _client import googlemaps.test as _test +from googlemaps.client import _X_GOOG_MAPS_EXPERIENCE_ID class ClientTest(_test.TestCase): @@ -291,6 +293,87 @@ def test_requests_version(self): googlemaps.Client(**client_args_timeout) googlemaps.Client(**client_args) + def test_single_experience_id(self): + experience_id1 = "Exp1" + client = googlemaps.Client(key="AIzaasdf", experience_id=experience_id1) + self.assertEqual(experience_id1, client.get_experience_id()) + + experience_id2 = "Exp2" + client.set_experience_id(experience_id2) + self.assertEqual(experience_id2, client.get_experience_id()) + + def test_multiple_experience_id(self): + client = googlemaps.Client(key="AIzaasdf") + + experience_id1 = "Exp1" + experience_id2 = "Exp2" + client.set_experience_id(experience_id1, experience_id2) + + result = "%s,%s" % (experience_id1, experience_id2) + self.assertEqual(result, client.get_experience_id()) + + def test_no_experience_id(self): + client = googlemaps.Client(key="AIzaasdf") + self.assertIsNone(client.get_experience_id()) + + def test_clearing_experience_id(self): + client = googlemaps.Client(key="AIzaasdf") + client.set_experience_id("ExpId") + client.clear_experience_id() + self.assertIsNone(client.get_experience_id()) + + def test_experience_id_sample(self): + # [START maps_experience_id] + experience_id = str(uuid.uuid4()) + + # instantiate client with experience id + client = googlemaps.Client( + key="AIza-Maps-API-Key", + experience_id=experience_id + ) + + # clear the current experience id + client.clear_experience_id() + + # set a new experience id + other_experience_id = str(uuid.uuid4()) + client.set_experience_id(experience_id, other_experience_id) + + # make API request, the client will set the header + # X-GOOG-MAPS-EXPERIENCE-ID: experience_id,other_experience_id + + # get current experience id + ids = client.get_experience_id() + # [END maps_experience_id] + + result = "%s,%s" % (experience_id, other_experience_id) + self.assertEqual(result, ids) + + @responses.activate + def _perform_mock_request(self, experience_id=None): + # Mock response + responses.add(responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json") + + # Perform network call + client = googlemaps.Client(key="AIzaasdf") + client.set_experience_id(experience_id) + client.geocode("Sesame St.") + return responses.calls[0].request + + def test_experience_id_in_header(self): + experience_id = "Exp1" + request = self._perform_mock_request(experience_id) + header_value = request.headers[_X_GOOG_MAPS_EXPERIENCE_ID] + self.assertEqual(experience_id, header_value) + + def test_experience_id_no_in_header(self): + request = self._perform_mock_request() + self.assertIsNone(request.headers.get(_X_GOOG_MAPS_EXPERIENCE_ID)) + @responses.activate def test_no_retry_over_query_limit(self): responses.add(responses.GET, From 700cfeca112d43700697db04a8f981903e9ff222 Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Tue, 21 Jan 2020 18:04:01 -0800 Subject: [PATCH 068/166] chore(release): 4.1.0 (#339) --- CHANGELOG.md | 7 ++++++- googlemaps/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2ca9736..ea74f8c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## [v4.1.0] +### Added +- Adding support for passing in `experience_id` to Client class (#338) + ## [v4.0.0] ### Changed - Python 2 is no longer supported @@ -41,7 +45,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/4.0.0...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/4.1.0...HEAD +[v4.1.0]: https://github.com/googlemaps/google-maps-services-python/compare/4.0.0...4.1.0 [v4.0.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.4...4.0.0 [v3.1.4]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.3...3.1.4 [v3.1.3]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.2...3.1.3 diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 89660007..8061142a 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.0.0" +__version__ = "4.1.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 3f17dd13..fb616993 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="googlemaps", - version="4.0.0", + version="4.1.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 7f70f0c0f32e0cbbdd7c1e1ccbbbe60c1c473fc3 Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Fri, 24 Jan 2020 14:06:37 -0800 Subject: [PATCH 069/166] docs(Template): update location of PR template (#340) --- .github/pull_request_template.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..009707d4 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +Thank you for opening a Pull Request! + +--- + +Before submitting your PR, there are a few things you can do to make sure it goes smoothly: +- [ ] Make sure to open a GitHub issue as a bug/feature request before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea +- [ ] Ensure the tests and linter pass +- [ ] Code coverage does not decrease (if any source code was changed) +- [ ] Appropriate docs were updated (if necessary) + +Fixes # 🦕 From 034411ec862b9234767176a8838794f865f532fb Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Thu, 30 Jan 2020 14:25:02 -0800 Subject: [PATCH 070/166] docs(Code of Conduct): adding CODE_OF_CONDUCT.md file. (#341) --- CODE_OF_CONDUCT.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..f8b12cb5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,63 @@ +# Google Open Source Community Guidelines + +At Google, we recognize and celebrate the creativity and collaboration of open +source contributors and the diversity of skills, experiences, cultures, and +opinions they bring to the projects and communities they participate in. + +Every one of Google's open source projects and communities are inclusive +environments, based on treating all individuals respectfully, regardless of +gender identity and expression, sexual orientation, disabilities, +neurodiversity, physical appearance, body size, ethnicity, nationality, race, +age, religion, or similar personal characteristic. + +We value diverse opinions, but we value respectful behavior more. + +Respectful behavior includes: + +* Being considerate, kind, constructive, and helpful. +* Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or + physically threatening behavior, speech, and imagery. +* Not engaging in unwanted physical contact. + +Some Google open source projects [may adopt][] an explicit project code of +conduct, which may have additional detailed expectations for participants. Most +of those projects will use our [modified Contributor Covenant][]. + +[may adopt]: https://opensource.google/docs/releasing/preparing/#conduct +[modified Contributor Covenant]: https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/ + +## Resolve peacefully + +We do not believe that all conflict is necessarily bad; healthy debate and +disagreement often yields positive results. However, it is never okay to be +disrespectful. + +If you see someone behaving disrespectfully, you are encouraged to address the +behavior directly with those involved. Many issues can be resolved quickly and +easily, and this gives people more control over the outcome of their dispute. +If you are unable to resolve the matter for any reason, or if the behavior is +threatening or harassing, report it. We are dedicated to providing an +environment where participants feel welcome and safe. + +## Reporting problems + +Some Google open source projects may adopt a project-specific code of conduct. +In those cases, a Google employee will be identified as the Project Steward, +who will receive and handle reports of code of conduct violations. In the event +that a project hasn’t identified a Project Steward, you can report problems by +emailing opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is +taken. The identity of the reporter will be omitted from the details of the +report supplied to the accused. In potentially harmful situations, such as +ongoing harassment or threats to anyone's safety, we may take action without +notice. + +*This document was adapted from the [IndieWeb Code of Conduct][] and can also +be found at .* + +[IndieWeb Code of Conduct]: https://indieweb.org/code-of-conduct From 553e86109b2e5ab04f8ead91b0ec712d8341d03d Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 30 Jan 2020 15:40:29 -0800 Subject: [PATCH 071/166] docs: minimize mention of client id (#342) --- README.md | 29 ----------------------------- googlemaps/client.py | 5 +++-- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index fdc7d9b2..cb222b68 100644 --- a/README.md +++ b/README.md @@ -86,31 +86,6 @@ directions_result = gmaps.directions("Sydney Town Hall", departure_time=now) ``` -Below is the same example, using client ID and client secret (digital signature) -for authentication. This code assumes you have previously loaded the `client_id` -and `client_secret` variables with appropriate values. - -For a guide on how to generate the `client_secret` (digital signature), see the -documentation for the API you're using. For example, see the guide for the -[Directions API](https://developers.google.com/maps/documentation/directions/get-api-key#client-id). - -```python -gmaps = googlemaps.Client(client_id=client_id, client_secret=client_secret) - -# Geocoding and address -geocode_result = gmaps.geocode('1600 Amphitheatre Parkway, Mountain View, CA') - -# Look up an address with reverse geocoding -reverse_geocode_result = gmaps.reverse_geocode((40.714224, -73.961452)) - -# Request directions via public transit -now = datetime.now() -directions_result = gmaps.directions("Sydney Town Hall", - "Parramatta, NSW", - mode="transit", - departure_time=now) -``` - For more usage examples, check out [the tests](https://github.com/googlemaps/google-maps-services-python/tree/master/googlemaps/test). ## Features @@ -120,10 +95,6 @@ For more usage examples, check out [the tests](https://github.com/googlemaps/goo Automatically retry when intermittent failures occur. That is, when any of the retriable 5xx errors are returned from the API. -### Client IDs - -Google Maps APIs Premium Plan customers can use their client ID and secret to authenticate, -instead of an API key. ## Building the Project diff --git a/googlemaps/client.py b/googlemaps/client.py index ac054917..d1136ce4 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -56,14 +56,15 @@ def __init__(self, key=None, client_id=None, client_secret=None, retry_over_query_limit=True, experience_id=None): """ :param key: Maps API key. Required, unless "client_id" and - "client_secret" are set. + "client_secret" are set. Most users should use an API key. :type key: string :param client_id: (for Maps API for Work customers) Your client ID. + Most users should use an API key instead. :type client_id: string :param client_secret: (for Maps API for Work customers) Your client - secret (base64 encoded). + secret (base64 encoded). Most users should use an API key instead. :type client_secret: string :param channel: (for Maps API for Work customers) When set, a channel From 06754323575d6c157ec1ba72f8429bf74344f29e Mon Sep 17 00:00:00 2001 From: romavlasov Date: Wed, 12 Feb 2020 17:23:44 +0300 Subject: [PATCH 072/166] feat: Add support of Maps Static API (#344) --- README.md | 2 + googlemaps/client.py | 17 ++- googlemaps/convert.py | 11 ++ googlemaps/maps.py | 252 ++++++++++++++++++++++++++++++++ googlemaps/test/test_convert.py | 8 + googlemaps/test/test_maps.py | 117 +++++++++++++++ 6 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 googlemaps/maps.py create mode 100644 googlemaps/test/test_maps.py diff --git a/README.md b/README.md index cb222b68..9f221f64 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ APIs: - Time Zone API - Roads API - Places API + - Maps Static API Keep in mind that the same [terms and conditions](https://developers.google.com/maps/terms) apply to usage of the APIs when they're accessed through this library. @@ -127,6 +128,7 @@ are returned from the API. - [Time Zone API](https://developers.google.com/maps/documentation/timezone/) - [Roads API](https://developers.google.com/maps/documentation/roads/) - [Places API](https://developers.google.com/places/) +- [Maps Static API](https://developers.google.com/maps/documentation/maps-static/) ### Support - [Report an issue](https://github.com/googlemaps/google-maps-services-python/issues) diff --git a/googlemaps/client.py b/googlemaps/client.py index d1136ce4..ae2b4891 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -390,6 +390,7 @@ def _generate_auth_url(self, path, params, accepts_clientid): from googlemaps.places import places_photo from googlemaps.places import places_autocomplete from googlemaps.places import places_autocomplete_query +from googlemaps.maps import static_map def make_api_method(func): @@ -433,6 +434,7 @@ def wrapper(*args, **kwargs): Client.places_photo = make_api_method(places_photo) Client.places_autocomplete = make_api_method(places_autocomplete) Client.places_autocomplete_query = make_api_method(places_autocomplete_query) +Client.static_map = make_api_method(static_map) def sign_hmac(secret, payload): @@ -463,11 +465,17 @@ def urlencode_params(params): """ # urlencode does not handle unicode strings in Python 2. # Firstly, normalize the values so they get encoded correctly. - params = [(key, normalize_for_urlencode(val)) for key, val in params] + extended = [] + for key, val in params: + if isinstance(val, (list, tuple)): + for v in val: + extended.append((key, normalize_for_urlencode(v))) + else: + extended.append((key, normalize_for_urlencode(val))) # Secondly, unquote unreserved chars which are incorrectly quoted # by urllib.urlencode, causing invalid auth signatures. See GH #72 # for more info. - return requests.utils.unquote_unreserved(urlencode(params)) + return requests.utils.unquote_unreserved(urlencode(extended)) try: @@ -489,4 +497,7 @@ def normalize_for_urlencode(value): def normalize_for_urlencode(value): """(Python 3) No-op.""" # urlencode in Python 3 handles all the types we are passing it. - return value + if isinstance(value, str): + return value + + return normalize_for_urlencode(str(value)) diff --git a/googlemaps/convert.py b/googlemaps/convert.py index b5823f67..602cf4d0 100644 --- a/googlemaps/convert.py +++ b/googlemaps/convert.py @@ -280,6 +280,17 @@ def bounds(arg): "but got %s" % type(arg).__name__) +def size(arg): + if isinstance(arg, int): + return "%sx%s" % (arg, arg) + elif _is_list(arg): + return "%sx%s" % (arg[0], arg[1]) + + raise TypeError( + "Expected a size int or list, " + "but got %s" % type(arg).__name__) + + def decode_polyline(polyline): """Decodes a Polyline string into a list of lat/lng dicts. diff --git a/googlemaps/maps.py b/googlemaps/maps.py new file mode 100644 index 00000000..eedcc422 --- /dev/null +++ b/googlemaps/maps.py @@ -0,0 +1,252 @@ +# +# Copyright 2020 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Performs requests to the Google Maps Static API.""" + +from googlemaps import convert + + +MAPS_IMAGE_FORMATS = set( + ['png8', 'png', 'png32', 'gif', 'jpg', 'jpg-baseline'] +) + +MAPS_MAP_TYPES = set( + ['roadmap', 'satellite', 'terrain', 'hybrid'] +) + + +class StaticMapParam(object): + """Base class to handle parameters for Maps Static API.""" + + def __init__(self): + self.params = [] + + def __str__(self): + """Converts a list of parameters to the format expected by + the Google Maps server. + + :rtype: str + + """ + return convert.join_list('|', self.params) + + +class StaticMapMarker(StaticMapParam): + """Handles marker parameters for Maps Static API.""" + + def __init__(self, locations, + size=None, color=None, label=None): + """ + :param locations: Specifies the locations of the markers on + the map. + :type locations: list + + :param size: Specifies the size of the marker. + :type size: str + + :param color: Specifies a color of the marker. + :type color: str + + :param label: Specifies a single uppercase alphanumeric + character to be displaied on marker. + :type label: str + """ + + super(StaticMapMarker, self).__init__() + + if size: + self.params.append("size:%s" % size) + + if color: + self.params.append("color:%s" % color) + + if label: + if len(label) != 1 or not label.isupper() or not label.isalnum(): + raise ValueError("Invalid label") + self.params.append("label:%s" % label) + + self.params.append(convert.location_list(locations)) + + +class StaticMapPath(StaticMapParam): + """Handles path parameters for Maps Static API.""" + + def __init__(self, points, + weight=None, color=None, + fillcolor=None, geodesic=None): + """ + :param points: Specifies the point through which the path + will be built. + :type points: list + + :param weight: Specifies the thickness of the path in pixels. + :type weight: int + + :param color: Specifies a color of the path. + :type color: str + + :param fillcolor: Indicates both that the path marks off a + polygonal area and specifies the fill color to use as an + overlay within that area. + :type fillcolor: str + + :param geodesic: Indicates that the requested path should be + interpreted as a geodesic line that follows the curvature + of the earth. + :type geodesic: bool + """ + + super(StaticMapPath, self).__init__() + + if weight: + self.params.append("weight:%s" % weight) + + if color: + self.params.append("color:%s" % color) + + if fillcolor: + self.params.append("fillcolor:%s" % fillcolor) + + if geodesic: + self.params.append("geodesic:%s" % geodesic) + + self.params.append(convert.location_list(points)) + + +def static_map(client, size, + center=None, zoom=None, scale=None, + format=None, maptype=None, language=None, region=None, + markers=None, path=None, visible=None, style=None): + """ + Downloads a map image from the Maps Static API. + + See https://developers.google.com/maps/documentation/maps-static/intro + for more info, including more detail for each parameter below. + + :param size: Defines the rectangular dimensions of the map image. + :type param: int or list + + :param center: Defines the center of the map, equidistant from all edges + of the map. + :type center: dict or list or string + + :param zoom: Defines the zoom level of the map, which determines the + magnification level of the map. + :type zoom: int + + :param scale: Affects the number of pixels that are returned. + :type scale: int + + :param format: Defines the format of the resulting image. + :type format: string + + :param maptype: defines the type of map to construct. There are several + possible maptype values, including roadmap, satellite, hybrid, + and terrain. + :type maptype: string + + :param language: defines the language to use for display of labels on + map tiles. + :type language: string + + :param region: defines the appropriate borders to display, based on + geo-political sensitivities. + :type region: string + + :param markers: define one or more markers to attach to the image at + specified locations. + :type markers: StaticMapMarker + + :param path: defines a single path of two or more connected points to + overlay on the image at specified locations. + :type path: StaticMapPath + + :param visible: specifies one or more locations that should remain visible + on the map, though no markers or other indicators will be displayed. + :type visible: list of dict + + :param style: defines a custom style to alter the presentation of + a specific feature (roads, parks, and other features) of the map. + :type style: list of dict + + :rtype: iterator containing the raw image data, which typically can be + used to save an image file locally. For example: + + ``` + f = open(local_filename, 'wb') + for chunk in client.static_map(size=(400, 400), + center=(52.520103, 13.404871), + zoom=15): + if chunk: + f.write(chunk) + f.close() + ``` + """ + + params = {"size": convert.size(size)} + + if not markers: + if not (center or zoom is not None): + raise ValueError( + "both center and zoom are required" + "when markers is not specifed" + ) + + if center: + params["center"] = convert.latlng(center) + + if zoom is not None: + params["zoom"] = zoom + + if scale is not None: + params["scale"] = scale + + if format: + if format not in MAPS_IMAGE_FORMATS: + raise ValueError("Invalid image format") + params['format'] = format + + if maptype: + if maptype not in MAPS_MAP_TYPES: + raise ValueError("Invalid maptype") + params["maptype"] = maptype + + if language: + params["language"] = language + + if region: + params["region"] = region + + if markers: + params["markers"] = markers + + if path: + params["path"] = path + + if visible: + params["visible"] = convert.location_list(visible) + + if style: + params["style"] = convert.components(style) + + response = client._request( + "/maps/api/staticmap", + params, + extract_body=lambda response: response, + requests_kwargs={"stream": True}, + ) + return response.iter_content() diff --git a/googlemaps/test/test_convert.py b/googlemaps/test/test_convert.py index 9cbde9ea..dc0fe2b1 100644 --- a/googlemaps/test/test_convert.py +++ b/googlemaps/test/test_convert.py @@ -113,6 +113,14 @@ def test_bounds(self): with self.assertRaises(TypeError): convert.bounds("test") + def test_size(self): + self.assertEqual("1x1", convert.size(1)) + + self.assertEqual("2x3", convert.size((2, 3))) + + with self.assertRaises(TypeError): + convert.size("test") + def test_polyline_decode(self): syd_mel_route = ("rvumEis{y[`NsfA~tAbF`bEj^h{@{KlfA~eA~`AbmEghAt~D|e@j" "lRpO~yH_\\v}LjbBh~FdvCxu@`nCplDbcBf_B|wBhIfhCnqEb~D~" diff --git a/googlemaps/test/test_maps.py b/googlemaps/test/test_maps.py new file mode 100644 index 00000000..a20b84fe --- /dev/null +++ b/googlemaps/test/test_maps.py @@ -0,0 +1,117 @@ +# +# Copyright 2020 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the maps module.""" + +from types import GeneratorType + +import responses + +import googlemaps +import googlemaps.test as _test + +from googlemaps.maps import StaticMapMarker +from googlemaps.maps import StaticMapPath + + +class MapsTest(_test.TestCase): + + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_static_map_marker(self): + marker = StaticMapMarker( + locations=[{"lat": -33.867486, "lng": 151.206990}, "Sydney"], + size='small', color='blue', label="S" + ) + + self.assertEqual( + "size:small|color:blue|label:S|" + "-33.867486,151.20699|Sydney", + str(marker) + ) + + with self.assertRaises(ValueError): + StaticMapMarker(locations=["Sydney"], label="XS") + + @responses.activate + def test_static_map_path(self): + path = StaticMapPath( + points=[{"lat": -33.867486, "lng": 151.206990}, "Sydney"], + weight=5, color="red", geodesic=True, fillcolor="Red" + ) + + self.assertEqual( + "weight:5|color:red|fillcolor:Red|""geodesic:True|" + "-33.867486,151.20699|Sydney", + str(path) + ) + + @responses.activate + def test_download(self): + url = 'https://maps.googleapis.com/maps/api/staticmap' + responses.add(responses.GET, url, status=200) + + path = StaticMapPath( + points=[(62.107733,-145.541936), 'Delta+Junction,AK'], + weight=5, color="red" + ) + + m1 = StaticMapMarker( + locations=[(62.107733,-145.541936)], + color="blue", label="S" + ) + + m2 = StaticMapMarker( + locations=['Delta+Junction,AK'], + size="tiny", color="green" + ) + + m3 = StaticMapMarker( + locations=["Tok,AK"], + size="mid", color="0xFFFF00", label="C" + ) + + response = self.client.static_map( + size=(400, 400), zoom=6, center=(63.259591,-144.667969), + maptype="hybrid", format="png", scale=2, visible=["Tok,AK"], + path=path, markers=[m1, m2, m3] + ) + + self.assertTrue(isinstance(response, GeneratorType)) + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + '%s?center=63.259591%%2C-144.667969&format=png&maptype=hybrid&' + 'markers=color%%3Ablue%%7Clabel%%3AS%%7C62.107733%%2C-145.541936&' + 'markers=size%%3Atiny%%7Ccolor%%3Agreen%%7CDelta%%2BJunction%%2CAK&' + 'markers=size%%3Amid%%7Ccolor%%3A0xFFFF00%%7Clabel%%3AC%%7CTok%%2CAK&' + 'path=weight%%3A5%%7Ccolor%%3Ared%%7C62.107733%%2C-145.541936%%7CDelta%%2BJunction%%2CAK&' + 'scale=2&size=400x400&visible=Tok%%2CAK&zoom=6&key=%s' + % (url, self.key), responses.calls[0].request.url) + + with self.assertRaises(ValueError): + self.client.static_map(size=(400, 400)) + + with self.assertRaises(ValueError): + self.client.static_map(size=(400, 400), center=(63.259591,-144.667969), + zoom=6, format='test') + + with self.assertRaises(ValueError): + self.client.static_map(size=(400, 400), center=(63.259591,-144.667969), + zoom=6, maptype='test') From e27988ecef8139f45c93a306a4b83834ebb35f37 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 12 Feb 2020 06:38:34 -0800 Subject: [PATCH 073/166] docs(README): update requirement to Python 3.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f221f64..88fb5fe5 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ contribute, please read contribute. ## Requirements - - Python 2.7 or later. + - Python 3.5 or later. - A Google Maps API key. ## API Keys From 3371e100791b017aa55a0c52b5b18d2aee3fea6d Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 12 Feb 2020 07:03:00 -0800 Subject: [PATCH 074/166] chore(release): 4.2.0 (#346) --- CHANGELOG.md | 7 ++++++- googlemaps/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea74f8c1..35deb20a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## [v4.2.0] +### Added +- Add support for Maps Static API (#344) + ## [v4.1.0] ### Added - Adding support for passing in `experience_id` to Client class (#338) @@ -45,7 +49,8 @@ All notable changes to this project will be documented in this file. **Note:** Start of changelog is 2019-08-27, [v3.0.2]. -[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/4.1.0...HEAD +[Unreleased]: https://github.com/googlemaps/google-maps-services-python/compare/4.2.0...HEAD +[v4.2.0]: https://github.com/googlemaps/google-maps-services-python/compare/4.1.0...4.2.0 [v4.1.0]: https://github.com/googlemaps/google-maps-services-python/compare/4.0.0...4.1.0 [v4.0.0]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.4...4.0.0 [v3.1.4]: https://github.com/googlemaps/google-maps-services-python/compare/3.1.3...3.1.4 diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 8061142a..d81cb6e9 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.1.0" +__version__ = "4.2.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index fb616993..b65cb9a8 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="googlemaps", - version="4.1.0", + version="4.2.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 6682591dda6f987b193bb9b3bdb8e9d50397d651 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 12 Feb 2020 07:10:08 -0800 Subject: [PATCH 075/166] docs(directions): add note about via in waypoints param closes #239. see https://developers.google.com/maps/documentation/directions/intro#Waypoints for more information. --- googlemaps/directions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/googlemaps/directions.py b/googlemaps/directions.py index f1713dfb..b38a4cdf 100644 --- a/googlemaps/directions.py +++ b/googlemaps/directions.py @@ -41,7 +41,9 @@ def directions(client, origin, destination, :type mode: string :param waypoints: Specifies an array of waypoints. Waypoints alter a - route by routing it through the specified location(s). + route by routing it through the specified location(s). To influence + route without adding stop prefix the waypoint with `via`, similar to + `waypoints = ["via:San Francisco", "via:Mountain View"]`. :type waypoints: a single location, or a list of locations, where a location is a string, dict, list, or tuple From c61a1e306f72bcbae365f3487c04d2368a22d30a Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 12 Feb 2020 10:02:19 -0800 Subject: [PATCH 076/166] fix: add python requires attribute to setup.py closes #345 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b65cb9a8..301cd743 100644 --- a/setup.py +++ b/setup.py @@ -38,4 +38,5 @@ "Programming Language :: Python :: 3.8", "Topic :: Internet", ], + python_requires='>=3.5' ) From 03a61f2f0bfe0d5604eefc64400166150f7c3688 Mon Sep 17 00:00:00 2001 From: FredaXin <48503813+FredaXin@users.noreply.github.com> Date: Mon, 24 Feb 2020 17:13:44 -0500 Subject: [PATCH 077/166] docs(README): added link to the github page doc (#347) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 88fb5fe5..2b6ad08d 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,9 @@ are returned from the API. $ nox -e docs && mv docs/_build/html generated_docs && git clean -Xdi && git checkout gh-pages ## Documentation & resources + +[Documentation for the `google-maps-services-python` library](https://googlemaps.github.io/google-maps-services-python/docs/index.html) + ### Getting started - [Get Started with Google Maps Platform](https://developers.google.com/maps/gmp-get-started) - [Generating/restricting an API key](https://developers.google.com/maps/gmp-get-started#api-key) From 9c5bec199a35d12016b7e088d278c5d0cb16e44c Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Tue, 24 Mar 2020 02:38:49 +0530 Subject: [PATCH 078/166] build: test and build docs with python3 (#351) --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 088ce82c..9f4024d4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,7 +36,7 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python="2.7") +@nox.session(python="3.6") def docs(session): _install_dev_packages(session) _install_doc_dependencies(session) From 7b5648bfaf1be2d951b6b761a2ed05246399435f Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Thu, 23 Apr 2020 16:45:21 -0700 Subject: [PATCH 079/166] build: Adding test, release, and publish workflows (#359) * build: Adding build and release workflows. * Adding .releaserc * Adding publish workflow. * Rename to test.yml * s/__version/__version__ * Remove travis. --- {.travis => .github/scripts}/distribution.sh | 0 {.travis => .github/scripts}/install.sh | 0 .github/workflows/publish.yml | 48 ++++++++++++++++++++ .github/workflows/release.yml | 37 +++++++++++++++ .github/workflows/test.yml | 46 +++++++++++++++++++ .releaserc | 22 +++++++++ .travis.yml | 35 -------------- noxfile.py | 2 +- 8 files changed, 154 insertions(+), 36 deletions(-) rename {.travis => .github/scripts}/distribution.sh (100%) rename {.travis => .github/scripts}/install.sh (100%) create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .releaserc delete mode 100644 .travis.yml diff --git a/.travis/distribution.sh b/.github/scripts/distribution.sh similarity index 100% rename from .travis/distribution.sh rename to .github/scripts/distribution.sh diff --git a/.travis/install.sh b/.github/scripts/install.sh similarity index 100% rename from .travis/install.sh rename to .github/scripts/install.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..a0ea83af --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,48 @@ +# Copyright 2020 Google LLC +# +# 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. + +# A workflow that pushes artifacts to Sonatype +name: Publish + +on: + push: + tags: + - '*' + repository_dispatch: + types: [publish] + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Python + uses: "actions/setup-python@v1" + with: + python-version: "3.6" + + - name: Install dependencies + run: ./.github/scripts/install.sh + + - name: Run distribution + run: python3 -m nox -e distribution + + - name: Deploy to PyPi + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..bebedf46 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +# Copyright 2020 Google LLC +# +# 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. + +name: Release +on: + push: + branches: [ master ] +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v2 + with: + extra_plugins: | + "@semantic-release/commit-analyzer" + "@semantic-release/release-notes-generator" + "@google/semantic-release-replace-plugin" + "@semantic-release/git + "@semantic-release/github + env: + GH_TOKEN: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..53ec5a12 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,46 @@ +# Copyright 2020 Google LLC +# +# 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. + +# A workflow that runs tests on every new pull request +name: Run tests + +on: + repository_dispatch: + types: [test] + pull_request: + branches: ['*'] + push: + branches: ['*'] + +jobs: + test: + name: "Run tests on Python ${{ matrix.python-version }}" + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup Python + uses: "actions/setup-python@v1" + with: + python-version: "${{ matrix.python-version }}" + + - name: Install dependencies + run: ./.github/scripts/install.sh + + - name: Run tests + run: | + python3 -m nox --session "tests-${{ matrix.python-version }}" + python3 -m nox -e distribution diff --git a/.releaserc b/.releaserc new file mode 100644 index 00000000..981e4031 --- /dev/null +++ b/.releaserc @@ -0,0 +1,22 @@ +branches: + - master +plugins: + - "@semantic-release/commit-analyzer" + - "@semantic-release/release-notes-generator" + - - "@google/semantic-release-replace-plugin" + - replacements: + - files: + - "./googlemaps/__init__.py" + from: "__version__ = \".*\"" + to: "__version__ = \"${nextRelease.version}\"" + - files: + - "./setup.py" + from: "version=\".*\"" + to: "version=\"${nextRelease.version}\"" + - - "@semantic-release/git" + - assets: + - "./googlemaps/__init__.py" + - "./setup.py" + - "@semantic-release/github" +options: + debug: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dc90fb60..00000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: python -dist: xenial - -matrix: - include: - - python: '3.5' - env: NOXSESSION="tests-3.5" - - python: '3.6' - env: NOXSESSION="tests-3.6" - - python: '3.7' - env: NOXSESSION="tests-3.7" - sudo: required # required for Python 3.7 (github.com/travis-ci/travis-ci#9069) - - python: '3.8' - env: NOXSESSION="tests-3.8" - - python: '3.6' - env: NOXSESSION="docs" - -install: -- ./.travis/install.sh - -script: -- python3 -m nox --session "$NOXSESSION" -- python3 -m nox -e distribution - -deploy: - on: - tags: true - python: '3.6' # only run this deploy once with python 3.6 - provider: pypi - distributions: 'sdist bdist_wheel' - user: __token__ # api token encrypted within travis - skip_existing: true - -notifications: - email: false diff --git a/noxfile.py b/noxfile.py index 9f4024d4..bdb7477e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -61,5 +61,5 @@ def docs(session): @nox.session() def distribution(session): - session.run("bash", ".travis/distribution.sh", external=True) + session.run("bash", ".github/scripts/distribution.sh", external=True) session.run("python", "-c", "import googlemaps") From 0f7c01066d1dfc7492fb4d54ef8d3c2155e79d36 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 23 Apr 2020 23:46:16 +0000 Subject: [PATCH 080/166] chore(release): 4.2.1 [skip ci] ## [4.2.1](https://github.com/googlemaps/google-maps-services-python/compare/v4.2.0...v4.2.1) (2020-04-23) ### Bug Fixes * add python requires attribute to setup.py ([c61a1e3](https://github.com/googlemaps/google-maps-services-python/commit/c61a1e306f72bcbae365f3487c04d2368a22d30a)), closes [#345](https://github.com/googlemaps/google-maps-services-python/issues/345) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index d81cb6e9..64b606a9 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.2.0" +__version__ = "4.2.1" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 301cd743..73bc6a20 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="googlemaps", - version="4.2.0", + version="4.2.1", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 68d35e6adb23799b262f46ac9ad2ba1bcd4473f8 Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Thu, 23 Apr 2020 16:56:53 -0700 Subject: [PATCH 081/166] chore: Fix test.yml spacing. --- .github/workflows/test.yml | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 53ec5a12..527b8db8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,20 +27,19 @@ jobs: test: name: "Run tests on Python ${{ matrix.python-version }}" runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Setup Python - uses: "actions/setup-python@v1" - with: - python-version: "${{ matrix.python-version }}" - - - name: Install dependencies - run: ./.github/scripts/install.sh - - - name: Run tests - run: | - python3 -m nox --session "tests-${{ matrix.python-version }}" - python3 -m nox -e distribution + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Setup Python + uses: "actions/setup-python@v1" + with: + python-version: "${{ matrix.python-version }}" + + - name: Install dependencies + run: ./.github/scripts/install.sh + + - name: Run tests + run: | + python3 -m nox --session "tests-${{ matrix.python-version }}" + python3 -m nox -e distribution From fc69668c5c28395a4935f212cb67c43f2a0eb566 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Fri, 24 Apr 2020 20:11:05 +0530 Subject: [PATCH 082/166] fix: use timezone-aware timestamp() instead of timetuple() (#350) fixes issue where convert.time ignores timezone in the datetime object. current implementation: times = [ datetime(2020, 3, 14, 8, 0, tzinfo=timezone.utc), datetime(2020, 3, 14, 9, 0, tzinfo=timezone(timedelta(hours=1))), datetime(2020, 3, 14, 8, 0, tzinfo=timezone(timedelta(hours=1))) ] for time in times: print(convert.time(time)) output: 1584153000 1584156600 1584153000 technically, as @atollena describes, the first two times should be considered to be the same, whereas the third datetime instance is expected be an hour behind the first two instances. but by using timetuple(), the specified timezone is ignored. so, using the timestamp() method instead of timetuple() ensures that the specified timezone is respected: new implementation (assuming the same list of times from before): for time in times: print(convert.time(time)) output: 1584172800 1584172800 1584169200 here, the specified timezone data is respected, and as expected the third datetime instance is 3600 seconds behind the first two instances. since python2 support has now been dropped, this can safely be included in the package. Signed-off-by: Chinmay D. Pai --- googlemaps/convert.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/googlemaps/convert.py b/googlemaps/convert.py index 602cf4d0..7dfa9882 100644 --- a/googlemaps/convert.py +++ b/googlemaps/convert.py @@ -28,8 +28,6 @@ # '-33.8674869,151.2069902' """ -import time as _time - def format_float(arg): """Formats a float value to be as short as possible. @@ -187,8 +185,8 @@ def time(arg): :type arg: datetime.datetime or int """ # handle datetime instances. - if _has_method(arg, "timetuple"): - arg = _time.mktime(arg.timetuple()) + if _has_method(arg, "timestamp"): + arg = arg.timestamp() if isinstance(arg, float): arg = int(arg) From 0c4aa3ccbb0bd142c3ee82466eef9248085211a7 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 24 Apr 2020 14:41:58 +0000 Subject: [PATCH 083/166] chore(release): 4.2.2 [skip ci] ## [4.2.2](https://github.com/googlemaps/google-maps-services-python/compare/v4.2.1...v4.2.2) (2020-04-24) ### Bug Fixes * use timezone-aware timestamp() instead of timetuple() ([#350](https://github.com/googlemaps/google-maps-services-python/issues/350)) ([fc69668](https://github.com/googlemaps/google-maps-services-python/commit/fc69668c5c28395a4935f212cb67c43f2a0eb566)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 64b606a9..fa073a41 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.2.1" +__version__ = "4.2.2" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 73bc6a20..27680192 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="googlemaps", - version="4.2.1", + version="4.2.2", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 5c3fedb2e3993b822db26b2fa5cab3c031d4d21e Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Fri, 24 Apr 2020 09:17:35 -0700 Subject: [PATCH 084/166] chore: Install setuptools --- .github/scripts/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index 9dd84bde..4d7872ec 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -5,7 +5,7 @@ set -exo pipefail if ! python3 -m pip --version; then curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py sudo python3 get-pip.py - sudo python3 -m pip install nox + sudo python3 -m pip install setuptools nox else - python3 -m pip install nox + python3 -m pip install setuptools nox fi From e0cfe650731c969dedde0326a0fe40e578d397bd Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Fri, 24 Apr 2020 09:24:11 -0700 Subject: [PATCH 085/166] chore: Upgrade setuptools. --- .github/scripts/install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index 4d7872ec..5cd76712 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -5,7 +5,9 @@ set -exo pipefail if ! python3 -m pip --version; then curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py sudo python3 get-pip.py - sudo python3 -m pip install setuptools nox + sudo python3 -m pip install --upgrade setuptools + sudo python3 -m pip install nox else - python3 -m pip install setuptools nox + sudo python3 -m pip install --upgrade setuptools + python3 -m pip install nox fi From 4609fce5b80a18182732af93b0c9322c76552f9f Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Fri, 24 Apr 2020 09:35:17 -0700 Subject: [PATCH 086/166] chore: Adding job strategy. --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 527b8db8..58839686 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,6 +27,9 @@ jobs: test: name: "Run tests on Python ${{ matrix.python-version }}" runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.5", "3.6", "3.7", "3.8"] steps: - name: Checkout repository uses: actions/checkout@v2 From 6eeb48a7d89575a1f8da68c56fef0e717f9737e5 Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Mon, 27 Apr 2020 08:52:18 -0700 Subject: [PATCH 087/166] feat: Add business_status (#356) --- googlemaps/places.py | 2 ++ googlemaps/test/test_places.py | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index eb748b35..1ead5fe4 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -23,6 +23,7 @@ PLACES_FIND_FIELDS_BASIC = set( [ + "business_status", "formatted_address", "geometry", "geometry/location", @@ -59,6 +60,7 @@ [ "address_component", "adr_address", + "business_status", "formatted_address", "geometry", "geometry/location", diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py index a21cd8ee..c2ce4ec9 100644 --- a/googlemaps/test/test_places.py +++ b/googlemaps/test/test_places.py @@ -47,14 +47,14 @@ def test_places_find(self): status=200, content_type='application/json') self.client.find_place('restaurant', 'textquery', - fields=['geometry/location', 'place_id'], + fields=['business_status', 'geometry/location', 'place_id'], location_bias='point:90,90', language=self.language) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&inputtype=textquery&' 'locationbias=point:90,90&input=restaurant' - '&fields=geometry/location,place_id&key=%s' + '&fields=business_status,geometry/location,place_id&key=%s' % (url, self.key), responses.calls[0].request.url) with self.assertRaises(ValueError): @@ -119,11 +119,12 @@ def test_place_detail(self): status=200, content_type='application/json') self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', - fields=['geometry/location', 'place_id'], language=self.language) + fields=['business_status', 'geometry/location', 'place_id'], + language=self.language) self.assertEqual(1, len(responses.calls)) self.assertURLEqual('%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4' - '&key=%s&fields=geometry/location,place_id' + '&key=%s&fields=business_status,geometry/location,place_id' % (url, self.key), responses.calls[0].request.url) with self.assertRaises(ValueError): From 15ca2c7314dfd280dbd7e5d318ba0bce15c67eaa Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 27 Apr 2020 15:53:09 +0000 Subject: [PATCH 088/166] chore(release): 4.3.0 [skip ci] # [4.3.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.2.2...v4.3.0) (2020-04-27) ### Features * Add business_status ([#356](https://github.com/googlemaps/google-maps-services-python/issues/356)) ([6eeb48a](https://github.com/googlemaps/google-maps-services-python/commit/6eeb48a7d89575a1f8da68c56fef0e717f9737e5)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index fa073a41..49ad240f 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.2.2" +__version__ = "4.3.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 27680192..5be33d49 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name="googlemaps", - version="4.2.2", + version="4.3.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 428b5c9d76d0c5c5e5000cbec8fc9ace810ac856 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 27 Apr 2020 10:11:54 -0700 Subject: [PATCH 089/166] fix: cleanup test and remove from package (#361) --- MANIFEST.in | 1 - googlemaps/test/test_directions.py | 278 -------------- googlemaps/test/test_distance_matrix.py | 150 -------- googlemaps/test/test_elevation.py | 116 ------ googlemaps/test/test_geocoding.py | 283 -------------- googlemaps/test/test_places.py | 181 --------- googlemaps/test/test_roads.py | 137 ------- noxfile.py | 4 +- setup.cfg | 2 +- setup.py | 8 +- test_requirements.txt | 4 - {googlemaps/test => tests}/__init__.py | 6 +- {googlemaps/test => tests}/test_client.py | 234 +++++++----- {googlemaps/test => tests}/test_convert.py | 59 +-- tests/test_directions.py | 325 ++++++++++++++++ tests/test_distance_matrix.py | 183 +++++++++ tests/test_elevation.py | 134 +++++++ tests/test_geocoding.py | 348 ++++++++++++++++++ .../test => tests}/test_geolocation.py | 25 +- {googlemaps/test => tests}/test_maps.py | 76 ++-- tests/test_places.py | 249 +++++++++++++ tests/test_roads.py | 158 ++++++++ {googlemaps/test => tests}/test_timezone.py | 56 +-- 23 files changed, 1654 insertions(+), 1363 deletions(-) delete mode 100644 googlemaps/test/test_directions.py delete mode 100644 googlemaps/test/test_distance_matrix.py delete mode 100644 googlemaps/test/test_elevation.py delete mode 100644 googlemaps/test/test_geocoding.py delete mode 100644 googlemaps/test/test_places.py delete mode 100644 googlemaps/test/test_roads.py delete mode 100644 test_requirements.txt rename {googlemaps/test => tests}/__init__.py (90%) rename {googlemaps/test => tests}/test_client.py (65%) rename {googlemaps/test => tests}/test_convert.py (68%) create mode 100644 tests/test_directions.py create mode 100644 tests/test_distance_matrix.py create mode 100644 tests/test_elevation.py create mode 100644 tests/test_geocoding.py rename {googlemaps/test => tests}/test_geolocation.py (63%) rename {googlemaps/test => tests}/test_maps.py (52%) create mode 100644 tests/test_places.py create mode 100644 tests/test_roads.py rename {googlemaps/test => tests}/test_timezone.py (56%) diff --git a/MANIFEST.in b/MANIFEST.in index fdf9f786..b248a6b8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ include CHANGELOG.md LICENSE README.md -recursive-include googlemaps/test *.py global-exclude __pycache__ global-exclude *.py[co] diff --git a/googlemaps/test/test_directions.py b/googlemaps/test/test_directions.py deleted file mode 100644 index de21dc1e..00000000 --- a/googlemaps/test/test_directions.py +++ /dev/null @@ -1,278 +0,0 @@ -# -# Copyright 2014 Google Inc. All rights reserved. -# -# -# 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. -# - -"""Tests for the directions module.""" - -from datetime import datetime -from datetime import timedelta -import time - -import responses - -import googlemaps -import googlemaps.test as _test - - -class DirectionsTest(_test.TestCase): - - def setUp(self): - self.key = 'AIzaasdf' - self.client = googlemaps.Client(self.key) - - @responses.activate - def test_simple_directions(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - # Simplest directions request. Driving directions by default. - routes = self.client.directions("Sydney", "Melbourne") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json' - '?origin=Sydney&destination=Melbourne&key=%s' % - self.key, - responses.calls[0].request.url) - - @responses.activate - def test_complex_request(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Sydney", "Melbourne", - mode="bicycling", - avoid=["highways", "tolls", "ferries"], - units="metric", - region="us") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Sydney&avoid=highways%%7Ctolls%%7Cferries&' - 'destination=Melbourne&mode=bicycling&key=%s' - '&units=metric®ion=us' % - self.key, - responses.calls[0].request.url) - - - def test_transit_without_time(self): - # With mode of transit, we need a departure_time or an - # arrival_time specified - with self.assertRaises(googlemaps.exceptions.ApiError): - self.client.directions("Sydney Town Hall", "Parramatta, NSW", - mode="transit") - - @responses.activate - def test_transit_with_departure_time(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - now = datetime.now() - routes = self.client.directions("Sydney Town Hall", "Parramatta, NSW", - mode="transit", - traffic_model="optimistic", - departure_time=now) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?origin=' - 'Sydney+Town+Hall&key=%s&destination=Parramatta%%2C+NSW&' - 'mode=transit&departure_time=%d&traffic_model=optimistic' % - (self.key, time.mktime(now.timetuple())), - responses.calls[0].request.url) - - @responses.activate - def test_transit_with_arrival_time(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - an_hour_from_now = datetime.now() + timedelta(hours=1) - routes = self.client.directions("Sydney Town Hall", - "Parramatta, NSW", - mode="transit", - arrival_time=an_hour_from_now) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Sydney+Town+Hall&arrival_time=%d&' - 'destination=Parramatta%%2C+NSW&mode=transit&key=%s' % - (time.mktime(an_hour_from_now.timetuple()), self.key), - responses.calls[0].request.url) - - - def test_invalid_travel_mode(self): - with self.assertRaises(ValueError): - self.client.directions("48 Pirrama Road, Pyrmont, NSW", - "Sydney Town Hall", - mode="crawling") - - @responses.activate - def test_travel_mode_round_trip(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Town Hall, Sydney", - "Parramatta, NSW", - mode="bicycling") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Town+Hall%%2C+Sydney&destination=Parramatta%%2C+NSW&' - 'mode=bicycling&key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_brooklyn_to_queens_by_transit(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - now = datetime.now() - routes = self.client.directions("Brooklyn", - "Queens", - mode="transit", - departure_time=now) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Brooklyn&key=%s&destination=Queens&mode=transit&' - 'departure_time=%d' % (self.key, time.mktime(now.timetuple())), - responses.calls[0].request.url) - - @responses.activate - def test_boston_to_concord_via_charlestown_and_lexington(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Boston, MA", - "Concord, MA", - waypoints=["Charlestown, MA", - "Lexington, MA"]) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Boston%%2C+MA&destination=Concord%%2C+MA&' - 'waypoints=Charlestown%%2C+MA%%7CLexington%%2C+MA&' - 'key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_adelaide_wine_tour(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Adelaide, SA", - "Adelaide, SA", - waypoints=["Barossa Valley, SA", - "Clare, SA", - "Connawarra, SA", - "McLaren Vale, SA"], - optimize_waypoints=True) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Adelaide%%2C+SA&destination=Adelaide%%2C+SA&' - 'waypoints=optimize%%3Atrue%%7CBarossa+Valley%%2C+' - 'SA%%7CClare%%2C+SA%%7CConnawarra%%2C+SA%%7CMcLaren+' - 'Vale%%2C+SA&key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_toledo_to_madrid_in_spain(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Toledo", "Madrid", - region="es") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Toledo®ion=es&destination=Madrid&key=%s' % - self.key, - responses.calls[0].request.url) - - @responses.activate - def test_zero_results_returns_response(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"ZERO_RESULTS","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Toledo", "Madrid") - self.assertIsNotNone(routes) - self.assertEqual(0, len(routes)) - - @responses.activate - def test_language_parameter(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Toledo", "Madrid", - region="es", - language="es") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Toledo®ion=es&destination=Madrid&key=%s&' - 'language=es' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_alternatives(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/directions/json', - body='{"status":"OK","routes":[]}', - status=200, - content_type='application/json') - - routes = self.client.directions("Sydney Town Hall", - "Parramatta Town Hall", - alternatives=True) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/directions/json?' - 'origin=Sydney+Town+Hall&destination=Parramatta+Town+Hall&' - 'alternatives=true&key=%s' % self.key, - responses.calls[0].request.url) - diff --git a/googlemaps/test/test_distance_matrix.py b/googlemaps/test/test_distance_matrix.py deleted file mode 100644 index 83ecfaf3..00000000 --- a/googlemaps/test/test_distance_matrix.py +++ /dev/null @@ -1,150 +0,0 @@ -# -# Copyright 2014 Google Inc. All rights reserved. -# -# -# 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. -# - -"""Tests for the distance matrix module.""" - -from datetime import datetime -import time - -import responses - -import googlemaps -import googlemaps.test as _test - - -class DistanceMatrixTest(_test.TestCase): - - def setUp(self): - self.key = 'AIzaasdf' - self.client = googlemaps.Client(self.key) - - @responses.activate - def test_basic_params(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/distancematrix/json', - body='{"status":"OK","rows":[]}', - status=200, - content_type='application/json') - - origins = ["Perth, Australia", "Sydney, Australia", - "Melbourne, Australia", "Adelaide, Australia", - "Brisbane, Australia", "Darwin, Australia", - "Hobart, Australia", "Canberra, Australia"] - destinations = ["Uluru, Australia", - "Kakadu, Australia", - "Blue Mountains, Australia", - "Bungle Bungles, Australia", - "The Pinnacles, Australia"] - - matrix = self.client.distance_matrix(origins, destinations) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/distancematrix/json?' - 'key=%s&origins=Perth%%2C+Australia%%7CSydney%%2C+' - 'Australia%%7CMelbourne%%2C+Australia%%7CAdelaide%%2C+' - 'Australia%%7CBrisbane%%2C+Australia%%7CDarwin%%2C+' - 'Australia%%7CHobart%%2C+Australia%%7CCanberra%%2C+Australia&' - 'destinations=Uluru%%2C+Australia%%7CKakadu%%2C+Australia%%7C' - 'Blue+Mountains%%2C+Australia%%7CBungle+Bungles%%2C+Australia' - '%%7CThe+Pinnacles%%2C+Australia' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_mixed_params(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/distancematrix/json', - body='{"status":"OK","rows":[]}', - status=200, - content_type='application/json') - - origins = ["Bobcaygeon ON", [41.43206, -81.38992]] - destinations = [(43.012486, -83.6964149), - {"lat": 42.8863855, "lng": -78.8781627}] - - matrix = self.client.distance_matrix(origins, destinations) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/distancematrix/json?' - 'key=%s&origins=Bobcaygeon+ON%%7C41.43206%%2C-81.38992&' - 'destinations=43.012486%%2C-83.6964149%%7C42.8863855%%2C' - '-78.8781627' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_all_params(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/distancematrix/json', - body='{"status":"OK","rows":[]}', - status=200, - content_type='application/json') - - origins = ["Perth, Australia", "Sydney, Australia", - "Melbourne, Australia", "Adelaide, Australia", - "Brisbane, Australia", "Darwin, Australia", - "Hobart, Australia", "Canberra, Australia"] - destinations = ["Uluru, Australia", - "Kakadu, Australia", - "Blue Mountains, Australia", - "Bungle Bungles, Australia", - "The Pinnacles, Australia"] - - now = datetime.now() - matrix = self.client.distance_matrix(origins, destinations, - mode="driving", - language="en-AU", - avoid="tolls", - units="imperial", - departure_time=now, - traffic_model="optimistic") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/distancematrix/json?' - 'origins=Perth%%2C+Australia%%7CSydney%%2C+Australia%%7C' - 'Melbourne%%2C+Australia%%7CAdelaide%%2C+Australia%%7C' - 'Brisbane%%2C+Australia%%7CDarwin%%2C+Australia%%7CHobart%%2C+' - 'Australia%%7CCanberra%%2C+Australia&language=en-AU&' - 'avoid=tolls&mode=driving&key=%s&units=imperial&' - 'destinations=Uluru%%2C+Australia%%7CKakadu%%2C+Australia%%7C' - 'Blue+Mountains%%2C+Australia%%7CBungle+Bungles%%2C+Australia' - '%%7CThe+Pinnacles%%2C+Australia&departure_time=%d' - '&traffic_model=optimistic' % - (self.key, time.mktime(now.timetuple())), - responses.calls[0].request.url) - - - @responses.activate - def test_lang_param(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/distancematrix/json', - body='{"status":"OK","rows":[]}', - status=200, - content_type='application/json') - - origins = ["Vancouver BC", "Seattle"] - destinations = ["San Francisco", "Victoria BC"] - - matrix = self.client.distance_matrix(origins, destinations, - language="fr-FR", - mode="bicycling") - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/distancematrix/json?' - 'key=%s&language=fr-FR&mode=bicycling&' - 'origins=Vancouver+BC%%7CSeattle&' - 'destinations=San+Francisco%%7CVictoria+BC' % - self.key, - responses.calls[0].request.url) diff --git a/googlemaps/test/test_elevation.py b/googlemaps/test/test_elevation.py deleted file mode 100644 index 41146939..00000000 --- a/googlemaps/test/test_elevation.py +++ /dev/null @@ -1,116 +0,0 @@ -# -# Copyright 2014 Google Inc. All rights reserved. -# -# -# 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. -# - -"""Tests for the elevation module.""" - -import datetime - -import responses - -import googlemaps -import googlemaps.test as _test - - -class ElevationTest(_test.TestCase): - - def setUp(self): - self.key = 'AIzaasdf' - self.client = googlemaps.Client(self.key) - - @responses.activate - def test_elevation_single(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/elevation/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.elevation((40.714728, -73.998672)) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/elevation/json?' - 'locations=enc:abowFtzsbM&key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_elevation_single_list(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/elevation/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.elevation([(40.714728, -73.998672)]) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/elevation/json?' - 'locations=enc:abowFtzsbM&key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_elevation_multiple(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/elevation/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - locations = [(40.714728, -73.998672), (-34.397, 150.644)] - results = self.client.elevation(locations) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/elevation/json?' - 'locations=enc:abowFtzsbMhgmiMuobzi@&key=%s' % self.key, - responses.calls[0].request.url) - - def test_elevation_along_path_single(self): - with self.assertRaises(googlemaps.exceptions.ApiError): - results = self.client.elevation_along_path( - [(40.714728, -73.998672)], 5) - - @responses.activate - def test_elevation_along_path(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/elevation/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - path = [(40.714728, -73.998672), (-34.397, 150.644)] - - results = self.client.elevation_along_path(path, 5) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/elevation/json?' - 'path=enc:abowFtzsbMhgmiMuobzi@&' - 'key=%s&samples=5' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_short_latlng(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/elevation/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.elevation((40, -73)) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/elevation/json?' - 'locations=40,-73&key=%s' % self.key, - responses.calls[0].request.url) diff --git a/googlemaps/test/test_geocoding.py b/googlemaps/test/test_geocoding.py deleted file mode 100644 index 0f946850..00000000 --- a/googlemaps/test/test_geocoding.py +++ /dev/null @@ -1,283 +0,0 @@ -# This Python file uses the following encoding: utf-8 -# -# Copyright 2014 Google Inc. All rights reserved. -# -# -# 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. -# - -"""Tests for the geocoding module.""" - -import datetime - -import responses - -import googlemaps -import googlemaps.test as _test - - -class GeocodingTest(_test.TestCase): - - def setUp(self): - self.key = 'AIzaasdf' - self.client = googlemaps.Client(self.key) - - @responses.activate - def test_simple_geocode(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('Sydney') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&address=Sydney' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_reverse_geocode(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.reverse_geocode((-33.8674869, 151.2069902)) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'latlng=-33.8674869,151.2069902&key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_geocoding_the_googleplex(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('1600 Amphitheatre Parkway, ' - 'Mountain View, CA') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&address=1600+Amphitheatre+Parkway%%2C+Mountain' - '+View%%2C+CA' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_geocode_with_bounds(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('Winnetka', - bounds={'southwest': (34.172684, -118.604794), - 'northeast':(34.236144, -118.500938)}) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'bounds=34.172684%%2C-118.604794%%7C34.236144%%2C' - '-118.500938&key=%s&address=Winnetka' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_geocode_with_region_biasing(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('Toledo', region='es') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'region=es&key=%s&address=Toledo' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_geocode_with_component_filter(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('santa cruz', - components={'country': 'ES'}) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&components=country%%3AES&address=santa+cruz' % - self.key, - responses.calls[0].request.url) - - @responses.activate - def test_geocode_with_multiple_component_filters(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('Torun', - components={'administrative_area': 'TX','country': 'US'}) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&components=administrative_area%%3ATX%%7C' - 'country%%3AUS&address=Torun' % self.key, - responses.calls[0].request.url) - - - @responses.activate - def test_geocode_with_just_components(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode( - components={'route': 'Annegatan', - 'administrative_area': 'Helsinki', - 'country': 'Finland'}) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&components=administrative_area%%3AHelsinki' - '%%7Ccountry%%3AFinland%%7Croute%%3AAnnegatan' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_simple_reverse_geocode(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.reverse_geocode((40.714224, -73.961452)) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'latlng=40.714224%%2C-73.961452&key=%s' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_reverse_geocode_restricted_by_type(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.reverse_geocode((40.714224, -73.961452), - location_type='ROOFTOP', - result_type='street_address') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'latlng=40.714224%%2C-73.961452&result_type=street_address&' - 'key=%s&location_type=ROOFTOP' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_reverse_geocode_multiple_location_types(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.reverse_geocode((40.714224, -73.961452), - location_type=['ROOFTOP', - 'RANGE_INTERPOLATED'], - result_type='street_address') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'latlng=40.714224%%2C-73.961452&result_type=street_address&' - 'key=%s&location_type=ROOFTOP%%7CRANGE_INTERPOLATED' % - self.key, - responses.calls[0].request.url) - - @responses.activate - def test_reverse_geocode_multiple_result_types(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.reverse_geocode((40.714224, -73.961452), - location_type='ROOFTOP', - result_type=['street_address', - 'route']) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'latlng=40.714224%%2C-73.961452&result_type=street_address' - '%%7Croute&key=%s&location_type=ROOFTOP' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_partial_match(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode('Pirrama Pyrmont') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&address=Pirrama+Pyrmont' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_utf_results(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - results = self.client.geocode(components={'postal_code': '96766'}) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&components=postal_code%%3A96766' % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_utf8_request(self): - responses.add(responses.GET, - 'https://maps.googleapis.com/maps/api/geocode/json', - body='{"status":"OK","results":[]}', - status=200, - content_type='application/json') - - self.client.geocode(self.u('\\u4e2d\\u56fd')) # China - self.assertURLEqual( - 'https://maps.googleapis.com/maps/api/geocode/json?' - 'key=%s&address=%s' % (self.key, '%E4%B8%AD%E5%9B%BD'), - responses.calls[0].request.url) diff --git a/googlemaps/test/test_places.py b/googlemaps/test/test_places.py deleted file mode 100644 index c2ce4ec9..00000000 --- a/googlemaps/test/test_places.py +++ /dev/null @@ -1,181 +0,0 @@ -# This Python file uses the following encoding: utf-8 -# -# Copyright 2016 Google Inc. All rights reserved. -# -# -# 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. -# - -"""Tests for the places module.""" - -import uuid - -from types import GeneratorType - -import responses - -import googlemaps -import googlemaps.test as _test - - -class PlacesTest(_test.TestCase): - - def setUp(self): - self.key = 'AIzaasdf' - self.client = googlemaps.Client(self.key) - self.location = (-33.86746, 151.207090) - self.type = 'liquor_store' - self.language = 'en-AU' - self.region = 'AU' - self.radius = 100 - - @responses.activate - def test_places_find(self): - url = 'https://maps.googleapis.com/maps/api/place/findplacefromtext/json' - responses.add(responses.GET, url, - body='{"status": "OK", "candidates": []}', - status=200, content_type='application/json') - - self.client.find_place('restaurant', 'textquery', - fields=['business_status', 'geometry/location', 'place_id'], - location_bias='point:90,90', - language=self.language) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?language=en-AU&inputtype=textquery&' - 'locationbias=point:90,90&input=restaurant' - '&fields=business_status,geometry/location,place_id&key=%s' - % (url, self.key), responses.calls[0].request.url) - - with self.assertRaises(ValueError): - self.client.find_place('restaurant', 'invalid') - with self.assertRaises(ValueError): - self.client.find_place('restaurant', 'textquery', - fields=['geometry', 'invalid']) - with self.assertRaises(ValueError): - self.client.find_place('restaurant', 'textquery', - location_bias='invalid') - - @responses.activate - def test_places_text_search(self): - url = 'https://maps.googleapis.com/maps/api/place/textsearch/json' - responses.add(responses.GET, url, - body='{"status": "OK", "results": [], "html_attributions": []}', - status=200, content_type='application/json') - - self.client.places('restaurant', location=self.location, - radius=self.radius, region=self.region, language=self.language, - min_price=1, max_price=4, open_now=True, - type=self.type) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?language=en-AU&location=-33.86746%%2C151.20709&' - 'maxprice=4&minprice=1&opennow=true&query=restaurant&' - 'radius=100®ion=AU&type=liquor_store&key=%s' - % (url, self.key), responses.calls[0].request.url) - - @responses.activate - def test_places_nearby_search(self): - url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json' - responses.add(responses.GET, url, - body='{"status": "OK", "results": [], "html_attributions": []}', - status=200, content_type='application/json') - - self.client.places_nearby(location=self.location, keyword='foo', - language=self.language, min_price=1, - max_price=4, name='bar', open_now=True, - rank_by='distance', type=self.type) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?keyword=foo&language=en-AU&location=-33.86746%%2C151.20709&' - 'maxprice=4&minprice=1&name=bar&opennow=true&rankby=distance&' - 'type=liquor_store&key=%s' - % (url, self.key), responses.calls[0].request.url) - - with self.assertRaises(ValueError): - self.client.places_nearby(radius=self.radius) - with self.assertRaises(ValueError): - self.client.places_nearby(self.location, rank_by="distance") - - with self.assertRaises(ValueError): - self.client.places_nearby(location=self.location, rank_by="distance", - keyword='foo', radius=self.radius) - - @responses.activate - def test_place_detail(self): - url = 'https://maps.googleapis.com/maps/api/place/details/json' - responses.add(responses.GET, url, - body='{"status": "OK", "result": {}, "html_attributions": []}', - status=200, content_type='application/json') - - self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', - fields=['business_status', 'geometry/location', 'place_id'], - language=self.language) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4' - '&key=%s&fields=business_status,geometry/location,place_id' - % (url, self.key), responses.calls[0].request.url) - - with self.assertRaises(ValueError): - self.client.place('ChIJN1t_tDeuEmsRUsoyG83frY4', - fields=['geometry', 'invalid']) - - @responses.activate - def test_photo(self): - url = 'https://maps.googleapis.com/maps/api/place/photo' - responses.add(responses.GET, url, status=200) - - ref = 'CnRvAAAAwMpdHeWlXl-lH0vp7lez4znKPIWSWvgvZFISdKx45AwJVP1Qp37YOrH7sqHMJ8C-vBDC546decipPHchJhHZL94RcTUfPa1jWzo-rSHaTlbNtjh-N68RkcToUCuY9v2HNpo5mziqkir37WU8FJEqVBIQ4k938TI3e7bf8xq-uwDZcxoUbO_ZJzPxremiQurAYzCTwRhE_V0' - response = self.client.places_photo(ref, max_width=100) - - self.assertTrue(isinstance(response, GeneratorType)) - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?maxwidth=100&photoreference=%s&key=%s' - % (url, ref, self.key), responses.calls[0].request.url) - - @responses.activate - def test_autocomplete(self): - url = 'https://maps.googleapis.com/maps/api/place/autocomplete/json' - responses.add(responses.GET, url, - body='{"status": "OK", "predictions": []}', - status=200, content_type='application/json') - - session_token = uuid.uuid4().hex - - self.client.places_autocomplete('Google', session_token=session_token, offset=3, - location=self.location, - radius=self.radius, - language=self.language, - types='geocode', - components={'country': 'au'}, - strict_bounds=True) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?components=country%%3Aau&input=Google&language=en-AU&' - 'location=-33.86746%%2C151.20709&offset=3&radius=100&' - 'strictbounds=true&types=geocode&key=%s&sessiontoken=%s' % - (url, self.key, session_token), responses.calls[0].request.url) - - @responses.activate - def test_autocomplete_query(self): - url = 'https://maps.googleapis.com/maps/api/place/queryautocomplete/json' - responses.add(responses.GET, url, - body='{"status": "OK", "predictions": []}', - status=200, content_type='application/json') - - self.client.places_autocomplete_query('pizza near New York') - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('%s?input=pizza+near+New+York&key=%s' % - (url, self.key), responses.calls[0].request.url) diff --git a/googlemaps/test/test_roads.py b/googlemaps/test/test_roads.py deleted file mode 100644 index 6e11b604..00000000 --- a/googlemaps/test/test_roads.py +++ /dev/null @@ -1,137 +0,0 @@ -# -# Copyright 2015 Google Inc. All rights reserved. -# -# -# 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. -# - -"""Tests for the roads module.""" - - -import responses - -import googlemaps -import googlemaps.test as _test - - -class RoadsTest(_test.TestCase): - - def setUp(self): - self.key = "AIzaasdf" - self.client = googlemaps.Client(self.key) - - @responses.activate - def test_snap(self): - responses.add(responses.GET, - "https://roads.googleapis.com/v1/snapToRoads", - body='{"snappedPoints":["foo"]}', - status=200, - content_type="application/json") - - results = self.client.snap_to_roads((40.714728, -73.998672)) - self.assertEqual("foo", results[0]) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://roads.googleapis.com/v1/snapToRoads?" - "path=40.714728%%2C-73.998672&key=%s" % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_nearest_roads(self): - responses.add(responses.GET, - "https://roads.googleapis.com/v1/nearestRoads", - body='{"snappedPoints":["foo"]}', - status=200, - content_type="application/json") - - results = self.client.nearest_roads((40.714728, -73.998672)) - self.assertEqual("foo", results[0]) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://roads.googleapis.com/v1/nearestRoads?" - "points=40.714728%%2C-73.998672&key=%s" % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_path(self): - responses.add(responses.GET, - "https://roads.googleapis.com/v1/speedLimits", - body='{"speedLimits":["foo"]}', - status=200, - content_type="application/json") - - results = self.client.snapped_speed_limits([(1, 2),(3, 4)]) - self.assertEqual("foo", results["speedLimits"][0]) - - self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://roads.googleapis.com/v1/speedLimits?" - "path=1%%2C2|3%%2C4" - "&key=%s" % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_speedlimits(self): - responses.add(responses.GET, - "https://roads.googleapis.com/v1/speedLimits", - body='{"speedLimits":["foo"]}', - status=200, - content_type="application/json") - - results = self.client.speed_limits("id1") - self.assertEqual("foo", results[0]) - self.assertEqual("https://roads.googleapis.com/v1/speedLimits?" - "placeId=id1&key=%s" % self.key, - responses.calls[0].request.url) - - @responses.activate - def test_speedlimits_multiple(self): - responses.add(responses.GET, - "https://roads.googleapis.com/v1/speedLimits", - body='{"speedLimits":["foo"]}', - status=200, - content_type="application/json") - - results = self.client.speed_limits(["id1", "id2", "id3"]) - self.assertEqual("foo", results[0]) - self.assertEqual("https://roads.googleapis.com/v1/speedLimits?" - "placeId=id1&placeId=id2&placeId=id3" - "&key=%s" % self.key, - responses.calls[0].request.url) - - def test_clientid_not_accepted(self): - client = googlemaps.Client(client_id="asdf", client_secret="asdf") - - with self.assertRaises(ValueError): - client.speed_limits("foo") - - @responses.activate - def test_retry(self): - class request_callback: - def __init__(self): - self.first_req = True - - def __call__(self, req): - if self.first_req: - self.first_req = False - return (500, {}, 'Internal Server Error.') - return (200, {}, '{"speedLimits":[]}') - - responses.add_callback(responses.GET, - "https://roads.googleapis.com/v1/speedLimits", - content_type="application/json", - callback=request_callback()) - - self.client.speed_limits([]) - - self.assertEqual(2, len(responses.calls)) - self.assertEqual(responses.calls[0].request.url, responses.calls[1].request.url) diff --git a/noxfile.py b/noxfile.py index bdb7477e..06eefe58 100644 --- a/noxfile.py +++ b/noxfile.py @@ -8,7 +8,9 @@ def _install_dev_packages(session): def _install_test_dependencies(session): - session.install("-r", "test_requirements.txt") + session.install("pytest") + session.install("pytest-cov") + session.install("responses") def _install_doc_dependencies(session): diff --git a/setup.cfg b/setup.cfg index 399af724..56320971 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ addopts = -rsxX --cov=googlemaps --cov-report= [coverage:run] omit = - googlemaps/test/* + tests/* [coverage:report] exclude_lines = diff --git a/setup.py b/setup.py index 5be33d49..198d283e 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,12 @@ -import sys -import io from setuptools import setup requirements = ["requests>=2.20.0,<3.0"] -# use io.open until python2.7 support is dropped -with io.open("README.md", encoding="utf8") as f: +with open("README.md") as f: readme = f.read() -with io.open("CHANGELOG.md", encoding="utf8") as f: +with open("CHANGELOG.md") as f: changelog = f.read() @@ -26,7 +23,6 @@ platforms="Posix; MacOS X; Windows", setup_requires=requirements, install_requires=requirements, - test_suite="googlemaps.test", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", diff --git a/test_requirements.txt b/test_requirements.txt deleted file mode 100644 index e5a18030..00000000 --- a/test_requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pytest -pytest-cov -responses -mock diff --git a/googlemaps/test/__init__.py b/tests/__init__.py similarity index 90% rename from googlemaps/test/__init__.py rename to tests/__init__.py index 38c70917..8a32b1ed 100644 --- a/googlemaps/test/__init__.py +++ b/tests/__init__.py @@ -18,14 +18,10 @@ import unittest import codecs -try: # Python 3 - from urllib.parse import urlparse, parse_qsl -except ImportError: # Python 2 - from urlparse import urlparse, parse_qsl +from urllib.parse import urlparse, parse_qsl class TestCase(unittest.TestCase): - def assertURLEqual(self, first, second, msg=None): """Check that two arguments are equivalent URLs. Ignores the order of query arguments. diff --git a/googlemaps/test/test_client.py b/tests/test_client.py similarity index 65% rename from googlemaps/test/test_client.py rename to tests/test_client.py index 9c689b67..3ad92772 100644 --- a/googlemaps/test/test_client.py +++ b/tests/test_client.py @@ -26,12 +26,11 @@ import googlemaps import googlemaps.client as _client -import googlemaps.test as _test +from . import TestCase from googlemaps.client import _X_GOOG_MAPS_EXPERIENCE_ID -class ClientTest(_test.TestCase): - +class ClientTest(TestCase): def test_no_api_key(self): with self.assertRaises(Exception): client = googlemaps.Client() @@ -56,13 +55,16 @@ def test_queries_per_second(self): queries_per_second = 3 query_range = range(queries_per_second * 2) for _ in query_range: - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") - client = googlemaps.Client(key="AIzaasdf", - queries_per_second=queries_per_second) + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + client = googlemaps.Client( + key="AIzaasdf", queries_per_second=queries_per_second + ) start = time.time() for _ in query_range: client.geocode("Sesame St.") @@ -71,35 +73,43 @@ def test_queries_per_second(self): @responses.activate def test_key_sent(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) client = googlemaps.Client(key="AIzaasdf") client.geocode("Sesame St.") self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://maps.googleapis.com/maps/api/geocode/json?" - "key=AIzaasdf&address=Sesame+St.", - responses.calls[0].request.url) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=AIzaasdf&address=Sesame+St.", + responses.calls[0].request.url, + ) @responses.activate def test_extra_params(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) client = googlemaps.Client(key="AIzaasdf") client.geocode("Sesame St.", extra_params={"foo": "bar"}) self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://maps.googleapis.com/maps/api/geocode/json?" - "key=AIzaasdf&address=Sesame+St.&foo=bar", - responses.calls[0].request.url) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=AIzaasdf&address=Sesame+St.&foo=bar", + responses.calls[0].request.url, + ) def test_hmac(self): """ @@ -110,18 +120,20 @@ def test_hmac(self): """ message = "The quick brown fox jumps over the lazy dog" - key = "a2V5" # "key" -> base64 + key = "a2V5" # "key" -> base64 signature = "3nybhbi3iqa8ino29wqQcBydtNk=" self.assertEqual(signature, _client.sign_hmac(key, message)) @responses.activate def test_url_signed(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) client = googlemaps.Client(client_id="foo", client_secret="a2V5") client.geocode("Sesame St.") @@ -129,20 +141,25 @@ def test_url_signed(self): self.assertEqual(1, len(responses.calls)) # Check ordering of parameters. - self.assertIn("address=Sesame+St.&client=foo&signature", - responses.calls[0].request.url) - self.assertURLEqual("https://maps.googleapis.com/maps/api/geocode/json?" - "address=Sesame+St.&client=foo&" - "signature=fxbWUIcNPZSekVOhp2ul9LW5TpY=", - responses.calls[0].request.url) + self.assertIn( + "address=Sesame+St.&client=foo&signature", responses.calls[0].request.url + ) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "address=Sesame+St.&client=foo&" + "signature=fxbWUIcNPZSekVOhp2ul9LW5TpY=", + responses.calls[0].request.url, + ) @responses.activate def test_ua_sent(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) client = googlemaps.Client(key="AIzaasdf") client.geocode("Sesame St.") @@ -163,10 +180,12 @@ def __call__(self, req): return (200, {}, '{"status":"OVER_QUERY_LIMIT"}') return (200, {}, '{"status":"OK","results":[]}') - responses.add_callback(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - content_type='application/json', - callback=request_callback()) + responses.add_callback( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + content_type="application/json", + callback=request_callback(), + ) client = googlemaps.Client(key="AIzaasdf") client.geocode("Sesame St.") @@ -176,10 +195,12 @@ def __call__(self, req): @responses.activate def test_transport_error(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - status=404, - content_type='application/json') + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + status=404, + content_type="application/json", + ) client = googlemaps.Client(key="AIzaasdf") with self.assertRaises(googlemaps.exceptions.HTTPError) as e: @@ -189,11 +210,13 @@ def test_transport_error(self): @responses.activate def test_host_override(self): - responses.add(responses.GET, - "https://foo.com/bar", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://foo.com/bar", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) client = googlemaps.Client(key="AIzaasdf") client._get("/bar", {}, base_url="https://foo.com") @@ -205,11 +228,13 @@ def test_custom_extract(self): def custom_extract(resp): return resp.json() - responses.add(responses.GET, - "https://maps.googleapis.com/bar", - body='{"error":"errormessage"}', - status=403, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/bar", + body='{"error":"errormessage"}', + status=403, + content_type="application/json", + ) client = googlemaps.Client(key="AIzaasdf") b = client._get("/bar", {}, extract_body=custom_extract) @@ -225,13 +250,15 @@ def __init__(self): def __call__(self, req): if self.first_req: self.first_req = False - return (500, {}, 'Internal Server Error.') + return (500, {}, "Internal Server Error.") return (200, {}, '{"status":"OK","results":[]}') - responses.add_callback(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - content_type="application/json", - callback=request_callback()) + responses.add_callback( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + content_type="application/json", + callback=request_callback(), + ) client = googlemaps.Client(key="AIzaasdf") client.geocode("Sesame St.") @@ -247,28 +274,31 @@ def test_invalid_channel(self): # https://developers.google.com/maps/premium/reports # /usage-reports#channels with self.assertRaises(ValueError): - client = googlemaps.Client(client_id="foo", client_secret="a2V5", - channel="auieauie$? ") + client = googlemaps.Client( + client_id="foo", client_secret="a2V5", channel="auieauie$? " + ) def test_auth_url_with_channel(self): - client = googlemaps.Client(key="AIzaasdf", - client_id="foo", - client_secret="a2V5", - channel="MyChannel_1") + client = googlemaps.Client( + key="AIzaasdf", client_id="foo", client_secret="a2V5", channel="MyChannel_1" + ) # Check ordering of parameters + signature. - auth_url = client._generate_auth_url("/test", - {"param": "param"}, - accepts_clientid=True) - self.assertEqual(auth_url, "/test?param=param" - "&channel=MyChannel_1" - "&client=foo" - "&signature=OH18GuQto_mEpxj99UimKskvo4k=") + auth_url = client._generate_auth_url( + "/test", {"param": "param"}, accepts_clientid=True + ) + self.assertEqual( + auth_url, + "/test?param=param" + "&channel=MyChannel_1" + "&client=foo" + "&signature=OH18GuQto_mEpxj99UimKskvo4k=", + ) # Check if added to requests to API with accepts_clientid=False - auth_url = client._generate_auth_url("/test", - {"param": "param"}, - accepts_clientid=False) + auth_url = client._generate_auth_url( + "/test", {"param": "param"}, accepts_clientid=False + ) self.assertEqual(auth_url, "/test?param=param&key=AIzaasdf") def test_requests_version(self): @@ -278,18 +308,18 @@ def test_requests_version(self): "client_secret": "a2V5", "channel": "MyChannel_1", "connect_timeout": 5, - "read_timeout": 5 + "read_timeout": 5, } client_args = client_args_timeout.copy() del client_args["connect_timeout"] del client_args["read_timeout"] - requests.__version__ = '2.3.0' + requests.__version__ = "2.3.0" with self.assertRaises(NotImplementedError): googlemaps.Client(**client_args_timeout) googlemaps.Client(**client_args) - requests.__version__ = '2.4.0' + requests.__version__ = "2.4.0" googlemaps.Client(**client_args_timeout) googlemaps.Client(**client_args) @@ -327,10 +357,7 @@ def test_experience_id_sample(self): experience_id = str(uuid.uuid4()) # instantiate client with experience id - client = googlemaps.Client( - key="AIza-Maps-API-Key", - experience_id=experience_id - ) + client = googlemaps.Client(key="AIza-Maps-API-Key", experience_id=experience_id) # clear the current experience id client.clear_experience_id() @@ -352,11 +379,13 @@ def test_experience_id_sample(self): @responses.activate def _perform_mock_request(self, experience_id=None): # Mock response - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/geocode/json", - body='{"status":"OK","results":[]}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) # Perform network call client = googlemaps.Client(key="AIzaasdf") @@ -376,14 +405,15 @@ def test_experience_id_no_in_header(self): @responses.activate def test_no_retry_over_query_limit(self): - responses.add(responses.GET, - "https://maps.googleapis.com/foo", - body='{"status":"OVER_QUERY_LIMIT"}', - status=200, - content_type="application/json") - - client = googlemaps.Client(key="AIzaasdf", - retry_over_query_limit=False) + responses.add( + responses.GET, + "https://maps.googleapis.com/foo", + body='{"status":"OVER_QUERY_LIMIT"}', + status=200, + content_type="application/json", + ) + + client = googlemaps.Client(key="AIzaasdf", retry_over_query_limit=False) with self.assertRaises(googlemaps.exceptions.ApiError): client._request("/foo", {}) diff --git a/googlemaps/test/test_convert.py b/tests/test_convert.py similarity index 68% rename from googlemaps/test/test_convert.py rename to tests/test_convert.py index dc0fe2b1..39546aee 100644 --- a/googlemaps/test/test_convert.py +++ b/tests/test_convert.py @@ -25,7 +25,6 @@ class ConvertTest(unittest.TestCase): - def test_latlng(self): expected = "1,2" ll = {"lat": 1, "lng": 2} @@ -122,31 +121,33 @@ def test_size(self): convert.size("test") def test_polyline_decode(self): - syd_mel_route = ("rvumEis{y[`NsfA~tAbF`bEj^h{@{KlfA~eA~`AbmEghAt~D|e@j" - "lRpO~yH_\\v}LjbBh~FdvCxu@`nCplDbcBf_B|wBhIfhCnqEb~D~" - "jCn_EngApdEtoBbfClf@t_CzcCpoEr_Gz_DxmAphDjjBxqCviEf}" - "B|pEvsEzbE~qGfpExjBlqCx}BvmLb`FbrQdpEvkAbjDllD|uDldD" - "j`Ef|AzcEx_Gtm@vuI~xArwD`dArlFnhEzmHjtC~eDluAfkC|eAd" - "hGpJh}N_mArrDlr@h|HzjDbsAvy@~~EdTxpJje@jlEltBboDjJdv" - "KyZpzExrAxpHfg@pmJg[tgJuqBnlIarAh}DbN`hCeOf_IbxA~uFt" - "|A|xEt_ArmBcN|sB|h@b_DjOzbJ{RlxCcfAp~AahAbqG~Gr}AerA" - "`dCwlCbaFo]twKt{@bsG|}A~fDlvBvz@tw@rpD_r@rqB{PvbHek@" - "vsHlh@ptNtm@fkD[~xFeEbyKnjDdyDbbBtuA|~Br|Gx_AfxCt}Cj" - "nHv`Ew\\lnBdrBfqBraD|{BldBxpG|]jqC`mArcBv]rdAxgBzdEb" - "{InaBzyC}AzaEaIvrCzcAzsCtfD~qGoPfeEh]h`BxiB`e@`kBxfA" - "v^pyA`}BhkCdoCtrC~bCxhCbgEplKrk@tiAteBwAxbCwuAnnCc]b" - "{FjrDdjGhhGzfCrlDruBzSrnGhvDhcFzw@n{@zxAf}Fd{IzaDnbD" - "joAjqJjfDlbIlzAraBxrB}K~`GpuD~`BjmDhkBp{@r_AxCrnAjrC" - "x`AzrBj{B|r@~qBbdAjtDnvCtNzpHxeApyC|GlfM`fHtMvqLjuEt" - "lDvoFbnCt|@xmAvqBkGreFm~@hlHw|AltC}NtkGvhBfaJ|~@riAx" - "uC~gErwCttCzjAdmGuF`iFv`AxsJftD|nDr_QtbMz_DheAf~Buy@" - "rlC`i@d_CljC`gBr|H|nAf_Fh{G|mE~kAhgKviEpaQnu@zwAlrA`" - "G~gFnvItz@j{Cng@j{D{]`tEftCdcIsPz{DddE~}PlnE|dJnzG`e" - "G`mF|aJdqDvoAwWjzHv`H`wOtjGzeXhhBlxErfCf{BtsCjpEjtD|" - "}Aja@xnAbdDt|ErMrdFh{CzgAnlCnr@`wEM~mE`bA`uD|MlwKxmB" - "vuFlhB|sN`_@fvBp`CxhCt_@loDsS|eDlmChgFlqCbjCxk@vbGxm" - "CjbMba@rpBaoClcCk_DhgEzYdzBl\\vsA_JfGztAbShkGtEhlDzh" - "C~w@hnB{e@yF}`D`_Ayx@~vGqn@l}CafC") + syd_mel_route = ( + "rvumEis{y[`NsfA~tAbF`bEj^h{@{KlfA~eA~`AbmEghAt~D|e@j" + "lRpO~yH_\\v}LjbBh~FdvCxu@`nCplDbcBf_B|wBhIfhCnqEb~D~" + "jCn_EngApdEtoBbfClf@t_CzcCpoEr_Gz_DxmAphDjjBxqCviEf}" + "B|pEvsEzbE~qGfpExjBlqCx}BvmLb`FbrQdpEvkAbjDllD|uDldD" + "j`Ef|AzcEx_Gtm@vuI~xArwD`dArlFnhEzmHjtC~eDluAfkC|eAd" + "hGpJh}N_mArrDlr@h|HzjDbsAvy@~~EdTxpJje@jlEltBboDjJdv" + "KyZpzExrAxpHfg@pmJg[tgJuqBnlIarAh}DbN`hCeOf_IbxA~uFt" + "|A|xEt_ArmBcN|sB|h@b_DjOzbJ{RlxCcfAp~AahAbqG~Gr}AerA" + "`dCwlCbaFo]twKt{@bsG|}A~fDlvBvz@tw@rpD_r@rqB{PvbHek@" + "vsHlh@ptNtm@fkD[~xFeEbyKnjDdyDbbBtuA|~Br|Gx_AfxCt}Cj" + "nHv`Ew\\lnBdrBfqBraD|{BldBxpG|]jqC`mArcBv]rdAxgBzdEb" + "{InaBzyC}AzaEaIvrCzcAzsCtfD~qGoPfeEh]h`BxiB`e@`kBxfA" + "v^pyA`}BhkCdoCtrC~bCxhCbgEplKrk@tiAteBwAxbCwuAnnCc]b" + "{FjrDdjGhhGzfCrlDruBzSrnGhvDhcFzw@n{@zxAf}Fd{IzaDnbD" + "joAjqJjfDlbIlzAraBxrB}K~`GpuD~`BjmDhkBp{@r_AxCrnAjrC" + "x`AzrBj{B|r@~qBbdAjtDnvCtNzpHxeApyC|GlfM`fHtMvqLjuEt" + "lDvoFbnCt|@xmAvqBkGreFm~@hlHw|AltC}NtkGvhBfaJ|~@riAx" + "uC~gErwCttCzjAdmGuF`iFv`AxsJftD|nDr_QtbMz_DheAf~Buy@" + "rlC`i@d_CljC`gBr|H|nAf_Fh{G|mE~kAhgKviEpaQnu@zwAlrA`" + "G~gFnvItz@j{Cng@j{D{]`tEftCdcIsPz{DddE~}PlnE|dJnzG`e" + "G`mF|aJdqDvoAwWjzHv`H`wOtjGzeXhhBlxErfCf{BtsCjpEjtD|" + "}Aja@xnAbdDt|ErMrdFh{CzgAnlCnr@`wEM~mE`bA`uD|MlwKxmB" + "vuFlhB|sN`_@fvBp`CxhCt_@loDsS|eDlmChgFlqCbjCxk@vbGxm" + "CjbMba@rpBaoClcCk_DhgEzYdzBl\\vsA_JfGztAbShkGtEhlDzh" + "C~w@hnB{e@yF}`D`_Ayx@~vGqn@l}CafC" + ) points = convert.decode_polyline(syd_mel_route) self.assertAlmostEqual(-33.86746, points[0]["lat"]) @@ -155,8 +156,10 @@ def test_polyline_decode(self): self.assertAlmostEqual(144.963180, points[-1]["lng"]) def test_polyline_round_trip(self): - test_polyline = ("gcneIpgxzRcDnBoBlEHzKjBbHlG`@`IkDxIi" - "KhKoMaLwTwHeIqHuAyGXeB~Ew@fFjAtIzExF") + test_polyline = ( + "gcneIpgxzRcDnBoBlEHzKjBbHlG`@`IkDxIi" + "KhKoMaLwTwHeIqHuAyGXeB~Ew@fFjAtIzExF" + ) points = convert.decode_polyline(test_polyline) actual_polyline = convert.encode_polyline(points) diff --git a/tests/test_directions.py b/tests/test_directions.py new file mode 100644 index 00000000..5a3c477a --- /dev/null +++ b/tests/test_directions.py @@ -0,0 +1,325 @@ +# +# Copyright 2014 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the directions module.""" + +from datetime import datetime +from datetime import timedelta +import time + +import responses + +import googlemaps +from . import TestCase + + +class DirectionsTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_simple_directions(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + # Simplest directions request. Driving directions by default. + routes = self.client.directions("Sydney", "Melbourne") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json" + "?origin=Sydney&destination=Melbourne&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_complex_request(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions( + "Sydney", + "Melbourne", + mode="bicycling", + avoid=["highways", "tolls", "ferries"], + units="metric", + region="us", + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Sydney&avoid=highways%%7Ctolls%%7Cferries&" + "destination=Melbourne&mode=bicycling&key=%s" + "&units=metric®ion=us" % self.key, + responses.calls[0].request.url, + ) + + def test_transit_without_time(self): + # With mode of transit, we need a departure_time or an + # arrival_time specified + with self.assertRaises(googlemaps.exceptions.ApiError): + self.client.directions( + "Sydney Town Hall", "Parramatta, NSW", mode="transit" + ) + + @responses.activate + def test_transit_with_departure_time(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + now = datetime.now() + routes = self.client.directions( + "Sydney Town Hall", + "Parramatta, NSW", + mode="transit", + traffic_model="optimistic", + departure_time=now, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?origin=" + "Sydney+Town+Hall&key=%s&destination=Parramatta%%2C+NSW&" + "mode=transit&departure_time=%d&traffic_model=optimistic" + % (self.key, time.mktime(now.timetuple())), + responses.calls[0].request.url, + ) + + @responses.activate + def test_transit_with_arrival_time(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + an_hour_from_now = datetime.now() + timedelta(hours=1) + routes = self.client.directions( + "Sydney Town Hall", + "Parramatta, NSW", + mode="transit", + arrival_time=an_hour_from_now, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Sydney+Town+Hall&arrival_time=%d&" + "destination=Parramatta%%2C+NSW&mode=transit&key=%s" + % (time.mktime(an_hour_from_now.timetuple()), self.key), + responses.calls[0].request.url, + ) + + def test_invalid_travel_mode(self): + with self.assertRaises(ValueError): + self.client.directions( + "48 Pirrama Road, Pyrmont, NSW", "Sydney Town Hall", mode="crawling" + ) + + @responses.activate + def test_travel_mode_round_trip(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions( + "Town Hall, Sydney", "Parramatta, NSW", mode="bicycling" + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Town+Hall%%2C+Sydney&destination=Parramatta%%2C+NSW&" + "mode=bicycling&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_brooklyn_to_queens_by_transit(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + now = datetime.now() + routes = self.client.directions( + "Brooklyn", "Queens", mode="transit", departure_time=now + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Brooklyn&key=%s&destination=Queens&mode=transit&" + "departure_time=%d" % (self.key, time.mktime(now.timetuple())), + responses.calls[0].request.url, + ) + + @responses.activate + def test_boston_to_concord_via_charlestown_and_lexington(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions( + "Boston, MA", "Concord, MA", waypoints=["Charlestown, MA", "Lexington, MA"] + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Boston%%2C+MA&destination=Concord%%2C+MA&" + "waypoints=Charlestown%%2C+MA%%7CLexington%%2C+MA&" + "key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_adelaide_wine_tour(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions( + "Adelaide, SA", + "Adelaide, SA", + waypoints=[ + "Barossa Valley, SA", + "Clare, SA", + "Connawarra, SA", + "McLaren Vale, SA", + ], + optimize_waypoints=True, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Adelaide%%2C+SA&destination=Adelaide%%2C+SA&" + "waypoints=optimize%%3Atrue%%7CBarossa+Valley%%2C+" + "SA%%7CClare%%2C+SA%%7CConnawarra%%2C+SA%%7CMcLaren+" + "Vale%%2C+SA&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_toledo_to_madrid_in_spain(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions("Toledo", "Madrid", region="es") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Toledo®ion=es&destination=Madrid&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_zero_results_returns_response(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"ZERO_RESULTS","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions("Toledo", "Madrid") + self.assertIsNotNone(routes) + self.assertEqual(0, len(routes)) + + @responses.activate + def test_language_parameter(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions("Toledo", "Madrid", region="es", language="es") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Toledo®ion=es&destination=Madrid&key=%s&" + "language=es" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_alternatives(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/directions/json", + body='{"status":"OK","routes":[]}', + status=200, + content_type="application/json", + ) + + routes = self.client.directions( + "Sydney Town Hall", "Parramatta Town Hall", alternatives=True + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/directions/json?" + "origin=Sydney+Town+Hall&destination=Parramatta+Town+Hall&" + "alternatives=true&key=%s" % self.key, + responses.calls[0].request.url, + ) diff --git a/tests/test_distance_matrix.py b/tests/test_distance_matrix.py new file mode 100644 index 00000000..a906f8ce --- /dev/null +++ b/tests/test_distance_matrix.py @@ -0,0 +1,183 @@ +# +# Copyright 2014 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the distance matrix module.""" + +from datetime import datetime +import time + +import responses + +import googlemaps +from . import TestCase + + +class DistanceMatrixTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_basic_params(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/distancematrix/json", + body='{"status":"OK","rows":[]}', + status=200, + content_type="application/json", + ) + + origins = [ + "Perth, Australia", + "Sydney, Australia", + "Melbourne, Australia", + "Adelaide, Australia", + "Brisbane, Australia", + "Darwin, Australia", + "Hobart, Australia", + "Canberra, Australia", + ] + destinations = [ + "Uluru, Australia", + "Kakadu, Australia", + "Blue Mountains, Australia", + "Bungle Bungles, Australia", + "The Pinnacles, Australia", + ] + + matrix = self.client.distance_matrix(origins, destinations) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/distancematrix/json?" + "key=%s&origins=Perth%%2C+Australia%%7CSydney%%2C+" + "Australia%%7CMelbourne%%2C+Australia%%7CAdelaide%%2C+" + "Australia%%7CBrisbane%%2C+Australia%%7CDarwin%%2C+" + "Australia%%7CHobart%%2C+Australia%%7CCanberra%%2C+Australia&" + "destinations=Uluru%%2C+Australia%%7CKakadu%%2C+Australia%%7C" + "Blue+Mountains%%2C+Australia%%7CBungle+Bungles%%2C+Australia" + "%%7CThe+Pinnacles%%2C+Australia" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_mixed_params(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/distancematrix/json", + body='{"status":"OK","rows":[]}', + status=200, + content_type="application/json", + ) + + origins = ["Bobcaygeon ON", [41.43206, -81.38992]] + destinations = [ + (43.012486, -83.6964149), + {"lat": 42.8863855, "lng": -78.8781627}, + ] + + matrix = self.client.distance_matrix(origins, destinations) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/distancematrix/json?" + "key=%s&origins=Bobcaygeon+ON%%7C41.43206%%2C-81.38992&" + "destinations=43.012486%%2C-83.6964149%%7C42.8863855%%2C" + "-78.8781627" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_all_params(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/distancematrix/json", + body='{"status":"OK","rows":[]}', + status=200, + content_type="application/json", + ) + + origins = [ + "Perth, Australia", + "Sydney, Australia", + "Melbourne, Australia", + "Adelaide, Australia", + "Brisbane, Australia", + "Darwin, Australia", + "Hobart, Australia", + "Canberra, Australia", + ] + destinations = [ + "Uluru, Australia", + "Kakadu, Australia", + "Blue Mountains, Australia", + "Bungle Bungles, Australia", + "The Pinnacles, Australia", + ] + + now = datetime.now() + matrix = self.client.distance_matrix( + origins, + destinations, + mode="driving", + language="en-AU", + avoid="tolls", + units="imperial", + departure_time=now, + traffic_model="optimistic", + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/distancematrix/json?" + "origins=Perth%%2C+Australia%%7CSydney%%2C+Australia%%7C" + "Melbourne%%2C+Australia%%7CAdelaide%%2C+Australia%%7C" + "Brisbane%%2C+Australia%%7CDarwin%%2C+Australia%%7CHobart%%2C+" + "Australia%%7CCanberra%%2C+Australia&language=en-AU&" + "avoid=tolls&mode=driving&key=%s&units=imperial&" + "destinations=Uluru%%2C+Australia%%7CKakadu%%2C+Australia%%7C" + "Blue+Mountains%%2C+Australia%%7CBungle+Bungles%%2C+Australia" + "%%7CThe+Pinnacles%%2C+Australia&departure_time=%d" + "&traffic_model=optimistic" % (self.key, time.mktime(now.timetuple())), + responses.calls[0].request.url, + ) + + @responses.activate + def test_lang_param(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/distancematrix/json", + body='{"status":"OK","rows":[]}', + status=200, + content_type="application/json", + ) + + origins = ["Vancouver BC", "Seattle"] + destinations = ["San Francisco", "Victoria BC"] + + matrix = self.client.distance_matrix( + origins, destinations, language="fr-FR", mode="bicycling" + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/distancematrix/json?" + "key=%s&language=fr-FR&mode=bicycling&" + "origins=Vancouver+BC%%7CSeattle&" + "destinations=San+Francisco%%7CVictoria+BC" % self.key, + responses.calls[0].request.url, + ) diff --git a/tests/test_elevation.py b/tests/test_elevation.py new file mode 100644 index 00000000..165f95b3 --- /dev/null +++ b/tests/test_elevation.py @@ -0,0 +1,134 @@ +# +# Copyright 2014 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the elevation module.""" + +import datetime + +import responses + +import googlemaps +from . import TestCase + + +class ElevationTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_elevation_single(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/elevation/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.elevation((40.714728, -73.998672)) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/elevation/json?" + "locations=enc:abowFtzsbM&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_elevation_single_list(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/elevation/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.elevation([(40.714728, -73.998672)]) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/elevation/json?" + "locations=enc:abowFtzsbM&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_elevation_multiple(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/elevation/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + locations = [(40.714728, -73.998672), (-34.397, 150.644)] + results = self.client.elevation(locations) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/elevation/json?" + "locations=enc:abowFtzsbMhgmiMuobzi@&key=%s" % self.key, + responses.calls[0].request.url, + ) + + def test_elevation_along_path_single(self): + with self.assertRaises(googlemaps.exceptions.ApiError): + results = self.client.elevation_along_path([(40.714728, -73.998672)], 5) + + @responses.activate + def test_elevation_along_path(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/elevation/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + path = [(40.714728, -73.998672), (-34.397, 150.644)] + + results = self.client.elevation_along_path(path, 5) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/elevation/json?" + "path=enc:abowFtzsbMhgmiMuobzi@&" + "key=%s&samples=5" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_short_latlng(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/elevation/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.elevation((40, -73)) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/elevation/json?" + "locations=40,-73&key=%s" % self.key, + responses.calls[0].request.url, + ) diff --git a/tests/test_geocoding.py b/tests/test_geocoding.py new file mode 100644 index 00000000..dfd9376d --- /dev/null +++ b/tests/test_geocoding.py @@ -0,0 +1,348 @@ +# This Python file uses the following encoding: utf-8 +# +# Copyright 2014 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the geocoding module.""" + +import datetime + +import responses + +import googlemaps +from . import TestCase + + +class GeocodingTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_simple_geocode(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode("Sydney") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&address=Sydney" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_reverse_geocode(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.reverse_geocode((-33.8674869, 151.2069902)) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "latlng=-33.8674869,151.2069902&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_geocoding_the_googleplex(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode("1600 Amphitheatre Parkway, " "Mountain View, CA") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&address=1600+Amphitheatre+Parkway%%2C+Mountain" + "+View%%2C+CA" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_geocode_with_bounds(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode( + "Winnetka", + bounds={ + "southwest": (34.172684, -118.604794), + "northeast": (34.236144, -118.500938), + }, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "bounds=34.172684%%2C-118.604794%%7C34.236144%%2C" + "-118.500938&key=%s&address=Winnetka" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_geocode_with_region_biasing(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode("Toledo", region="es") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "region=es&key=%s&address=Toledo" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_geocode_with_component_filter(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode("santa cruz", components={"country": "ES"}) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&components=country%%3AES&address=santa+cruz" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_geocode_with_multiple_component_filters(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode( + "Torun", components={"administrative_area": "TX", "country": "US"} + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&components=administrative_area%%3ATX%%7C" + "country%%3AUS&address=Torun" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_geocode_with_just_components(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode( + components={ + "route": "Annegatan", + "administrative_area": "Helsinki", + "country": "Finland", + } + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&components=administrative_area%%3AHelsinki" + "%%7Ccountry%%3AFinland%%7Croute%%3AAnnegatan" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_simple_reverse_geocode(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.reverse_geocode((40.714224, -73.961452)) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "latlng=40.714224%%2C-73.961452&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_reverse_geocode_restricted_by_type(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.reverse_geocode( + (40.714224, -73.961452), + location_type="ROOFTOP", + result_type="street_address", + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "latlng=40.714224%%2C-73.961452&result_type=street_address&" + "key=%s&location_type=ROOFTOP" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_reverse_geocode_multiple_location_types(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.reverse_geocode( + (40.714224, -73.961452), + location_type=["ROOFTOP", "RANGE_INTERPOLATED"], + result_type="street_address", + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "latlng=40.714224%%2C-73.961452&result_type=street_address&" + "key=%s&location_type=ROOFTOP%%7CRANGE_INTERPOLATED" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_reverse_geocode_multiple_result_types(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.reverse_geocode( + (40.714224, -73.961452), + location_type="ROOFTOP", + result_type=["street_address", "route"], + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "latlng=40.714224%%2C-73.961452&result_type=street_address" + "%%7Croute&key=%s&location_type=ROOFTOP" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_partial_match(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode("Pirrama Pyrmont") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&address=Pirrama+Pyrmont" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_utf_results(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode(components={"postal_code": "96766"}) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&components=postal_code%%3A96766" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_utf8_request(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + self.client.geocode(self.u("\\u4e2d\\u56fd")) # China + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&address=%s" % (self.key, "%E4%B8%AD%E5%9B%BD"), + responses.calls[0].request.url, + ) diff --git a/googlemaps/test/test_geolocation.py b/tests/test_geolocation.py similarity index 63% rename from googlemaps/test/test_geolocation.py rename to tests/test_geolocation.py index 64b6f36a..8eeb2cbc 100644 --- a/googlemaps/test/test_geolocation.py +++ b/tests/test_geolocation.py @@ -21,25 +21,28 @@ import responses import googlemaps -import googlemaps.test as _test +from . import TestCase -class GeolocationTest(_test.TestCase): - +class GeolocationTest(TestCase): def setUp(self): - self.key = 'AIzaasdf' + self.key = "AIzaasdf" self.client = googlemaps.Client(self.key) @responses.activate def test_simple_geolocate(self): - responses.add(responses.POST, - 'https://www.googleapis.com/geolocation/v1/geolocate', - body='{"location": {"lat": 51.0,"lng": -0.1},"accuracy": 1200.4}', - status=200, - content_type='application/json') + responses.add( + responses.POST, + "https://www.googleapis.com/geolocation/v1/geolocate", + body='{"location": {"lat": 51.0,"lng": -0.1},"accuracy": 1200.4}', + status=200, + content_type="application/json", + ) results = self.client.geolocate() self.assertEqual(1, len(responses.calls)) - self.assertURLEqual('https://www.googleapis.com/geolocation/v1/geolocate?' - 'key=%s' % self.key, responses.calls[0].request.url) + self.assertURLEqual( + "https://www.googleapis.com/geolocation/v1/geolocate?" "key=%s" % self.key, + responses.calls[0].request.url, + ) diff --git a/googlemaps/test/test_maps.py b/tests/test_maps.py similarity index 52% rename from googlemaps/test/test_maps.py rename to tests/test_maps.py index a20b84fe..db83ee04 100644 --- a/googlemaps/test/test_maps.py +++ b/tests/test_maps.py @@ -22,14 +22,13 @@ import responses import googlemaps -import googlemaps.test as _test +from . import TestCase from googlemaps.maps import StaticMapMarker from googlemaps.maps import StaticMapPath -class MapsTest(_test.TestCase): - +class MapsTest(TestCase): def setUp(self): self.key = "AIzaasdf" self.client = googlemaps.Client(self.key) @@ -38,13 +37,13 @@ def setUp(self): def test_static_map_marker(self): marker = StaticMapMarker( locations=[{"lat": -33.867486, "lng": 151.206990}, "Sydney"], - size='small', color='blue', label="S" + size="small", + color="blue", + label="S", ) self.assertEqual( - "size:small|color:blue|label:S|" - "-33.867486,151.20699|Sydney", - str(marker) + "size:small|color:blue|label:S|" "-33.867486,151.20699|Sydney", str(marker) ) with self.assertRaises(ValueError): @@ -54,64 +53,75 @@ def test_static_map_marker(self): def test_static_map_path(self): path = StaticMapPath( points=[{"lat": -33.867486, "lng": 151.206990}, "Sydney"], - weight=5, color="red", geodesic=True, fillcolor="Red" + weight=5, + color="red", + geodesic=True, + fillcolor="Red", ) self.assertEqual( - "weight:5|color:red|fillcolor:Red|""geodesic:True|" + "weight:5|color:red|fillcolor:Red|" + "geodesic:True|" "-33.867486,151.20699|Sydney", - str(path) + str(path), ) @responses.activate def test_download(self): - url = 'https://maps.googleapis.com/maps/api/staticmap' + url = "https://maps.googleapis.com/maps/api/staticmap" responses.add(responses.GET, url, status=200) path = StaticMapPath( - points=[(62.107733,-145.541936), 'Delta+Junction,AK'], - weight=5, color="red" + points=[(62.107733, -145.541936), "Delta+Junction,AK"], + weight=5, + color="red", ) m1 = StaticMapMarker( - locations=[(62.107733,-145.541936)], - color="blue", label="S" + locations=[(62.107733, -145.541936)], color="blue", label="S" ) m2 = StaticMapMarker( - locations=['Delta+Junction,AK'], - size="tiny", color="green" + locations=["Delta+Junction,AK"], size="tiny", color="green" ) m3 = StaticMapMarker( - locations=["Tok,AK"], - size="mid", color="0xFFFF00", label="C" + locations=["Tok,AK"], size="mid", color="0xFFFF00", label="C" ) response = self.client.static_map( - size=(400, 400), zoom=6, center=(63.259591,-144.667969), - maptype="hybrid", format="png", scale=2, visible=["Tok,AK"], - path=path, markers=[m1, m2, m3] + size=(400, 400), + zoom=6, + center=(63.259591, -144.667969), + maptype="hybrid", + format="png", + scale=2, + visible=["Tok,AK"], + path=path, + markers=[m1, m2, m3], ) self.assertTrue(isinstance(response, GeneratorType)) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - '%s?center=63.259591%%2C-144.667969&format=png&maptype=hybrid&' - 'markers=color%%3Ablue%%7Clabel%%3AS%%7C62.107733%%2C-145.541936&' - 'markers=size%%3Atiny%%7Ccolor%%3Agreen%%7CDelta%%2BJunction%%2CAK&' - 'markers=size%%3Amid%%7Ccolor%%3A0xFFFF00%%7Clabel%%3AC%%7CTok%%2CAK&' - 'path=weight%%3A5%%7Ccolor%%3Ared%%7C62.107733%%2C-145.541936%%7CDelta%%2BJunction%%2CAK&' - 'scale=2&size=400x400&visible=Tok%%2CAK&zoom=6&key=%s' - % (url, self.key), responses.calls[0].request.url) + "%s?center=63.259591%%2C-144.667969&format=png&maptype=hybrid&" + "markers=color%%3Ablue%%7Clabel%%3AS%%7C62.107733%%2C-145.541936&" + "markers=size%%3Atiny%%7Ccolor%%3Agreen%%7CDelta%%2BJunction%%2CAK&" + "markers=size%%3Amid%%7Ccolor%%3A0xFFFF00%%7Clabel%%3AC%%7CTok%%2CAK&" + "path=weight%%3A5%%7Ccolor%%3Ared%%7C62.107733%%2C-145.541936%%7CDelta%%2BJunction%%2CAK&" + "scale=2&size=400x400&visible=Tok%%2CAK&zoom=6&key=%s" % (url, self.key), + responses.calls[0].request.url, + ) with self.assertRaises(ValueError): self.client.static_map(size=(400, 400)) with self.assertRaises(ValueError): - self.client.static_map(size=(400, 400), center=(63.259591,-144.667969), - zoom=6, format='test') + self.client.static_map( + size=(400, 400), center=(63.259591, -144.667969), zoom=6, format="test" + ) with self.assertRaises(ValueError): - self.client.static_map(size=(400, 400), center=(63.259591,-144.667969), - zoom=6, maptype='test') + self.client.static_map( + size=(400, 400), center=(63.259591, -144.667969), zoom=6, maptype="test" + ) diff --git a/tests/test_places.py b/tests/test_places.py new file mode 100644 index 00000000..5316fe8e --- /dev/null +++ b/tests/test_places.py @@ -0,0 +1,249 @@ +# This Python file uses the following encoding: utf-8 +# +# Copyright 2016 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the places module.""" + +import uuid + +from types import GeneratorType + +import responses + +import googlemaps +from . import TestCase + + +class PlacesTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + self.location = (-33.86746, 151.207090) + self.type = "liquor_store" + self.language = "en-AU" + self.region = "AU" + self.radius = 100 + + @responses.activate + def test_places_find(self): + url = "https://maps.googleapis.com/maps/api/place/findplacefromtext/json" + responses.add( + responses.GET, + url, + body='{"status": "OK", "candidates": []}', + status=200, + content_type="application/json", + ) + + self.client.find_place( + "restaurant", + "textquery", + fields=["business_status", "geometry/location", "place_id"], + location_bias="point:90,90", + language=self.language, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?language=en-AU&inputtype=textquery&" + "locationbias=point:90,90&input=restaurant" + "&fields=business_status,geometry/location,place_id&key=%s" + % (url, self.key), + responses.calls[0].request.url, + ) + + with self.assertRaises(ValueError): + self.client.find_place("restaurant", "invalid") + with self.assertRaises(ValueError): + self.client.find_place( + "restaurant", "textquery", fields=["geometry", "invalid"] + ) + with self.assertRaises(ValueError): + self.client.find_place("restaurant", "textquery", location_bias="invalid") + + @responses.activate + def test_places_text_search(self): + url = "https://maps.googleapis.com/maps/api/place/textsearch/json" + responses.add( + responses.GET, + url, + body='{"status": "OK", "results": [], "html_attributions": []}', + status=200, + content_type="application/json", + ) + + self.client.places( + "restaurant", + location=self.location, + radius=self.radius, + region=self.region, + language=self.language, + min_price=1, + max_price=4, + open_now=True, + type=self.type, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?language=en-AU&location=-33.86746%%2C151.20709&" + "maxprice=4&minprice=1&opennow=true&query=restaurant&" + "radius=100®ion=AU&type=liquor_store&key=%s" % (url, self.key), + responses.calls[0].request.url, + ) + + @responses.activate + def test_places_nearby_search(self): + url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json" + responses.add( + responses.GET, + url, + body='{"status": "OK", "results": [], "html_attributions": []}', + status=200, + content_type="application/json", + ) + + self.client.places_nearby( + location=self.location, + keyword="foo", + language=self.language, + min_price=1, + max_price=4, + name="bar", + open_now=True, + rank_by="distance", + type=self.type, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?keyword=foo&language=en-AU&location=-33.86746%%2C151.20709&" + "maxprice=4&minprice=1&name=bar&opennow=true&rankby=distance&" + "type=liquor_store&key=%s" % (url, self.key), + responses.calls[0].request.url, + ) + + with self.assertRaises(ValueError): + self.client.places_nearby(radius=self.radius) + with self.assertRaises(ValueError): + self.client.places_nearby(self.location, rank_by="distance") + + with self.assertRaises(ValueError): + self.client.places_nearby( + location=self.location, + rank_by="distance", + keyword="foo", + radius=self.radius, + ) + + @responses.activate + def test_place_detail(self): + url = "https://maps.googleapis.com/maps/api/place/details/json" + responses.add( + responses.GET, + url, + body='{"status": "OK", "result": {}, "html_attributions": []}', + status=200, + content_type="application/json", + ) + + self.client.place( + "ChIJN1t_tDeuEmsRUsoyG83frY4", + fields=["business_status", "geometry/location", "place_id"], + language=self.language, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4" + "&key=%s&fields=business_status,geometry/location,place_id" + % (url, self.key), + responses.calls[0].request.url, + ) + + with self.assertRaises(ValueError): + self.client.place( + "ChIJN1t_tDeuEmsRUsoyG83frY4", fields=["geometry", "invalid"] + ) + + @responses.activate + def test_photo(self): + url = "https://maps.googleapis.com/maps/api/place/photo" + responses.add(responses.GET, url, status=200) + + ref = "CnRvAAAAwMpdHeWlXl-lH0vp7lez4znKPIWSWvgvZFISdKx45AwJVP1Qp37YOrH7sqHMJ8C-vBDC546decipPHchJhHZL94RcTUfPa1jWzo-rSHaTlbNtjh-N68RkcToUCuY9v2HNpo5mziqkir37WU8FJEqVBIQ4k938TI3e7bf8xq-uwDZcxoUbO_ZJzPxremiQurAYzCTwRhE_V0" + response = self.client.places_photo(ref, max_width=100) + + self.assertTrue(isinstance(response, GeneratorType)) + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?maxwidth=100&photoreference=%s&key=%s" % (url, ref, self.key), + responses.calls[0].request.url, + ) + + @responses.activate + def test_autocomplete(self): + url = "https://maps.googleapis.com/maps/api/place/autocomplete/json" + responses.add( + responses.GET, + url, + body='{"status": "OK", "predictions": []}', + status=200, + content_type="application/json", + ) + + session_token = uuid.uuid4().hex + + self.client.places_autocomplete( + "Google", + session_token=session_token, + offset=3, + location=self.location, + radius=self.radius, + language=self.language, + types="geocode", + components={"country": "au"}, + strict_bounds=True, + ) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?components=country%%3Aau&input=Google&language=en-AU&" + "location=-33.86746%%2C151.20709&offset=3&radius=100&" + "strictbounds=true&types=geocode&key=%s&sessiontoken=%s" + % (url, self.key, session_token), + responses.calls[0].request.url, + ) + + @responses.activate + def test_autocomplete_query(self): + url = "https://maps.googleapis.com/maps/api/place/queryautocomplete/json" + responses.add( + responses.GET, + url, + body='{"status": "OK", "predictions": []}', + status=200, + content_type="application/json", + ) + + self.client.places_autocomplete_query("pizza near New York") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "%s?input=pizza+near+New+York&key=%s" % (url, self.key), + responses.calls[0].request.url, + ) diff --git a/tests/test_roads.py b/tests/test_roads.py new file mode 100644 index 00000000..bfea2bf7 --- /dev/null +++ b/tests/test_roads.py @@ -0,0 +1,158 @@ +# +# Copyright 2015 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the roads module.""" + + +import responses + +import googlemaps +from . import TestCase + + +class RoadsTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_snap(self): + responses.add( + responses.GET, + "https://roads.googleapis.com/v1/snapToRoads", + body='{"snappedPoints":["foo"]}', + status=200, + content_type="application/json", + ) + + results = self.client.snap_to_roads((40.714728, -73.998672)) + self.assertEqual("foo", results[0]) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://roads.googleapis.com/v1/snapToRoads?" + "path=40.714728%%2C-73.998672&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_nearest_roads(self): + responses.add( + responses.GET, + "https://roads.googleapis.com/v1/nearestRoads", + body='{"snappedPoints":["foo"]}', + status=200, + content_type="application/json", + ) + + results = self.client.nearest_roads((40.714728, -73.998672)) + self.assertEqual("foo", results[0]) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://roads.googleapis.com/v1/nearestRoads?" + "points=40.714728%%2C-73.998672&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_path(self): + responses.add( + responses.GET, + "https://roads.googleapis.com/v1/speedLimits", + body='{"speedLimits":["foo"]}', + status=200, + content_type="application/json", + ) + + results = self.client.snapped_speed_limits([(1, 2), (3, 4)]) + self.assertEqual("foo", results["speedLimits"][0]) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://roads.googleapis.com/v1/speedLimits?" + "path=1%%2C2|3%%2C4" + "&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_speedlimits(self): + responses.add( + responses.GET, + "https://roads.googleapis.com/v1/speedLimits", + body='{"speedLimits":["foo"]}', + status=200, + content_type="application/json", + ) + + results = self.client.speed_limits("id1") + self.assertEqual("foo", results[0]) + self.assertEqual( + "https://roads.googleapis.com/v1/speedLimits?" + "placeId=id1&key=%s" % self.key, + responses.calls[0].request.url, + ) + + @responses.activate + def test_speedlimits_multiple(self): + responses.add( + responses.GET, + "https://roads.googleapis.com/v1/speedLimits", + body='{"speedLimits":["foo"]}', + status=200, + content_type="application/json", + ) + + results = self.client.speed_limits(["id1", "id2", "id3"]) + self.assertEqual("foo", results[0]) + self.assertEqual( + "https://roads.googleapis.com/v1/speedLimits?" + "placeId=id1&placeId=id2&placeId=id3" + "&key=%s" % self.key, + responses.calls[0].request.url, + ) + + def test_clientid_not_accepted(self): + client = googlemaps.Client(client_id="asdf", client_secret="asdf") + + with self.assertRaises(ValueError): + client.speed_limits("foo") + + @responses.activate + def test_retry(self): + class request_callback: + def __init__(self): + self.first_req = True + + def __call__(self, req): + if self.first_req: + self.first_req = False + return (500, {}, "Internal Server Error.") + return (200, {}, '{"speedLimits":[]}') + + responses.add_callback( + responses.GET, + "https://roads.googleapis.com/v1/speedLimits", + content_type="application/json", + callback=request_callback(), + ) + + self.client.speed_limits([]) + + self.assertEqual(2, len(responses.calls)) + self.assertEqual(responses.calls[0].request.url, responses.calls[1].request.url) diff --git a/googlemaps/test/test_timezone.py b/tests/test_timezone.py similarity index 56% rename from googlemaps/test/test_timezone.py rename to tests/test_timezone.py index 23daeca3..9d2edc1c 100644 --- a/googlemaps/test/test_timezone.py +++ b/tests/test_timezone.py @@ -21,58 +21,62 @@ import datetime import responses -import mock - +from unittest import mock import googlemaps -import googlemaps.test as _test - +from . import TestCase -class TimezoneTest(_test.TestCase): +class TimezoneTest(TestCase): def setUp(self): self.key = "AIzaasdf" self.client = googlemaps.Client(self.key) @responses.activate def test_los_angeles(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/timezone/json", - body='{"status":"OK"}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/timezone/json", + body='{"status":"OK"}', + status=200, + content_type="application/json", + ) ts = 1331766000 timezone = self.client.timezone((39.603481, -119.682251), ts) self.assertIsNotNone(timezone) self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://maps.googleapis.com/maps/api/timezone/json" - "?location=39.603481,-119.682251×tamp=%d" - "&key=%s" % - (ts, self.key), - responses.calls[0].request.url) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/timezone/json" + "?location=39.603481,-119.682251×tamp=%d" + "&key=%s" % (ts, self.key), + responses.calls[0].request.url, + ) class MockDatetime(object): - def now(self): return datetime.datetime.fromtimestamp(1608) + utcnow = now @responses.activate @mock.patch("googlemaps.timezone.datetime", MockDatetime()) def test_los_angeles_with_no_timestamp(self): - responses.add(responses.GET, - "https://maps.googleapis.com/maps/api/timezone/json", - body='{"status":"OK"}', - status=200, - content_type="application/json") + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/timezone/json", + body='{"status":"OK"}', + status=200, + content_type="application/json", + ) timezone = self.client.timezone((39.603481, -119.682251)) self.assertIsNotNone(timezone) self.assertEqual(1, len(responses.calls)) - self.assertURLEqual("https://maps.googleapis.com/maps/api/timezone/json" - "?location=39.603481,-119.682251×tamp=%d" - "&key=%s" % - (1608, self.key), - responses.calls[0].request.url) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/timezone/json" + "?location=39.603481,-119.682251×tamp=%d" + "&key=%s" % (1608, self.key), + responses.calls[0].request.url, + ) From 5fd6f0e962a2afbff4e6190e4ae91841b756ff4f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 27 Apr 2020 17:12:45 +0000 Subject: [PATCH 090/166] chore(release): 4.3.1 [skip ci] ## [4.3.1](https://github.com/googlemaps/google-maps-services-python/compare/v4.3.0...v4.3.1) (2020-04-27) ### Bug Fixes * cleanup test and remove from package ([#361](https://github.com/googlemaps/google-maps-services-python/issues/361)) ([428b5c9](https://github.com/googlemaps/google-maps-services-python/commit/428b5c9d76d0c5c5e5000cbec8fc9ace810ac856)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 49ad240f..d5730529 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.3.0" +__version__ = "4.3.1" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 198d283e..8771dec9 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.3.0", + version="4.3.1", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From c6292185c1e2722119ea576c4a22b26b8d6c9d02 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 4 May 2020 09:54:39 -0700 Subject: [PATCH 091/166] docs: removed old image from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2b6ad08d..e2c8d1d4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ Python Client for Google Maps Services Use Python? Want to geocode something? Looking for directions? Maybe matrices of directions? This library brings the Google Maps Platform Web Services to your Python application. -![Analytics](https://maps-ga-beacon.appspot.com/UA-12846745-20/google-maps-services-python/readme?pixel) The Python Client for Google Maps Services is a Python Client library for the following Google Maps APIs: From cc13049aa2490351da78cb088793e8565d25e9a3 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 8 May 2020 09:11:25 -0700 Subject: [PATCH 092/166] docs: update broken link in readme closes #363 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2c8d1d4..31840082 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ directions_result = gmaps.directions("Sydney Town Hall", departure_time=now) ``` -For more usage examples, check out [the tests](https://github.com/googlemaps/google-maps-services-python/tree/master/googlemaps/test). +For more usage examples, check out [the tests](https://github.com/googlemaps/google-maps-services-python/tree/master/tests). ## Features From c875f3561c040c9b16c2d5c2b58d75cb0a7793cf Mon Sep 17 00:00:00 2001 From: thatguysimon Date: Thu, 14 May 2020 19:14:23 +0300 Subject: [PATCH 093/166] feat: Allow overriding base_url on Client object initialization (#364) --- googlemaps/client.py | 13 +++++++++++-- tests/test_client.py | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index ae2b4891..546f7796 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -53,7 +53,8 @@ def __init__(self, key=None, client_id=None, client_secret=None, timeout=None, connect_timeout=None, read_timeout=None, retry_timeout=60, requests_kwargs=None, queries_per_second=50, channel=None, - retry_over_query_limit=True, experience_id=None): + retry_over_query_limit=True, experience_id=None, + base_url=_DEFAULT_BASE_URL): """ :param key: Maps API key. Required, unless "client_id" and "client_secret" are set. Most users should use an API key. @@ -115,6 +116,10 @@ def __init__(self, key=None, client_id=None, client_secret=None, implemented. See the official requests docs for more info: http://docs.python-requests.org/en/latest/api/#main-interface :type requests_kwargs: dict + + :param base_url: The base URL for all requests. Defaults to the Maps API + server. Should not have a trailing slash. + :type base_url: string """ if not key and not (client_secret and client_id): @@ -167,6 +172,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.retry_over_query_limit = retry_over_query_limit self.sent_times = collections.deque("", queries_per_second) self.set_experience_id(experience_id) + self.base_url = base_url def set_experience_id(self, *experience_id_args): """Sets the value for the HTTP header field name @@ -204,7 +210,7 @@ def clear_experience_id(self): self.requests_kwargs["headers"] = headers def _request(self, url, params, first_request_time=None, retry_counter=0, - base_url=_DEFAULT_BASE_URL, accepts_clientid=True, + base_url=None, accepts_clientid=True, extract_body=None, requests_kwargs=None, post_json=None): """Performs HTTP GET/POST with credentials, returning the body as JSON. @@ -246,6 +252,9 @@ def _request(self, url, params, first_request_time=None, retry_counter=0, exceute a request. """ + if base_url is None: + base_url = self.base_url + if not first_request_time: first_request_time = datetime.now() diff --git a/tests/test_client.py b/tests/test_client.py index 3ad92772..a18221fa 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -209,7 +209,22 @@ def test_transport_error(self): self.assertEqual(e.exception.status_code, 404) @responses.activate - def test_host_override(self): + def test_host_override_on_init(self): + responses.add( + responses.GET, + "https://foo.com/bar", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + client = googlemaps.Client(key="AIzaasdf", base_url="https://foo.com") + client._get("/bar", {}) + + self.assertEqual(1, len(responses.calls)) + + @responses.activate + def test_host_override_per_request(self): responses.add( responses.GET, "https://foo.com/bar", From 865cf78caa46aaa8d1e5918e8469bb3b2ac06314 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 14 May 2020 16:18:36 +0000 Subject: [PATCH 094/166] chore(release): 4.4.0 [skip ci] # [4.4.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.3.1...v4.4.0) (2020-05-14) ### Features * Allow overriding base_url on Client object initialization ([#364](https://github.com/googlemaps/google-maps-services-python/issues/364)) ([c875f35](https://github.com/googlemaps/google-maps-services-python/commit/c875f3561c040c9b16c2d5c2b58d75cb0a7793cf)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index d5730529..3255e2c1 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.3.1" +__version__ = "4.4.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 8771dec9..be72bd82 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.3.1", + version="4.4.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 5b289d7484ae6dcf15575bdd2b5c2bd32cd68aca Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 26 May 2020 08:28:46 -0700 Subject: [PATCH 095/166] fix: mark permanently_closed as deprecated (#365) --- googlemaps/places.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 1ead5fe4..0456b461 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -100,6 +100,13 @@ ^ PLACES_DETAIL_FIELDS_ATMOSPHERE ) +DEPRECATED_FIELDS = {"permanently_closed"} +DEPRECATED_FIELDS_MESSAGE = ( + "Fields, %s, are deprecated. " + "Read more at https://developers.google.com/maps/deprecations." +) + + def find_place( client, input, input_type, fields=None, location_bias=None, language=None ): @@ -143,7 +150,14 @@ def find_place( "the given value is invalid: '%s'" % input_type ) - if fields: + if fields: + deprecated_fields = set(fields) & DEPRECATED_FIELDS + if deprecated_fields: + warnings.warn( + DEPRECATED_FIELDS_MESSAGE % str(list(deprecated_fields)), + DeprecationWarning, + ) + invalid_fields = set(fields) - PLACES_FIND_FIELDS if invalid_fields: raise ValueError( @@ -422,6 +436,13 @@ def place(client, place_id, session_token=None, fields=None, language=None): params = {"placeid": place_id} if fields: + deprecated_fields = set(fields) & DEPRECATED_FIELDS + if deprecated_fields: + warnings.warn( + DEPRECATED_FIELDS_MESSAGE % str(list(deprecated_fields)), + DeprecationWarning, + ) + invalid_fields = set(fields) - PLACES_DETAIL_FIELDS if invalid_fields: raise ValueError( From 104ab3466d40a1061f511ad689a56fefa9fbdc87 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 26 May 2020 15:29:57 +0000 Subject: [PATCH 096/166] chore(release): 4.4.1 [skip ci] ## [4.4.1](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.0...v4.4.1) (2020-05-26) ### Bug Fixes * mark permanently_closed as deprecated ([#365](https://github.com/googlemaps/google-maps-services-python/issues/365)) ([5b289d7](https://github.com/googlemaps/google-maps-services-python/commit/5b289d7484ae6dcf15575bdd2b5c2bd32cd68aca)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 3255e2c1..f98dfcc0 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.0" +__version__ = "4.4.1" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index be72bd82..038f0e37 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.0", + version="4.4.1", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From d39ff32820a8e77e2f789af81590fcc3a3770797 Mon Sep 17 00:00:00 2001 From: Mariatta Date: Tue, 14 Jul 2020 08:09:19 -0700 Subject: [PATCH 097/166] docs: fix params for find_place. (#369) --- googlemaps/places.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 0456b461..a4ab63ed 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -123,10 +123,9 @@ def find_place( or 'phonenumber'. :type input_type: string - :param fields: The fields specifying the types of place data to return, - separated by a comma. For full details see: + :param fields: The fields specifying the types of place data to return. For full details see: https://developers.google.com/places/web-service/search#FindPlaceRequests - :type input: list + :type fields: list :param location_bias: Prefer results in a specified area, by specifying either a radius plus lat/lng, or two lat/lng pairs From adf9cdeeb8eae5e1c07716b2138be2174e5972dd Mon Sep 17 00:00:00 2001 From: Bharat Raghunathan Date: Wed, 22 Jul 2020 17:52:21 +0000 Subject: [PATCH 098/166] docs: add proper code block formatting to the docs (#371) --- googlemaps/places.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index a4ab63ed..af070c4c 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -477,13 +477,13 @@ def places_photo(client, photo_reference, max_width=None, max_height=None): :rtype: iterator containing the raw image data, which typically can be used to save an image file locally. For example: - ``` + .. code-block:: python + f = open(local_filename, 'wb') for chunk in client.places_photo(photo_reference, max_width=100): if chunk: f.write(chunk) f.close() - ``` """ if not (max_width or max_height): From c942b865c2247a6dcdffd3dd5e20a367d8d705d2 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 28 Jul 2020 11:45:09 -0600 Subject: [PATCH 099/166] fix: static map label (#374) --- googlemaps/maps.py | 4 ++-- tests/test_maps.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/googlemaps/maps.py b/googlemaps/maps.py index eedcc422..763e0126 100644 --- a/googlemaps/maps.py +++ b/googlemaps/maps.py @@ -75,8 +75,8 @@ def __init__(self, locations, self.params.append("color:%s" % color) if label: - if len(label) != 1 or not label.isupper() or not label.isalnum(): - raise ValueError("Invalid label") + if len(label) != 1 or (label.isalpha() and not label.isupper()) or not label.isalnum(): + raise ValueError("Marker label must be alphanumeric and uppercase.") self.params.append("label:%s" % label) self.params.append(convert.location_list(locations)) diff --git a/tests/test_maps.py b/tests/test_maps.py index db83ee04..8db6298f 100644 --- a/tests/test_maps.py +++ b/tests/test_maps.py @@ -49,6 +49,8 @@ def test_static_map_marker(self): with self.assertRaises(ValueError): StaticMapMarker(locations=["Sydney"], label="XS") + self.assertEqual("label:1|Sydney", str(StaticMapMarker(locations=["Sydney"], label="1"))) + @responses.activate def test_static_map_path(self): path = StaticMapPath( From 5253cbc8b7e47bb1f5c1774b2a30705b158fcd8c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 28 Jul 2020 17:45:55 +0000 Subject: [PATCH 100/166] chore(release): 4.4.2 [skip ci] ## [4.4.2](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.1...v4.4.2) (2020-07-28) ### Bug Fixes * static map label ([#374](https://github.com/googlemaps/google-maps-services-python/issues/374)) ([c942b86](https://github.com/googlemaps/google-maps-services-python/commit/c942b865c2247a6dcdffd3dd5e20a367d8d705d2)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index f98dfcc0..b6f45f8c 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.1" +__version__ = "4.4.2" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 038f0e37..b9b3aeea 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.1", + version="4.4.2", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 44cafa059f0d76599525001fc39f448fc0c722c8 Mon Sep 17 00:00:00 2001 From: Harsh Mishra Date: Mon, 1 Mar 2021 23:48:50 +0530 Subject: [PATCH 101/166] fix: fixed code quality issues (#398) * Add .deepsource.toml * Fixed Object Inheritance * Replace ternary syntax with if expression * Use literal syntax to create data structure --- .deepsource.toml | 10 ++++++++++ googlemaps/client.py | 4 ++-- googlemaps/convert.py | 4 +--- googlemaps/maps.py | 11 +++-------- googlemaps/places.py | 29 +++++++++-------------------- tests/test_timezone.py | 2 +- 6 files changed, 26 insertions(+), 34 deletions(-) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 00000000..f772ce8c --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,10 @@ +version = 1 + +test_patterns = ["tests/**"] + +[[analyzers]] +name = "python" +enabled = true + + [analyzers.meta] + runtime_version = "3.x.x" diff --git a/googlemaps/client.py b/googlemaps/client.py index 546f7796..980509e2 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -43,10 +43,10 @@ _USER_AGENT = "GoogleGeoApiClientPython/%s" % googlemaps.__version__ _DEFAULT_BASE_URL = "https://maps.googleapis.com" -_RETRIABLE_STATUSES = set([500, 503, 504]) +_RETRIABLE_STATUSES = {500, 503, 504} -class Client(object): +class Client: """Performs requests to the Google Maps API web services.""" def __init__(self, key=None, client_id=None, client_secret=None, diff --git a/googlemaps/convert.py b/googlemaps/convert.py index 7dfa9882..2b3d056e 100644 --- a/googlemaps/convert.py +++ b/googlemaps/convert.py @@ -160,9 +160,7 @@ def _is_list(arg): return False if isinstance(arg, str): # Python 3-only, as str has __iter__ return False - return (not _has_method(arg, "strip") - and _has_method(arg, "__getitem__") - or _has_method(arg, "__iter__")) + return _has_method(arg, "__getitem__") if not _has_method(arg, "strip") else _has_method(arg, "__iter__") def is_string(val): diff --git a/googlemaps/maps.py b/googlemaps/maps.py index 763e0126..cc1a054e 100644 --- a/googlemaps/maps.py +++ b/googlemaps/maps.py @@ -20,16 +20,11 @@ from googlemaps import convert -MAPS_IMAGE_FORMATS = set( - ['png8', 'png', 'png32', 'gif', 'jpg', 'jpg-baseline'] -) +MAPS_IMAGE_FORMATS = {'png8', 'png', 'png32', 'gif', 'jpg', 'jpg-baseline'} -MAPS_MAP_TYPES = set( - ['roadmap', 'satellite', 'terrain', 'hybrid'] -) +MAPS_MAP_TYPES = {'roadmap', 'satellite', 'terrain', 'hybrid'} - -class StaticMapParam(object): +class StaticMapParam: """Base class to handle parameters for Maps Static API.""" def __init__(self): diff --git a/googlemaps/places.py b/googlemaps/places.py index af070c4c..f84bb84d 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -21,9 +21,7 @@ from googlemaps import convert -PLACES_FIND_FIELDS_BASIC = set( - [ - "business_status", +PLACES_FIND_FIELDS_BASIC = {"business_status", "formatted_address", "geometry", "geometry/location", @@ -42,13 +40,11 @@ "photos", "place_id", "plus_code", - "types", - ] -) + "types",} -PLACES_FIND_FIELDS_CONTACT = set(["opening_hours"]) +PLACES_FIND_FIELDS_CONTACT = {"opening_hours"} -PLACES_FIND_FIELDS_ATMOSPHERE = set(["price_level", "rating", "user_ratings_total"]) +PLACES_FIND_FIELDS_ATMOSPHERE = {"price_level", "rating", "user_ratings_total"} PLACES_FIND_FIELDS = ( PLACES_FIND_FIELDS_BASIC @@ -56,9 +52,7 @@ ^ PLACES_FIND_FIELDS_ATMOSPHERE ) -PLACES_DETAIL_FIELDS_BASIC = set( - [ - "address_component", +PLACES_DETAIL_FIELDS_BASIC = {"address_component", "adr_address", "business_status", "formatted_address", @@ -82,17 +76,11 @@ "type", "url", "utc_offset", - "vicinity", - ] -) + "vicinity",} -PLACES_DETAIL_FIELDS_CONTACT = set( - ["formatted_phone_number", "international_phone_number", "opening_hours", "website"] -) +PLACES_DETAIL_FIELDS_CONTACT = {"formatted_phone_number", "international_phone_number", "opening_hours", "website"} -PLACES_DETAIL_FIELDS_ATMOSPHERE = set( - ["price_level", "rating", "review", "user_ratings_total"] -) +PLACES_DETAIL_FIELDS_ATMOSPHERE = {"price_level", "rating", "review", "user_ratings_total"} PLACES_DETAIL_FIELDS = ( PLACES_DETAIL_FIELDS_BASIC @@ -658,3 +646,4 @@ def _autocomplete( url = "/maps/api/place/%sautocomplete/json" % url_part return client._request(url, params).get("predictions", []) + \ No newline at end of file diff --git a/tests/test_timezone.py b/tests/test_timezone.py index 9d2edc1c..a1d7394e 100644 --- a/tests/test_timezone.py +++ b/tests/test_timezone.py @@ -53,7 +53,7 @@ def test_los_angeles(self): responses.calls[0].request.url, ) - class MockDatetime(object): + class MockDatetime: def now(self): return datetime.datetime.fromtimestamp(1608) From f9c8febd3d2705a9a0ac329dd8e0ce6dcef4d0c2 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 1 Mar 2021 18:19:45 +0000 Subject: [PATCH 102/166] chore(release): 4.4.3 [skip ci] ## [4.4.3](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.2...v4.4.3) (2021-03-01) ### Bug Fixes * fixed code quality issues ([#398](https://github.com/googlemaps/google-maps-services-python/issues/398)) ([44cafa0](https://github.com/googlemaps/google-maps-services-python/commit/44cafa059f0d76599525001fc39f448fc0c722c8)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index b6f45f8c..f0568e3d 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.2" +__version__ = "4.4.3" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index b9b3aeea..11e0dc62 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.2", + version="4.4.3", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 6b29efd1ac377e7fe9d5d552bf3bc85894fa959a Mon Sep 17 00:00:00 2001 From: Jake <1993773+jengel3@users.noreply.github.com> Date: Fri, 12 Mar 2021 12:54:39 -0600 Subject: [PATCH 103/166] fix: make Places textsearch query parameter optional (#399) --- googlemaps/places.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index f84bb84d..e288dc91 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -168,7 +168,7 @@ def find_place( def places( client, - query, + query=None, location=None, radius=None, language=None, From dfd09e647f03edc107d16e3e051a22e62250dbfc Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 12 Mar 2021 19:00:18 +0000 Subject: [PATCH 104/166] chore(release): 4.4.4 [skip ci] ## [4.4.4](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.3...v4.4.4) (2021-03-12) ### Bug Fixes * make Places textsearch query parameter optional ([#399](https://github.com/googlemaps/google-maps-services-python/issues/399)) ([6b29efd](https://github.com/googlemaps/google-maps-services-python/commit/6b29efd1ac377e7fe9d5d552bf3bc85894fa959a)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index f0568e3d..2bf13650 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.3" +__version__ = "4.4.4" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 11e0dc62..f7089df2 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.3", + version="4.4.4", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 14a8f0b92b55405a3853682c155c1d9329a9823b Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 15 Mar 2021 11:49:10 -0600 Subject: [PATCH 105/166] chore: run publish in release exec (#401) --- .github/scripts/install.sh | 2 +- .github/workflows/publish.yml | 48 ----------------------------------- .github/workflows/release.yml | 10 ++++++++ .releaserc | 11 ++++++-- 4 files changed, 20 insertions(+), 51 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index 5cd76712..91235742 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -6,7 +6,7 @@ if ! python3 -m pip --version; then curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py sudo python3 get-pip.py sudo python3 -m pip install --upgrade setuptools - sudo python3 -m pip install nox + sudo python3 -m pip install nox twine else sudo python3 -m pip install --upgrade setuptools python3 -m pip install nox diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index a0ea83af..00000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2020 Google LLC -# -# 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. - -# A workflow that pushes artifacts to Sonatype -name: Publish - -on: - push: - tags: - - '*' - repository_dispatch: - types: [publish] - -jobs: - publish: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup Python - uses: "actions/setup-python@v1" - with: - python-version: "3.6" - - - name: Install dependencies - run: ./.github/scripts/install.sh - - - name: Run distribution - run: python3 -m nox -e distribution - - - name: Deploy to PyPi - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bebedf46..d27f41d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,10 +20,18 @@ jobs: release: runs-on: ubuntu-latest steps: + - name: Setup Python + uses: "actions/setup-python@v1" + with: + python-version: "3.6" - name: Checkout uses: actions/checkout@v2 with: token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} + - name: Install dependencies + run: ./.github/scripts/install.sh + - name: Run distribution + run: python3 -m nox -e distribution - name: Semantic Release uses: cycjimmy/semantic-release-action@v2 with: @@ -35,3 +43,5 @@ jobs: "@semantic-release/github env: GH_TOKEN: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/.releaserc b/.releaserc index 981e4031..7daf2b19 100644 --- a/.releaserc +++ b/.releaserc @@ -11,8 +11,15 @@ plugins: to: "__version__ = \"${nextRelease.version}\"" - files: - "./setup.py" - from: "version=\".*\"" - to: "version=\"${nextRelease.version}\"" + from: 'version=".*"' + to: 'version="${nextRelease.version}"' + - - "@semantic-release/exec" + - prepareCmd: + - rm -rf dist + - python3 setup.py sdist + - python3 -m twine check dist/* + - publishCmd: + - python3 -m twine upload dist/* - - "@semantic-release/git" - assets: - "./googlemaps/__init__.py" From bd0788f617679663e3dd7dbafe88ddb05421f9f7 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 15 Mar 2021 18:07:46 +0000 Subject: [PATCH 106/166] chore: include exec plugin --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d27f41d4..c4708c48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,6 +39,7 @@ jobs: "@semantic-release/commit-analyzer" "@semantic-release/release-notes-generator" "@google/semantic-release-replace-plugin" + "@semantic-release/exec" "@semantic-release/git "@semantic-release/github env: From fc0d02660f56a4a4a3d2edd7656e2a881701ea0c Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 15 Mar 2021 18:19:03 +0000 Subject: [PATCH 107/166] chore: fix exec command --- .releaserc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.releaserc b/.releaserc index 7daf2b19..6d2b9617 100644 --- a/.releaserc +++ b/.releaserc @@ -14,12 +14,8 @@ plugins: from: 'version=".*"' to: 'version="${nextRelease.version}"' - - "@semantic-release/exec" - - prepareCmd: - - rm -rf dist - - python3 setup.py sdist - - python3 -m twine check dist/* - - publishCmd: - - python3 -m twine upload dist/* + - prepareCmd: rm -rf dist && python3 setup.py sdist && python3 -m twine check dist/* + - publishCmd: python3 -m twine upload dist/* - - "@semantic-release/git" - assets: - "./googlemaps/__init__.py" From 5df4f403ed87fa963fb43b405744ca531a2f6881 Mon Sep 17 00:00:00 2001 From: Andrew Gomez Date: Mon, 15 Mar 2021 13:24:58 -0500 Subject: [PATCH 108/166] docs: specify support for place ID parameter in distance_matrix.py (#400) Updated param comments to specify support for place ID via 'place_id:' Per API documentation: "using place IDs is preferred over using addresses or latitude/longitude coordinates" Created test_place_id_param() to test case when origin/destination consist solely of place IDs. Updated test_mixed_params() to include a place ID in the origin. --- googlemaps/distance_matrix.py | 16 +++++++------- tests/test_distance_matrix.py | 39 +++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/googlemaps/distance_matrix.py b/googlemaps/distance_matrix.py index 1d848253..a30cbe09 100755 --- a/googlemaps/distance_matrix.py +++ b/googlemaps/distance_matrix.py @@ -26,17 +26,19 @@ def distance_matrix(client, origins, destinations, transit_routing_preference=None, traffic_model=None, region=None): """ Gets travel distance and time for a matrix of origins and destinations. - :param origins: One or more locations and/or latitude/longitude values, - from which to calculate distance and time. If you pass an address as - a string, the service will geocode the string and convert it to a + :param origins: One or more addresses, Place IDs, and/or latitude/longitude + values, from which to calculate distance and time. Each Place ID string + must be prepended with 'place_id:'. If you pass an address as a string, + the service will geocode the string and convert it to a latitude/longitude coordinate to calculate directions. :type origins: a single location, or a list of locations, where a location is a string, dict, list, or tuple - :param destinations: One or more addresses and/or lat/lng values, to - which to calculate distance and time. If you pass an address as a - string, the service will geocode the string and convert it to a - latitude/longitude coordinate to calculate directions. + :param destinations: One or more addresses, Place IDs, and/or lat/lng values + , to which to calculate distance and time. Each Place ID string must be + prepended with 'place_id:'. If you pass an address as a string, the + service will geocode the string and convert it to a latitude/longitude + coordinate to calculate directions. :type destinations: a single location, or a list of locations, where a location is a string, dict, list, or tuple diff --git a/tests/test_distance_matrix.py b/tests/test_distance_matrix.py index a906f8ce..6946782e 100644 --- a/tests/test_distance_matrix.py +++ b/tests/test_distance_matrix.py @@ -84,7 +84,10 @@ def test_mixed_params(self): content_type="application/json", ) - origins = ["Bobcaygeon ON", [41.43206, -81.38992]] + origins = [ + "Bobcaygeon ON", [41.43206, -81.38992], + "place_id:ChIJ7cv00DwsDogRAMDACa2m4K8" + ] destinations = [ (43.012486, -83.6964149), {"lat": 42.8863855, "lng": -78.8781627}, @@ -95,7 +98,8 @@ def test_mixed_params(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual( "https://maps.googleapis.com/maps/api/distancematrix/json?" - "key=%s&origins=Bobcaygeon+ON%%7C41.43206%%2C-81.38992&" + "key=%s&origins=Bobcaygeon+ON%%7C41.43206%%2C-81.38992%%7C" + "place_id%%3AChIJ7cv00DwsDogRAMDACa2m4K8&" "destinations=43.012486%%2C-83.6964149%%7C42.8863855%%2C" "-78.8781627" % self.key, responses.calls[0].request.url, @@ -181,3 +185,34 @@ def test_lang_param(self): "destinations=San+Francisco%%7CVictoria+BC" % self.key, responses.calls[0].request.url, ) + @responses.activate + def test_place_id_param(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/distancematrix/json", + body='{"status":"OK","rows":[]}', + status=200, + content_type="application/json", + ) + + origins = [ + 'place_id:ChIJ7cv00DwsDogRAMDACa2m4K8', + 'place_id:ChIJzxcfI6qAa4cR1jaKJ_j0jhE', + ] + destinations = [ + 'place_id:ChIJPZDrEzLsZIgRoNrpodC5P30', + 'place_id:ChIJjQmTaV0E9YgRC2MLmS_e_mY', + ] + + matrix = self.client.distance_matrix(origins, destinations) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/distancematrix/json?" + "key=%s&" + "origins=place_id%%3AChIJ7cv00DwsDogRAMDACa2m4K8%%7C" + "place_id%%3AChIJzxcfI6qAa4cR1jaKJ_j0jhE&" + "destinations=place_id%%3AChIJPZDrEzLsZIgRoNrpodC5P30%%7C" + "place_id%%3AChIJjQmTaV0E9YgRC2MLmS_e_mY" % self.key, + responses.calls[0].request.url, + ) From 4ad0c4905972e35d58dce6a9091f84db4640bd3f Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 15 Mar 2021 18:31:10 +0000 Subject: [PATCH 109/166] build: fix escape --- .github/workflows/release.yml | 4 ++-- .releaserc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4708c48..8e149580 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,8 +40,8 @@ jobs: "@semantic-release/release-notes-generator" "@google/semantic-release-replace-plugin" "@semantic-release/exec" - "@semantic-release/git - "@semantic-release/github + "@semantic-release/git" + "@semantic-release/github" env: GH_TOKEN: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} TWINE_USERNAME: __token__ diff --git a/.releaserc b/.releaserc index 6d2b9617..d34a775c 100644 --- a/.releaserc +++ b/.releaserc @@ -14,8 +14,8 @@ plugins: from: 'version=".*"' to: 'version="${nextRelease.version}"' - - "@semantic-release/exec" - - prepareCmd: rm -rf dist && python3 setup.py sdist && python3 -m twine check dist/* - - publishCmd: python3 -m twine upload dist/* + - prepareCmd: "rm -rf dist && python3 setup.py sdist && python3 -m twine check dist/*" + - publishCmd: "python3 -m twine upload dist/*" - - "@semantic-release/git" - assets: - "./googlemaps/__init__.py" From 6d2ee9642a1849ed64cdce76692f68a753fc286a Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 15 Mar 2021 19:04:38 +0000 Subject: [PATCH 110/166] chore: options is not an array --- .releaserc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.releaserc b/.releaserc index d34a775c..db70f6e2 100644 --- a/.releaserc +++ b/.releaserc @@ -14,8 +14,8 @@ plugins: from: 'version=".*"' to: 'version="${nextRelease.version}"' - - "@semantic-release/exec" - - prepareCmd: "rm -rf dist && python3 setup.py sdist && python3 -m twine check dist/*" - - publishCmd: "python3 -m twine upload dist/*" + - prepareCmd: "rm -rf dist && python3 setup.py sdist && python3 -m twine check dist/*" + publishCmd: "python3 -m twine upload dist/*" - - "@semantic-release/git" - assets: - "./googlemaps/__init__.py" From b963ff945c2343d34fa9d3b3e6acec02f987ca95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Vasi=C4=87?= Date: Mon, 15 Mar 2021 20:14:29 +0100 Subject: [PATCH 111/166] fix: Add parameter 'origin' to places autocomplete (#392) --- googlemaps/places.py | 11 +++++++++++ tests/test_places.py | 2 ++ 2 files changed, 13 insertions(+) diff --git a/googlemaps/places.py b/googlemaps/places.py index e288dc91..91c14dfe 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -501,6 +501,7 @@ def places_autocomplete( input_text, session_token=None, offset=None, + origin=None, location=None, radius=None, language=None, @@ -525,6 +526,12 @@ def places_autocomplete( service will match on 'Goo'. :type offset: int + :param origin: The origin point from which to calculate straight-line distance + to the destination (returned as distance_meters). + If this value is omitted, straight-line distance will + not be returned. + :type origin: string, dict, list, or tuple + :param location: The latitude/longitude value for which you wish to obtain the closest, human-readable address. :type location: string, dict, list, or tuple @@ -558,6 +565,7 @@ def places_autocomplete( input_text, session_token=session_token, offset=offset, + origin=origin, location=location, radius=radius, language=language, @@ -611,6 +619,7 @@ def _autocomplete( input_text, session_token=None, offset=None, + origin=None, location=None, radius=None, language=None, @@ -629,6 +638,8 @@ def _autocomplete( params["sessiontoken"] = session_token if offset: params["offset"] = offset + if origin: + params["origin"] = convert.latlng(origin) if location: params["location"] = convert.latlng(location) if radius: diff --git a/tests/test_places.py b/tests/test_places.py index 5316fe8e..50781773 100644 --- a/tests/test_places.py +++ b/tests/test_places.py @@ -212,6 +212,7 @@ def test_autocomplete(self): "Google", session_token=session_token, offset=3, + origin=self.location, location=self.location, radius=self.radius, language=self.language, @@ -223,6 +224,7 @@ def test_autocomplete(self): self.assertEqual(1, len(responses.calls)) self.assertURLEqual( "%s?components=country%%3Aau&input=Google&language=en-AU&" + "origin=-33.86746%%2C151.20709&" "location=-33.86746%%2C151.20709&offset=3&radius=100&" "strictbounds=true&types=geocode&key=%s&sessiontoken=%s" % (url, self.key, session_token), From 975071f2d68e4847feae78b9dfe07e9895f2eaef Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Tue, 16 Mar 2021 06:15:19 +1100 Subject: [PATCH 112/166] docs: fix simple typo, preffix -> prefix (#389) There is a small typo in googlemaps/directions.py. Should read `prefix` rather than `preffix`. --- googlemaps/directions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/directions.py b/googlemaps/directions.py index b38a4cdf..353145cc 100644 --- a/googlemaps/directions.py +++ b/googlemaps/directions.py @@ -33,7 +33,7 @@ def directions(client, origin, destination, :param destination: The address or latitude/longitude value from which you wish to calculate directions. You can use a place_id as destination - by putting 'place_id:' as a preffix in the passing parameter. + by putting 'place_id:' as a prefix in the passing parameter. :type destination: string, dict, list, or tuple :param mode: Specifies the mode of transport to use when calculating From 923f8f3149d673c163a0b3a65b08b30ef7c9c827 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Mon, 15 Mar 2021 19:39:01 +0000 Subject: [PATCH 113/166] chore: update release to match pypa/gh-action-pypi-publish --- .github/scripts/install.sh | 1 + .github/workflows/release.yml | 4 +++- .releaserc | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index 91235742..a585594c 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -10,4 +10,5 @@ if ! python3 -m pip --version; then else sudo python3 -m pip install --upgrade setuptools python3 -m pip install nox + python3 -m pip install --prefer-binary twine fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e149580..f5704229 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,11 +19,13 @@ on: jobs: release: runs-on: ubuntu-latest + env: + PYTHONDONTWRITEBYTECODE: 1 steps: - name: Setup Python uses: "actions/setup-python@v1" with: - python-version: "3.6" + python-version: "3.9" - name: Checkout uses: actions/checkout@v2 with: diff --git a/.releaserc b/.releaserc index db70f6e2..15fa5beb 100644 --- a/.releaserc +++ b/.releaserc @@ -14,7 +14,7 @@ plugins: from: 'version=".*"' to: 'version="${nextRelease.version}"' - - "@semantic-release/exec" - - prepareCmd: "rm -rf dist && python3 setup.py sdist && python3 -m twine check dist/*" + - prepareCmd: "python3 setup.py sdist && python3 -m twine check dist/*" publishCmd: "python3 -m twine upload dist/*" - - "@semantic-release/git" - assets: From 760bbce63e4ea4ad0c300020bad3f53a4159f028 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 15 Mar 2021 19:47:20 +0000 Subject: [PATCH 114/166] chore(release): 4.4.5 [skip ci] ## [4.4.5](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.4...v4.4.5) (2021-03-15) ### Bug Fixes * Add parameter 'origin' to places autocomplete ([#392](https://github.com/googlemaps/google-maps-services-python/issues/392)) ([b963ff9](https://github.com/googlemaps/google-maps-services-python/commit/b963ff945c2343d34fa9d3b3e6acec02f987ca95)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 2bf13650..937db7ee 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.4" +__version__ = "4.4.5" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index f7089df2..bda02c1f 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.4", + version="4.4.5", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From a9460c0138ea95d4eacfe4ed2dcd1528ad7c4b54 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 15:29:09 -0600 Subject: [PATCH 115/166] chore: Upgrade to GitHub-native Dependabot (#405) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..491deae0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: pip + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From 68276459f1d9cb3317c0c6c81b5ef9d6f45cd6b8 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 14 Jul 2021 09:40:33 -0600 Subject: [PATCH 116/166] fix: allow channel without client_id (#411) --- googlemaps/client.py | 6 ++---- tests/test_client.py | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 980509e2..7b84b7b4 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -130,13 +130,11 @@ def __init__(self, key=None, client_id=None, client_secret=None, raise ValueError("Invalid API key provided.") if channel: - if not client_id: - raise ValueError("The channel argument must be used with a " - "client ID") if not re.match("^[a-zA-Z0-9._-]*$", channel): raise ValueError("The channel argument must be an ASCII " "alphanumeric string. The period (.), underscore (_)" - "and hyphen (-) characters are allowed.") + "and hyphen (-) characters are allowed. If used without " + "client_id, it must be 0-999.") self.session = requests.Session() self.key = key diff --git a/tests/test_client.py b/tests/test_client.py index a18221fa..4f01e397 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -280,10 +280,6 @@ def __call__(self, req): self.assertEqual(2, len(responses.calls)) - def test_channel_without_client_id(self): - with self.assertRaises(ValueError): - client = googlemaps.Client(key="AIzaasdf", channel="mychannel") - def test_invalid_channel(self): # Cf. limitations here: # https://developers.google.com/maps/premium/reports From cca7a307774c35cfae24d0311e8211d3220f3fb4 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 14 Jul 2021 15:41:47 +0000 Subject: [PATCH 117/166] chore(release): 4.4.6 [skip ci] ## [4.4.6](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.5...v4.4.6) (2021-07-14) ### Bug Fixes * allow channel without client_id ([#411](https://github.com/googlemaps/google-maps-services-python/issues/411)) ([6827645](https://github.com/googlemaps/google-maps-services-python/commit/68276459f1d9cb3317c0c6c81b5ef9d6f45cd6b8)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 937db7ee..a0b81a69 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.5" +__version__ = "4.4.6" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index bda02c1f..4bdae609 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.5", + version="4.4.6", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From a8afb86892ed06857099aecf10ca63c2271ce569 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 14 Jul 2021 09:50:58 -0600 Subject: [PATCH 118/166] fix: run sdist after tag created --- .releaserc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.releaserc b/.releaserc index 15fa5beb..78e982ac 100644 --- a/.releaserc +++ b/.releaserc @@ -15,7 +15,7 @@ plugins: to: 'version="${nextRelease.version}"' - - "@semantic-release/exec" - prepareCmd: "python3 setup.py sdist && python3 -m twine check dist/*" - publishCmd: "python3 -m twine upload dist/*" + publishCmd: "python3 setup.py sdist && python3 -m twine upload dist/*" - - "@semantic-release/git" - assets: - "./googlemaps/__init__.py" From 8ad82645782a3d7417ca82b1a0cb31ab7930238b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 14 Jul 2021 15:52:17 +0000 Subject: [PATCH 119/166] chore(release): 4.4.7 [skip ci] ## [4.4.7](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.6...v4.4.7) (2021-07-14) ### Bug Fixes * run sdist after tag created ([a8afb86](https://github.com/googlemaps/google-maps-services-python/commit/a8afb86892ed06857099aecf10ca63c2271ce569)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index a0b81a69..6649d28a 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.6" +__version__ = "4.4.7" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 4bdae609..99273db0 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.6", + version="4.4.7", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 891321c3209e13130b760661d23b2e5cd741e39f Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 14 Jul 2021 10:55:13 -0600 Subject: [PATCH 120/166] chore: delete unnecessary file --- .deepsource.toml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml deleted file mode 100644 index f772ce8c..00000000 --- a/.deepsource.toml +++ /dev/null @@ -1,10 +0,0 @@ -version = 1 - -test_patterns = ["tests/**"] - -[[analyzers]] -name = "python" -enabled = true - - [analyzers.meta] - runtime_version = "3.x.x" From a93b3c0fe1ec4680fbb34fc29ac8714b9a8c7da4 Mon Sep 17 00:00:00 2001 From: northtree Date: Thu, 29 Jul 2021 23:42:53 +1000 Subject: [PATCH 121/166] feat: allow passings requests session (#414) --- googlemaps/client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 7b84b7b4..1334571d 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -54,6 +54,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, retry_timeout=60, requests_kwargs=None, queries_per_second=50, channel=None, retry_over_query_limit=True, experience_id=None, + requests_session=None, base_url=_DEFAULT_BASE_URL): """ :param key: Maps API key. Required, unless "client_id" and @@ -116,6 +117,9 @@ def __init__(self, key=None, client_id=None, client_secret=None, implemented. See the official requests docs for more info: http://docs.python-requests.org/en/latest/api/#main-interface :type requests_kwargs: dict + + :param requests_session: Reused persistent session for flexibility. + :type requests_session: requests.Session :param base_url: The base URL for all requests. Defaults to the Maps API server. Should not have a trailing slash. @@ -136,7 +140,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, "and hyphen (-) characters are allowed. If used without " "client_id, it must be 0-999.") - self.session = requests.Session() + self.session = requests_session or requests.Session() self.key = key if timeout and (connect_timeout or read_timeout): From 0717028106b0b9ba7f2fc607f7b2cbabdf25dcd6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 29 Jul 2021 13:44:22 +0000 Subject: [PATCH 122/166] chore(release): 4.5.0 [skip ci] # [4.5.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.4.7...v4.5.0) (2021-07-29) ### Features * allow passings requests session ([#414](https://github.com/googlemaps/google-maps-services-python/issues/414)) ([a93b3c0](https://github.com/googlemaps/google-maps-services-python/commit/a93b3c0fe1ec4680fbb34fc29ac8714b9a8c7da4)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 6649d28a..803bf5a9 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.4.7" +__version__ = "4.5.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 99273db0..c2a605ba 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.4.7", + version="4.5.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From c7c32368811ada02a96b05480cbdb595639c44fa Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 29 Jul 2021 08:16:33 -0600 Subject: [PATCH 123/166] fix: only run publishCmd --- .releaserc | 1 - 1 file changed, 1 deletion(-) diff --git a/.releaserc b/.releaserc index 78e982ac..469c01f0 100644 --- a/.releaserc +++ b/.releaserc @@ -14,7 +14,6 @@ plugins: from: 'version=".*"' to: 'version="${nextRelease.version}"' - - "@semantic-release/exec" - - prepareCmd: "python3 setup.py sdist && python3 -m twine check dist/*" publishCmd: "python3 setup.py sdist && python3 -m twine upload dist/*" - - "@semantic-release/git" - assets: From 3502f8afdee1ba14b9acba7d8693225ca05bb80f Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 29 Jul 2021 08:22:46 -0600 Subject: [PATCH 124/166] chore: fix formatting --- .releaserc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.releaserc b/.releaserc index 469c01f0..5f53e6f5 100644 --- a/.releaserc +++ b/.releaserc @@ -13,8 +13,7 @@ plugins: - "./setup.py" from: 'version=".*"' to: 'version="${nextRelease.version}"' - - - "@semantic-release/exec" - publishCmd: "python3 setup.py sdist && python3 -m twine upload dist/*" + - [ "@semantic-release/exec", { publishCmd: "python3 setup.py sdist && python3 -m twine upload dist/*" }] - - "@semantic-release/git" - assets: - "./googlemaps/__init__.py" From 5045dce3eaf793ff20ab5b33c78402b1c501f329 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 29 Jul 2021 14:24:08 +0000 Subject: [PATCH 125/166] chore(release): 4.5.1 [skip ci] ## [4.5.1](https://github.com/googlemaps/google-maps-services-python/compare/v4.5.0...v4.5.1) (2021-07-29) ### Bug Fixes * only run publishCmd ([c7c3236](https://github.com/googlemaps/google-maps-services-python/commit/c7c32368811ada02a96b05480cbdb595639c44fa)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 803bf5a9..593f4ba8 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.5.0" +__version__ = "4.5.1" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index c2a605ba..369cb0b5 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.5.0", + version="4.5.1", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From dc8e116992666cac83bea7d41aac864eeea1d882 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 29 Jul 2021 08:34:30 -0600 Subject: [PATCH 126/166] fix: cleanup old dist --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5704229..869f8d65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,8 @@ jobs: run: ./.github/scripts/install.sh - name: Run distribution run: python3 -m nox -e distribution + - name: Cleanup old dist + run: rm -rf googlemaps-* - name: Semantic Release uses: cycjimmy/semantic-release-action@v2 with: From 5420d7d1f7bc1945e50e62d6c8a910657249a34d Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 29 Jul 2021 14:35:53 +0000 Subject: [PATCH 127/166] chore(release): 4.5.2 [skip ci] ## [4.5.2](https://github.com/googlemaps/google-maps-services-python/compare/v4.5.1...v4.5.2) (2021-07-29) ### Bug Fixes * cleanup old dist ([dc8e116](https://github.com/googlemaps/google-maps-services-python/commit/dc8e116992666cac83bea7d41aac864eeea1d882)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 593f4ba8..b272c645 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.5.1" +__version__ = "4.5.2" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 369cb0b5..bff54133 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.5.1", + version="4.5.2", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 964cecd5ce2df29ac5cd91e62560cd0a69c7125d Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 29 Jul 2021 08:38:49 -0600 Subject: [PATCH 128/166] fix: cleanup dist --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 869f8d65..ce78e642 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - name: Run distribution run: python3 -m nox -e distribution - name: Cleanup old dist - run: rm -rf googlemaps-* + run: rm -rf googlemaps-* dist/ - name: Semantic Release uses: cycjimmy/semantic-release-action@v2 with: From 00a87a0f623c00286f20e3f4c576a62a73284e5c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 29 Jul 2021 14:39:57 +0000 Subject: [PATCH 129/166] chore(release): 4.5.3 [skip ci] ## [4.5.3](https://github.com/googlemaps/google-maps-services-python/compare/v4.5.2...v4.5.3) (2021-07-29) ### Bug Fixes * cleanup dist ([964cecd](https://github.com/googlemaps/google-maps-services-python/commit/964cecd5ce2df29ac5cd91e62560cd0a69c7125d)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index b272c645..63665644 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.5.2" +__version__ = "4.5.3" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index bff54133..be29f9b1 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.5.2", + version="4.5.3", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 25e26092cb19e998764cae37474dd4c35ff42473 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Fri, 1 Oct 2021 11:15:52 -0600 Subject: [PATCH 130/166] chore: Created local 'SECURITY.md' from remote 'SECURITY.md' (#418) --- SECURITY.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..6d19135d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,10 @@ +# Report a security issue + +To report a security issue, please use https://g.co/vulnz. We use +https://g.co/vulnz for our intake, and do coordination and disclosure here on +GitHub (including using GitHub Security Advisory). The Google Security Team will +respond within 5 working days of your report on g.co/vulnz. + +To contact us about other bugs, please open an issue on GitHub. + +> **Note**: This file is synchronized from the https://github.com/googlemaps/.github repository. From db1292f267897d1bd5db34655f1745e61d1a96c6 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Wed, 2 Feb 2022 13:52:31 -0700 Subject: [PATCH 131/166] chore: update test matrix python versions (#428) --- .github/workflows/test.yml | 2 +- noxfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58839686..f8123e23 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.5", "3.6", "3.7", "3.8"] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - name: Checkout repository uses: actions/checkout@v2 diff --git a/noxfile.py b/noxfile.py index 06eefe58..d3357fe4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,6 +1,6 @@ import nox -SUPPORTED_PY_VERSIONS = ["3.5", "3.6", "3.7", "3.8"] +SUPPORTED_PY_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] def _install_dev_packages(session): From 1364711c1557b44e72d1ea60d53a7f134f892de6 Mon Sep 17 00:00:00 2001 From: Andy Klimczak Date: Wed, 2 Feb 2022 16:15:53 -0500 Subject: [PATCH 132/166] feat: Geocode by place id (#427) Geocode endpoint accepts a `place_id` param as an alternative to geocode Google docs: https://developers.google.com/maps/documentation/geocoding/requests-places-geocoding --- googlemaps/geocoding.py | 9 ++++++++- tests/test_geocoding.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/googlemaps/geocoding.py b/googlemaps/geocoding.py index b665d776..e409a49e 100644 --- a/googlemaps/geocoding.py +++ b/googlemaps/geocoding.py @@ -19,7 +19,7 @@ from googlemaps import convert -def geocode(client, address=None, components=None, bounds=None, region=None, +def geocode(client, address=None, place_id=None, components=None, bounds=None, region=None, language=None): """ Geocoding is the process of converting addresses @@ -30,6 +30,10 @@ def geocode(client, address=None, components=None, bounds=None, region=None, :param address: The address to geocode. :type address: string + :param place_id: A textual identifier that uniquely identifies a place, + returned from a Places search. + :type place_id: string + :param components: A component filter for which you wish to obtain a geocode, for example: ``{'administrative_area': 'TX','country': 'US'}`` :type components: dict @@ -53,6 +57,9 @@ def geocode(client, address=None, components=None, bounds=None, region=None, if address: params["address"] = address + if place_id: + params["place_id"] = place_id + if components: params["components"] = convert.components(components) diff --git a/tests/test_geocoding.py b/tests/test_geocoding.py index dfd9376d..813241f6 100644 --- a/tests/test_geocoding.py +++ b/tests/test_geocoding.py @@ -201,6 +201,25 @@ def test_geocode_with_just_components(self): responses.calls[0].request.url, ) + @responses.activate + def test_geocode_place_id(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[]}', + status=200, + content_type="application/json", + ) + + results = self.client.geocode(place_id="ChIJeRpOeF67j4AR9ydy_PIzPuM") + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "key=%s&place_id=ChIJeRpOeF67j4AR9ydy_PIzPuM" % self.key, + responses.calls[0].request.url, + ) + @responses.activate def test_simple_reverse_geocode(self): responses.add( From 4dd8db6b53049869cf98f2fed3ba8e56676d1709 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 2 Feb 2022 21:17:04 +0000 Subject: [PATCH 133/166] chore(release): 4.6.0 [skip ci] # [4.6.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.5.3...v4.6.0) (2022-02-02) ### Features * Geocode by place id ([#427](https://github.com/googlemaps/google-maps-services-python/issues/427)) ([1364711](https://github.com/googlemaps/google-maps-services-python/commit/1364711c1557b44e72d1ea60d53a7f134f892de6)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 63665644..ffde203b 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.5.3" +__version__ = "4.6.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index be29f9b1..8826822d 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="googlemaps", - version="4.5.3", + version="4.6.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 2723079fce5a677ae068568018900c391b1a1f69 Mon Sep 17 00:00:00 2001 From: googlemaps-bot Date: Mon, 9 May 2022 14:54:46 -0600 Subject: [PATCH 134/166] chore: Synced file(s) with googlemaps/.github (#432) * chore: Created local '.github/CODEOWNERS' from remote '.github/CODEOWNERS' * chore: Created local '.github/sync-repo-settings.yaml' from remote '.github/sync-repo-settings.yaml' --- .github/CODEOWNERS | 17 +++++++++++++++ .github/sync-repo-settings.yaml | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/sync-repo-settings.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..e95e611e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,17 @@ +# Copyright 2022 Google LLC +# +# 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. + +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners + +.github/ @googlemaps/admin diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml new file mode 100644 index 00000000..84693ce8 --- /dev/null +++ b/.github/sync-repo-settings.yaml @@ -0,0 +1,38 @@ +# Copyright 2022 Google LLC +# +# 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. + +# https://github.com/googleapis/repo-automation-bots/tree/main/packages/sync-repo-settings + +rebaseMergeAllowed: true +squashMergeAllowed: true +mergeCommitAllowed: false +deleteBranchOnMerge: true +branchProtectionRules: +- pattern: main + isAdminEnforced: false + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - 'cla/google' + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true +- pattern: master + isAdminEnforced: false + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + - 'cla/google' + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true +permissionRules: + - team: admin + permission: admin From 628ab311f8416a2b49a4acbccb8d27747bf0a4a5 Mon Sep 17 00:00:00 2001 From: googlemaps-bot Date: Mon, 9 May 2022 15:36:13 -0600 Subject: [PATCH 135/166] chore: Synced local '.github/sync-repo-settings.yaml' with remote '.github/sync-repo-settings.yaml' (#433) --- .github/sync-repo-settings.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 84693ce8..98d0b463 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -24,6 +24,7 @@ branchProtectionRules: requiresStrictStatusChecks: false requiredStatusCheckContexts: - 'cla/google' + - 'test' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true - pattern: master @@ -31,6 +32,7 @@ branchProtectionRules: requiresStrictStatusChecks: false requiredStatusCheckContexts: - 'cla/google' + - 'test' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true permissionRules: From e710ba7b5aeb9156b5112dbd55bd71903128f8c4 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 10 May 2022 12:15:23 -0600 Subject: [PATCH 136/166] build: update required checks (#435) --- .github/sync-repo-settings.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 98d0b463..746f8e52 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -25,6 +25,8 @@ branchProtectionRules: requiredStatusCheckContexts: - 'cla/google' - 'test' + - 'snippet-bot-check' + - 'header-check' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true - pattern: master @@ -33,6 +35,8 @@ branchProtectionRules: requiredStatusCheckContexts: - 'cla/google' - 'test' + - 'snippet-bot-check' + - 'header-check' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true permissionRules: From c67d453305fb103aa6ccc3639cf1733854ea480b Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 10 May 2022 12:47:51 -0600 Subject: [PATCH 137/166] chore: fix typo in check name (#436) --- .github/sync-repo-settings.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 746f8e52..a7b2d39c 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -25,7 +25,7 @@ branchProtectionRules: requiredStatusCheckContexts: - 'cla/google' - 'test' - - 'snippet-bot-check' + - 'snippet-bot check' - 'header-check' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true @@ -35,7 +35,7 @@ branchProtectionRules: requiredStatusCheckContexts: - 'cla/google' - 'test' - - 'snippet-bot-check' + - 'snippet-bot check' - 'header-check' requiredApprovingReviewCount: 1 requiresCodeOwnerReviews: true From 1d995b4420ab369d8c23fcdef8b55e284f80dde0 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 10 May 2022 14:06:01 -0600 Subject: [PATCH 138/166] build: update workflow and standardize check name --- .github/workflows/test.yml | 8 +++++++- README.md | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8123e23..ddcc2e6c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ on: branches: ['*'] jobs: - test: + matrix: name: "Run tests on Python ${{ matrix.python-version }}" runs-on: ubuntu-latest strategy: @@ -46,3 +46,9 @@ jobs: run: | python3 -m nox --session "tests-${{ matrix.python-version }}" python3 -m nox -e distribution + test: + name: Wait for matrix to finish + needs: [matrix] + runs-on: ubuntu-latest + steps: + - run: echo "Test matrix finished" diff --git a/README.md b/README.md index 31840082..21564486 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ Python Client for Google Maps Services ==================================== -[![Build Status](https://travis-ci.org/googlemaps/google-maps-services-python.svg?branch=master)](https://travis-ci.org/googlemaps/google-maps-services-python) +![Test](https://github.com/googlemaps/google-maps-services-js/workflows/test/badge.svg) +![Release](https://github.com/googlemaps/google-maps-services-js/workflows/release/badge.svg) [![codecov](https://codecov.io/gh/googlemaps/google-maps-services-python/branch/master/graph/badge.svg)](https://codecov.io/gh/googlemaps/google-maps-services-python) [![PyPI version](https://badge.fury.io/py/googlemaps.svg)](https://badge.fury.io/py/googlemaps) ![PyPI - Downloads](https://img.shields.io/pypi/dd/googlemaps) From f59f66b81326f8f2b9aeadd41df8bba4ac58ff9b Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Tue, 10 May 2022 14:34:42 -0600 Subject: [PATCH 139/166] docs: fix badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21564486..54c024b3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ Python Client for Google Maps Services ==================================== -![Test](https://github.com/googlemaps/google-maps-services-js/workflows/test/badge.svg) -![Release](https://github.com/googlemaps/google-maps-services-js/workflows/release/badge.svg) +![Test](https://github.com/googlemaps/google-maps-services-js/workflows/Test/badge.svg) +![Release](https://github.com/googlemaps/google-maps-services-js/workflows/Release/badge.svg) [![codecov](https://codecov.io/gh/googlemaps/google-maps-services-python/branch/master/graph/badge.svg)](https://codecov.io/gh/googlemaps/google-maps-services-python) [![PyPI version](https://badge.fury.io/py/googlemaps.svg)](https://badge.fury.io/py/googlemaps) ![PyPI - Downloads](https://img.shields.io/pypi/dd/googlemaps) From dfa5e7f2e5f5a0371f607b07538b6c084d8c5b42 Mon Sep 17 00:00:00 2001 From: googlemaps-bot Date: Tue, 10 May 2022 15:31:35 -0600 Subject: [PATCH 140/166] chore: Created local '.github/workflows/dependabot.yml' from remote '.github/workflows/dependabot.yml' (#434) --- .github/workflows/dependabot.yml | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/dependabot.yml diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 00000000..597e7636 --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,36 @@ +# Copyright 2022 Google LLC +# +# 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. + +name: Dependabot +on: pull_request + +permissions: + contents: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.SYNCED_GITHUB_TOKEN_REPO}} + steps: + - name: approve + run: gh pr review --comment -b "Automatically approved since dependabot is [configured](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels) with label `automatic`." + if: ${{ github.event.label.name == 'automatic' }} + - name: approve-instructions + run: echo "configure dependabot with label 'automatic' to have it automatically approved and merged. https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels" + if: ${{ github.event.label.name != 'automatic' }} + - name: merge + run: gh pr merge --auto --squash --delete-branch "$PR_URL" From 381563b0ea97d28beb1a0a54299549536fe6ce9c Mon Sep 17 00:00:00 2001 From: googlemaps-bot Date: Wed, 11 May 2022 15:35:06 -0600 Subject: [PATCH 141/166] chore: Synced local '.github/workflows/dependabot.yml' with remote '.github/workflows/dependabot.yml' (#437) --- .github/workflows/dependabot.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 597e7636..12013710 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -27,10 +27,10 @@ jobs: GITHUB_TOKEN: ${{secrets.SYNCED_GITHUB_TOKEN_REPO}} steps: - name: approve - run: gh pr review --comment -b "Automatically approved since dependabot is [configured](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels) with label `automatic`." - if: ${{ github.event.label.name == 'automatic' }} + run: gh pr review --comment -b "Automatically approved since dependabot is not [configured](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels) with label `do not merge`." + if: ${{ github.event.label.name != 'do not merge' }} - name: approve-instructions - run: echo "configure dependabot with label 'automatic' to have it automatically approved and merged. https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels" - if: ${{ github.event.label.name != 'automatic' }} + run: echo "Configure dependabot with label 'do not merge' to prevent it from being automatically approved and merged. https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels" + if: ${{ github.event.label.name == 'do not merge' }} - name: merge run: gh pr merge --auto --squash --delete-branch "$PR_URL" From 8d334a87946442e6d786f9eb0a80a5840eb3cd72 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 12 May 2022 09:49:55 -0600 Subject: [PATCH 142/166] chore: simplify dependabot workflow --- .github/workflows/dependabot.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 12013710..c4c66544 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -28,9 +28,5 @@ jobs: steps: - name: approve run: gh pr review --comment -b "Automatically approved since dependabot is not [configured](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels) with label `do not merge`." - if: ${{ github.event.label.name != 'do not merge' }} - - name: approve-instructions - run: echo "Configure dependabot with label 'do not merge' to prevent it from being automatically approved and merged. https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels" - if: ${{ github.event.label.name == 'do not merge' }} - name: merge run: gh pr merge --auto --squash --delete-branch "$PR_URL" From fd2933fc8c23e9caa3bb4a5bc74ae0692babde05 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 12 May 2022 09:52:52 -0600 Subject: [PATCH 143/166] chore: update pull request approval comment --- .github/workflows/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index c4c66544..b9853abf 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -27,6 +27,6 @@ jobs: GITHUB_TOKEN: ${{secrets.SYNCED_GITHUB_TOKEN_REPO}} steps: - name: approve - run: gh pr review --comment -b "Automatically approved since dependabot is not [configured](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#labels) with label `do not merge`." + run: gh pr review --comment -b "Automatically approved dependabot pull request." - name: merge run: gh pr merge --auto --squash --delete-branch "$PR_URL" From 407e81be1ded5026752c1412f0c8330ca0caa6bd Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 12 May 2022 09:59:57 -0600 Subject: [PATCH 144/166] chore: fix approval by providing url to pr --- .github/workflows/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index b9853abf..aacd8097 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -27,6 +27,6 @@ jobs: GITHUB_TOKEN: ${{secrets.SYNCED_GITHUB_TOKEN_REPO}} steps: - name: approve - run: gh pr review --comment -b "Automatically approved dependabot pull request." + run: gh pr review --comment -b "Automatically approved dependabot pull request." "$PR_URL" - name: merge run: gh pr merge --auto --squash --delete-branch "$PR_URL" From 7548bb2487d17e93149c40015c9e8b76576180e3 Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 12 May 2022 10:09:50 -0600 Subject: [PATCH 145/166] chore: add --approve --- .github/workflows/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index aacd8097..904a1990 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -27,6 +27,6 @@ jobs: GITHUB_TOKEN: ${{secrets.SYNCED_GITHUB_TOKEN_REPO}} steps: - name: approve - run: gh pr review --comment -b "Automatically approved dependabot pull request." "$PR_URL" + run: gh pr review --approve --comment -b "Automatically approved dependabot pull request." "$PR_URL" - name: merge run: gh pr merge --auto --squash --delete-branch "$PR_URL" From 62e1a306059296b7fb9034b709253e2ed6c6ca9c Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 12 May 2022 10:21:04 -0600 Subject: [PATCH 146/166] chore: only approve --- .github/workflows/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 904a1990..6b46ebbb 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -27,6 +27,6 @@ jobs: GITHUB_TOKEN: ${{secrets.SYNCED_GITHUB_TOKEN_REPO}} steps: - name: approve - run: gh pr review --approve --comment -b "Automatically approved dependabot pull request." "$PR_URL" + run: gh pr review --approve "$PR_URL" - name: merge run: gh pr merge --auto --squash --delete-branch "$PR_URL" From 8f7209439db457c38da2ea1b6e9115cb727ee39d Mon Sep 17 00:00:00 2001 From: Justin Poehnelt Date: Thu, 19 May 2022 13:24:17 -0600 Subject: [PATCH 147/166] build: change dependabot interval to weekly (#439) --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 491deae0..55a9ccdd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,5 +3,5 @@ updates: - package-ecosystem: pip directory: "/" schedule: - interval: daily + interval: "weekly" open-pull-requests-limit: 10 From 3bcb05f56ec0745d9ecaac677c31963cac8ece18 Mon Sep 17 00:00:00 2001 From: anglarett Date: Wed, 9 Nov 2022 17:19:51 +0100 Subject: [PATCH 148/166] feat: Adds support for Address Validation API (#448) --- .github/dependabot.yml | 14 + .github/scripts/distribution.sh | 14 + .github/scripts/install.sh | 14 + .github/stale.yml | 14 + .gitignore | 2 + README.md | 7 + coverage.xml | 849 ++++++++++++++++++++++++++++++++ docs/conf.py | 14 + googlemaps/addressvalidation.py | 80 +++ googlemaps/client.py | 36 +- noxfile.py | 14 + setup.py | 14 + tests/test_addressvalidation.py | 48 ++ text.py | 19 + 14 files changed, 1133 insertions(+), 6 deletions(-) create mode 100644 coverage.xml create mode 100644 googlemaps/addressvalidation.py create mode 100644 tests/test_addressvalidation.py create mode 100644 text.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 55a9ccdd..36f9436b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,17 @@ +# Copyright 2022 Google LLC +# +# 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. + version: 2 updates: - package-ecosystem: pip diff --git a/.github/scripts/distribution.sh b/.github/scripts/distribution.sh index f7c08690..e779cd1d 100755 --- a/.github/scripts/distribution.sh +++ b/.github/scripts/distribution.sh @@ -1,4 +1,18 @@ #!/bin/bash +# Copyright 2022 Google LLC +# +# 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. + rm -rf dist diff --git a/.github/scripts/install.sh b/.github/scripts/install.sh index a585594c..39e7f9f0 100755 --- a/.github/scripts/install.sh +++ b/.github/scripts/install.sh @@ -1,4 +1,18 @@ #!/bin/bash +# Copyright 2022 Google LLC +# +# 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. + set -exo pipefail diff --git a/.github/stale.yml b/.github/stale.yml index 8ed0e080..1d39e65d 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,3 +1,17 @@ +# Copyright 2022 Google LLC +# +# 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. + # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale diff --git a/.gitignore b/.gitignore index c4477ba5..74d89080 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ googlemaps.egg-info *.egg .vscode/ .idea/ +index.py +test.py diff --git a/README.md b/README.md index 54c024b3..cb5f7821 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ APIs: - Roads API - Places API - Maps Static API + - Address Validation API Keep in mind that the same [terms and conditions](https://developers.google.com/maps/terms) apply to usage of the APIs when they're accessed through this library. @@ -85,6 +86,12 @@ directions_result = gmaps.directions("Sydney Town Hall", "Parramatta, NSW", mode="transit", departure_time=now) + +# Validate an address with address validation +addressvalidation_result = gmaps.addressvalidation(['1600 Amphitheatre Pk'], + regionCode='US', + locality='Mountain View', + enableUspsCass=True) ``` For more usage examples, check out [the tests](https://github.com/googlemaps/google-maps-services-python/tree/master/tests). diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 00000000..1c38ca3d --- /dev/null +++ b/coverage.xml @@ -0,0 +1,849 @@ + + + + + + /Users/anglarett/Public/Drop Box/dev-se-git/google-maps-services-python + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/conf.py b/docs/conf.py index 19cdfef5..0d8314fb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,17 @@ +# Copyright 2022 Google LLC +# +# 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. + # -*- coding: utf-8 -*- # # Maps API documentation build configuration file, created by diff --git a/googlemaps/addressvalidation.py b/googlemaps/addressvalidation.py new file mode 100644 index 00000000..45b74655 --- /dev/null +++ b/googlemaps/addressvalidation.py @@ -0,0 +1,80 @@ +# +# Copyright 2014 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Performs requests to the Google Maps Address Validation API.""" +from googlemaps import exceptions + + +_ADDRESSVALIDATION_BASE_URL = "https://addressvalidation.googleapis.com" + + +def _addressvalidation_extract(response): + """ + Mimics the exception handling logic in ``client._get_body``, but + for addressvalidation which uses a different response format. + """ + body = response.json() + return body + + # if response.status_code in (200, 404): + # return body + + # try: + # error = body["error"]["errors"][0]["reason"] + # except KeyError: + # error = None + + # if response.status_code == 403: + # raise exceptions._OverQueryLimit(response.status_code, error) + # else: + # raise exceptions.ApiError(response.status_code, error) + + +def addressvalidation(client, addressLines, regionCode=None , locality=None, enableUspsCass=None): + """ + The Google Maps Address Validation API returns a verification of an address + See https://developers.google.com/maps/documentation/address-validation/overview + request must include parameters below. + :param addressLines: The address to validate + :type addressLines: array + :param regionCode: (optional) The country code + :type regionCode: string + :param locality: (optional) Restrict to a locality, ie:Mountain View + :type locality: string + :param enableUspsCass For the "US" and "PR" regions only, you can optionally enable the Coding Accuracy Support System (CASS) from the United States Postal Service (USPS) + :type locality: boolean + """ + + params = { + "address":{ + "addressLines": addressLines + } + } + + if regionCode is not None: + params["address"]["regionCode"] = regionCode + + if locality is not None: + params["address"]["locality"] = locality + + if enableUspsCass is not False or enableUspsCass is not None: + params["enableUspsCass"] = enableUspsCass + + return client._request("/v1:validateAddress", {}, # No GET params + base_url=_ADDRESSVALIDATION_BASE_URL, + extract_body=_addressvalidation_extract, + post_json=params) \ No newline at end of file diff --git a/googlemaps/client.py b/googlemaps/client.py index 1334571d..54838fa0 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -31,6 +31,8 @@ import requests import random import time +import math +import sys import googlemaps @@ -52,7 +54,7 @@ class Client: def __init__(self, key=None, client_id=None, client_secret=None, timeout=None, connect_timeout=None, read_timeout=None, retry_timeout=60, requests_kwargs=None, - queries_per_second=50, channel=None, + queries_per_second=60, queries_per_minute=6000,channel=None, retry_over_query_limit=True, experience_id=None, requests_session=None, base_url=_DEFAULT_BASE_URL): @@ -93,11 +95,16 @@ def __init__(self, key=None, client_id=None, client_secret=None, seconds. :type retry_timeout: int - :param queries_per_second: Number of queries per second permitted. + :param queries_per_second: Number of queries per second permitted. Unset queries_per_minute to None. If set smaller number will be used. If the rate limit is reached, the client will sleep for the appropriate amount of time before it runs the current query. :type queries_per_second: int + :param queries_per_minute: Number of queries per minute permitted. Unset queries_per_second to None. If set smaller number will be used. + If the rate limit is reached, the client will sleep for the + appropriate amount of time before it runs the current query. + :type queries_per_minute: int + :param retry_over_query_limit: If True, requests that result in a response indicating the query rate limit was exceeded will be retried. Defaults to True. @@ -169,10 +176,26 @@ def __init__(self, key=None, client_id=None, client_secret=None, "timeout": self.timeout, "verify": True, # NOTE(cbro): verify SSL certs. }) - + + self.queries_quota : int self.queries_per_second = queries_per_second + self.queries_per_minute = queries_per_minute + try: + if (type(self.queries_per_second) == int and type(self.queries_per_minute) == int ): + self.queries_quota = math.floor(min(self.queries_per_second, self.queries_per_minute/60)) + elif (self.queries_per_second and type(self.queries_per_second) == int ): + self.queries_quota = math.floor(self.queries_per_second) + elif (self.queries_per_minute and type(self.queries_per_minute) == int ): + self.queries_quota = math.floor(self.queries_per_minute/60) + else: + sys.exit("MISSING VALID NUMBER for queries_per_second or queries_per_minute") + print("\n","API queries_quota:", self.queries_quota,"\n") + + except NameError: + sys.exit("MISSING VALUE for queries_per_second or queries_per_minute") + self.retry_over_query_limit = retry_over_query_limit - self.sent_times = collections.deque("", queries_per_second) + self.sent_times = collections.deque("", self.queries_quota) self.set_experience_id(experience_id) self.base_url = base_url @@ -303,7 +326,7 @@ def _request(self, url, params, first_request_time=None, retry_counter=0, # Check if the time of the nth previous query (where n is # queries_per_second) is under a second ago - if so, sleep for # the difference. - if self.sent_times and len(self.sent_times) == self.queries_per_second: + if self.sent_times and len(self.sent_times) == self.queries_quota: elapsed_since_earliest = time.time() - self.sent_times[0] if elapsed_since_earliest < 1: time.sleep(1 - elapsed_since_earliest) @@ -402,7 +425,7 @@ def _generate_auth_url(self, path, params, accepts_clientid): from googlemaps.places import places_autocomplete from googlemaps.places import places_autocomplete_query from googlemaps.maps import static_map - +from googlemaps.addressvalidation import addressvalidation def make_api_method(func): """ @@ -446,6 +469,7 @@ def wrapper(*args, **kwargs): Client.places_autocomplete = make_api_method(places_autocomplete) Client.places_autocomplete_query = make_api_method(places_autocomplete_query) Client.static_map = make_api_method(static_map) +Client.addressvalidation = make_api_method(addressvalidation) def sign_hmac(secret, payload): diff --git a/noxfile.py b/noxfile.py index d3357fe4..2bc3b2a1 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,3 +1,17 @@ +# Copyright 2022 Google LLC +# +# 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. + import nox SUPPORTED_PY_VERSIONS = ["3.7", "3.8", "3.9", "3.10"] diff --git a/setup.py b/setup.py index 8826822d..b2536fc3 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,17 @@ +# Copyright 2022 Google LLC +# +# 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. + from setuptools import setup diff --git a/tests/test_addressvalidation.py b/tests/test_addressvalidation.py new file mode 100644 index 00000000..69ad8b70 --- /dev/null +++ b/tests/test_addressvalidation.py @@ -0,0 +1,48 @@ +# This Python file uses the following encoding: utf-8 +# +# Copyright 2017 Google Inc. All rights reserved. +# +# +# 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. +# + +"""Tests for the addressvalidation module.""" + +import responses + +import googlemaps +from . import TestCase + + +class AddressValidationTest(TestCase): + def setUp(self): + self.key = "AIzaasdf" + self.client = googlemaps.Client(self.key) + + @responses.activate + def test_simple_addressvalidation(self): + responses.add( + responses.POST, + "https://addressvalidation.googleapis.com/v1:validateAddress", + body='{"address": {"regionCode": "US","locality": "Mountain View","addressLines": "1600 Amphitheatre Pkwy"},"enableUspsCass":true}', + status=200, + content_type="application/json", + ) + + results = self.client.addressvalidation('1600 Amphitheatre Pk', regionCode='US', locality='Mountain View', enableUspsCass=True) + + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://addressvalidation.googleapis.com/v1:validateAddress?" "key=%s" % self.key, + responses.calls[0].request.url, + ) \ No newline at end of file diff --git a/text.py b/text.py new file mode 100644 index 00000000..13734488 --- /dev/null +++ b/text.py @@ -0,0 +1,19 @@ +import math + +queries_quota : int +queries_per_second = 60 # None or 60 +queries_per_minute = None # None or 6000 + +try: + if (type(queries_per_second) == int and type(queries_per_minute) == int ): + queries_quota = math.floor(min(queries_per_second, queries_per_minute/60)) + elif (queries_per_second): + queries_quota = math.floor(queries_per_second) + elif (queries_per_minute): + queries_quota = math.floor(queries_per_minute/60) + else: + print("MISSING VALID NUMBER for queries_per_second or queries_per_minute") + print(queries_quota) + +except NameError: + print("MISSING VALUE for queries_per_second or queries_per_minute") \ No newline at end of file From 6d7a993d49c090c3e06fd1e3bf3019d184279b47 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 9 Nov 2022 16:20:57 +0000 Subject: [PATCH 149/166] chore(release): 4.7.0 [skip ci] # [4.7.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.6.0...v4.7.0) (2022-11-09) ### Features * Adds support for Address Validation API ([#448](https://github.com/googlemaps/google-maps-services-python/issues/448)) ([3bcb05f](https://github.com/googlemaps/google-maps-services-python/commit/3bcb05f56ec0745d9ecaac677c31963cac8ece18)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index ffde203b..4a3b3b91 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.6.0" +__version__ = "4.7.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index b2536fc3..51ccabf8 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.6.0", + version="4.7.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 83ad82772fcfba2ebe798ae544d7f01df1f6ded7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Lilleb=C3=B8=20Gundersen?= Date: Mon, 21 Nov 2022 22:59:47 +0100 Subject: [PATCH 150/166] fix: Convert print statement to info log (#455) --- googlemaps/client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 54838fa0..6684ed1e 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -22,6 +22,7 @@ import base64 import collections +import logging from datetime import datetime from datetime import timedelta import functools @@ -41,6 +42,8 @@ except ImportError: # Python 2 from urllib import urlencode +logger = logging.getLogger(__name__) + _X_GOOG_MAPS_EXPERIENCE_ID = "X-Goog-Maps-Experience-ID" _USER_AGENT = "GoogleGeoApiClientPython/%s" % googlemaps.__version__ _DEFAULT_BASE_URL = "https://maps.googleapis.com" @@ -189,7 +192,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.queries_quota = math.floor(self.queries_per_minute/60) else: sys.exit("MISSING VALID NUMBER for queries_per_second or queries_per_minute") - print("\n","API queries_quota:", self.queries_quota,"\n") + logger.info("API queries_quota:", self.queries_quota) except NameError: sys.exit("MISSING VALUE for queries_per_second or queries_per_minute") From 8fde9501b4df675a1ae9f763c29344b407aacf6f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 21 Nov 2022 22:00:51 +0000 Subject: [PATCH 151/166] chore(release): 4.7.1 [skip ci] ## [4.7.1](https://github.com/googlemaps/google-maps-services-python/compare/v4.7.0...v4.7.1) (2022-11-21) ### Bug Fixes * Convert print statement to info log ([#455](https://github.com/googlemaps/google-maps-services-python/issues/455)) ([83ad827](https://github.com/googlemaps/google-maps-services-python/commit/83ad82772fcfba2ebe798ae544d7f01df1f6ded7)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 4a3b3b91..226be765 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.7.0" +__version__ = "4.7.1" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 51ccabf8..053cd4d8 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.7.0", + version="4.7.1", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 5b952d73f8374baac876b4d845fd46cebec6ed7e Mon Sep 17 00:00:00 2001 From: anglarett Date: Tue, 22 Nov 2022 00:09:25 +0100 Subject: [PATCH 152/166] fix: fixes broken support for python 3.5 (#453) * fix tests * updates after review --- .github/workflows/test.yml | 4 ++-- .gitignore | 1 - googlemaps/addressvalidation.py | 5 +++-- googlemaps/client.py | 1 - tests/test_addressvalidation.py | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ddcc2e6c..286ff75a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,10 +32,10 @@ jobs: python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Python - uses: "actions/setup-python@v1" + uses: "actions/setup-python@v3" with: python-version: "${{ matrix.python-version }}" diff --git a/.gitignore b/.gitignore index 74d89080..93e7a2fc 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,3 @@ googlemaps.egg-info .vscode/ .idea/ index.py -test.py diff --git a/googlemaps/addressvalidation.py b/googlemaps/addressvalidation.py index 45b74655..149f3b48 100644 --- a/googlemaps/addressvalidation.py +++ b/googlemaps/addressvalidation.py @@ -1,5 +1,5 @@ # -# Copyright 2014 Google Inc. All rights reserved. +# Copyright 2022 Google Inc. All rights reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -77,4 +77,5 @@ def addressvalidation(client, addressLines, regionCode=None , locality=None, ena return client._request("/v1:validateAddress", {}, # No GET params base_url=_ADDRESSVALIDATION_BASE_URL, extract_body=_addressvalidation_extract, - post_json=params) \ No newline at end of file + post_json=params) + \ No newline at end of file diff --git a/googlemaps/client.py b/googlemaps/client.py index 6684ed1e..301d6cab 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -180,7 +180,6 @@ def __init__(self, key=None, client_id=None, client_secret=None, "verify": True, # NOTE(cbro): verify SSL certs. }) - self.queries_quota : int self.queries_per_second = queries_per_second self.queries_per_minute = queries_per_minute try: diff --git a/tests/test_addressvalidation.py b/tests/test_addressvalidation.py index 69ad8b70..d1f2f589 100644 --- a/tests/test_addressvalidation.py +++ b/tests/test_addressvalidation.py @@ -26,7 +26,7 @@ class AddressValidationTest(TestCase): def setUp(self): - self.key = "AIzaasdf" + self.key = "AIzaSyD_sJl0qMA65CYHMBokVfMNA7AKyt5ERYs" self.client = googlemaps.Client(self.key) @responses.activate From c5982e7eadd8ded7bfcef862fa6182516125d19b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 21 Nov 2022 23:10:43 +0000 Subject: [PATCH 153/166] chore(release): 4.7.2 [skip ci] ## [4.7.2](https://github.com/googlemaps/google-maps-services-python/compare/v4.7.1...v4.7.2) (2022-11-21) ### Bug Fixes * fixes broken support for python 3.5 ([#453](https://github.com/googlemaps/google-maps-services-python/issues/453)) ([5b952d7](https://github.com/googlemaps/google-maps-services-python/commit/5b952d73f8374baac876b4d845fd46cebec6ed7e)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 226be765..ca3b7518 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.7.1" +__version__ = "4.7.2" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 053cd4d8..89c10a0f 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.7.1", + version="4.7.2", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From dcebf77cb8dc4e45a2bd8e95f8851d86811322a7 Mon Sep 17 00:00:00 2001 From: sandre35 <65041823+sandre35@users.noreply.github.com> Date: Tue, 22 Nov 2022 20:29:15 +0100 Subject: [PATCH 154/166] fix: correct lazy attribute inside logger (#457) --- googlemaps/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/client.py b/googlemaps/client.py index 301d6cab..d1f4ab6a 100644 --- a/googlemaps/client.py +++ b/googlemaps/client.py @@ -191,7 +191,7 @@ def __init__(self, key=None, client_id=None, client_secret=None, self.queries_quota = math.floor(self.queries_per_minute/60) else: sys.exit("MISSING VALID NUMBER for queries_per_second or queries_per_minute") - logger.info("API queries_quota:", self.queries_quota) + logger.info("API queries_quota: %s", self.queries_quota) except NameError: sys.exit("MISSING VALUE for queries_per_second or queries_per_minute") From ca042f50ad8c2a68ed99dd89dad12bfe9b31cdf3 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 22 Nov 2022 19:30:32 +0000 Subject: [PATCH 155/166] chore(release): 4.7.3 [skip ci] ## [4.7.3](https://github.com/googlemaps/google-maps-services-python/compare/v4.7.2...v4.7.3) (2022-11-22) ### Bug Fixes * correct lazy attribute inside logger ([#457](https://github.com/googlemaps/google-maps-services-python/issues/457)) ([dcebf77](https://github.com/googlemaps/google-maps-services-python/commit/dcebf77cb8dc4e45a2bd8e95f8851d86811322a7)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index ca3b7518..f25d9359 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.7.2" +__version__ = "4.7.3" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 89c10a0f..239eceb0 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.7.2", + version="4.7.3", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 2c30f3a61d6632a6c7e7ec91718b97d6a1051379 Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Mon, 23 Jan 2023 15:09:05 -0800 Subject: [PATCH 156/166] docs: Adding docs.yml workflow to update docs on a new tag. (#360) * docs: Adding docs.yml workflow to update docs on a new tag. Once the docs have been generated, a new PR will be opened against the `gh-pages` branch. Resolves #348 Co-authored-by: Angela Yu <5506675+wangela@users.noreply.github.com> --- .github/workflows/docs.yml | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..c83d6c38 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,63 @@ +# Copyright 2023 Google LLC +# +# 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. + +# A workflow that pushes artifacts to Sonatype +name: Publish + +on: + push: + tags: + - '*' + repository_dispatch: + types: [docs] + +jobs: + docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Python + uses: "actions/setup-python@v3" + with: + python-version: "3.9" + + - name: Install dependencies + run: ./.github/scripts/install.sh + + - name: Generate docs + run: python3 -m nox --session docs + + - name: Update gh-pages branch with docs + run: | + echo "Creating tar for generated docs" + cd ./docs/_build/html && tar cvf ~/docs.tar . + + echo "Unpacking tar into gh-pages branch" + git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* + cd $GITHUB_WORKSPACE && git checkout gh-pages && tar xvf ~/docs.tar + + - name: PR Changes + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} + commit-message: 'docs: Update docs' + committer: googlemaps-bot + author: googlemaps-bot + title: 'docs: Update docs' + body: | + Updated GitHub pages with latest from `python3 -m nox --session docs`. + branch: googlemaps-bot/update_gh_pages From 88268363f4612fa97a525c149f7dce3e36f293ef Mon Sep 17 00:00:00 2001 From: Angela Yu <5506675+wangela@users.noreply.github.com> Date: Mon, 23 Jan 2023 23:34:06 -0800 Subject: [PATCH 157/166] chore: update release action dependencies (#473) * chore: update release action dependencies * chore: set semantic release version --- .github/workflows/release.yml | 7 ++++--- .github/workflows/test.yml | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce78e642..a0a28dce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,11 +23,11 @@ jobs: PYTHONDONTWRITEBYTECODE: 1 steps: - name: Setup Python - uses: "actions/setup-python@v1" + uses: "actions/setup-python@v3" with: python-version: "3.9" - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: token: ${{ secrets.SYNCED_GITHUB_TOKEN_REPO }} - name: Install dependencies @@ -37,8 +37,9 @@ jobs: - name: Cleanup old dist run: rm -rf googlemaps-* dist/ - name: Semantic Release - uses: cycjimmy/semantic-release-action@v2 + uses: cycjimmy/semantic-release-action@v3 with: + semantic_version: 19 extra_plugins: | "@semantic-release/commit-analyzer" "@semantic-release/release-notes-generator" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 286ff75a..570a087a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# Copyright 2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -51,4 +51,6 @@ jobs: needs: [matrix] runs-on: ubuntu-latest steps: - - run: echo "Test matrix finished" + - run: | + echo "Test matrix finished"; + exit 0; From 9f09ccbaecef7c1244f07f8cccc39024ad5fddee Mon Sep 17 00:00:00 2001 From: Joe Loffredo <60896458+jloffredo2@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:11:59 -0500 Subject: [PATCH 158/166] feat: add `editorial_summary` to Place Details atmosphere fields (#472) --- googlemaps/places.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 91c14dfe..ac3bf53e 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -80,7 +80,7 @@ PLACES_DETAIL_FIELDS_CONTACT = {"formatted_phone_number", "international_phone_number", "opening_hours", "website"} -PLACES_DETAIL_FIELDS_ATMOSPHERE = {"price_level", "rating", "review", "user_ratings_total"} +PLACES_DETAIL_FIELDS_ATMOSPHERE = {"editorial_summary","price_level", "rating", "review", "user_ratings_total"} PLACES_DETAIL_FIELDS = ( PLACES_DETAIL_FIELDS_BASIC From 65d5a263e683e3763a6e1c076d1c5778211fd745 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 24 Jan 2023 19:13:04 +0000 Subject: [PATCH 159/166] chore(release): 4.8.0 [skip ci] # [4.8.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.7.3...v4.8.0) (2023-01-24) ### Features * add `editorial_summary` to Place Details atmosphere fields ([#472](https://github.com/googlemaps/google-maps-services-python/issues/472)) ([9f09ccb](https://github.com/googlemaps/google-maps-services-python/commit/9f09ccbaecef7c1244f07f8cccc39024ad5fddee)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index f25d9359..be61bb0e 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.7.3" +__version__ = "4.8.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 239eceb0..3958aed2 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.7.3", + version="4.8.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 8afe62898f1733316fea705e8daa0313add7b93c Mon Sep 17 00:00:00 2001 From: nnolan <36713193+nnolan@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:23:43 -0600 Subject: [PATCH 160/166] feat: add support for sorting reviews in Place Details requests (#468) --- googlemaps/places.py | 16 ++++++++++++++-- tests/test_places.py | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index ac3bf53e..35b818f2 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -396,7 +396,14 @@ def _places( return client._request(url, params) -def place(client, place_id, session_token=None, fields=None, language=None): +def place( + client, + place_id, + session_token=None, + fields=None, + language=None, + reviews_sort="most_relevant" +): """ Comprehensive details for an individual place. @@ -416,6 +423,10 @@ def place(client, place_id, session_token=None, fields=None, language=None): :param language: The language in which to return results. :type language: string + :param reviews_sort: The sorting method to use when returning reviews. + Can be set to most_relevant (default) or newest. + :type reviews_sort: string + :rtype: result dict with the following keys: result: dict containing place details html_attributions: set of attributions which must be displayed @@ -444,6 +455,8 @@ def place(client, place_id, session_token=None, fields=None, language=None): params["language"] = language if session_token: params["sessiontoken"] = session_token + if reviews_sort: + params["reviews_sort"] = reviews_sort return client._request("/maps/api/place/details/json", params) @@ -657,4 +670,3 @@ def _autocomplete( url = "/maps/api/place/%sautocomplete/json" % url_part return client._request(url, params).get("predictions", []) - \ No newline at end of file diff --git a/tests/test_places.py b/tests/test_places.py index 50781773..dd29935d 100644 --- a/tests/test_places.py +++ b/tests/test_places.py @@ -36,6 +36,7 @@ def setUp(self): self.type = "liquor_store" self.language = "en-AU" self.region = "AU" + self.reviews_sort="newest" self.radius = 100 @responses.activate @@ -165,11 +166,12 @@ def test_place_detail(self): "ChIJN1t_tDeuEmsRUsoyG83frY4", fields=["business_status", "geometry/location", "place_id"], language=self.language, + reviews_sort=self.reviews_sort, ) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - "%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4" + "%s?reviews_sort=newest&language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4" "&key=%s&fields=business_status,geometry/location,place_id" % (url, self.key), responses.calls[0].request.url, From 72c482c78d1cbb86eb9847e09e0976b16c0f49a6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 24 Jan 2023 22:24:47 +0000 Subject: [PATCH 161/166] chore(release): 4.9.0 [skip ci] # [4.9.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.8.0...v4.9.0) (2023-01-24) ### Features * add support for sorting reviews in Place Details requests ([#468](https://github.com/googlemaps/google-maps-services-python/issues/468)) ([8afe628](https://github.com/googlemaps/google-maps-services-python/commit/8afe62898f1733316fea705e8daa0313add7b93c)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index be61bb0e..6aabb596 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.8.0" +__version__ = "4.9.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 3958aed2..634cfb42 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.8.0", + version="4.9.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 6c69310997e2e82edb5ee3e7235a3057945e7c76 Mon Sep 17 00:00:00 2001 From: Angela Yu <5506675+wangela@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:43:40 -0800 Subject: [PATCH 162/166] feat: add new place details fields and reviews request modifiers (#474) * feat: add new place details fields and support for reviews sorting and translation * feat: support reviews (plural) field --- googlemaps/places.py | 97 ++++++++++++++++++++++++++++++-------------- tests/test_places.py | 12 +++--- 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/googlemaps/places.py b/googlemaps/places.py index 35b818f2..269a17fa 100644 --- a/googlemaps/places.py +++ b/googlemaps/places.py @@ -52,35 +52,64 @@ ^ PLACES_FIND_FIELDS_ATMOSPHERE ) -PLACES_DETAIL_FIELDS_BASIC = {"address_component", - "adr_address", - "business_status", - "formatted_address", - "geometry", - "geometry/location", - "geometry/location/lat", - "geometry/location/lng", - "geometry/viewport", - "geometry/viewport/northeast", - "geometry/viewport/northeast/lat", - "geometry/viewport/northeast/lng", - "geometry/viewport/southwest", - "geometry/viewport/southwest/lat", - "geometry/viewport/southwest/lng", - "icon", - "name", - "permanently_closed", - "photo", - "place_id", - "plus_code", - "type", - "url", - "utc_offset", - "vicinity",} - -PLACES_DETAIL_FIELDS_CONTACT = {"formatted_phone_number", "international_phone_number", "opening_hours", "website"} - -PLACES_DETAIL_FIELDS_ATMOSPHERE = {"editorial_summary","price_level", "rating", "review", "user_ratings_total"} +PLACES_DETAIL_FIELDS_BASIC = { + "address_component", + "adr_address", + "business_status", + "formatted_address", + "geometry", + "geometry/location", + "geometry/location/lat", + "geometry/location/lng", + "geometry/viewport", + "geometry/viewport/northeast", + "geometry/viewport/northeast/lat", + "geometry/viewport/northeast/lng", + "geometry/viewport/southwest", + "geometry/viewport/southwest/lat", + "geometry/viewport/southwest/lng", + "icon", + "name", + "permanently_closed", + "photo", + "place_id", + "plus_code", + "type", + "url", + "utc_offset", + "vicinity", + "wheelchair_accessible_entrance" +} + +PLACES_DETAIL_FIELDS_CONTACT = { + "formatted_phone_number", + "international_phone_number", + "opening_hours", + "current_opening_hours", + "secondary_opening_hours", + "website", +} + +PLACES_DETAIL_FIELDS_ATMOSPHERE = { + "curbside_pickup", + "delivery", + "dine_in", + "editorial_summary", + "price_level", + "rating", + "reservable", + "review", # prefer "reviews" to match API documentation + "reviews", + "serves_beer", + "serves_breakfast", + "serves_brunch", + "serves_dinner", + "serves_lunch", + "serves_vegetarian_food", + "serves_wine", + "takeout", + "user_ratings_total" +} PLACES_DETAIL_FIELDS = ( PLACES_DETAIL_FIELDS_BASIC @@ -88,7 +117,7 @@ ^ PLACES_DETAIL_FIELDS_ATMOSPHERE ) -DEPRECATED_FIELDS = {"permanently_closed"} +DEPRECATED_FIELDS = {"permanently_closed", "review"} DEPRECATED_FIELDS_MESSAGE = ( "Fields, %s, are deprecated. " "Read more at https://developers.google.com/maps/deprecations." @@ -402,7 +431,8 @@ def place( session_token=None, fields=None, language=None, - reviews_sort="most_relevant" + reviews_no_translations=False, + reviews_sort="most_relevant", ): """ Comprehensive details for an individual place. @@ -423,6 +453,9 @@ def place( :param language: The language in which to return results. :type language: string + :param reviews_no_translations: Specify reviews_no_translations=True to disable translation of reviews; reviews_no_translations=False (default) enables translation of reviews. + :type reviews_no_translations: bool + :param reviews_sort: The sorting method to use when returning reviews. Can be set to most_relevant (default) or newest. :type reviews_sort: string @@ -455,6 +488,8 @@ def place( params["language"] = language if session_token: params["sessiontoken"] = session_token + if reviews_no_translations: + params["reviews_no_translations"] = "true" if reviews_sort: params["reviews_sort"] = reviews_sort diff --git a/tests/test_places.py b/tests/test_places.py index dd29935d..32f03d16 100644 --- a/tests/test_places.py +++ b/tests/test_places.py @@ -36,7 +36,6 @@ def setUp(self): self.type = "liquor_store" self.language = "en-AU" self.region = "AU" - self.reviews_sort="newest" self.radius = 100 @responses.activate @@ -164,15 +163,18 @@ def test_place_detail(self): self.client.place( "ChIJN1t_tDeuEmsRUsoyG83frY4", - fields=["business_status", "geometry/location", "place_id"], + fields=["business_status", "geometry/location", + "place_id", "reviews"], language=self.language, - reviews_sort=self.reviews_sort, + reviews_no_translations=True, + reviews_sort="newest", ) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( - "%s?reviews_sort=newest&language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4" - "&key=%s&fields=business_status,geometry/location,place_id" + "%s?language=en-AU&placeid=ChIJN1t_tDeuEmsRUsoyG83frY4" + "&reviews_no_translations=true&reviews_sort=newest" + "&key=%s&fields=business_status,geometry/location,place_id,reviews" % (url, self.key), responses.calls[0].request.url, ) From 82674b7fccb00d611f36b11cab6f281827174f8b Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 26 Jan 2023 16:44:56 +0000 Subject: [PATCH 163/166] chore(release): 4.10.0 [skip ci] # [4.10.0](https://github.com/googlemaps/google-maps-services-python/compare/v4.9.0...v4.10.0) (2023-01-26) ### Features * add new place details fields and reviews request modifiers ([#474](https://github.com/googlemaps/google-maps-services-python/issues/474)) ([6c69310](https://github.com/googlemaps/google-maps-services-python/commit/6c69310997e2e82edb5ee3e7235a3057945e7c76)) --- googlemaps/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/googlemaps/__init__.py b/googlemaps/__init__.py index 6aabb596..61ec45d0 100644 --- a/googlemaps/__init__.py +++ b/googlemaps/__init__.py @@ -15,7 +15,7 @@ # the License. # -__version__ = "4.9.0" +__version__ = "4.10.0" from googlemaps.client import Client from googlemaps import exceptions diff --git a/setup.py b/setup.py index 634cfb42..df9f32f1 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="googlemaps", - version="4.9.0", + version="4.10.0", description="Python client library for Google Maps Platform", long_description=readme + changelog, long_description_content_type="text/markdown", From 80cb54e16964e686d24cfd536ab8db329395e274 Mon Sep 17 00:00:00 2001 From: Angela Yu <5506675+wangela@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:52:42 -0800 Subject: [PATCH 164/166] chore: update nox docs python version --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 2bc3b2a1..e5b92961 100644 --- a/noxfile.py +++ b/noxfile.py @@ -52,7 +52,7 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python="3.6") +@nox.session(python="3.7") def docs(session): _install_dev_packages(session) _install_doc_dependencies(session) From 645e07de5a27c4c858b2c0673f0dd6f23ca62d28 Mon Sep 17 00:00:00 2001 From: Angela Yu <5506675+wangela@users.noreply.github.com> Date: Thu, 26 Jan 2023 17:01:18 -0800 Subject: [PATCH 165/166] docs: fix code format in static maps docs (#475) --- googlemaps/maps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/googlemaps/maps.py b/googlemaps/maps.py index cc1a054e..746223d6 100644 --- a/googlemaps/maps.py +++ b/googlemaps/maps.py @@ -123,7 +123,7 @@ def __init__(self, points, def static_map(client, size, - center=None, zoom=None, scale=None, + center=None, zoom=None, scale=None, format=None, maptype=None, language=None, region=None, markers=None, path=None, visible=None, style=None): """ @@ -181,7 +181,8 @@ def static_map(client, size, :rtype: iterator containing the raw image data, which typically can be used to save an image file locally. For example: - ``` + .. code-block:: python + f = open(local_filename, 'wb') for chunk in client.static_map(size=(400, 400), center=(52.520103, 13.404871), @@ -189,7 +190,6 @@ def static_map(client, size, if chunk: f.write(chunk) f.close() - ``` """ params = {"size": convert.size(size)} From 9ec69cb66eec929d08ca90a82081b8ee4eef8b54 Mon Sep 17 00:00:00 2001 From: Thomas Clifford Date: Tue, 16 Jul 2024 10:47:22 -0700 Subject: [PATCH 166/166] =?UTF-8?q?feat!:=20Added=20Address=20Descriptors?= =?UTF-8?q?=20to=20Geocoding=20response.=20Refactored=20Geo=E2=80=A6=20(#5?= =?UTF-8?q?16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat! Added Address Descriptors to Geocoding response. Refactored Geocoding response to allow fields outside the geocoding result to be exposed through the client. --------- Co-authored-by: Tom Clifford --- README.md | 4 +++ googlemaps/geocoding.py | 18 ++++++++++---- tests/test_geocoding.py | 54 +++++++++++++++++++++++++++++------------ 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index cb5f7821..40823790 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,10 @@ addressvalidation_result = gmaps.addressvalidation(['1600 Amphitheatre Pk'], regionCode='US', locality='Mountain View', enableUspsCass=True) + +# Get an Address Descriptor of a location in the reverse geocoding response +address_descriptor_result = gmaps.reverse_geocode((40.714224, -73.961452), enable_address_descriptor=True) + ``` For more usage examples, check out [the tests](https://github.com/googlemaps/google-maps-services-python/tree/master/tests). diff --git a/googlemaps/geocoding.py b/googlemaps/geocoding.py index e409a49e..590bb627 100644 --- a/googlemaps/geocoding.py +++ b/googlemaps/geocoding.py @@ -49,7 +49,9 @@ def geocode(client, address=None, place_id=None, components=None, bounds=None, r :param language: The language in which to return results. :type language: string - :rtype: list of geocoding results. + :rtype: result dict with the following keys: + status: status code + results: list of geocoding results """ params = {} @@ -72,11 +74,11 @@ def geocode(client, address=None, place_id=None, components=None, bounds=None, r if language: params["language"] = language - return client._request("/maps/api/geocode/json", params).get("results", []) + return client._request("/maps/api/geocode/json", params) def reverse_geocode(client, latlng, result_type=None, location_type=None, - language=None): + language=None, enable_address_descriptor=False): """ Reverse geocoding is the process of converting geographic coordinates into a human-readable address. @@ -94,7 +96,10 @@ def reverse_geocode(client, latlng, result_type=None, location_type=None, :param language: The language in which to return results. :type language: string - :rtype: list of reverse geocoding results. + :rtype: result dict with the following keys: + status: status code + results: list of reverse geocoding results + address_descriptor: address descriptor for the target """ # Check if latlng param is a place_id string. @@ -113,4 +118,7 @@ def reverse_geocode(client, latlng, result_type=None, location_type=None, if language: params["language"] = language - return client._request("/maps/api/geocode/json", params).get("results", []) + if enable_address_descriptor: + params["enable_address_descriptor"] = "true" + + return client._request("/maps/api/geocode/json", params) diff --git a/tests/test_geocoding.py b/tests/test_geocoding.py index 813241f6..8734c8b8 100644 --- a/tests/test_geocoding.py +++ b/tests/test_geocoding.py @@ -41,7 +41,7 @@ def test_simple_geocode(self): content_type="application/json", ) - results = self.client.geocode("Sydney") + results = self.client.geocode("Sydney").get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -60,7 +60,7 @@ def test_reverse_geocode(self): content_type="application/json", ) - results = self.client.reverse_geocode((-33.8674869, 151.2069902)) + results = self.client.reverse_geocode((-33.8674869, 151.2069902)).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -79,7 +79,7 @@ def test_geocoding_the_googleplex(self): content_type="application/json", ) - results = self.client.geocode("1600 Amphitheatre Parkway, " "Mountain View, CA") + results = self.client.geocode("1600 Amphitheatre Parkway, " "Mountain View, CA").get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -105,7 +105,7 @@ def test_geocode_with_bounds(self): "southwest": (34.172684, -118.604794), "northeast": (34.236144, -118.500938), }, - ) + ).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -125,7 +125,7 @@ def test_geocode_with_region_biasing(self): content_type="application/json", ) - results = self.client.geocode("Toledo", region="es") + results = self.client.geocode("Toledo", region="es").get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -144,7 +144,7 @@ def test_geocode_with_component_filter(self): content_type="application/json", ) - results = self.client.geocode("santa cruz", components={"country": "ES"}) + results = self.client.geocode("santa cruz", components={"country": "ES"}).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -165,7 +165,7 @@ def test_geocode_with_multiple_component_filters(self): results = self.client.geocode( "Torun", components={"administrative_area": "TX", "country": "US"} - ) + ).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -191,7 +191,7 @@ def test_geocode_with_just_components(self): "administrative_area": "Helsinki", "country": "Finland", } - ) + ).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -211,7 +211,7 @@ def test_geocode_place_id(self): content_type="application/json", ) - results = self.client.geocode(place_id="ChIJeRpOeF67j4AR9ydy_PIzPuM") + results = self.client.geocode(place_id="ChIJeRpOeF67j4AR9ydy_PIzPuM").get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -230,7 +230,7 @@ def test_simple_reverse_geocode(self): content_type="application/json", ) - results = self.client.reverse_geocode((40.714224, -73.961452)) + results = self.client.reverse_geocode((40.714224, -73.961452)).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -253,7 +253,7 @@ def test_reverse_geocode_restricted_by_type(self): (40.714224, -73.961452), location_type="ROOFTOP", result_type="street_address", - ) + ).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -277,7 +277,7 @@ def test_reverse_geocode_multiple_location_types(self): (40.714224, -73.961452), location_type=["ROOFTOP", "RANGE_INTERPOLATED"], result_type="street_address", - ) + ).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -301,7 +301,7 @@ def test_reverse_geocode_multiple_result_types(self): (40.714224, -73.961452), location_type="ROOFTOP", result_type=["street_address", "route"], - ) + ).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -311,6 +311,28 @@ def test_reverse_geocode_multiple_result_types(self): responses.calls[0].request.url, ) + @responses.activate + def test_reverse_geocode_with_address_descriptors(self): + responses.add( + responses.GET, + "https://maps.googleapis.com/maps/api/geocode/json", + body='{"status":"OK","results":[], "address_descriptor":{ "landmarks": [ { "placeId": "id" } ] } }', + status=200, + content_type="application/json", + ) + + response = self.client.reverse_geocode((-33.8674869, 151.2069902), enable_address_descriptor=True) + + address_descriptor = response.get("address_descriptor", []) + + self.assertEqual(1, len(address_descriptor["landmarks"])) + self.assertEqual(1, len(responses.calls)) + self.assertURLEqual( + "https://maps.googleapis.com/maps/api/geocode/json?" + "latlng=-33.8674869,151.2069902&enable_address_descriptor=true&key=%s" % self.key, + responses.calls[0].request.url, + ) + @responses.activate def test_partial_match(self): responses.add( @@ -321,7 +343,7 @@ def test_partial_match(self): content_type="application/json", ) - results = self.client.geocode("Pirrama Pyrmont") + results = self.client.geocode("Pirrama Pyrmont").get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -340,7 +362,7 @@ def test_utf_results(self): content_type="application/json", ) - results = self.client.geocode(components={"postal_code": "96766"}) + results = self.client.geocode(components={"postal_code": "96766"}).get("results", []) self.assertEqual(1, len(responses.calls)) self.assertURLEqual( @@ -359,7 +381,7 @@ def test_utf8_request(self): content_type="application/json", ) - self.client.geocode(self.u("\\u4e2d\\u56fd")) # China + self.client.geocode(self.u("\\u4e2d\\u56fd")).get("results", []) # China self.assertURLEqual( "https://maps.googleapis.com/maps/api/geocode/json?" "key=%s&address=%s" % (self.key, "%E4%B8%AD%E5%9B%BD"),