Skip to content

DevTools agent and error reports

The in-app half of PythonNative Go and DevTools: the agent that answers DevTools, the inspector it uses, the REPL, live performance sampling, and the debugger integration. Development builds only; release bundles omit pythonnative.devtools.

pythonnative.devtools

The devtools agent: answer DevTools and publish what the app is doing.

One Agent runs in every dev client (PythonNative Go, a project's debug build) and in the browser preview's process. The dev server forwards DevTools requests to it with Agent.dispatch, and the agent publishes events back through the publish callback its owner supplies: the dev client sends them over the dev session, and the preview hands them to the server directly.

Requests run on the application loop, so they see a consistent tree and may await. See the method table in RFC 0006 for the request names.

The agent also owns the device-side development UI, through the native DevSupport module: the dev menu, the element inspector, the highlight outline, and the performance monitor.

Classes:

Name Description
MenuItem

One dev menu entry.

Agent

Serve DevTools requests and publish app events.

Functions:

Name Description
current

The process's agent, if one is installed.

install

Create, start, and register the process's agent (replacing any previous one).

uninstall

Stop and forget the process's agent (and restore breakpoint()).

MenuItem

MenuItem(title: str, action: Callable[[], Any])

One dev menu entry.

Agent

Agent(
    publish: Publish,
    *,
    kind: str = "app",
    disconnect: Optional[Callable[[], None]] = None,
    reload: Optional[Callable[[], None]] = None
)

Serve DevTools requests and publish app events.

Parameters:

Name Type Description Default
publish Publish

publish(topic, data); called from any thread.

required
kind str

"go", "app", or "preview"; shapes the dev menu.

'app'
disconnect Optional[Callable[[], None]]

Called by the dev menu's Disconnect item (Go only).

None
reload Optional[Callable[[], None]]

Remounts the app; defaults to reloading the app's modules and remounting every screen.

None

Methods:

Name Description
start

Subscribe to warnings, errors, network requests, and native dev events.

stop

Undo start.

dispatch

Run request method on the application loop and reply(result, error).

request

Call a request handler directly (tests and the preview).

rpc_tree

Every live screen's component tree and the selected node.

rpc_inspect

One node's props, hooks, context, frame, and source; it becomes the selection.

rpc_set_state

Replace a use_state slot of node id with a Python literal.

rpc_highlight

Outline node id on screen, or clear the outline when id is None.

rpc_set_inspecting

Turn the on-device element inspector on or off.

rpc_eval

Evaluate code in the REPL namespace (see Console.evaluate).

rpc_reload

Remount the app with fresh state.

rpc_menu

Show the dev menu.

rpc_perf

Turn the DevTools panel's sampling (and optionally the on-device monitor) on or off.

rpc_trace

"start" recording a Chrome trace, or "stop" and return it.

rpc_debug

Start the in-process debug adapter; returns {"port"}.

rpc_status

What the agent is doing: inspector, monitor, sampling, debugger.

set_perf_monitor

Show or hide the floating on-device performance monitor.

menu_items

The dev menu's entries, in order.

menu_subtitle

The dev menu's message: what this client runs and where it's connected.

open_menu

Show the dev menu (from any thread).

start

start() -> None

Subscribe to warnings, errors, network requests, and native dev events.

stop

stop() -> None

Undo start.

dispatch

dispatch(
    method: str,
    params: Optional[Dict[str, Any]],
    reply: Reply,
) -> None

Run request method on the application loop and reply(result, error).

Safe to call from any thread. reply runs on the application loop.

request async

request(method: str, **params: Any) -> Any

Call a request handler directly (tests and the preview).

rpc_tree

rpc_tree() -> Dict[str, Any]

Every live screen's component tree and the selected node.

rpc_inspect

rpc_inspect(id: int) -> Dict[str, Any]

One node's props, hooks, context, frame, and source; it becomes the selection.

rpc_set_state

rpc_set_state(id: int, index: int, value: str) -> bool

Replace a use_state slot of node id with a Python literal.

rpc_highlight

rpc_highlight(id: Optional[int] = None) -> bool

Outline node id on screen, or clear the outline when id is None.

rpc_set_inspecting

rpc_set_inspecting(enabled: bool) -> bool

Turn the on-device element inspector on or off.

rpc_eval async

rpc_eval(code: str) -> Dict[str, Any]

Evaluate code in the REPL namespace (see Console.evaluate).

rpc_reload

rpc_reload() -> bool

Remount the app with fresh state.

rpc_menu

rpc_menu() -> bool

Show the dev menu.

rpc_perf

rpc_perf(
    enabled: bool, monitor: Optional[bool] = None
) -> Dict[str, Any]

Turn the DevTools panel's sampling (and optionally the on-device monitor) on or off.

rpc_trace

rpc_trace(action: str) -> Any

"start" recording a Chrome trace, or "stop" and return it.

rpc_debug async

rpc_debug(
    host_paths: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]

Start the in-process debug adapter; returns {"port"}.

rpc_status

rpc_status() -> Dict[str, Any]

What the agent is doing: inspector, monitor, sampling, debugger.

set_perf_monitor

set_perf_monitor(visible: bool) -> None

Show or hide the floating on-device performance monitor.

menu_items

menu_items() -> List[MenuItem]

The dev menu's entries, in order.

menu_subtitle

menu_subtitle() -> str

The dev menu's message: what this client runs and where it's connected.

open_menu

open_menu() -> None

Show the dev menu (from any thread).

current

current() -> Optional['Agent']

The process's agent, if one is installed.

reload_app

reload_app() -> None

Re-import the app's modules and remount every screen with fresh state.

install

install(publish: Publish, **options: Any) -> Agent

Create, start, and register the process's agent (replacing any previous one).

uninstall

uninstall() -> None

Stop and forget the process's agent (and restore breakpoint()).

run_threadsafe

run_threadsafe(
    coro_factory: Callable[[], Awaitable[Any]],
    timeout: float = 10.0,
) -> Any

Run a coroutine on the application loop from another thread and wait for it.

Inspector

Read and edit the mounted component tree for DevTools.

Every ScreenHost owns a reconciler whose mounted VNodes form the tree DevTools shows. Node ids are id(vnode): nodes are reused across renders, so an id stays valid while the node is mounted, and every lookup walks the live trees, so a stale id simply isn't found.

Values are summarized with summarize before they leave the process: DevTools sees short, typed descriptions, never the objects themselves.

Functions:

Name Description
find_node

The mounted node whose id is node_id, or None.

find_node_by_tag

The native node that owns view tag, or None.

summarize

A short, JSON-safe description of value for DevTools.

tree

Every live screen's tree, as nested {"id", "name", "kind", ...} dicts.

node_label_path

Component names from the root down to node, skipping framework components.

inspect_node

Props, hooks, context, frame, and source of one node.

set_state

Replace use_state slot index of a component with a Python literal.

live_reconcilers

live_reconcilers() -> List[Tuple[str, Any]]

(label, reconciler) for every live screen host.

find_node

find_node(
    node_id: int,
    reconcilers: Optional[Iterable[Tuple[str, Any]]] = None,
) -> Optional[Any]

The mounted node whose id is node_id, or None.

find_node_by_tag

find_node_by_tag(
    tag: int,
    reconcilers: Optional[Iterable[Tuple[str, Any]]] = None,
) -> Optional[Any]

The native node that owns view tag, or None.

summarize

summarize(value: Any, depth: int = 0) -> Dict[str, Any]

A short, JSON-safe description of value for DevTools.

Returns a dict with type (the class name), repr (a truncated one-line description), and, for containers and dataclasses within the depth limit, items (a list of [key, summary] pairs).

tree

tree(
    reconcilers: Optional[Iterable[Tuple[str, Any]]] = None,
) -> List[Dict[str, Any]]

Every live screen's tree, as nested {"id", "name", "kind", ...} dicts.

node_label_path

node_label_path(node: Any) -> List[str]

Component names from the root down to node, skipping framework components.

owner_component

owner_component(node: Any) -> Any

The nearest component at or above node (node itself for components).

native_root_tag

native_root_tag(node: Any) -> Optional[int]

The tag of the first native view node renders, if any.

inspect_node

inspect_node(node: Any) -> Dict[str, Any]

Props, hooks, context, frame, and source of one node.

set_state

set_state(node: Any, index: int, source: str) -> None

Replace use_state slot index of a component with a Python literal.

Raises:

Type Description
ValueError

When source isn't a literal or the slot has no setter.

Console

The DevTools REPL: evaluate Python inside the running app.

Code runs on the application loop, so it sees the same state components do and may await (await pn.fetch(...), await asyncio.sleep(1)). Each agent keeps one namespace across evaluations. It starts with pn (pythonnative), asyncio, app (the entry module, once imported), and node, a NodeHandle for the component selected in DevTools.

Classes:

Name Description
NodeHandle

The component selected in DevTools, as seen from the REPL.

Console

One persistent REPL namespace.

NodeHandle

NodeHandle(vnode: Any)

The component selected in DevTools, as seen from the REPL.

Attributes are read live from the mounted node, so node.state reflects the latest render.

Methods:

Name Description
set_state

Call the setter of use_state slot index with value.

Attributes:

Name Type Description
name str

The component or view name.

props Dict[str, Any]

The props the node last rendered with.

state List[Any]

The values of its use_state and use_reducer slots, in order.

refs List[Any]

The current values of its use_ref slots.

name property

name: str

The component or view name.

props property

props: Dict[str, Any]

The props the node last rendered with.

state property

state: List[Any]

The values of its use_state and use_reducer slots, in order.

refs property

refs: List[Any]

The current values of its use_ref slots.

set_state

set_state(index: int, value: Any) -> None

Call the setter of use_state slot index with value.

Console

Console()

One persistent REPL namespace.

Methods:

Name Description
select

Bind node to the selected component (None clears it).

evaluate

Run source and describe the outcome.

select

select(vnode: Optional[Any]) -> None

Bind node to the selected component (None clears it).

evaluate async

evaluate(source: str) -> Dict[str, Any]

Run source and describe the outcome.

A single expression returns its repr; statements return None. Top-level await is allowed.

Returns:

Type Description
Dict[str, Any]

{"ok": True, "repr": str | None, "type": str} or

Dict[str, Any]

{"ok": False, "error": traceback text}.

Performance

Live performance numbers and trace capture for DevTools.

While DevTools' Performance panel or the on-device performance monitor is open, Sampler keeps a Profiler installed as the process session and summarizes it once a second:

  • Loop lag: how late a timer scheduled on the application loop fired, the Python counterpart of a dropped frame.
  • Renders and commits: reconciler passes, components rendered, native commits, operations, and the time each phase took.
  • UI frame rate: reported by the native DevSupport module.

Trace capture records the same profiler's events into a Chrome trace document that Perfetto and Chrome's trace viewer open.

Classes:

Name Description
Sampler

Summarize runtime work once a second while enabled.

Sampler

Sampler(
    publish: Callable[[Dict[str, Any]], None],
    frame_stats: Optional[
        Callable[[], Optional[Dict[str, Any]]]
    ] = None,
)

Summarize runtime work once a second while enabled.

Parameters:

Name Type Description Default
publish Callable[[Dict[str, Any]], None]

Called with each sample dict on the application loop.

required
frame_stats Optional[Callable[[], Optional[Dict[str, Any]]]]

Returns the native UI thread's {"fps", "dropped"}, or None where there's no native UI thread.

None

Methods:

Name Description
acquire

Start sampling (reference counted: the panel and the monitor share it).

release

Stop sampling once the last consumer releases it.

start_trace

Begin recording a trace (sampling may continue alongside it).

stop_trace

Stop recording and return the Chrome trace document.

monitor_lines

Text lines for the on-device performance monitor.

Attributes:

Name Type Description
running bool

Whether samples are being produced.

running property

running: bool

Whether samples are being produced.

acquire

acquire() -> None

Start sampling (reference counted: the panel and the monitor share it).

release

release() -> None

Stop sampling once the last consumer releases it.

start_trace

start_trace() -> None

Begin recording a trace (sampling may continue alongside it).

stop_trace

stop_trace() -> Dict[str, Any]

Stop recording and return the Chrome trace document.

monitor_lines

monitor_lines() -> List[str]

Text lines for the on-device performance monitor.

Debugger

Breakpoint debugging through debugpy, inside any dev client.

pn start accepts Debug Adapter Protocol connections on 127.0.0.1:5678 and asks the selected dev client to start a debug adapter. start runs debugpy with its adapter in this process (in_process_debug_adapter=True): mobile platforms can't spawn the separate adapter process debugpy normally uses. The adapter listens on a localhost port, which the dev server reaches directly (the browser preview runs in its own process) or through the dev session's tunnel (devices).

On a device debugpy arrives through the dev session's package sync: the dev server sends its Python sources, without the compiled speedups, into the overlay's site-packages. The pure-Python tracer is slower than the compiled one, which only matters while a debugger is attached.

Path mapping makes breakpoints set in the editor bind on the device: the dev server sends the host paths of the project, the pythonnative package, and the installed packages, and the device maps its own copies to them.

Functions:

Name Description
mark_dev_thread

Keep the debugger from tracing or suspending a development thread.

attached

Whether a debugger client is connected to this process.

start

Start the in-process debug adapter (once) and return its port.

install_breakpoint_hook

Make breakpoint() useful in a dev client.

mark_dev_thread

mark_dev_thread(thread: Optional[Thread] = None) -> None

Keep the debugger from tracing or suspending a development thread.

The dev client's network and tunnel threads carry the debugger's own traffic; pausing them at a breakpoint would freeze the session that reports the breakpoint. pydevd skips threads marked this way.

attached

attached() -> bool

Whether a debugger client is connected to this process.

start

start(host_paths: Optional[Dict[str, Any]] = None) -> int

Start the in-process debug adapter (once) and return its port.

Parameters:

Name Type Description Default
host_paths Optional[Dict[str, Any]]

{"project", "pythonnative", "site_packages"} on the developer's machine, for path mapping. None (the browser preview) needs no mapping.

None

Raises:

Type Description
RuntimeError

When debugpy isn't importable here.

install_breakpoint_hook

install_breakpoint_hook(log: Any = None) -> None

Make breakpoint() useful in a dev client.

With a debugger attached it pauses there. Without one it reports the location and how to attach, instead of starting pdb with no terminal to read from.

pythonnative.errors

Structured error reports for the development error screen and DevTools.

A traceback says where Python was; it doesn't say which component was rendering, and it buries the application's frames among the reconciler's. ErrorReport adds both:

  • A component stack. In development mode the reconciler records the component path while rendering, running effects, and updating, and attaches it to an exception the first time the exception crosses a component (see add_component_frame). PythonNative's own components (navigators, host roots) are left out. The path also appears in the printed traceback as a note (PEP 678):
ZeroDivisionError: division by zero
The above error occurred in <Counter> (app/main.py:14)
  in <HomeScreen> (app/main.py:30)
  in <App> (app/main.py:52)
  • Classified frames with source excerpts. Frames from PythonNative, the standard library, and installed packages are marked as framework frames, which the error screens collapse; application frames carry a few lines of source around the failing line.

The development error screen on iOS, Android, and in the browser preview renders ErrorReport.to_dict, and DevTools lists the same reports under Problems. Release builds never build reports: the error screen is development-only.

Classes:

Name Description
ComponentFrame

One component in a component stack.

StackFrame

One traceback frame, classified and with a source excerpt.

ErrorReport

An error or warning, ready for an error screen or DevTools.

Functions:

Name Description
add_component_frame

Record that exc passed through component while unwinding.

component_stack

The component stack recorded on exc (innermost first), or ().

project_path

filename relative to the project (app/main.py) when it's app code.

report

Build an ErrorReport for exc.

warning_report

Build a warning report, attributed to the component rendering now, if any.

ComponentFrame dataclass

ComponentFrame(name: str, file: str = '', line: int = 0)

One component in a component stack.

Attributes:

Name Type Description
name str

The component's display name.

file str

The file that defines it, relative to the project when possible (app/main.py); empty for built-ins.

line int

The first line of its definition (0 when unknown).

Methods:

Name Description
describe

<Name> (app/main.py:14), or <Name> without a location.

to_dict

The JSON shape native error screens and DevTools read.

describe

describe() -> str

<Name> (app/main.py:14), or <Name> without a location.

to_dict

to_dict() -> Dict[str, Any]

The JSON shape native error screens and DevTools read.

StackFrame dataclass

StackFrame(
    file: str,
    line: int,
    function: str,
    code: str = "",
    excerpt: Tuple[Tuple[int, str], ...] = (),
    framework: bool = False,
)

One traceback frame, classified and with a source excerpt.

Attributes:

Name Type Description
file str

The frame's file, relative to the project for application code (app/main.py) and absolute otherwise.

line int

The line number.

function str

The function name.

code str

The failing line's source, stripped.

excerpt Tuple[Tuple[int, str], ...]

(line number, text) pairs around the failing line.

framework bool

Whether the frame belongs to PythonNative, the standard library, or an installed package.

Methods:

Name Description
excerpt_text

The excerpt as text, with > marking the failing line.

to_dict

The JSON shape native error screens and DevTools read.

title property

title: str

app/main.py:14 in Counter.

excerpt_text

excerpt_text() -> str

The excerpt as text, with > marking the failing line.

to_dict

to_dict() -> Dict[str, Any]

The JSON shape native error screens and DevTools read.

ErrorReport dataclass

ErrorReport(
    phase: str,
    type: str,
    message: str,
    component_stack: Tuple[ComponentFrame, ...] = (),
    frames: Tuple[StackFrame, ...] = (),
    text: str = "",
    level: str = "error",
    timestamp: float = 0.0,
)

An error or warning, ready for an error screen or DevTools.

Attributes:

Name Type Description
phase str

Where it happened: "render", "effect", "event", "hot reload", or "warning".

type str

The exception's class name ("warning" for warnings).

message str

str(exception) or the warning text.

component_stack Tuple[ComponentFrame, ...]

Innermost component first.

frames Tuple[StackFrame, ...]

Innermost frame last, like a traceback.

text str

The formatted traceback, notes included.

level str

"error" or "warning".

Methods:

Name Description
to_dict

The JSON shape native error screens and DevTools read.

title property

title: str

ZeroDivisionError in render: division by zero.

to_dict

to_dict() -> Dict[str, Any]

The JSON shape native error screens and DevTools read.

add_component_frame

add_component_frame(
    exc: BaseException, component: Any
) -> None

Record that exc passed through component while unwinding.

The reconciler calls this, innermost component first, as an exception leaves each component it was rendering. The stack is kept on the exception and mirrored into one PEP 678 note, which is rewritten as frames are added so the printed traceback always shows the full path. Built-in containers (Column, Text) aren't components and don't appear.

Parameters:

Name Type Description Default
exc BaseException

The propagating exception.

required
component Any required

complete_component_stack

complete_component_stack(
    exc: BaseException, components: Sequence[Any]
) -> None

Append components (innermost first) and stop collecting.

Used when an update or effect fails in the middle of the tree: the reconciler knows the remaining ancestors from the mounted nodes, so the stack is completed at once and later frames are ignored.

component_stack

component_stack(
    exc: BaseException,
) -> Tuple[ComponentFrame, ...]

The component stack recorded on exc (innermost first), or ().

project_path

project_path(filename: str) -> str

filename relative to the project (app/main.py) when it's app code.

Device builds load the app from their bundle or the dev client's overlay; both hold an app/ package, so the project-relative path is the same everywhere and the dev server can map it back to a file on the developer's machine. Anything else is returned unchanged.

report

report(
    exc: BaseException, phase: str = "runtime"
) -> ErrorReport

Build an ErrorReport for exc.

Parameters:

Name Type Description Default
exc BaseException

The exception, with its traceback.

required
phase str

Where it happened ("render", "effect", ...).

'runtime'

warning_report

warning_report(
    message: str, component: Any = None
) -> ErrorReport

Build a warning report, attributed to the component rendering now, if any.

Parameters:

Name Type Description Default
message str

The warning text.

required
component Any

The rendering component, or its name.

None