-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplatform.py
More file actions
152 lines (119 loc) · 4.52 KB
/
platform.py
File metadata and controls
152 lines (119 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""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"