Revision ca092af4

b/kamaki/cli/argument.py
44 44
try:
45 45
    from progress.bar import ShadyBar as KamakiProgressBar
46 46
except ImportError:
47
    try:
48
        from progress.bar import Bar as KamakiProgressBar
49
    except ImportError:
50
        pass
47 51
    # progress not installed - pls, pip install progress
48 52
    pass
49 53

  
b/kamaki/cli/commands/errors.py
37 37
from kamaki.clients import ClientError
38 38
from kamaki.cli.errors import CLIError, raiseCLIError, CLISyntaxError
39 39
from kamaki.cli import _debug, kloger
40
from kamaki.cli.utils import format_size
40 41

  
41 42
sendlog = logging.getLogger('clients.send')
42 43
datasendlog = logging.getLogger('data.send')
......
398 399
    '  1. Set store.container variable (permanent)',
399 400
    '     /config set store.container <container>',
400 401
    '  2. --container=<container> (temporary, overrides 1)',
401
    '  3. Use the container:path format (temporary, overrides all)']
402
    '  3. Use the container:path format (temporary, overrides all)',
403
    'For a list of containers: /store list']
402 404

  
403 405
    @classmethod
404 406
    def connection(this, foo):
......
429 431
                return foo(self, *args, **kwargs)
430 432
            except ClientError as ce:
431 433
                if ce.status == 404 and 'container' in ('%s' % ce).lower():
432
                        cont = '%s or %s' if dst_cont else self.container
434
                        cont = '%s or %s' % (self.container, dst_cont)\
435
                        if dst_cont else self.container
433 436
                        raiseCLIError(ce,
434
                            'No container %s in account %s' % (
437
                            'Is container %s in account %s ?' % (
435 438
                                cont,
436 439
                                self.account),
437 440
                            details=this.container_howto)
......
452 455
                        details=this.container_howto)
453 456
                raise
454 457
        return _raise
458

  
459
    @classmethod
460
    def object_size(this, foo):
461
        def _raise(self, *args, **kwargs):
462
            size = kwargs.get('size', None)
463
            start = kwargs.get('start', 0)
464
            end = kwargs.get('end', 0)
465
            if size:
466
                try:
467
                    size = int(size)
468
                except ValueError as ve:
469
                    raiseCLIError(ve,
470
                        'Invalid file size %s ' % size,
471
                        details=['size must be a positive integer'],
472
                        importance=1)
473
            else:
474
                try:
475
                    start = int(start)
476
                except ValueError as e:
477
                    raiseCLIError(e,
478
                        'Invalid start value %s in range' % start,
479
                        details=['size must be a positive integer'],
480
                        importance=1)
481
                try:
482
                    end = int(end)
483
                except ValueError as e:
484
                    raiseCLIError(e,
485
                        'Invalid end value %s in range' % end,
486
                        details=['size must be a positive integer'],
487
                        importance=1)
488
                if start > end:
489
                    raiseCLIError(
490
                        'Invalid range %s-%s' % (start, end),
491
                        details=['size must be a positive integer'],
492
                        importance=1)
493
                size = end - start
494
            try:
495
                return foo(self, *args, **kwargs)
496
            except ClientError as ce:
497
                err_msg = ('%s' % ce).lower()
498
                if size and (ce.status == 416 or
499
                (ce.status == 400 and\
500
                    'object length is smaller than range length' in err_msg)):
501
                    raiseCLIError(ce,
502
                        'Remote object %s:%s <= %s %s' % (
503
                            self.container,
504
                            self.path,
505
                            format_size(size),
506
                            ('(%sB)' % size) if size >= 1024 else ''))
507
                raise
508
        return _raise
b/kamaki/cli/commands/pithos_cli.py
696 696
            default=False)
697 697
    )
698 698

  
699
    def main(self, local_path, container___path):
700
        super(self.__class__, self).main(
701
            container___path,
702
            path_is_optional=False)
703
        progress_bar = self.arguments['progress_bar']
704
        try:
705
            upload_cb = progress_bar.get_generator('Appending blocks')
706
        except Exception:
707
            upload_cb = None
699
    @errors.generic.all
700
    @errors.pithos.connection
701
    @errors.pithos.container
702
    @errors.pithos.object_path
703
    def _run(self, local_path):
704
        (progress_bar, upload_cb) = self._safe_progress_bar('Appending')
708 705
        try:
709 706
            f = open(local_path, 'rb')
710 707
            self.client.append_object(self.path, f, upload_cb)
711
        except ClientError as err:
712
            progress_bar.finish()
713
            if err.status == 404:
714
                if 'container' in ('%s' % err).lower():
715
                    raiseCLIError(
716
                        err,
717
                        'No container %s in account %s'\
718
                        % (self.container, self.account),
719
                        details=errors.pithos.container_howto)
