shfmt-py
raw JSON → 3.12.0.2 verified Fri May 01 auth: no python
A Python wrapper around shfmt, the shell script formatter. Downloads and manages the shfmt binary automatically. Current version 3.12.0.2, wraps mvdan/sh v3.12.0. Supports Python >=3.9. Releases follow upstream shfmt versions with a patch suffix.
pip install shfmt-py Common errors
error ModuleNotFoundError: No module named 'shfmt' ↓
cause Importing the package using the PyPI name (shfmt) instead of the Python module name (shfmt_py).
fix
Use 'from shfmt_py import shfmt' or 'import shfmt_py'.
error OSError: [Errno 8] Exec format error: '/path/to/shfmt' ↓
cause The downloaded shfmt binary is not compatible with the OS/architecture. The wrapper downloads a binary for the current platform, but if you manually set SHPATH to a binary for a different platform, this error occurs.
fix
Delete the cached binary and let shfmt-py re-download. Alternatively, set SHPATH to a valid binary for the current platform.
error shfmt_py.exceptions.ShhhFormatError: shfmt reported errors ↓
cause The provided shell code contains syntax errors that shfmt cannot parse. The error message from shfmt is included.
fix
Fix the shell syntax errors indicated in the error output.
Warnings
gotcha The shfmt binary is downloaded automatically on first use, which may fail in air-gapped environments. Cache the binary manually. ↓
fix Set environment variable SHPATH to a pre-downloaded binary path, or use the binary caching mechanism (see docs).
deprecated Passing a string to the 'lang' parameter without specifying 'filename' may produce incorrect formatting. In future versions, 'filename' will be required for proper language detection. ↓
fix Always provide a filename (e.g., 'script.sh') when calling shfmt() to ensure correct language detection.
gotcha The package version includes the upstream shfmt version plus a patch number (e.g., 3.12.0.2). This can cause confusion when comparing versions. Breaking changes in shfmt reflect in the first three numbers. ↓
fix Check upstream shfmt release notes for breaking changes. The fourth number is the wrapper patch.
Imports
- shfmt wrong
from shfmt import shfmtcorrectfrom shfmt_py import shfmt - ShfmtError wrong
from shfmt_py.shfmt import ShfmtErrorcorrectfrom shfmt_py import ShfmtError
Quickstart
from shfmt_py import shfmt
# Format a string
code = 'if [ $foo = bar ]; then echo hello ; fi'
formatted = shfmt(code, lang='bash', space_redirects=True)
print(formatted)
# Format a file
shfmt(code, write=True, filename='script.sh')
# Check for formatting differences (intended for CI)
import sys
from shfmt_py import ShfmtError
try:
shfmt(code, filename='script.sh') # raises ShfmtError if not formatted
except ShfmtError as e:
print(e.diff())
sys.exit(1)