Source code for default_profile.util.copytree

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""This actually got implemented in the stdlib in a much cleaner way.

But this might continue holding some improvements to the std. lib for a little.
"""

import os
import shutil
from glob import glob
from shutil import Error, copytree, copystat
import warnings


[docs]class CopyTree: """Rewrite :func:`shutil.copytree()`. Parameters ---------- src : str (path-like) Directory tree to copy. dest : str (path-like) Directory to move tree to. symlinks : Bool, optional Whether to follow symlinks. Defaults to False. ignore : :func:`glob.glob()` pattern Files to not copy. copy_function : :mod:`shutil` copy function, optional Defaults to :func:`shutil.copy2`. Returns ------- dst : TODO type TODO Notes ----- Copying file access times may fail on Windows and if so ignore it. """ errors = []
[docs] def __init__( self, src, dst, symlinks=False, ignore=None, copy_function=shutil.copy2 ): warnings.warn("This class is going to be removed shortly." "Discontinue usage.") self.src = src self.dst = dst self.symlinks = symlinks self.ignore = ignore self.copy_function = copy_function
[docs] @property def destination_files(self): names = os.listdir(self.src) if self.ignore is not None: ignored_names = self.ignore(self.src, names) else: ignored_names = set() return ignored_names
def _path_to_str(self): """Convert user provided :ref:`src` and :ref:dst` to str if necessary.""" if hasattr(self.src, __fspath__): self.src = self.src.__fspath__() if hasattr(self.dst, __fspath__): self.dst = self.dst.__fspath__()
[docs] def make_dest_dirs(self): """Create the dirs needed in the destination.""" try: os.makedirs(self.dst) except FileExistsError: pass
[docs] def copytree(self): """Let's try and do `shutil.copytree()` a little better. First let's do everyone the courtesy of checking whether `src` and `dest` are pathlib.Path objects. """ for name in self.destination_files: if name in glob(self.ignore): continue srcname = os.path.join(self.src, name) dstname = os.path.join(self.dst, name) try: if os.path.islink(srcname): linkto = os.readlink(srcname) if self.symlinks: # We can't just leave it to `copy_function` because legacy # code with a custom `copy_function` may rely on copytree # doing the right thing. os.symlink(linkto, dstname) copystat(srcname, dstname, follow_symlinks=not self.symlinks) else: # ignore dangling symlink if the flag is on if not os.path.exists(linkto) and ignore_dangling_symlinks: continue # otherwise let the copy occurs. copy2 will raise an error if os.path.isdir(srcname): copytree( srcname, dstname, self.symlinks, glob(self.ignore), self.copy_function, ) else: self.copy_function(srcname, dstname) elif os.path.isdir(srcname): copytree( srcname, dstname, self.symlinks, self.ignore, self.copy_function ) else: # Will raise a SpecialFileError for unsupported file types self.copy_function(srcname, dstname) # catch the Error from the recursive copytree so that we can # continue with other files except Error as err: self.errors.extend(err.args[0]) except OSError as why: self.errors.append((srcname, dstname, str(why))) try: copystat(self.src, self.dst) except OSError as why: if getattr(why, "winerror", None) is None: self.errors.append((self.src, self.dst, str(why))) if self.errors: # do i need to do len(self.errors) > 0? raise Error(self.errors) return self.dst