Git Product home page Git Product logo

wakepy's Introduction

GitHub tag (latest by date) PyPI PyPI - Downloads Ruff mypy mypy coverage pyversions License

⏰😴 wakepy

Cross-platform wakelock / keep-awake / stay-awake written in Python.

What is wakepy?

Wakepy is a package with an Python API and a CLI tool for keeping a system awake. It has two main modes:

Keeping CPU awake: For long running tasks. Inhibit the automatic, timer based sleep or suspend action, but allow screenlock and screensaver turning on and monitor turning off. E.g. for training machine learning models, video encoding and web scraping. (See: keep.running)

🖥️ Keeping screen awake: For long running tasks which require also the screen on and screenlock and screensaver inhibited. E.g. for showing a video and dashboard / monitoring apps. (See: keep.presenting)

Supported runtime environments

Wakepy may keep the following systems awake. ⌛: keep.running mode, 🖥️:keep.presenting mode.

Runtime environment Methods Modes
Windows[1] SetThreadExecutionState ⌛ 🖥️
macOS[2] caffeinate ⌛ 🖥️
Unix + GNOME[3] org.gnome.SessionManager
org.freedesktop.ScreenSaver
⌛ 🖥️
Unix + KDE Plasma[4] org.freedesktop.PowerManagement
org.freedesktop.ScreenSaver
⌛ 🖥️
Unix + Freedesktop.org DE[5] org.freedesktop.PowerManagement
org.freedesktop.ScreenSaver
⌛ 🖥️

