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) path = get_path_casing(path) self.absolute_path = path def __contains__(self, other): return other.absolute_path.startswith(self.absolute_path) def __hash__(self): return hash(self.absolute_path) @property def basename(self): return os.path.basename(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 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) subpath = subpath.lstrip(os.sep) def patternize(piece): ''' Create a pattern like "[u]ser" from "user", forcing glob to look up the correct path name, and 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 pattern = [patternize(piece) for piece in subpath.split(os.sep)] pattern = os.sep.join(pattern) pattern = drive.upper() + os.sep + pattern #print(pattern) try: return glob.glob(pattern)[0] except IndexError: return path