8

How I can prevent sleep-mode on python without using extra apps on different OS(Ubuntu, Windows...) but in most cases I need Linux solution

I am making app that works big amount of time. It uses like 80% of CPU, so a user just start this app and go away from keyboard. So I think I need something like system api or library that lock sleep-mode. I am sure that, it exists. For example, if you open any video player on your OS, your (PC, Laptop) will not go to sleep mode, the same thing in browser.

Also, there is the same thing in Android (WakeLock) or Windows (SetThreadExecutionState)

Max Dev
  • 89
  • 1
  • 1
  • 9
  • What kind of app is this? Is it made using `tkinter` ? You need to give details for people to help you. – mishsx Aug 25 '19 at 14:49
  • @mishsx No, it does not use tkinter, just simple app that runs in terminal. – Max Dev Aug 25 '19 at 14:57
  • I would not expect that a cross-platform solution is available. You might need to implement a platform-specific solution yourself, which uses `WakeLock` or `SetThreadExecutionState` or something else depending on the OS on which it is running. – zvone Aug 25 '19 at 15:07

5 Answers5

7

I ran into similar situation where a process took long enough to execute itself that windows would hibernate. To overcome this problem I wrote a script.

The following simple piece of code can prevent this problem. When used, it will ask windows not to sleep while the script runs. (In some cases, such as when the battery is running out, Windows will ignore your request.)

    class WindowsInhibitor:
        '''Prevent OS sleep/hibernate in windows; code from:
        https://github.com/h3llrais3r/Deluge-PreventSuspendPlus/blob/master/preventsuspendplus/core.py
        API documentation:
        https://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx'''
        ES_CONTINUOUS = 0x80000000
        ES_SYSTEM_REQUIRED = 0x00000001

        def __init__(self):
            pass

        def inhibit(self):
            import ctypes
            print("Preventing Windows from going to sleep")
            ctypes.windll.kernel32.SetThreadExecutionState(
                WindowsInhibitor.ES_CONTINUOUS | \
                WindowsInhibitor.ES_SYSTEM_REQUIRED)

        def uninhibit(self):
            import ctypes
            print("Allowing Windows to go to sleep")
            ctypes.windll.kernel32.SetThreadExecutionState(
                WindowsInhibitor.ES_CONTINUOUS)

To run the script, simply :

    import os

    osSleep = None
    # in Windows, prevent the OS from sleeping while we run
    if os.name == 'nt':
        osSleep = WindowsInhibitor()
        osSleep.inhibit()

    # do slow stuff

    if osSleep:
        osSleep.uninhibit()
HashRocketSyntax
  • 2,653
  • 3
  • 23
  • 47
mishsx
  • 1,153
  • 4
  • 15
  • 29
  • I suppose you mentioned `windows`. That is the reason my answer was tailored to `windows` in specific. – mishsx Aug 25 '19 at 15:20
  • For Linux you can refer - https://github.com/h3llrais3r/Deluge-PreventSuspendPlus/tree/master/preventsuspendplus – mishsx Aug 25 '19 at 15:23
4

Created sample TK application to keep windows awake

import tkinter as tk
import ctypes
import sys

def display_on():
    print("Always On")
    ctypes.windll.kernel32.SetThreadExecutionState(0x80000002)

def display_reset():
    ctypes.windll.kernel32.SetThreadExecutionState(0x80000000)
    sys.exit(0)


root = tk.Tk()
root.geometry("200x60")
root.title("Display App")
frame = tk.Frame(root)
frame.pack()

button = tk.Button(frame,
                   text="Quit",
                   fg="red",
                   command=display_reset)
button.pack(side=tk.LEFT)
slogan = tk.Button(frame,
                   text="Always ON",
                   command=display_on)
slogan.pack(side=tk.LEFT)

root.mainloop()
Devil
  • 91
  • 2
1

The logic in Keep.Awake will solve your problem on ubuntu or any Linux Distro running Gnome (old and new including Unity), works on both Wayland and X. It is easy to use.

I posted a solution to a similar question here: https://askubuntu.com/a/1231975/183131

So logic-wise do the following:

  1. Use DBus command via subprocess.Popen(...) to clear the sleep/suspend counter.
  2. Use psutil.cpu_percent() to query the the amount of cpu usage periodically or just put logic once your program is done doing its thing to re-set the sleep config.

You can view the code here for details or hints on how you can mod your code: https://launchpad.net/keep.awake

Alternatively you can just run keepawake.py on the Linux box you're running your cpu-intensive program on and it will solve your problem! It just works!

Example usage taken from the webpage:

To run as background service and set minimum CPU load as 13%:

nohup ./keepawake.py -c 13 -r > /dev/null 2>&1 &

To run as background service and set 15 min (900 sec) as the user activity idle time before it determines that the user is idle:

nohup ./keepawake.py -u 900 -r > /dev/null 2>&1 &

To run as background service and set minimum network traffic as 5KB (5120 bytes):

nohup ./keepawake.py -s 5120 -r > /dev/null 2>&1 &

To run as background service and set the schedule to sleep/suspend after 1 hour (this value is only set if user-activity, cpu, and network traffic are all determined to be idle) :

nohup ./keepawake.py -w 3600 -r > /dev/null 2>&1 &

To run all settings above (network, CPU, User idle, sleep schedule) in the one go and set the log-file path to "/home/$USER/sleep/log/Keep.Awake/" with detailed output:

