"""File path utilitiesSee the source code on Github: `mltk/utils/path.py <https://github.com/siliconlabs/mltk/blob/master/mltk/utils/path.py>`_"""importosimporttimeimportreimportglobimportshutilimporttempfileimportdatetimeimportsubprocessimportcontextlibfrompathlibimportPathfromtypingimportCallable,List,Union,Iterator,Tuplefrom.systemimportget_username# This is the base directory used for temporary files# It includes the the current user's name in the path# to separate temp files for different user's that use the same tempdirTEMP_BASE_DIR=os.environ.get('MLTK_TEMP_DIR',f'{tempfile.gettempdir()}/{get_username()}/mltk')
[docs]deffullpath(path:str,cwd:str=None)->str:"""Return the full, normalized path of the given path"""orig_cwd=os.getcwd()ifcwd:os.chdir(cwd)try:path=os.path.expandvars(path)path=os.path.expanduser(path)path=os.path.normpath(path)path=os.path.abspath(path)finally:os.chdir(orig_cwd)# Return the filepath as it actually exists on the FS# If the path doesn't exist, then just return the normalized pathpath=get_actual_path(path)orpathreturnpath.replace('\\','/')
[docs]defget_actual_path(path:str):"""Return the file path as it actually appears on the FS (including upper/lower case) Return None if the path doesn't exist """try:dirs=path.split('\\')# disk lettertest_name=[dirs[0].upper()]fordindirs[1:]:test_name+=["%s[%s]"%(d[:-1],d[-1])]res=glob.glob('\\'.join(test_name))ifnotres:#File not foundreturnNonereturnres[0]except:returnNone
[docs]defextension(path:str)->str:"""Return the extension of the given path"""idx=path.rfind('.')ifidx==-1:return''ifidx==0:return''returnpath[idx+1:]
[docs]defhas_filename(path:str)->bool:"""Return if the given path has a filename with an extension or is only a directory path"""ifnotpath:returnFalsepath=fullpath(path)last_slash_index=path.rfind('/')ext_index=path[last_slash_index+1:].rfind('.')returnext_index>0
[docs]defcreate_dir(path:str)->str:"""Create the given path's directories"""path=fullpath(path)ifhas_filename(path):path=os.path.dirname(path)ifnotpath:returnNoneos.makedirs(path,exist_ok=True)returnpath
[docs]defcreate_tempdir(subdir='')->str:"""Create a temporary directory as <temp dir>/<username>/mltk"""d=TEMP_BASE_DIRifsubdir:subdir=subdir.replace('\\','/')ifnotsubdir.startswith('/'):d+=f'/{subdir}'d=fullpath(d)os.makedirs(d,exist_ok=True)returnd
[docs]defcreate_user_dir(suffix:str='',base_dir:str=None)->str:"""Create a user directory This creates the directory in one of the following base directories based on availability: - base_dir argument - OS environment variable: MLTK_CACHE_DIR - ~/.mltk - <user temp dir>/<username>/mltk Args: suffix: Optional suffix to append to the base directory base_dir: Optional base directory, default to MLTK_CACHE_DIR, ~/.mltk, or <user temp dir>/<user name>/mltk if omitted Returns: path to created directory """ifsuffix:suffix=suffix.replace('\\','/')ifnotsuffix.startswith('/'):suffix=f'/{suffix}'is_read_only=os.environ.get('MLTK_READONLY')user_dir=base_dirifbase_direlseos.environ.get('MLTK_CACHE_DIR','~/.mltk')user_dir=fullpath(user_dir+suffix)# If the MLTK_READONLY environment variable is set# then don't check if the user_dir directory is writableifnotis_read_only:try:# Try to create the directory in MLTK_CACHE_DIR or ~/.mltk# if we have permissionos.makedirs(user_dir,exist_ok=True)ifnotos.access(user_dir,os.W_OK):raiseException()except:# Otherwise just create in the temp directoryuser_dir=create_tempdir(suffix)returnuser_dir
[docs]defget_user_setting(name:str,default=None):"""Return the value of a user setting if it exists User settings are defined in the file: - Environment variable: MLTK_USER_SETTINGS_PATH - OR <user home>/.mltk/user_settings.yaml User settings include: - model_paths: list of directories to search for MLTK models - commander: Simplicity Commander options device: Device code serial_number: Adapter serial number ip_address: Adapter IP address See `settings_file <https://siliconlabs.github.io/mltk/docs/other/settings_file.html>`_ """user_settings_path=fullpath(os.environ.get('MLTK_USER_SETTINGS_PATH','~/.mltk/user_settings.yaml'))ifnotos.path.exists(user_settings_path):returndefaulttry:# Import the YAML package here# in-case it's not installed yetimportyamlwithopen(user_settings_path,'r')asfp:user_settings=yaml.load(fp,Loader=yaml.SafeLoader)exceptExceptionase:ifnot'_printed_user_settings_error'inglobals():globals()['_printed_user_settings_error']=Trueprint(f'WARN: Failed to parse {user_settings_path}, err: {e}')returndefaultifuser_settingsandnameinuser_settings:returnuser_settings[name]returndefault
[docs]defadd_user_setting(name:str,value:object):"""Add an entry to the user settings User settings are defined in the file: <user home>/.mltk/user_settings.yaml """# Import the YAML package here# in-case it's not installed yetimportyamluser_settings_paths=fullpath('~/.mltk/user_settings.yaml')ifos.path.exists(user_settings_paths):withopen(user_settings_paths,'r')asfp:user_settings=yaml.load(fp,Loader=yaml.SafeLoader)else:user_settings=dict()user_settings[name]=valuewithopen(user_settings_paths,'w')asf:yaml.dump(user_settings,f,Dumper=yaml.SafeDumper)
[docs]defremove_directory(path:str):"""Remove the directory at the given path This will remove non-empty directories and retry a few times if necessary """ifnotpath:returndef_remove_dir(d):ifos.name=='nt':subprocess.check_output(['cmd','/C','rmdir','/S','/Q',os.path.abspath(d)])else:subprocess.check_output(['rm','-rf',os.path.abspath(d)])retries=5whileretries>0andos.path.exists(path):try:_remove_dir(path)except:passfinally:time.sleep(.001)retries-=1
[docs]defclean_directory(path:str):"""Remove all files within directory and subdirectories """forroot,_,filesinos.walk(path):forfninfiles:p=f'{root}/{fn}'for_inrange(3):try:os.remove(p)breakexcept:time.sleep(.001)
[docs]defcopy_directory(src,dst,exclude_dirs=None):"""Recursively copy a directory. Only copy files that are new or out-dated"""ifexclude_dirs:forxinexclude_dirs:ifsrc.replace('\\','/').startswith(x.replace('\\','/')):returnifnotos.path.exists(dst):os.makedirs(dst)foriteminos.listdir(src):s=os.path.join(src,item)d=os.path.join(dst,item)ifos.path.isdir(s):copy_directory(s,d,exclude_dirs=exclude_dirs)else:ifnotos.path.exists(d)oros.stat(s).st_mtime-os.stat(d).st_mtime>1:shutil.copy2(s,d)
[docs]deffile_is_in_use(file_path:str)->bool:"""Return if the file is currently opened"""path=Path(file_path)ifnotpath.exists():raiseFileNotFoundErrortry:path.rename(path)exceptPermissionError:returnTrueelse:returnFalse
[docs]defrecursive_listdir(base_dir:str,followlinks=True,regex:Union[str,re.Pattern,Callable[[str],bool]]=None,return_relative_paths:bool=False)->List[str]:"""Return list of all files recursively found in base_dir Args: base_dir: The base directory to recursively search followlinks: IF true then follow symbolic links regex: Optional regex of file paths to INCLUDE in the returned list This can either be a string, re.Pattern, or a callback function If return_relative_paths=False then the tested path is the absolute path with forward slashes If return_relative_paths=True then the tested path is the path relative to the base_dir with forward slashes If a callback function is given, if the function returns True then the path is INCLUDED, else it is excluded return_relative_paths: If true then return paths relative to the base_dir, else return absolute paths Returns: List of file paths with forward slashes for directory delimiters """base_dir=fullpath(base_dir)ifregexisnotNone:ifisinstance(regex,str):regex=re.compile(regex)regex_func=regex.matchelifisinstance(regex,re.Pattern):regex_func=regex.matchelse:regex_func=regexelse:regex_func=lambdap:Trueretval=[]forroot,_,filesinos.walk(base_dir,followlinks=followlinks):forfninfiles:p=os.path.join(root,fn).replace('\\','/')ifreturn_relative_paths:p=os.path.relpath(p,base_dir).replace('\\','/')ifnotregex_func(p):continueretval.append(p)returnretval
[docs]defwalk_with_depth(base_dir:str,depth=1,followlinks=True,)->Iterator[Tuple[str,List[str],List[str]]]:"""Walk a directory with a max depth. This is similar to os.walk except it has an optional maximum directory depth that it will walk """base_dir=base_dir.rstrip(os.path.sep)assertos.path.isdir(base_dir)num_sep=base_dir.count(os.path.sep)forroot,dirs,filesinos.walk(base_dir,followlinks=followlinks):yieldroot,dirs,filesnum_sep_this=root.count(os.path.sep)ifnum_sep+depth<=num_sep_this:deldirs[:]
[docs]@contextlib.contextmanagerdefpushd(new_dir:str):"""Change to the given directory, execute, and return to the previous directory Example: print(f'Old path: {os.getcwd()}') with pushd('some/path') print(f'New path: {os.getcwd()}') print(f'Old path: {os.getcwd()}') """old_dir=os.getcwd()os.chdir(new_dir)try:yieldfinally:os.chdir(old_dir)
Important: We use cookies only for functional and traffic analytics.
We DO NOT use cookies for any marketing purposes. By using our site you acknowledge you have read and understood our Cookie Policy.