From 28908660801757d2db0f91e619a9007bcfcc8596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 16:18:12 +0100 Subject: [PATCH 01/13] Moving file handling down to base endpoint --- arcsecond/api/endpoints/_base.py | 15 +++++++++++++++ arcsecond/api/main.py | 14 -------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index 8f03f97..e0601c7 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -1,5 +1,6 @@ import threading import uuid +import os import click import requests @@ -14,6 +15,7 @@ API_AUTH_PATH_REGISTER) from arcsecond.api.error import ArcsecondConnectionError, ArcsecondError +from arcsecond.api.helpers import make_file_upload_payload from arcsecond.config import config_file_read_api_key, config_file_read_organisation_memberships from arcsecond.options import State @@ -84,10 +86,22 @@ def _check_organisation_membership_and_permission(self, method_name, organisatio memberships = config_file_read_organisation_memberships(self.state.config_section()) if self.state.organisation not in memberships.keys(): raise ArcsecondError('No membership found for organisation {}'.format(organisation)) + membership = memberships[self.state.organisation] if method_name not in SAFE_METHODS and membership not in WRITABLE_MEMBERSHIPS: raise ArcsecondError('Membership for organisation {} has no write permission'.format(organisation)) + def _check_for_file_in_payload(self, payload): + if isinstance(payload, str) and os.path.exists(payload) and os.path.isfile(payload): + return make_file_upload_payload(payload) # transform a str into a dict + elif isinstance(payload, dict) and 'file' in payload.keys(): + file_value = payload.pop('file') # .pop() not .get() + if file_value and os.path.exists(file_value) and os.path.isfile(file_value): + payload.update(**make_file_upload_payload(file_value)) # unpack the resulting dict of make_file...() + else: + payload.update(file=file_value) # do nothing, it's not a file... + return payload + def _async_perform_request(self, url, method, payload=None, files=None, **headers): def _async_perform_request_store_response(storage, method, url, payload, files, headers): try: @@ -156,6 +170,7 @@ def list(self, name='', **headers): return self._perform_request(self._list_url(name), 'get', None, **headers) def create(self, payload, **headers): + payload = self._check_for_file_in_payload(payload) return self._perform_request(self._list_url(), 'post', payload, **headers) def read(self, id_name_uuid, **headers): diff --git a/arcsecond/api/main.py b/arcsecond/api/main.py index 5d5136a..c16ec86 100644 --- a/arcsecond/api/main.py +++ b/arcsecond/api/main.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import json -import os import pprint import types import webbrowser @@ -21,7 +20,6 @@ from arcsecond.options import State from .auth import AuthAPIEndPoint from .error import ArcsecondInvalidEndpointError, ArcsecondNotLoggedInError, ArcsecondTooManyPrefixesError -from .helpers import make_file_upload_payload from .endpoints import (ActivitiesAPIEndPoint, CataloguesAPIEndPoint, DatasetsAPIEndPoint, ExoplanetsAPIEndPoint, DataFilesAPIEndPoint, FindingChartsAPIEndPoint, InstrumentsAPIEndPoint, NightLogAPIEndPoint, @@ -166,22 +164,10 @@ def _check_endpoint_class(self, endpoint): raise ArcsecondInvalidEndpointError(endpoint, ENDPOINTS) return endpoint - def _check_for_file_in_payload(self, payload): - if isinstance(payload, str) and os.path.exists(payload) and os.path.isfile(payload): - return make_file_upload_payload(payload) # transform a str into a dict - elif isinstance(payload, dict) and 'file' in payload.keys(): - file_value = payload.pop('file') # .pop() not .get() - if file_value and os.path.exists(file_value) and os.path.isfile(file_value): - payload.update(**make_file_upload_payload(file_value)) # unpack the resulting dict of make_file...() - else: - payload.update(file=file_value) # do nothing, it's not a file... - return payload - def list(self, name=None, **headers): return self._echo_response(self.endpoint.list(name, **headers)) def create(self, payload, **headers): - payload = self._check_for_file_in_payload(payload) return self._echo_response(self.endpoint.create(payload, **headers)) def read(self, id_name_uuid, **headers): From 0d46edb00b55b64fc66c9e21d3df8105d9df7183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 16:18:19 +0100 Subject: [PATCH 02/13] Fixed + imrpoved error message --- arcsecond/api/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arcsecond/api/main.py b/arcsecond/api/main.py index c16ec86..67e2cb6 100644 --- a/arcsecond/api/main.py +++ b/arcsecond/api/main.py @@ -29,6 +29,7 @@ pp = pprint.PrettyPrinter(indent=4, depth=5) ECHO_PREFIX = u' • ' +ECHO_ERROR_PREFIX = u' • [error] ' __all__ = ["ArcsecondAPI"] @@ -142,13 +143,15 @@ def _echo_error(cls, state, error): else: json_obj = json.loads(error) if 'detail' in json_obj.keys(): - click.echo(ECHO_PREFIX + json_obj['detail']) + detail_msg = ', '.join(json_obj['detail']) if isinstance(json_obj['detail'], list) else json_obj['detail'] + click.echo(ECHO_ERROR_PREFIX + detail_msg) elif 'error' in json_obj.keys(): - click.echo(ECHO_PREFIX + json_obj['error']) + error_msg = ', '.join(json_obj['error']) if isinstance(json_obj['error'], list) else json_obj['error'] + click.echo(ECHO_ERROR_PREFIX + error_msg) elif 'non_field_errors' in json_obj.keys(): errors = json_obj['non_field_errors'] message = ', '.join(errors) if isinstance(error, list) else str(errors) - click.echo(ECHO_PREFIX + message) + click.echo(ECHO_ERROR_PREFIX + message) else: click.echo(ECHO_PREFIX + str(error)) From 4a6786386f9db38a703218fa412ef02148f52744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 17:05:15 +0100 Subject: [PATCH 03/13] Better doc --- arcsecond/cli.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arcsecond/cli.py b/arcsecond/cli.py index 772d213..140a038 100644 --- a/arcsecond/cli.py +++ b/arcsecond/cli.py @@ -149,7 +149,7 @@ def activities(state, method, pk, **kwargs): api = Arcsecond.create_activities_api(state) if method == 'create': kwargs.update(coordinates=make_coords_dict(kwargs)) - api.create(kwargs) + api.create(kwargs) # the kwargs dict is the payload! elif method == 'read': api.read(pk) # will handle list if pk is None elif method == 'update': @@ -169,7 +169,7 @@ def activities(state, method, pk, **kwargs): def datasets(state, method, uuid, **kwargs): api = Arcsecond.create_datasets_api(state) if method == 'create': - api.create(kwargs) + api.create(kwargs) # the kwargs dict is the payload! elif method == 'read': api.read(uuid) # will handle list if pk is None elif method == 'update': @@ -184,7 +184,8 @@ def datasets(state, method, uuid, **kwargs): @click.argument('dataset', required=True, nargs=1) @click.argument('method', required=False, nargs=1, type=MethodChoiceParamType(), default='read') @click.argument('pk', required=False, nargs=1) -@click.option('--file', required=False, nargs=1, help="The path to the data file to upload. Can be zipped with gzip or bzip2.") +@click.option('--file', required=False, nargs=1, + help="The path to the data file to upload. Can be zipped with gzip or bzip2.") @click.option('--instrument', required=False, nargs=1, help="The UUID of the instrument.") @organisation_options @pass_state @@ -200,7 +201,7 @@ def datafiles(state, dataset, method, pk, **kwargs): kwargs.pop('organisation') api = Arcsecond.create_datafiles_api(state=state, dataset=dataset) if method == 'create': - api.create(kwargs) + api.create(kwargs) # the kwargs dict is the payload! elif method == 'read': api.read(pk) # will handle list if pk is None elif method == 'update': From f30ea0786e8a51f1c3d31617191550abda725f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 17:09:55 +0100 Subject: [PATCH 04/13] Preparing for callback events --- arcsecond/api/endpoints/_base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index e0601c7..1906052 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -22,6 +22,12 @@ SAFE_METHODS = ['GET', 'OPTIONS'] WRITABLE_MEMBERSHIPS = ['superadmin', 'admin', 'member'] +EVENT_METHOD_WILL_START = 'EVENT_METHOD_WILL_START' +EVENT_METHOD_DID_FINISH = 'EVENT_METHOD_DID_FINISH' +EVENT_METHOD_DID_FAIL = 'EVENT_METHOD_DID_FAIL' +EVENT_METHOD_PROGRESS_PERCENT = 'EVENT_METHOD_PROGRESS_PERCENT' + + class APIEndPoint(object): name = None From 221e2786cf674ab9d5ba2ab4e06c296acdc18ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 17:12:08 +0100 Subject: [PATCH 05/13] Spliting the preparation of request from its perfomance --- arcsecond/api/endpoints/_base.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index 1906052..eca1432 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -135,31 +135,37 @@ def _async_perform_request_store_response(storage, method, url, payload, files, return storage.get('response', None) - def _perform_request(self, url, method, payload, **headers): + def _prepare_request(self, url, method, payload, **headers): assert (url and method) if not isinstance(method, str) or callable(method): raise ArcsecondError('Invalid HTTP request method {}. '.format(str(method))) - # Check API key, hence login state. Must do before check for org. - headers = self._check_and_set_api_key(headers, url) - # Put method name aside in its own var. method_name = method.upper() if isinstance(method, str) else '' - method = getattr(requests, method.lower()) if isinstance(method, str) else method - files = payload.pop('files', None) if payload else None if self.state and self.state.organisation: self._check_organisation_membership_and_permission(method_name, self.state.organisation) + # Check API key, hence login state. Must do before check for org. + headers = self._check_and_set_api_key(headers, url) + method = getattr(requests, method.lower()) if isinstance(method, str) else method + if payload: + # Filtering None values out of payload. payload = {k: v for k, v in payload.items() if v is not None} + return url, method_name, method, payload, headers + + def _perform_request(self, url, method, payload, callback=None, **headers): + url, method_name, method, payload, headers = self._prepare_request(url, method, payload, **headers) + if self.state.verbose: click.echo('Sending {} request to {}'.format(method_name, url)) click.echo('Payload: {}'.format(payload)) - response = self._async_perform_request(url, method, payload, files, **headers) + payload, files = self._extract_files_from_payload(payload) + response = method(url, json=payload, files=files, headers=headers) if response is None: raise ArcsecondConnectionError(url) From c15c4f3c1fa1147035f9f4ba23c9591c869ef44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 17:18:13 +0100 Subject: [PATCH 06/13] Using correct method for extracting file --- arcsecond/api/endpoints/_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index eca1432..b1f835e 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -28,7 +28,6 @@ EVENT_METHOD_PROGRESS_PERCENT = 'EVENT_METHOD_PROGRESS_PERCENT' - class APIEndPoint(object): name = None @@ -164,7 +163,8 @@ def _perform_request(self, url, method, payload, callback=None, **headers): click.echo('Sending {} request to {}'.format(method_name, url)) click.echo('Payload: {}'.format(payload)) - payload, files = self._extract_files_from_payload(payload) + payload = self._check_for_file_in_payload(payload) + files = payload.pop('files', None) if payload else None response = method(url, json=payload, files=files, headers=headers) if response is None: From 530c1e3f2156318ca9a6e1c3dfc5d90b573b11fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 17:25:31 +0100 Subject: [PATCH 07/13] Distinguishing upload requests from the other --- arcsecond/api/endpoints/_base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index b1f835e..d18fd22 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -99,12 +99,14 @@ def _check_organisation_membership_and_permission(self, method_name, organisatio def _check_for_file_in_payload(self, payload): if isinstance(payload, str) and os.path.exists(payload) and os.path.isfile(payload): return make_file_upload_payload(payload) # transform a str into a dict + elif isinstance(payload, dict) and 'file' in payload.keys(): file_value = payload.pop('file') # .pop() not .get() if file_value and os.path.exists(file_value) and os.path.isfile(file_value): payload.update(**make_file_upload_payload(file_value)) # unpack the resulting dict of make_file...() else: payload.update(file=file_value) # do nothing, it's not a file... + return payload def _async_perform_request(self, url, method, payload=None, files=None, **headers): @@ -165,7 +167,10 @@ def _perform_request(self, url, method, payload, callback=None, **headers): payload = self._check_for_file_in_payload(payload) files = payload.pop('files', None) if payload else None - response = method(url, json=payload, files=files, headers=headers) + if files: + response = method(url, json=payload, files=files, headers=headers) + else: + response = self._async_perform_request(url, method, payload, **headers) if response is None: raise ArcsecondConnectionError(url) From 8746217b3b2f0744c39310acd9eb6699e3790e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 17:50:39 +0100 Subject: [PATCH 08/13] One need this to monitor upload --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b196cdd..5306b48 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ 'six', 'click', 'requests', + 'requests_toolbelt', 'pygments', 'configparser', 'progress' From b38f03fee09f639bdfe763e99d829ba97fd476e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 18:51:21 +0100 Subject: [PATCH 09/13] Making a progressbar for file upload! --- arcsecond/api/endpoints/_base.py | 56 +++++++++++++++++--------------- arcsecond/api/helpers.py | 19 +++++++++-- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index d18fd22..934e9ca 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -1,10 +1,12 @@ import threading import uuid -import os import click import requests +from requests_toolbelt.multipart import encoder + from progress.spinner import Spinner +from progress.bar import Bar from arcsecond.api.constants import ( ARCSECOND_API_URL_DEV, @@ -15,7 +17,7 @@ API_AUTH_PATH_REGISTER) from arcsecond.api.error import ArcsecondConnectionError, ArcsecondError -from arcsecond.api.helpers import make_file_upload_payload +from arcsecond.api.helpers import transform_payload_for_multipart_encoder_fields from arcsecond.config import config_file_read_api_key, config_file_read_organisation_memberships from arcsecond.options import State @@ -96,23 +98,10 @@ def _check_organisation_membership_and_permission(self, method_name, organisatio if method_name not in SAFE_METHODS and membership not in WRITABLE_MEMBERSHIPS: raise ArcsecondError('Membership for organisation {} has no write permission'.format(organisation)) - def _check_for_file_in_payload(self, payload): - if isinstance(payload, str) and os.path.exists(payload) and os.path.isfile(payload): - return make_file_upload_payload(payload) # transform a str into a dict - - elif isinstance(payload, dict) and 'file' in payload.keys(): - file_value = payload.pop('file') # .pop() not .get() - if file_value and os.path.exists(file_value) and os.path.isfile(file_value): - payload.update(**make_file_upload_payload(file_value)) # unpack the resulting dict of make_file...() - else: - payload.update(file=file_value) # do nothing, it's not a file... - - return payload - - def _async_perform_request(self, url, method, payload=None, files=None, **headers): - def _async_perform_request_store_response(storage, method, url, payload, files, headers): + def _async_perform_request(self, url, method, payload=None, **headers): + def _async_perform_request_store_response(storage, method, url, payload, headers): try: - storage['response'] = method(url, json=payload, files=files, headers=headers) + storage['response'] = method(url, json=payload, headers=headers) except requests.exceptions.ConnectionError: storage['error'] = ArcsecondConnectionError(self._get_base_url()) except Exception as e: @@ -120,7 +109,7 @@ def _async_perform_request_store_response(storage, method, url, payload, files, storage = {} thread = threading.Thread(target=_async_perform_request_store_response, - args=(storage, method, url, payload, files, headers)) + args=(storage, method, url, payload, headers)) thread.start() spinner = Spinner() @@ -158,18 +147,34 @@ def _prepare_request(self, url, method, payload, **headers): return url, method_name, method, payload, headers - def _perform_request(self, url, method, payload, callback=None, **headers): + def _perform_request(self, url, method, payload, **headers): + if self.state.verbose: + click.echo('Preparing request...') + url, method_name, method, payload, headers = self._prepare_request(url, method, payload, **headers) if self.state.verbose: click.echo('Sending {} request to {}'.format(method_name, url)) - click.echo('Payload: {}'.format(payload)) - payload = self._check_for_file_in_payload(payload) - files = payload.pop('files', None) if payload else None - if files: - response = method(url, json=payload, files=files, headers=headers) + payload, fields = transform_payload_for_multipart_encoder_fields(payload) + if fields: + encoded_data = encoder.MultipartEncoder(fields=fields) + bar, bar_callback = None, None + + if self.state.verbose: + bar = Bar('Uploading ' + fields['file'][0], suffix='%(percent)d%%') + bar_callback = lambda m: bar.goto(m.bytes_read / m.len * 100) + + upload_monitor = encoder.MultipartEncoderMonitor(encoded_data, bar_callback) + headers.update(**{'Content-Type': upload_monitor.content_type}) + response = method(url, data=upload_monitor, headers=headers) + + if self.state.verbose: + bar.finish() else: + if self.state.verbose: + click.echo('Payload: {}'.format(payload)) + response = self._async_perform_request(url, method, payload, **headers) if response is None: @@ -187,7 +192,6 @@ def list(self, name='', **headers): return self._perform_request(self._list_url(name), 'get', None, **headers) def create(self, payload, **headers): - payload = self._check_for_file_in_payload(payload) return self._perform_request(self._list_url(), 'post', payload, **headers) def read(self, id_name_uuid, **headers): diff --git a/arcsecond/api/helpers.py b/arcsecond/api/helpers.py index 7de00be..1905d2d 100644 --- a/arcsecond/api/helpers.py +++ b/arcsecond/api/helpers.py @@ -3,8 +3,23 @@ from .error import ArcsecondInputValueError -def make_file_upload_payload(filepath): - return {'files': {'file': open(os.path.abspath(filepath), 'rb')}} +def make_file_upload_multipart_dict(filepath): + return {'fields': {'file': (os.path.basename(filepath), open(os.path.abspath(filepath), 'rb'))}} + + +def transform_payload_for_multipart_encoder_fields(payload): + if isinstance(payload, str) and os.path.exists(payload) and os.path.isfile(payload): + payload = make_file_upload_multipart_dict(payload) # transform a str into a dict + + elif isinstance(payload, dict) and 'file' in payload.keys(): + file_value = payload.pop('file') # .pop() not .get() + if file_value and os.path.exists(file_value) and os.path.isfile(file_value): + payload.update(**make_file_upload_multipart_dict(file_value)) # unpack the resulting dict of make_file...() + else: + payload.update(file=file_value) # do nothing, it's not a file... + + fields = payload.pop('fields', None) if payload else None + return payload, fields def make_coords_dict(kwargs): From 9b331b298b4e3e0276ea12686923463b6d27d026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Sat, 11 Jan 2020 22:29:19 +0100 Subject: [PATCH 10/13] Introducing event and callback for python module --- arcsecond/api/endpoints/_base.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index 934e9ca..9e1ad7c 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -147,7 +147,7 @@ def _prepare_request(self, url, method, payload, **headers): return url, method_name, method, payload, headers - def _perform_request(self, url, method, payload, **headers): + def _perform_request(self, url, method, payload, callback, **headers): if self.state.verbose: click.echo('Preparing request...') @@ -159,13 +159,15 @@ def _perform_request(self, url, method, payload, **headers): payload, fields = transform_payload_for_multipart_encoder_fields(payload) if fields: encoded_data = encoder.MultipartEncoder(fields=fields) - bar, bar_callback = None, None + bar, upload_callback = None, None - if self.state.verbose: + if self.state.is_using_cli is False and callback: + upload_callback = lambda m: callback(EVENT_METHOD_PROGRESS_PERCENT, m.bytes_read / m.len * 100) + elif self.state.verbose: bar = Bar('Uploading ' + fields['file'][0], suffix='%(percent)d%%') - bar_callback = lambda m: bar.goto(m.bytes_read / m.len * 100) + upload_callback = lambda m: bar.goto(m.bytes_read / m.len * 100) - upload_monitor = encoder.MultipartEncoderMonitor(encoded_data, bar_callback) + upload_monitor = encoder.MultipartEncoderMonitor(encoded_data, upload_callback) headers.update(**{'Content-Type': upload_monitor.content_type}) response = method(url, data=upload_monitor, headers=headers) @@ -189,16 +191,16 @@ def _perform_request(self, url, method, payload, **headers): return None, response.text def list(self, name='', **headers): - return self._perform_request(self._list_url(name), 'get', None, **headers) + return self._perform_request(self._list_url(name), 'get', None, None, **headers) - def create(self, payload, **headers): - return self._perform_request(self._list_url(), 'post', payload, **headers) + def create(self, payload, callback=None, **headers): + return self._perform_request(self._list_url(), 'post', payload, callback, **headers) def read(self, id_name_uuid, **headers): - return self._perform_request(self._detail_url(id_name_uuid), 'get', None, **headers) + return self._perform_request(self._detail_url(id_name_uuid), 'get', None, None, **headers) def update(self, id_name_uuid, payload, **headers): - return self._perform_request(self._detail_url(id_name_uuid), 'put', payload, **headers) + return self._perform_request(self._detail_url(id_name_uuid), 'put', payload, None, **headers) def delete(self, id_name_uuid, **headers): - return self._perform_request(self._detail_url(id_name_uuid), 'delete', None, **headers) + return self._perform_request(self._detail_url(id_name_uuid), 'delete', None, None, **headers) From 79aa76c5e02e06b1a68d149d7e06dca6fce0ec1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Mon, 13 Jan 2020 22:35:54 +0100 Subject: [PATCH 11/13] Making state argument optional --- arcsecond/api/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcsecond/api/main.py b/arcsecond/api/main.py index 67e2cb6..77052eb 100644 --- a/arcsecond/api/main.py +++ b/arcsecond/api/main.py @@ -69,7 +69,7 @@ def get_api_state(state=None, **kwargs): def set_api_factory(cls): - def factory(endpoint_class, state, **kwargs): + def factory(endpoint_class, state=None, **kwargs): return ArcsecondAPI(endpoint_class, state, **kwargs) for endpoint_class in ENDPOINTS: From 95fda9b5c10e06702e18bbc92aba705cda551b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Mon, 13 Jan 2020 22:36:32 +0100 Subject: [PATCH 12/13] Bumping version --- arcsecond/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcsecond/__init__.py b/arcsecond/__init__.py index 219fb48..0d5965b 100644 --- a/arcsecond/__init__.py +++ b/arcsecond/__init__.py @@ -7,4 +7,4 @@ "ArcsecondConnectionError", "ArcsecondInvalidEndpointError"] -__version__ = '0.7.3' +__version__ = '0.7.4' From 5fd48aa04b007f941d1fc4991485fb2c322db76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Foellmi?= Date: Mon, 13 Jan 2020 22:39:36 +0100 Subject: [PATCH 13/13] Making all tests pass --- arcsecond/api/endpoints/_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcsecond/api/endpoints/_base.py b/arcsecond/api/endpoints/_base.py index 9e1ad7c..3e75700 100644 --- a/arcsecond/api/endpoints/_base.py +++ b/arcsecond/api/endpoints/_base.py @@ -147,7 +147,7 @@ def _prepare_request(self, url, method, payload, **headers): return url, method_name, method, payload, headers - def _perform_request(self, url, method, payload, callback, **headers): + def _perform_request(self, url, method, payload, callback=None, **headers): if self.state.verbose: click.echo('Preparing request...')