Search code examples
pythonterminalsudospawn

Python: Spawn sudo process (in new terminal), wait until completed


Edit: my final code goes something like this:

#WARNING: all " in command need to be escaped: \\"
def spawnInNewTerminal(command):
    #creates lock file
    lock = open(lockPath, 'w')
    lock.write("Currently performing task in separate terminal.")
    lock.close()

    #adds line to command to remove lock file
    command += ";rm " + lockPath

    #executes the command in a new terminal
    process = subprocess.Popen (
        ['x-terminal-emulator', '-e',  'sh -c "{0}"'.format(command) ]
        , stdout=subprocess.PIPE )
    process.wait()

    #doesn't let us proceed until the lock file has been removed by the bash command
    while os.path.exists(lockPath):
        time.sleep(0.1)

Original question:

I am writing a simple wrapper that installs any missing packages "on the fly" before finally running LuaLaTeX. It mostly works, but near the end, I have to run the command

sudo tlmgr install [string of packages]

and furthermore, because there's no guarantee the LaTeX editor will allow user input, I have to call a new terminal to do this in so they can enter their sudo password.

I have mostly figured this out: either

process = subprocess.Popen(
    shlex.split('''x-terminal-emulator -t \'Installing new packages\' -e \'sudo tlmgr install ''' + packagesString + '''\''''), stdout=subprocess.PIPE)
retcode = process.wait()

or

os.system('''x-terminal-emulator -t \'Installing new packages\' -e \'sudo tlmgr install ''' + packagesString + '''\'''')

The only problem is, this line does not wait until the spawned terminal process is done. In fact, it continues to the next line (running the actual LuaLaTeX) immediately, before the user can even enter their password or download the packages!

From what I understand, this is because the sudo child process finishes right away. Is there a way to make sure the tlmgr process finishes before continuing?


Solution

  • The reason is that x-terminal-emulator spawns a new process and exits, so you can't know when the executed command actually finishes. To work around that, a solution would be to modify your command to add another command that notifies you. Since apparently x-terminal-emulator only ever executes one command, we can use a shell to chain them. Probably not the best way to do, but one would be:

    os.system('x-terminal-emulator -t "Installing new packages" -e "sh -c \\"sudo tlmgr install %s; touch /tmp/install_completed\\""' % packagesString)
    while not os.path.exists("/tmp/install_completed"):
        time.sleep(0.1)
    os.remove("/tmp/install_completed")