def copytree(source, destination, ignore=None, include=None):
    '\n    Similar to shutil.copytree except that it removes the limitation that the destination directory should\n    be present.\n\n    :type source: str\n    :param source:\n        Path to the source folder to copy\n\n    :type destination: str\n    :param destination:\n        Path to destination folder\n\n    :type ignore: function\n    :param ignore:\n        A function that returns a set of file names to ignore, given a list of available file names. Similar to the\n        ``ignore`` property of ``shutils.copytree`` method\n\n    :type include: Callable[[str], bool]\n    :param include:\n        A function that will decide whether a file should be copied or skipped it. It accepts file name as parameter\n        and return True or False. Returning True will continue copy operation, returning False will skip copy operation\n        for that file\n    '
    if (not os.path.exists(source)):
        LOG.warning('Skipping copy operation since source %s does not exist', source)
        return
    if (not os.path.exists(destination)):
        LOG.debug('Creating target folders at %s', destination)
        os.makedirs(destination)
        try:
            LOG.debug('Copying directory metadata from source (%s) to destination (%s)', source, destination)
            shutil.copystat(source, destination)
        except OSError as ex:
            LOG.debug('Unable to copy file access times from %s to %s', source, destination, exc_info=ex)
    names = os.listdir(source)
    if (ignore is not None):
        ignored_names = ignore(source, names)
    else:
        ignored_names = set()
    for name in names:
        if (name in ignored_names):
            LOG.debug('File (%s) is in ignored set, skipping it', name)
            continue
        new_source = os.path.join(source, name)
        new_destination = os.path.join(destination, name)
        if (include and (not os.path.isdir(new_source)) and (not include(name))):
            LOG.debug("File (%s) doesn't satisfy the include rule, skipping it", name)
            continue
        if os.path.isdir(new_source):
            copytree(new_source, new_destination, ignore=ignore, include=include)
        else:
            LOG.debug('Copying source file (%s) to destination (%s)', new_source, new_destination)
            shutil.copy2(new_source, new_destination)
Similar to shutil.copytree except that it removes the limitation that the destination directory should
be present.

:type source: str
:param source:
    Path to the source folder to copy

:type destination: str
:param destination:
    Path to destination folder

:type ignore: function
:param ignore:
    A function that returns a set of file names to ignore, given a list of available file names. Similar to the
    ``ignore`` property of ``shutils.copytree`` method

:type include: Callable[[str], bool]
:param include:
    A function that will decide whether a file should be copied or skipped it. It accepts file name as parameter
    and return True or False. Returning True will continue copy operation, returning False will skip copy operation
    for that file
def copytree(source, destination, ignore=None, include=None):
    '\n    Similar to shutil.copytree except that it removes the limitation that the destination directory should\n    be present.\n\n    :type source: str\n    :param source:\n        Path to the source folder to copy\n\n    :type destination: str\n    :param destination:\n        Path to destination folder\n\n    :type ignore: function\n    :param ignore:\n        A function that returns a set of file names to ignore, given a list of available file names. Similar to the\n        ``ignore`` property of ``shutils.copytree`` method\n\n    :type include: Callable[[str], bool]\n    :param include:\n        A function that will decide whether a file should be copied or skipped it. It accepts file name as parameter\n        and return True or False. Returning True will continue copy operation, returning False will skip copy operation\n        for that file\n    '
    if (not os.path.exists(source)):
        LOG.warning('Skipping copy operation since source %s does not exist', source)
        return
    if (not os.path.exists(destination)):
        LOG.debug('Creating target folders at %s', destination)
        os.makedirs(destination)
        try:
            LOG.debug('Copying directory metadata from source (%s) to destination (%s)', source, destination)
            shutil.copystat(source, destination)
        except OSError as ex:
            LOG.debug('Unable to copy file access times from %s to %s', source, destination, exc_info=ex)
    names = os.listdir(source)
    if (ignore is not None):
        ignored_names = ignore(source, names)
    else:
        ignored_names = set()
    for name in names:
        if (name in ignored_names):
            LOG.debug('File (%s) is in ignored set, skipping it', name)
            continue
        new_source = os.path.join(source, name)
        new_destination = os.path.join(destination, name)
        if (include and (not os.path.isdir(new_source)) and (not include(name))):
            LOG.debug("File (%s) doesn't satisfy the include rule, skipping it", name)
            continue
        if os.path.isdir(new_source):
            copytree(new_source, new_destination, ignore=ignore, include=include)
        else:
            LOG.debug('Copying source file (%s) to destination (%s)', new_source, new_destination)
            shutil.copy2(new_source, new_destination)