diff --git a/.travis.yml b/.travis.yml index a1533bd..bbb1fd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ language: python python: - "3.4" - - "3.3" - "2.7" - "pypy" # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors @@ -15,5 +14,5 @@ install: script: - flake8 - python runtests.py - - coverage run --source skyscanner setup.py test -after_success: coveralls \ No newline at end of file + - coverage run --source skyscanner setup.py test +after_success: coveralls diff --git a/README.rst b/README.rst index 292be86..b826b5b 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,18 @@ Skyscanner Python SDK =============================== +Important +========= + As of May 1st, 2020, the project is deprecated and no longer maintained. + The latest update in v1.1.5 includes changing the library to use a secure connection when calling Skyscanner's APIs as this is required for all users and http is no longer supported. The development of this SDK from that point on is fully abandoned. + + As we cannot guarantee for how long it will continue working, you should not to use the SDK when starting new projects. If it is already in use in your projects, please upgrade to its latest version and keep in mind that due to planned changes in our Skyscanner APIs it may not work as expected after July 2020. + + +You will need to contact us to request an API key to use our APIs via the following form: https://partners.skyscanner.net/contact. + +We receive a large number of requests and although we do our best to reply to all we cannot guarantee that your application will be successful. + .. image:: https://api.travis-ci.org/Skyscanner/skyscanner-python-sdk.svg :target: https://travis-ci.org/Skyscanner/skyscanner-python-sdk @@ -20,7 +32,7 @@ Skyscanner Python SDK for Skyscanner's API * Free software: Apache license * SDK Documentation: https://skyscanner.readthedocs.org. -* API Documentation: http://business.skyscanner.net/portal/en-GB/Documentation/ApiOverview +* API Documentation: https://business.skyscanner.net/portal/en-GB/Documentation/ApiOverview Features @@ -46,13 +58,15 @@ Or, if you have virtualenvwrapper installed:: Quick start ----------- -1. Request for an API Key from `Skyscanner for Business Contact Page`_. -2. Set your API Key:: +1. Contact us to request an API key: https://partners.skyscanner.net/contact +2. If you don't already have one, create a `Skyscanner account`_. +3. Sign into your account and click 'Import Existing App' and use your API key to create an App +4. Set your API Key in your code:: from skyscanner.skyscanner import Flights flights_service = Flights('') -3. Get the flights live pricing result by writing a few lines of code:: +5. Get the flights live pricing result by writing a few lines of code:: from skyscanner.skyscanner import Flights @@ -63,24 +77,25 @@ Quick start locale='en-GB', originplace='SIN-sky', destinationplace='KUL-sky', - outbounddate='2016-07-28', - inbounddate='2016-07-31', + outbounddate='2017-05-28', + inbounddate='2017-05-31', adults=1).parsed print(result) Note that both the ``inbounddate`` and ``outbounddate`` might need to be updated. -.. _Skyscanner for Business Contact Page: http://en.business.skyscanner.net/en-gb/contact/ +.. _Skyscanner account: https://partners.skyscanner.net/log-in/ More examples ------------- -For more example usage, `refer to our documentation`_. +For more example usage, refer to the `SDK documentation`_ or the `API documentation`_. + +.. _SDK documentation: https://skyscanner.readthedocs.org/en/latest/usage.html +.. _API documentation: https://skyscanner.github.io/slate/ -.. _refer to our documentation: https://skyscanner.readthedocs.org/en/latest/usage.html - Known Issues ------------ @@ -88,4 +103,9 @@ Known Issues * Tests might appear to be broken sometimes, this is due to the throttling in the API. In such cases, you will see the following error in the build log:: requests.exceptions.HTTPError: 429 Client Error: Too many requests in the last minute. - + +* Please allow up to 15 minutes for your API key to be activated. Until it is activated you will get a 403 exception:: + + requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://partners.api.skyscanner.net/apiservices/pricing/v1.0?apiKey= + + diff --git a/docs/usage.rst b/docs/usage.rst index 8cb9ffe..0c3dd97 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -2,37 +2,39 @@ Usage ======== -To use Skyscanner Python SDK in a project, make sure that you set your API Key. For example:: +To use Skyscanner Python SDK in a project, you will need to request an API key. Please use the following form: https://partners.skyscanner.net/contact/ + +Once you have an API key you can set it as follows: from skyscanner.skyscanner import Flights - - flights_service = Flights('') + + flights_service = Flights('') Flights: Live Pricing ~~~~~~~~~~~~~~~~~~~~~ -http://business.skyscanner.net/portal/en-GB/Documentation/FlightsLivePricingList +https://business.skyscanner.net/portal/en-GB/Documentation/FlightsLivePricingList Get live prices:: from skyscanner.skyscanner import Flights - + flights_service = Flights('') result = flights_service.get_result( - country='UK', - currency='GBP', - locale='en-GB', - originplace='SIN-sky', - destinationplace='KUL-sky', - outbounddate='2015-05-28', - inbounddate='2015-05-31', + country='UK', + currency='GBP', + locale='en-GB', + originplace='SIN-sky', + destinationplace='KUL-sky', + outbounddate='2017-05-28', + inbounddate='2017-05-31', adults=1).parsed Flights: Browse Cache ~~~~~~~~~~~~~~~~~~~~~ -http://business.skyscanner.net/portal/en-GB/Documentation/FlightsBrowseCacheOverview +https://business.skyscanner.net/portal/en-GB/Documentation/FlightsBrowseCacheOverview Cheapest quotes:: @@ -41,12 +43,12 @@ Cheapest quotes:: flights_cache_service = FlightsCache('') result = flights_cache_service.get_cheapest_quotes( market='UK', - currency='GBP', - locale='en-GB', - originplace='SIN-sky', - destinationplace='KUL-sky', - outbounddate='2015-05', - inbounddate='2015-06').parsed + currency='GBP', + locale='en-GB', + originplace='SIN-sky', + destinationplace='KUL-sky', + outbounddate='2017-05', + inbounddate='2017-06').parsed Cheapest price by route:: @@ -55,12 +57,12 @@ Cheapest price by route:: flights_cache_service = FlightsCache('') result = flights_cache_service.get_cheapest_price_by_route( market='UK', - currency='GBP', - locale='en-GB', - originplace='SIN-sky', - destinationplace='KUL-sky', - outbounddate='2015-05', - inbounddate='2015-06').parsed + currency='GBP', + locale='en-GB', + originplace='SIN-sky', + destinationplace='KUL-sky', + outbounddate='2017-05', + inbounddate='2017-06').parsed Cheapest price by date:: @@ -69,12 +71,12 @@ Cheapest price by date:: flights_cache_service = FlightsCache('') result = flights_cache_service.get_cheapest_price_by_date( market='UK', - currency='GBP', - locale='en-GB', - originplace='SIN-sky', - destinationplace='KUL-sky', - outbounddate='2015-05', - inbounddate='2015-06').parsed + currency='GBP', + locale='en-GB', + originplace='SIN-sky', + destinationplace='KUL-sky', + outbounddate='2017-05', + inbounddate='2017-06').parsed Grid of prices by date:: @@ -83,31 +85,31 @@ Grid of prices by date:: flights_cache_service = FlightsCache('') result = flights_cache_service.get_grid_prices_by_date( market='UK', - currency='GBP', - locale='en-GB', - originplace='SIN-sky', - destinationplace='KUL-sky', - outbounddate='2015-05', - inbounddate='2015-06').parsed + currency='GBP', + locale='en-GB', + originplace='SIN-sky', + destinationplace='KUL-sky', + outbounddate='2017-05', + inbounddate='2017-06').parsed Car Hire ~~~~~~~~ -http://business.skyscanner.net/portal/en-GB/Documentation/CarHireLivePricing +https://business.skyscanner.net/portal/en-GB/Documentation/CarHireLivePricing Get live prices:: - + from skyscanner.skyscanner import CarHire carhire_service = CarHire('') result = carhire_service.get_result( - market='UK', - currency='GBP', - locale='en-GB', - pickupplace='LHR-sky', - dropoffplace='LHR-sky', - pickupdatetime='2015-05-29T12:00', - dropoffdatetime='2015-05-29T18:00', + market='UK', + currency='GBP', + locale='en-GB', + pickupplace='LHR-sky', + dropoffplace='LHR-sky', + pickupdatetime='2017-05-29T12:00', + dropoffdatetime='2017-05-29T18:00', driverage='30', userip='175.156.244.174').parsed @@ -117,25 +119,25 @@ Car hire autosuggest:: carhire_service = CarHire('') result = carhire_service.location_autosuggest( - market='UK', - currency='GBP', - locale='en-GB', + market='UK', + currency='GBP', + locale='en-GB', query='Kuala').parsed Hotels ~~~~~~ -http://business.skyscanner.net/portal/en-GB/Documentation/HotelsOverview +https://business.skyscanner.net/portal/en-GB/Documentation/HotelsOverview Hotels autosuggest:: - + from skyscanner.skyscanner import Hotels hotels_service = Hotels('') result = hotels_service.location_autosuggest( - market='UK', - currency='GBP', - locale='en-GB', + market='UK', + currency='GBP', + locale='en-GB', query='Kuala').parsed Hotels prices and details:: @@ -144,11 +146,11 @@ Hotels prices and details:: hotels_service = Hotels(self.api_key) result = hotels_service.get_result( - market='UK', - currency='GBP', - locale='en-GB', - entityid=27543923, - checkindate='2015-05-26', - checkoutdate='2015-05-30', - guests=1, - rooms=1).parsed \ No newline at end of file + market='UK', + currency='GBP', + locale='en-GB', + entityid=27543923, + checkindate='2017-05-26', + checkoutdate='2017-05-30', + guests=1, + rooms=1).parsed diff --git a/skyscanner/__init__.py b/skyscanner/__init__.py index b60224f..e81d8fe 100755 --- a/skyscanner/__init__.py +++ b/skyscanner/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- __author__ = 'Ardy Dedase' __email__ = 'ardy.dedase@skyscanner.net' -__version__ = '1.1.3' +__version__ = '1.1.5' __copyright__ = "Copyright (C) 2016 Skyscanner Ltd" __license__ = """ Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/skyscanner/skyscanner.py b/skyscanner/skyscanner.py index faa1c47..9fab86e 100755 --- a/skyscanner/skyscanner.py +++ b/skyscanner/skyscanner.py @@ -45,6 +45,7 @@ def configure_logger(log_level=logging.WARN): logger.addHandler(sa) return logger + log = configure_logger() STRICT, GRACEFUL, IGNORE = 'strict', 'graceful', 'ignore' @@ -73,7 +74,7 @@ class Transport(object): """ Parent class for initialization """ - API_HOST = 'http://partners.api.skyscanner.net' + API_HOST = 'https://partners.api.skyscanner.net' MARKET_SERVICE_URL = '{api_host}/apiservices/reference/v1.0/countries'\ .format(api_host=API_HOST) LOCATION_AUTOSUGGEST_URL = '{api_host}/apiservices/autosuggest/v1.0'\ @@ -92,7 +93,7 @@ def __init__(self, api_key, response_format='json'): if response_format.lower() not in self._SUPPORTED_FORMATS: raise ValueError( 'Unknown response format: {}'.format(response_format) + - ', supported formats are '.format( + + ', supported formats are '.format( ', '.join(self._SUPPORTED_FORMATS) ) ) @@ -202,7 +203,7 @@ def make_request(self, service_url, method='get', headers=None, data=None, def get_markets(self, market): """ Get the list of markets - http://business.skyscanner.net/portal/en-GB/Documentation/Markets + https://business.skyscanner.net/portal/en-GB/Documentation/Markets """ url = "{url}/{market}".format(url=self.MARKET_SERVICE_URL, market=market) @@ -213,13 +214,13 @@ def location_autosuggest(self, **params): Location Autosuggest Services Doc URLs: - http://business.skyscanner.net/portal/en-GB/ + https://business.skyscanner.net/portal/en-GB/ Documentation/Autosuggest - http://business.skyscanner.net/portal/en-GB/ + https://business.skyscanner.net/portal/en-GB/ Documentation/CarHireAutoSuggest - http://business.skyscanner.net/portal/en-GB/ + https://business.skyscanner.net/portal/en-GB/ Documentation/HotelsAutoSuggest Format: @@ -424,7 +425,7 @@ class Flights(Transport): Flights Live Pricing Please see: - http://business.skyscanner.net/portal/en-GB/ + https://business.skyscanner.net/portal/en-GB/ Documentation/FlightsLivePricingList """ @@ -465,7 +466,7 @@ class FlightsCache(Flights): Flights Browse Cache Please see: - http://business.skyscanner.net/portal/en-GB/ + https://business.skyscanner.net/portal/en-GB/ Documentation/FlightsBrowseCacheOverview """ diff --git a/tests/test_skyscanner.py b/tests/test_skyscanner.py index d67e19c..e61a811 100755 --- a/tests/test_skyscanner.py +++ b/tests/test_skyscanner.py @@ -343,8 +343,8 @@ def test_default_resp_callback_xml(self): t._default_resp_callback, FakeResponse(content='invalid XML') ) - except: - self.assertRaises(Exception, + except Exception as e: + self.assertRaises(e, t._default_resp_callback, FakeResponse(content='invalid XML') ) @@ -411,7 +411,7 @@ def test_location_autosuggest_xml(self): def test_get_result_json(self): """ - http://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ + https://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ {pickupdatetime}/{dropoffdatetime}/{driverage} ?apiKey={apiKey}&userip={userip} @@ -436,7 +436,7 @@ def test_get_result_json(self): # def test_get_result_xml(self): # """ - # http://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ + # https://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ # {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ # {pickupdatetime}/{dropoffdatetime}/{driverage} # ?apiKey={apiKey}&userip={userip} @@ -461,7 +461,7 @@ def test_get_result_json(self): def test_create_session(self): """ - http://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ + https://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ {market}/{currency}/{locale}/{pickupplace}/ {dropoffplace}/{pickupdatetime}/{dropoffdatetime}/{driverage} ?apiKey={apiKey}&userip={userip} @@ -519,76 +519,78 @@ def test_location_autosuggest_xml(self): self.assertTrue( len(self.result.findall('./Results/HotelResultDto')) > 0) - def test_get_result_json(self): - """ - http://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ - {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ - {pickupdatetime}/{dropoffdatetime}/{driverage} - ?apiKey={apiKey}&userip={userip} - - YYYY-MM-DDThh:mm - """ - - hotels_service = Hotels(self.api_key, response_format='json') - self.result = hotels_service.get_result( - market='UK', - currency='GBP', - locale='en-GB', - entityid=27543923, - checkindate=self.checkin, - checkoutdate=self.checkout, - guests=1, - rooms=1).parsed - - self.assertTrue('hotels' in self.result) - self.assertTrue(len(self.result['hotels']) > 0) - - def test_get_result_xml(self): - """ - http://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ - {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ - {pickupdatetime}/{dropoffdatetime}/{driverage} - ?apiKey={apiKey}&userip={userip} - - YYYY-MM-DDThh:mm - """ - - hotels_service = Hotels(self.api_key, response_format='xml') - self.result = hotels_service.get_result( - market='DE', - currency='EUR', - locale='de-DE', - entityid=27543923, - checkindate=self.checkin, - checkoutdate=self.checkout, - guests=1, - rooms=1).parsed - - self.assertTrue(self.result.find('./Hotels') is not None) - self.assertTrue(len(self.result.findall('./Hotels/HotelDto')) > 0) - - def test_create_session(self): - """ - http://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ - {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ - {pickupdatetime}/{dropoffdatetime}/{driverage} - ?apiKey={apiKey}&userip={userip} - - YYYY-MM-DDThh:mm - """ - hotels_service = Hotels(self.api_key) - - poll_url = hotels_service.create_session( - market='UK', - currency='GBP', - locale='en-GB', - entityid=27543923, - checkindate=self.checkin, - checkoutdate=self.checkout, - guests=1, - rooms=1) - - self.assertTrue(poll_url) + # Tests below are ignored due to deprecated hotel endpoints + # + # def test_get_result_json(self): + # """ + # https://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ + # {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ + # {pickupdatetime}/{dropoffdatetime}/{driverage} + # ?apiKey={apiKey}&userip={userip} + # + # YYYY-MM-DDThh:mm + # """ + # + # hotels_service = Hotels(self.api_key, response_format='json') + # self.result = hotels_service.get_result( + # market='UK', + # currency='GBP', + # locale='en-GB', + # entityid=27543923, + # checkindate=self.checkin, + # checkoutdate=self.checkout, + # guests=1, + # rooms=1).parsed + # + # self.assertTrue('hotels' in self.result) + # self.assertTrue(len(self.result['hotels']) > 0) + # + # def test_get_result_xml(self): + # """ + # https://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ + # {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ + # {pickupdatetime}/{dropoffdatetime}/{driverage} + # ?apiKey={apiKey}&userip={userip} + # + # YYYY-MM-DDThh:mm + # """ + # + # hotels_service = Hotels(self.api_key, response_format='xml') + # self.result = hotels_service.get_result( + # market='DE', + # currency='EUR', + # locale='de-DE', + # entityid=27543923, + # checkindate=self.checkin, + # checkoutdate=self.checkout, + # guests=1, + # rooms=1).parsed + # + # self.assertTrue(self.result.find('./Hotels') is not None) + # self.assertTrue(len(self.result.findall('./Hotels/HotelDto')) > 0) + # + # def test_create_session(self): + # """ + # https://partners.api.skyscanner.net/apiservices/carhire/liveprices/v2/ + # {market}/{currency}/{locale}/{pickupplace}/{dropoffplace}/ + # {pickupdatetime}/{dropoffdatetime}/{driverage} + # ?apiKey={apiKey}&userip={userip} + # + # YYYY-MM-DDThh:mm + # """ + # hotels_service = Hotels(self.api_key) + # + # poll_url = hotels_service.create_session( + # market='UK', + # currency='GBP', + # locale='en-GB', + # entityid=27543923, + # checkindate=self.checkin, + # checkoutdate=self.checkout, + # guests=1, + # rooms=1) + # + # self.assertTrue(poll_url) class TestFlights(SkyScannerTestCase): @@ -838,5 +840,6 @@ def test_get_result_xml(self): self.assertTrue( len(self.result.findall('./Itineraries/ItineraryApiDto')) > 0) + if __name__ == '__main__': unittest.main()