FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

"""Platform detection and shared helpers. This module is imported early by most other modules, so it avoids importing platform-specific packages at module level. The detection results are cached the first time [`IS_ANDROID`][pythonnative.utils.IS_ANDROID] and [`IS_IOS`][pythonnative.utils.IS_IOS] are read. The Android variants also expose a small global registry for the current `Activity` / `Context` and the screen's fragment container view; both are populated by the bundled Android template before any PythonNative code runs. Attributes: IS_ANDROID: `True` when running inside an Android process (either because `ANDROID_*` env vars are present or because Chaquopy's `java` module imports successfully). IS_IOS: `True` when running inside an iOS app bundle (signaled by `PN_PLATFORM=ios`, `sys.platform == "ios"`, or a Simulator `HOME` path). Importing `rubicon-objc` alone is intentionally not enough to trigger this flag. IS_DESKTOP: `True` when running the desktop preview backend (signaled by `PN_PLATFORM=desktop`, set by ``pn preview``). This drives the Tkinter native-view registry so a PythonNative app can render in a real OS window for fast local iteration. """ import os import sys from typing import Any, Optional # ====================================================================== # Platform detection # ====================================================================== _is_android: Optional[bool] = None _is_ios: Optional[bool] = None _is_desktop: Optional[bool] = None def _detect_android() -> bool: """Return whether we're running inside an Android process.""" env = os.environ if "ANDROID_BOOTLOGO" in env or "ANDROID_ROOT" in env or "ANDROID_DATA" in env or "ANDROID_ARGUMENT" in env: return True try: from java import jclass # noqa: F401 return True except Exception: pass return False def _detect_ios() -> bool: """Detect whether we're running inside an iOS app bundle. Signals, in priority order: - Explicit `PN_PLATFORM=ios` env var (set by the iOS template's `ViewController.swift` before Python starts). This is the canonical signal and survives even on hosts where `sys.platform` is generic `darwin`. - `sys.platform == "ios"` (CPython 3.13+ native iOS builds). - `/CoreSimulator/Devices/` in `$HOME` (iOS Simulator fallback if the template signal is missing for some reason). Crucially, having `rubicon-objc` importable is *not* enough: developers frequently install it on macOS via the `[ios]` extra, and treating that as iOS would cause subtle side effects (e.g., stdout redirection) on desktop machines. """ if os.environ.get("PN_PLATFORM") == "ios": return True if sys.platform == "ios": return True home = os.environ.get("HOME", "") if "/CoreSimulator/Devices/" in home: return True return False def _detect_desktop() -> bool: """Detect whether we're running the desktop (Tkinter) preview backend. The only signal is the explicit ``PN_PLATFORM=desktop`` env var, set by ``pn preview`` before importing PythonNative. Desktop is a *development* target: it renders the app in a native OS window via the pure-Python Tkinter registry so the inner dev loop doesn't require a device build. Off-device unit tests deliberately leave this flag ``False`` so they keep using an injected mock registry and ``Platform.OS == "test"``. """ return os.environ.get("PN_PLATFORM") == "desktop" def _ensure_platform_detection() -> None: """Populate `_is_android` / `_is_ios` / `_is_desktop` once, then reuse.""" global _is_android, _is_ios, _is_desktop if _is_android is None: _is_android = _detect_android() if _is_ios is None: _is_ios = (not _is_android) and _detect_ios() if _is_desktop is None: _is_desktop = (not _is_android) and (not _is_ios) and _detect_desktop() def _get_is_android() -> bool: """Return the cached Android-detection result.""" _ensure_platform_detection() assert _is_android is not None return _is_android def _get_is_ios() -> bool: """Return the cached iOS-detection result.""" _ensure_platform_detection() assert _is_ios is not None return _is_ios def _get_is_desktop() -> bool: """Return the cached desktop-detection result.""" _ensure_platform_detection() assert _is_desktop is not None return _is_desktop IS_ANDROID: bool = _get_is_android() """``True`` when running inside an Android process. The flag is computed once at import time, by checking for `ANDROID_*` environment variables and trying to import Chaquopy's `java` module. """ IS_IOS: bool = _get_is_ios() """``True`` when running inside an iOS app bundle. The flag is computed once at import time, by checking `PN_PLATFORM=ios`, `sys.platform == "ios"`, and the iOS Simulator `HOME` path. """ IS_DESKTOP: bool = _get_is_desktop() """``True`` when running the desktop (Tkinter) preview backend. Set by ``pn preview`` via ``PN_PLATFORM=desktop``. Mutually exclusive with `IS_ANDROID` / `IS_IOS`. Off-device unit tests leave this ``False`` and inject a mock registry instead. """ # ====================================================================== # Android context management # ====================================================================== _android_context: Any = None _android_fragment_container: Any = None def set_android_context(context: Any) -> None: """Record the current Android `Activity`/`Context`. Called by the bundled Android template before any view is created; most app code should not call this directly. Args: context: A Java `android.content.Context` (typically the current `Activity`). """ global _android_context _android_context = context def set_android_fragment_container(container_view: Any) -> None: """Record the current `Fragment` root container `ViewGroup`. Args: container_view: A Java `android.view.ViewGroup` that holds the screen's view tree. """ global _android_fragment_container _android_fragment_container = container_view def get_android_context() -> Any: """Return the current Android `Activity`/`Context`. Returns: The most recently recorded Android context. Raises: RuntimeError: If called on a non-Android platform, or before the template has registered a context. """ if not IS_ANDROID: raise RuntimeError("get_android_context() called on non-Android platform") if _android_context is None: raise RuntimeError( "Android context not set. Ensure the screen host is initialized from an Activity before constructing views." ) return _android_context def get_android_fragment_container() -> Any: """Return the current `Fragment` container `ViewGroup`. Returns: The most recently recorded fragment container view. Raises: RuntimeError: If called on a non-Android platform, or before `ScreenFragment` has registered its container. """ if not IS_ANDROID: raise RuntimeError("get_android_fragment_container() called on non-Android platform") if _android_fragment_container is None: raise RuntimeError( "Android fragment container not set. Ensure ScreenFragment has been created before set_root_view." ) return _android_fragment_container

Back | FazBrowse Home | New Git URL