⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fileutilities.py

📁 BleachBit 删除了不必要的文件(例如缓存器
💻 PY
字号:
# vim: ts=4:sw=4:expandtab# -*- coding: UTF-8 -*-## BleachBit## Copyright (C) 2009 Andrew Ziem## http://bleachbit.sourceforge.net#### This program is free software: you can redistribute it and/or modify## it under the terms of the GNU General Public License as published by## the Free Software Foundation, either version 3 of the License, or## (at your option) any later version.## ## This program is distributed in the hope that it will be useful,## but WITHOUT ANY WARRANTY; without even the implied warranty of## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the## GNU General Public License for more details.## ## You should have received a copy of the GNU General Public License## along with this program.  If not, see <http://www.gnu.org/licenses/>."""File-related utilities"""import ConfigParserimport datetimeimport globimport localeimport osimport os.pathimport shleximport statimport subprocessHAVE_GNOME_VFS = Truetry:    import gnomevfsexcept:    try:        # this is the deprecated name        import gnome.vfs    except:        HAVE_GNOME_VFS = False    else:        gnomevfs = gnome.vfsif not "iglob" in dir(glob):    glob.iglob = glob.globfrom Options import optionsclass OpenFiles:    """Cached way to determine whether a file is open by active process"""    def __init__(self):        self.last_scan_time = None        self.files = []    def file_qualifies(self, filename):        """Return boolean wehether filename qualifies to enter cache (check \        against blacklist)"""        return not filename.startswith("/dev") and \            not filename.startswith("/proc")    def scan(self):        """Update cache"""        self.last_scan_time = datetime.datetime.now()        self.files = []        for filename in glob.iglob("/proc/*/fd/*"):            try:                target = os.path.realpath(filename)            except TypeError:                # happens, for example, when link points to                # '/etc/password\x00 (deleted)'                continue            if self.file_qualifies(target):                self.files.append(target)    def is_open(self, filename):        """Return boolean whether filename is open by running process"""        if None == self.last_scan_time or (datetime.datetime.now() -             self.last_scan_time).seconds > 10:            self.scan()        return filename in self.filesdef bytes_to_human(bytes):    """Display a file size in human terms (megabytes, etc.)"""    storage_multipliers = { 1024**5 : 'PB', 1024**4 : 'TB', \        1024**3 : 'GB', 1024**2: 'MB', 1024: 'KB', 1 : 'B' }    if 0 == bytes:        return "0"    if bytes >= 1024**3:        decimals = 2    elif bytes >= 1024:        decimals = 1    else:        decimals = 0    for key in sorted(storage_multipliers.keys(), reverse = True):        if bytes >= key:            abbrev = (1.0 * bytes) / key            suf = storage_multipliers[key]            format = "%." + str(decimals) + "f" + suf            if hasattr(locale, 'format_string'):                return locale.format_string(format, abbrev)            else:                return locale.format(format, abbrev)def children_in_directory(top, list_directories = False):    """Iterate files and, optionally, subdirectories in directory"""    for (dirpath, dirnames, filenames) in os.walk(top, topdown=False):        if list_directories:            for dirname in dirnames:                yield os.path.join(dirpath, dirname)        for filename in filenames:            yield os.path.join(dirpath, filename)def delete(path, shred = False):    """Delete path that is either file, directory, link or FIFO"""    print "info: removing '%s'" % (path,)    mode = os.lstat(path)[stat.ST_MODE]    if stat.S_ISFIFO(mode) or stat.S_ISLNK(mode):        os.remove(path)    elif stat.S_ISDIR(mode):        os.rmdir(path)    elif stat.S_ISREG(mode):        if options.get('shred') or shred:            # http://en.wikipedia.org/wiki/Data_remanence            # 2006 NIST Special Publication 800-88 (p. 7): "Studies have            # shown that most of today's media can be effectively cleared            # by one overwrite"            args = ["shred", "--remove", "--iterations=0", "--zero", path]            ret = subprocess.call(args)            if 0 != ret:                raise Exception("shred subprocess returned non-zero error code " % (ret,))        else:            os.remove(path)    else:        raise Exception("Unsupported special file type")def ego_owner(filename):    """Return whether current user owns the file"""    return os.lstat(filename).st_uid == os.getuid()def exists_in_path(filename):    """Returns boolean whether the filename exists in the path"""    for dirname in os.getenv('PATH').split(":"):        if os.path.exists(os.path.join(dirname, filename)):            return True    return Falsedef exe_exists(pathname):    """Returns boolean whether executable exists"""    if os.path.isabs(pathname):        if not os.path.exists(pathname):            return False    else:        if not exists_in_path(pathname):            return False    return Truedef getsize(path):    """Return the actual file size considering spare files       and symlinks"""    __stat = os.lstat(path)    return __stat.st_blocks * 512def __is_broken_xdg_desktop_application(config, desktop_pathname):    """Returns boolean whether application deskop entry file is broken"""    if not config.has_option('Desktop Entry', 'Exec'):        print "info: is_broken_xdg_menu: missing required option 'Exec': '%s'" \            % (desktop_pathname)        return True    exe = config.get('Desktop Entry', 'Exec').split(" ")[0]    if not exe_exists(exe):        print "info: is_broken_xdg_menu: executable '%s' does not exist '%s'" \            % (exe, desktop_pathname)        return True    if 'env' == exe:        # Wine v1.0 creates .desktop files like this        # Exec=env WINEPREFIX="/home/z/.wine" wine "C:\\Program Files\\foo\\foo.exe"        execs = shlex.split(config.get('Desktop Entry', 'Exec'))        wineprefix = None        del(execs[0])        while True:            if 0 <= execs[0].find("="):                (name, value) = execs[0].split("=")                if 'WINEPREFIX' == name:                    wineprefix = value                del(execs[0])            else:                break        if not exe_exists(execs[0]):            print "info: is_broken_xdg_menu: executable '%s'" \                "does not exist '%s'" % (execs[0], desktop_pathname)            return True        # check the Windows executable exists        if wineprefix:            windows_exe = wine_to_linux_path(wineprefix, execs[1])            if not os.path.exists(windows_exe):                print "info: is_broken_xdg_menu: Windows executable" \                    "'%s' does not exist '%s'" % \                    (windows_exe, desktop_pathname)                return True    return Falsedef is_broken_xdg_desktop(pathname):    """Returns boolean whether the given XDG desktop entry file is broken.    Reference: http://standards.freedesktop.org/desktop-entry-spec/latest/"""    config = ConfigParser.RawConfigParser()    config.read(pathname)    if not config.has_section('Desktop Entry'):        print "info: is_broken_xdg_menu: missing required section " \            "'Desktop Entry': '%s'" % (pathname)        return True    if not config.has_option('Desktop Entry', 'Type'):        print "info: is_broken_xdg_menu: missing required option 'Type': '%s'" % (pathname)        return True    file_type = config.get('Desktop Entry', 'Type').strip().lower()    if 'link' == file_type:        if not config.has_option('Desktop Entry', 'URL') and \            not config.has_option('Desktop Entry', 'URL[$e]'):            print "info: is_broken_xdg_menu: missing required option 'URL': '%s'" % (pathname)            return True        return False    if 'mimetype' == file_type:        if not config.has_option('Desktop Entry', 'MimeType'):            print "info: is_broken_xdg_menu: missing required option 'MimeType': '%s'" % (pathname)            return True        mimetype = config.get('Desktop Entry', 'MimeType').strip().lower()        if HAVE_GNOME_VFS and 0 == len(gnomevfs.mime_get_all_applications(mimetype)):            print "info: is_broken_xdg_menu: MimeType '%s' not " \                "registered '%s'" % (mimetype, pathname)            return True        return False    if 'application' != file_type:        print "Warning: unhandled type '%s': file '%s'" % (file_type, pathname)        return False    if __is_broken_xdg_desktop_application(config, pathname):        return True    return Falsedef wine_to_linux_path(wineprefix, windows_pathname):    """Return a Linux pathname from an absolute Windows pathname and Wine prefix"""    drive_letter = windows_pathname[0]    windows_pathname = windows_pathname.replace(drive_letter + ":", \        "drive_" + drive_letter.lower())    windows_pathname = windows_pathname.replace("\\","/")    return os.path.join(wineprefix, windows_pathname)openfiles = OpenFiles()import unittestclass TestFileUtilities(unittest.TestCase):    """Unit test for module FileUtilities"""    def __touch(self, filename):        """Create an empty file"""        f = open(filename, "w")    def __human_to_bytes(self, string):        """Convert a string like 10.2GB into bytes"""        multiplier = { 'B' : 1, 'KB': 1024, 'MB': 1024**2, \            'GB': 1024**3, 'TB': 1024**4 }        import re        matches = re.findall("^([0-9]*)(\.[0-9]{1,2})?([KMGT]{0,1}B)$", string)        if 2 > len(matches[0]):            raise ValueError("Invalid input for '%s'" % (string))        return int(float(matches[0][0]+matches[0][1]) * multiplier[matches[0][2]])    def test_bytes_to_human(self):        """Unit test for class bytes_to_human"""        old_locale = locale.getlocale(locale.LC_NUMERIC)        locale.setlocale(locale.LC_NUMERIC, 'en_US.UTF-8')        # test one-way conversion for predefined values        tests = [ ("0", bytes_to_human(0)),                  ("1B", bytes_to_human(1)),                  ("1.0KB", bytes_to_human(1024)),                  ("1.0MB", bytes_to_human(1024**2)),                  ("1.00GB", bytes_to_human(1024**3)),                  ("1.00TB", bytes_to_human(1024**4)) ]        for test in tests:            self.assertEqual(test[0], test[1])        # test roundtrip conversion for random values        import random        for n in range(0, 1000):            bytes = random.randrange(0, 1024**4)            human = bytes_to_human(bytes)            bytes2 = self.__human_to_bytes(human)            error =  abs(float(bytes2 - bytes) / bytes)            self.assert_(abs(error) < 0.01, \                "%d (%s) is %.2f%% different than %d" % \                (bytes, human, error * 100, bytes2))        # test localization        if hasattr(locale, 'format_string'):            try:                locale.setlocale(locale.LC_NUMERIC, 'de_DE.utf8')            except:                print "Warning: exception when setlocale to de_DE.utf8"            else:                self.assertEqual("1,00TB", bytes_to_human(1024**4))        # clean up        locale.setlocale(locale.LC_NUMERIC, old_locale)    def test_children_in_directory(self):         """Unit test for function children_in_directory()"""        import tempfile        # test an existing directory that usually exists        dirname = os.path.expanduser("~/.config")        for filename in children_in_directory(dirname, True):            self.assert_ (type(filename) is str)            self.assert_ (os.path.isabs(filename))        for filename in children_in_directory(dirname, False):            self.assert_ (type(filename) is str)            self.assert_ (os.path.isabs(filename))            self.assert_ (not os.path.isdir(filename))        # test a constructed file in a constructed directory        dirname = tempfile.mkdtemp()        filename = os.path.join(dirname, "somefile")        self.__touch(filename)        for loopfilename in children_in_directory(dirname, True):            self.assertEqual (loopfilename, filename)        for loopfilename in children_in_directory(dirname, False):            self.assertEqual (loopfilename, filename)        os.remove(filename)        # test subdirectory        subdirname = os.path.join(dirname, "subdir")        os.mkdir(subdirname)        for filename in children_in_directory(dirname, True):            self.assertEqual (filename, subdirname)        for filename in children_in_directory(dirname, False):            self.assert_ (False)        os.rmdir(subdirname)        os.rmdir(dirname)    def test_delete(self):        """Unit test for method delete()"""        import tempfile        hebrew = "注执讘职专执讬转"        katanana = "銈

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -