Esempio n. 1
0
    def send_request(self, requests_callable, *args, **kwargs):
        # this is the timeout of requests module
        requests_timeout = kwargs.pop('timeout', self.requests_timeout)

        files = kwargs.pop('files', None)
        if files:
            for key, f in files.items():
                # file can be a tuple
                # if so, the fileobj is in second position
                if isinstance(f, (tuple, list)):
                    f = f[1]
                if f is None or isinstance(
                        f, (string_types, bytes, bytearray, int, float, bool)):
                    continue
                error = "Unsupported file object type '{}' for key '{}'".format(
                    type(f), key)
                # seek files before each retry, to avoid silently retrying with different input
                if hasattr(f, 'seek'):
                    if hasattr(f, 'seekable') and not f.seekable():
                        raise DeepomaticException(
                            "{}: not seekable".format(error))
                    f.seek(0)
                    continue

                raise DeepomaticException(
                    "{}: not a scalar or seekable.".format(error))

        return requests_callable(*args,
                                 files=files,
                                 timeout=requests_timeout,
                                 verify=self.verify_ssl,
                                 **kwargs)
    def update(self,
               replace=False,
               content_type='application/json',
               files=None,
               **kwargs):
        assert (self._pk is not None)

        if self._helper.check_query_parameters:
            for arg_name in kwargs:
                if arg_name not in self.object_template:
                    raise DeepomaticException("Unexpected keyword argument: " +
                                              arg_name)
            for arg_name, arg in self.object_template.items():
                if not arg._mutable and arg_name in kwargs:
                    raise DeepomaticException("Immutable keyword argument: " +
                                              arg_name)

        if replace:
            self._data = self._helper.put(self._uri(pk=self._pk),
                                          data=kwargs,
                                          content_type=content_type,
                                          files=files)
        else:
            self._data = self._helper.patch(self._uri(pk=self._pk),
                                            data=kwargs,
                                            content_type=content_type,
                                            files=files)
    def create(self, content_type='application/json', files=None, **kwargs):
        post_kwargs = {}
        # TODO: this is a hack, kwargs shouldn't be the data to post
        # it should be the requests kwargs
        # the fix will be a breaking change, so for now we just pop them
        for key in ['http_retry', 'timeout']:
            try:
                post_kwargs[key] = kwargs.pop(key)
            except KeyError:
                pass

        if self._helper.check_query_parameters:
            for arg_name in kwargs:
                if arg_name not in self.object_template or self.object_template[
                        arg_name]._shoud_be_present_when_adding is False:
                    raise DeepomaticException("Unexpected keyword argument: " +
                                              arg_name)
            for arg_name, arg in self.object_template.items():
                if arg._shoud_be_present_when_adding and arg_name not in kwargs:
                    raise DeepomaticException("Missing keyword argument: " +
                                              arg_name)

        if files is not None:
            content_type = 'multipart/mixed'

        data = self._helper.post(self._uri(),
                                 data=kwargs,
                                 content_type=content_type,
                                 files=files,
                                 **post_kwargs)
        return self.__class__(self._helper, pk=data['id'], data=data)
Esempio n. 4
0
    def _refresh_tasks_status(self, pending_tasks, success_tasks, error_tasks,
                              positions):
        logger.debug("Refreshing batch of Task {}".format(pending_tasks))
        task_ids = [task.pk for idx, task in pending_tasks]
        functor = functools.partial(self.list, task_ids=task_ids)
        refreshed_tasks = warn_on_http_retry_error(
            functor,
            suffix="Retrying until Task.batch_wait timeouts.",
            reraise=True)

        pending_tasks[:] = []  # clear the list (we have to keep the reference)
        for task in refreshed_tasks:
            status = task['status']
            pos = positions[task.pk]

            if is_pending_status(status):
                pending_tasks.append((pos, task))
            elif is_error_status(status):
                error_tasks.append((pos, task))
            elif is_success_status(status):
                success_tasks.append((pos, task))
            else:
                raise DeepomaticException("Unknown task status %s" % status)

        return pending_tasks
    def __init__(self, source, encoding=None):
        is_file = hasattr(source, 'read')
        is_raw = False
        if not is_file:
            is_raw = (
                (sys.version_info >=
                 (3, 0) and isinstance(source, bytes)) or not any(
                     [source.startswith(p) for p in self.supported_protocols]))
            if is_raw:
                if encoding is None:
                    raise DeepomaticException(
                        "You must specify 'encoding' when passing raw data")
                if encoding not in self.supported_encodings:
                    raise DeepomaticException("Unknown 'encoding' type.")

                # Send binary directly to minimize load
                if encoding == 'base64':
                    source = base64.b64decode(source)
                    encoding = 'binary'

        self._source = source
        self._need_multipart = is_file or (is_raw and encoding == 'binary')
