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 return cmp(n2
.size
, n1
.size
) or cmp(n1
.get_name(), n2
.get_name())
23 nodes
.sort(cmp=cmp_fn
)
25 class _pysize_node(object):
26 """The parent class of all displayed nodes, as these nodes are displayed on
27 screen, there should be few instances."""
28 def __init__(self
, parent
, basename
):
29 self
.rectangle
= (0, 0, 0, 0)
32 self
.basename
= basename
34 self
.size
= compute_size
.slow(basename
)
38 def compute_height(self
):
40 children_height
= max([c
.compute_height() for c
in self
.children
])
41 return children_height
+ 1
44 def compute_depth(self
):
52 def minimum_real_size(self
):
54 for child
in self
.children
:
55 child_min_real_size
= child
.minimum_real_size()
56 if child_min_real_size
>= 0 and child_min_real_size
< res
:
57 res
= child_min_real_size
60 def get_dirname(self
):
61 return os
.path
.dirname(self
.get_fullpaths()[0])
67 """Does the node correspond to a path that actually exists?"""
70 def get_fullname(self
):
71 fullpaths
= self
.get_fullpaths()
72 (prefix
, suffixes
) = _extract_prefix_suffixes(fullpaths
)
73 fullname
= _join_prefix_suffixes(prefix
, suffixes
)
76 def get_fullpaths(self
):
78 fullpath
= self
.basename
80 fullpath
= parent
.basename
+ '/' + fullpath
81 parent
= parent
.parent
85 """Return the name that should be displayed for this node."""
89 """The iterator is a depth first traversal."""
91 for child
in self
.children
:
95 def contains_point(self
, p
):
96 """Is the point p in the graphical representation of this node?"""
97 (x0
, x1
, y0
, y1
) = self
.rectangle
98 return x0
< p
.x
and p
.x
< x1
and y0
< p
.y
and p
.y
< y1
100 class _pysize_node_collection(_pysize_node
):
102 def __init__(self
, parent
, prefix
, children
, max_depth
, min_size
):
103 super(_pysize_node_collection
, self
).__init
__(parent
, None)
104 self
.basename
= prefix
105 self
.size
= sum([compute_size
.slow(prefix
+ '/' + p
) for p
in children
])
110 cookie
= chdir_browsing
.init(prefix
)
112 for child
in children
:
113 node
= create_node(self
, child
, max_depth
- 1, min_size
)
115 self
.children
.append(node
)
116 children_size
+= node
.size
119 remaining_nodes
.append(node
)
120 remaining_size
+= node
.size
122 _sort_nodes(self
.children
)
123 if remaining_size
> min_size
:
124 _sort_nodes(remaining_nodes
)
125 names
= [n
.__name
for n
in remaining_nodes
]
126 rem
= _pysize_node_remaining(self
, names
)
127 self
.children
.append(rem
)
129 chdir_browsing
.finalize(cookie
)
130 self
.size
= max(children_size
+ remaining_size
, self
.size
)
133 """This node does not actually exists, it is an aggregate."""
136 class _pysize_node_forest(_pysize_node_collection
):
137 def __init__(self
, parent
, children
, max_depth
, min_size
):
138 (prefix
, suffixes
) = _extract_prefix_suffixes(children
)
139 super(_pysize_node_forest
, self
).__init
__(parent
, prefix
, suffixes
,
141 self
.basename
= prefix
142 self
.forest_paths
= suffixes
143 self
.forest_name
= _join_prefix_suffixes(prefix
, suffixes
)
146 return self
.forest_name
148 def get_dirname(self
):
151 def get_fullpaths(self
):
152 fullpaths
= self
.forest_paths
155 fullpaths
= [parent
.basename
+ '/' + fp
for fp
in fullpaths
]
156 parent
= parent
.parent
159 class _pysize_node_dir(_pysize_node_collection
):
161 def __init__(self
, parent
, basename
, max_depth
, min_size
):
162 super(_pysize_node_dir
, self
).__init
__(parent
, basename
,
163 os
.listdir(basename
), max_depth
,
165 self
.basename
= basename
166 self
.size
= max(self
.size
, compute_size
.slow(basename
))
175 return self
.basename
+ '/'
177 class _pysize_node_remaining(_pysize_node_collection
):
178 """The sum of a directory's children that are too small to be drawn."""
179 def __init__(self
, parent
, elements
):
180 _pysize_node
.__init
__(self
, parent
, None)
181 # The parent constructor would visit the files
182 self
.size
= sum(map(compute_size
.slow
, elements
))
183 self
.remaining_elements
= elements
185 def minimum_real_size(self
):
189 return '{' + ','.join(self
.remaining_elements
) + '}'
191 def get_fullname(self
):
192 if self
.remaining_elements
:
193 return _pysize_node_collection
.get_fullname(self
)
194 return '' # This is the initial node
196 def get_fullpaths(self
):
197 fullpaths
= self
.remaining_elements
200 fullpaths
= [parent
.basename
+ '/' + fp
for fp
in fullpaths
]
201 parent
= parent
.parent
204 class _pysize_node_file(_pysize_node
):
206 def __init__(self
, parent
, basename
):
207 super(_pysize_node_file
, self
).__init
__(parent
, basename
)
209 class _pysize_node_hardlink(_pysize_node_file
):
210 """A hardlink, the canonical one, or a link"""
211 def __init__(self
, parent
, basename
):
212 super(_pysize_node_hardlink
, self
).__init
__(parent
, basename
)
214 def create_node(parent
, what
, max_depth
, min_size
):
215 """Return a pysize_node for parent/basename traversing up to max_depth
216 levels and only taking into account elements bigger than min_size."""
218 return _pysize_node_remaining(parent
, [])
220 if isinstance(what
, list):
221 size
= sum(map(compute_size
.slow
, what
))
225 size
= compute_size
.slow(what
)
228 if isinstance(what
, str):
230 node
= _pysize_node_remaining(parent
, what
)
231 elif isinstance(what
, list):
232 node
= _pysize_node_forest(parent
, what
, max_depth
, min_size
)
235 if stat
.S_ISDIR(st
.st_mode
):
236 node
= _pysize_node_dir(parent
, what
, max_depth
, min_size
)
237 elif st
.st_nlink
> 1:
238 node
= _pysize_node_hardlink(parent
, what
)
240 node
= _pysize_node_file(parent
, what
)