Files

r2pipe for Python

Interact with radare2 using the #!pipe command or in standalone scripts that communicate with local or remote r2 via pipe, tcp or http.

Installation

$ pip install r2pipe

or

$ pip3 install r2pipe

Usage example:

import r2pipe

r2 = r2pipe.open("/bin/ls")
r2.cmd('aa')
print(r2.cmd("afl"))
print(r2.cmdj("aflj"))            # evaluates JSONs and returns an object
print(r2.cmdj("ij")["core"]["format"])  # shows file format
r2.quit()

Structured commands (r2pipe2)

cmd2() accepts boolean trim and json options. trim=True strips output whitespace; json=True asks radare2 to embed the command's JSON output in the response's res field. cmd2j() defaults both options to True, and explicit False values override those defaults.

with r2pipe.open("-", flags=["-N"]) as r2:
    print(r2.cmd2("?e hello", trim=True).res)  # "hello"
    print(r2.cmd2("ij", json=True).res.core.file)
    print(r2.cmd2j("?e hello", json=False, trim=False).res)  # "hello\n"

Both methods accept callback=..., which receives the raw response string before Python parses the JSON. Callbacks are handled in Python and are never included in the request JSON. Synchronous call(), cmd(), cmdj(), and cmdJ() also support callbacks. Unknown r2pipe2 keywords, non-boolean flags, and callbacks that are neither callable nor None raise TypeError before execution. call() accepts only the optional callback keyword.

Asyncio

Import the async client to await commands inside an event loop:

import asyncio
from r2pipe.open_async import open as async_open

async def main():
    async with async_open("/bin/ls", flags=["-N"]) as r2:
        reply = await r2.cmd2j("ij")
        print(reply.res.core.file)
        print(await r2.cmd("afl"))

asyncio.run(main())

cmd, cmdj, cmdJ, call, cmd2, and cmd2j return awaitables inside the client's event loop. Outside a running loop, the async client keeps its existing blocking behavior. A client created before starting the event loop can be used inside it; once commands have run, use that same loop for the rest of the session. Embedded #!pipe and r2lang commands retain their blocking behavior.

Native ccall:// sessions also support these awaitable methods. Each session runs core creation, commands, and cleanup on one dedicated worker thread so native calls leave the event loop responsive.

Use async with or await r2.close() / await r2.quit() to clean up. Closing keeps the caller's event loop open. Commands on one client run in sequence, including commands submitted with asyncio.gather. Canceling a command that has already started lets its response finish draining before the next command runs; the command may still change radare2 state. Closing waits for these started commands and rejects commands still waiting to run. Callbacks receive the raw response before JSON parsing, as with blocking calls.

Async HTTP/TCP connection failures, read errors, and response timeouts raise exceptions from the command call (or its awaitable). Success callbacks are not called for failed commands; errors are not returned as response strings.

IO address values

IO request addresses may be hexadecimal strings or legacy JSON integers. r2pipe.parse_address(value) validates and returns an exact unsigned 64-bit Python int. It rejects booleans, floats, invalid strings and out-of-range values; cmdj() otherwise preserves the usual JSON types.

r2pipe.parse_address("0xffffffffffffffff")  # 18446744073709551615
r2pipe.parse_address(9007199254740993)       # exact legacy integer

See examples/io-plugin.py for a complete memory-backed IO server accepting both representations.

Releasing

Update the version in pyproject.toml, then run:

make check  # build the source archive and wheel, then validate their metadata
make pub    # build, validate, and upload to PyPI

The first build creates .venv-release and installs the build and upload tools there. This requires Python 3.9 or newer with venv support and access to PyPI. Use make PYTHON3=python3.12 to choose the interpreter when creating the environment. Run make twine to update the release tools. BUILD and TWINE remain overridable.

For uploads, provide a PyPI API token through Twine's password prompt, TWINE_PASSWORD, or the [pypi] section in ~/.pypirc:

[pypi]
username = __token__
password = <your PyPI API token>