TL;DR:
L3viathan's reply has a SynaxError Edit: works fine on Python 3.5+. Here is a version for Python 3.4.3 (distributed by default on Ubuntu 16.04) and below:
if os.geteuid() == 0:
# do root things
else:
subprocess.call(['sudo', 'python3'] + sys.argv) # modified
However, I went with a different approach because it made the code around it simpler in my use case:
if os.geteuid() != 0:
os.execvp('sudo', ['sudo', 'python3'] + sys.argv)
# do root things
Explaination
L3viathan's reply depends on PEP 448, which was included in Python 3.5 and introduced additional contexts where star argument expansion is permitted. For Python 3.4 and below, list concatenation can be used to do the same thing:
import os
import sys
import subprocess
if os.geteuid() == 0:
print("We're root!")
else:
print("We're not root.")
subprocess.call(['sudo', 'python3'] + sys.argv) # modified
But note: subprocess.call()
launches a child process, meaning after root finishes running the script, the original user will continue running their script as well. This means you need to put the elevated logic into one side of an if/else
block so when the original script finishes, it doesn't try to run any of the logic that requires elevation (L3viathan's example does this).
This isn't necessarily a bad thing - it means both normal/elevated logic can be written in the same script and separated nicely - but my task required root for all the logic. I didn't want to waste an indentation level if the other block was going to be empty, and I hadn't realized how using a child process would affect things, so I tried this:
import os
import sys
import subprocess
if os.geteuid() != 0:
subprocess.call(['sudo', 'python3'] + sys.argv)
# do things that require root
...and it broke of course, because after root was done, the regular user resumed execution of its script and tried to run the statements that required root.
Then I found this Gist recommending os.execvp()
- which replaces the running process instead of launching a child:
import os
import sys
if os.geteuid() != 0:
os.execvp('sudo', ['sudo', 'python3'] + sys.argv) # final version
# do things that require root
This appears to behave as expected, saves an indentation level and 3 lines of code.
Caveat: I didn't know about os.execvp()
ten minutes ago, and don't know anything yet about possible pitfalls or subtleties around its usage. YMMV.