Mercurial > hg > config
annotate python/tree.py @ 425:5417eb6364ee
stubbing
author | Jeff Hammel <jhammel@mozilla.com> |
---|---|
date | Tue, 06 Aug 2013 18:26:42 -0700 |
parents | cccfe246452e |
children | 866c5b1bc4e8 |
rev | line source |
---|---|
382 | 1 #!/usr/bin/env python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 tree in python | |
6 """ | |
7 | |
425 | 8 # TODO: script2package |
9 | |
382 | 10 import optparse |
11 import os | |
12 import sys | |
13 | |
387 | 14 # ASCII delimeters |
388 | 15 ascii_delimeters = { |
389 | 16 'vertical_line' : '|', |
17 'item_marker' : '+', | |
18 'last_child' : '\\' | |
388 | 19 } |
387 | 20 |
21 # unicode delimiters | |
389 | 22 unicode_delimeters = { |
23 'vertical_line' : '│', | |
24 'item_marker' : '├', | |
25 'last_child' : '└' | |
26 } | |
382 | 27 |
425 | 28 |
382 | 29 def depth(directory): |
387 | 30 """returns the integer depth of a directory or path relative to '/' """ |
31 | |
382 | 32 directory = os.path.abspath(directory) |
33 level = 0 | |
34 while True: | |
35 directory, remainder = os.path.split(directory) | |
36 level += 1 | |
37 if not remainder: | |
38 break | |
39 return level | |
40 | |
425 | 41 |
42 ### stuff for tree generalization | |
43 | |
44 class Tree(object): | |
45 """tree structure in python""" | |
46 | |
47 def __init__(self, parent=None): | |
48 self.parent = parent | |
49 | |
50 def children(self): | |
51 """returns children of the tree""" | |
52 | |
53 def add(self, item): | |
54 """add a child to the tree root""" | |
55 | |
56 def update(self, tree): | |
57 """add a subtree to the tree""" | |
58 self.add(tree) | |
59 tree.parent = self # XXX .add should probably do this for scary reasons | |
60 | |
61 def output(self, serializer): | |
62 """output the tree via the given serializer""" | |
63 # XXX or should this method exist at all and instead the | |
64 # __call__ method of serializers take a Tree object? | |
65 | |
66 class DirectoryTree(Tree): | |
67 """directory structure as a tree""" | |
68 | |
69 ### | |
70 | |
388 | 71 def tree(directory, |
389 | 72 item_marker=unicode_delimeters['item_marker'], |
73 vertical_line=unicode_delimeters['vertical_line'], | |
74 last_child=unicode_delimeters['last_child'], | |
388 | 75 sort_key=lambda x: x.lower()): |
76 """ | |
77 display tree directory structure for `directory` | |
78 """ | |
383
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
79 |
385 | 80 retval = [] |
383
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
81 indent = [] |
384 | 82 last = {} |
386 | 83 top = depth(directory) |
84 | |
382 | 85 for dirpath, dirnames, filenames in os.walk(directory, topdown=True): |
383
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
86 |
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
87 abspath = os.path.abspath(dirpath) |
384 | 88 basename = os.path.basename(abspath) |
89 parent = os.path.dirname(abspath) | |
383
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
90 level = depth(abspath) - top |
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
91 |
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
92 # sort articles of interest |
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
93 for resource in (dirnames, filenames): |
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
94 resource[:] = sorted(resource, key=sort_key) |
382 | 95 |
389 | 96 files_end = item_marker |
97 dirpath_marker = item_marker | |
385 | 98 |
99 if level > len(indent): | |
389 | 100 indent.append(vertical_line) |
385 | 101 indent = indent[:level] |
102 | |
383
8d1ad56761b0
this still, somehow, eludes my tired brain
Jeff Hammel <jhammel@mozilla.com>
parents:
382
diff
changeset
|
103 if dirnames: |
389 | 104 files_end = item_marker |
385 | 105 last[abspath] = dirnames[-1] |
106 else: | |
389 | 107 files_end = last_child |
384 | 108 |
385 | 109 if last.get(parent) == os.path.basename(abspath): |
110 # last directory of parent | |
389 | 111 dirpath_mark = last_child |
385 | 112 indent[-1] = ' ' |
113 elif not indent: | |
114 dirpath_mark = '' | |
115 else: | |
389 | 116 dirpath_mark = item_marker |
385 | 117 |
388 | 118 # append the directory and piece of tree structure |
119 # if the top-level entry directory, print as passed | |
120 retval.append('%s%s%s'% (''.join(indent[:-1]), | |
121 dirpath_mark, | |
122 basename if retval else directory)) | |
123 # add the files | |
385 | 124 if filenames: |
125 last_file = filenames[-1] | |
387 | 126 retval.extend([('%s%s%s' % (''.join(indent), |
389 | 127 files_end if filename == last_file else item_marker, |
385 | 128 filename)) |
129 for index, filename in enumerate(filenames)]) | |
130 | |
382 | 131 return '\n'.join(retval) |
132 | |
133 def main(args=sys.argv[1:]): | |
134 | |
390
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
135 # parse command line options |
382 | 136 usage = '%prog [options]' |
137 parser = optparse.OptionParser(usage=usage, description=__doc__) | |
390
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
138 parser.add_option('-a', '--ascii', dest='use_ascii', |
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
139 action='store_true', default=False, |
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
140 help="use ascii delimeters (%s)" % ascii_delimeters) |
382 | 141 options, args = parser.parse_args(args) |
142 if not args: | |
143 args = ['.'] | |
144 | |
390
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
145 # sanity check |
382 | 146 not_directory = [arg for arg in args |
147 if not os.path.isdir(arg)] | |
148 if not_directory: | |
149 parser.error("Not a directory: %s" % (', '.join(not_directory))) | |
150 | |
390
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
151 delimeters = unicode_delimeters |
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
152 if options.use_ascii: |
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
153 delimeters = ascii_delimeters |
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
154 |
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
155 # print the tree |
382 | 156 for arg in args: |
390
9d02187611ae
make delimeters CLI switchable
Jeff Hammel <jhammel@mozilla.com>
parents:
389
diff
changeset
|
157 print (tree(arg, **delimeters)) |
382 | 158 |
159 if __name__ == '__main__': | |
160 main() |