Files and folders

Various methods to copy, delete and create files and folders. Inludes methods to move files and folders to the trash bin instead of deleting them.

The following imports will be needed:

import os
import shutil
from send2trash import send2trash

Creating directories

This method creates directories like mkdirs does, but without worrying if the directory structure already exists. Plus, it supports passing a complete filename instead of only its path, if include=False is used.

Note: this method is also used in several snippets below.

def make_dirs(filename, inclusive=False):
    '''
    Creates intermediate directories for the given file (not dir!)
    '''
    f = filename if inclusive else os.path.dirname(filename)
    if not os.path.exists(f):
        try:
            os.makedirs(f)
        except OSError as exc:
            if exc.errno != errno.EEXIST:
                raise

Deleting directories

The send2trash package can delete non-empty folder structures, shutil cannot.

def rm_dir(path, trash=False):
    '''
    Removes a non-empty directory or file to the trash
    '''
    if os.path.isdir(path):
        if trash:
            send2trash(path)
        else:
            shutil.rmtree(path)

Copying directories

shutil.copytree is usually a good fit for copying directory structures, except when the target directory is not empty. It is designed to copy into empty directories only.

The snippet below, found here replaces copytree with a version that can copy and recursively merge directories into existing ones.

def copytree(src, dst, symlinks=False, ignore=None):
    '''
    A fixed version of shutil.copytree. It's basically copied 1-1, except the first
    os.makedirs() put behind an if-else-construct.
    Credits to https://stackoverflow.com/a/12687372/609794
    '''
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    if not os.path.isdir(dst): # This one line does the trick
        os.makedirs(dst)
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks, ignore)
            else:
                # Will raise a SpecialFileError for unsupported file types
                shutil.copy2(srcname, dstname)
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except err:
            errors.extend(err.args[0])
        except EnvironmentError as why:
            errors.append((srcname, dstname, str(why)))
    try:
        shutil.copystat(src, dst)
    except OSError as why:
        if WindowsError is not None and isinstance(why, WindowsError):
            # Copying file access times may fail on Windows
            pass
        else:
            errors.extend((src, dst, str(why)))
    if errors:
        raise errors

Move and copy files

To move or copy a file to a directory that might not exist yet, use these:

def copy_file(src, dst):
    make_dirs(dst)
    shutil.copy2(src, dst)

def move_file(src, dst):
    make_dirs(dst)
    shutil.move(src, dst)