Skip to main content

Verified line-addressed file editor using lnhash addresses

Project description

exhash — Verified Line-Addressed File Editor

exhash combines Can Bölük's very clever line number + hash editing system with the powerful and expressive syntax of the classic ex editor.

Install via pip to get both a convenient Python API, and native CLI binaries:

pip install exhash

Or install just the CLI binaries via cargo:

cargo install exhash

lnhash format

We refer to an lnhash as a tag of the form lineno|hash|, where hash is the lower 16 bits of Rust's DefaultHasher over the line content.

Address forms:

  • lineno|hash| — hash-verified address
  • $ — last line (no hash)
  • % — whole file (1,$, no hashes)

CLI

The native Rust binaries are installed into your PATH via pip.

View

# Shows every line prefixed with its lnhash
lnhashview path/to/file.txt
# Optional line number range to show
lnhashview path/to/file.txt 10 20

Edit

# Substitute on one line
exhash file.txt '12|abcd|s/foo/bar/g'

# Transliterate characters on one line
exhash file.txt '12|abcd|y/abc/ABC/'

# Append multiline text (terminated by a single dot)
exhash file.txt '12|abcd|a' <<'EOF'
new line 1
new line 2
.
EOF

# Dry-run
exhash --dry-run file.txt '12|abcd|d'

# Set shift width for < and >
exhash --sw 2 file.txt '12|abcd|>1'

# Last line and whole file shorthands (no hash)
exhash file.txt '$d'
exhash file.txt '%j'

# Move a line to EOF using $ as the destination
exhash file.txt '12|abcd|m$'

Substitute uses Rust regex syntax:

  • Pattern syntax is from regex
  • Replacement syntax is from regex::Replacer, e.g. $1, $0, ${name}
  • \/ escapes the command delimiter in pattern/replacement
  • Custom delimiters: s, y, g, g!, and v all accept any non-alphanumeric char as delimiter instead of /, e.g. s@pat@rep@, g@pat@cmd. Each command in a combo picks its own delimiter independently: g@a/b@s/old/new/
  • Literal newlines in pattern/replacement are supported (joins/splits lines as needed)
  • Transliteration uses y/src/dst/ and requires source/destination to have equal character counts

When passing multiple commands, each command's lnhashes are verified immediately before that command runs.

For a/i/c commands, provide the text block on stdin:

printf "new line 1\nnew line 2\n.\n" | exhash file.txt "2|beef|a"

Stdin filter mode

cat file.txt | exhash --stdin - '1|abcd|s/foo/bar/'

In --stdin mode, multiline a/i/c text blocks are not available.

Python API

from exhash import exhash, exhash_file, lnhash, lnhashview, lnhashview_file, line_hash

Viewing

text = "foo\nbar\n"
view = lnhashview(text)       # ["1|a1b2|  foo", "2|c3d4|  bar"]
view = lnhashview_file("f.py") # same but reads from file

Editing

exhash(text, cmds, sw=4) takes the text and a required iterable of command strings (use [] for no-op). sw controls how far < and > shift. For a/i/c commands, lines after the command are the text block (no . terminator needed):

addr = lnhash(1, "foo")  # "1|a1b2|"
res = exhash(text, [f"{addr}s/foo/baz/"])
print(res["lines"])    # ["baz", "bar"]
print(res["modified"]) # [1]

# Multiple commands
a1, a2 = lnhash(1, "foo"), lnhash(2, "bar")
res = exhash(text, [f"{a1}s/foo/FOO/", f"{a2}s/bar/BAR/"])

# Hashes are checked just-in-time per command.
# If earlier commands change/shift a later target line, recompute lnhash first.

# Append multiline text (no dot terminator)
res = exhash(text, [f"{addr}a\nnew line 1\nnew line 2"])

# Change shift width for < and >
res = exhash(text, [f"{addr}>1"], sw=2)

# Custom delimiters (useful when pattern/replacement contains /)
res = exhash(text, [f"{addr}s|foo|bar|"])

# Literal newlines in pattern/replacement (joins/splits lines)
a1, a2 = lnhash(1, "foo"), lnhash(2, "bar")
res = exhash("foo\nbar\n", [f"{a1},{a2}s/foo\nbar/replaced/"])

File helpers

exhash_file and lnhashview_file read directly from a file path:

view = lnhashview_file("file.py")

# Returns EditResult, file unchanged
res = exhash_file("file.py", [f"{addr}s/foo/bar/"])

# With inplace=True, writes back on success and returns diff string
diff = exhash_file("file.py", [f"{addr}s/foo/bar/"], inplace=True)

EditResult

