Python bindings for yescrypt, implemented as a small Go shared library loaded via ctypes.
No CPython C extension code. The Python package is pure Python, plus a platform-specific shared library:
- macOS:
libyescrypt.dylib - Linux (glibc):
libyescrypt.so
Windows is currently unsupported because the Go-based build has only been validated on macOS and glibc-based Linux.
import pyyescrypt
hash_str = pyyescrypt.generate_hash("my password")
ok = pyyescrypt.verify_hash("my password", hash_str)generate_hash(password: str) -> strreturns a modular-crypt yescrypt hash string (typically starts with$y$).verify_hash(password: str, hash_str: str) -> boolverifies a password against a stored hash string.
Errors raised from the native layer become ValueError in Python.
Installing pyyescrypt also provides a pyyescrypt-cli executable (packaged Go binary). It exposes the same operations as the Python module:
# Generate a hash (reads the password from stdin)
printf "secret" | pyyescrypt-cli generate
# Verify returns exit status 0 on success and prints "1"
printf "secret" | pyyescrypt-cli verify --hash '$y$...'The Python package automatically falls back to this CLI whenever the shared library cannot be loaded (for example on musl-based systems), and you can force it via PYYESCRYPT_BACKEND=cli.
capi/Go code compiled as a C-shared library (-buildmode=c-shared)src/pyyescrypt/_native.pyloads the shared library withctypesand exposes the Python APIsrc/pyyescrypt/_native/destination directory for the built shared librarytests/Python tests (ABI-level tests viactypes)
- Go toolchain available on PATH
- Python 3.10+ required
- On macOS, Xcode Command Line Tools installed (
xcode-select --install)
make nativeThis builds the shared library into:
src/pyyescrypt/_native/libyescrypt.dylibon macOSsrc/pyyescrypt/_native/libyescrypt.soon Linux
To build the bundled CLI (used automatically on musl) run:
make cliwhich outputs src/pyyescrypt/_cli/pyyescrypt-cli.
make testThis will:
- run
go test -cover ./... - create a local virtualenv at
.venv - install test dependencies into the venv
- build the native library
- run
pytest
If you want only the Python tests:
make py-testCPython extension modules require a C ABI boundary anyway. This project avoids the CPython C-API completely by exporting a small C ABI from Go and calling it from Python using ctypes.
Native functions return char* allocated by the native library. Python must free them by calling yc_free. The Python wrapper handles this.
glibc-based distros (manylinux wheels) are fully supported today. Musl-based
systems such as Alpine cannot load the Go-built shared library because Go
currently emits initial-exec TLS relocations for -buildmode=c-shared, which
musl's dynamic loader refuses. Installing the wheel on Alpine succeeds, but the
native backend fails to import.
The package now ships a small Go CLI binary that the Python module falls back
to automatically whenever the native backend cannot be loaded (or when
PYYESCRYPT_BACKEND=cli is set). Set PYYESCRYPT_BACKEND=native if you prefer
to fail fast when the shared library is unavailable.
The wheel must include the shared library under pyyescrypt/_native/.
For local development, make native places the library in the repo tree where the tests expect it.
To validate packaging, use a separate workflow:
- build a wheel
- install it into a clean venv
- import
pyyescryptand run the tests against the installed package data
See LICENSE.