Skip to content

Commit

Permalink
Change how platform support is defined
Browse files Browse the repository at this point in the history
  • Loading branch information
fohrloop committed Sep 13, 2024
1 parent 19bad39 commit 37c3eaa
Show file tree
Hide file tree
Showing 20 changed files with 358 additions and 137 deletions.
10 changes: 9 additions & 1 deletion docs/source/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,19 @@ Wakepy Core
.. autoclass:: wakepy.ModeExit
:exclude-members: args, with_traceback
.. autoclass:: wakepy.core.constants.PlatformName
.. autoclass:: wakepy.core.constants.PlatformType
:members:
:undoc-members:
:member-order: bysource
.. autoclass:: wakepy.core.constants.IdentifiedPlatformType
:members:
:undoc-members:
:member-order: bysource
.. autodata:: wakepy.core.platform.CURRENT_PLATFORM
:no-value:
DBus Adapter
-------------
DBus adapters are an advanced concept of wakepy. They would be used in such a case where wants to use other D-Bus python library than the default (which is `jeepney <https://jeepney.readthedocs.io/>`_).
Expand Down
1 change: 1 addition & 0 deletions src/wakepy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def handle_activation_error(result: ActivationResult) -> None:
def _get_activation_error_text(result: ActivationResult) -> str:
from wakepy import __version__

# LATER: This should be improved in https://github.com/fohrloop/wakepy/issues/378
error_text = f"""
Wakepy could not activate the "{result.mode_name}" mode. This might occur because of a bug or because your current platform is not yet supported or your system is missing required software.
Expand Down
3 changes: 2 additions & 1 deletion src/wakepy/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from .activationresult import ActivationResult as ActivationResult
from .activationresult import MethodActivationResult as MethodActivationResult
from .constants import BusType as BusType
from .constants import IdentifiedPlatformType as IdentifiedPlatformType
from .constants import ModeName as ModeName
from .constants import PlatformName as PlatformName
from .constants import PlatformType as PlatformType
from .dbus import DBusAdapter as DBusAdapter
from .dbus import DBusAddress as DBusAddress
from .dbus import DBusMethod as DBusMethod
Expand Down
52 changes: 44 additions & 8 deletions src/wakepy/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,58 @@
"""


class PlatformName(StrEnum):
"""All the different platforms wakepy knows about. Any platform that is not
detected will be named ``OTHER``."""
class IdentifiedPlatformType(StrEnum):
"""The type identifier for the (:attr:`~wakepy.core.platform.CURRENT_PLATFORM`).
Any process will be categorized into exactly one
:class:`IdentifiedPlatformType` type; these are mutually exclusive options.
Any platform that is not detected will be labeled as ``UNKNOWN``.
WINDOWS = auto()
See also: :class:`PlatformType`, which you should use with Method
subclasses.""" # noqa: W505

WINDOWS = auto()
LINUX = auto()

MACOS = auto()
FREEBSD = auto()
UNKNOWN = auto()


class PlatformType(StrEnum):
"""Enumeration for supported platform types. Each identified platform can
be categorized to at least one, but potentially many of these. In other
words, each :class:`IdentifiedPlatformType` (type of
:attr:`~wakepy.core.platform.CURRENT_PLATFORM`) maps into one or many
``PlatformType``.
To be used in subclasses of :class:`~wakepy.Method` in the
:meth:`~wakepy.Method.supported_platforms`.
"""

WINDOWS = IdentifiedPlatformType.WINDOWS.value
"""Any Windows version from Windows 10 onwards."""

LINUX = IdentifiedPlatformType.LINUX.value
"""Includes any Linux distro. Excludes things like Android & ChromeOS"""

MACOS = IdentifiedPlatformType.MACOS.value
"""Mac OS (Darwin)"""

OTHER = auto()
"""Anything else"""
FREEBSD = IdentifiedPlatformType.FREEBSD.value
"""FreeBSD. Also includes GhostBSD"""

UNKNOWN = IdentifiedPlatformType.UNKNOWN.value
"""Any non-identified platform"""

BSD = auto()
"""Any BSD system (Currently just FreeBSD / GhostBSD, but is likely to
change in the future)."""

UNIX_LIKE_FOSS = auto()
"""Unix-like desktop environment, but FOSS. Includes: LINUX and BSD.
Excludes: Android (mobile), MacOS (non-FOSS), ChromeOS (non-FOSS)."""

PlatformNameValue = Literal["WINDOWS", "LINUX", "MACOS", "OTHER"]
ANY = auto()
"""Means any platform."""


class ModeName(StrEnum):
Expand Down
75 changes: 40 additions & 35 deletions src/wakepy/core/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
from typing import Type, cast

from .activationresult import MethodActivationResult
from .constants import PlatformName, StageName
from .constants import PlatformType, StageName
from .heartbeat import Heartbeat
from .platform import CURRENT_PLATFORM
from .platform import CURRENT_PLATFORM, get_platform_supported
from .registry import register_method
from .strenum import StrEnum, auto

