Revision 5655d560

b/Changelog
19 19
    This operation was implemented by accident, due to the symetry between
20 20
    move and copy
21 21
- Add optional output for file methods [#3756, #3732]:
22
    mkdir, touch, create, move, create, copy, move
22
    mkdir, touch, create, move, create, copy, move, append, delete, purge,
23
    info, meta, upload
24
- Transliterate permissions and metadata methods to (get, set delete) groups
23 25

  
24 26
Features:
25 27

  
b/kamaki/cli/commands/pithos.py
505 505
        meta=KeyValueArgument(
506 506
            'set container metadata (can be repeated)',
507 507
            '--meta'),
508
        with_output=FlagArgument('show request headers', ('--with-output')),
508
        with_output=FlagArgument('show response headers', ('--with-output')),
509 509
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
510 510
    )
511 511

  
......
514 514
    @errors.pithos.container
515 515
    def _run(self, container):
516 516
        r = self.client.create_container(
517
            container=container,
518
            sizelimit=self['limit'],
519
            versioning=self['versioning'],
520
            metadata=self['meta'])
517
            container=container, sizelimit=self['limit'],
518
            versioning=self['versioning'], metadata=self['meta'])
521 519
        if self['json_output']:
522 520
            print_json(r)
523 521
        elif self['with_output']:
......
728 726
        source_version=ValueArgument(
729 727
            'copy specific version',
730 728
            ('-S', '--source-version')),
731
        with_output=FlagArgument('show request headers', ('--with-output')),
729
        with_output=FlagArgument('show response headers', ('--with-output')),
732 730
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
733 731
    )
734 732

  
......
823 821
            'Suffix of src to replace with add_suffix, if matched',
824 822
            '--suffix-to-replace',
825 823
            default=''),
826
        with_output=FlagArgument('show request headers', ('--with-output')),
824
        with_output=FlagArgument('show response headers', ('--with-output')),
827 825
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
828 826
    )
829 827

  
......
879 877
        progress_bar=ProgressBarArgument(
880 878
            'do not show progress bar',
881 879
            ('-N', '--no-progress-bar'),
882
            default=False)
880
            default=False),
881
        with_output=FlagArgument('show response headers', ('--with-output')),
882
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
883 883
    )
884 884

  
885 885
    @errors.generic.all
......
890 890
        (progress_bar, upload_cb) = self._safe_progress_bar('Appending')
891 891
        try:
892 892
            f = open(local_path, 'rb')
893
            self.client.append_object(self.path, f, upload_cb)
893
            r = self.client.append_object(self.path, f, upload_cb)
894
            if self['json_output']:
895
                print_json(r)
896
            elif self['with_output']:
897
                print_items(r)
894 898
        except Exception:
895 899
            self._safe_progress_bar_finish(progress_bar)
896 900
            raise
......
1061 1065
        recursive=FlagArgument(
1062 1066
            'Recursively upload directory *contents* + subdirectories',
1063 1067
            ('-R', '--recursive')),
1064
        details=FlagArgument(
1065
            'Show a detailed list of uploaded objects at the end',
1066
            ('-l', '--details'))
1068
        with_output=FlagArgument(
1069
            'Show uploaded objects response headers',
1070
            ('--with-output')),
1071
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1067 1072
    )
1068 1073

  
1069 1074
    def _check_container_limit(self, path):
......
1176 1181
                    rpath, f,
1177 1182
                    etag=self['etag'], withHashFile=self['use_hashes'],
1178 1183
                    **params)
1179
                if self['details']:
1184
                if self['with_output'] or self['json_output']:
1180 1185
                    r['name'] = '%s: %s' % (self.client.container, rpath)
1181 1186
                    uploaded.append(r)
1182 1187
            else:
......
1195 1200
                        upload_cb=upload_cb,
1196 1201
                        container_info_cache=container_info_cache,
1197 1202
                        **params)
1198
                    if self['details']:
1203
                    if self['with_output'] or self['json_output']:
1199 1204
                        r['name'] = '%s: %s' % (self.client.container, rpath)
1200 1205
                        uploaded.append(r)
1201 1206
                except Exception:
......
1203 1208
                    raise
1204 1209
                finally:
1205 1210
                    self._safe_progress_bar_finish(progress_bar)
