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. Do not include an ex-style trailing . line here: unlike CLI/script mode, exhash(text, cmds) does not use one, and a final . line is inserted literally.

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"])

# Wrong for the Python API: the trailing "." would be inserted literally
# res = exhash(text, [f"{addr}a\nnew line 1\nnew line 2\n."])

# 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.1.tar.gz (31.1 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.1-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.1-cp313-cp313-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

exhash-0.3.1-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.1-cp312-cp312-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

exhash-0.3.1-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.1-cp311-cp311-macosx_11_0_arm64.whl (1.9 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

exhash-0.3.1-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.1-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.1.tar.gz.

File metadata

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

File hashes

Hashes for exhash-0.3.1.tar.gz
Algorithm Hash digest
SHA256 bc66c92aa5ade9f1d225799904db5b7efd3868a23a80d6b6b69cc48a4a7a8958
MD5 f8e4c93df206291bb914ef51a3db1bb6
BLAKE2b-256 45c029c9ecc425dbb1e6eec91b5f6e91122b4427be1ca6e6610568981af63c6e

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1.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.1-cp313-cp313-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 d13d0e5759eea637f9279f47a06f254178b79dce6cfe21b691c575a64426aacf
MD5 7df762720375dc326e040861dcb25562
BLAKE2b-256 00d038292cfda1fbb1f03e65751e64ccb42930ed755a9d0a2e62a4248401e5d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e8109eaf9c6b74f06718ff415ba0bee9e1b7c64e7d33fb4c87d29101ccb3a4c4
MD5 c376d6c9bc1dc5a99db81d5785342837
BLAKE2b-256 e75333734871f7274ed8954cde8076d65f6d5318a44932ba27c6cc4d9fe00399

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 74768d103cb2df506cd73847215b0e6dc4a8fade9378e9cb9cfd7a6573703bb9
MD5 6f19e06a349371dc945bf061b9b7d357
BLAKE2b-256 0246fb853fb3e8225be24f85ce4e17226b4b405a41400d0a2a50248fdc31e752

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 58aa2340367656e18f37e962eb60cdfe21aa86973a251c2857de7ab54b434393
MD5 fcf9b48236e14a194b388c6db59d402d
BLAKE2b-256 a51627e7ddf1becca5c55bb9f1f78489077a96dd42ab099a2af5270aca6a8b20

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp311-cp311-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 0c3ce1913040a39975388b1d953027df618fb9e2a4ded83b3c864c52e43331ec
MD5 d4a65839ce7296b9ae64d5b38777c313
BLAKE2b-256 d9d87836e07dd02c2c32493f014437fd268d7a7b1c413912dea1ca56befe3fd6

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 081fd83983bfca203d6ef37f16006db969a9b4ec9c7e9028257b26fffdc7526b
MD5 a93ef2292155797d5a5746dee6a85b32
BLAKE2b-256 6fba1c89b0eb9613b8dffc5529f682ed7479ad2e936886b07657ca83fafc5085

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp310-cp310-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 a08790b3723f9e21c627e71e72670b2b5943b50564448d22ddbabdcc1792b4ee
MD5 cd5df8928cbb8459aba58ff9410da3fe
BLAKE2b-256 ed7e0f306c83084fdab479084b7d4be93eb87e6b763fb966866b4b027a194109

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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.1-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for exhash-0.3.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 79eda0c87d1c997bf2e696a4ec9f8ebefb0edd9b3dff5930ba10f7723354cd3e
MD5 4adebd33a67098af5fff6dbde8adcd30
BLAKE2b-256 77d15203dbb9f29c48e0b4c042dc4281aad5252589150a3dca9e9399a3124859

See more details on using hashes here.

Provenance

The following attestation bundles were made for exhash-0.3.1-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