Esempio n. 6
0
 def recursive_json_dump(prefix, obj, data_dict, omit_dot=False):
     if isinstance(obj, dict):
         if not omit_dot:  # see comment below
             prefix += '.'
         for key, value in obj.items():
             recursive_json_dump(prefix + key, value, data_dict)
     elif isinstance(obj, list):
         for i, value in enumerate(obj):
             # omit_dot is True as DRF parses list of dictionnaries like this:
             # {"parent": [{"subfield": 0}]} would be:
             # 'parent[0]subfield': 0
             recursive_json_dump(prefix + '[{}]'.format(i),
                                 value,
                                 data_dict,
                                 omit_dot=True)
     else:
         if prefix in data_dict:
             raise DeepomaticException("Duplicate key: " + prefix)
         data_dict[prefix] = obj
Esempio n. 7
0
 def get_base_uri(self, pk, **kwargs):
     raise DeepomaticException('Unimplemented')
Esempio n. 8
0
    def make_request(self,
                     func,
                     resource,
                     params=None,
                     data=None,
                     content_type='application/json',
                     files=None,
                     stream=False,
                     *args,
                     **kwargs):

        if content_type is not None:
            if content_type.strip() == 'application/json':
                if data is not None:
                    data = json.dumps(data)
            elif content_type.strip() == 'multipart/mixed':
                # If no files are provided, requests will default to form-urlencoded content type
                # But the API doesn't support it.
                if not files:
                    raise DeepomaticException(
                        "Cannot send the request as multipart without files provided."
                    )
                # requests will build the good multipart content types with the boundaries
                content_type = None
                data = self.dump_json_for_multipart(data)
                files = self.dump_json_for_multipart(files)
            else:
                raise DeepomaticException("Unsupported Content-Type")

        headers = self.setup_headers(content_type=content_type)
        params = self.format_params(params)

        opened_files = []
        if files is not None:
            new_files = {}
            for key, file in files.items():
                if isinstance(file, string_types):
                    try:
                        # this may raise if file is a string containing bytes
                        if os.path.exists(file):
                            file = open(file, 'rb')
                            opened_files.append(file)
                    except TypeError:
                        pass
                elif hasattr(file, 'seek'):
                    file.seek(0)

                if hasattr(file, 'read') or \
                   isinstance(file, bytes) or \
                   (isinstance(file, tuple) and len(file) == 3):
                    new_files[key] = file
                else:
                    new_files[key] = (None, file, 'application/json')

            files = new_files

        if not resource.startswith('http'):
            resource = self.resource_prefix + resource

        response = self.maybe_retry_send_request(func,
                                                 resource,
                                                 *args,
                                                 params=params,
                                                 data=data,
                                                 files=files,
                                                 headers=headers,
                                                 stream=stream,
                                                 **kwargs)

        # Close opened files
        for file in opened_files:
            file.close()

        status_code = response.status_code
        if status_code == 204:  # delete
            return None

        if status_code < 200 or status_code >= 300:
            if status_code >= 400 and status_code < 500:
                raise ClientError(response)
            elif status_code >= 500 and status_code < 600:
                raise ServerError(response)
            else:
                raise BadStatus(response)

        if stream:
            # we asked for a stream, we let the user download it as he wants or it will load everything in RAM
            # not good for big files
            return response
        elif 'application/json' in response.headers.get('Content-Type', ''):
            return response.json()
        else:
            return response.content