1206
        if self['details']:
1211
        if self['json_output']:
1212
            print_json(uploaded)
1213
        elif self['with_output']:
1207 1214
            print_items(uploaded)
1208 1215
        else:
1209 1216
            print('Upload completed')
......
1232 1239
            '--if-unmodified-since'),
1233 1240
        object_version=ValueArgument(
1234 1241
            'get the specific version',
1235
            ('-j', '--object-version'))
1242
            ('-O', '--object-version'))
1236 1243
    )
1237 1244

  
1238 1245
    @errors.generic.all
......
1286 1293
            '--if-unmodified-since'),
1287 1294
        object_version=ValueArgument(
1288 1295
            'get the specific version',
1289
            ('-j', '--object-version')),
1296
            ('-O', '--object-version')),
1290 1297
        poolsize=IntArgument('set pool size', '--with-pool-size'),
1291 1298
        progress_bar=ProgressBarArgument(
1292 1299
            'do not show progress bar',
......
1417 1424
                    download_cb) = self._safe_progress_bar(
1418 1425
                        'Download %s' % rpath)
1419 1426
                self.client.download_object(
1420
                    rpath,
1421
                    f,
1427
                    rpath, f,
1422 1428
                    download_cb=download_cb,
1423 1429
                    range_str=self['range'],
1424 1430
                    version=self['object_version'],
......
1442 1448
                    finally:
1443 1449
                        stdout.flush()
1444 1450
                        timeout += 0.1
1445

  
1446 1451
            print('\nDownload canceled by user')
1447 1452
            if local_path is not None:
1448 1453
                print('to resume, re-run with --resume')
......
1474 1479
            '--if-unmodified-since'),
1475 1480
        object_version=ValueArgument(
1476 1481
            'get the specific version',
1477
            ('-j', '--object-version'))
1482
            ('-O', '--object-version')),
1483
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1478 1484
    )
1479 1485

  
1480 1486
    @errors.generic.all
......
1489 1495
            if_none_match=self['if_none_match'],
1490 1496
            if_modified_since=self['if_modified_since'],
1491 1497
            if_unmodified_since=self['if_unmodified_since'])
1492
        print_dict(data)
1498
        printer = print_json if self['json_output'] else print_dict
1499
        printer(data)
1493 1500

  
1494 1501
    def main(self, container___path):
1495 1502
        super(self.__class__, self)._run(
......
1522 1529
        yes=FlagArgument('Do not prompt for permission', '--yes'),
1523 1530
        recursive=FlagArgument(
1524 1531
            'empty dir or container and delete (if dir)',
1525
            ('-R', '--recursive'))
1532
            ('-R', '--recursive')),
1533
        with_output=FlagArgument('show response headers', ('--with-output')),
1534
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1526 1535
    )
1527 1536

  
1528 1537
    def __init__(self, arguments={}):
......
1537 1546
    @errors.pithos.container
1538 1547
    @errors.pithos.object_path
1539 1548
    def _run(self):
1549
        r = {}
1540 1550
        if self.path:
1541 1551
            if self['yes'] or ask_user(
1542 1552
                    'Delete %s:%s ?' % (self.container, self.path)):
