'Returns modifications that would be made by sourcing files.
Args:
*args (list of str): list of files to be sourced
Returns:
EnvironmentModifications: an object that, if executed, has
the same effect on the environment as sourcing the files
passed as parameters'

@staticmethod
def from_sourcing_files(*args, **kwargs):

    env = EnvironmentModifications()
    files = [line.split('    ')[0] for line in args]
    non_existing = [file for file in files if (not os.path.isfile(file))]
    if non_existing:
        message = 'trying    to    source    non-existing    files\n'
        message += '\n'.join(non_existing)
        raise RuntimeError(message)
    info = dict(kwargs)
    info.setdefault('shell', '/bin/bash')
    info.setdefault('shell_options', '-c')
    info.setdefault('source_command', 'source')
    info.setdefault('suppress_output', '&>    /dev/null')
    info.setdefault('concatenate_on_success', '&&')
    shell = '{shell}'.format(**info)
    shell_options = '{shell_options}'.format(**info)
    source_file = '{source_command}    {file}    {concatenate_on_success}'
    dump_cmd = 'import    os,    json;    print(json.dumps(dict(os.environ)))'
    dump_environment = ('python    -c    "%s"' % dump_cmd)
    command = [source_file.format(file=file, **info) for file in args]
    command.append(dump_environment)
    command = '    '.join(command)
    command = [shell, shell_options, command]
    proc = subprocess.Popen(command, stdout=subprocess.PIPE, env=os.environ)
    proc.wait()
    if (proc.returncode != 0):
        raise RuntimeError('sourcing    files    returned    a    non-zero    exit    code')
    output = ''.join([line.decode('utf-8') for line in proc.stdout])
    this_environment = dict(os.environ)
    after_source_env = json.loads(output)
    if (sys.version_info[0] < 3):
        after_source_env = dict(((k.encode('utf-8'), v.encode('utf-8')) for (k, v) in after_source_env.items()))
    to_be_filtered = ('SHLVL', '_', 'PWD', 'OLDPWD')
    for d in (after_source_env, this_environment):
        for name in to_be_filtered:
            d.pop(name, None)
    new_variables = (set(after_source_env) - set(this_environment))
    for x in new_variables:
        env.set(x, after_source_env[x])
    unset_variables = (set(this_environment) - set(after_source_env))
    for x in unset_variables:
        env.unset(x)
    common_variables = set(this_environment).intersection(set(after_source_env))
    modified_variables = [x for x in common_variables if (this_environment[x] != after_source_env[x])]
    def return_separator_if_any(first_value, second_value):
        separators = (':', ';')
        for separator in separators:
            if ((separator in first_value) and (separator in second_value)):
                return separator
        return None
    for x in modified_variables:
        current = this_environment[x]
        modified = after_source_env[x]
        sep = return_separator_if_any(current, modified)
        if (sep is None):
            env.set(x, after_source_env[x])
        else:
            current_list = current.split(sep)
            modified_list = modified.split(sep)
            remove_list = [ii for ii in current_list if (ii not in modified_list)]
            remaining_list = [ii for ii in current_list if (ii in modified_list)]
            start = modified_list.index(remaining_list[0])
            end = modified_list.index(remaining_list[(-1)])
            search = sep.join(modified_list[start:(end + 1)])
            if (search not in current):
                env.set(x, after_source_env[x])
                break
            else:
                try:
                    prepend_list = modified_list[:start]
                except KeyError:
                    prepend_list = []
                try:
                    append_list = modified_list[(end + 1):]
                except KeyError:
                    append_list = []
                for item in remove_list:
                    env.remove_path(x, item)
                for item in append_list:
                    env.append_path(x, item)
                for item in prepend_list:
                    env.prepend_path(x, item)
    return env
