swiftly/cli/put.py
def cli_put_object(context, path):
    """
    Performs a PUT on the object.

    See :py:mod:`swiftly.cli.put` for context usage information.

    See :py:class:`CLIPut` for more information.
    """
    if context.different and context.encrypt:
        raise ReturnCode(
            'context.different will not work properly with context.encrypt '
            'since encryption may change the object size')
    put_headers = dict(context.headers)
    if context.empty:
        body = ''
        put_headers['content-length'] = '0'
    elif not context.input_ or context.input_ == '-':
        stdin = context.io_manager.get_stdin()

        if context.stdin_segmentation:
            def reader():
                while True:
                    chunk = stdin.read(65536)
                    if chunk:
                        yield chunk
                    else:
                        return

            segment_body = FileLikeIter(reader(), context.segment_size)

            prefix = _create_container(context, path, time.time(), 0)
            new_context = context.copy()
            new_context.stdin_segmentation = False
            new_context.stdin = segment_body
            new_context.headers = dict(context.headers)
            segment_n = 0
            path2info = {}
            while not segment_body.is_empty():
                segment_path = _get_segment_path(prefix, segment_n)
                etag = cli_put_object(new_context, segment_path)
                size = segment_body.limit - segment_body.left
                path2info[segment_path] = (size, etag)

                segment_body.reset_limit()
                segment_n += 1
            body = _get_manifest_body(context, prefix, path2info, put_headers)
        else:
            if hasattr(context, 'stdin'):
                body = context.stdin
            else:
                body = stdin
    elif context.seek is not None:
        if context.encrypt:
            raise ReturnCode(
                'putting object %r: Cannot use encryption and context.seek' %
                path)
        body = open(context.input_, 'rb')
        body.seek(context.seek)
    else:
        l_mtime = os.path.getmtime(context.input_)
        l_size = os.path.getsize(context.input_)
        put_headers['content-length'] = str(l_size)
        if context.newer or context.different:
            r_mtime = None
            r_size = None
            with context.client_manager.with_client() as client:
                status, reason, headers, contents = client.head_object(
                    *path.split('/', 1), headers=context.headers,
                    query=context.query, cdn=context.cdn)
                if hasattr(contents, 'read'):
                    contents.read()
            if status // 100 == 2:
                r_mtime = headers.get('x-object-meta-mtime')
                if r_mtime:
                    try:
                        r_mtime = float(r_mtime)
                    except ValueError:
                        r_mtime = None
                r_size = headers.get('content-length')
                if r_size:
                    try:
                        r_size = int(r_size)
                    except ValueError:
                        r_size = None
            elif status != 404:
                raise ReturnCode(
                    'could not head %r for conditional check; skipping put: '
                    '%s %s' % (path, status, reason))
            if context.newer and r_mtime is not None or l_mtime <= r_mtime:
                return
            if context.different and r_mtime is not None and \
                    l_mtime == r_mtime and r_size is not None and \
                    l_size == r_size:
                return
        put_headers['x-object-meta-mtime'] = '%f' % l_mtime
        size = os.path.getsize(context.input_)
        if size > context.segment_size:
            if context.encrypt:
                raise ReturnCode(
                    'putting object %r: Cannot use encryption for objects '
                    'greater than the segment size' % path)
            prefix = _create_container(context, path, l_mtime, size)
            conc = Concurrency(context.concurrency)
            start = 0
            segment = 0
            path2info = {}
            while start < size:
                new_context = context.copy()
                new_context.headers = dict(context.headers)
                new_context.headers['content-length'] = str(min(
                    size - start, context.segment_size))
                new_context.seek = start
                new_path = _get_segment_path(prefix, segment)
                for (ident, (exc_type, exc_value, exc_tb, result)) in \
                        six.iteritems(conc.get_results()):
                    if exc_value:
                        conc.join()
                        raise exc_value
                    path2info[ident] = result
                conc.spawn(
                    new_path, cli_put_object, new_context, new_path)
                segment += 1
                start += context.segment_size
            conc.join()
            for (ident, (exc_type, exc_value, exc_tb, result)) in \
                    six.iteritems(conc.get_results()):
                if exc_value:
                    raise exc_value
                path2info[ident] = result
            body = _get_manifest_body(context, prefix, path2info, put_headers)
        else:
            body = open(context.input_, 'rb')
    with context.client_manager.with_client() as client:
        if context.encrypt:
            content_length = put_headers.get('content-length')
            if content_length:
                content_length = int(content_length)
            if hasattr(body, 'read'):
                body = FileLikeIter(aes_encrypt(
                    context.encrypt, body, preamble=AES256CBC,
                    chunk_size=getattr(client, 'chunk_size', 65536),
                    content_length=content_length))
            else:
                body = FileLikeIter(aes_encrypt(
                    context.encrypt, FileLikeIter([body]), preamble=AES256CBC,
                    chunk_size=getattr(client, 'chunk_size', 65536),
                    content_length=content_length))
            if 'content-length' in put_headers:
                del put_headers['content-length']
        container, obj = path.split('/', 1)
        status, reason, headers, contents = client.put_object(
            container, obj, body, headers=put_headers, query=context.query,
            cdn=context.cdn)
        if hasattr(contents, 'read'):
            contents = contents.read()
    if status // 100 != 2:
        raise ReturnCode(
            'putting object %r: %s %s %r' % (path, status, reason, contents))
    if context.seek is not None:
        content_length = put_headers.get('content-length')
        etag = headers.get('etag')
        if content_length and etag:
            content_length = int(content_length)
        else:
            with context.client_manager.with_client() as client:
                container, obj = path.split('/', 1)
                status, reason, headers, contents = client.head_object(
                    container, obj, cdn=context.cdn)
                if hasattr(contents, 'read'):
                    contents = contents.read()
            if status // 100 != 2:
                raise ReturnCode(
                    'heading object %r: %s %s %r' %
                    (path, status, reason, contents))
            content_length = headers.get('content-length')
            etag = headers.get('etag')
            if content_length:
                content_length = int(content_length)
        return content_length, etag
    if context.stdin is not None:
        return headers.get('etag')
