Skip to content

Bridge

The Python half of the native bridge: JSON codec, per-platform transports, protocol handshake, main-queue posting, and the single callback native uses to reach Python. App code never calls this module directly; the BridgeBackend, BridgeModule, and NativeScreenHost do.

The native bridge: one channel between Python and Swift / Kotlin.

Everything that crosses into native code goes through a Transport (apply a transaction, measure a view, run a command, drive an animate request, or call a native module), and everything native sends back arrives at native_callback. The protocol is documented in docs/concepts/bridge.md.

Off-device (tests, pn preview) there is no transport; the desktop registry renders with Tkinter and native modules fall back to their Python implementations. Tests that want to exercise the bridge itself install a FakeTransport with set_transport.

Modules:

Name Description
android

Android transport: the com.pythonnative.runtime.PNBridge class via Chaquopy.

codec

JSON encoding for the native bridge.

fake

An in-process stand-in for the native side of the bridge.

ios

iOS transport: C-ABI calls into PythonNativeKit through ctypes.

Classes:

Name Description
Transport

The Python -> native half of the bridge.

Functions:

Name Description
get_transport

Return the active transport, creating the platform one on first use.

has_transport

Whether a transport exists or can be created without raising.

set_transport

Install a transport explicitly (tests) or reset with None.

handshake

Verify the native library speaks our protocol version.

post_to_main

Queue fn for the next main-thread turn (never runs inline).

native_callback

Single entry point for every native -> Python message.

Attributes:

Name Type Description
PROTOCOL_VERSION

Bridge protocol version this Python package speaks.

PROTOCOL_VERSION module-attribute

PROTOCOL_VERSION = 1

Bridge protocol version this Python package speaks.

Transport

Bases: Protocol

The Python -> native half of the bridge.

Methods:

Name Description
protocol_version

Return the protocol version compiled into the native library.

apply

Apply one serialized transaction (a JSON array of ops).

measure

Return the intrinsic (width, height) of the view tag under the constraints.

command

Run an imperative command on one view; returns its JSON result or None.

animate

Handle an animation request (set / start / cancel) for one view.

call

Call a native module method with a {"call_id", "args"} envelope.

set_callback

Install callback as the native -> Python entry point.

protocol_version

protocol_version() -> int

Return the protocol version compiled into the native library.

apply

apply(transaction_json: str) -> None

Apply one serialized transaction (a JSON array of ops).

measure

measure(
    tag: int, max_width: float, max_height: float
) -> Tuple[float, float]

Return the intrinsic (width, height) of the view tag under the constraints.

command

command(
    tag: int, name: str, args_json: str
) -> Optional[str]

Run an imperative command on one view; returns its JSON result or None.

animate

animate(tag: int, request_json: str) -> Optional[str]

Handle an animation request (set / start / cancel) for one view.

call

call(
    module: str, method: str, args_json: str
) -> Optional[str]

Call a native module method with a {"call_id", "args"} envelope.

set_callback

set_callback(
    callback: Callable[[str, int, str, str], Optional[str]],
) -> None

Install callback as the native -> Python entry point.

get_transport

get_transport() -> Transport

Return the active transport, creating the platform one on first use.

Raises:

Type Description
RuntimeError

Off-device, where no native runtime exists.

has_transport

has_transport() -> bool

Whether a transport exists or can be created without raising.

set_transport

set_transport(transport: Optional[Transport]) -> None

Install a transport explicitly (tests) or reset with None.

handshake

handshake() -> int

Verify the native library speaks our protocol version.

Called by the native templates right after Python starts. Returns the negotiated version.

Raises:

Type Description
RuntimeError

On a version mismatch, with a hint to rebuild.

post_to_main

post_to_main(fn: Callable[[], None]) -> None

Queue fn for the next main-thread turn (never runs inline).

native_callback

native_callback(
    kind: str, tag: int, name: str, payload: str
) -> Optional[str]

Single entry point for every native -> Python message.

Parameters:

Name Type Description Default
kind str

"event", "module", "host", "animation", or "pump".

required
tag int

View tag (events), screen id (host), otherwise 0.

required
name str

Event name, module name, or host event.

required
payload str

JSON text whose shape depends on kind.

required

Returns:

Type Description
Optional[str]

A JSON string for request-style messages (a handler's return

Optional[str]

value, "true" / "false" for back_pressed), else

Optional[str]

None. Never raises: failures are reported through

Optional[str]

diagnostics so nothing propagates into UIKit or the

Optional[str]

Android looper.

