Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

pyxpo-device

CI PyPI Python License: MIT

Facts about the device a pyxpo app runs on, written in Python: brand, model, OS and version, memory, CPU architectures, phone or tablet, uptime and Android system features. The names follow expo-device, in Python style.

The device module on Android and iOS: brand, model, OS version, memory and device type

import pyxpo_device as Device

print(Device.brand, Device.model_name, Device.os_name, Device.os_version)   # "Google", "sdk_gphone64_arm64", "Android", "15"
if Device.device_type == Device.DeviceType.TABLET:
    ...
uptime_ms = await Device.get_uptime_async()

Features

  • Constants with no bridge call: brand, model, OS, memory and the rest are read once at startup and are plain attributes of the module.
  • Phone, tablet, TV or desktop: Device.device_type is a DeviceType enum, worked out from the platform's own idiom and the screen size.
  • Runs on the desktop too: the web page and the desktop window fill the same fields from Python's platform module, so layout code can run the same everywhere.
  • Android extras: the runtime memory limit, the list of system features, a feature check (android.hardware.camera) and whether the app may install other apps.
  • Honest values: a value a platform does not have is None, not a guess. The rooted check is documented as easy to fool.
  • No permissions: nothing here needs a prompt, an entitlement or a manifest entry.
  • Typed: ships py.typed. Every name also has its expo-device spelling (Device.modelName, Device.getUptimeAsync).

Contents

If you are... Start here
Showing the model in a support screen Quick start → Constants
Switching layouts for tablets Device type
Checking for a camera or other hardware Android system features
Wondering whether it is an emulator is_device
Looking for exact signatures API reference · Errors
Seeing it in a real app Recipe: a diagnostics line for support
Stuck Testing · Troubleshooting · FAQ

Platform support

Target What works
Android, API 24+ Everything.
iOS Everything except the Android-only values, which are None or an empty result.
Web page and desktop window (pyxpo run web / pyxpo run desktop) Constants from Python's platform module, device_type is DESKTOP, uptime from the monotonic clock.

Requires Python 3.9+ and pyxpo>=0.1.0a2.

Install

pip install pyxpo-device

Add the module to your app's pyproject.toml:

[tool.pyxpo]
modules = ["device"]

The module has native code, so rebuild the app after adding it (pyxpo run android or pyxpo run ios).

Quick start

import pyxpo_device as Device
from pyxpo import Text, View, component, use_async


@component
def DeviceInfo():
    uptime = use_async(Device.get_uptime_async, [])     # (loading, value, error)
    hours = (uptime.value or 0) / 3_600_000
    return View(
        Text("%s %s" % (Device.brand or "", Device.model_name or "")),
        Text("%s %s" % (Device.os_name, Device.os_version)),
        Text("Up for %.1f hours" % hours),
        Text("Tablet" if Device.device_type == Device.DeviceType.TABLET else "Phone"),
    )

Tip: the constants resolve when you read them, not when the module is imported, because the startup environment is not there yet at import time. Read them inside a component or a function. Do not copy them into module-level variables at import.

Constants

Read once at app start. None where a platform has no value.

Python name expo name iOS Android
is_device isDevice False on the Simulator False on an emulator (best-effort check)
brand, manufacturer brand, manufacturer "Apple" Build.BRAND, Build.MANUFACTURER
model_name modelName Marketing name for known iPhones, else the identifier Build.MODEL
model_id modelId Identifier, for example "iPhone17,3" None
design_name, product_name designName, productName None Build.DEVICE, Build.PRODUCT
device_year_class deviceYearClass Release year of known iPhones None
total_memory totalMemory Bytes of RAM Bytes of RAM
supported_cpu_architectures supportedCpuArchitectures ["arm64"] Build.SUPPORTED_ABIS
os_name osName "iOS" or "iPadOS" "Android"
os_version osVersion For example "26.0" For example "15"
os_build_id, os_internal_build_id osBuildId, osInternalBuildId Kernel OS build Build.DISPLAY, Build.ID
os_build_fingerprint, platform_api_level osBuildFingerprint, platformApiLevel None Build.FINGERPRINT, the API level
device_name deviceName UIDevice.name (a generic name on iOS 16+) The device name setting, else the model
device_type deviceType DeviceType from the interface idiom DeviceType from TV mode and screen width (600dp and up is a tablet)

