Skip to main content

CLI exit handling helpers: clean signals, exit codes, and error printing

Project description

lib_cli_exit_tools

CI CodeQL License: MIT Jupyter PyPI PyPI - Downloads Code Style: Ruff codecov Maintainability Known Vulnerabilities

Small helpers for robust CLI exit handling:

  • Portable signal handling (SIGINT, SIGTERM/SIGBREAK)
  • Consistent exception → exit code mapping
  • Concise error printing with optional traceback and subprocess stdout/stderr capture

Install

Pick one of the options below. All methods register the lib_cli_exit_tools and cli-exit-tools commands on your PATH.

1) Standard virtualenv (pip)

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\\Scripts\\activate
pip install -e .[dev]       # dev install
# or for runtime only:
pip install .

2) Per-user (no venv)

pip install --user .

Note: respects PEP 668; avoid on system Python if “externally managed”. Ensure ~/.local/bin (POSIX) is on PATH.

3) pipx (isolated, recommended for end users)

pipx install .
pipx upgrade lib_cli_exit_tools
# From Git tag/commit:
pipx install "git+https://github.com/bitranox/lib_cli_exit_tools@v0.1.0"

4) uv (fast installer/runner)

uv pip install -e .[dev]
uv tool install .
uvx lib_cli_exit_tools --help

5) From artifacts

python -m build
pip install dist/lib_cli_exit_tools-*.whl
pip install dist/lib_cli_exit_tools-*.tar.gz   # sdist

6) Poetry / PDM (project-managed envs)

# Poetry
poetry add lib_cli_exit_tools     # as dependency
poetry install                    # for local dev

# PDM
pdm add lib_cli_exit_tools
pdm install

7) From Git via pip (CI-friendly)

pip install "git+https://github.com/bitranox/lib_cli_exit_tools@v0.1.0#egg=lib_cli_exit_tools"

8) Conda/mamba (optional)

mamba create -n cli-exit python=3.12 pip
mamba activate cli-exit
pip install .

9) System package managers (optional distribution)

  • Homebrew formula (macOS): brew install lib_cli_exit_tools (if published)
  • Nix: flake/package for reproducible installs
  • Deb/RPM via fpm for OS-native packages

Make Targets

Target Description
help Show help
install Install package editable
dev Install package with dev extras
test Lint, type-check, run tests with coverage, upload to Codecov
run Run module CLI (requires dev install or src on PYTHONPATH)
version-current Print current version from pyproject.toml
bump Bump version (updates pyproject.toml and CHANGELOG.md)
bump-patch Bump patch version (X.Y.Z -> X.Y.(Z+1))
bump-minor Bump minor version (X.Y.Z -> X.(Y+1).0)
bump-major Bump major version ((X+1).0.0)
clean Remove caches, build artifacts, and coverage
push Commit all changes once and push to GitHub (no CI monitoring)
build Build wheel/sdist and attempt conda, brew, and nix builds (auto-installs tools if missing)
menu Interactive TUI to run targets and edit parameters (requires dev dep: textual)

Target Parameters (env vars)

  • Global

    • PY (default: python3) — Python interpreter used to run scripts
    • PIP (default: pip) — pip executable used by bootstrap/install
  • install

    • No specific parameters (respects PY, PIP).
  • dev

    • No specific parameters (respects PY, PIP).
  • test

    • COVERAGE=on|auto|off (default: on) — controls pytest coverage run and Codecov upload
    • SKIP_BOOTSTRAP=1 — skip auto-install of dev tools if missing
    • TEST_VERBOSE=1 — echo each command executed by the test harness
    • Also respects CODECOV_TOKEN when needed for uploads
  • run

    • No parameters via make (always shows --help). For custom args: python scripts/run_cli.py -- <args>.
  • version-current

    • No parameters
  • bump

    • VERSION=X.Y.Z — explicit target version
    • PART=major|minor|patch — semantic part to bump (default if VERSION not set: patch)
    • Examples:
      • make bump VERSION=1.0.2
      • make bump PART=minor
  • bump-patch / bump-minor / bump-major

    • No parameters; shorthand for make bump PART=...
  • clean

    • No parameters
  • push

    • REMOTE=<name> (default: origin) — git remote to push to
  • build

    • No parameters via make. Advanced: use the script directly, e.g. python scripts/build.py --no-conda --no-nix.
  • release

    • REMOTE=<name> (default: origin) — git remote to push to
    • Advanced (via script): python scripts/release.py --retries 5 --retry-wait 3.0

