├── tests ├── __init__.py ├── unit │ ├── __init__.py │ ├── test_core │ │ ├── __init__.py │ │ ├── test_method │ │ │ ├── __init__.py │ │ │ └── test_method.py │ │ ├── test_heartbeat.py │ │ ├── test_constants.py │ │ ├── test_strenum.py │ │ ├── conftest.py │ │ ├── test_dbus.py │ │ ├── test_registry.py │ │ ├── test_calls.py │ │ ├── testmethods.py │ │ └── test_platform.py │ ├── test_methods │ │ ├── __init__.py │ │ ├── test_macos.py │ │ ├── test_gnome.py │ │ └── test_windows.py │ ├── test__init__.py │ ├── conftest.py │ ├── test_modes.py │ └── test_keep.py ├── integration │ ├── __init__.py │ ├── test_macos.py │ └── test_dbus │ │ ├── conftest.py │ │ └── dbus_service.py ├── helpers.py ├── conftest.py └── tox_build_wakepy.py ├── src └── wakepy │ ├── py.typed │ ├── modes │ └── __init__.py │ ├── dbus_adapters │ ├── __init__.py │ └── jeepney.py │ ├── core │ ├── heartbeat.py │ ├── __init__.py │ ├── constants.py │ ├── strenum.py │ ├── platform.py │ └── registry.py │ ├── methods │ ├── _testing.py │ ├── __init__.py │ ├── macos.py │ └── gnome.py │ └── __init__.py ├── docs ├── source │ ├── _templates │ │ ├── author.html │ │ └── sbt-sidebar-nav.html │ ├── cli-api.md │ ├── index.md │ ├── _static │ │ ├── wakepy-docs.js │ │ └── wakepy-docs.css │ ├── multithreading.md │ ├── installing.md │ ├── api-reference.md │ ├── migration.md │ ├── tests-and-ci.md │ ├── conf.py │ ├── post-keepawake-behavior.md │ └── modes.md ├── Makefile └── make.bat ├── .gitattributes ├── requirements ├── requirements-check.txt ├── requirements-mypy.txt ├── requirements-dev.txt ├── requirements-test.txt └── requirements-docs.txt ├── .gitignore ├── scripts ├── example-test.py └── example-test-with-wakepy.py ├── .readthedocs.yaml ├── LICENSE.txt ├── toxfile.py ├── .github └── workflows │ ├── publish-a-release.yml │ └── build-and-run-tests.yml ├── tasks.py ├── tox.ini ├── DEV.md └── pyproject.toml /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/wakepy/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/wakepy/modes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/test_core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/source/_templates/author.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.svg linguist-vendored 2 | -------------------------------------------------------------------------------- /tests/unit/test_core/test_method/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/requirements-check.txt: -------------------------------------------------------------------------------- 1 | # requirements for code style; linters, code analysis and formatting tools 2 | invoke==2.2.0 3 | black==24.8.0 4 | isort==5.13.2 5 | ruff==0.6.5 6 | -r requirements-mypy.txt 7 | -------------------------------------------------------------------------------- /src/wakepy/dbus_adapters/__init__.py: -------------------------------------------------------------------------------- 1 | """This package contains dbus adapters to be used with wakepy. 2 | 3 | Each dbus adapter represents one python dbus library. A dbus adapter should 4 | subclass the wakepy.core.DBusAdapter, and it may be used as an argument to 5 | wakepy modes (keep.running & keep.presenting). 6 | """ 7 | -------------------------------------------------------------------------------- /requirements/requirements-mypy.txt: -------------------------------------------------------------------------------- 1 | # This only supports Python 3.8+ 2 | mypy==1.11.2; python_version>='3.8' 3 | # Last mypy version to support Python 3.7 4 | mypy==1.4.1; python_version=='3.7' 5 | 6 | 7 | # For mypy: 8 | pytest -c requirements-test.txt 9 | types-colorama 10 | types-colorama==0.4.15.12; python_version>='3.7' 11 | 12 | -------------------------------------------------------------------------------- /tests/unit/test_core/test_heartbeat.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from wakepy import Method 4 | from wakepy.core.heartbeat import Heartbeat 5 | 6 | 7 | @pytest.fixture 8 | def method0() -> Method: 9 | return Method() 10 | 11 | 12 | class TestHeartBeat: 13 | 14 | def test_heartbeat(self, method0): 15 | 16 | hb = Heartbeat(method0) 17 | hb.start() 18 | hb.stop() 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created automatically by setuptools-scm 2 | src/wakepy/_version.py 3 | 4 | # python stuff 5 | __pycache__/ 6 | *.py[cod] 7 | *.egg-info/ 8 | 9 | # builds 10 | dist/ 11 | docs/build/ 12 | 13 | # Unit tests, checks, coverage reports 14 | htmlcov/ 15 | .tox/ 16 | .coverage 17 | .ruff_cache 18 | .mypy_cache 19 | 20 | # pyenv 21 | .python-version 22 | 23 | # Virtual environments 24 | .venv 25 | venv/ 26 | 27 | # IDEs 28 | .vscode/* 29 | .idea/* 30 | -------------------------------------------------------------------------------- /tests/unit/test_methods/__init__.py: -------------------------------------------------------------------------------- 1 | """Test all the different wakepy Methods which may be used to enter or keep a 2 | Mode. 3 | 4 | Tests include: 5 | * Test for using all the defined methods; for example, running `enter_mode()` 6 | and `exit_mode` using fake/mock adapters (DBusAdapter, etc.), or mocks for 7 | io related things. 8 | 9 | Tests Exclude: 10 | * IO of any kind & calling 3rd party executables 11 | * Usage of DBus services, even fake ones 12 | """ 13 | -------------------------------------------------------------------------------- /scripts/example-test.py: -------------------------------------------------------------------------------- 1 | """This is a simple script for testing wakepy (manually) 2 | """ 3 | 4 | import datetime as dt 5 | import time 6 | 7 | start = dt.datetime.now() 8 | now = None 9 | while True: 10 | prev = now or dt.datetime.now() 11 | now = dt.datetime.now() 12 | now_str = now.strftime("%b %d %H:%M:%S") 13 | delta_str = f"{(now-prev)/dt.timedelta(seconds=1)}s" 14 | print(f"{now_str} | elapsed {now-start} | delta: {delta_str}") 15 | time.sleep(2) 16 | -------------------------------------------------------------------------------- /tests/integration/test_macos.py: -------------------------------------------------------------------------------- 1 | import platform 2 | 3 | import pytest 4 | 5 | from wakepy import keep 6 | 7 | if platform.system() != "Darwin": 8 | pytest.skip("These tests are only for macOS", allow_module_level=True) 9 | 10 | 11 | class TestMacOS: 12 | def test_caffeinate_works(self): 13 | # Test that the caffeinate command can be run without errors 14 | with keep.running() as m: 15 | assert m.active is True 16 | assert str(m.method) == "caffeinate" 17 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | version: 2 6 | 7 | python: 8 | install: 9 | - method: pip 10 | path: . 11 | - requirements: requirements/requirements-docs.txt 12 | 13 | build: 14 | os: ubuntu-22.04 15 | tools: 16 | python: "3.10" 17 | jobs: 18 | pre_build: 19 | # Need to create the wakepy._version module (setuptools-scm) 20 | - python -m build 21 | 22 | 23 | sphinx: 24 | configuration: docs/source/conf.py 25 | -------------------------------------------------------------------------------- /tests/helpers.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | from wakepy import Method, MethodInfo 4 | 5 | TEST_MODE = "test-mode" 6 | 7 | 8 | @lru_cache(maxsize=None) 9 | def get_test_method(name: str, mode_name: str = TEST_MODE) -> Method: 10 | 11 | class TestMethod(Method): ... 12 | 13 | TestMethod.mode_name = mode_name 14 | TestMethod.name = name 15 | 16 | return TestMethod() 17 | 18 | 19 | def get_method_info(method_name: str, mode_name: str = TEST_MODE) -> MethodInfo: 20 | """Get a MethodInfo object for the given method name.""" 21 | method = get_test_method(method_name, mode_name=mode_name) 22 | return MethodInfo._from_method(method) 23 | -------------------------------------------------------------------------------- /requirements/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # These are the requirements for development. If you're installing wakepy 2 | # source tree for development (pip install -e .), add this file as a 3 | # requirement. The other requirements files are used for automation. 4 | -r requirements-test.txt 5 | -r requirements-docs.txt 6 | -r requirements-check.txt 7 | 8 | # On Windows, cannot use later version of sphinx-autobuild until 9 | # https://github.com/sphinx-doc/sphinx-autobuild/issues/197 is resolved. 10 | # See also: https://github.com/fohrloop/wakepy/issues/444 11 | sphinx-autobuild==2024.4.16 12 | IPython 13 | invoke==2.2.0 14 | # Colorama is used with the tasks.py (invoke commands) 15 | colorama==0.4.6 16 | -------------------------------------------------------------------------------- /src/wakepy/core/heartbeat.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import datetime as dt 4 | import typing 5 | 6 | if typing.TYPE_CHECKING: 7 | from typing import Optional 8 | 9 | from .method import Method 10 | 11 | 12 | class Heartbeat: 13 | # This is just temporary dummy implementation. 14 | # Will be created as part of https://github.com/fohrloop/wakepy/issues/109 15 | def __init__( 16 | self, method: Method, heartbeat_call_time: Optional[dt.datetime] = None 17 | ): 18 | self.method = method 19 | self.prev_call = heartbeat_call_time 20 | 21 | def start(self) -> bool: 22 | return True 23 | 24 | def stop(self) -> bool: 25 | return True 26 | -------------------------------------------------------------------------------- /requirements/requirements-test.txt: -------------------------------------------------------------------------------- 1 | # requirements for running unit tests 2 | 3 | tox==4.28.3; python_version>='3.9' 4 | # Python 3.8 support dropped in tox 4.26.0 5 | tox==4.25.0; python_version=='3.8' 6 | # Python 3.7 support dropped in tox 4.9.0 7 | tox==4.8.0; python_version=='3.7' 8 | 9 | # Python 3.7 support dropped in pytest 8.0.0 10 | pytest==8.3.3; python_version>='3.8' 11 | pytest==7.4.4; python_version=='3.7' 12 | 13 | # Python 3.7 support dropped in pytest-cov 5.0.0 14 | pytest-cov==5.0.0; python_version>='3.8' 15 | pytest-cov==4.1.0; python_version=='3.7' 16 | coverage-conditional-plugin==0.9.0 17 | 18 | # Jeepney is used in the integration tests for creating a D-Bus server 19 | jeepney==0.8.0;sys_platform=='linux' -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /tests/unit/test_core/test_constants.py: -------------------------------------------------------------------------------- 1 | from wakepy.core import BusType, ModeName 2 | from wakepy.core.constants import ( 3 | BusTypeValue, 4 | IdentifiedPlatformType, 5 | ModeNameValue, 6 | PlatformType, 7 | ) 8 | 9 | 10 | def test_mode_name(assert_strenum_values): 11 | assert_strenum_values(ModeName, ModeNameValue) 12 | 13 | 14 | def test_bustype(assert_strenum_values): 15 | assert_strenum_values(BusType, BusTypeValue) 16 | 17 | 18 | def test_platform_types_in_sync(): 19 | """Test that each IdentifiedPlatformType is also in PlatformType: anything 20 | that can be detected can also be selected by the Method sublasses in 21 | supported_platforms.""" 22 | 23 | identified = {member.value for member in IdentifiedPlatformType} 24 | selectable = {member.value for member in PlatformType} 25 | assert not (identified - selectable) 26 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /src/wakepy/methods/_testing.py: -------------------------------------------------------------------------------- 1 | """This module defines the WakepyFakeSuccess method, which can be used to fake 2 | activation success. It is controlled with the WAKEPY_FAKE_SUCCESS environment 3 | variable and meant to be used in CI pipelines / tests.""" 4 | 5 | from wakepy.core import Method, PlatformType 6 | from wakepy.core.constants import WAKEPY_FAKE_SUCCESS 7 | 8 | 9 | class WakepyFakeSuccess(Method): 10 | """This is a special fake method to be used with any mode. It can be used 11 | in tests for faking wakepy mode activation. This way all IO and real 12 | executable, library and dbus calls are prevented. To use this method (and 13 | skip using any other methods), set WAKEPY_FAKE_SUCCESS environment variable 14 | to a truthy value (e.g. "1", or "True"). 15 | """ 16 | 17 | name = WAKEPY_FAKE_SUCCESS 18 | mode_name = "_fake" 19 | supported_platforms = (PlatformType.ANY,) 20 | 21 | def enter_mode(self) -> None: 22 | """Does nothing ("succeeds" automatically; Will never raise an 23 | Exception)""" 24 | -------------------------------------------------------------------------------- /scripts/example-test-with-wakepy.py: -------------------------------------------------------------------------------- 1 | r"""This is a simple script for testing wakepy (manually). It is similar to 2 | scripts\example-test.py but there's a wakepy mode activated & deactivated 3 | before the timer starts. 4 | 5 | Can be used to determine the status of the system sleep timer right after 6 | exiting the wakepy mode. 7 | """ 8 | 9 | import datetime as dt 10 | import time 11 | 12 | from wakepy import keep 13 | 14 | start = dt.datetime.now() 15 | now = None 16 | MODE_ACTIVE_TIME = 6 * 60 # seconds 17 | 18 | print(dt.datetime.now().strftime("%b %d %H:%M:%S")) 19 | print("Wakepy inhibit start") 20 | with keep.presenting() as m: 21 | print(f"Method: {m.result.method}, " f"Mode: {m.result.mode_name}") 22 | time.sleep(MODE_ACTIVE_TIME) 23 | print("Wakepy inhibit end") 24 | 25 | while True: 26 | prev = now or dt.datetime.now() 27 | now = dt.datetime.now() 28 | now_str = now.strftime("%b %d %H:%M:%S") 29 | delta_str = f"{(now-prev)/dt.timedelta(seconds=1)}s" 30 | print(f"{now_str} | elapsed {now-start} | delta: {delta_str}") 31 | time.sleep(2) 32 | -------------------------------------------------------------------------------- /tests/unit/test__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import pytest 4 | 5 | import wakepy 6 | 7 | 8 | def test_imports(): 9 | """tests the public API""" 10 | from wakepy import ActivationError as ActivationError 11 | from wakepy import ActivationResult as ActivationResult 12 | from wakepy import Method as Method 13 | from wakepy import MethodActivationResult as MethodActivationResult 14 | from wakepy import Mode as Mode 15 | from wakepy import ModeExit as ModeExit 16 | from wakepy import keep as keep 17 | 18 | 19 | @pytest.mark.skipif( 20 | not sys.platform.lower().startswith("linux"), 21 | reason="dbus methods only supported on linux", 22 | ) 23 | def test_import_dbus(): 24 | from wakepy import JeepneyDBusAdapter as JeepneyDBusAdapter 25 | 26 | 27 | def test_successful_attribute_access(): 28 | """sanity check that the lazy-loading does not mess up anything""" 29 | wakepy.Method 30 | wakepy.keep 31 | 32 | 33 | def test_failing_import(): 34 | 35 | with pytest.raises(AttributeError): 36 | wakepy.something_that_does_not_exist 37 | -------------------------------------------------------------------------------- /requirements/requirements-docs.txt: -------------------------------------------------------------------------------- 1 | # Requirements for building the documentation. 2 | accessible-pygments==0.0.5 3 | alabaster==0.7.16 4 | Babel==2.15.0 5 | beautifulsoup4==4.12.3 6 | # Need the build in order to get the _version.py 7 | build==1.1.1 8 | certifi==2024.2.2 9 | charset-normalizer==3.3.2 10 | docutils==0.20.1 11 | idna==3.7 12 | imagesize==1.4.1 13 | Jinja2==3.1.4 14 | markdown-it-py==3.0.0 15 | MarkupSafe==2.1.5 16 | mdit-py-plugins==0.4.1 17 | mdurl==0.1.2 18 | myst-parser==2.0.0 19 | numpydoc==1.7.0 20 | packaging==24.1 21 | pydata-sphinx-theme==0.15.3 22 | Pygments==2.18.0 23 | pyproject_hooks==1.1.0 24 | PyYAML==6.0.1 25 | requests==2.32.3 26 | snowballstemmer==2.2.0 27 | soupsieve==2.5 28 | Sphinx==7.2.6 29 | sphinx-book-theme==1.1.2 30 | sphinx-copybutton==0.5.2 31 | sphinx_design==0.5.0 32 | sphinxcontrib-applehelp==1.0.8 33 | sphinxcontrib-devhelp==1.0.6 34 | sphinxcontrib-htmlhelp==2.0.5 35 | sphinxcontrib-jsmath==1.0.1 36 | sphinxcontrib-qthelp==1.0.7 37 | sphinxcontrib-serializinghtml==1.1.10 38 | tabulate==0.9.0 39 | tomli==2.0.1 40 | typing_extensions==4.12.0 41 | urllib3==2.2.1 -------------------------------------------------------------------------------- /tests/unit/test_methods/test_macos.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from wakepy import keep 4 | from wakepy.core import ModeName, PlatformType 5 | from wakepy.methods import macos 6 | 7 | 8 | class DummyMacCaffeinate(macos._MacCaffeinate): 9 | """Test class for _MacCaffeinate to allow instantiation.""" 10 | 11 | command = "ls" # some linux command for testing 12 | name = "DummyMacCaffeinate" 13 | mode_name = ModeName.KEEP_RUNNING 14 | supported_platforms = (PlatformType.ANY,) 15 | 16 | 17 | def test_mac_caffeinate_context_manager(): 18 | """Test that the context manager works as expected.""" 19 | 20 | # If this does to raise an Exception, the context manager works as 21 | # expected. 22 | with keep.running(methods=["DummyMacCaffeinate"], on_fail="error"): 23 | ... 24 | 25 | 26 | def test_exit_before_enter(caplog): 27 | method = DummyMacCaffeinate() 28 | 29 | # Act 30 | with caplog.at_level(logging.DEBUG): 31 | method.exit_mode() 32 | 33 | # Assert 34 | assert caplog.text.startswith("DEBUG ") 35 | assert "No need to terminate process (not started)" in caplog.text 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2024 Niko Föhr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from wakepy import DBusAdapter 4 | from wakepy.core.registry import register_method 5 | from wakepy.methods._testing import WakepyFakeSuccess 6 | 7 | 8 | class TestDBusAdapter(DBusAdapter): 9 | """A fake dbus adapter used in tests""" 10 | 11 | 12 | @pytest.fixture(scope="session") 13 | def fake_dbus_adapter(): 14 | return TestDBusAdapter 15 | 16 | 17 | class TestUtils: 18 | """Any functions needed to be "imported" anywhere from any tests. Available 19 | as a fixture called `testutils`. 20 | """ 21 | 22 | @staticmethod 23 | def empty_method_registry(monkeypatch): 24 | """ 25 | Make the method registry empty for duration of a test. Keep 26 | the WakepyFakeSuccess method in the registry. 27 | """ 28 | monkeypatch.setattr("wakepy.core.registry._method_registry", (dict())) 29 | # The fake method should always be part of the registry. 30 | register_method(WakepyFakeSuccess) 31 | 32 | 33 | @pytest.fixture(scope="session") 34 | def testutils(): 35 | return TestUtils 36 | 37 | 38 | @pytest.fixture(scope="function", name="empty_method_registry") 39 | def empty_method_registry_fixture(monkeypatch): 40 | TestUtils.empty_method_registry(monkeypatch) 41 | 42 | 43 | @pytest.fixture(scope="function", name="WAKEPY_FAKE_SUCCESS_eq_1") 44 | def _wakepy_fake_success_fixture(monkeypatch): 45 | monkeypatch.setenv("WAKEPY_FAKE_SUCCESS", "1") 46 | -------------------------------------------------------------------------------- /docs/source/_templates/sbt-sidebar-nav.html: -------------------------------------------------------------------------------- 1 | {# 2 | Most of the contents of this file are copied from the sphinx-book-theme 3 | theme/sphinx_book_theme/components/sbt-sidebar-nav.html 4 | #} 5 | -------------------------------------------------------------------------------- /src/wakepy/core/__init__.py: -------------------------------------------------------------------------------- 1 | """This package is private to wakepy; anything inside here is to be considered 2 | as implementation details! 3 | 4 | See the public Python API at: https://wakepy.readthedocs.io/ 5 | """ 6 | 7 | from .activationresult import ActivationResult as ActivationResult 8 | from .activationresult import MethodActivationResult as MethodActivationResult 9 | from .constants import BusType as BusType 10 | from .constants import IdentifiedPlatformType as IdentifiedPlatformType 11 | from .constants import ModeName as ModeName 12 | from .constants import PlatformType as PlatformType 13 | from .dbus import DBusAdapter as DBusAdapter 14 | from .dbus import DBusAddress as DBusAddress 15 | from .dbus import DBusMethod as DBusMethod 16 | from .dbus import DBusMethodCall as DBusMethodCall 17 | from .method import Method as Method 18 | from .method import MethodInfo as MethodInfo 19 | from .mode import ActivationError as ActivationError 20 | from .mode import ActivationWarning as ActivationWarning 21 | from .mode import ContextAlreadyEnteredError as ContextAlreadyEnteredError 22 | from .mode import Mode as Mode 23 | from .mode import ModeExit as ModeExit 24 | from .mode import NoCurrentModeError as NoCurrentModeError 25 | from .mode import current_mode as current_mode 26 | from .mode import global_modes as global_modes 27 | from .mode import modecount as modecount 28 | from .platform import CURRENT_PLATFORM as CURRENT_PLATFORM 29 | from .registry import get_method as get_method 30 | from .registry import get_methods as get_methods 31 | from .registry import get_methods_for_mode as get_methods_for_mode 32 | from .strenum import StrEnum as StrEnum 33 | -------------------------------------------------------------------------------- /src/wakepy/methods/__init__.py: -------------------------------------------------------------------------------- 1 | """This sub-package provides all types of methods for activating different 2 | modes. The methods should be placed into modules with following rules. Pick 3 | the module name from the first rule that is matches: 4 | 5 | (1) If the method depends on certain desktop environment, it is listed in a 6 | module named after the desktop environment. (Even if the method would have 7 | additional dependencies) 8 | 9 | (2) If the method depends on existence of some software or shared library, it 10 | is listed in a module named after the software or shared library. In case of 11 | multiple dependencies of this type, use the name of the "most specific" or 12 | "least widespread" software. 13 | 14 | Examples 15 | (1) If a method needs D-Bus and GNOME, it is listed in gnome.py since GNOME is 16 | a desktop environment. 17 | (2) If a method needs a hypothetical (not well known) programX and systemd and 18 | D-Bus, it is listed under programx.py, because programX is the "most 19 | specific" or "least widespread" software. 20 | (3) If a method needs systemd and D-Bus, it is listed under systemd, as D-Bus 21 | is more widespread than systemd, and you may have D-Bus without systemd 22 | but not vice versa. 23 | """ 24 | 25 | # NOTE: All modules containing wakepy.Methods must be imported here! The reason 26 | # is that the Methods are registered into the method registry only if the class 27 | # definition is executed (if the module containing the Method class definition 28 | # is imported) 29 | from . import _testing as _testing 30 | from . import freedesktop as freedesktop 31 | from . import gnome as gnome 32 | from . import macos as macos 33 | from . import windows as windows 34 | -------------------------------------------------------------------------------- /docs/source/cli-api.md: -------------------------------------------------------------------------------- 1 | # CLI API 2 | 3 | It is possibe to start wakepy from the command line either by running 4 | 5 | ```{code-block} text 6 | wakepy 7 | ``` 8 | 9 | or 10 | 11 | ```{code-block} text 12 | python -m wakepy 13 | ``` 14 | 15 | This starts wakepy in the *default mode* (`-r`), which corresponds to a [`keep.running`](#keep-running-mode) mode with default arguments. The available options are: 16 | 17 | ```{code-block} output 18 | usage: wakepy [-h] [-r] [-p] [-v] 19 | 20 | options: 21 | -h, --help show this help message and exit 22 | -r, --keep-running Keep programs running (DEFAULT); inhibit automatic idle timer based 23 | sleep / suspend. If a screen lock (or a screen saver) with a 24 | password is enabled, your system *may* still lock the session 25 | automatically. You may, and probably should, lock the session 26 | manually. Locking the workstation does not stop programs from 27 | executing. 28 | -p, --keep-presenting Presentation mode; inhibit automatic idle timer based sleep, 29 | screensaver, screenlock and display power management. 30 | -v, --verbose Increase verbosity level (-v for INFO, -vv for DEBUG). Default is 31 | WARNING, which shows only really important messages. 32 | ``` 33 | 34 | 35 | ````{admonition} Command "wakepy" not found? 36 | :class: note 37 | 38 | If you just installed `wakepy`, you might need to restart shell / terminal application to make added to the PATH. 39 | ```` 40 | 41 | ```{versionchanged} 0.10.0 42 | Renamed `-k` to `-r` and `--presentation` to `--keep-presenting` ([wakepy/#355](https://github.com/fohrloop/wakepy/issues/355)). 43 | ``` -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Optional 3 | 4 | import pytest 5 | 6 | from tests.unit.test_core.testmethods import TestMethod 7 | from wakepy.core.strenum import StrEnum 8 | 9 | if sys.version_info < (3, 8): # pragma: no-cover-if-py-gte-38 10 | import typing_extensions as typing 11 | else: # pragma: no-cover-if-py-lt-38 12 | import typing 13 | 14 | 15 | @pytest.fixture 16 | def method1(): 17 | return TestMethod() 18 | 19 | 20 | @pytest.fixture 21 | def do_assert(): 22 | """Function to be used instead of assert statement.""" 23 | 24 | # Fixes issue with mypy: https://github.com/python/mypy/issues/11969 25 | # In short, when testing and needing to assert that a variable has certain 26 | # value, and then mutating the value and asserting the value again ( 27 | # against new assumed value), mypy does not handle that case but you'll get 28 | # [unreachable] errors. Using `do_assert(...)` instead of `assert ...` in 29 | # tests fixes this. 30 | 31 | def _do_assert( 32 | expression: bool, 33 | message: Optional[str] = None, 34 | ) -> None: 35 | """Original idea: Nikita Sobolev (safe-assert)[1]. Fixed the return 36 | type to make this usable[2] 37 | 38 | [1] https://github.com/wemake-services/safe-assert/blob/e3ebfe72a910915e227a9f5447a0f7f56d5219e6/safe_assert/__init__.py 39 | [2] https://github.com/wemake-services/safe-assert/pull/131 40 | """ 41 | if not expression: 42 | if message: 43 | raise AssertionError(message) 44 | raise AssertionError 45 | 46 | return _do_assert 47 | 48 | 49 | @pytest.fixture 50 | def assert_strenum_values(): 51 | 52 | def _assert_strenum_values(strenum_cls: typing.Type[StrEnum], values: typing.Any): 53 | """Note: `values` is a typing.Literal. Could not find a type annotation 54 | for that""" 55 | assert set(typing.get_args(values)) == {member.value for member in strenum_cls} 56 | 57 | return _assert_strenum_values 58 | -------------------------------------------------------------------------------- /docs/source/index.md: -------------------------------------------------------------------------------- 1 | ![](./img/wakepy-banner.svg) 2 |


