imip-agent

Annotated imiptools/filesys.py

524:65092bd5fd29
2015-05-15 Paul Boddie Added convenience methods for object copying, property access and removal, and time zone identifier acquisition.
paul@147 1
#!/usr/bin/env python
paul@147 2
paul@147 3
"""
paul@147 4
Filesystem utilities.
paul@147 5
paul@147 6
Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
paul@147 7
paul@147 8
This program is free software; you can redistribute it and/or modify it under
paul@147 9
the terms of the GNU General Public License as published by the Free Software
paul@147 10
Foundation; either version 3 of the License, or (at your option) any later
paul@147 11
version.
paul@147 12
paul@147 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@147 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@147 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@147 16
details.
paul@147 17
paul@147 18
You should have received a copy of the GNU General Public License along with
paul@147 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@147 20
"""
paul@147 21
paul@303 22
import errno
paul@330 23
from imiptools.config import DEFAULT_PERMISSIONS, DEFAULT_DIR_PERMISSIONS
paul@147 24
from os.path import abspath, commonprefix, exists, join
paul@303 25
from os import chmod, makedirs, mkdir, rmdir
paul@303 26
from time import sleep, time
paul@147 27
paul@147 28
def check_dir(base, dir):
paul@147 29
    return commonprefix([base, abspath(dir)]) == base
paul@147 30
paul@330 31
def fix_permissions(filename, is_dir=False):
paul@147 32
    try:
paul@330 33
        chmod(filename, is_dir and DEFAULT_DIR_PERMISSIONS or DEFAULT_PERMISSIONS)
paul@147 34
    except OSError:
paul@147 35
        pass
paul@147 36
paul@356 37
def make_path(base, parts):
paul@356 38
    for part in parts:
paul@356 39
        pathname = join(base, part)
paul@356 40
        if not exists(pathname):
paul@356 41
            mkdir(pathname)
paul@356 42
            fix_permissions(pathname, True)
paul@356 43
        base = pathname
paul@356 44
paul@147 45
class FileBase:
paul@147 46
paul@147 47
    "Basic filesystem operations."
paul@147 48
paul@303 49
    lock_name = "__lock__"
paul@303 50
paul@147 51
    def __init__(self, store_dir):
paul@147 52
        self.store_dir = store_dir
paul@147 53
        if not exists(self.store_dir):
paul@356 54
            makedirs(self.store_dir)
paul@356 55
            fix_permissions(self.store_dir, True)
paul@147 56
paul@147 57
    def get_file_object(self, base, *parts):
paul@147 58
        pathname = join(base, *parts)
paul@147 59
        return check_dir(base, pathname) and pathname or None
paul@147 60
paul@147 61
    def get_object_in_store(self, *parts):
paul@147 62
paul@147 63
        """
paul@147 64
        Return the name of any valid object stored within a hierarchy specified
paul@147 65
        by the given 'parts'.
paul@147 66
        """
paul@147 67
paul@147 68
        parent = expected = self.store_dir
paul@147 69
paul@147 70
        for part in parts:
paul@147 71
            filename = self.get_file_object(expected, part)
paul@147 72
            if not filename:
paul@147 73
                return False
paul@147 74
            parent = expected
paul@147 75
            expected = filename
paul@147 76
paul@147 77
        if not exists(parent):
paul@356 78
            make_path(self.store_dir, parts[:-1])
paul@147 79
paul@147 80
        return filename
paul@147 81
paul@303 82
    # Locking methods.
paul@303 83
    # This uses the directory creation method exploited by MoinMoin.util.lock.
paul@303 84
    # However, a simple single lock type mechanism is employed here.
paul@303 85
paul@303 86
    def get_lock_dir(self, *parts):
paul@303 87
        parts = parts and list(parts) or []
paul@303 88
        parts.append(self.lock_name)
paul@303 89
        return self.get_object_in_store(*parts)
paul@303 90
paul@303 91
    def acquire_lock(self, timeout=None, *parts):
paul@303 92
paul@303 93
        """
paul@303 94
        Acquire an exclusive lock on the directory or a path within it described
paul@303 95
        by 'parts'.
paul@303 96
        """
paul@303 97
paul@303 98
        start = now = time()
paul@303 99
paul@303 100
        while not timeout or now - start < timeout:
paul@303 101
            try:
paul@303 102
                mkdir(self.get_lock_dir(*parts))
paul@303 103
                break
paul@303 104
            except OSError, exc:
paul@303 105
                if exc.errno != errno.EEXIST:
paul@303 106
                    raise
paul@303 107
            sleep(1)
paul@303 108
            now = time()
paul@303 109
paul@303 110
    def release_lock(self, *parts):
paul@303 111
paul@303 112
        """
paul@303 113
        Release an acquired lock on the directory or a path within it described
paul@303 114
        by 'parts'.
paul@303 115
        """
paul@303 116
paul@303 117
        try:
paul@303 118
            rmdir(self.get_lock_dir(*parts))
paul@303 119
        except OSError, exc:
paul@303 120
            if exc.errno != errno.ENOENT:
paul@303 121
                raise
paul@303 122
paul@147 123
# vim: tabstop=4 expandtab shiftwidth=4