├── 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 | 
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 > > - elements.
28 | */
29 | function makeTocLiElementsActive() {
30 | // The sidebar TOC is