exhash() returns an EditResult with attributes (also accessible via res["key"]):

  • lines — list of output lines
  • hashes — lnhash for each output line
  • modified — 1-based line numbers of modified/added lines
  • deleted — 1-based line numbers of removed lines (in original)
  • origins — for each output line, the 1-based original line number (None if inserted)

res.format_diff(context=1) returns a unified-diff-style summary showing only changed lines with context:

res = exhash(text, [f"{addr}s/foo/baz/"])
print(res.format_diff())
# -1|a1b2|  foo
# +1|c3d4|  baz
#  2|e5f6|  bar

Tests

cargo test && pytest -q

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

exhash-0.3.0.tar.gz (30.4 kB view details)

Uploaded Source

Built Distributions

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

exhash-0.3.0-cp313-cp313-manylinux_2_34_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.34+ x86-64

exhash-0.3.0-cp313-cp313-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

exhash-0.3.0-cp312-cp312-manylinux_2_34_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

exhash-0.3.0-cp312-cp312-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

exhash-0.3.0-cp311-cp311-manylinux_2_34_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.34+ x86-64

exhash-0.3.0-cp311-cp311-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

exhash-0.3.0-cp310-cp310-manylinux_2_34_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.34+ x86-64

exhash-0.3.0-cp310-cp310-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file exhash-0.3.0.tar.gz.

File metadata

  • Download URL: exhash-0.3.0.tar.gz
  • Upload date:
  • Size: 30.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for exhash-0.3.0.tar.gz
Algorithm Hash digest
SHA256 ba0b20522d236195a4280a1b2fa0c621f37dcf60347a61b178880574aeca5df1
MD5 1c245fc2cfff6f4783484415ceffbe7a
BLAKE2b-256 9098c9b99d9371a05a16b81338d3f1fe09f9cf0b164224ed727f3ea004d3af43

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0.tar.gz:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp313-cp313-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c7001002fa14951dd9e6f8290305f8411ff4ebf7a0b804e9219032d0a46ea852
MD5 87f3655dd417ba8b85381b4e839aac78
BLAKE2b-256 3127b9cfbc18d6b624ae1ced871a1682060f6243cdee8c65095f4819c4d4bb9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp313-cp313-manylinux_2_34_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 01e83a219dede6d7a3ae5f75e9077ea00de84a74efe4faa220d8d1b60795b0e1
MD5 d12a5cc8b8375d3561233deb1db4031e
BLAKE2b-256 1550d759f0bd1aa1aa4fb7a9442cce9862c64a9be61b2f7e964031d3ade13f78

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 9af3d12bf738b45bc73b513420ae1f4e0efe007167ff8e91a39e48fc814e3f28
MD5 3521d22b347d3f4fef928e8a79fb9198
BLAKE2b-256 339f6b55de4ff8cc7408f8c5fb2cc1a93ec5d7650807e362fff106b9e552c2dc

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp312-cp312-manylinux_2_34_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3b10e51d06e9cd6e9ad1ce0e71072983d7894766f76550e7d8982962ef8ef698
MD5 b776c794ef140c5bbc19db8ec8808362
BLAKE2b-256 738ba953a111977cdc680b8d8683394684996850e714b972225e01ea22888138

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp311-cp311-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c898b1e59f37158704bafce31e618abec33e2da2b98a37eff499f0ce766accf4
MD5 4cc5f2dd12b86f367f40facd77835db9
BLAKE2b-256 d9e5b2dfa209eeaaf884f449a7f07ff93ee8e2d85efc74aba67fcb7bf9244864

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp311-cp311-manylinux_2_34_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 06a8ecb76e5d002c79a780b806caffcd1096892a1687720fa9e69d05f547a73d
MD5 05f4204098b2b0d986bcedbd3196d5f4
BLAKE2b-256 bfa7ca905dcd40dd5d2dc3add335210375a9e8a2aeb591b9258be2da6518caf8

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp310-cp310-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 5c6f893bdac4e828385f4e9beea52cbc97f4b48bda93092006b8b29c6523e315
MD5 839809dd09b587dfac8fcfc184644cf7
BLAKE2b-256 c96ac8c6fcf12c010c49d55e889d05c8bc38269d7a798efc3469201c1be874f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp310-cp310-manylinux_2_34_x86_64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exhash-0.3.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d3033278ca994a3a62393f71b9146fc2ffb4a8086e7210e4ff65f5870232e309
MD5 70b6b11a362535f0f3a09da9ab7f47f9
BLAKE2b-256 cf2c5f9bc9427d57e1cfecd584460af450fa8e16abfd6e4efbb862243eea79e2

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.0-cp310-cp310-macosx_11_0_arm64.whl:

Publisher: ci.yml on AnswerDotAI/exhash

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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