Interactive Menu (Textual)

make menu launches a colorful terminal UI (powered by textual) to browse targets, edit parameters, and run them with live output.

Install dev extras if you haven’t:

pip install -e .[dev]

Run the menu:

make menu

Target Details

  • test: single entry point for local CI — runs ruff lint + format check, pyright, pytest (including doctests) with coverage (enabled by default), and uploads coverage to Codecov if configured (reads .env).
  • Auto‑bootstrap: make test will try to install dev tools (pip install -e .[dev]) if ruff/pyright/pytest are missing. Set SKIP_BOOTSTRAP=1 to skip this behavior.
  • build: convenient builder — creates Python wheel/sdist, then attempts Conda, Homebrew, and Nix builds. It auto‑installs missing tools (Miniforge, Homebrew, Nix) when needed.
  • install/dev/user-install: common install flows for editable or per‑user installs.
  • version-current: prints current version from pyproject.toml.
  • bump: updates pyproject.toml version and inserts a new section in CHANGELOG.md. Use VERSION=X.Y.Z make bump or make bump-minor/bump-major/bump-patch.
  • pipx-* and uv-*: isolated CLI installations for end users and fast developer tooling.
  • which-cmd/verify-install: quick diagnostics to ensure the command is on PATH.

Usage

Console script:

# After install (pip/pipx/uv tool)
lib_cli_exit_tools --help
cli-exit-tools --help  # alias
lib_cli_exit_tools info

Library:

import lib_cli_exit_tools

lib_cli_exit_tools.config.traceback = False  # show short messages
try:
    raise FileNotFoundError("missing.txt")
except Exception as e:
    code = lib_cli_exit_tools.get_system_exit_code(e)   # 2 on POSIX
    lib_cli_exit_tools.print_exception_message()        # prints: FileNotFoundError: missing.txt
    raise SystemExit(code)

Command names registered on install

  • lib_cli_exit_tools (default)
  • cli-exit-tools (alias)
  • python -m lib_cli_exit_tools (module entry)

If you installed with --user or in a venv, make sure the corresponding bin directory is on PATH:

  • Linux/macOS venv: .venv/bin
  • Linux/macOS user: ~/.local/bin
  • Windows venv: .venv\Scripts
  • Windows user: %APPDATA%\Python\PythonXY\Scripts

Exit Codes

  • SIGINT → 130, SIGTERM → 143 (POSIX), SIGBREAK → 149 (Windows)
  • SystemExit(n) → n
  • Common exceptions map to POSIX/Windows codes (FileNotFoundError, PermissionError, ValueError, etc.)

Broken pipe behavior

  • Default: exit 141 quietly (128+SIGPIPE), no noisy error output.
  • Configure: config.broken_pipe_exit_code = 0 to treat as benign truncation, or 32 (EPIPE).

Sysexits mode (optional)

  • Set config.exit_code_style = "sysexits" to map ValueError/TypeError → EX_USAGE(64), FileNotFoundError → EX_NOINPUT(66), PermissionError → EX_NOPERM(77), generic OSError → EX_IOERR(74).

Development

make test                 # ruff + pyright + pytest + coverage (default ON)
SKIP_BOOTSTRAP=1 make test  # skip auto-install of dev deps
COVERAGE=off make test       # disable coverage locally
COVERAGE=on make test        # force coverage and generate coverage.xml/codecov.xml