Expand All @@ -31,7 +31,7 @@

from wakepy.core import DBusAdapter, DBusMethodCall

from .constants import ModeName, PlatformName
from .constants import ModeName, PlatformType

MethodCls = Type["Method"]

Expand Down Expand Up @@ -69,20 +69,26 @@ class Method(ABC):
defines the Mode `foo` (:class:`Mode` classes are themselves not defined or
registered anywhere)"""

supported_platforms: Tuple[PlatformName, ...] = (
PlatformName.LINUX,
PlatformName.WINDOWS,
PlatformName.MACOS,
PlatformName.OTHER,
)
"""Lists the platforms the Method supports. If a platform is not listed in
``method.supported_platforms``, the ``method`` is not going to be used on
the platform (when used as part of a :class:`Mode`), and the Method
activation result will show a fail in the "PLATFORM" stage.
supported_platforms: Tuple[PlatformType, ...] = (PlatformType.ANY,)
"""Lists the platforms the Method supports. If the current platform is not
part of any of the platform types listed in ``method.supported_platforms``,
the ``method`` is not* going to be used (when used as part of a
:class:`Mode`), and the Method activation result will show a fail in the
"PLATFORM" stage.
When subclassing the ``Method``, defining ``supported_platforms`` reduces
some work required when writing the logic for :meth:`caniuse`.
Additionally, it aids in distinguishing the "PLATFORM" stage fail as a
separate type of failure.
When subclassing, defining ``supported_platforms`` reduces some work
required when writing the logic for :meth:`caniuse`. Additionally, it aids
in distinguishing the "PLATFORM" stage fail as a separate type of failure.
As an example, if the Method would support all Unix-like FOSS desktop
operating systems, the supported_platforms would be
``(PlatformType.UNIX_LIKE_FOSS, )``. See the
:class:`~wakepy.core.constants.PlatformType` for all options.
\*unless the current platform is unidentified (``UNKNOWN``). In this case,
platform check does not fail (nor succeed), and the activation process
continues normally.
Default: Support all platforms.
"""
Expand Down Expand Up @@ -114,6 +120,7 @@ def __init__(self, **kwargs: object) -> None:

# waits for https://github.com/fohrloop/wakepy/issues/256
# self.method_kwargs = kwargs
_check_supported_platforms(self.supported_platforms, self.__class__.__name__)

def __init_subclass__(cls, **kwargs: object) -> None:
register_method(cls)
Expand Down Expand Up @@ -285,6 +292,22 @@ def is_unnamed(cls) -> bool:
return cls.name == unnamed


def _check_supported_platforms(
supported_platforms: Tuple[PlatformType, ...], classname: str
) -> None:
err_supported_platforms = (
f"The supported_platforms of {classname} must be a tuple of PlatformType!"
)

if not isinstance(supported_platforms, tuple):
raise ValueError(err_supported_platforms)
for p in supported_platforms:
if not isinstance(p, PlatformType):
raise ValueError(
err_supported_platforms + f' One item ({p}) is of type "{type(p)}"'
)


def activate_method(method: Method) -> Tuple[MethodActivationResult, Heartbeat | None]:
"""Activates a mode defined by a single Method.
Expand All @@ -303,7 +326,7 @@ def activate_method(method: Method) -> Tuple[MethodActivationResult, Heartbeat |
success=False, method_name=method.name, mode_name=method.mode_name
)

if not get_platform_supported(method, platform=CURRENT_PLATFORM):
if get_platform_supported(CURRENT_PLATFORM, method.supported_platforms) is False:
result.failure_stage = StageName.PLATFORM_SUPPORT
return result, None

Expand Down Expand Up @@ -372,24 +395,6 @@ def deactivate_method(method: Method, heartbeat: Optional[Heartbeat] = None) ->
method.dbus_adapter.close_connections()


def get_platform_supported(method: Method, platform: PlatformName) -> bool:
"""Checks if method is supported by the platform
Parameters
----------
method: Method
The method which platform support to check.
platform:
The platform to check against.
Returns
-------
is_supported: bool
If True, the platform is supported. Otherwise, False.
"""
return platform in method.supported_platforms


def caniuse_fails(method: Method) -> tuple[bool, str]:
"""Check if the requirements of a Method are met or not.
Expand Down
94 changes: 87 additions & 7 deletions src/wakepy/core/platform.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,103 @@
from __future__ import annotations

import platform
import typing
import warnings

from .constants import PlatformName
from .constants import IdentifiedPlatformType, PlatformType

if typing.TYPE_CHECKING:
from typing import Callable, Tuple

PlatformFunc = Callable[[IdentifiedPlatformType], bool]