720
                elif 'object' in ('%s' % err).lower():
721
                    raiseCLIError(
722
                        err,
723
                        'No object %s in container %s'\
724
                        % (self.path, self.container),
725
                        details=errors.pithos.container_howto)
726
            raise_connection_errors(err)
727
            raiseCLIError(err)
728
        except Exception as e:
729
            progress_bar.finish()
730
            raiseCLIError(e)
708
        except Exception:
709
            self._safe_progress_bar_finish(progress_bar)
710
            raise
731 711
        finally:
732
            progress_bar.finish()
712
            self._safe_progress_bar_finish(progress_bar)
713

  
714
    def main(self, local_path, container___path):
715
        super(self.__class__, self)._run(
716
            container___path,
717
            path_is_optional=False)
718
        self._run(local_path)
733 719

  
734 720

  
735 721
@command(pithos_cmds)
736 722
class store_truncate(_store_container_command):
737 723
    """Truncate remote file up to a size (default is 0)"""
738 724

  
725
    @errors.generic.all
726
    @errors.pithos.connection
727
    @errors.pithos.container
728
    @errors.pithos.object_path
729
    @errors.pithos.object_size
730
    def _run(self, size=0):
731
        self.client.truncate_object(self.path, size)
732

  
739 733
    def main(self, container___path, size=0):
740
        super(self.__class__, self).main(container___path)
741
        try:
742
            self.client.truncate_object(self.path, size)
743
        except ClientError as err:
744
            if err.status == 404:
745
                if 'container' in ('%s' % err).lower():
746
                    raiseCLIError(
747
                        err,
748
                        'No container %s in account %s'\
749
                        % (self.container, self.account),
750
                        details=errors.pithos.container_howto)
751
                elif 'object' in ('%s' % err).lower():
752
                    raiseCLIError(
753
                        err,
754
                        'No object %s in container %s'\
755
                        % (self.path, self.container),
756
                        details=errors.pithos.container_howto)
757
            if err.status == 400 and\
758
                'object length is smaller than range length'\
759
                in ('%s' % err).lower():
760
                raiseCLIError(err, 'Object %s:%s <= %sb' % (
761
                    self.container,
762
                    self.path,
763
                    size))
764
            raise_connection_errors(err)
765
            raiseCLIError(err)
766
        except Exception as e:
767
            raiseCLIError(e)
734
        super(self.__class__, self)._run(container___path)
735
        self._run(size=size)
768 736

  
769 737

  
770 738
@command(pithos_cmds)
......
784 752
            default=False)
785 753
    )
786 754

  
787
    def main(self, local_path, container____path__, start, end):
788
        (start, end) = check_range(start, end)
789
        super(self.__class__, self).main(container____path__)
790
        try:
791
            f = open(local_path, 'rb')
792
            f.seek(0, 2)
793
            f_size = f.tell()
794
            f.seek(start, 0)
795
        except Exception as e:
796
            raiseCLIError(e)
797
        progress_bar = self.arguments['progress_bar']
798
        try:
799
            upload_cb = progress_bar.get_generator(
800
                'Overwriting %s blocks' % end - start)
801
        except Exception:
802
            upload_cb = None
755
    def _open_file(self, local_path, start):
756
        f = open(path.abspath(local_path), 'rb')
757
        f.seek(0, 2)
758
        f_size = f.tell()
759
        f.seek(start, 0)
760
        return (f, f_size)
761

  
762
    @errors.generic.all
763
    @errors.pithos.connection
764
    @errors.pithos.container
765
    @errors.pithos.object_path
766
    @errors.pithos.object_size
767
    def _run(self, local_path, start, end):
768
        (start, end) = (int(start), int(end))
769
        (f, f_size) = self._open_file(local_path, start)
770
        (progress_bar, upload_cb) = self._safe_progress_bar(
771
            'Overwrite %s bytes' % (end - start))
803 772
        try:
804
            self.path = self.path if self.path else path.basename(local_path)
805 773
            self.client.overwrite_object(
806 774
                obj=self.path,
807 775
                start=start,
808 776
                end=end,
809 777
                source_file=f,
810 778
                upload_cb=upload_cb)
811
        except ClientError as err:
812
            progress_bar.finish()
813
            if (err.status == 400 and\
814
            'content length does not match range' in ('%s' % err).lower()) or\
815
            err.status == 416:
816
                raiseCLIError(err, details=[
817
                    'Content size: %s' % f_size,
818
                    'Range: %s-%s (size: %s)' % (start, end, end - start)])
819
            elif err.status == 404:
820
                if 'container' in ('%s' % err).lower():
821
                    raiseCLIError(
822
                        err,
823
                        'No container %s in account %s'\
824
                        % (self.container, self.account),
825
                        details=errors.pithos.container_howto)
