def __new__(cls, *args, **kwargs): if kwargs.pop("expand", False): _ = expanduser(str(BasePath(*args, **kwargs))) p = BasePath(_, *args[1:], **kwargs).resolve() args = (str(p),) + args[1:] if kwargs.pop("create", False): BasePath(*args, **kwargs).mkdir(parents=True, exist_ok=True) return super(Path, cls).__new__(cls, *args, **kwargs)
class Path(BasePath): """ Extension of the base class Path from pathlib. :param expand: expand user's path :param create: create the directory if it doesn't exist """ _flavour = BasePath()._flavour # fix to AttributeError def __new__(cls, *parts, **kwargs): expand = kwargs.pop("expand", False) create = kwargs.pop("create", False) _ = super(Path, cls).__new__(cls, *parts, **kwargs) if expand: _ = _.expanduser().absolute() if create and not _.exists(): _.mkdir(parents=True) # exist_ok does not work in Python 2 return _ @property def basename(self): """ Dummy alias for name attribute. """ return self.name @property def bytes(self): """ Get file's content as bytes. """ with self.open('rb') as f: return f.read() @property def child(self): """ Get the child path relative to self's one. """ return Path(*self.parts[1:]) @property def filename(self): """ Get the file name, without the complete path. """ return self.stem + self.suffix @property def size(self): """ Get path's size. """ if self.is_file() or self.is_symlink(): return self.stat().st_size elif self.is_dir(): s = 4096 # include the size of the directory itself for root, dirs, files in os.walk(str(self)): s += 4096 * len(dirs) for f in files: s += os.stat(str(Path(root).joinpath(f))).st_size return s @property def text(self): """ Get file's content as a string. """ return self.read_text() def __add_text(self, data, mode='w', encoding=None, errors=None): """ Allows to write/append text to the file, both in Python 2 and 3. """ if not isinstance(data, string_types): raise TypeError("data must be str, not %s" % data.__class__.__name__) with self.open(mode=mode, encoding=encoding, errors=errors) as f: return f.write(u(data)) def append_bytes(self, data): """ Allows to append bytes to the file, as only write_bytes is available in pathlib, overwritting the former bytes at each write. """ with self.open(mode='ab') as f: return f.write(memoryview(data)) def append_line(self, line): """ Shortcut for appending a single line (text with newline). """ self.append_text(["\n", ""][self.size == 0] + line) def append_lines(self, *lines): """ Shortcut for appending a bunch of lines. """ for line in lines: self.append_line(line) def append_text(self, text, encoding=None, errors=None): """ Allows to append text to the file, as only write_text is available in pathlib, overwritting the former text at each write. """ return self.__add_text(text, 'a', encoding, errors) def choice(self, *filetypes): """ Return a random file from the current directory. """ if not self.is_dir(): return self filetypes = list(filetypes) while len(filetypes) > 0: filetype = choice(filetypes) filetypes.remove(filetype) l = list(self.iterfiles(filetype, filename_only=True)) if len(l) > 0: return self.joinpath(choice(l)) def expanduser(self): """ Fixed expanduser() method, working for both Python 2 and 3. """ return Path(os.path.expanduser(str(self))) def find(self, name=None, regex=False): """ Find a folder or file from the current path. """ if name is None: f = lambda p: True elif not regex: if "*" not in name: f = lambda p: p.basename == name else: r = "^{}$".format( name.replace(".", "\\.").replace("?", "\\?").replace( "-", "\\-").replace("+", "\\+").replace("[", "\\[").replace( "]", "\\]").replace("(", "\\(").replace( ")", "\\)").replace("{", "\\{").replace( "}", "\\}").replace("*", ".*")) f = lambda p: search(r, p.basename) is not None else: f = lambda p: search(name, p.basename) is not None for item in self.walk(filter_func=f): yield item def generate(self, prefix="", suffix="", length=8, alphabet="0123456789abcdef"): """ Generate a random folder name. """ if not self.is_dir(): return self while True: _ = "".join(choice(alphabet) for i in range(length)) new = self.joinpath(str(prefix) + _ + str(suffix)) if not new.exists(): return new def is_hidden(self): """ Check if the current path is hidden. """ if DARWIN: fnd = import_module("Foundation") u, f = fnd.NSURL.fileURLWithPath_(str(self)), fnd.NSURLIsHiddenKey return u.getResourceValue_forKey_error_(None, f, None)[1] elif LINUX: return self.stem.startswith(".") elif WINDOWS: import win32api, win32con return win32api.GetFileAttributes(p) & \ (win32con.FILE_ATTRIBUTE_HIDDEN | \ win32con.FILE_ATTRIBUTE_SYSTEM) raise NotImplementedError("Cannot check for the hidden status on this" " platform") def is_samepath(self, otherpath): """ Check if both paths have the same parts. """ return self.absolute().parts == Path(otherpath).absolute().parts def iterfiles(self, filetype=None, filename_only=False): """ List all files from the current directory. """ for i in self.iterdir(): if i.is_file() and (filetype is None or i.suffix == filetype): yield i.filename if filename_only else i def iterpubdir(self): """ List all visible subdirectories from the current directory. """ for i in self.iterdir(): if i.is_dir() and not i.is_hidden(): yield i def listdir(self, filter_func=lambda p: True, sort=True): """ List the current path using the given filter. """ l = os.listdir(str(self)) if sort: l = sorted(l) for item in l: item = self.joinpath(item) if filter_func(item): yield item def mkdir(self, mode=0o777, parents=False, exist_ok=False): """ Fix to non-existing argument exist_ok in Python 2. """ arg = (exist_ok, ) if PYTHON3 else () super(Path, self).mkdir(mode, parents, *arg) def read_lines(self, encoding=None, errors=None): """ Extra method for reading a file as lines. """ for l in self.read_text(encoding, errors).splitlines(): yield l def read_text(self, encoding=None, errors=None): """ Fix to non-existing method in Python 2. """ with self.open(mode='r', encoding=encoding, errors=errors) as f: return f.read() def reset(self): """ Ensure the file exists and is empty. """ with self.open('w') as f: pass def remove(self): """ Extension for removing a directory or a file. """ if self.is_dir(): rmtree(str(self)) else: os.remove(str(self)) def walk(self, breadthfirst=True, filter_func=lambda p: True, sort=True): """ Walk the current path for directories and files using os.listdir(), breadth-first or depth-first, sorted or not, based on a filter function. """ if breadthfirst: for item in self.listdir(lambda p: not p.is_dir(), sort): if filter_func(item): yield item for item in self.listdir(lambda p: p.is_dir(), sort): if breadthfirst and filter_func(item): yield item for subitem in item.walk(breadthfirst, filter_func): yield subitem if not breadthfirst and filter_func(item): yield item if not breadthfirst: for item in self.listdir(lambda p: not p.is_dir(), sort): if filter_func(item): yield item def write_text(self, data, encoding=None, errors=None): """ Fix to non-existing method in Python 2. """ return self.__add_text(data, 'w', encoding, errors)
class Path(type(BasePath())): def ls(self): """When the path points to a directory, return a list of path objects of the directory contents """ return [item for item in self.iterdir()]
from pathlib import Path as BasePath from shutil import copytree, copy2 as copy PathType = type(BasePath()) PathCopy = 'project.core.path.Path' class Path(PathType): def copy(self, destination: BasePath) -> None: if self.is_dir(): func = copytree destination /= self.name else: func = copy src = str(self.resolve()) dest = str(destination.resolve()) func(src, dest) def clone(self) -> PathCopy: return type(self)(self)
class Path(BasePath): """ Extension of the base class Path from pathlib. :param expand: expand user's path :param create: create the directory if it doesn't exist :param touch: create the file if it doesn't exist (mutually exclusive with 'create') """ _flavour = BasePath()._flavour # fix to AttributeError def __new__(cls, *parts, **kwargs): expand = kwargs.pop("expand", False) create = kwargs.pop("create", False) touch = kwargs.pop("touch", False) p = super(Path, cls).__new__(cls, *parts, **kwargs) if expand: p = super(Path, cls).__new__(cls, str(p.expanduser().absolute()), **kwargs) if create and touch: raise ValueError("Conflicting options ; 'create' creates a folder hwile 'touch' creates a file") elif (create or touch) and not p.exists(): if create: p.mkdir(parents=True) # exist_ok does not work in Python 2 elif touch: p.touch() return p @property def basename(self): """ Dummy alias for name attribute. """ return self.name @property def bytes(self): """ Get file's content as bytes. """ with self.open('rb') as f: return f.read() @property def child(self): """ Get the child path relative to self's one. """ return Path(*self.parts[1:]) @property def dirname(self): """ Get the directory name. """ return self if self.is_dir() else self.parent @property def extension(self): """ Get the extension based on the stem and suffix. """ return self.suffix @property def filename(self): """ Get the file name, without the complete path. """ return self.basename @property def mime_type(self): """ Get the MIME type of the current Path object. """ return guess_type(str(self))[0] @property def permissions(self): """ Get the permissions of the current Path object. """ return os.stat(str(self)).st_mode @property def size(self): """ Get path's size. """ if self.is_file() or self.is_symlink(): return self.stat().st_size elif self.is_dir(): s = 4096 # include the size of the directory itself for root, dirs, files in os.walk(str(self)): s += 4096 * len(dirs) for f in files: s += os.stat(str(Path(root).joinpath(f))).st_size return s @property def stem(self): """ Stem also handling some common double extensions. """ try: return DOUBLE_EXT.search(self.basename).group(1) except AttributeError: return super(Path, self).stem @property def suffix(self): """ Suffix also handling some common double extensions. """ try: return DOUBLE_EXT.search(self.basename).group(2) except AttributeError: return super(Path, self).suffix @property def text(self): """ Get file's content as a string. """ return self.read_text() def __add_text(self, data, mode='w', encoding=None, errors=None): """ Allows to write/append text to the file, both in Python 2 and 3. """ if not isinstance(data, string_types): raise TypeError("data must be str, not %s" % data.__class__.__name__) with self.open(mode=mode, encoding=encoding, errors=errors) as f: return f.write(u(data)) def append_bytes(self, data): """ Allows to append bytes to the file, as only write_bytes is available in pathlib, overwritting the former bytes at each write. """ with self.open(mode='ab') as f: return f.write(memoryview(data)) def append_line(self, line): """ Shortcut for appending a single line (text with newline). """ self.append_text(["\n", ""][self.size == 0] + line) def append_lines(self, *lines): """ Shortcut for appending a bunch of lines. """ for line in lines: self.append_line(line) def append_text(self, text, encoding=None, errors=None): """ Allows to append text to the file, as only write_text is available in pathlib, overwritting the former text at each write. """ return self.__add_text(text, 'a', encoding, errors) def choice(self, *filetypes): """ Return a random file from the current directory. """ if not self.is_dir(): return self filetypes = list(filetypes) while len(filetypes) > 0: filetype = choice(filetypes) filetypes.remove(filetype) l = list(self.iterfiles(filetype, filename_only=True)) if len(l) > 0: return self.joinpath(choice(l)) def copy(self, new_path, **kwargs): """ Copy this folder or file to the given destination. """ try: copytree(str(self), str(new_path), **kwargs) except OSError as e: # does not use NotADirectoryError as it is only available from Python3 if e.errno == errno.ENOTDIR: (copy2 if kwargs.pop('metadata', True) else copy)(str(self), str(new_path), **kwargs) else: return self return self.__class__(new_path) def expanduser(self): """ Fixed expanduser() method, working for both Python 2 and 3. """ return Path(os.path.expanduser(str(self))) def find(self, name=None, regex=False): """ Find a folder or file from the current path. """ if name is None: f = lambda p: True elif not regex: if "*" not in name: f = lambda p: p.basename == name else: r = "^{}$".format(name.replace(".", "\\.").replace("?", "\\?").replace("-", "\\-").replace("+", "\\+") .replace("[", "\\[").replace("]", "\\]").replace("(", "\\(").replace(")", "\\)") .replace("{", "\\{").replace("}", "\\}").replace("*", ".*")) f = lambda p: search(r, p.basename) is not None else: f = lambda p: search(name, p.basename) is not None for item in self.walk(filter_func=f): yield item def generate(self, prefix="", suffix="", length=8, alphabet="0123456789abcdef"): """ Generate a random folder name. """ # simply return self if it exists and it is not a directory if self.exists() and not self.is_dir(): return self # ensure this is a newly generated path while True: new = self.joinpath(str(prefix) + "".join(choice(alphabet) for i in range(length)) + str(suffix)) if not new.exists(): return new rand_folder_name = generate def is_hidden(self): """ Check if the current path is hidden. """ if DARWIN: fnd = importlib.import_module("Foundation") u, f = fnd.NSURL.fileURLWithPath_(str(self)), fnd.NSURLIsHiddenKey return u.getResourceValue_forKey_error_(None, f, None)[1] elif LINUX: return self.stem.startswith(".") elif WINDOWS: import win32api, win32con return win32api.GetFileAttributes(p) & (win32con.FILE_ATTRIBUTE_HIDDEN | win32con.FILE_ATTRIBUTE_SYSTEM) raise NotImplementedError("Cannot check for the hidden status on this platform") def is_samepath(self, otherpath): """ Check if both paths have the same parts. """ return self.absolute().parts == Path(otherpath).absolute().parts def is_under(self, parentpath): """ Check if the path is under a parent path. """ p = Path(parentpath) if not p.is_dir(): p = Path(p.dirname) return p in self.parents def iterfiles(self, filetype=None, filename_only=False, relative=False): """ List all files from the current directory. """ for i in self.iterdir(): if i.is_file() and (filetype is None or i.suffix == filetype): yield i.filename if filename_only else i.relative_to(self) if relative else i def iterpubdir(self): """ List all visible subdirectories from the current directory. """ for i in self.iterdir(): if i.is_dir() and not i.is_hidden(): yield i def listdir(self, filter_func=lambda p: True, sort=True): """ List the current path using the given filter. """ l = os.listdir(str(self)) if sort: l = sorted(l) for item in l: item = self.joinpath(item) if filter_func(item): yield item def mkdir(self, mode=0o777, parents=False, exist_ok=False): """ Fix to non-existing argument exist_ok in Python 2. """ arg = (exist_ok, ) if PYTHON3 else () super(Path, self).mkdir(mode, parents, *arg) def read_lines(self, encoding=None, errors=None): """ Extra method for reading a file as lines. """ for l in self.read_text(encoding, errors).splitlines(): yield l def read_text(self, encoding=None, errors=None): """ Fix to non-existing method in Python 2. """ with self.open(mode='r', encoding=encoding, errors=errors) as f: return f.read() def reset(self): """ Ensure the file exists and is empty. """ with self.open('w') as f: pass def remove(self, error=True): """ Extension for removing a directory or a file. """ try: rmtree(str(self)) if self.is_dir() else os.remove(str(self)) except OSError: if error: raise def walk(self, breadthfirst=True, filter_func=lambda p: True, sort=True, base_cls=True, relative=False): """ Walk the current path for directories and files using os.listdir(), breadth-first or depth-first, sorted or not, based on a filter function. """ rel = lambda i: i.relative_to(self) if relative else i out = lambda i: Path(str(i)) if base_cls else i if breadthfirst: for item in self.listdir(lambda p: not p.is_dir(), sort): if filter_func(item): yield out(rel(item)) for item in self.listdir(lambda p: p.is_dir(), sort): if self.is_symlink() and self.resolve() == self.parent: continue # e.g.: /usr/bin/X11 -> /usr/bin if breadthfirst and filter_func(item): yield out(rel(item)) for subitem in item.walk(breadthfirst, filter_func, sort, base_cls): yield out(rel(subitem)) if not breadthfirst and filter_func(item): yield out(rel(item)) if not breadthfirst: for item in self.listdir(lambda p: not p.is_dir(), sort): if filter_func(item): yield out(rel(item)) def write_bytes(self, data): """ Fix to non-existing method in Python 2. """ with self.open(mode='wb') as f: return f.write(memoryview(data)) def write_text(self, data, encoding=None, errors=None): """ Fix to non-existing method in Python 2. """ return self.__add_text(data, 'w', encoding, errors)
def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = BasePath(app('BASE_PATH')) return cls._instance
class Path(BasePath): """ Extension of the base class Path from pathlib. """ _flavour = BasePath()._flavour # fix to AttributeError def __new__(cls, *args, **kwargs): if kwargs.pop("expand", False): _ = expanduser(str(BasePath(*args, **kwargs))) p = BasePath(_, *args[1:], **kwargs).resolve() args = (str(p),) + args[1:] if kwargs.pop("create", False): BasePath(*args, **kwargs).mkdir(parents=True, exist_ok=True) return super(Path, cls).__new__(cls, *args, **kwargs) @property def child(self): """ Get the child path relative to self's one. """ return Path(*self.parts[1:]) @property def filename(self): """ Get the file name, without the complete path. """ return self.stem + self.suffix @property def size(self): """ Get path's size. """ if self.is_file() or self.is_symlink(): return self.stat().st_size elif self.is_dir(): s = 4096 # include the size of the directory itself for root, dirs, files in os.walk(str(self)): s += 4096 * len(dirs) for f in files: s += os.stat(str(Path(root).joinpath(f))).st_size return s raise AttributeError("object 'Path' has no attribute 'size'") def append_bytes(self, text): """ Allows to append bytes to the file, as only write_bytes is available in pathlib, overwritting the former bytes at each write. """ with open(str(self), 'ab') as f: f.write(text) def append_line(self, line): """ Shortcut for appending a single line (text with newline). """ self.append_text(line + '\n') def append_lines(self, *lines): """ Shortcut for appending a bunch of lines. """ for line in lines: self.append_line(line) def append_text(self, text): """ Allows to append text to the file, as only write_text is available in pathlib, overwritting the former text at each write. """ with open(str(self), 'a') as f: f.write(text) def choice(self, *filetypes): """ Return a random file from the current directory. """ filetypes = list(filetypes) while len(filetypes) > 0: filetype = random.choice(filetypes) filetypes.remove(filetype) l = list(self.iterfiles(filetype, filename_only=True)) try: return self.joinpath(random.choice(l)) except: continue def expanduser(self): """ Fixed expanduser() method, working for both Python 2 and 3. """ return Path(expanduser(str(self))) def generate(self, prefix="", suffix="", length=8, alphabet="0123456789abcdef"): """ Generate a random folder name. """ rname = "".join(random.choice(alphabet) for i in range(length)) return self.joinpath(prefix + rname + suffix) def iterpubdir(self): """ List all public subdirectories from the current directory. """ for i in self.iterdir(): if i.is_dir() and not i.stem.startswith("."): yield i def iterfiles(self, filetype=None, filename_only=False, relative=False): """ List all files from the current directory. """ for i in self.iterdir(): if i.is_file(): if filetype is None or i.suffix == filetype: yield i.filename if filename_only else \ i.relative_to(self) if relative else i def read_text(self): """ Fix to non-existing method in Python 2. """ try: super(Path, self).read_text() except AttributeError: # occurs with Python 2 ; no write_text method with open(str(self), 'r') as f: c = f.read() return c def reset(self): """ Ensure the file exists and is empty. """ if self.exists(): self.unlink() self.touch() def rmtree(self): """ Extension for recursively removing a directory. """ shutil.rmtree(str(self)) def samepath(self, otherpath): """ Check if both paths have the same parts. """ return self.parts == otherpath.parts def write_text(self, text): """ Fix to non-existing method in Python 2. """ try: super(Path, self).write_text(text) except AttributeError: # occurs with Python 2 ; no write_text method with open(str(self), 'w+') as f: f.write(text)