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
- Flushes host stdio, so the native side's direct fd-1/fd-2 writes can't land ahead of earlier buffered Python output.
- Imports the native module and looks up
run_cli(helpfulBinShimErrorif the wheel for this platform is missing). - Calls
run_cli(argv)(defaultsys.argv[1:]), passing argv through byte-faithfully — no shell interpretation. - Translates the returned code into process semantics:
- ordinary codes are returned for you to
sys.exitwith; - 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 —
130exits plainly as130; - a
KeyboardInterruptescaping the native call is treated as the SIGINT-shutdown path, and no traceback ever reaches the user.
- ordinary codes are returned for you to
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(the128 + NPOSIX 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:
run_cli(argv) -> intalways returns; it never terminates the process.- 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.
- 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)
| 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 logSecurity 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 logA 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.