See More

"""Platform-aware constants and selectors. A small, RN-style helper for branching app code on the host platform. The public surface is the [`Platform`][pythonnative.Platform] class exposing ``OS``, ``Version``, ``is_ios``, ``is_android``, and ``select`` so user code can write ``Platform.select({"ios": ..., ...})`` without importing the ``IS_*`` flags directly. Example: ```python import pythonnative as pn title_size = pn.Platform.select({"ios": 17, "android": 16, "default": 16}) @pn.component def App(): return pn.Text( f"Running on {pn.Platform.OS} {pn.Platform.Version}", style={"font_size": title_size}, ) ``` """ from __future__ import annotations import os import sys from typing import Any, Dict, Optional from .utils import IS_ANDROID, IS_IOS def _detect_os() -> str: if IS_ANDROID: return "android" if IS_IOS: return "ios" return "test" def _detect_version() -> str: """Return a human-readable platform version string. On iOS, reads the Simulator/device's reported OS version. On Android, reads ``Build.VERSION.RELEASE``. Off-device (the test environment), returns the host Python's version so user code can still introspect *something*. """ if IS_IOS: try: from rubicon.objc import ObjCClass UIDevice = ObjCClass("UIDevice") return str(UIDevice.currentDevice.systemVersion) except Exception: pass sim_version = os.environ.get("SIMULATOR_RUNTIME_VERSION") if sim_version: return sim_version if IS_ANDROID: try: from java import jclass Build = jclass("android.os.Build$VERSION") return str(Build.RELEASE) except Exception: pass return f"python-{sys.version_info.major}.{sys.version_info.minor}" class Platform: """Platform-aware constants and the ``select`` dispatcher. All attributes are read at import time. ``OS`` is one of ``"ios"``, ``"android"``, or ``"test"`` (the latter when running off-device, e.g., in unit tests). """ OS: str = _detect_os() """``"ios"``, ``"android"``, or ``"test"``.""" Version: str = _detect_version() """Best-effort OS version string (``"17.4"``, ``"14"``, ``"python-3.11"``).""" is_ios: bool = IS_IOS """``True`` when running inside an iOS app bundle.""" is_android: bool = IS_ANDROID """``True`` when running inside an Android process.""" is_test: bool = OS == "test" """``True`` when running off-device (no native runtime).""" @staticmethod def select(spec: Dict[str, Any], default: Any = None) -> Any: """Pick the value matching the current platform. Looks up ``spec[Platform.OS]``, then falls back to ``spec["native"]`` (matches both iOS and Android), then to ``spec["default"]``, then to the explicit ``default`` argument. Args: spec: Mapping from platform name to value. Recognized keys: ``"ios"``, ``"android"``, ``"test"``, ``"native"``, ``"default"``. default: Value returned when ``spec`` has no matching key and no ``"default"`` entry. Returns: The matching value, or ``default`` when nothing matches. Example: ```python font = pn.Platform.select( {"ios": "Helvetica", "android": "Roboto", "default": None} ) ``` """ if Platform.OS in spec: return spec[Platform.OS] if (Platform.is_ios or Platform.is_android) and "native" in spec: return spec["native"] if "default" in spec: return spec["default"] return default def get_platform() -> str: """Return the active platform name. Equivalent to reading ``Platform.OS``, exposed as a function for introspection from non-component code. """ return Platform.OS def _set_platform_for_test(name: Optional[str]) -> None: """Override ``Platform.OS`` for unit tests. Production code should not call this. Tests can pass ``"ios"``, ``"android"``, ``"test"``, or ``None`` (to reset to autodetect). """ if name is None: Platform.OS = _detect_os() Platform.is_ios = IS_IOS Platform.is_android = IS_ANDROID Platform.is_test = Platform.OS == "test" return Platform.OS = name Platform.is_ios = name == "ios" Platform.is_android = name == "android" Platform.is_test = name == "test"