Constants on Android and iOS

is_device and emulators

is_device is False on the iOS Simulator and, on a best-effort basis, on the Android emulator. Use it to skip features that need real hardware (haptics, camera). Do not use it for security.

Device type

if Device.device_type == Device.DeviceType.TABLET:
    columns = 3
DeviceType Value Meaning
UNKNOWN 0 The platform could not tell.
PHONE 1 A phone.
TABLET 2 A tablet (an iPad, or Android 600dp and wider).
DESKTOP 3 The web page or the desktop window.
TV 4 Android TV mode.

await Device.get_device_type_async() returns the same value as the constant.

Memory and uptime

total = Device.total_memory                       # bytes of RAM
limit = await Device.get_max_memory_async()       # Android: the runtime limit; elsewhere MAX_SAFE_INTEGER
ms = await Device.get_uptime_async()              # milliseconds since boot, not counting deep sleep

MAX_SAFE_INTEGER (2 ** 53 - 1) is what expo-device returns when memory has no inherent limit, which is the case on iOS and the desktop.

Android system features

if await Device.has_platform_feature_async("android.hardware.camera"):
    show_camera_button()
names = await Device.get_platform_features_async()        # every feature the device reports

has_platform_feature_async(feature) takes a non-empty string and raises ValueError otherwise. On iOS and the desktop it returns False, and get_platform_features_async() returns [].

is_side_loading_enabled_async() tells whether this app may install other apps. It needs the REQUEST_INSTALL_PACKAGES permission on Android 8 and later, and is always False on iOS and the desktop.

is_rooted_experimental_async() is a best-effort jailbreak or root check. It is easy to fool, so never use it as a security decision.

Errors

Error When What to do
ValueError has_platform_feature_async gets an empty or non-string feature. Pass a feature name such as "android.hardware.camera".
ModuleNotLinkedError (from pyxpo.module_api) The app was built without "device" in modules. Add it and rebuild. The desktop is exempt.
AttributeError A name that is not a constant, for example a typo. Check the Constants table.

Config

There are no module options and no config plugin. The only native setup is the modules entry.

Web and desktop

The same names work. Values come from Python's platform module (uname()), total_memory from sysconf where available, device_type is DESKTOP, brand and model are None, and get_uptime_async() uses the monotonic clock. is_rooted_experimental_async(), is_side_loading_enabled_async() and has_platform_feature_async() return False.

Recipe: a diagnostics line for support

One line the user can paste into a support email.

import pyxpo_application as Application
import pyxpo_device as Device


def diagnostics() -> str:
    gb = (Device.total_memory or 0) / 1024 ** 3
    parts = [
        "%s %s" % (Device.brand or "?", Device.model_name or "?"),
        "%s %s" % (Device.os_name, Device.os_version),
        "%.1f GB" % gb,
        "app %s" % (Application.native_application_version or "dev"),
    ]
    if Device.platform_api_level:
        parts.append("API %s" % Device.platform_api_level)
    if not Device.is_device:
        parts.append("emulator")
    return " | ".join(parts)

Production checklist

  • No decision about security or licensing depends on is_rooted_experimental_async() or is_device.
  • Layouts do not assume a phone: DeviceType.TABLET and DESKTOP are handled.
  • Android-only values (platform_api_level, model_id on iOS) are None-checked.
  • A feature that needs hardware checks has_platform_feature_async before showing its button.
  • Device names (device_name) are not sent to your server without the user knowing.

Testing

Constants are read through pyxpo.module_api. Patch _all to control them:

import pyxpo_device as Device