def get_current_platform() -> PlatformName:
def get_current_platform() -> IdentifiedPlatformType:
# Ref: https://docs.python.org/3/library/platform.html#platform.system
system = platform.system()
if system == "Windows":
return PlatformName.WINDOWS
return IdentifiedPlatformType.WINDOWS
elif system == "Darwin":
return PlatformName.MACOS
return IdentifiedPlatformType.MACOS
elif system == "Linux":
return PlatformName.LINUX
return IdentifiedPlatformType.LINUX
elif system == "FreeBSD":
return IdentifiedPlatformType.FREEBSD

# LATER: This should be improved in https://github.com/fohrloop/wakepy/issues/378
warnings.warn(
f"Could not detect current platform! platform.system() returned {system}"
)
return PlatformName.OTHER
return IdentifiedPlatformType.UNKNOWN


CURRENT_PLATFORM: IdentifiedPlatformType = get_current_platform()
"""The current platform as detected. If the platform cannot be detected,
defaults to ``UNKNOWN``."""


def get_platform_supported(
platform: IdentifiedPlatformType, supported_platforms: Tuple[PlatformType, ...]
) -> bool | None:
"""Checks if a platform is in the supported platforms.
Parameters
----------
platform: IdentifiedPlatformType
The platform to check.
supported_platforms:
The platforms to check against.
Returns
-------
is_supported: bool | None
If platform is supported, returns True. If the support is unknown,
returns None, and if the platform is not supported, returns False.
"""
for supported_platform in supported_platforms:
func = PLATFORM_INFO_FUNCS[supported_platform]
if func(platform) is True:
return True
if is_unknown(platform):
return None
return False


def is_windows(current_platform: IdentifiedPlatformType) -> bool:
return current_platform == IdentifiedPlatformType.WINDOWS


def is_linux(current_platform: IdentifiedPlatformType) -> bool:
return current_platform == IdentifiedPlatformType.LINUX


def is_freebsd(current_platform: IdentifiedPlatformType) -> bool:
return current_platform == IdentifiedPlatformType.FREEBSD


def is_macos(current_platform: IdentifiedPlatformType) -> bool:
return current_platform == IdentifiedPlatformType.MACOS


def is_bsd(current_platform: IdentifiedPlatformType) -> bool:
return is_freebsd(current_platform)


def is_unknown(current_platform: IdentifiedPlatformType) -> bool:
return current_platform == IdentifiedPlatformType.UNKNOWN


def is_unix_like_foss(current_platform: IdentifiedPlatformType) -> bool:
return is_bsd(current_platform) or is_linux(current_platform)


CURRENT_PLATFORM = get_current_platform()
PLATFORM_INFO_FUNCS: dict[PlatformType, PlatformFunc] = {
PlatformType.WINDOWS: is_windows,
PlatformType.LINUX: is_linux,
PlatformType.MACOS: is_macos,
PlatformType.FREEBSD: is_freebsd,
PlatformType.UNKNOWN: is_unknown,
PlatformType.BSD: is_bsd,
PlatformType.ANY: lambda _: True,
PlatformType.UNIX_LIKE_FOSS: is_unix_like_foss,
}
4 changes: 2 additions & 2 deletions src/wakepy/methods/_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
activation success. It is controlled with the WAKEPY_FAKE_SUCCESS environment
variable and meant to be used in CI pipelines / tests."""

from wakepy.core import CURRENT_PLATFORM, Method
from wakepy.core import Method, PlatformType
from wakepy.core.constants import WAKEPY_FAKE_SUCCESS


Expand All @@ -16,7 +16,7 @@ class WakepyFakeSuccess(Method):

name = WAKEPY_FAKE_SUCCESS
mode_name = "_fake"
supported_platforms = (CURRENT_PLATFORM,)
supported_platforms = (PlatformType.ANY,)

def enter_mode(self) -> None:
"""Does nothing ("succeeds" automatically; Will never raise an
Expand Down
4 changes: 2 additions & 2 deletions src/wakepy/methods/freedesktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
DBusMethodCall,
Method,
ModeName,
PlatformName,
PlatformType,
)

if typing.TYPE_CHECKING:
Expand All @@ -41,7 +41,7 @@ class FreedesktopInhibitorWithCookieMethod(Method):
"""Base class for freedesktop.org D-Bus based methods."""

service_dbus_address: DBusAddress
supported_platforms = (PlatformName.LINUX,)
supported_platforms = (PlatformType.LINUX,)

def __init__(self, **kwargs: object) -> None:
super().__init__(**kwargs)
Expand Down
4 changes: 2 additions & 2 deletions src/wakepy/methods/gnome.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
DBusMethodCall,
Method,
ModeName,
PlatformName,
PlatformType,
)

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -56,7 +56,7 @@ class _GnomeSessionManager(Method, ABC):
params=("inhibit_cookie",),
).of(session_manager)

supported_platforms = (PlatformName.LINUX,)
supported_platforms = (PlatformType.LINUX,)

@property
@abstractmethod
Expand Down
Loading

0 comments on commit 37c3eaa

Please sign in to comment.