1543
                self.client.del_object(
1553
                r = self.client.del_object(
1544 1554
                    self.path,
1545 1555
                    until=self['until'],
1546 1556
                    delimiter=self['delimiter'])
......
1552 1562
            else:
1553 1563
                ask_msg = 'Delete container'
1554 1564
            if self['yes'] or ask_user('%s %s ?' % (ask_msg, self.container)):
1555
                self.client.del_container(
1565
                r = self.client.del_container(
1556 1566
                    until=self['until'],
1557 1567
                    delimiter=self['delimiter'])
1558 1568
            else:
1559 1569
                print('Aborted')
1570
                return
1571
        if self['json_output']:
1572
            print_json(r)
1573
        elif self['with_output']:
1574
            print_dict(r)
1560 1575

  
1561 1576
    def main(self, container____path__=None):
1562 1577
        super(self.__class__, self)._run(container____path__)
......
1576 1591

  
1577 1592
    arguments = dict(
1578 1593
        yes=FlagArgument('Do not prompt for permission', '--yes'),
1579
        force=FlagArgument('purge even if not empty', ('-F', '--force'))
1594
        force=FlagArgument('purge even if not empty', ('-F', '--force')),
1595
        with_output=FlagArgument('show response headers', ('--with-output')),
1596
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1580 1597
    )
1581 1598

  
1582 1599
    @errors.generic.all
......
1585 1602
    def _run(self):
1586 1603
        if self['yes'] or ask_user('Purge container %s?' % self.container):
1587 1604
            try:
1588
                self.client.purge_container()
1605
                r = self.client.purge_container()
1589 1606
            except ClientError as ce:
1590 1607
                if ce.status in (409,):
1591 1608
                    if self['force']:
1592 1609
                        self.client.del_container(delimiter='/')
1593
                        self.client.purge_container()
1610
                        r = self.client.purge_container()
1594 1611
                    else:
1595 1612
                        raiseCLIError(ce, details=['Try -F to force-purge'])
1596 1613
                else:
1597 1614
                    raise
1615
            if self['json_output']:
1616
                print_json(r)
1617
            elif self['with_output']:
1618
                print_dict(r)
1598 1619
        else:
1599 1620
            print('Aborted')
1600 1621

  
......
1630 1651
class file_unpublish(_file_container_command):
1631 1652
    """Unpublish an object"""
1632 1653

  
1654
    arguments = dict(
1655
        with_output=FlagArgument('show response headers', ('--with-output')),
1656
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1657
    )
1658

  
1633 1659
    @errors.generic.all
1634 1660
    @errors.pithos.connection
1635 1661
    @errors.pithos.container
1636 1662
    @errors.pithos.object_path
1637 1663
    def _run(self):
1638
            self.client.unpublish_object(self.path)
1664
            r = self.client.unpublish_object(self.path)
1665
            if self['json_output']:
1666
                print_json(r)
1667
            elif self['with_output']:
1668
                print_dict(r)
1639 1669

  
1640 1670
    def main(self, container___path):
1641 1671
        super(self.__class__, self)._run(
......
1645 1675

  
1646 1676

  
1647 1677
@command(pithos_cmds)
1648
class file_permissions(_file_container_command):
1649
    """Get read and write permissions of an object
1650
    Permissions are lists of users and user groups. There is read and write
1678
class file_permissions(_pithos_init):
1679
    """Manage user and group accessibility for objects
1680
    Permissions are lists of users and user groups. There are read and write
1651 1681
    permissions. Users and groups with write permission have also read
1652 1682
    permission.
1653 1683
    """
1654 1684

  
1685

  
1686
@command(pithos_cmds)
1687
class file_permissions_get(_file_container_command):
1688
    """Get read and write permissions of an object"""
1689

  
1655 1690
    @errors.generic.all
1656 1691
    @errors.pithos.connection
1657 1692
    @errors.pithos.container
......
1668 1703

  
1669 1704

  
1670 1705
@command(pithos_cmds)
1671
class file_setpermissions(_file_container_command):
1706
class file_permissions_set(_file_container_command):
1672 1707
    """Set permissions for an object
1673 1708
    New permissions overwrite existing permissions.
1674 1709
    Permission format:
1675 1710
    -   read=<username>[,usergroup[,...]]
1676 1711
    -   write=<username>[,usegroup[,...]]
1677 1712
    E.g. to give read permissions for file F to users A and B and write for C:
1678
    .       /file setpermissions F read=A,B write=C
1713
    .       /file permissions set F read=A,B write=C
1679 1714
    """
1680 1715

  
1681 1716
    @errors.generic.all
......
1712 1747

  
1713 1748

  
1714 1749
@command(pithos_cmds)
1715
class file_delpermissions(_file_container_command):
1750
class file_permissions_delete(_file_container_command):
1716 1751
    """Delete all permissions set on object
1717
    To modify permissions, use /file setpermssions
1752
    To modify permissions, use /file permissions set
1718 1753
    """
1719 1754

  
1720 1755
    @errors.generic.all
......
1742 1777
    arguments = dict(
1743 1778
        object_version=ValueArgument(
1744 1779
            'show specific version \ (applies only for objects)',
1745
            ('-j', '--object-version'))
1780
            ('-O', '--object-version')),
1781
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1746 1782
    )
1747 1783

  
1748 1784
    @errors.generic.all
......
1758 1794
            r = self.client.get_object_info(
1759 1795
                self.path,
1760 1796
                version=self['object_version'])
1761
        print_dict(r)
1797
        printer = print_json if self['json_output'] else print_dict
1798
        printer(r)
1762 1799

  
1763 1800
    def main(self, container____path__=None):
1764 1801
        super(self.__class__, self)._run(container____path__)
......
1766 1803

  
1767 1804

  
1768 1805
@command(pithos_cmds)
1769
class file_meta(_file_container_command):
1806
class file_metadata(_pithos_init):
1807
    """Metadata are attached on objects. They are formed as key:value pairs.
1808
    They can have arbitary values.
1809
    """
1810

  
1811

  
1812
@command(pithos_cmds)
1813
class file_metadata_get(_file_container_command):
1770 1814
    """Get metadata for account, containers or objects"""
1771 1815

  
1772 1816
    arguments = dict(
......
1774 1818
        until=DateArgument('show metadata until then', '--until'),
1775 1819
        object_version=ValueArgument(
1776 1820
            'show specific version \ (applies only for objects)',
1777
            ('-j', '--object-version'))
1821
            ('-O', '--object-version')),
1822
        json_output=FlagArgument('show headers in json', ('-j', '--json'))
1778 1823
    )
1779 1824

  
1780 1825
    @errors.generic.all
......
1789 1834
            else:
1790 1835
                r = self.client.get_account_meta(until=until)
1791 1836
                r = pretty_keys(r, '-')
1792
            if r:
1793
                print(bold(self.client.account))
1837
            print(bold(self.client.account))
1794 1838
        elif self.path is None:
1795 1839
            if self['detail']:
1796 1840
                r = self.client.get_container_info(until=until)
......
1811 1855
                r = self.client.get_object_meta(
1812 1856
                    self.path,
1813 1857
                    version=self['object_version'])
1814
            if r:
1815 1858
                r = pretty_keys(pretty_keys(r, '-'))
1816 1859
        if r:
1817
            print_dict(r)
1860
            printer = print_json if self['json_output'] else print_dict
1861
            printer(r)
1818 1862

  
1819 1863
    def main(self, container____path__=None):
1820 1864
        super(self.__class__, self)._run(container____path__)
......
1822 1866

  
1823 1867

  
1824 1868
@command(pithos_cmds)
1825
class file_setmeta(_file_container_command):
1826
    """Set a piece of metadata for account, container or object
1827
    Metadata are formed as key:value pairs
1828
    """
1869
class file_metadata_set(_file_container_command):
1870
    """Set a piece of metadata for account, container or object"""
1829 1871

  
1830 1872
    @errors.generic.all
1831 1873
    @errors.pithos.connection
......
1845 1887

  
1846 1888

  
1847 1889
@command(pithos_cmds)
1848
class file_delmeta(_file_container_command):
1890
class file_metadata_delete(_file_container_command):
1849 1891
    """Delete metadata with given key from account, container or object
1850
    Metadata are formed as key:value objects
1851
    - to get metadata of current account:     /file meta
1852
    - to get metadata of a container:         /file meta <container>
1853
    - to get metadata of an object:           /file meta <container>:<path>
1892
    - to get metadata of current account: /file metadata get
1893
    - to get metadata of a container:     /file metadata get <container>
1894
    - to get metadata of an object:       /file metadata get <container>:<path>
1854 1895
    """
1855 1896

  
1856 1897
    @errors.generic.all
b/kamaki/clients/livetest/pithos.py
393 393
    def _test_0050_container_put(self):
394 394
        self.client.container = self.c2
395 395

  
396
        r = self.client.container_put()
397
        self.assertEqual(r.status_code, 202)
396
        r = self.client.create_container()
397
        self.assertTrue(isinstance(r, dict))
398 398

  
399 399
        r = self.client.get_container_limit(self.client.container)
400 400
        cquota = r.values()[0]
401 401
        newquota = 2 * int(cquota)
402 402

  
403
        r = self.client.container_put(quota=newquota)
404
        self.assertEqual(r.status_code, 202)
403
        r = self.client.create_container(sizelimit=newquota)
404
        self.assertTrue(isinstance(r, dict))
405 405

  
406 406
        r = self.client.get_container_limit(self.client.container)
407 407
        xquota = int(r.values()[0])
408 408
        self.assertEqual(newquota, xquota)
409 409

  
410
        r = self.client.container_put(versioning='auto')
411
        self.assertEqual(r.status_code, 202)
410
        r = self.client.create_container(versioning='auto')
411
        self.assertTrue(isinstance(r, dict))
412 412

  
413 413
        r = self.client.get_container_versioning(self.client.container)
414 414
        nvers = r.values()[0]
......
421 421
        nvers = r.values()[0]
422 422
        self.assertEqual('none', nvers)
423 423

  
424
        r = self.client.container_put(metadata={'m1': 'v1', 'm2': 'v2'})
425
        self.assertEqual(r.status_code, 202)
424
        r = self.client.create_container(metadata={'m1': 'v1', 'm2': 'v2'})
425
        self.assertTrue(isinstance(r, dict))
426 426

  
427 427
        r = self.client.get_container_meta(self.client.container)
428 428
        self.assertTrue('x-container-meta-m1' in r)
b/kamaki/clients/pithos/__init__.py
101 101
        cnt_back_up = self.container
102 102
        try:
103 103
            self.container = container or cnt_back_up
104
            self.container_delete(until=unicode(time()))
104
            r = self.container_delete(until=unicode(time()))
105 105
        finally:
106 106
            self.container = cnt_back_up
107
        return r.headers
107 108

  
108 109
    def upload_object_unchunked(
109 110
            self, obj, f,
......
838 839
        ret = [''] * num_of_blocks
839 840
        self._init_thread_limit()
840 841
        flying = dict()
841
        for blockid, blockhash in enumerate(remote_hashes):
842
            start = blocksize * blockid
843
            is_last = start + blocksize > total_size
844
            end = (total_size - 1) if is_last else (start + blocksize - 1)
845
            (start, end) = _range_up(start, end, range_str)
846
            if start < end:
847
                self._watch_thread_limit(flying.values())
848
                flying[blockid] = self._get_block_async(obj, **restargs)
849
            for runid, thread in flying.items():
850
                if (blockid + 1) == num_of_blocks:
851
                    thread.join()
852
                elif thread.isAlive():
853
                    continue
854
                if thread.exception:
855
                    raise thread.exception
856
                ret[runid] = thread.value.content
857
                self._cb_next()
858
                flying.pop(runid)
859
        return ''.join(ret)
842
        try:
843
            for blockid, blockhash in enumerate(remote_hashes):
844
                start = blocksize * blockid
845
                is_last = start + blocksize > total_size
846
                end = (total_size - 1) if is_last else (start + blocksize - 1)
847
                (start, end) = _range_up(start, end, range_str)
848
                if start < end:
849
                    self._watch_thread_limit(flying.values())
850
                    flying[blockid] = self._get_block_async(obj, **restargs)
851
                for runid, thread in flying.items():
852
                    if (blockid + 1) == num_of_blocks:
853
                        thread.join()
854
                    elif thread.isAlive():
855
                        continue
856
                    if thread.exception:
857
                        raise thread.exception
858
                    ret[runid] = thread.value.content
859
                    self._cb_next()
860
                    flying.pop(runid)
861
            return ''.join(ret)
862
        except KeyboardInterrupt:
863
            sendlog.info('- - - wait for threads to finish')
864
            for thread in activethreads():
865
                thread.join()
860 866

  
861 867
    #Command Progress Bar method
862 868
    def _cb_next(self, step=1):
......
1028 1034
            raise ClientError(
1029 1035
                'Container "%s" is not empty' % self.container,
1030 1036
                r.status_code)
1037
        return r.headers
1031 1038

  
1032 1039
    def get_container_versioning(self, container=None):
1033 1040
        """
......
1128 1135
        :param delimiter: (str)
1129 1136
        """
1130 1137
        self._assert_container()
1131
        self.object_delete(obj, until=until, delimiter=delimiter)
1138
        r = self.object_delete(obj, until=until, delimiter=delimiter)
1139
        return r.headers
1132 1140

  
1133 1141
    def set_object_meta(self, obj, metapairs):
1134 1142
        """
......
1163 1171
        """
1164 1172
        :param obj: (str) remote object path
1165 1173
        """
1166
        self.object_post(obj, update=True, public=False)
1174
        r = self.object_post(obj, update=True, public=False)
1175
        return r.headers
1167 1176

  
1168 1177
    def get_object_info(self, obj, version=None):
1169 1178
        """
......
1255 1264
        filesize = fstat(source_file.fileno()).st_size
1256 1265
        nblocks = 1 + (filesize - 1) // blocksize
1257 1266
        offset = 0
1267
        headers = {}
1258 1268
        if upload_cb:
1259
            upload_gen = upload_cb(nblocks)
1260
            upload_gen.next()
1261
        for i in range(nblocks):
1262
            block = source_file.read(min(blocksize, filesize - offset))
1263
            offset += len(block)
1264
            self.object_post(
1265
                obj,
1266
                update=True,
1267
                content_range='bytes */*',
1268
                content_type='application/octet-stream',
1269
                content_length=len(block),
1270
                data=block)
1269
            self.progress_bar_gen = upload_cb(nblocks)
1270
            self._cb_next()
1271
        flying = {}
1272
        self._init_thread_limit()
1273
        try:
1274
            for i in range(nblocks):
1275
                block = source_file.read(min(blocksize, filesize - offset))
1276
                offset += len(block)
1271 1277

  
1272
            if upload_cb:
1273
                upload_gen.next()
1278
                self._watch_thread_limit(flying.values())
1279
                unfinished = {}
1280
                flying[i] = SilentEvent(
1281
                    method=self.object_post,
1282
                    obj=obj,
1283
                    update=True,
1284
                    content_range='bytes */*',
1285
                    content_type='application/octet-stream',
1286
                    content_length=len(block),
1287
                    data=block)
1288
                flying[i].start()
1289

  
1290
                for key, thread in flying.items():
1291
                    if thread.isAlive():
1292
                        if i < nblocks:
1293
                            unfinished[key] = thread
1294
                            continue
1295
                        thread.join()
1296
                    if thread.exception:
1297
                        raise thread.exception
1298
                    headers[key] = thread.value.headers
1299
                    self._cb_next()
1300
                flying = unfinished
1301
        except KeyboardInterrupt:
1302
            sendlog.info('- - - wait for threads to finish')
1303
            for thread in activethreads():
1304
                thread.join()
1305
        return headers.values()
1274 1306

  
1275 1307
    def truncate_object(self, obj, upto_bytes):
1276 1308
        """
b/kamaki/clients/pithos/test.py
1159 1159

  
1160 1160
    #  Pithos+ only methods
1161 1161

  
1162
    @patch('%s.container_put' % pithos_pkg, return_value=FR())
1163
    def test_create_container(self, CP):
1164
        FR.headers = container_info
1165
        cont = 'an0th3r_c0n741n3r'
1166

  
1167
        r = self.client.create_container()
1168
        self.assert_dicts_are_equal(r, container_info)
1169
        CP.assert_called_once_with(quota=None, versioning=None, metadata=None)
1170

  
1171
        bu_cont = self.client.container
1172
        r = self.client.create_container(cont)
1173
        self.assertEqual(self.client.container, bu_cont)
1174
        self.assert_dicts_are_equal(r, container_info)
1175
        self.assertEqual(
1176
            CP.mock_calls[-1],
1177
            call(quota=None, versioning=None, metadata=None))
1178

  
1179
        meta = dict(k1='v1', k2='v2')
1180
        r = self.client.create_container(cont, 42, 'auto', meta)
1181
        self.assertEqual(self.client.container, bu_cont)
1182
        self.assert_dicts_are_equal(r, container_info)
1183
        self.assertEqual(
1184
            CP.mock_calls[-1],
1185
            call(quota=42, versioning='auto', metadata=meta))
1186

  
1162 1187
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1163 1188
    def test_purge_container(self, CD):
1164 1189
        self.client.purge_container()
......
1672 1697
                upload_cb=append_gen if turn else None)
1673 1698
            self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1674 1699
            (args, kwargs) = post.mock_calls[-1][1:3]
1675
            self.assertEqual(args, (obj,))
1700
            self.assertEqual(kwargs['obj'], obj)
1676 1701
            self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1677 1702
            fsize = num_of_blocks * int(kwargs['content_length'])
1678 1703
            self.assertEqual(fsize, file_size)

Also available in: Unified diff