4 from pysize
.core
import chdir_browsing
5 from pysize
.core
import compute_size
7 def _extract_prefix_suffixes(paths
):
8 """['/prefix/suffix1', '/prefix/suffix2', '/prefix/suffix3'] =>
9 ('/prefix', ['suffix1', 'suffix2', 'suffix3'])"""
10 prefix_len
= os
.path
.commonprefix(paths
).rfind('/') + 1
11 prefix
= paths
[0][:prefix_len
- 1]
12 suffixes
= [p
[prefix_len
:] for p
in paths
]
13 return (prefix
, suffixes
)
15 def _join_prefix_suffixes(prefix
, suffixes
):
16 if len(suffixes
) == 1:
17 return prefix
+ '/' + suffixes
[0]
18 return prefix
+ '/{' + ','.join(suffixes
) + '}'
20 def _sort_nodes(nodes
):
22 size_diff
= n2
.size
- n1
.size
25 return cmp(n1
.get_name(), n2
.get_name())
26 nodes
.sort(cmp=cmp_fn
)
28 class _pysize_node(object):
29 """The parent class of all displayed nodes, as these nodes are displayed on
30 screen, there should be few instances."""
31 def __init__(self
, parent
, basename
):
32 self
.rectangle
= (0, 0, 0, 0)
35 self
.basename
= basename
37 self
.size
= compute_size
.slow(basename
)
41 def compute_height(self
):
43 children_height
= max([c
.compute_height() for c
in self
.children
])
44 return children_height
+ 1
47 def compute_depth(self
):
55 def minimum_real_size(self
):
57 for child
in self
.children
:
58 child_min_real_size
= child
.minimum_real_size()
59 if child_min_real_size
>= 0 and child_min_real_size
< res
:
60 res
= child_min_real_size
63 def get_dirname(self
):
64 return os
.path
.dirname(self
.get_fullpaths()[0])
70 """Does the node correspond to a path that actually exists?"""
73 def get_fullname(self
):
74 fullpaths
= self
.get_fullpaths()
75 (prefix
, suffixes
) = _extract_prefix_suffixes(fullpaths
)
76 fullname
= _join_prefix_suffixes(prefix
, suffixes
)
79 def get_fullpaths(self
):
81 fullpath
= self
.basename
83 fullpath
= parent
.basename
+ '/' + fullpath
84 parent
= parent
.parent
88 """Return the name that should be displayed for this node."""
92 """The iterator is a depth first traversal."""
94 for child
in self
.children
:
98 def contains_point(self
, p
):
99 """Is the point p in the graphical representation of this node?"""
100 (x0
, x1
, y0
, y1
) = self
.rectangle
101 return x0
< p
.x
and p
.x
< x1
and y0
< p
.y
and p
.y
< y1
103 class _pysize_node_collection(_pysize_node
):
105 def __init__(self
, parent
, prefix
, children
, max_depth
, min_size
):
106 super(_pysize_node_collection
, self
).__init
__(parent
, None)
107 self
.basename
= prefix
108 self
.size
= sum([compute_size
.slow(prefix
+ '/' + p
) for p
in children
])
113 cookie
= chdir_browsing
.init(prefix
)
115 for child
in children
:
116 node
= create_node(self
, child
, max_depth
- 1, min_size
)
118 self
.children
.append(node
)
119 children_size
+= node
.size
122 remaining_items
.append(node
)
123 remaining_size
+= node
.size
125 _sort_nodes(self
.children
)
126 if remaining_size
> min_size
:
127 _sort_nodes(remaining_items
)
128 names
= [n
.__name
for n
in remaining_items
]
129 rem
= _pysize_node_remaining(self
, names
)
130 self
.children
.append(rem
)
132 chdir_browsing
.finalize(cookie
)
133 self
.size
= max(children_size
+ remaining_size
, self
.size
)
136 """This node does not actually exists, it is an aggregate."""
139 class _pysize_node_forest(_pysize_node_collection
):
140 def __init__(self
, parent
, children
, max_depth
, min_size
):
141 (prefix
, suffixes
) = _extract_prefix_suffixes(children
)
142 super(_pysize_node_forest
, self
).__init
__(parent
, prefix
, suffixes
,
144 self
.basename
= prefix
145 self
.forest_items
= suffixes
146 self
.forest_name
= _join_prefix_suffixes(prefix
, suffixes
)
149 return self
.forest_name
151 def get_dirname(self
):
154 def get_fullpaths(self
):
155 fullpaths
= self
.forest_items
158 fullpaths
= [parent
.basename
+ '/' + fp
for fp
in fullpaths
]
159 parent
= parent
.parent
162 class _pysize_node_dir(_pysize_node_collection
):
164 def __init__(self
, parent
, basename
, max_depth
, min_size
):
165 super(_pysize_node_dir
, self
).__init
__(parent
, basename
,
166 os
.listdir(basename
), max_depth
,
168 self
.basename
= basename
169 self
.size
= max(self
.size
, compute_size
.slow(basename
))
178 return self
.basename
+ '/'
180 class _pysize_node_remaining(_pysize_node_collection
):
181 """The sum of a directory's children that are too small to be drawn."""
182 def __init__(self
, parent
, elements
):
183 _pysize_node
.__init
__(self
, parent
, None)
184 # The parent constructor would visit the files
185 self
.size
= sum(map(compute_size
.slow
, elements
))
186 self
.remaining_elements
= elements
188 def minimum_real_size(self
):
192 return '{' + ','.join(self
.remaining_elements
) + '}'
194 def get_fullname(self
):
195 if self
.remaining_elements
:
196 return _pysize_node_collection
.get_fullname(self
)
197 return '' # This is the initial node
199 def get_fullpaths(self
):
200 fullpaths
= self
.remaining_elements
203 fullpaths
= [parent
.basename
+ '/' + fp
for fp
in fullpaths
]
204 parent
= parent
.parent
207 class _pysize_node_file(_pysize_node
):
209 def __init__(self
, parent
, basename
):
210 super(_pysize_node_file
, self
).__init
__(parent
, basename
)
212 class _pysize_node_hardlink(_pysize_node_file
):
213 """A hardlink, the canonical one, or a link"""
214 def __init__(self
, parent
, basename
):
215 super(_pysize_node_hardlink
, self
).__init
__(parent
, basename
)
217 def create_node(parent
, what
, max_depth
, min_size
):
218 """Return a pysize_node for parent/basename traversing up to max_depth
219 levels and only taking into account elements bigger than min_size."""
221 return _pysize_node_remaining(parent
, [])
223 if isinstance(what
, list):
224 size
= sum(map(compute_size
.slow
, what
))
228 size
= compute_size
.slow(what
)
231 if isinstance(what
, str):
233 node
= _pysize_node_remaining(parent
, what
)
234 elif isinstance(what
, list):
235 node
= _pysize_node_forest(parent
, what
, max_depth
, min_size
)
238 if stat
.S_ISDIR(st
.st_mode
):
239 node
= _pysize_node_dir(parent
, what
, max_depth
, min_size
)
240 elif st
.st_nlink
> 1:
241 node
= _pysize_node_hardlink(parent
, what
)
243 node
= _pysize_node_file(parent
, what
)