def test_tablet_layout(monkeypatch):
    monkeypatch.setattr(Device, "_all", lambda: {**{k: None for k in Device._FIELDS}, "deviceType": 2})
    assert Device.device_type == Device.DeviceType.TABLET

On the desktop test host the values come from Python's platform module, and device_type is DESKTOP.

Troubleshooting

Symptom Cause and fix
brand and model_name are None You are on the desktop or the web page. They only exist on a phone.
model_id is None on Android It is iOS only. Use product_name or design_name.
device_year_class is None Unknown iPhone model, or Android.
get_max_memory_async() returns a huge number iOS and the desktop have no runtime limit, so it is MAX_SAFE_INTEGER.
is_side_loading_enabled_async() is always False Add REQUEST_INSTALL_PACKAGES to the Android manifest, and note iOS is always False.
ModuleNotLinkedError Add "device" to [tool.pyxpo] modules and rebuild.

FAQ

Is device_name the user's name? It is the name in the device settings. On iOS 16 and later Apple returns a generic name such as "iPhone" unless the app has a special entitlement.

Does it need any permission? No.

Is the tablet check reliable? On iOS it follows the interface idiom (iPad is a tablet). On Android it follows TV mode and a 600dp screen width.

Why total_memory and not free memory? It is the installed RAM. Free memory changes every second and is not something the system promises to report.

API reference

Constants

is_device, brand, manufacturer, model_name, model_id, design_name, product_name, device_year_class, total_memory, supported_cpu_architectures, os_name, os_version, os_build_id, os_internal_build_id, os_build_fingerprint, platform_api_level, device_name, device_type. Each has a camelCase twin.

Functions

Function (expo name) Platforms Returns
get_device_type_async() (getDeviceTypeAsync) all DeviceType
get_uptime_async() (getUptimeAsync) all int milliseconds
get_max_memory_async() (getMaxMemoryAsync) all int bytes, MAX_SAFE_INTEGER when unlimited
is_rooted_experimental_async() (isRootedExperimentalAsync) all bool
is_side_loading_enabled_async() (isSideLoadingEnabledAsync) Android bool, False elsewhere
get_platform_features_async() (getPlatformFeaturesAsync) Android list[str], [] elsewhere
has_platform_feature_async(feature) (hasPlatformFeatureAsync) Android bool, False elsewhere

Types

Name Description
DeviceType IntEnum: UNKNOWN, PHONE, TABLET, DESKTOP, TV.
MAX_SAFE_INTEGER 2 ** 53 - 1.

License

MIT

Metadata

Release files for pyxpo-device 0.1.0a2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyxpo-device 0.1.0a2
File Size Uploaded Provenance Actions
pyxpo_device-0.1.0a2.tar.gz 19.0 kB With provenance Details for pyxpo_device-0.1.0a2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pyxpo-device 0.1.0a2
File Interpreter ABI Platform Actions
pyxpo_device-0.1.0a2-py3-none-any.whl Python 3 none any Details for pyxpo_device-0.1.0a2-py3-none-any.whl

Total release size: 33.8 kB

Release files / pyxpo_device-0.1.0a2.tar.gz

Download URL pyxpo_device-0.1.0a2.tar.gz
Size 19.0 kB
Tags Source
SHA-256 checksum
How to use checksums
2a4e10b328224ee3a82f5e0b4cb7e97ae194912204f1db2d7466f1a8316f7ff5
BLAKE2b-256 checksum
How to use checksums
b1a4dbad9a79517b6d47d7cac4735360d14f9fd54a208bb09fc6546fd23f6d58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / pyxpo_device-0.1.0a2-py3-none-any.whl

Download URL pyxpo_device-0.1.0a2-py3-none-any.whl
Size 14.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9d8ed2647651bb9bfb73142a15c79898f5a7e7640ef6581a57b1eeb580c00d25
BLAKE2b-256 checksum
How to use checksums
a78a76eca1f53638f19180f881bc52d4ca3460a05e0fffe7ef291d31d4044e29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Security for pyxpo-device 0.1.0a2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pyxpo_device-0.1.0a2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page