Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
dir_util.py233 linesDownload Raw Back to _distutils
1"""distutils.dir_util2 3Utility functions for manipulating directories and directory trees."""4 5import functools6import itertools7import os8import pathlib9 10from . import file_util11from ._log import log12from .errors import DistutilsFileError, DistutilsInternalError13 14 15class SkipRepeatAbsolutePaths(set):16    """17    Cache for mkpath.18 19    In addition to cheapening redundant calls, eliminates redundant20    "creating /foo/bar/baz" messages in dry-run mode.21    """22 23    def __init__(self):24        SkipRepeatAbsolutePaths.instance = self25 26    @classmethod27    def clear(cls):28        super(cls, cls.instance).clear()29 30    def wrap(self, func):31        @functools.wraps(func)32        def wrapper(path, *args, **kwargs):33            if path.absolute() in self:34                return35            result = func(path, *args, **kwargs)36            self.add(path.absolute())37            return result38 39        return wrapper40 41 42# Python 3.8 compatibility43wrapper = SkipRepeatAbsolutePaths().wrap44 45 46@functools.singledispatch47@wrapper48def mkpath(name: pathlib.Path, mode=0o777, verbose=True) -> None:49    """Create a directory and any missing ancestor directories.50 51    If the directory already exists (or if 'name' is the empty string, which52    means the current directory, which of course exists), then do nothing.53    Raise DistutilsFileError if unable to create some directory along the way54    (eg. some sub-path exists, but is a file rather than a directory).55    If 'verbose' is true, log the directory created.56    """57    if verbose and not name.is_dir():58        log.info("creating %s", name)59 60    try:61        name.mkdir(mode=mode, parents=True, exist_ok=True)62    except OSError as exc:63        raise DistutilsFileError(f"could not create '{name}': {exc.args[-1]}")64 65 66@mkpath.register67def _(name: str, *args, **kwargs):68    return mkpath(pathlib.Path(name), *args, **kwargs)69 70 71@mkpath.register72def _(name: None, *args, **kwargs):73    """74    Detect a common bug -- name is None.75    """76    raise DistutilsInternalError(f"mkpath: 'name' must be a string (got {name!r})")77 78 79def create_tree(base_dir, files, mode=0o777, verbose=True):80    """Create all the empty directories under 'base_dir' needed to put 'files'81    there.82 83    'base_dir' is just the name of a directory which doesn't necessarily84    exist yet; 'files' is a list of filenames to be interpreted relative to85    'base_dir'.  'base_dir' + the directory portion of every file in 'files'86    will be created if it doesn't already exist.  'mode' and 'verbose'87    flags are as for 'mkpath()'.88    """89    # First get the list of directories to create90    need_dir = set(os.path.join(base_dir, os.path.dirname(file)) for file in files)91 92    # Now create them93    for dir in sorted(need_dir):94        mkpath(dir, mode, verbose=verbose)95 96 97def copy_tree(98    src,99    dst,100    preserve_mode=True,101    preserve_times=True,102    preserve_symlinks=False,103    update=False,104    verbose=True,105):106    """Copy an entire directory tree 'src' to a new location 'dst'.107 108    Both 'src' and 'dst' must be directory names.  If 'src' is not a109    directory, raise DistutilsFileError.  If 'dst' does not exist, it is110    created with 'mkpath()'.  The end result of the copy is that every111    file in 'src' is copied to 'dst', and directories under 'src' are112    recursively copied to 'dst'.  Return the list of files that were113    copied or might have been copied, using their output name.  The114    return value is unaffected by 'update': it is simply115    the list of all files under 'src', with the names changed to be116    under 'dst'.117 118    'preserve_mode' and 'preserve_times' are the same as for119    'copy_file'; note that they only apply to regular files, not to120    directories.  If 'preserve_symlinks' is true, symlinks will be121    copied as symlinks (on platforms that support them!); otherwise122    (the default), the destination of the symlink will be copied.123    'update' and 'verbose' are the same as for 'copy_file'.124    """125    if not os.path.isdir(src):126        raise DistutilsFileError(f"cannot copy tree '{src}': not a directory")127    try:128        names = os.listdir(src)129    except OSError as e:130        raise DistutilsFileError(f"error listing files in '{src}': {e.strerror}")131 132    mkpath(dst, verbose=verbose)133 134    copy_one = functools.partial(135        _copy_one,136        src=src,137        dst=dst,138        preserve_symlinks=preserve_symlinks,139        verbose=verbose,140        preserve_mode=preserve_mode,141        preserve_times=preserve_times,142        update=update,143    )144    return list(itertools.chain.from_iterable(map(copy_one, names)))145 146 147def _copy_one(148    name,149    *,150    src,151    dst,152    preserve_symlinks,153    verbose,154    preserve_mode,155    preserve_times,156    update,157):158    src_name = os.path.join(src, name)159    dst_name = os.path.join(dst, name)160 161    if name.startswith('.nfs'):162        # skip NFS rename files163        return164 165    if preserve_symlinks and os.path.islink(src_name):166        link_dest = os.readlink(src_name)167        if verbose >= 1:168            log.info("linking %s -> %s", dst_name, link_dest)169        os.symlink(link_dest, dst_name)170        yield dst_name171 172    elif os.path.isdir(src_name):173        yield from copy_tree(174            src_name,175            dst_name,176            preserve_mode,177            preserve_times,178            preserve_symlinks,179            update,180            verbose=verbose,181        )182    else:183        file_util.copy_file(184            src_name,185            dst_name,186            preserve_mode,187            preserve_times,188            update,189            verbose=verbose,190        )191        yield dst_name192 193 194def _build_cmdtuple(path, cmdtuples):195    """Helper for remove_tree()."""196    for f in os.listdir(path):197        real_f = os.path.join(path, f)198        if os.path.isdir(real_f) and not os.path.islink(real_f):199            _build_cmdtuple(real_f, cmdtuples)200        else:201            cmdtuples.append((os.remove, real_f))202    cmdtuples.append((os.rmdir, path))203 204 205def remove_tree(directory, verbose=True):206    """Recursively remove an entire directory tree.207 208    Any errors are ignored (apart from being reported to stdout if 'verbose'209    is true).210    """211    if verbose >= 1:212        log.info("removing '%s' (and everything under it)", directory)213    cmdtuples = []214    _build_cmdtuple(directory, cmdtuples)215    for cmd in cmdtuples:216        try:217            cmd[0](cmd[1])218            # Clear the cache219            SkipRepeatAbsolutePaths.clear()220        except OSError as exc:221            log.warning("error removing %s: %s", directory, exc)222 223 224def ensure_relative(path):225    """Take the full path 'path', and make it a relative path.226 227    This is useful to make 'path' the second argument to os.path.join().228    """229    drive, path = os.path.splitdrive(path)230    if path[0:1] == os.sep:231        path = drive + path[1:]232    return path233 
codekingpro/portable-devtools · Team Ai