3 | # Overview 4 | 5 | ```{include} ../../README.md 6 | :start-after: 7 | :end-before: 8 | ``` 9 | 10 | ```{admonition} Restart your shell after installing wakepy 11 | :class: note 12 | To get the `wakepy` CLI command working, you might need to restart the shell / terminal application. 13 | ``` 14 | 15 | ```{include} ../../README.md 16 | :start-after: 17 | :end-before: 18 | ``` 19 | 20 | ```{admonition} New in wakepy 1.0.0 21 | :class: note 22 | 23 | [Decorator syntax](#decorator-syntax) was added in wakepy 1.0.0. 24 | ``` 25 | 26 | 27 | ```{include} ../../README.md 28 | :start-after: 29 | :end-before: 30 | ``` 31 | 32 | 33 | ```{admonition} Next Steps 34 | :class: seealso 35 | 36 | See the [User Guide](#user-guide) and the available wakepy [Modes](#wakepy-modes) and [Methods](#wakepy-methods) 37 | ``` 38 | 39 | 40 | ```{include} ../../README.md 41 | :start-after: 42 | :end-before: 43 | ``` 44 | 45 | ```{toctree} 46 | :hidden: 47 | :maxdepth: 2 48 | :numbered: -1 49 | :titlesonly: 50 | 51 | installing 52 | user-guide 53 | tests-and-ci 54 | ``` 55 | 56 | ```{toctree} 57 | :hidden: 58 | :caption: 'Reference Manual:' 59 | :maxdepth: 2 60 | :numbered: -1 61 | :titlesonly: 62 | 63 | modes 64 | methods-reference 65 | api-reference 66 | cli-api 67 | ``` 68 | 69 | 70 | ```{toctree} 71 | :hidden: 72 | :caption: 'Technical Details:' 73 | :maxdepth: 2 74 | :numbered: -1 75 | :titlesonly: 76 | 77 | wakepy-mode-lifecycle 78 | multithreading 79 | post-keepawake-behavior 80 | test-manually 81 | ``` 82 | 83 | ```{toctree} 84 | :hidden: 85 | :caption: 'Development:' 86 | :maxdepth: 2 87 | :numbered: -1 88 | :titlesonly: 89 | 90 | changelog 91 | migration 92 | ``` -------------------------------------------------------------------------------- /tests/tox_build_wakepy.py: -------------------------------------------------------------------------------- 1 | """This module is used solely by tox and is meant for the .pkg_external 2 | environment. See tox.ini for more details. 3 | """ 4 | 5 | import shutil 6 | import subprocess 7 | import sys 8 | from pathlib import Path 9 | 10 | dist_dir = Path(__file__).resolve().parent.parent / "dist" 11 | tox_asks_rebuild = dist_dir / ".TOX-ASKS-REBUILD" 12 | 13 | # A list of errors to skip. 14 | skip_errors = [ 15 | # This occurs every time sdist is used to create wheel as git files are not 16 | # included in the sdist (on every build.) 17 | "ERROR setuptools_scm._file_finders.git listing git files failed - pretending there aren't any" # noqa: E501 18 | ] 19 | 20 | 21 | def build(): 22 | print(f"Checking {tox_asks_rebuild}") 23 | if not tox_asks_rebuild.exists(): 24 | print("Build already done. skipping.") 25 | return 26 | 27 | print(f"Removing {dist_dir} and building sdist and wheel into {dist_dir}") 28 | 29 | # Cleanup. Remove all older builds; the /dist folder and its contents. 30 | # Note that tox would crash if there were two files with .whl extension. 31 | # This also resets the TOX-ASKS-REBUILD so we build only once. 32 | shutil.rmtree(dist_dir, ignore_errors=True) 33 | 34 | # This creates first sdist from the source tree and then wheel from the 35 | # sdist. By running tests agains the wheel we test all, the source tree, 36 | # the sdist and the wheel. 37 | 38 | out = subprocess.run( 39 | f"python -m build --no-isolation -o {dist_dir}", 40 | stdout=sys.stdout, 41 | stderr=subprocess.PIPE, 42 | shell=True, 43 | universal_newlines=True, 44 | ) 45 | 46 | errors = [err for err in out.stderr.split("\n") if err and err not in skip_errors] 47 | if errors: 48 | sys.stderr.write("Captured errors:\n") 49 | for line in errors: 50 | sys.stderr.write(line) 51 | 52 | if out.returncode != 0: 53 | print("\n", end="") 54 | raise subprocess.CalledProcessError(out.returncode, out.args) 55 | 56 | 57 | if __name__ == "__main__": 58 | 59 | build() 60 | -------------------------------------------------------------------------------- /src/wakepy/__init__.py: -------------------------------------------------------------------------------- 1 | # NOTE The methods sub-package is imported for registering all the methods. 2 | from __future__ import annotations 3 | 4 | import typing 5 | 6 | if typing.TYPE_CHECKING: 7 | from typing import Type 8 | 9 | from . import methods as methods 10 | 11 | try: 12 | from ._version import __version__ as __version__ 13 | from ._version import version_tuple as version_tuple 14 | except ImportError: # pragma: no cover 15 | # Likely an editable install. Should ever happen if installed from a 16 | # distribution package (sdist or wheel) 17 | __version__ = "undefined" 18 | version_tuple = (0, 0, 0, "undefined") 19 | 20 | 21 | from .core import ActivationError as ActivationError 22 | from .core import ActivationResult as ActivationResult 23 | from .core import ActivationWarning as ActivationWarning 24 | from .core import ContextAlreadyEnteredError as ContextAlreadyEnteredError 25 | from .core import DBusAdapter as DBusAdapter 26 | from .core import Method as Method 27 | from .core import MethodActivationResult as MethodActivationResult 28 | from .core import MethodInfo as MethodInfo 29 | from .core import Mode as Mode 30 | from .core import ModeExit as ModeExit 31 | from .core import ModeName as ModeName 32 | from .core import NoCurrentModeError as NoCurrentModeError 33 | from .core import current_mode as current_mode 34 | from .core import global_modes as global_modes 35 | from .core import modecount as modecount 36 | from .modes import keep as keep 37 | 38 | JeepneyDBusAdapter: Type[DBusAdapter] 39 | """This is lazily imported below. The reason for this is that no all systems 40 | support DBus, but it's nice to be able to import this directly from wakepy 41 | top level package.""" 42 | 43 | 44 | def __getattr__(name: str) -> object: 45 | """Some lazy implementation of lazy loading. 46 | 47 | See: https://peps.python.org/pep-0562/ 48 | """ 49 | if name == "JeepneyDBusAdapter": 50 | from wakepy.dbus_adapters.jeepney import JeepneyDBusAdapter 51 | 52 | return JeepneyDBusAdapter # pragma: no-cover-if-no-dbus 53 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 54 | -------------------------------------------------------------------------------- /docs/source/_static/wakepy-docs.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Function to check if we are on the API Reference page 4 | * @returns {bool} True of on API Reference page. False otherwise. 5 | */ 6 | function shouldDisableScrollSpy() { 7 | // The API Reference page has

API Reference

as the first h1 element 8 | var h1Elements = document.querySelectorAll("h1"); 9 | var h1Array = Array.from(h1Elements); 10 | var isOnApiReferencePage = h1Array[0].textContent.trim() == 'API Reference' 11 | return isOnApiReferencePage 12 | } 13 | 14 | /** 15 | * Function to diable the Bootstrap Scroll Spy. 16 | */ 17 | function disableScrollSpy() { 18 | var scrollSpyBody = document.querySelector('body[data-bs-spy="scroll"][data-bs-target=".bd-toc-nav"]'); 19 | if (scrollSpyBody) { 20 | scrollSpyBody.removeAttribute('data-bs-spy'); 21 | scrollSpyBody.removeAttribute('data-bs-target'); 22 | } 23 | } 24 | 25 | /** 26 | * Make TOC
  • elemenents active; 27 | * Add CSS class "active" to all the >