From bf153940b56a4cfb34a4fcd9c9b40767c4715c5c Mon Sep 17 00:00:00 2001 From: Nicola Cammillini Date: Wed, 20 Jun 2018 01:26:44 +0200 Subject: [PATCH 01/64] 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 02/64] 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 03/64] 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 04/64] 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 05/64] 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 06/64] 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 07/64] 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 08/64] 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 09/64] 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 10/64] 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 11/64] 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 12/64] 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 13/64] 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 14/64] 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 15/64] 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 16/64] 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 17/64] 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 18/64] 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 19/64] 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 20/64] 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 21/64] 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 22/64] 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 23/64] 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 24/64] 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 25/64] 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 26/64] 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 27/64] 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 28/64] 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 29/64] 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 30/64] 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 31/64] 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 32/64] 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 33/64] 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 34/64] 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 35/64] 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 36/64] 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 37/64] 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 38/64] 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 39/64] 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 40/64] 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 41/64] 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 42/64] 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 43/64] 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 44/64] 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 45/64] 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 46/64] 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 47/64] 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 48/64] 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 49/64] 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 50/64] 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 51/64] 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 52/64] 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 53/64] 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 54/64] 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 55/64] 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 56/64] 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 57/64] 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 58/64] 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 59/64] 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 60/64] 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 61/64] 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 62/64] 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 63/64] 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 64/64] 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)