Skip to main content

handsets — Python bindings

A small, Pythonic wrapper around the Handsets CLI (hs). Drives Android devices from Python without reimplementing the subprocess + JSON-parse + exit-code boilerplate every caller used to write by hand.

Install

pip install handsets

You also need the hs binary on $PATH. See the project install instructions.

Usage

from handsets import Session

with Session() as d:                   # `hs use` on enter, `hs drop` on exit
    for node in d.ui():
        print(node.cls, node.text, node.coords)

    d.tap("Continue")                  # text lookup
    d.tap(540, 860)                    # raw coords
    d.fill("EditText", "you@x.com")    # atomic ACTION_SET_TEXT against the selector
    d.type("hello")                    # keystrokes to the focused field
    d.submit()
    d.wait(text="Welcome", timeout="15s")

Errors map to typed exceptions:

from handsets import Session, NotFound, Timeout, Ambiguous

try:
    d.tap("Submit", unique=True, timeout="5s")
except NotFound:
    ...  # exit code 2 — selector matched nothing
except Timeout:
    ...  # exit code 3 — wait budget exhausted
except Ambiguous:
    ...  # exit code 4 — --unique saw multiple matches

Everything else (daemon errors, bad arguments, secure-window blocks) raises a generic HandsetsError whose .code attribute carries the structured ErrCode enum value from the CLI's JSON output.

Batching

For tight loops, Session.batch() opens a warm-socket context that keeps one hs run - subprocess alive across calls. Per-call process startup (~5–10 ms each) collapses into one startup for the whole batch:

with Session() as d:
    with d.batch(timeout="5s", retries=2) as b:
        for label in labels_to_press:
            b.tap(label, visible=True)
        b.wait(text="Done")

batch() covers the action verbs (tap, type, fill, submit, paste, wait, go, swipe). Query verbs (find, ui) still spawn per-call — they emit multiple JSON lines which would desync the batch read loop.

Talking to a specific device

Session(serial="PIXEL6_SERIAL")

Multiple sessions can run side-by-side; each one shells out independently.

Why a thin wrapper?

The CLI already does the hard work: warm daemon, push-mirrored state, millisecond round-trips. The Python layer's job is to make that ergonomic — context managers, typed exceptions, no manual subprocess.run. Future versions may keep an hs run subprocess warm and stream commands over its stdin to amortise per-call process overhead.

Metadata

Release files for handsets 0.1.38

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

Source distribution (sdist)

Source distribution for handsets 0.1.38
File Size Uploaded Provenance
handsets-0.1.38.tar.gz 9.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for handsets 0.1.38
File Interpreter ABI Platform
handsets-0.1.38-py3-none-any.whl Python 3 none any Details

Total release size: 19.4 kB

Release files / handsets-0.1.38.tar.gz

Download URL handsets-0.1.38.tar.gz
Size 9.8 kB
Tags Source
SHA-256 checksum
How to use checksums
e2fc9e1f00bbb0421278e702e37f68ca482336b7cd49f14d07e80f3e700c47fd
BLAKE2b-256 checksum
How to use checksums
681c6303dce3c61aae5af57b48156088d56a4e5f9a02e9ae351ac8abd6fd0462
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.9

Release files / handsets-0.1.38-py3-none-any.whl

Download URL handsets-0.1.38-py3-none-any.whl
Size 9.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9412d3bb24122a3269420bc6d91022941014f3d076313a95853dc15cc9879f47
BLAKE2b-256 checksum
How to use checksums
553e98cb502c67a9e2fd2dc43e657e751258b77c5f9848474ede4d57b2fa7392
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.9

Security for handsets 0.1.38

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

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