python-3.xlinuxsubprocessdisplayxorg

Turn screen off in Linux via Python3


I am trying to implement a python3 file to turn off the computer screen as detailed here, the command:

xset -display :0.0 dpms force off

Entered into a terminal does work, it turns the screen off, and it doesn't even need the other command to turn it back on because any keypress will turn it on.

Now how to implement this into python, especially with subprocess since I heard that the os module is not very safe to use.

I have tried

import subprocess

subprocess.run(["xset","-display :0.0 dpms force off"])

But it doesn't work, it gives me an error. Please help, and tell me whether it is safe to run it like this or whether there is another solution to do this from python.


Solution

  • First things first: in future questions, don't just say "it doesn't work, it gives me an error". Show us exactly what the error message was.

    My guess is that the message you got started with something like this:

    xset: unknown option -display :0.0 dpms force off
    
    usage: xset [-display host:dpy] option ...
    

    followed by a long list of the options that xset will accept.

    That happens because your subprocess.run argument list has all of the xset command arguments jammed into a single item in the list. That's equivalent to running this command in the terminal:

    xset "-display :0.0 dpms force off"
    

    where the quotes will cause all of the argument words to be grouped into one single large argument that xset does not understand. If you run that command in the terminal you should get exactly the same message you got from your Python program.

    To fix it, instead of writing this:

    subprocess.run(["xset", "-display :0.0 dpms force off"])
    

    write this:

    subprocess.run(["xset", "-display", ":0.0", "dpms", "force", "off"])
    

    That causes xset to see each argument as a separate word, and it knows how to process them when they're in that form.

    As an alternative you could pass a single string to subprocess.run in place of a list:

    subprocess.run("xset -display :0.0 dpms force off")
    

    but as the Frequently Used Arguments section of the subprocess module documentation says, using a string can get ugly if your individual arguments need to contain spaces or other characters that need special treatment. So generally the variant that has everything as a separate list item is safer to use.

    It's safe to use subprocess.run like this. It would have been safe (but less efficient) to use os.system too, because in this case the command and arguments that you're using are fixed and are provided entirely by your program. os.system becomes dangerous when its arguments are specified by variables whose values come from somewhere outside your program.