Source code for oasislmf.utils.path
__all__ = [
'as_path',
'empty_dir',
'PathCleaner',
'import_from_string',
'get_custom_module',
'setcwd'
]
import os
import sys
import importlib
import re
import shutil
from contextlib import contextmanager
from .exceptions import OasisException
[docs]
def as_path(path, label, is_dir=False, preexists=True, null_is_valid=True):
"""
Processes the path and returns the absolute path.
If the path does not exist and ``preexists`` is true
an ``OasisException`` is raised.
:param path: The path to process
:type path: str
:param label: Human-readable label of the path (used for error reporting)
:type label: str
:param is_dir: Whether the path is a directory
:type is_dir: bool
:param preexists: Flag whether to raise an error if the path
does not exist.
:type preexists: bool
:param null_is_valid: flag to indicate if None is a valid value
:type null_is_valid: bool
:return: The absolute path of the input path
"""
if path is None and null_is_valid:
return
if not isinstance(path, str):
if preexists:
raise OasisException(f'The path {path} ({label}) is indicated as preexisting but is not a valid path')
else:
return
if not os.path.isabs(path):
path = os.path.abspath(path)
if preexists and not os.path.exists(path):
raise OasisException(f'The path {path} ({label}) is indicated as preexisting but does not exist')
if is_dir and preexists and not os.path.isdir(path):
raise OasisException(f'The path {path} ({label}) is indicated as a preexisting directory but is not actually a directory')
return os.path.normpath(path)
[docs]
def empty_dir(dir_fp):
"""
Empties the contents of a directory, but leaves the directory in place.
:param dir_fp: A pre-existing directory path
:type dir_fp: str
"""
_dir_fp = as_path(dir_fp, dir_fp, is_dir=True)
for p in (os.path.join(_dir_fp, fn) for fn in os.listdir(_dir_fp)):
os.remove(p) if os.path.isfile(p) else (shutil.rmtree(p) if os.path.isdir(p) else None)
[docs]
class PathCleaner(object):
"""
A callable that generates the absolute path of the given path and checks
that it exists if indicated as preexisting.
:param label: A user-friendly label for the path (used for error reporting)
:type label: str
:param preexists: Flag whether to raise an error if the path
does not exist.
:type preexists: bool
"""
def __init__(self, label, preexists=True):
[docs]
self.preexists = preexists
def __call__(self, path):
return as_path(path, self.label, preexists=self.preexists)
[docs]
def import_from_string(name):
"""
return the object or module from the path given
>>> import os.path
>>> mod = import_from_string('os.path')
>>> os.path is mod
True
>>> from os.path import isabs
>>> cls = import_from_string('os.path.isabs')
>>> isabs is cls
True
"""
components = name.split('.')
res = __import__(components[0])
for comp in components[1:]:
res = getattr(res, comp)
return res
[docs]
def get_custom_module(custom_module_path, label):
"""
return the custom module present at the custom_module_path.
the try loop allow for the custom module to work even if it depends on other module of its package
by testing recursively for the presence of __init__.py file
(ex: this module "path" is using from .exceptions import OasisException so it can only be imported as part of the
utils package => sys.path.insert(0, path_to_utils);importlib.import_module('utils.path'))
>>> mod = get_custom_module(__file__, "test module")
>>> mod.__name__.rsplit('.', 1)[-1]
'path'
"""
custom_module_path = as_path(custom_module_path, label, preexists=True, null_is_valid=False)
package_dir = os.path.dirname(custom_module_path)
module_name = re.sub(r'\.py$', '', os.path.basename(custom_module_path))
while True:
sys.path.insert(0, package_dir)
try:
custom_module = importlib.import_module(module_name)
importlib.reload(custom_module)
return custom_module
except ImportError:
if '__init__.py' in os.listdir(package_dir):
module_name = os.path.basename(package_dir) + '.' + module_name
package_dir, old_package_dir = os.path.dirname(package_dir), package_dir
if package_dir == old_package_dir:
raise
else:
raise
finally:
sys.path.pop(0)
@contextmanager
[docs]
def setcwd(path):
orig_path = os.getcwd()
try:
os.chdir(str(path))
yield path
finally:
os.chdir(orig_path)
if __name__ == '__main__':
import doctest
doctest.testmod()