"""
Implements the class :class:`FilePath` - a helper class for file path
iteration, extension detection etc.
"""
from __future__ import annotations
import inspect
import os.path
import platform
[docs]class FilePath:
"""
Helper class for handling file paths - such as detecting the path or the
extension of a file.
"""
SEP = os.path.sep
"The OS specific path separator"
[docs] @staticmethod
def dirname(filename: str, slash: bool = True) -> str:
"""
Returns the directory name of a file
As of now just a wrapper of os.path.dirname().
:param filename: The file's name
:param slash: If passed it will normalize the path to Unix style using slashes only
which is supported by Windows and Linux in most cases.
True by default.
:return: The directory the file is within
"""
res = os.path.dirname(filename)
if slash:
res = res.replace("\\", "/")
return res
[docs] @staticmethod
def norm_path(path: str, slash: bool = True) -> str:
"""
Normalizes a path, e.g. integrates relative path definitions such as ..
and . into the path.
As of now just a wrapper of os.path.normpath().
:param path: The path, e.g. /home/user/data/../documents
:param slash: If passed it will normalize the path to Unix style using slashes only
which is supported by Windows and Linux in most cases.
True by default.
:return: The "cleaned" path, e.g. The path, e.g. /home/user/documents
"""
res = os.path.normpath(path)
if slash:
res = res.replace("\\", "/")
return res
[docs] @staticmethod
def exists(path: str) -> bool:
"""
Returns if given path exists
As of now just a wrapper of os.path.exists().
:param path: The path name
:return: True if it exists
"""
return os.path.exists(path)
[docs] @staticmethod
def basename(path: str) -> str:
"""
Returns the path's base name (e.g the filename)
As of now just a wrapper of os.path.basename().
:param path: The path
:return: The element within the path, e.g. the file name
"""
return os.path.basename(path)
[docs] @classmethod
def script_filename(cls, level=1) -> str:
"""
Returns the file name of the calling method
:param level: The stack level relative to this function,
for internal use only. (+1 = caller, +2 = caller's caller etc.)
:return: The absolute filename of the script file
"""
return inspect.stack()[level].filename
[docs] @classmethod
def script_path(cls, level=1) -> str:
"""
Returns the file name of the calling method
:param level: The stack level relative to this function,
for internal use only. (+1 = caller, +2 = caller's caller etc.)
:return: The absolute filename of the script file
"""
return cls.dirname(inspect.stack()[level].filename)
[docs] @classmethod
def absolute(cls, path: str):
"""
Returns the absolute path for given relative path (relative to the
current getcwd() path.
As of now just a wrapper of os.path.abspath().
:param path: The relative path e.g. "./../data"
:return: The absolute path, e.g. "/home/user/scripts/data"
"""
return os.path.abspath(path).replace("\\", "/")
[docs] @classmethod
def absolute_comb(cls, rel_path: str, absolute_path: str | None = None):
"""
Returns the absolute, normalized path of a relative path object
combined with an absolute path object.
What makes this function pretty handy is that if no path is given the
calling script's path or Jupyter notebook's path will be used so
relative includes can be easily located.
:param rel_path: The (relative) path of which we want to determine the
absolute path of
:param absolute_path: The path at which we orient - has to be absolute.
If no path is passed the calling function or the location of the
executing Jupyter notebook will be used.
:return: The absolute path
"""
if absolute_path is None:
absolute_path = cls.script_path(level=2)
return cls.norm_path(os.path.join(absolute_path, rel_path))
[docs] @classmethod
def split_ext(cls, filename: str) -> tuple[str, str]:
"""
Returns the extension and file component of given file path
As of now just a wrapper of os.path.split_ext().
:param filename: The filename
:return: Tuple of filename and the file extension
(e.g. ("image", ".png")
"""
return tuple(os.path.splitext(filename))
[docs] @classmethod
def split_path_components(cls, path_name: str) -> list[str]:
"""
Returns the single path components as a list
As of now just a wrapper of os.path.split().
:param path_name: The filename or dir name
:return: The single path components
"""
# clean path
slashed = path_name.replace("\\", "/")
while "//" in slashed:
slashed = path_name.replace("//", "/")
return slashed.split("/")
[docs] @classmethod
def make_dirs(cls, path: str, exist_ok: bool = False) -> bool:
"""
Creates the defined directory and all directories in between
:param path: The path of the directory to create
:param exist_ok: Defines if the operation shall fail if the directory
already exists.
:return: True on success
"""
if os.path.exists(path) and not exist_ok:
return False
os.makedirs(path, exist_ok=True)
return True