1 #!/usr/bin/env python 2 3 """ 4 Directory context functionality. 5 6 Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from os import makedirs, walk 23 from os.path import abspath, commonprefix, exists, join 24 import fnmatch 25 26 # Get the directory with trailing path separator when assessing path prefixes 27 # in order to prevent sibling directory confusion. 28 29 def inside(filename, dirname): 30 31 "Return whether 'filename' is inside 'dirname'." 32 33 dirname = join(dirname, "") 34 return commonprefix((filename, dirname)) == dirname 35 36 def within(filename, dirname): 37 38 "Return the part of 'filename' found within 'dirname'." 39 40 dirname = join(dirname, "") 41 prefix = commonprefix((filename, dirname)) 42 43 if prefix == dirname: 44 return filename[len(prefix):] 45 else: 46 return None 47 48 49 50 class Directory: 51 52 "A directory abstraction." 53 54 def __init__(self, filename): 55 56 "Initialise the abstraction with the given 'filename'." 57 58 self.filename = abspath(filename) 59 60 def exists(self, filename): 61 62 """ 63 Return whether 'filename' exists within the directory. This filename 64 is relative to the directory. 65 """ 66 67 return exists(self.get_filename(filename)) 68 69 def get_filename(self, filename): 70 71 """ 72 Return the full path of a file with the given 'filename' found within 73 the directory. The full path is an absolute path. 74 """ 75 76 # Get the absolute path for the combination of directory and filename. 77 78 pathname = abspath(join(self.filename, filename)) 79 80 if inside(pathname, self.filename): 81 return pathname 82 else: 83 raise ValueError, filename 84 85 def makedirs(self, filename): 86 87 """ 88 Ensure that a directory having the given 'filename' exists by creating 89 it and any directories needed for it to be created. This filename is 90 relative to the directory. 91 """ 92 93 pathname = self.get_filename(filename) 94 95 if not exists(pathname): 96 makedirs(pathname) 97 98 def select_files(self, pattern): 99 100 """ 101 Return a list of filenames found within the directory matching 102 'pattern'. These filenames are relative to the directory. 103 """ 104 105 selected = [] 106 107 # Obtain pathnames, directory names and filenames within the directory. 108 109 for dirpath, dirnames, filenames in walk(self.filename): 110 for filename in filenames: 111 112 # Qualify filenames with the directory path. 113 114 pathname = join(dirpath, filename) 115 116 # Obtain the local filename within the directory. 117 118 local_filename = self.within(pathname) 119 120 # Match filenames supporting the pattern. 121 122 if local_filename and fnmatch.fnmatch(local_filename, pattern): 123 selected.append(local_filename) 124 125 return selected 126 127 def within(self, filename): 128 129 """ 130 Return the given 'filename' translated to be relative to this directory. 131 """ 132 133 return within(filename, self.filename) 134 135 # vim: tabstop=4 expandtab shiftwidth=4