More context in the error message
[pysize.git] / pysize / core / compute_size.py
blobc4b7f76feffff7fe9841f34382a621117071206c
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 # See the COPYING file for license information.
17 # Copyright (c) 2006 Guillaume Chazarain <guichaz@yahoo.fr>
19 import os
20 import stat
22 from pysize.core import chdir_browsing
23 from pysize.core.observable import observable
24 from pysize.core.pysize_global_fs_cache import get_dev_ino, cache_add_dir
25 from pysize.core.pysize_global_fs_cache import cache_add_hardlink
26 from pysize.core.pysize_global_fs_cache import cache_get_dir_size
28 size_observable = observable()
30 def log_error(text, e):
31 print text, e
33 def _fast(path, dir_ino, cross_device, error_cb):
34 """Return the size of the file or directory at path, using or updating the
35 cache. dir_ino is the inode of the directory containing the path, it is
36 used only with hardlinks.
37 """
38 try:
39 size_observable.fire_observers()
40 st = os.lstat(path)
41 if stat.S_ISDIR(st.st_mode): # Directory
42 dev_ino = st.st_dev, st.st_ino
43 cached_size = cache_get_dir_size(dev_ino)
44 if cached_size is None:
45 size = st.st_blocks * 512
46 dir_ino = st.st_ino
47 cookie = chdir_browsing.init(path)
48 try:
49 for child in chdir_browsing.browsedir(cookie, cross_device):
50 size += _fast(child, dir_ino, cross_device, error_cb)
51 finally:
52 chdir_browsing.finalize(cookie)
53 cache_add_dir(dev_ino, size)
54 else:
55 size = cached_size
56 elif st.st_nlink > 1: # Hardlink
57 if cache_add_hardlink(st.st_dev, st.st_ino, dir_ino, path):
58 size = st.st_blocks * 512
59 else:
60 size = 0
61 else: # File
62 size = st.st_blocks * 512
63 return size
64 except OSError, e:
65 error_cb('(%s) size.fast(%s)' % (os.getcwd(), path), e)
66 return 0
68 def slow(path, cross_device=True, error_cb=log_error):
69 """Same as _size_fast(path, dir_ino), except that dir_ino is computed."""
70 def parent_inode():
71 """Return the inode of the parent directory"""
72 path_st = os.lstat(path)
73 if stat.S_ISDIR(path_st.st_mode):
74 parent_path = path + '/..'
75 else:
76 parent_path = os.path.dirname(path) or '.'
77 return get_dev_ino(parent_path)[1]
78 try:
79 parent_ino = parent_inode()
80 except OSError, e:
81 error_cb('size.slow(%s)' % (path), e)
82 return 0
83 return _fast(path, parent_ino, cross_device, error_cb)
85 def slow_sum(paths, cross_device=True, error_cb=log_error):
86 res = 0
87 for p in paths:
88 res += slow(p, cross_device, error_cb)
89 return res