Python’s binhex module (deprecated Python 3.11, removed Python 3.13) encodes and decodes files in the BinHex 4.0 format — a text-safe binary encoding that combines a Macintosh file’s data and resource forks with CRC checksums. import binhex. Encode: binhex.binhex(input_file, output_file) — read input_file path, write .hqx to output_file path. Decode: binhex.hexbin(input_file, output_file) — read .hqx from input_file, write data fork to output_file. Low-level encoder: binhex.BinHex(info_tuple, output_file) → object with .write(data) and .close(); info_tuple = (filename, creator, type, dlen, rlen). Low-level decoder: binhex.HexBin(input_file) → object with .read(n), .read_data(), .read_rsrc(), .close(); .FInfo has .Creator, .Type, .Flags. Exception: binhex.Error — raised on CRC mismatch, truncated data, or unsupported header. BinHex 4.0 encodes binary using a 6-bit alphabet (similar to base64) wrapped in : delimiters and applies run-length compression before encoding. Claude Code generates legacy Mac file archive tools, BinHex stream validators, and binary-to-text encoding pipeline handlers.
CLAUDE.md for binhex
## binhex Stack
- Stdlib: import binhex (3.11 deprecated, removed 3.13)
- Encode: binhex.binhex("input.bin", "output.hqx")
- Decode: binhex.hexbin("input.hqx", "output.bin")
- Error: binhex.Error (CRC fail / bad format)
- Low-level encode:
- hbx = binhex.BinHex(("filename", "APPL", "TEXT", data_len, 0), outf)
- hbx.write(data); hbx.close()
- Low-level decode:
- hbx = binhex.HexBin(infile)
- data = hbx.read_data(); hbx.close()
- Note: BinHex 4 = RLE compress → 6-bit encode (like base64) → wrap in ':'
binhex BinHex 4.0 Pipeline
# app/binhexutil.py — encode, decode, roundtrip, inspect, batch, stream validator
from __future__ import annotations
import io
import os
import struct
import tempfile
from dataclasses import dataclass
from pathlib import Path
# Graceful import — removed in Python 3.13
_BINHEX_AVAILABLE = False
try:
import binhex as _bh
_BINHEX_AVAILABLE = True
except ImportError:
pass
# ─────────────────────────────────────────────────────────────────────────────
# 1. High-level encode / decode helpers
# ─────────────────────────────────────────────────────────────────────────────
def encode_file(src: "str | Path", dst: "str | Path | None" = None) -> Path:
"""
BinHex-encode src to dst (default: src + '.hqx').
Returns the output path.
Example:
out = encode_file("document.rtf") # → "document.rtf.hqx"
out = encode_file("image.png", "/tmp/img.hqx")
"""
if not _BINHEX_AVAILABLE:
raise RuntimeError("binhex module not available (Python >= 3.13)")
src = Path(src)
dst = Path(dst) if dst else src.with_suffix(src.suffix + ".hqx")
_bh.binhex(str(src), str(dst))
return dst
def decode_file(src: "str | Path", dst: "str | Path | None" = None) -> Path:
"""
Decode a BinHex .hqx file to dst (default: strip .hqx extension).
Returns the output path.
Example:
out = decode_file("document.rtf.hqx") # → "document.rtf"
out = decode_file("archive.hqx", "/tmp/out.bin")
"""
if not _BINHEX_AVAILABLE:
raise RuntimeError("binhex module not available (Python >= 3.13)")
src = Path(src)
if dst is None:
dst = src.with_suffix("") if src.suffix == ".hqx" else src.with_suffix(".bin")
else:
dst = Path(dst)
_bh.hexbin(str(src), str(dst))
return dst
def encode_bytes(data: bytes,
filename: str = "data.bin",
file_type: str = " ",
creator: str = " ") -> bytes:
"""
BinHex-encode raw bytes in memory. Returns the .hqx bytes.
Example:
hqx = encode_bytes(b"Hello, world!", "hello.txt", "TEXT", "ttxt")
print(hqx.decode("ascii")[:80])
"""
if not _BINHEX_AVAILABLE:
raise RuntimeError("binhex module not available (Python >= 3.13)")
buf = io.BytesIO()
info = (filename, creator[:4], file_type[:4], len(data), 0)
enc = _bh.BinHex(info, buf)
enc.write(data)
enc.close()
return buf.getvalue()
def decode_bytes(hqx: bytes) -> bytes:
"""
Decode BinHex-encoded bytes and return the data fork as bytes.
Example:
data = decode_bytes(encode_bytes(b"Hello!"))
assert data == b"Hello!"
"""
if not _BINHEX_AVAILABLE:
raise RuntimeError("binhex module not available (Python >= 3.13)")
buf = io.BytesIO(hqx)
dec = _bh.HexBin(buf)
data = dec.read_data()
dec.close()
return data
# ─────────────────────────────────────────────────────────────────────────────
# 2. Roundtrip check
# ─────────────────────────────────────────────────────────────────────────────
def roundtrip_check(data: bytes) -> bool:
"""
Encode and immediately decode data; return True if output matches input.
Example:
assert roundtrip_check(b"Claude Skills 360")
assert roundtrip_check(bytes(range(256)))
"""
if not _BINHEX_AVAILABLE:
return True # assume ok if module missing
try:
hqx = encode_bytes(data)
recovered = decode_bytes(hqx)
return recovered == data
except Exception:
return False
# ─────────────────────────────────────────────────────────────────────────────
# 3. BinHex header inspector
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class HqxHeader:
filename: str
file_type: str
creator: str
data_len: int
rsrc_len: int
ok: bool
error: str = ""
def inspect_hqx(hqx: "bytes | str | Path") -> HqxHeader:
"""
Parse and return the header info from a BinHex stream without fully
decoding it. Returns HqxHeader with ok=False and error set on failure.
Example:
header = inspect_hqx("archive.hqx")
print(header.filename, header.file_type, header.data_len)
"""
if not _BINHEX_AVAILABLE:
return HqxHeader("", "", "", 0, 0, False, "binhex module not available")
try:
if isinstance(hqx, (str, Path)):
buf: io.IOBase = open(str(hqx), "rb")
else:
buf = io.BytesIO(hqx)
dec = _bh.HexBin(buf)
fi = dec.FInfo
name = getattr(dec, "FName", "")
data_len = getattr(dec, "FDataLen", 0)
rsrc_len = getattr(dec, "FRsrcLen", 0)
dec.close()
return HqxHeader(
filename=name,
file_type=fi.Type,
creator=fi.Creator,
data_len=data_len,
rsrc_len=rsrc_len,
ok=True,
)
except Exception as e:
return HqxHeader("", "", "", 0, 0, False, str(e))
# ─────────────────────────────────────────────────────────────────────────────
# 4. Batch encoder
# ─────────────────────────────────────────────────────────────────────────────
def encode_directory(src_dir: "str | Path",
dst_dir: "str | Path | None" = None,
pattern: str = "*") -> list[tuple[Path, Path]]:
"""
BinHex-encode all matching files in src_dir, writing .hqx to dst_dir.
Returns list of (src, dst) pairs for successfully encoded files.
Example:
pairs = encode_directory("mac_files/", "hqx_out/", pattern="*.bin")
for src, dst in pairs:
print(f" {src.name} → {dst.name}")
"""
src_dir = Path(src_dir)
dst_dir = Path(dst_dir) if dst_dir else src_dir / "hqx"
dst_dir.mkdir(parents=True, exist_ok=True)
results: list[tuple[Path, Path]] = []
for src in src_dir.glob(pattern):
if not src.is_file():
continue
dst = dst_dir / (src.name + ".hqx")
try:
encode_file(src, dst)
results.append((src, dst))
except Exception:
pass
return results
# ─────────────────────────────────────────────────────────────────────────────
# 5. BinHex stream validator
# ─────────────────────────────────────────────────────────────────────────────
def validate_hqx_stream(hqx: bytes) -> tuple[bool, str]:
"""
Validate a BinHex stream by attempting a full decode. Returns
(True, "") on success or (False, error_message) on failure.
Example:
ok, msg = validate_hqx_stream(encode_bytes(b"test"))
assert ok
ok, msg = validate_hqx_stream(b"(corrupted)")
assert not ok
"""
if not _BINHEX_AVAILABLE:
return True, ""
try:
buf = io.BytesIO(hqx)
dec = _bh.HexBin(buf)
dec.read_data()
dec.read_rsrc()
dec.close()
return True, ""
except _bh.Error as e:
return False, str(e)
except Exception as e:
return False, str(e)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== binhex demo ===")
print(f" binhex available: {_BINHEX_AVAILABLE}")
if not _BINHEX_AVAILABLE:
print(" (Python >= 3.13: binhex was removed)")
print(" Use base64 module for binary-to-text encoding instead.")
else:
# ── encode_bytes / decode_bytes ───────────────────────────────────────
print("\n--- encode_bytes / decode_bytes ---")
test_data = b"Claude Skills 360 — BinHex test payload\x00\x01\x02\xff"
hqx = encode_bytes(test_data, "test.bin", "TEXT", "CLAS")
print(f" original : {len(test_data)} bytes")
print(f" encoded : {len(hqx)} bytes ({len(hqx)/len(test_data):.1f}x)")
decoded = decode_bytes(hqx)
print(f" decoded : {len(decoded)} bytes")
print(f" match : {decoded == test_data}")
print(f" hqx preview: {hqx[:80].decode('ascii', errors='replace')!r}")
# ── roundtrip_check ───────────────────────────────────────────────────
print("\n--- roundtrip_check ---")
for payload in [b"", b"x", bytes(range(256)), b"A" * 1024]:
ok = roundtrip_check(payload)
print(f" {len(payload):5d} bytes: {'OK' if ok else 'FAIL'}")
# ── inspect header ────────────────────────────────────────────────────
print("\n--- inspect_hqx header ---")
hdr = inspect_hqx(encode_bytes(b"hello", "readme.txt", "TEXT", "MSWD"))
print(f" filename = {hdr.filename!r}")
print(f" file_type = {hdr.file_type!r}")
print(f" creator = {hdr.creator!r}")
print(f" data_len = {hdr.data_len}")
print(f" ok = {hdr.ok}")
# ── validate_hqx_stream ───────────────────────────────────────────────
print("\n--- validate_hqx_stream ---")
good_hqx = encode_bytes(b"good data", "g.bin")
ok, msg = validate_hqx_stream(good_hqx)
print(f" good stream: ok={ok} msg={msg!r}")
ok, msg = validate_hqx_stream(b"(this is not valid binhex)")
print(f" bad stream: ok={ok} msg={msg!r}")
# ── file-based encode / decode ────────────────────────────────────────
print("\n--- file encode/decode ---")
with tempfile.TemporaryDirectory() as tmpdir:
src = os.path.join(tmpdir, "payload.bin")
hqx_path = os.path.join(tmpdir, "payload.bin.hqx")
out = os.path.join(tmpdir, "recovered.bin")
original = bytes(range(128)) * 4
open(src, "wb").write(original)
encode_file(src, hqx_path)
decode_file(hqx_path, out)
recovered = open(out, "rb").read()
print(f" encoded size : {os.path.getsize(hqx_path)} bytes")
print(f" match : {recovered == original}")
print("\n=== done ===")
For the base64 stdlib replacement — base64.b64encode(data) and base64.b64decode(s) provide the modern, cross-platform binary-to-text encoding used in MIME email, JSON, and HTTP — use base64 for all new code; BinHex 4.0 was a Macintosh-specific format from the 1980s–90s and the binhex module was removed in Python 3.13. For the binascii stdlib alternative — binascii.b2a_hqx(data) and binascii.a2b_hqx(data) expose the raw BinHex 4.0 6-bit codec at the per-chunk level used internally by binhex — use binascii if you need low-level per-block HQX encoding/decoding without the full file envelope (headers, run-length compression, CRC); use binhex for complete .hqx file compliance when working with legacy Macintosh archives on Python ≤ 3.11. The Claude Skills 360 bundle includes binhex skill sets covering encode_file()/decode_file() path helpers, encode_bytes()/decode_bytes() in-memory codecs, roundtrip_check() verifier, inspect_hqx()/HqxHeader dataclass header parser, encode_directory() batch encoder, and validate_hqx_stream() integrity checker. Start with the free tier to try BinHex encoding patterns and binhex pipeline code generation.