7824: Further ordering fixes. Now all arv-ls tests passes.
[arvados.git] / sdk / python / arvados / commands / ls.py
1 #!/usr/bin/env python
2
3 from __future__ import print_function
4
5 import argparse
6 import collections
7 import sys
8
9 import arvados
10 import arvados.commands._util as arv_cmd
11
12 from arvados._version import __version__
13
14 FileInfo = collections.namedtuple('FileInfo', ['stream_name', 'name', 'size'])
15
16 def parse_args(args):
17     parser = argparse.ArgumentParser(
18         description='List contents of a manifest',
19         parents=[arv_cmd.retry_opt])
20
21     parser.add_argument('locator', type=str,
22                         help="""Collection UUID or locator""")
23     parser.add_argument('-s', action='store_true',
24                         help="""List file sizes, in KiB.""")
25     parser.add_argument('--version', action='version',
26                         version="%s %s" % (sys.argv[0], __version__),
27                         help='Print version and exit.')
28
29     return parser.parse_args(args)
30
31 def size_formatter(coll_file):
32     return "{:>10}".format((coll_file.size + 1023) / 1024)
33
34 def name_formatter(coll_file):
35     return "{}/{}".format(coll_file.stream_name, coll_file.name)
36
37 def main(args, stdout, stderr, api_client=None):
38     args = parse_args(args)
39
40     if api_client is None:
41         api_client = arvados.api('v1')
42
43     try:
44         cr = arvados.CollectionReader(args.locator, api_client=api_client,
45                                       num_retries=args.retries)
46     except (arvados.errors.ArgumentError,
47             arvados.errors.NotFoundError) as error:
48         print("arv-ls: error fetching collection: {}".format(error),
49               file=stderr)
50         return 1
51
52     formatters = []
53     if args.s:
54         formatters.append(size_formatter)
55     formatters.append(name_formatter)
56
57     for f in files_in_collection(cr):
58         print(*(info_func(f) for info_func in formatters), file=stdout)
59
60     return 0
61
62 def files_in_collection(c, stream_name='.'):
63     # Sort first by file type, then alphabetically by file path.
64     for i in sorted(c.keys(),
65                     key=lambda k: (
66                         isinstance(c[k], arvados.collection.Subcollection),
67                         k.upper())):
68         if isinstance(c[i], arvados.arvfile.ArvadosFile):
69             yield FileInfo(stream_name=stream_name,
70                            name=i,
71                            size=c[i].size())
72         elif isinstance(c[i], arvados.collection.Subcollection):
73             for f in files_in_collection(c[i], "{}/{}".format(stream_name, i)):
74                 yield f