826
                elif 'object' in ('%s' % err).lower():
827
                    raiseCLIError(
828
                        err,
829
                        'No object %s in container %s'\
830
                        % (self.path, self.container),
831
                        details=errors.pithos.container_howto)
832
            raise_connection_errors(err)
833
            raiseCLIError(err)
834
        except Exception as e:
835
            progress_bar.finish()
836
            raiseCLIError(e)
779
        except Exception:
780
            self._safe_progress_bar_finish(progress_bar)
781
            raise
837 782
        finally:
838
            progress_bar.finish()
783
            self._safe_progress_bar_finish(progress_bar)
784

  
785
    def main(self, local_path, container____path__, start, end):
786
        super(self.__class__, self)._run(container____path__)
787
        self.path = self.path if self.path else path.basename(local_path)
788
        self._run(local_path=local_path, start=start, end=end)
839 789

  
840 790

  
841 791
@command(pithos_cmds)
b/kamaki/clients/pithos.py
908 908

  
909 909
        :returns: (dict)
910 910
        """
911
        r = self.object_head(obj, version=version)
912
        return r.headers
911
        try:
912
            r = self.object_head(obj, version=version)
913
            return r.headers
914
        except ClientError as ce:
915
            if ce.status == 404:
916
                raise ClientError('Object not found', status=404)
917
            raise
913 918

  
914 919
    def get_object_meta(self, obj, version=None):
915 920
        """
......
985 990
        filesize = fstat(source_file.fileno()).st_size
986 991
        nblocks = 1 + (filesize - 1) // blocksize
987 992
        offset = 0
988
        if upload_cb is not None:
993
        if upload_cb:
989 994
            upload_gen = upload_cb(nblocks)
995
            upload_gen.next()
990 996
        for i in range(nblocks):
991 997
            block = source_file.read(min(blocksize, filesize - offset))
992 998
            offset += len(block)
......
998 1004
                data=block)
999 1005
            r.release()
1000 1006

  
1001
            if upload_cb is not None:
1007
            if upload_cb:
1002 1008
                upload_gen.next()
1003 1009

  
1004 1010
    def truncate_object(self, obj, upto_bytes):
......
1034 1040
        :param upload_db: progress.bar for uploading
1035 1041
        """
1036 1042

  
1043
        r = self.get_object_info(obj)
1044
        rf_size = int(r['content-length'])
1045
        if rf_size < int(start):
1046
            raise ClientError(
1047
                'Range start exceeds file size',
1048
                status=416)
1049
        elif rf_size < int(end):
1050
            raise ClientError(
1051
                'Range end exceeds file size',
1052
                status=416)
1037 1053
        self._assert_container()
1038 1054
        meta = self.get_container_info()
1039 1055
        blocksize = int(meta['x-container-block-size'])
......
1041 1057
        datasize = int(end) - int(start) + 1
1042 1058
        nblocks = 1 + (datasize - 1) // blocksize
1043 1059
        offset = 0
1044
        if upload_cb is not None:
1060
        if upload_cb:
1045 1061
            upload_gen = upload_cb(nblocks)
1062
            upload_gen.next()
1046 1063
        for i in range(nblocks):
1047 1064
            block = source_file.read(min(blocksize,
1048 1065
                filesize - offset,
1049 1066
                datasize - offset))
1050
            offset += len(block)
1051 1067
            r = self.object_post(obj,
1052 1068
                update=True,
1053 1069
                content_type='application/octet-stream',
1054 1070
                content_length=len(block),
1055
                content_range='bytes %s-%s/*' % (start, end),
1071
                content_range='bytes %s-%s/*' % (
1072
                    start + offset,
1073
                    start + offset + len(block) - 1),
1056 1074
                data=block)
1075
            offset += len(block)
1057 1076
            r.release()
1058 1077

  
1059
            if upload_cb is not None:
1078
            if upload_cb:
1060 1079
                upload_gen.next()
1061 1080

  
1062 1081
    def copy_object(self, src_container, src_object, dst_container,
b/setup.py
34 34
# or implied, of GRNET S.A.
35 35

  
36 36
from setuptools import setup
37
#from sys import version_info
37
from sys import version_info
38 38
import collections
39 39

  
40 40
import kamaki
......
42 42

  
43 43
optional = ['ansicolors',
44 44
            'progress>=1.0.2']
45
requires = ['objpool',
46
            'argparse']
45
requires = ['objpool']
46

  
47
if version_info[:1] == (2, 6):
48
    requires.append('argparse')
47 49

  
48 50
if not hasattr(collections, "OrderedDict"):  # Python 2.6
49 51
    requires.append("ordereddict")

Also available in: Unified diff