Unix above refers to Linux in wakepy 0.9.x, but upcoming releases of wakepy will support any Unix-like systems, e.g. FreeBSD (wakepy/#359). See also: Wakepy roadmap.

Installing

Wakepy supports CPython 3.7 to 3.13 (PyPy support: wakepy/#274), and may be installed with

pip install wakepy

To get the wakepy CLI command working, you might need to restart the shell / terminal application.

Why wakepy?

Here's some reasons why you might want to consider using wakepy:

🙅🏼‍♂️ Non-disruptive methods ✅
No mouse wiggling or pressing random keys like F15. Wakepy is completely non-disruptive. It uses the APIs and programs the system provides for keeping a system awake.
🛡️ Safe to crash 💥
No changing of any system settings; killing the process abruptly will not leave the keepawake on, and will not require any manual clean-up.
🚨 For security reasons 🔒
With keep.running mode you can disable just the automatic suspend and keep the automatic screen lock untouched.
🌐 You need a cross-platform solution 🦸
Same code works on Windows, macOS and Linux on multiple different Desktop Environments.
💪 You want to have more control ⚙️
It is possible to whitelist or blacklist the used wakepy Methods. It is also possible to prioritize them and define a on-fail action in case activating a wakepy mode fails.
✂️ You want to keep the amount of dependencies low 📦
If you're running wakepy on Linux, jeepney (a dependecy free package) is required for D-Bus based methods. On Python 3.7, typing-extensions is needed for typing. Otherwise: wakepy has no python dependencies.
⚖️ Package needs to have a permissive licence ✔️
Wakepy is licenced under permissive MIT License.

Command line interface (CLI)

To keep system from sleeping, run

wakepy

For presentation mode, add -p flag. See also: CLI API.

Basic usage within Python

In the simplest case, keeping a system running long running task with wakepy would be in python (See: keep.running):

from wakepy import keep

with keep.running():
    # Do something that takes a long time. The system may start screensaver
    # / screenlock or blank the screen, but CPU will keep running.

If you want to also prevent screen lock and screen blank, use the keep.presenting mode:

from wakepy import keep

with keep.presenting():
    # Do something that takes a long time and requires the screen to be awake

Tip

See the User Guide and the available wakepy Modes and Methods

Note

Wakepy API is still experimental 🚧

Since wakepy is still 0.x.x, the API might change without further notice from one release to another. After that, breaking changes should occur only part of a major release (e.g. 1.x.x -> 2.0.0).

Where wakepy is used?

Links

Wakepy roadmap

Wakepy vision is to support any environment which runs Python. The following runtime environments will get support in the future. Please vote or comment on the issue to raise them towards top of priorities. I'm also happy to receive PRs or comments explaining how it could be implemented.

: if technically possible.

Runtime environment Issue
WSL wakepy/#36
cygwin wakepy/#363
Unix + Cinnamon wakepy/#312
Unix + Xfce wakepy/#311
Unix + Mate wakepy/#314
Unix + LXQt wakepy/#313
Unix + systemd wakepy/#335
ChromeOS wakepy/#364
Android wakepy/#358
Jupyter Notebook (hosted on eg. Google Colab) wakepy/#195
Browser (Pyodide, PyPy.js, Brython, Transcrypt, Skulpt) wakepy/#362

In addition, supporting PyPy is on the roadmap. If you have ideas or comments, please post yours on wakepy/#317.


Footnotes

[1] Windows XP or higher. Windows Server 2003 or higher.
[2] Mac OS X 10.8 Mountain Lion (July 2012) or newer.
[3] GNOME 2.24 (Sept 2008) onwards.
[4] KDE Plasma 5.12.90 (May 2018) onwards.
[5] Freedesktop.org compliant Desktop Environments on Unix-line (Linux/BSD) system which implements the listed D-Bus interfaces.

wakepy's People

Contributors

aymane11 avatar fohrloop avatar hfortney avatar ipeevski avatar mikeckennedy avatar mohammad-mohsen avatar rileyyy avatar stehlampe2020 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

wakepy's Issues

ModeController class

Finalize the ModeController class, which should have following interface:

class ModeController:
    def __init__(self, call_processor: CallProcessor):
        ...
    def activate(
        self,
        method_classes: list[Type[Method]],
        methods_priority: Optional[MethodsPriorityOrder] = None,
    ) -> ActivationResult:
        ...
    def deactivate(self):
        ...

Error on macOS + Python 3

Hey! Trying this on macOS and Python 3.9 I got:

Traceback (most recent call last):
  File "/Users/michaelkennedy/Desktop/wakepy/example_app/macos/macos_app.py", line 18, in <module>
    wakepy.unset_keepawake()
  File "/Users/michaelkennedy/Desktop/wakepy/wakepy/_darwin.py", line 38, in unset_keepawake
    _process.stdin.write(BREAK)
TypeError: a bytes-like object is required, not 'str'

I'll submit a PR to fix this in a moment.

Make it possible to select what happens with errors

The current implementation simply raises NotImplementedError on linux if none of the existing methods (jeepney, dbus-python, systemd) can be used. It would be nicer for the user to be able to control what happens if using keepawake is not possible.

Currently, if someone would like to continue when setting keepawake is not possible, something like this must be used:

Option A

try:
    from wakepy import keepawake
except NotImplementedError:
	# Make a mock for replacing keepawake..
	
    from contextlib import contextmanager
	@contextmanager
	def keepawake(*args, **kwargs):
		yield


... # some code

with keepawake():
    # do something

Option B

try:
    from wakepy import set_keepawake, unset_keepawake
except NotImplementedError:
    # Make mocks for replacing set_keepawake and unset_keepawake..	
    set_keepawake = lambda:None
    unset_keepawake = lambda:None


... # some code

set_keepawake()
# do something
unset_keepawake()

Idea

It would be nicer if the user could decide what occurs when:
(a) A keepawake method fails (dbus address not found, etc..) --> on_method_failure
(b) Setting keepawake fails completely --> on_failure

Then, code could look something like this:

from wakepy import keepawake

with keepawake(on_failure='warn'):
    # do something

This is much cleaner code and nicer to the user. Suggested options for on_failure and on_method_failure could be

  • error (raise wakepy.KeepAwakeError) -- default for on_failure?
  • warn (warnings.warn)
  • logwarn (logger.warning)
  • loginfo (logger.info) -- default for on_method_failure?
  • logdebug (logger.debug)
  • pass (do nothing)
  • callable (custom callable called with single string as argument)

So, on linux, with multiple methods defined: keepawake(method_linux=('jeepney', 'systemd')): would, in absense of dbus address and sudo rights, by default:

  • Fail jeepney (dbus) because no DBUS_SESSION_BUS_ADDRESS found in environment variables. Tell that with logger.info
  • Fail systemd because no sudo rights. Tell that with logger.info
  • Fail to set keepawake completely as no more methods to try. Raise KeepAwakeError and tell that jeepney cannot be used because DBUS_SESSION_BUS_ADDRESS is not set and systemd cannot be used because sudo rights are missing.

rename tags to start with v

When checking out a branch and pressing tab, git shows a list of branches and tags sorted alphabetically. This puts all the tags starting with a digit to the front of everything, like this:

(venv) ~/code/wakepy$ git checkout 
0.2.0                       0.5.0                       FETCH_HEAD                  ORIG_HEAD 
0.3.0                       0.6.0                       HEAD                        origin/HEAD 
0.3.1                       0.7.0                       issue-52-activationresult   origin/master 
0.4.2                       0.7.1                       master  

It is not uncommon to have tens or 100+ tags in a project. Perhaps better to have them in the end (start with v). This seems to be a common practice, used for example in matplotlib, scipy, pandas, numpy, pydantic and rich (but not everyone, like pip)

Task:

  • Rename tags from [major].[minor].[patch] to v[major].[minor].[patch]
  • Update developer docs

Support multiprocessing

It might be interesting to have multiprocessing-supporting approach that uses a counter under the hood; for each set_keepawake() call there should be unset_keepawake() and for example if one calls set_keepawake() three times, the unset_keepawake() should be called three times before it would actually bet unset. In current implementation, if there were three long running processes (2hrs, 30hrs and 30hrs), and all of them would be using unset_keepawake(), PC would sleep soon after 2hrs.

Another possible implementation would be to use mouse movement or pressing a key, but these typically need some (or a lot) of other dependencies.

Update the CLI for 0.8.0

The cli.py needs some attention before 0.8.0 release. In particular, the deprecation message needs to be removed (and all the deprecated things).

Linux D-Bus method for keep.running / keepawake does not lock screen automatically

Affected versions: at least 0.6.0, 0.7.0, 0.7.1

Description:

The D-Bus method used on Linux uses org.freedesktop.ScreenSaver interface, Inhibit method to take an inhibitor lock. This causes the screen not to lock at all, even if using keepawake(keep_screen_awake=False) (wakepy <=0.6.0) or keep.running() (wakepy >=0.7.0). This is known and mentioned in documentation.

Task

Use some another method on Linux with keep.running(), which only inhibits suspend (and does not inhibit screesaver / screen lock).

Function to activate "mode" with a single Method

Splitting this work from #69 into its own ticket.

Motivation

  • Method class in current main branch (unreleased) has too much responsibility. Make the Method class to be just for definitions of the Method. This will be used for subclassing.
  • a activate_using(method, ...) function could be used to activate a "mode" with just a Method class; no need for Mode. This is going to be a helper function in #69.

Task

  • Refactor the Method class as described above
  • Create a activate_using function which looks something like this
def activate_using(methodcls: MethodCls, ...) -> tuple[MethodUsageResult, HeartBeat|None]:
    method = methodcls()
    if check_platform_support(method) is False:
         return MethodUsageResult(failure_stage=PLATFORM_SUPPORT), None
    if check_requirements(method) is False:
        return MethodUsageResult(failure_stage=REQUIREMENTS), None
    entered = try_enter_mode(method)
    heartbeat_started = try_heartbeat(method)
    if not entered or not heartbeat_started:
        return MethodUsageResult(failure_stage=ACTIVATION), None
    if heartbeat_started:
        heartbeat = Heartbeat(method)
        heartbeat.start()
    return MethodUsageResult(), heartbeat
  • In this first version, the returned heartbeat can always be None and there will be separate task for creating the Heartbeat class. The Heartbeat might not be part of 0.8.0 release as there are no heartbeat-based Methods yet.
  • Create the required functions for checking platform support, checking requirements, entering a mode and calling heartbeat. Clean up the old implementations.

Clarify requirements for each method - systemd, dbus-send, dbus-daemon..? (linux)

There are multiple possible ways to set a keepawake / inhibitor on linux:

  1. Using D-Bus: Jeepney + org.freedesktop.ScreenSaver
  • Requires: jeepney python package, dbus-daemon running and DBUS_SESSION_BUS_ADDRESS available
  1. Using D-Bus: Calling dbus-send + org.freedesktop.ScreenSaver (?)
  • Requires dbus-send (+ probably a dbus-daemon running)
  1. Using systemd (systemctl mask)
  • This required sudo. Does it need dbus-daemon?
  1. Using dbus-python (with libdbus)
  • This requires: dbus-python (python package) and libdbus

These need clarification

Q1) If a system has dbus-daemon running, does it also can dbus-send executable available? Could dbus-send replace jeepney solution? Or can jeepney be used as dbus solution in a situation where dbus-send executable is not available?

Q2) If a system does have systemd (systemctl command) available, does it automatically mean that dbus is available? Could systemd solution be dropped in favor of a dbus solution? Does the systemctl command use dbus internally?

