import glob import os class Path: ''' I started to use pathlib.Path, but it was too much of a pain. ''' def __init__(self, path): path = os.path.normpath(path) path = os.path.abspath(path) self.absolute_path = path def __contains__(self, other): this = os.path.normcase(self.absolute_path) that = os.path.normcase(other.absolute_path) return that.startswith(this) def __eq__(self, other): if not hasattr(other, 'absolute_path'): return False this = os.path.normcase(self.absolute_path) that = os.path.normcase(other.absolute_path) return this == that def __hash__(self): return hash(os.path.normcase(self.absolute_path)) def __repr__(self): return '{c}({path})'.format(c=self.__class__.__name__, path=repr(self.absolute_path)) @property def basename(self): return os.path.basename(self.absolute_path) def correct_case(self): self.absolute_path = get_path_casing(self.absolute_path) return self.absolute_path @property def exists(self): return os.path.exists(self.absolute_path) @property def is_dir(self): return os.path.isdir(self.absolute_path) @property def is_file(self): return os.path.isfile(self.absolute_path) @property def is_link(self): return os.path.islink(self.absolute_path) @property def parent(self): parent = os.path.dirname(self.absolute_path) parent = self.__class__(parent) return parent @property def relative_path(self): relative = self.absolute_path relative = relative.replace(os.getcwd(), '') relative = relative.lstrip(os.sep) return relative @property def size(self): if self.is_file: return os.path.getsize(self.absolute_path) else: return None @property def stat(self): return os.stat(self.absolute_path) def with_child(self, basename): basename = os.path.basename(basename) return Path(os.path.join(self.absolute_path, basename)) def get_path_casing(path): ''' Take what is perhaps incorrectly cased input and get the path's actual casing according to the filesystem. Thank you: Ethan Furman http://stackoverflow.com/a/7133137/5430534 xvorsx http://stackoverflow.com/a/14742779/5430534 ''' if isinstance(path, Path): path = path.absolute_path (drive, subpath) = os.path.splitdrive(path) drive = drive.upper() subpath = subpath.lstrip(os.sep) pattern = [glob_patternize(piece) for piece in subpath.split(os.sep)] pattern = os.sep.join(pattern) pattern = drive + os.sep + pattern #print(pattern) try: return glob.glob(pattern)[0] except IndexError: return path def glob_patternize(piece): ''' Create a pattern like "[u]ser" from "user", forcing glob to look up the correct path name, while guaranteeing that the only result will be the correct path. Special cases are: !, because in glob syntax, [!x] tells glob to look for paths that don't contain "x". [!] is invalid syntax, so we pick the first non-! character to put in the brackets. [, because this starts a capture group ''' piece = glob.escape(piece) for character in piece: if character not in '![]': replacement = '[%s]' % character #print(piece, character, replacement) piece = piece.replace(character, replacement, 1) break return piece