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