nohup ./keepawake.py -s 5120 -c 13 -u 900 -w 3600 -l /home/$USER/sleep/log/Keep.Awake/ -v Detail -r > /dev/null 2>&1 &
1

Based on multiple approaches I've found throughout the internet I've come up with this module below. Special thanks to @mishsx for the Windows workaround.

Using it is very simple. You may opt for the decorator approach using standby_lock or via StandbyLock as a context manager:

## decorator
@standby_lock
def foo(*args, **kwargs):
    # do something lazy here...
    pass

## context manager
with StandbyLock():
    # ...or do something lazy here instead
    pass

While foo is executing, your system will stay awake.

Note: There are still some caveats, as Linux may require sudo privileges and OS X (Darwin) is not tested yet.

from functools import wraps
import platform

class MetaStandbyLock(type):
    """
    """

    SYSTEM = platform.system()

    def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
        if not ('inhibit' in attrs and 'release' in attrs):
            raise TypeError("Missing implementations for classmethods 'inhibit(cls)' and 'release(cls)'.")
        else:
            if name == 'StandbyLock':
                cls._superclass = super().__new__(cls, name, bases, attrs)
                return cls._superclass
            if cls.SYSTEM.upper() in name.upper():
                if not hasattr(cls, '_superclass'):
                    raise ValueError("Class 'StandbyLock' must be implemented.")
                cls._superclass._subclass = super().__new__(cls, name, bases, attrs)
                return cls._superclass._subclass
            else:
                return super().__new__(cls, name, bases, attrs)

class StandbyLock(metaclass=MetaStandbyLock):
    """
    """

    _subclass = None

    @classmethod
    def inhibit(cls):
        if cls._subclass is None:
            raise OSError(f"There is no 'StandbyLock' implementation for OS '{platform.system()}'.")
        else:
            return cls._subclass.inhibit()

    @classmethod
    def release(cls):
        if cls._subclass is None:
            raise OSError(f"There is no 'StandbyLock' implementation for OS '{platform.system()}'.")
        else:
            return cls._subclass.release()

    def __enter__(self, *args, **kwargs):
        self.inhibit()
        return self

    def __exit__(self, *args, **kwargs):
        self.release()

class WindowsStandbyLock(StandbyLock):
    """
    """

    ES_CONTINUOUS      = 0x80000000
    ES_SYSTEM_REQUIRED = 0x00000001

    INHIBIT = ES_CONTINUOUS | ES_SYSTEM_REQUIRED
    RELEASE = ES_CONTINUOUS

    @classmethod
    def inhibit(cls):
        import ctypes
        ctypes.windll.kernel32.SetThreadExecutionState(cls.INHIBIT)

    @classmethod
    def release(cls):
        import ctypes
        ctypes.windll.kernel32.SetThreadExecutionState(cls.RELEASE)

class LinuxStandbyLock(metaclass=MetaStandbyLock):
    """
    """

    COMMAND = 'systemctl'
    ARGS = ['sleep.target', 'suspend.target', 'hibernate.target', 'hybrid-sleep.target']

    @classmethod
    def inhibit(cls):
        import subprocess
        subprocess.run([cls.COMMAND, 'mask', *cls.ARGS])

    @classmethod
    def release(cls):
        import subprocess
        subprocess.run([cls.COMMAND, 'unmask', *cls.ARGS])

class DarwinStandbyLock(metaclass=MetaStandbyLock):
    """
    """

    COMMAND = 'caffeinate'
    BREAK = b'\003'

    _process = None

    @classmethod
    def inhibit(cls):
        from subprocess import Popen, PIPE
        cls._process = Popen([cls.COMMAND], stdin=PIPE, stdout=PIPE)

    @classmethod
    def release(cls):
        cls._process.stdin.write(cls.BREAK)
        cls._process.stdin.flush()
        cls._process.stdin.close()
        cls._process.wait()

def standby_lock(callback):
    """ standby_lock(callable) -> callable
        This decorator guarantees that the system will not enter standby mode while 'callable' is running.
    """
    @wraps(callback)
    def new_callback(*args, **kwargs):
        with StandbyLock():
            return callback(*args, **kwargs)
    return new_callback
Pedro
  • 1,009
  • 6
  • 14
  • 1
    Great solution! Would you mind releasing that as a PyPI package? – Ben Hagen Sep 04 '20 at 10:53
  • 1
    This is very nice. Did you test the linux/osx versions yet? One caveat in the decorator and context manager approaches is that if you happen to use it twice, even in different processes, the first one exiting will remove the "lock". – np8 Jan 10 '21 at 21:03
  • Thanks for your advice! My approach was indeed very simple and it just didn't occoured to me considering a multiprocess scenario. I guess I will follow @BenHagen's request and build a pypi package soon, taking into account multiprocessing stuff. – Pedro Jan 13 '21 at 01:36
  • Hey, I also just put up a PyPI package [wakepy](https://github.com/np-8/wakepy) for Windows use case, since I did not find such package. I could include to linux/OS X solutions, but I am not able to test them well. – np8 Jan 13 '21 at 19:59
0

While googling around to find a solution there was no package available, so I decided to package it to put it to PyPI: wakepy. I got PRs for cross-platform support, and currently wakepy supports Windows, Linux and macOS.

CLI

python -m wakepy [-s]

Using the optional -s flag will also keep the screen on.

Python API

from wakepy import set_keepawake, unset_keepawake

set_keepawake(keep_screen_awake=False)
# do stuff that takes long time
unset_keepawake()
np8
  • 14,736
  • 8
  • 50
  • 67