transport_state

transport_state() -> Dict[str, Any]

Diagnostics snapshot (used by pn doctor and tests).

Codec

JSON encoding for the native bridge.

The bridge speaks JSON in both directions (see docs/concepts/bridge.md). Prop values coming out of the reconciler are almost, but not quite, JSON: they may contain frozenset event-name sets, tuples, floats that are infinite, and Python callables that native must never see. to_jsonable normalizes a value into something json.dumps accepts and reports the keys it had to drop so the backend can keep them Python-side.

Functions:

Name Description
to_jsonable

Return a JSON-serializable copy of value.

split_props

Split props into (wire_props, python_props).

encode_transaction

Encode a mutation batch as the bridge's transaction JSON.

dumps

Compact json.dumps used for every bridge payload.

loads

Parse a bridge payload (str or UTF-8 bytes); empty / None input yields None.

Attributes:

Name Type Description
INF

Wire spelling of math.inf (JSON has no infinity literal).

NEG_INF

Wire spelling of -math.inf.

INF module-attribute

INF = 'inf'

Wire spelling of math.inf (JSON has no infinity literal).

NEG_INF module-attribute

NEG_INF = '-inf'

Wire spelling of -math.inf.

to_jsonable

to_jsonable(value: Any) -> Any

Return a JSON-serializable copy of value.

  • set / frozenset / tuple become lists (sets are sorted when their members are strings so the output is deterministic).
  • Infinite floats become the strings "inf" / "-inf"; NaN becomes None (native clamps missing geometry to zero).
  • Dataclass-like objects with to_json() are converted through it.

Raises:

Type Description
TypeError

When value (or something nested in it) has no JSON representation; callers decide whether to drop it.

split_props

split_props(
    props: Dict[str, Any],
) -> Tuple[Dict[str, Any], Dict[str, Any]]

Split props into (wire_props, python_props).

wire_props is JSON-ready; python_props holds every prop that can't cross the bridge (callables such as render_row, arbitrary objects). The backend keeps the latter in a per-tag sidecar so native-backed handlers that need them (virtualized list rows) can still reach them.

encode_transaction

encode_transaction(
    ops: Sequence[Mutation], prop_filter: Any = None
) -> Tuple[str, List[Tuple[int, Dict[str, Any]]]]

Encode a mutation batch as the bridge's transaction JSON.

Parameters:

Name Type Description Default
ops Sequence[Mutation]

Ordered mutations from the reconciler.

required
prop_filter Any

Unused hook kept for symmetry with the desktop registry; reserved for per-type prop rewriting.

None

Returns:

Type Description
str

(json_text, python_props) where python_props lists

List[Tuple[int, Dict[str, Any]]]

(tag, props) pairs for values that stayed Python-side (for

Tuple[str, List[Tuple[int, Dict[str, Any]]]]

c and u ops). An update that removes a Python-side

Tuple[str, List[Tuple[int, Dict[str, Any]]]]

prop reports it as None so the sidecar can drop it.

dumps

dumps(value: Any) -> str

Compact json.dumps used for every bridge payload.

loads

loads(text: Union[str, bytes, bytearray, None]) -> Any

Parse a bridge payload (str or UTF-8 bytes); empty / None input yields None.

Fake transport (tests)

An in-process stand-in for the native side of the bridge.

FakeTransport decodes the same JSON the Swift and Kotlin runtimes receive and keeps a tiny view tree so tests can assert on what would have reached native: created types and props, insert order, frames, commands, animation requests, and module calls. It also lets tests play the native side, firing events and module results back through native_callback.

Classes:

Name Description
FakeNativeView

One decoded native view: type, merged props, children, and frame.

FakeTransport

Decode bridge traffic into inspectable Python structures.

ModuleHandler module-attribute

ModuleHandler = Callable[[str, Dict[str, Any]], Any]

handler(method, args) -> value for a fake native module.

FakeNativeView

FakeNativeView(
    tag: int, type_name: str, props: Dict[str, Any]
)

One decoded native view: type, merged props, children, and frame.

FakeTransport

FakeTransport(
    *,
    version: int = 1,
    measure: Optional[Dict[str, Tuple[float, float]]] = None
)

Decode bridge traffic into inspectable Python structures.

Attributes:

Name Type Description
views Dict[int, FakeNativeView]

Live views by tag.

transactions List[List[Any]]

Every decoded transaction (list of op lists).

commands List[Tuple[int, str, Dict[str, Any]]]

(tag, name, args) tuples in call order.

