-
-
Notifications
You must be signed in to change notification settings - Fork 269
Fix issues #618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issues #618
Changes from all commits
cabdc89
681a7f6
53f4e21
17ea6cb
6d94d63
7bb6d0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,17 @@ | ||
| import io | ||
| import os | ||
| import time | ||
| import requests | ||
| import warnings | ||
|
|
||
| import arff | ||
| import xmltodict | ||
|
|
||
| from . import config | ||
| from .exceptions import (OpenMLServerError, OpenMLServerException, | ||
| OpenMLServerNoResult) | ||
|
|
||
|
|
||
| def _perform_api_call(call, data=None, file_elements=None, | ||
| add_authentication=True): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't you need the authentication flag? GET requests don't require authentication, POST/DELETE requests do.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is handled internally and does not need to be an argument to the openml-api call API. |
||
| def _perform_api_call(call, data=None, file_elements=None): | ||
| """ | ||
| Perform an API call at the OpenML server. | ||
| return self._read_url(url, data=data, filePath=filePath, | ||
| def _read_url(self, url, add_authentication=False, data=None, filePath=None): | ||
|
|
||
| Parameters | ||
| ---------- | ||
|
|
@@ -27,8 +22,6 @@ def _read_url(self, url, add_authentication=False, data=None, filePath=None): | |
| file_elements : dict | ||
| Mapping of {filename: str} of strings which should be uploaded as | ||
| files to the server. | ||
| add_authentication : bool | ||
| Whether to add authentication (api key) to the request. | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -50,12 +43,12 @@ def _read_url(self, url, add_authentication=False, data=None, filePath=None): | |
|
|
||
|
|
||
| def _file_id_to_url(file_id, filename=None): | ||
| ''' | ||
| """ | ||
| Presents the URL how to download a given file id | ||
| filename is optional | ||
| ''' | ||
| """ | ||
| openml_url = config.server.split('/api/') | ||
| url = openml_url[0] + '/data/download/%s' %file_id | ||
| url = openml_url[0] + '/data/download/%s' % file_id | ||
| if filename is not None: | ||
| url += '/' + filename | ||
| return url | ||
|
|
@@ -71,7 +64,12 @@ def _read_url_files(url, data=None, file_elements=None): | |
| file_elements = {} | ||
| # Using requests.post sets header 'Accept-encoding' automatically to | ||
| # 'gzip,deflate' | ||
| response = requests.post(url, data=data, files=file_elements) | ||
| response = send_request( | ||
| request_method='post', | ||
| url=url, | ||
| data=data, | ||
| files=file_elements, | ||
| ) | ||
| if response.status_code != 200: | ||
| raise _parse_server_exception(response, url=url) | ||
| if 'Content-Encoding' not in response.headers or \ | ||
|
|
@@ -87,12 +85,16 @@ def _read_url(url, data=None): | |
| data['api_key'] = config.apikey | ||
|
|
||
| if len(data) == 0 or (len(data) == 1 and 'api_key' in data): | ||
| # do a GET | ||
| response = requests.get(url, params=data) | ||
| else: # an actual post request | ||
| response = send_request( | ||
| request_method='get', url=url, data=data, | ||
| ) | ||
|
|
||
| else: | ||
| # Using requests.post sets header 'Accept-encoding' automatically to | ||
| # 'gzip,deflate' | ||
| response = requests.post(url, data=data) | ||
| response = send_request( | ||
| request_method='post', url=url, data=data, | ||
| ) | ||
|
|
||
| if response.status_code != 200: | ||
| raise _parse_server_exception(response, url=url) | ||
|
|
@@ -102,12 +104,44 @@ def _read_url(url, data=None): | |
| return response.text | ||
|
|
||
|
|
||
| def send_request( | ||
| request_method, | ||
| url, | ||
| data, | ||
| files=None, | ||
| ): | ||
| n_retries = config.connection_n_retries | ||
| response = None | ||
| with requests.Session() as session: | ||
| # Start at one to have a non-zero multiplier for the sleep | ||
| for i in range(1, n_retries + 1): | ||
| try: | ||
| if request_method == 'get': | ||
| response = session.get(url, params=data) | ||
| elif request_method == 'post': | ||
| response = session.post(url, data=data, files=files) | ||
| else: | ||
| raise NotImplementedError() | ||
| break | ||
| except ( | ||
| requests.exceptions.ConnectionError, | ||
| requests.exceptions.SSLError, | ||
| ) as e: | ||
| if i == n_retries: | ||
| raise e | ||
| else: | ||
| time.sleep(0.1 * i) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice |
||
| if response is None: | ||
| raise ValueError('This should never happen!') | ||
| return response | ||
|
|
||
|
|
||
| def _parse_server_exception(response, url=None): | ||
| # OpenML has a sopisticated error system | ||
| # where information about failures is provided. try to parse this | ||
| try: | ||
| server_exception = xmltodict.parse(response.text) | ||
| except: | ||
| except Exception: | ||
| raise OpenMLServerError(('Unexpected server error. Please ' | ||
| 'contact the developers!\nStatus code: ' | ||
| '%d\n' % response.status_code) + response.text) | ||
|
|
@@ -117,7 +151,7 @@ def _parse_server_exception(response, url=None): | |
| additional = None | ||
| if 'oml:additional_information' in server_exception['oml:error']: | ||
| additional = server_exception['oml:error']['oml:additional_information'] | ||
| if code in [372, 512, 500, 482, 542, 674]: # datasets, | ||
| if code in [372, 512, 500, 482, 542, 674]: | ||
| # 512 for runs, 372 for datasets, 500 for flows | ||
| # 482 for tasks, 542 for evaluations, 674 for setups | ||
| return OpenMLServerNoResult(code, message, additional) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,6 +65,10 @@ def setUp(self): | |
| with open(openml.config.config_file, 'w') as fh: | ||
| fh.write('apikey = %s' % openml.config.apikey) | ||
|
|
||
| # Increase the number of retries to avoid spurios server failures | ||
| self.connection_n_retries = openml.config.connection_n_retries | ||
| openml.config.connection_n_retries = 10 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused about the number of retries being stored in the _default dict, the openml.config file and self.connection_n_retries. So, by default it is 2, then when that doesn't work, it is increased to 10 and stored in the openml.config, and then it stops (it doesn't get higher than 10)?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, it's increased to 10 for testing in every case. It is stored in the class to restore the original value afterwards.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that it is unfortunate that the value of 2 is twice in the original file, I just made a note to change it later. |
||
|
|
||
| def tearDown(self): | ||
| os.chdir(self.cwd) | ||
| try: | ||
|
|
@@ -76,6 +80,7 @@ def tearDown(self): | |
| else: | ||
| raise | ||
| openml.config.server = self.production_server | ||
| openml.config.connection_n_retries = self.connection_n_retries | ||
|
|
||
| def _get_sentinel(self, sentinel=None): | ||
| if sentinel is None: | ||
|
|
Uh oh!
There was an error while loading. Please reload this page.