def cli_put_object(context, path):
    """
    Performs a PUT on the object.

    See :py:mod:`swiftly.cli.put` for context usage information.

    See :py:class:`CLIPut` for more information.
    """
    if context.different and context.encrypt:
        raise ReturnCode(
            'context.different will not work properly with context.encrypt '
            'since encryption may change the object size')
    put_headers = dict(context.headers)
    if context.empty:
        body = ''
        put_headers['content-length'] = '0'
    elif not context.input_ or context.input_ == '-':
        stdin = context.io_manager.get_stdin()

        if context.stdin_segmentation:
            def reader():
                while True:
                    chunk = stdin.read(65536)
                    if chunk:
                        yield chunk
                    else:
                        return

            segment_body = FileLikeIter(reader(), context.segment_size)

            prefix = _create_container(context, path, time.time(), 0)
            new_context = context.copy()
            new_context.stdin_segmentation = False
            new_context.stdin = segment_body
            new_context.headers = dict(context.headers)
            segment_n = 0
            path2info = {}
            while not segment_body.is_empty():
                segment_path = _get_segment_path(prefix, segment_n)
                etag = cli_put_object(new_context, segment_path)
                size = segment_body.limit - segment_body.left
                path2info[segment_path] = (size, etag)

                segment_body.reset_limit()
                segment_n += 1
            body = _get_manifest_body(context, prefix, path2info, put_headers)
        else:
            if hasattr(context, 'stdin'):
                body = context.stdin
            else:
                body = stdin
    elif context.seek is not None:
        if context.encrypt:
            raise ReturnCode(
                'putting object %r: Cannot use encryption and context.seek' %
                path)
        body = open(context.input_, 'rb')
        body.seek(context.seek)
    else:
        l_mtime = os.path.getmtime(context.input_)
        l_size = os.path.getsize(context.input_)
        put_headers['content-length'] = str(l_size)
        if context.newer or context.different:
            r_mtime = None
            r_size = None
            with context.client_manager.with_client() as client:
                status, reason, headers, contents = client.head_object(
                    *path.split('/', 1), headers=context.headers,
                    query=context.query, cdn=context.cdn)
                if hasattr(contents, 'read'):
                    contents.read()
            if status // 100 == 2:
                r_mtime = headers.get('x-object-meta-mtime')
                if r_mtime:
                    try:
                        r_mtime = float(r_mtime)
                    except ValueError:
                        r_mtime = None
                r_size = headers.get('content-length')
                if r_size:
                    try:
                        r_size = int(r_size)
                    except ValueError:
                        r_size = None
            elif status != 404:
                raise ReturnCode(
                    'could not head %r for conditional check; skipping put: '
                    '%s %s' % (path, status, reason))
            if context.newer and r_mtime is not None or l_mtime <= r_mtime:
                return
            if context.different and r_mtime is not None and \
                    l_mtime == r_mtime and r_size is not None and \
                    l_size == r_size:
                return
        put_headers['x-object-meta-mtime'] = '%f' % l_mtime
        size = os.path.getsize(context.input_)
        if size > context.segment_size:
            if context.encrypt:
                raise ReturnCode(
                    'putting object %r: Cannot use encryption for objects '
                    'greater than the segment size' % path)
            prefix = _create_container(context, path, l_mtime, size)
            conc = Concurrency(context.concurrency)
            start = 0
            segment = 0
            path2info = {}
            while start < size:
                new_context = context.copy()
                new_context.headers = dict(context.headers)
                new_context.headers['content-length'] = str(min(
                    size - start, context.segment_size))
                new_context.seek = start
                new_path = _get_segment_path(prefix, segment)
                for (ident, (exc_type, exc_value, exc_tb, result)) in \
                        six.iteritems(conc.get_results()):
                    if exc_value:
                        conc.join()
                        raise exc_value
                    path2info[ident] = result
                conc.spawn(
                    new_path, cli_put_object, new_context, new_path)
                segment += 1
                start += context.segment_size
            conc.join()
            for (ident, (exc_type, exc_value, exc_tb, result)) in \
                    six.iteritems(conc.get_results()):
                if exc_value:
                    raise exc_value
                path2info[ident] = result
            body = _get_manifest_body(context, prefix, path2info, put_headers)
        else:
            body = open(context.input_, 'rb')
    with context.client_manager.with_client() as client:
        if context.encrypt:
            content_length = put_headers.get('content-length')
            if content_length:
                content_length = int(content_length)
            if hasattr(body, 'read'):
                body = FileLikeIter(aes_encrypt(
                    context.encrypt, body, preamble=AES256CBC,
                    chunk_size=getattr(client, 'chunk_size', 65536),
                    content_length=content_length))
            else:
                body = FileLikeIter(aes_encrypt(
                    context.encrypt, FileLikeIter([body]), preamble=AES256CBC,
                    chunk_size=getattr(client, 'chunk_size', 65536),
                    content_length=content_length))
            if 'content-length' in put_headers:
                del put_headers['content-length']
        container, obj = path.split('/', 1)
        status, reason, headers, contents = client.put_object(
            container, obj, body, headers=put_headers, query=context.query,
            cdn=context.cdn)
        if hasattr(contents, 'read'):
            contents = contents.read()
    if status // 100 != 2:
        raise ReturnCode(
            'putting object %r: %s %s %r' % (path, status, reason, contents))
    if context.seek is not None:
        content_length = put_headers.get('content-length')
        etag = headers.get('etag')
        if content_length and etag:
            content_length = int(content_length)
        else:
            with context.client_manager.with_client() as client:
                container, obj = path.split('/', 1)
                status, reason, headers, contents = client.head_object(
                    container, obj, cdn=context.cdn)
                if hasattr(contents, 'read'):
                    contents = contents.read()
            if status // 100 != 2:
                raise ReturnCode(
                    'heading object %r: %s %s %r' %
                    (path, status, reason, contents))
            content_length = headers.get('content-length')
            etag = headers.get('etag')
            if content_length:
                content_length = int(content_length)
        return content_length, etag
    if context.stdin is not None:
        return headers.get('etag')
Performs a PUT on the object.

    See :py:mod:`swiftly.cli.put` for context usage information.

    See :py:class:`CLIPut` for more information.