Source code for pylablib.core.utils.files

"""
Utilities for working with the file system: creating/removing/listing folders, comparing folders and files, working with zip archives.
"""

# from io import open
from builtins import range

from . import general, string, funcargparse

try:
    from Tkinter import Tk
    from tkFileDialog import askopenfilename, asksaveasfilename, askdirectory
except ImportError:
    try:
        from tkinter import Tk
        from tkinter.filedialog import askopenfilename, asksaveasfilename, askdirectory
    except ImportError:
        pass

import os
import os.path
import errno
import stat
import filecmp
import shutil
import datetime
import time
import zipfile
import collections




### General routines ###
[docs]def eof(f, strict=False): """ Standard EOF function. Return ``True`` if the the marker is at the end of the file. If ``strict==True``, only return ``True`` if the marker is exactly at the end of file; otherwise, return ``True`` if it's at the end of further. """ p=f.tell() f.seek(0,2) ep=f.tell() f.seek(p) return (ep==p) or (ep<=p and not strict)
[docs]def get_file_creation_time(path, timestamp=True): """ Try to find a file creation time. Return current time if an error occurs. If ``timestamp==True``, return UNIX timestamp; otherwise, return :class:`datetime.datetime`. """ if not timestamp: return datetime.datetime.fromtimestamp(get_file_creation_time(path,timestamp=True)) try: return os.path.getctime(path) except: return time.time()
[docs]def get_file_modification_time(path, timestamp=True): """ Try to find a file modification time. Return current time if an error occurs. If ``timestamp==True``, return UNIX timestamp; otherwise, return :class:`datetime.datetime` """ if not timestamp: return datetime.datetime.fromtimestamp(get_file_modification_time(path,timestamp=True)) try: return os.path.getmtime(path) except: return time.time()
[docs]def touch(fname, times=None): """ Update file access and modification times. Args: times(tuple): Access and modification times; if `times` is ``None``, use current time. """ with open(fname,'ab'): os.utime(fname,times)
### Working with paths ###
[docs]def generate_indexed_filename(name_format, idx_start=0, folder=""): """ Generate an unused indexed filename in `folder`. The name has `name_format` (using standard Python :func:`format()` rules), with index starting with `idx_start`. """ while True: name=name_format.format(idx_start) if not os.path.exists(os.path.join(folder,name)): return name idx_start=idx_start+1
[docs]def generate_prefix_filename(prefix="", suffix="", idx_start=None, folder=""): """ Generate an unused filename in `folder` with the given prefix and suffix. The format is ``prefix_{:d}_suffix``, where the parameter is the index starting with `idx_start`. If `idx_start` is ``None`` first check ``prefix+suffix`` name before using numbered indices. """ if idx_start is None: name=prefix+suffix if not os.path.exists(os.path.join(folder,name)): return name idx_start=0 return generate_indexed_filename(prefix+"_{:d}"+suffix,idx_start=idx_start,folder=folder)
[docs]def generate_temp_filename(prefix="__tmp__", idx_start=0, idx_template="d", folder=""): """ Generate a temporary filename with a given prefix. `idx_template` is the number index format (only the parameter itself, not the whole string). """ name_format=prefix+"{:"+idx_template+"}" return generate_indexed_filename(name_format=name_format,idx_start=idx_start,folder=folder)
[docs]def fullsplit(path, ignore_empty=True): """ Split path into list. If ``ignore_empty==True``, omit empty folder names. """ names=[] while True: path,name=os.path.split(path) if name!="" or not ignore_empty: names.append(name) if path=="" or path[-1] in "\\/": if not all([e in "\\/" for e in path]): names.append(path) break return names[::-1]
[docs]def normalize_path(p): """Normalize filesystem path (case and origin). If two paths are identical, they should be equal when normalized.""" return os.path.normcase(os.path.abspath(p))
[docs]def case_sensitive_path(): """Check if OS path names are case-sensitive (e.g., Linux)""" return os.path.normcase("TEMP")!="temp"
[docs]def paths_equal(a, b): """ Determine if the two paths are equal (can be local or have different case). """ a_norm=normalize_path(a) b_norm=normalize_path(b) return a_norm==b_norm
[docs]def relative_path(a, b, check_paths=True): """ Determine return path `a` as seen from `b`. If ``check_paths==True``, check if `a` is contained in `b` and raise the :exc:OSError if it isn't. """ a_split=fullsplit(os.path.abspath(a)) b_split=fullsplit(os.path.abspath(b)) blen=len(b_split) if check_paths and not ( paths_equal(os.path.join(*a_split[:blen]),os.path.join(*b_split)) and len(a_split)>=blen ): raise OSError("path {0} is not contained in a path {1}".format(a,b)) if len(a_split)==blen: return "" return os.path.join(*a_split[blen:])
### Temp file ###
[docs]class TempFile(object): """ Temporary file context manager. Upon creation, generate an unused temporary filename. Upon entry, create the file using supplied mode and return self. Upon exit, close and remove the file. Args: folder (str): Containing folder. name (str): File name. If ``None``, generate new temporary name. mode (str): File opening mode. wait_time (float): Waiting time between attempts to create the file if the first try fails. rep_time (int): Number of attempts to create the file if the first try fails. Attributes: f: File object. name (str): File name. full_name (str): File name including containing folder. """ _default_wait_time=0.3 _default_rep_time=5 def __init__(self, folder="", name=None, mode="w", wait_time=None, rep_time=None): self.folder=folder if name is None: name=generate_temp_filename(folder=folder) self.name=name self.full_name=os.path.join(self.folder,self.name) self.wait_time=self._default_wait_time if wait_time is None else wait_time self.rep_time=self._default_rep_time if rep_time is None else rep_time self.mode=mode self.f=None def __enter__(self): self.f=general.retry_wait(lambda: open(self.full_name,mode=self.mode),self.rep_time,self.wait_time) self.f=self.f.__enter__() if self.wait_time!=0: time.sleep(self.wait_time) return self def __exit__(self, *args, **vargs): res=self.f.__exit__(*args,**vargs) retry_remove(self.full_name,self.rep_time,self.wait_time) return res
### Moving and copying files ###
[docs]def copy_file(source, dest, overwrite=True, cmp_on_overwrite=True): """ Copy file, creating a containing folder if necessary. Return ``True`` if the operation was performed. Args: overwrite (bool): If ``True``, overwrite existing file. cmp_on_overwrite (bool): If ``True`` and the two files are compared to be the same, don't perform overwrite. """ if paths_equal(source,dest): return False if os.path.exists(dest): if not overwrite or (cmp_on_overwrite and filecmp.cmp(source,dest,shallow=False)): return False else: ensure_dir(os.path.split(dest)[0]) shutil.copy(source,dest) return True
[docs]def move_file(source, dest, overwrite=True, cmp_on_overwrite=True, preserve_if_not_move=False): """ Move file, creating a containing folder if necessary. Returns ``True`` if the operation was performed. Args: overwrite (bool): If ``True``, overwrite existing file (if the existing file isn't overwritten, preserve the original). cmp_on_overwrite (bool): If ``True`` and the two files are compared to be the same, don't perform overwrite. preserve_if_not_move (bool): If ``True`` and the files are identical, preserve the original. """ if paths_equal(source,dest): return if os.path.exists(dest): if not overwrite or (cmp_on_overwrite and filecmp.cmp(source,dest,shallow=False)): if not preserve_if_not_move: os.remove(source) return else: ensure_dir(os.path.split(dest)[0]) shutil.move(source,dest)
### Creating directories ###
[docs]def ensure_dir_singlelevel(path, error_on_file=True): if os.path.exists(path): if not os.path.isdir(path): if error_on_file: raise OSError("path {0} is not a directory".format(path)) else: os.mkdir(path)
[docs]def ensure_dir(path, error_on_file=True): """ Ensure that the folder exists (create a new one if necessary). If ``error_on_file==True``, raise :exc:`OSError` if there's a file with the same name. """ dirs=fullsplit(path) for i in range(len(dirs)): ensure_dir_singlelevel(os.path.join(*dirs[:i+1]),error_on_file=error_on_file)
def _handleRemoveReadonly(func, path, exc): excvalue = exc[1] if func in (os.rmdir,os.remove) and excvalue.errno==errno.EACCES: os.chmod(path,stat.S_IRWXU|stat.S_IRWXG|stat.S_IRWXO) # 0777 func(path) else: raise
[docs]def remove_dir(path, error_on_file=True): """ Remove the folder recursively if it exists. If ``error_on_file==True``, raise :exc:`OSError` if there's a file with the same name. """ if os.path.exists(path): if not os.path.isdir(path): if error_on_file: raise IOError("path {0} is not a directory".format(path)) else: for _ in range(10): os.remove(path) if not os.path.exists(path): return True for _ in range(10): shutil.rmtree(path,ignore_errors=False,onerror=_handleRemoveReadonly) if not os.path.exists(path): return True else: return False
[docs]def remove_dir_if_empty(path, error_on_file=True): """ Remove the folder only if it's empty. If ``error_on_file==True``, raise :exc:`OSError` if there's a file with the same name. """ if os.path.exists(path) and dir_empty(path,error_on_file=error_on_file): return remove_dir(path) else: return False
[docs]def clean_dir(path, error_on_file=True): """ Remove the folder and then recreate it. If ``error_on_file==True``, raise :exc:`OSError` if there's a file with the same name. """ remove_dir(path,error_on_file=error_on_file) ensure_dir(path,error_on_file=error_on_file)
### Folder walking, extension of os.walk ###
[docs]class FolderList(collections.namedtuple("FolderList",["folders","files"])): # making Sphinx autodoc generate correct docstring """ Describes folder content. """
[docs]def list_dir(folder="", folder_filter=None, file_filter=None, separate_kinds=True, error_on_file=True): """ Return folder content filtered by `folder_filter` and `file_filter`. Args: folder (str): Path to the folder. folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). separate_kinds (bool): if ``True``, return :class:`FolderList` with files and folder separate; otherwise, return a single list (works much faster). error_on_file (bool): if ``True``, raise :exc:`OSError` if there's a file with the same name as the target folder. """ folder=folder or "." if not os.path.exists(folder): return FolderList([], []) if not os.path.isdir(folder): if error_on_file: raise IOError("path {0} is not a directory".format(folder)) else: return FolderList([], []) elements=os.listdir(folder) elements.sort() file_filter=string.get_string_filter(file_filter,match_case=case_sensitive_path()) folder_filter=string.get_string_filter(folder_filter,match_case=case_sensitive_path()) if separate_kinds: folders,files=general.partition_list(lambda p: os.path.isdir(os.path.join(folder,p)), elements) files=string.filter_string_list(files,file_filter) folders=string.filter_string_list(folders,folder_filter) return FolderList(folders, files) else: elements=string.filter_string_list(elements,folder_filter) elements=string.filter_string_list(elements,file_filter) return elements
[docs]def dir_empty(folder, folder_filter=None, file_filter=None, level="single", error_on_file=True): """ Check if the folder is empty (only checks content filtered by `folder_filter` and `file_filter`). Args: folder (str): Path to the folder. folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). level (str): if ``'single'``, check only immediate folder content; if ``'recursive'``, follow recursively in all folders passing `folder_filter`. error_on_file (bool): if ``True``, raise :exc:`OSError` if there's a file with the same name as the target folder. """ funcargparse.check_parameter_range(level,"level",{"single","recursive"}) folders,files=list_dir(folder,folder_filter,file_filter,error_on_file=error_on_file) if level=="single": return len(folders)==0 and len(files)==0 else: if len(files)!=0: return False for f in folders: if not dir_empty(os.path.join(folder,f),folder_filter=folder_filter,file_filter=file_filter,level="recursive"): return False return True
[docs]def walk_dir(folder, folder_filter=None, file_filter=None, rel_path=True, topdown=True, visit_folder_filter=None, max_depth=None): """ Modification of :func:`os.walk` function. Acts in a similar way, but `followlinks` is always ``False`` and errors of :func:`os.listdir` are always passed. Args: folder (str): Path to the folder. folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). rel_path (bool): If ``True``, the returned folder path is specified relative to the initial path. topdown (bool): If ``True``, return folder before its subfolders. visit_folder_filter: Filter for visiting folders (more description at :func:`.string.get_string_filter`). If not ``None``, specifies filter for visiting folders which is different from `folder_filter` (filter for returned folders). max_depth (int): If not ``None``, limits the recursion depth. Yields: For each folder (including the original) yields a tuple ``(folder_path, folders, files)``, where `folder_path` is the containing folder name and `folders` and `files` are its content (similar to :func:`list_dir`). """ folder=folder or "." if max_depth is not None and max_depth<0: return if not os.path.exists(folder): return if not os.path.isdir(folder): raise OSError("path {0} is not a directory".format(folder)) if visit_folder_filter is not None: all_folders,files=list_dir(folder,file_filter=file_filter) file_filter=string.get_string_filter(file_filter,match_case=case_sensitive_path()) folder_filter=string.get_string_filter(folder_filter,match_case=case_sensitive_path()) return_folders=string.filter_string_list(all_folders,folder_filter) walk_folders=string.filter_string_list(all_folders,visit_folder_filter) else: walk_folders,files=list_dir(folder,folder_filter=folder_filter,file_filter=file_filter) return_folders=walk_folders def process_folders(): for f in walk_folders: for path,dirs,files in walk_dir(os.path.join(folder,f),folder_filter,file_filter,rel_path=True,topdown=topdown, visit_folder_filter=visit_folder_filter,max_depth=None if (max_depth is None) else max_depth-1): if path=="": path=f else: path=os.path.join(f,path) if not rel_path: path=os.path.join(folder,path) yield path,dirs,files if topdown: yield ("" if rel_path else folder), return_folders, files for t in process_folders(): yield t else: for t in process_folders(): yield t yield ("" if rel_path else folder), return_folders, files
[docs]def list_dir_recursive(folder, folder_filter=None, file_filter=None, topdown=True, visit_folder_filter=None, max_depth=None): """ Recursive walk analog of :func:`list_dir`. Parameters are the same as :func:`walk_dir`. Returns: :class:`FolderList` """ all_folders=[] all_files=[] for cf, folders, files in walk_dir(folder,folder_filter,file_filter,topdown=topdown,visit_folder_filter=visit_folder_filter,max_depth=max_depth): all_folders=all_folders+[os.path.join(cf,f) for f in folders] all_files=all_files+[os.path.join(cf,f) for f in files] return FolderList(all_folders,all_files)
[docs]def copy_dir(source, dest, folder_filter=None, file_filter=None, overwrite=True, cmp_on_overwrite=True): """ Copy files satisfying the filtering conditions. Args: source (str): Source path. dest (str): Destination path. folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). overwrite (bool): If ``True``, overwrite existing files. cmp_on_overwrite (bool): If ``True`` and the two files are compared to be the same, don't perform overwrite. """ if paths_equal(source,dest): return for path,_,files in walk_dir(source,folder_filter=folder_filter,file_filter=file_filter): source_dir=os.path.join(source,path) dest_dir=os.path.join(dest,path) ensure_dir(dest_dir) for f in files: source_path=os.path.join(source_dir,f) dest_path=os.path.join(dest_dir,f) copy_file(source_path,dest_path,overwrite=overwrite,cmp_on_overwrite=cmp_on_overwrite)
[docs]def move_dir(source, dest, folder_filter=None, file_filter=None, overwrite=True, cmp_on_overwrite=True, preserve_if_not_move=False): """ Move files satisfying the filtering conditions. Args: source (str): Source path. dest (str): Destination path. folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). overwrite (bool): If ``True``, overwrite existing files (if the existing file isn't overwritten, preserve the original). cmp_on_overwrite (bool): If ``True`` and the two files are compared to be the same, don't perform overwrite. preserve_if_not_move (bool): If ``True`` and the files are identical, preserve the original. """ if paths_equal(source,dest): return for path,folders,files in walk_dir(source,folder_filter=folder_filter,file_filter=file_filter,topdown=False): source_dir=os.path.join(source,path) dest_dir=os.path.join(dest,path) ensure_dir(dest_dir) for f in files: source_path=os.path.join(source_dir,f) dest_path=os.path.join(dest_dir,f) move_file(source_path,dest_path,overwrite=overwrite,cmp_on_overwrite=cmp_on_overwrite,preserve_if_not_move=preserve_if_not_move) for f in folders: if dir_empty(f): remove_dir(f)
def _diff_from_cnt(c1, c2): if c1 and c2: return "*" if c1: return "+" if c2: return "-" return "="
[docs]def combine_diff(d1, d2): if d1=="=": return d2 if d2=="=": return d1 return d1 if (d1==d2) else "*"
def _diff_dirs(a, b, folder_filter=None, file_filter=None, shallow=True): list_a=list_dir(a,folder_filter=folder_filter,file_filter=file_filter) list_b=list_dir(b,folder_filter=folder_filter,file_filter=file_filter) files_both,files_a_only,files_b_only=general.compare_lists(list_a.files,list_b.files,sort_lists=True) diff=_diff_from_cnt(files_a_only,files_b_only) if diff=="*": return diff for f in files_both: if not filecmp.cmp(os.path.join(a,f),os.path.join(b,f),shallow=shallow): return "*" folders_both,folders_a_only,folders_b_only=general.compare_lists(list_a.folders,list_b.folders,sort_lists=True) diff=combine_diff(diff,_diff_from_cnt(folders_a_only,folders_b_only)) if diff=="*": return diff for f in folders_both: sub_diff=_diff_dirs(os.path.join(a,f),os.path.join(b,f),shallow=shallow,folder_filter=folder_filter,file_filter=file_filter) diff=combine_diff(diff,sub_diff) if diff=="*": return "*" return diff
[docs]def cmp_dirs(a, b, folder_filter=None, file_filter=None, shallow=True, return_difference=False): """ Compare the folders based on the content filtered by `folder_filter` and `file_filter`. Args: a (str): First folder path b (str): Second folder path folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). shallow: If ``True``, do shallow comparison of the files (see :func:`filecmp.cmp`). return_difference: If ``False``, simply return `bool`; otherwise, return difference type (``'='``, ``'+'``, ``'-'`` or ``'*'``). """ if return_difference: return _diff_dirs(a,b,folder_filter=folder_filter,file_filter=file_filter,shallow=shallow) list_a=list_dir(a,folder_filter=folder_filter,file_filter=file_filter) list_b=list_dir(b,folder_filter=folder_filter,file_filter=file_filter) files_both,files_a_only,files_b_only=general.compare_lists(list_a.files,list_b.files,sort_lists=True) if len(files_a_only)!=0 or len(files_b_only)!=0: return False for f in files_both: if not filecmp.cmp(os.path.join(a,f),os.path.join(b,f),shallow=shallow): return False folders_both,folders_a_only,folders_b_only=general.compare_lists(list_a.folders,list_b.folders,sort_lists=True) if len(folders_a_only)!=0 or len(folders_b_only)!=0: return False for f in folders_both: if not cmp_dirs(os.path.join(a,f),os.path.join(b,f),shallow=shallow,return_difference=False,folder_filter=folder_filter,file_filter=file_filter): return False return True
### Retrying os modifying calls ###
[docs]def retry_copy(source, dest, overwrite=True, cmp_on_overwrite=True, try_times=5, delay=0.3): """ Retrying version of :func:`copy_file`. If the operation raises error, wait for `delay` (in seconds) and call it again. Try total of `try_times` times. """ general.retry_wait(lambda: copy_file(source,dest,overwrite,cmp_on_overwrite), try_times, delay)
[docs]def retry_move(source, dest, overwrite=True, cmp_on_overwrite=True, preserve_if_not_move=False, try_times=5, delay=0.3): """ Retrying version of :func:`move_file` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: move_file(source,dest,overwrite,cmp_on_overwrite,preserve_if_not_move), try_times, delay)
[docs]def retry_remove(path, try_times=5, delay=0.3): """ Retrying version of :func:`os.remove` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: os.remove(path), try_times, delay)
[docs]def retry_ensure_dir(path, error_on_file=True, try_times=5, delay=0.3): """ Retrying version of :func:`ensure_dir` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: ensure_dir(path,error_on_file=error_on_file), try_times, delay)
[docs]def retry_copy_dir(source, dest, folder_filter=None, file_filter=None, overwrite=True, cmp_on_overwrite=True, try_times=5, delay=0.3): """ Retrying version of :func:`copy_dir` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: copy_dir(source,dest,folder_filter,file_filter,overwrite,cmp_on_overwrite), try_times, delay)
[docs]def retry_move_dir(source, dest, folder_filter=None, file_filter=None, overwrite=True, cmp_on_overwrite=True, preserve_if_not_move=False, try_times=5, delay=0.3): """ Retrying version of :func:`move_dir` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: move_dir(source,dest,folder_filter,file_filter,overwrite,cmp_on_overwrite,preserve_if_not_move), try_times, delay)
[docs]def retry_remove_dir(path, error_on_file=True, try_times=5, delay=0.3): """ Retrying version of :func:`remove_dir` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: remove_dir(path,error_on_file=error_on_file), try_times, delay)
[docs]def retry_remove_dir_if_empty(path, error_on_file=True, try_times=5, delay=0.3): """ Retrying version of :func:`remove_dir_if_empty` (see :func:`retry_copy` for details on retrying). """ general.retry_wait(lambda: remove_dir_if_empty(path,error_on_file=error_on_file), try_times, delay)
[docs]def retry_clean_dir(path, error_on_file=True, try_times=5, delay=0.3): """ Retrying version of :func:`clean_dir` (see :func:`retry_copy` for details on retrying). """ retry_remove_dir(path,error_on_file,try_times,delay) retry_ensure_dir(path,error_on_file,try_times,delay)
### Archiving zip files ###
[docs]def zip_folder(zip_path, source_path, inside_path="", folder_filter=None, file_filter=None, mode="a", compression=zipfile.ZIP_DEFLATED): """ Add a folder into a zip archive. Args: zip_path (str): Path to the .zip file. source_path (str): Path to the source folder. inside_path (str): Destination path inside the zip archive. folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). mode (str): Zip archive adding mode (see :class:`zipfile.ZipFile`). compression: Zip archive compression (see :class:`zipfile.ZipFile`). """ with zipfile.ZipFile(zip_path, mode=mode, compression=compression) as zf: for containing_folder,_,files in walk_dir(source_path,rel_path=True,folder_filter=folder_filter,file_filter=file_filter): for f in files: zf.write(os.path.join(source_path,containing_folder,f),os.path.join(inside_path,containing_folder,f),compress_type=compression)
[docs]def zip_file(zip_path, source_path, inside_name=None, mode="a", compression=zipfile.ZIP_DEFLATED): """ Add a file into a zip archive. Args: zip_path (str): Path to the .zip file. source_path (str): Path to the source file. inside_name (str): Destination file name inside the zip archive (source name on the top level by default). mode (str): Zip archive adding mode (see :class:`zipfile.ZipFile`). compression: Zip archive compression (see :class:`zipfile.ZipFile`). """ if inside_name is None: inside_name=os.path.split(source_path)[1] with zipfile.ZipFile(zip_path, mode=mode, compression=compression) as zf: zf.write(source_path,inside_name,compress_type=compression)
[docs]def zip_multiple_files(zip_path, source_paths, inside_names=None, mode="a", compression=zipfile.ZIP_DEFLATED): """ Add a multiple files into a zip archive. Args: zip_path (str): Path to the .zip file. source_paths ([str]): List of path to the source files. inside_names ([str] or None): List of destination file names inside the zip archive (source name on the top level by default). mode (str): Zip archive adding mode (see :class:`zipfile.ZipFile`). compression: Zip archive compression (see :class:`zipfile.ZipFile`). """ if inside_names is None: inside_names=[None]*len(source_paths) for sp,inm in zip(source_paths,inside_names): zip_file(zip_path,sp,inside_name=inm,mode=mode,compression=compression)
[docs]def unzip_folder(zip_path, dest_path, inside_path="", folder_filter=None, file_filter=None): """ Extract a folder from a zip archive (create containing folder if necessary). Args: zip_path (str): Path to the .zip file. dest_path (str): Path to the destination folder. inside_path (str): Source path inside the zip archive; extracted data paths are relative (i.e., they don't include `inside_path`). folder_filter: Folder filter function (more description at :func:`.string.get_string_filter`). file_filter: File filter function (more description at :func:`.string.get_string_filter`). """ with zipfile.ZipFile(zip_path, mode="r") as zf: if inside_path=="" and folder_filter is None and file_filter is None: zf.extractall(dest_path) else: folder_filter=string.get_string_filter(folder_filter) file_filter=string.get_string_filter(file_filter) inside_path=fullsplit(inside_path) for f in zf.filelist: path=fullsplit(f.filename) if path[:len(inside_path)]==inside_path and file_filter(path[-1]) and all([folder_filter(p) for p in path[:-1]]): if len(inside_path)==0: zf.extract(f,dest_path) else: dest_filepath=os.path.join(dest_path,*path[len(inside_path):]) with zf.open(f,"r") as source_file: ensure_dir(os.path.split(dest_filepath)[0]) with open(dest_filepath,"wb") as dest_file: shutil.copyfileobj(source_file,dest_file)
[docs]def unzip_file(zip_path, dest_path, inside_path): """ Extract a file from a zip archive (create containing folder if necessary). Args: zip_path (str): Path to the .zip file. dest_path (str): Destination file path. inside_path (str): Source path inside the zip archive. """ with zipfile.ZipFile(zip_path, mode="r") as zf: with zf.open(inside_path,"r") as source_file: ensure_dir(os.path.split(dest_path)[0]) with open(dest_path,"wb") as dest_file: shutil.copyfileobj(source_file,dest_file)
### File-related dialog windows ### ## Use default Tkinter window manager
[docs]def openfiledialog(**options): """ Open file dialog, wrapper for tkFileDialog. """ main=Tk() main.withdraw() path=askopenfilename(**options) main.quit() return path
[docs]def savefiledialog(**options): """ Save file dialog, wrapper for tkFileDialog. """ main=Tk() main.withdraw() path=asksaveasfilename(**options) main.quit() return path
[docs]def opendirdialog(**options): """ Open directory dialog, wrapper for tkFileDialog. """ main=Tk() main.withdraw() path=askdirectory(**options) main.quit() return path