File: //lib/hubdns/paths.py
#
# Copyright (c) 2010 Liraz Siri <liraz@turnkeylinux.org>
#
# This file is part of TKLBAM (TurnKey GNU/Linux Backup and Migration).
#
# TKLBAM is open source 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.
#
"""
DESCRIPTION
This modules contains:
1) Paths: high-level class for representing file paths
2) make_relative: convenience function for recalculating a path relative to another base path
File paths are accessible as instance attributes
. and - are replaced for _
The files attribute is "inherited".
USAGE
class FooPaths(Paths):
files = ["foo", "sub.dir/sub-file"]
class BarPaths(FooPaths):
files = [ "bar" ] + subdir("sub.dir2", ["sub-file2"])
paths = BarPaths("/tmp")
print paths.foo
print paths.sub_dir
print paths.sub_dir.sub_file
print paths.sub_dir2.sub_file2
print paths.make_relative(paths.sub_dir, paths.sub_dir.sub_file)
"""
import re
import os
from os.path import *
__all__ = ['make_relative', 'Paths', 'subdir']
def make_relative(base, path):
"""Return <path> relative to <base>.
For example:
make_relative("../../", "file") == "path/to/file"
make_relative("/base", "/tmp") == "../tmp"
make_relative("/base", "/base/backups/file") == "backups/file"
"""
up_count = 0
base = realpath(str(base)).rstrip('/')
path = realpath(str(path)).rstrip('/')
while True:
if path == base or path.startswith(base.rstrip("/") + "/"):
return ("../" * up_count) + path[len(base) + 1:]
base = dirname(base).rstrip('/')
up_count += 1
class Paths(str):
make_relative = staticmethod(make_relative)
files = []
def __new__(cls, path, files=[]):
return str.__new__(cls, path)
def __init__(self, path, files=[]):
self.path = path
self.files = {}
def classfiles(cls):
files = cls.files
for base in cls.__bases__:
if issubclass(base, Paths):
files += classfiles(base)
return files
for file in files + classfiles(self.__class__):
self.register(file)
def __getattr__(self, name):
if name in self.files:
return join(self.path, self.files[name])
raise AttributeError("no such attribute: " + name)
@staticmethod
def _fname2attr(fname):
return re.sub(r'[\.-]', '_', fname)
def listdir(self):
"Return a list containing the names of the entries in directory"""
return list(self.files.values())
def register(self, filename):
if '/' in filename:
subdir, filename = filename.split('/', 1)
attr = self._fname2attr(subdir)
subpaths = getattr(self, attr, None)
if not subpaths or not isinstance(subpaths, Paths):
subpaths = Paths(join(self.path, subdir))
setattr(self, attr, subpaths)
subpaths.register(filename)
else:
attr = self._fname2attr(filename)
self.files[attr] = filename
def subdir(dir, files):
return [ os.path.join(dir, file) for file in files ]
def test():
class FooPaths(Paths):
files = ["foo", "sub.dir/sub-file"]
class BarPaths(FooPaths):
files = [ "bar" ] + subdir("sub.dir2", ["sub-file2"])
paths = BarPaths("/tmp")
print(paths.foo)
print(paths.sub_dir)
print(paths.sub_dir.sub_file)
print(paths.sub_dir2.sub_file2)
return paths
if __name__ == "__main__":
test()