Q3) In which situations dbus-python would be needed? (Related: #32 )

Edit: Added dbus-python

Non-DBUS based methods on GNOME

It would be nice to have non-DBus based on methods available on GNOME. This would make D-Bus (and the python package jeepney) an optional dependency on GNOME.

Task:

  • Search for non-DBus based keep.running and keep.presenting methods on GNOME
  • If there are any, implement those with the new API; using wakepy.core.Method

Split responsibilities to Mode, Method, activate() and activate_using() (v. 0.8.0)

This is basically a ticket to myself, and is continuation of work in the PR #51, which left the main branch in an unfinished and bit messy state.

Modes

  • Clean up code for Mode. The purpose of Mode is to simply provide the main API and context manager for the user. Everything else should be handled elsewhere. (PR #70)
  • Remove creation of DBusAdapter instances from Modes. Pass the DBusAdapter class to a ModeActivationManager -> ModeActivator -> Caller and create an instance of the adapter there (in separate thread) (PR #70)
  • Remove prioritization of methods from Modes. Prioritization should be done in ModeActivator. (PR #70)
  • Implement Mode.__exit__ and add ModeExit exception which can be used to exit the mode with statement block. (PR #72)
  • The callables for Modes like keep.running and keep.presenting should be functions which return instances of Modes. Calling keep.running() should call return a fresh instance of Mode (for supporting running wakepy in multiple threads) (PR #71)
  • Create tests for Mode (PR #71)

Deprecated

work done on this ticket but not relevant anymore

  • Create a ModeActivationManager class. (PR #76)
    using the queues.

Make it possible for the user to select the used method

On linux, there are multiple methods which could be used:

  • jeepney (dbus without libdbus)
  • dbus-python (dbus with libdbus)
  • systemd

These all are tried in order. It would be nicer for the user if the method could be selected when setting the keepawake. An iterable of methods could also be accepted.

Possible syntax:

with keepawake(method_linux='jeepney'):
   ...

or

with keepawake(method_linux=('jeepney', 'systemd')):
   ...

etc.

Context manager

Hi, wouldn't it make sense to treat the disabled sleep mode as context, which could be returned by set_keepawake() and used for automatic unset_keepawake()?

A new return value? (mode.active instead of result.success)

If has been thought that wakepy 0.7.0+ would look like this:

from wakepy import keep

with keep.running() as k:
    if not k.success:
        # warn user
    # do something

where k is an ActivationResult object.

But would it be nicer if the API was

with keep.running() as k:
    if not k.active:
        # warn user
    # do something

I.e. the word was "active" instead of "success"? In addition, the return value from Mode.__enter__() could be the Mode (self), and not an ActivationResult? Thinking about how well this reads:

from wakepy import keep

# create a mode instance
mode = keep.running()

with mode as m:
   if not m.active:
       # warn user
  # do something

if m is the mode (instance of Mode) , this reads pretty well. Not so sure if the m is a ActivationResult instance. The full activation result could be put into .res or .result attribute of Mode.

Wakepy and pyinstaller - does it works?

Hello,

I have a python program packed with pyinstaller which uses wakepy. After packing for windows 10 .exe program is halted when wakepy with keep.running() is called. Have you tested it with pyinstaller?

Documentation - How will killing while lock set affect it?

Hi, thanks for this :)
While super simple, it's always good to have a cross-platform package, especially when it's source code is easy to read and understand.

One thing that could be a factor in decisions on whether to use this is how force killing a process (Task manager in windows, kill in Linux etc) will affect it?

I've tested on Windows, and it seems that killing the python process successfully removes the lock (makes sense that SetThreadExecutionState would die with the process).

But is this the same for linux / mac?
Regardless of result it could be good to document in the readme.

Alpha version for ActivationResult (data storage & retrieval)

Background

The modes in wakepy current 0.8.0dev return a ActivationResult object, like this:

with keep.running() as k:
    if not k.success:
        print('failed to set keepawake')

here k is an ActivationResult object.

Task

Edit: Marking Communication with the thread which switches the mode out of scope. In scope: Just the data storage and retrieval mechanism for ActivationResult

  • Make constructing ActivationResult objects work.
  • Properties:
    • success
    • failure
    • real_success
  • Methods:
    • get_details()
  • Create unit tests for it.

wakepy crashes a program which has been built into an executable

The following program (I used pyinstaller to make an executable) immediately crashes - see below.

from wakepy import keep
import time

with keep.running() as k:
    while True:
        time.sleep(10)

Stack backtrace:
D:\PyCharm Projects\keepAwake\dist>keepawake.exe
Traceback (most recent call last):
File "main.py", line 4, in
File "contextlib.py", line 117, in enter
File "wakepy\keep.py", line 87, in running
File "wakepy\keep.py", line 45, in temporary_solution_for_using_old_functions_with_new_api
File "wakepy_deprecated_init
.py", line 37, in get_function
File "importlib_init_.py", line 127, in import_module
File "", line 1030, in _gcd_import
File "", line 1007, in _find_and_load
File "", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'wakepy._deprecated._windows'
[12692] Failed to execute script 'main' due to unhandled exception!

Still going to sleep - running Fedora 36

I'm running Fedora 36 on a Lenovo Legion 7, and I ran the simple code:

from wakepy import set_keepawake, unset_keepawake
set_keepawake()

Yet my computer still goes to sleep after a few minutes. What's happening?

Support for Heartbeat-type of methods

Splitting this work from #69 into its own ticket.

Motivation

  • Some methods might need a "heartbeat"; some call (executable, dbus, ...) done periodically.

Task

  • Create a Hearbeat class which runs the Method.heartbeat() in separate thread. It uses the Callers to process calls.

As there are not yet any Methods which utilize a heartbeat, this can be postponed until some release after the 0.8.0.

Consider making D-Bus support install optional

Wakepy has one dependency on linux: jeepney, which provides dbus support. There are yet no D-Bus free methods on linux, but there is a ticket (#64) for searching for such methods on GNOME.

It could be possible to either:
A) Make just the necessities install by default and have special options for adding something.
B) Make everything install by default and have special options for leaving something out

Alternative A1

Consider making the jeepney dependency optional. This means,

pip install wakepy

would install only wakepy and nothing else, ever. Trying to use wakepy when non-DBus based methods are not available, would not succeed:

with keep.presenting() as k:
    if not k.success:
        print('Failed setting keepawake')

Installing:

pip install wakepy[dbus]

would install the supported dbus package (jeepney).

Pros:

  • Installing wakepy would not need any dependencies

Cons:

  • Wakepy does not work without the extras on all systems. This is an inconvenience, although it might be possible to find a set of non-DBus based methods?

Alternative B1

Keep installing a dbus python package by default when running

pip install wakepy

Users can opt-out from the dbus with

pip install wakepy --no-deps

Pros:

  • Wakepy would work out of the box with easy to remember "pip install" name in more use cases

Cons:

  • Not possible to determine --no-deps in requirements.txt (or setup.py?); Users wanting to opt-out need to install wakepy separately.
  • Opting out from dbus will opt-out from anything else (if something else needed some day?); this will probably not work well with pip install wakepy[some-extra]

Alternative B2

Ideally, pip install wakepy would install wakepy with DBus and there would be another extras which would remove the dbus python package dependency:

pip install wakepy[no-dbus]

This would be super handy as only experienced users might want to drop out this dependency. Unfortunately this does not seem to be supported by pip.

Pros:

  • The cleanest solution?

Cons:

  • Currently technically impossible.

Alternative B3

Taken from a SO answer: Use environment variables to determine the D-Bus need. It could be either

NODBUS=1

or making use of the DBUS_SESSION_BUS_ADDRESS which should be set in most of the cases if D-Bus is available (if that is set, install D-Bus dependencies automatically, otherwise not). The example had a setup.py file like this:

import os
from setuptools import setup

install_requires_base = [...]

setup(
    ...
    install_requires=install_requires_base  + ([] if os.getenv('NODBUS', False) else ['jeepney']),
    ...
)

I'm not sure if this would be possible with pyproject.toml or not.

Pros:

  • Simple for average user, but also possible to opt-out for some extras.

Cons:

  • Uses setup.py. Already transitioned to pyproject.toml, and no wishes to go back to setup.py if not absolutely required. Pyproject.toml has wider range of support, for example, for automatic dependency / reverse dependency resolution.

Alternative B4

Make

pip install wakepy

to install the default set of dependencies. Jeepney (or some other dbus python package) would be automatically included on systems like linux. This is because an average user should not have to think more than pip install wakepy. Behind the scenes this would install some wakepy-vanilla which is the base version and then for example jeepney for dbus. In the future, this default set could evolve, but users could rely on the fact that anything needed for the typical use case is installed automaticlaly. This would not be same as installing "ALL" possible extras, like some move-mouse-package (does not exist). Then, power users could then use

pip install wakepy-vanilla

For installing the vanilla version, or even using wakepy-vanilla[dbus] for just dbus extras.

Motivation

  • Most of the users (the average user) does not want to read out the documentation to make the package work with dbus based methods. The barrier for installing and using wakepy should be as low as possible to the average user.
  • Wakepy should work out of the box on linux when dbus-based methods are required. As wakepy won't raise an Exception on unsuccesful wakelock set, this is even more important! It would not feel nice for the user to find out that pip install wakepy did not install a required package for their system and that when setting the wakelock, there was no exceptions and they have to dig out the documentation to see why their system is still going to sleep.
  • Power users, on the other hand, know how to read documentation and how to tweak their install scripts or requirements file. If a power user knows they will not need dbus, they can opt-out. Opting out can be more difficult than opting in, as it is not the default route, and people wanting to opt-out know what they're doing.

Pros:

  • Everything is simple for the average user
  • Everything is possible for the power user

Cons:

  • Have to built two packages: wakepy and wakepy-vanilla.

Edit: Renamed alternatives (A, B, C, D) to (A1, B1, B2, B3). Added alternative B4.

Add tests for the CLI

There was previously (wakepy v.0.7.1) an exception thrown on older versions (< 3.9) of Python because of unsupported type annotations (#50). It would be handy to have automatic tests for the __main__.py / cli part, too.

Loading module on Linux raise `FileNotFoundError: [Errno 2] No such file or directory: 'pidof systemd'`

When trying to load this module on Linux system with systemd, the module fail with the following trace:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/subprocess.py", line 411, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
  File "/usr/lib/python3.8/subprocess.py", line 489, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.8/subprocess.py", line 1702, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'pidof systemd'

The line subprocess.check_output('pidof systemd') should be replace by subprocess.check_output(['pidof','systemd'])

Using D-Bus for requesting a wakelock

Pros

  • It works great on my Manjaro PC.
  • It's nice and clean (IMHO).
  • It doesn't require superuser privileges.

Cons

  • It requires a cookie for removing the wakelock, which is not quite supported by the current architecture of wakepy.
    • This is why I didn't open a Pull Request in the first place. If you would like to, I could do so in the future.

Neutral

  • I'm not sure how good the support for this is compared to the current systemd method. Maybe even better? I haven't heard of distros without D-Bus, contrary to distros without systemd.
  • I have little knowledge of D-Bus etc., so this proposal comes in the sense of “it works for me”.

I've written a small proof of concept:

from contextlib import contextmanager
import dbus
import time


@contextmanager
def inhibit(application, reason):
    bus = dbus.SessionBus()
    obj = bus.get_object('org.freedesktop.PowerManagement.Inhibit',
                         '/org/freedesktop/PowerManagement/Inhibit')
    inhibitMethod = obj.get_dbus_method('Inhibit', 'org.freedesktop.PowerManagement.Inhibit')
    unInhibitMethod = obj.get_dbus_method('UnInhibit', 'org.freedesktop.PowerManagement.Inhibit')

    cookie = inhibitMethod(application, reason)
    try:
        yield cookie
    finally:
        unInhibitMethod(cookie)


print('Starting…')
with inhibit('TestApp', 'TestReason'):
    time.sleep(10)
print('Done!')

Documentation: Linux Mint + wakepy behaviour when battery dies while wakelock set

I had my battery running low on my laptop with Linux Mint 21.1 with Cinnamon.
I set the lock and waited.
When the battery reached 2% the computer shut down as if I had pressed "Shut Down" even though the setting what to do when the battery dies was set to "Do Nothing".
After starting up again the option "suspend" was missing from the power menu, pressing the power button (set to suspend) did nothing and systemctl suspend errored out. Everything returned to normal after running wakepi.unset_keepawake() in Python3, even though I set the lock with

import wakepy, time
with wakepy.keepawake(True):
    while True:
        time.sleep(3)
        print('Another 3 seconds!')

Failing to import from wakepy when running test with tox (KeyError: 'DBUS_SESSION_BUS_ADDRESS')

Setup:

  • System: Ubuntu (with dbus installed)
  • Running with tox, which runs pytest

What happens?

When calling

from wakepy import set_keepawake, unset_keepawake

user will get:

KeyError: 'DBUS_SESSION_BUS_ADDRESS'

Steps to reproduce

  1. Create some tox config file, e.g. tox.ini with following contents
[tox]
env_list = py{38,39,310,311}
minversion = 4.4.11

[testenv]
description = run the tests with pytest
package = wheel
wheel_build_env = .pkg
deps =
    pytest>=6
commands =
    pytest {tty:--color=yes} {posargs}
  1. Create simple test which imports set_keepawake:
# tests/test_wakepy.py
def test_import_set_and_unset_keeepawake():
    from wakepy import set_keepawake, unset_keepawake
  1. Run tox:
niko@niko-ubuntu-home:~/tmp/repos/wakepy$ tox -e py310
.pkg: _optional_hooks> python /home/niko/venvs/wakepy/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: get_requires_for_build_wheel> python /home/niko/venvs/wakepy/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
.pkg: build_wheel> python /home/niko/venvs/wakepy/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
py310: install_package> python -I -m pip install --force-reinstall --no-deps /home/niko/tmp/repos/wakepy/.tox/.tmp/package/10/wakepy-0.6.0-py3-none-any.whl
py310: commands[0]> pytest --color=yes
=================================================================== test session starts ====================================================================
platform linux -- Python 3.10.6, pytest-7.2.2, pluggy-1.0.0
cachedir: .tox/py310/.pytest_cache
rootdir: /home/niko/tmp/repos/wakepy
collected 1 item                                                                                                                                           

tests/test_wakepy.py F                                                                                                                               [100%]

========================================================================= FAILURES =========================================================================
___________________________________________________________ test_import_set_and_unset_keeepawake ___________________________________________________________

    def test_import_set_and_unset_keeepawake():
>       from wakepy import set_keepawake, unset_keepawake

tests/test_wakepy.py:2: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/py310/lib/python3.10/site-packages/wakepy/__init__.py:35: in <module>
    from ._linux import set_keepawake, unset_keepawake
.tox/py310/lib/python3.10/site-packages/wakepy/_linux/__init__.py:17: in <module>
    my_module = import_module(f".{module}", f"wakepy._linux")
/usr/lib/python3.10/importlib/__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
.tox/py310/lib/python3.10/site-packages/wakepy/_linux/_jeepney_dbus.py:26: in <module>
    connection = open_dbus_connection(bus="SESSION")
.tox/py310/lib/python3.10/site-packages/jeepney/io/blocking.py:341: in open_dbus_connection
    bus_addr = get_bus(bus)
.tox/py310/lib/python3.10/site-packages/jeepney/bus.py:53: in get_bus
    return find_session_bus()
.tox/py310/lib/python3.10/site-packages/jeepney/bus.py:42: in find_session_bus
    addr = os.environ['DBUS_SESSION_BUS_ADDRESS']
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = environ({'HOME': '/home/niko', 'LANG': 'en_US.UTF-8', 'TERM': 'xterm-256color', 'PATH': '/home/niko/tmp/repos/wakepy/....NS': '156', 'LINES': '26', 'PYTEST_CURRENT_TEST': 'tests/test_wakepy.py::test_import_set_and_unset_keeepawake (call)'})
key = 'DBUS_SESSION_BUS_ADDRESS'

    def __getitem__(self, key):
        try:
            value = self._data[self.encodekey(key)]
        except KeyError:
            # raise KeyError with the original key value
>           raise KeyError(key) from None
E           KeyError: 'DBUS_SESSION_BUS_ADDRESS'

/usr/lib/python3.10/os.py:679: KeyError
================================================================= short test summary info ==================================================================
FAILED tests/test_wakepy.py::test_import_set_and_unset_keeepawake - KeyError: 'DBUS_SESSION_BUS_ADDRESS'
==================================================================== 1 failed in 0.08s =====================================================================
py310: exit 1 (0.33 seconds) /home/niko/tmp/repos/wakepy> pytest --color=yes pid=8462
.pkg: _exit> python /home/niko/venvs/wakepy/lib/python3.10/site-packages/pyproject_api/_backend.py True setuptools.build_meta __legacy__
  py310: FAIL code 1 (1.18=setup[0.84]+cmd[0.33] seconds)
  evaluation failed :( (1.22 seconds)

wakepy WSL support

Wakepy current is said to support windows and linux. But how about WSL? Does it work currently, and if not, what method should be used on WSL?

Documentation: Ubuntu Unity 22.04 on iMac + wakelock

Ubutu Unity 22.04 locks and turns off the screen (and forgets the screen brightness, which it always does on my iMac when the screen got turned off) after the set timeout even after running and not cancelling wakepy -s.
The "Suspend" system menu entry does nothing.

Create general dbus interface class

Create general D-Bus interface class + jeepney implementation of that class.

The class should have the functionality needed to do the things wakepy does currently, including:

  1. Getting a bus (session bus)
bus = DBus.get_bus('session')
  1. Calling a given method on dbus interface, object (path) and service (bus name). Should take signature as argument, too?
retval = bus.call_method(bus_name, object_path, interface, method, signature)

Add support for defining maximum number of used methods for setting a keepawake

A (keepawake) mode is activated by trying a set of different methods. It might be nice to be able to set a maximum number of used methods, so for example:

with keep.running(max_methods=None) as k:
   ...

could try to use all of the methods in keep.running.methods, but

with keep.running(max_methods=1) as k:
   ...

would stop at the first successful method.

Update docs for 0.8.0

Go through the docs and update them.

PART 1

PART 2

  • Make sure all methods are documented well
  • Check if it makes sense to have some code going through wakepy methods and making automatic or semi-automatic table from them. --> later.

Related PRs

macOS keep screen awake seems backwards

It looks like the code for keeping the screen awake is backwards. See the details for caffinate:

-d Create an assertion to prevent the display from sleeping

And the code (ARGS is -d -u):

    if keep_screen_awake:
        global _process
        _process = Popen([COMMAND], stdin=PIPE, stdout=PIPE)
    else:
        _process = Popen([COMMAND] + ARGS, stdin=PIPE, stdout=PIPE)

It's sending -d -u if we do NOT want to keep the screen awake. I'll send a PR.

Function to activate mode using a list of methods

Splitting this work from #69 into its own ticket.

Task

  • Create an activate() function which looks something like this
def activate(methods: list[MethodCls], ...) -> tuple[ActivationResult, HeartBeat|None]:
    prioritized_methods = prioritize_methods(methods)
    result = ActivationResult()

    for method in prioritized_methods:
        methodresult, heartbeat = activate_using(method, ...)
        result.add(methodresult)
        if result.success:
            break
    return result, heartbeat
  • In this first version, the returned heartbeat can always be None and there will be separate task for creating the Heartbeat class. The Heartbeat might not be part of 0.8.0 release as there are no heartbeat-based Methods yet.

Related PRs:

Prioritization and selecting used Methods

Think about how prioritization and selecting used Methods should work. What kind of API there should be? Would using list of strings be good, or should user need to use list of Method classes?

Tasks:

  • Think about how prioritization and selecting used Methods should work -- (below)
  • methods_priority dictionary for setting priority (PRs #90, #91, #97)
  • Possibility of getting methods based on mode (PR #88)
  • Possibility of selecting modes with "skip" and "use_only" (PR #89)
  • Add support for selecting and prioritizing methods to keep.running and keep.presenting (PR #97)

Detailed plan

This is detailed summary from all the messages from below. Will be updating this to reflect the latest view of how this is going to be implemented; no need to read all the messages below as much of it is obsolete and not relevant.

In the main thread

  • The API looks something like this (assuming keep.running has methods called A, B, C, D, E, F and G):
mode = keep.running(
    dont_use=['G'], priority_order = [{'A', 'B'}, '*', {'E', 'F'}]
)
  • The implementation looks something like this
def running(only_use:list[str], dont_use:list[str], priority_order:list[str|set[str]]) -> Mode:
    methods_for_mode = get_methods_for_mode(ModeName.KEEP_RUNNING)
    selected_methods = select_methods(methods_for_mode, only_use=only_use, dont_use=dont_use)
    return Mode(methods=selected_methods, priority_order=priority_order)
  • In Mode.__init__, check the priority order against the selected methods. (PR #91)

In the activation thread

  • Get a list of prioritized methods.
# methods: List[MethodCls]
# priority_order: List[str | Set[str]]
prioritized_methods: List[MethodCls] = get_prioritized_methods(methods, priority_order)
  • When resolving priority,
    (1) prioritize using the priority_order (PR #94) and
    (2) prioritize within each set automatically (PR #96) and
    (3) form a flat list (PR #96) ;
# let's say we have
priority_order = [{'A', 'B'}, '*', {'E', 'F'}]
methods = [A, B, C, D, E, F]

# (1) could make this
methods = [{A, B}, {C, D}, {E, F}]

# (2) could make this
methods = [[B, A], [C, D], [F, E]]

# (3) could make this
methods = [B, A, C, D, F, E]
  • Then, follow the normal mode activation process; in priority order, try to activate the mode using the prioritized methods.

Import failing when DBUS_SESSION_BUS_ADDRESS not available

Hej,

The issue originally stems from another project where I am importing wakepy and it is causing all sorts of issues in readthedocs generation. I was able to reproduce the issue locally though with just wakepy.

Presently running an Ubuntu 20.04.4 LTS VM on WSL.
While I did get dbus-x11 installed and running, it seems $DBUS_SESSION_BUS_ADDRESS isn't set and that is causing problems.
I'm not sure what else is missing but maybe it could be caught by expanding the except after the import dbus

$ sudo apt-get install dbus-x11
Reading package lists... Done
Building dependency tree
Reading state information... Done
dbus-x11 is already the newest version (1.12.16-2ubuntu2.3).
0 upgraded, 0 newly installed, 0 to remove and 136 not upgraded.
$ service dbus status
 * dbus is running
$ pip install dbus-python pydbus
Requirement already satisfied: dbus-python in /usr/lib/python3/dist-packages (1.2.16)
Requirement already satisfied: pydbus in /home/riley/.local/lib/python3.8/site-packages (0.6.0)
riley@OHENP-PW012RVH0:/mnt/c/SourceCode/PythonDriver$ python3
Python 3.8.10 (default, Jun 22 2022, 20:18:18)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import wakepy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/riley/.local/lib/python3.8/site-packages/wakepy/__init__.py", line 35, in <module>
    from ._linux import set_keepawake, unset_keepawake
  File "/home/riley/.local/lib/python3.8/site-packages/wakepy/_linux/__init__.py", line 17, in <module>
    my_module = import_module(f".{module}", f"wakepy._linux")
  File "/usr/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/home/riley/.local/lib/python3.8/site-packages/wakepy/_linux/_jeepney_dbus.py", line 26, in <module>
    connection = open_dbus_connection(bus="SESSION")
  File "/home/riley/.local/lib/python3.8/site-packages/jeepney/io/blocking.py", line 341, in open_dbus_connection
    bus_addr = get_bus(bus)
  File "/home/riley/.local/lib/python3.8/site-packages/jeepney/bus.py", line 53, in get_bus
    return find_session_bus()
  File "/home/riley/.local/lib/python3.8/site-packages/jeepney/bus.py", line 42, in find_session_bus
    addr = os.environ['DBUS_SESSION_BUS_ADDRESS']
  File "/usr/lib/python3.8/os.py", line 675, in __getitem__
    raise KeyError(key) from None
KeyError: 'DBUS_SESSION_BUS_ADDRESS'

Remove the deprecated parts

Some parts of wakepy, namely the contents of wakepy._deprecated subpackage, are deprecated in 0.7.0. Remove these.

Check for platform instead of system

The current implementation has three systems:

  • windows
  • linux
  • darwin

These are all actually just directly platform.system().lower().

Might be better the change the name to be "platform" as python might be running on WSL or Cygwin on Windows..? Not yet sure if these two can be supported but at least is should be possible to detect that wakepy is running on WSL/Cygwin.

Task:

  • Replace the SystemName with PlatformName class (or just platform module with constants WINDOWS, LINUX, and MACOS)
  • Create function which can detect these platforms: Windows, Linux, MacOS

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.