Packaging sync (Conda/Brew/Nix)

  • make test and make push automatically align the packaging skeletons in packaging/ with the current pyproject.toml:

    • Conda: updates {% set version = "X.Y.Z" %} and both python >=X.Y constraints to match requires-python.
    • Homebrew: updates the source URL tag to vX.Y.Z and sets depends_on "python@X.Y" to match requires-python.
    • Nix: updates the package version, example rev = "vX.Y.Z", and switches pkgs.pythonXYZPackages / pkgs.pythonXYZ to match the minimum Python version from requires-python.
  • To run just the sync without bumping versions: python scripts/bump_version.py --sync-packaging.

  • On release tags (v*.*.*), CI validates that packaging files are consistent with pyproject.toml and will fail if they drift.

Versioning & Metadata

  • Single source of truth for package metadata is pyproject.toml ([project]).
  • The library reads its own installed metadata at runtime via importlib.metadata (see src/lib_cli_exit_tools/__init__conf__.py).
  • Do not duplicate the version in code; bump only pyproject.toml and update CHANGELOG.md.
  • Console script name is discovered from entry points; defaults to lib_cli_exit_tools.

Packaging Skeletons

Starter files for package managers live under packaging/:

  • Conda: packaging/conda/recipe/meta.yaml (update version + sha256)
  • Homebrew: packaging/brew/Formula/lib-cli-exit-tools.rb (fill sha256 and vendored resources)
  • Nix: packaging/nix/flake.nix (use working tree or pin to GitHub rev with sha256)

These are templates; fill placeholders (e.g., sha256) before publishing. Version and Python constraints are auto-synced from pyproject.toml by make test/make push and during version bumps.

CI & Publishing

GitHub Actions workflows are included:

  • .github/workflows/ci.yml — lint/type/test, build wheel/sdist, verify pipx and uv installs, Nix and Conda builds (CI-only; no local install required).
  • .github/workflows/release.yml — on tags v*.*.*, builds artifacts and publishes to PyPI when PYPI_API_TOKEN secret is set.

To publish a release:

  1. Bump pyproject.toml version and update CHANGELOG.md.
  2. Tag the commit (git tag v0.1.1 && git push --tags).
  3. Ensure PYPI_API_TOKEN secret is configured in the repo.
  4. Release workflow uploads wheel/sdist to PyPI.

Conda/Homebrew/Nix: use files in packaging/ to submit to their ecosystems. CI also attempts builds to validate recipes, but does not publish automatically.

Local Codecov uploads

  • make test (with coverage enabled) generates coverage.xml and codecov.xml, then attempts to upload via the Codecov CLI or the bash uploader.
  • For private repos, set CODECOV_TOKEN (see .env.example) or export it in your shell.
  • For public repos, a token is typically not required.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lib_cli_exit_tools-1.0.3.tar.gz (42.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lib_cli_exit_tools-1.0.3-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file lib_cli_exit_tools-1.0.3.tar.gz.

File metadata

  • Download URL: lib_cli_exit_tools-1.0.3.tar.gz
  • Upload date:
  • Size: 42.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for lib_cli_exit_tools-1.0.3.tar.gz
Algorithm Hash digest
SHA256 12d49d9eb8fba8b92a7ba7f8c44a1047e8af116737d92524670252dbd2534724
MD5 7d9f8ac0dd67c311346d60ea2163635b
BLAKE2b-256 d45a9f87e9cd309f0120de0692acee3ecb19bd4d2753c1fa27af61103bb30db7

See more details on using hashes here.

File details

Details for the file lib_cli_exit_tools-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for lib_cli_exit_tools-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 78918aa618c7a1197d436374607f7370606fbaf05452d6eaae8d2e26dbcdcbb5
MD5 b5b20c008243a678ab90ac5d93f7b243
BLAKE2b-256 55fa1902f693b7d13d69c5203676546ba7e2450135e3a7074f15cead3fd5b17a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page