animations List[Tuple[int, Dict[str, Any]]]

(tag, request) tuples in call order.

calls List[Tuple[str, str, Dict[str, Any]]]

(module, method, args) tuples in call order.

Methods:

Name Description
protocol_version

Return the protocol version compiled into the native library.

set_callback

Install callback as the native -> Python entry point.

apply

Apply one serialized transaction (a JSON array of ops).

measure

Return the intrinsic (width, height) of the view tag under the constraints.

command

Run an imperative command on one view; returns its JSON result or None.

animate

Handle an animation request (set / start / cancel) for one view.

call

Call a native module method with a {"call_id", "args"} envelope.

fire

Emit a view event as native would; returns the decoded handler result.

emit_module_event

Push an unsolicited module event (AppState change, ...) into Python.

resolve_pending

Settle a call that returned pending.

host_event

Deliver a screen lifecycle event as the native host would; returns the decoded result.

pump

Deliver the pump callback (what Host.post would trigger).

complete_animation

Report a native animation as finished (or interrupted) to Python.

find

Every live view whose type is type_name, in creation order.

roots

Views with no parent (the attached screen roots and detached subtrees).

pending

Sentinel a module handler returns to answer pending.

protocol_version

protocol_version() -> int

Return the protocol version compiled into the native library.

set_callback

set_callback(
    callback: Callable[[str, int, str, str], Optional[str]],
) -> None

Install callback as the native -> Python entry point.

apply

apply(transaction_json: str) -> None

Apply one serialized transaction (a JSON array of ops).

measure

measure(
    tag: int, max_width: float, max_height: float
) -> Tuple[float, float]

Return the intrinsic (width, height) of the view tag under the constraints.

command

command(
    tag: int, name: str, args_json: str
) -> Optional[str]

Run an imperative command on one view; returns its JSON result or None.

animate

animate(tag: int, request_json: str) -> Optional[str]

Handle an animation request (set / start / cancel) for one view.

call

call(
    module: str, method: str, args_json: str
) -> Optional[str]

Call a native module method with a {"call_id", "args"} envelope.

fire

fire(tag: int, name: str, *args: Any) -> Any

Emit a view event as native would; returns the decoded handler result.

emit_module_event

emit_module_event(
    module: str, event: str, payload: Any = None
) -> None

Push an unsolicited module event (AppState change, ...) into Python.

resolve_pending

resolve_pending(
    call_id: int,
    value: Any = None,
    *,
    error: Optional[str] = None
) -> None

Settle a call that returned pending.

host_event

host_event(
    screen: int, event: str, payload: Any = None
) -> Any

Deliver a screen lifecycle event as the native host would; returns the decoded result.

pump

pump() -> None

Deliver the pump callback (what Host.post would trigger).

complete_animation

complete_animation(
    anim_id: int, finished: bool = True
) -> None

Report a native animation as finished (or interrupted) to Python.

find

find(type_name: str) -> List[FakeNativeView]

Every live view whose type is type_name, in creation order.

roots

roots() -> List[FakeNativeView]

Views with no parent (the attached screen roots and detached subtrees).

pending staticmethod

pending() -> object

Sentinel a module handler returns to answer pending.

Bootstrap

Entry point the native app templates run right after Python starts.

Both templates execute one line of Python once the interpreter is up:

import pythonnative.bootstrap; pythonnative.bootstrap.start()

start connects the two halves of the bridge (installing the native -> Python callback on iOS), verifies the protocol version, routes print() to the console on iOS, and warms the asyncio runtime. From then on the native runtime drives everything through callback("host", ...); see docs/concepts/bridge.md.

Functions:

Name Description
start

Connect the bridge and prepare the runtime.

status

Return the result of the last start (empty before it ran).

start

start(
    dev: bool = False, strict: bool = False
) -> Dict[str, Any]

Connect the bridge and prepare the runtime.

Parameters:

Name Type Description Default
dev bool

Enable dev mode (RedBox, validation warnings). Debug templates pass True; hot reload turns it on as well.

False
strict bool

Re-raise the failure after recording it. The templates pass True so a broken bridge surfaces as a bootstrap error screen with the full traceback.

False

Returns:

Type Description
Dict[str, Any]

A status dict ({"protocol": 1, "platform": "ios"}) that the

Dict[str, Any]

template logs. Unless strict is set this never raises:

Dict[str, Any]

failures are printed and reported in the dict under

Dict[str, Any]

"error".

status

status() -> Dict[str, Any]

Return the result of the last start (empty before it ran).

Next steps