Package source files and upload a compress tar file to S3. The S3 location will be
    ``s3://<bucket>/s3_key_prefix/sourcedir.tar.gz``.

    If directory is an S3 URI, an UploadedCode object will be returned, but nothing will be
    uploaded to S3 (this allow reuse of code already in S3).

    If directory is None, the script will be added to the archive at ``./<basename of script>``.

    If directory is not None, the (recursive) contents of the directory will be added to
    the archive. directory is treated as the base path of the archive, and the script name is
    assumed to be a filename or relative path inside the directory.

    Args:
        session (boto3.Session): Boto session used to access S3.
        bucket (str): S3 bucket to which the compressed file is uploaded.
        s3_key_prefix (str): Prefix for the S3 key.
        script (str): Script filename or path.
        directory (str): Optional. Directory containing the source file. If it starts with "s3://",
            no action is taken.
        dependencies (List[str]): Optional. A list of paths to directories (absolute or relative)
                                containing additional libraries that will be copied into
                                /opt/ml/lib
        kms_key (str):  Optional. KMS key ID used to upload objects to the bucket (default: None).

    Returns:
        sagemaker.fw_utils.UserCode: An object with the S3 bucket and key (S3 prefix) and
            script name.

def tar_and_upload_dir(session, bucket, s3_key_prefix, script,
                       directory=None, dependencies=None, kms_key=None):
    """Package source files and upload a compress tar file to S3. The S3 location will be
    ``s3://<bucket>/s3_key_prefix/sourcedir.tar.gz``.

    If directory is an S3 URI, an UploadedCode object will be returned, but nothing will be
    uploaded to S3 (this allow reuse of code already in S3).

    If directory is None, the script will be added to the archive at ``./<basename of script>``.

    If directory is not None, the (recursive) contents of the directory will be added to
    the archive. directory is treated as the base path of the archive, and the script name is
    assumed to be a filename or relative path inside the directory.

    Args:
        session (boto3.Session): Boto session used to access S3.
        bucket (str): S3 bucket to which the compressed file is uploaded.
        s3_key_prefix (str): Prefix for the S3 key.
        script (str): Script filename or path.
        directory (str): Optional. Directory containing the source file. If it starts with "s3://",
            no action is taken.
        dependencies (List[str]): Optional. A list of paths to directories (absolute or relative)
                                containing additional libraries that will be copied into
                                /opt/ml/lib
        kms_key (str):  Optional. KMS key ID used to upload objects to the bucket (default: None).

    Returns:
        sagemaker.fw_utils.UserCode: An object with the S3 bucket and key (S3 prefix) and
            script name.
    """
    if directory and directory.lower().startswith('s3://'):
        return UploadedCode(s3_prefix=directory, script_name=os.path.basename(script))

    script_name = script if directory else os.path.basename(script)
    dependencies = dependencies or []
    key = '%s/sourcedir.tar.gz' % s3_key_prefix
    tmp = tempfile.mkdtemp()

    try:
        source_files = _list_files_to_compress(script, directory) + dependencies
        tar_file = sagemaker.utils.create_tar_file(source_files,
                                                   os.path.join(tmp, _TAR_SOURCE_FILENAME))

        if kms_key:
            extra_args = {'ServerSideEncryption': 'aws:kms', 'SSEKMSKeyId': kms_key}
        else:
            extra_args = None

        session.resource('s3').Object(bucket, key).upload_file(tar_file, ExtraArgs=extra_args)
    finally:
        shutil.rmtree(tmp)

    return UploadedCode(s3_prefix='s3://%s/%s' % (bucket, key), script_name=script_name)