Generates a ZIP file in-memory from file search patterns.

    Args:
        root (str): base directory to list files from.
        includes (list[str]): inclusion patterns. Only files  matching those
            patterns will be included in the result.
        excludes (list[str]): exclusion patterns. Files matching those
            patterns will be excluded from the result. Exclusions take
            precedence over inclusions.
        follow_symlinks (bool): If true, symlinks will be included in the
            resulting zip file

    See Also:
        :func:`_zip_files`, :func:`_find_files`.

    Raises:
        RuntimeError: when the generated archive would be empty.

def _zip_from_file_patterns(root, includes, excludes, follow_symlinks):
    """Generates a ZIP file in-memory from file search patterns.

    Args:
        root (str): base directory to list files from.
        includes (list[str]): inclusion patterns. Only files  matching those
            patterns will be included in the result.
        excludes (list[str]): exclusion patterns. Files matching those
            patterns will be excluded from the result. Exclusions take
            precedence over inclusions.
        follow_symlinks (bool): If true, symlinks will be included in the
            resulting zip file

    See Also:
        :func:`_zip_files`, :func:`_find_files`.

    Raises:
        RuntimeError: when the generated archive would be empty.

    """
    logger.info('lambda: base directory: %s', root)

    files = list(_find_files(root, includes, excludes, follow_symlinks))
    if not files:
        raise RuntimeError('Empty list of files for Lambda payload. Check '
                           'your include/exclude options for errors.')

    logger.info('lambda: adding %d files:', len(files))

    for fname in files:
        logger.debug('lambda: + %s', fname)

    return _zip_files(files, root)