Skip to main content

bin-shim (Python)

Runtime shim for native CLIs exposed in-process as run_cli(argv) -> int.

The pattern: your Rust (or C, or Zig) CLI is compiled once as a library and exposed through Python bindings (e.g. pyo3) as a function that takes argv and returns an exit code. Your PyPI package's console script then needs to make the process behave exactly as if the binary had been run directly — exit codes, signals, stdio ordering. bin-shim owns those process semantics so your launcher reduces to one line.

The npm sibling package (same repo) covers the spawn pattern for Node; both implementations execute the same process-semantics conformance spec.

Install

uv add bin-shim

Quickstart

Your package ships a native bindings module (say yourpkg._native) exposing run_cli. Point a console script at a two-line entry:

# yourpkg/cli.py
import sys
from bin_shim import main

def entry() -> None:
    sys.exit(main("yourpkg._native"))
# pyproject.toml
[project.scripts]
yourcli = "yourpkg.cli:entry"

What main does

  1. Flushes host stdio, so the native side's direct fd-1/fd-2 writes can't land ahead of earlier buffered Python output.
  2. Imports the native module and looks up run_cli (helpful BinShimError if the wheel for this platform is missing).
  3. Calls run_cli(argv) (default sys.argv[1:]), passing argv through byte-faithfully — no shell interpretation.
  4. Translates the returned code into process semantics:
    • ordinary codes are returned for you to sys.exit with;
    • on POSIX, 130/143 (the core's graceful-shutdown codes for SIGINT/SIGTERM) restore the default handler and re-raise the signal, so the parent shell observes genuine signal death and job control works;
    • on Windows there is no re-raise — 130 exits plainly as 130;
    • a KeyboardInterrupt escaping the native call is treated as the SIGINT-shutdown path, and no traceback ever reaches the user.

API

main(module, argv=None, *, entry_point="run_cli", importer=None, run_cli=None, platform=None) -> int

Resolve and invoke the native entry point, apply exit semantics, and return the code to pass to sys.exit. For the POSIX signal-shutdown codes it does not return (the process dies by re-raised signal). importer and run_cli are injectable seams for testing; platform defaults to sys.platform.

resolve_run_cli(module, *, entry_point="run_cli", importer=None) -> RunCli

Import module and return its entry_point callable, or raise BinShimError with an installation hint.

apply_exit_semantics(code, *, platform=None) -> int

The translation step alone: passthrough for ordinary codes, signal re-raise for 130/143 on POSIX, plain passthrough on Windows.

Constants and types

  • SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143 (the 128 + N POSIX convention)
  • RunCli = Callable[[list[str]], int]
  • Importer = Callable[[str], ModuleType]
  • BinShimError — resolution failures and contract violations

The run_cli contract

What this library requires of the native side:

  1. run_cli(argv) -> int always returns; it never terminates the process.
  2. It registers its own SIGINT/SIGTERM (POSIX) / ctrl-c (Windows) handling for the duration of the run; on receipt it performs graceful shutdown and returns 130/143. Shutdown logic lives in the core, once — launchers only translate.
  3. It writes only to file descriptors 1/2; it never touches host-language stdio objects.

What bin-shim does not do

  • Build or package the native module. That's your build backend (maturin, setuptools-rust, …).
  • Argv middleware. Preprocess argv yourself and pass the result to main.
  • Spawn anything. This is the in-process strategy; nothing is executed as a child process.

License

MIT

Metadata

Release files for bin-shim 0.1.1

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

Source distribution (sdist)

Source distribution for bin-shim 0.1.1
File Size Uploaded Provenance
bin_shim-0.1.1.tar.gz 10.4 kB With provenance Details

Release files / bin_shim-0.1.1.tar.gz

Download URL bin_shim-0.1.1.tar.gz
Size 10.4 kB
Tags Source
SHA-256 checksum
How to use checksums
9972911c22754a5370f666245cbe5d9d9aa04d9f7289d166142c9c3c6ee3b8c3
BLAKE2b-256 checksum
How to use checksums
6afc2e09f2de12c0937834fc3f2bacde597f6e25499b0bf4b3d71292b7dbf822
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 20, 2026.

Transparency log

Security for bin-shim 0.1.1

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 1 file in this release has been uploaded with provenance metadata.

Attestation for bin_shim-0.1.1.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 Aug 20, 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.

Release history Release notifications | RSS feed

This release

0.1.1 This release

1 release file Full provenance

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