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, exhash_result, 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 result dict, file unchanged
res = exhash_file("file.py", [f"{addr}s/foo/bar/"])

# With inplace=True, writes back on success; no changes if any command fails
res = exhash_file("file.py", [f"{addr}s/foo/bar/"], inplace=True)

Result dict

  • 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)

exhash_result([res1, res2, ...]) renders modified lines in lnhash format, matching the old repr(EditResult) style.

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.2.6.tar.gz (29.2 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.2.6-cp313-cp313-manylinux_2_34_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.34+ x86-64

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

Uploaded CPython 3.13macOS 11.0+ ARM64

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

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

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

Uploaded CPython 3.12macOS 11.0+ ARM64

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

Uploaded CPython 3.11manylinux: glibc 2.34+ x86-64

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

Uploaded CPython 3.11macOS 11.0+ ARM64

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

Uploaded CPython 3.10manylinux: glibc 2.34+ x86-64

exhash-0.2.6-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.2.6.tar.gz.

File metadata

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

File hashes

Hashes for exhash-0.2.6.tar.gz
Algorithm Hash digest
SHA256 e946478d5ad44f2e47d1a167008915d3e1d63b88ee725063fd1dc0771e02c767
MD5 edad988f5924eefdcd27860ed43ebae3
BLAKE2b-256 94b82927fa9d077052ac32aca9ebcd95e9819d2e05c779afa7a0a50cc09aece2

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 e32c088f1168231a6fe416acb7e9d99d18047011fe68209078fa3ca331763b63
MD5 63dd4d066920c28ea151a411b87cbf93
BLAKE2b-256 3820ab258cd6f9e6edd37457e079b009c9a19fc43e6c209c45b2447bedac42fa

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dd264299c054fe4f6a0a614a32a8a716546c48beb2915c59df9f9b9568486e55
MD5 daab97f211380db303c7701b14305a63
BLAKE2b-256 92ca0fdc1510e87153222604c8a5bfb6c7381a6c08e2ed9063ea824b403d553a

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 488be98a9ed31a7ee41c30191739e85a791001655e0e4e8e5d7eed139c13f5f4
MD5 7253795949bc8908da29c610f8e24923
BLAKE2b-256 a5c92515381796a0344a62c5a39a3da4bd0f073ba6ae7206cb126e6493b3ffe9

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 087c3193ccac0188fa34c2f9dd2feb8cd3e69ab87ab72bc9a8f6ddb74e46d418
MD5 f9c69ed6c7edc94f52bfaa860e43b42f
BLAKE2b-256 28940d35f1e5c61dc647ce64352b1987dd5f2743e54072d659081f610cf76add

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 e83297695b6143dbcf78f9bbab97150364e38be7551fc83ca5eca26168ccf483
MD5 4ae2dfe90964a9598bf53058999b81f6
BLAKE2b-256 79e852ceb26a912e7794addcce70b37cbbb7cd5c3b3b8d26fced1d7bdaf1ea61

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 af8c8e91c69eb6aa406850106f70fb4513ba0977d622f82a9a29418ee128bdd5
MD5 db99cfb15438a244dea7e88a198d9ad8
BLAKE2b-256 b51a920da0f3b2a8652ce493d3df922e796b11d9b60be4701c3f3149c12eb5fa

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c74747dc822e5516c9507dde418aa729be3d8de8a2575d91f2f7b0ae1157232a
MD5 f405f4ad8a4d2e47dff11ff4a6142e8f
BLAKE2b-256 2f9933212da51a6f4cab7dee300ded2bd91421e4fd36d5be74d8bb7b52cf7832

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for exhash-0.2.6-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ab14fb2da759fe4164bc51b4259dd4097379cf177e9718d381b0d1dfd13f4c1a
MD5 7e116e4224597dd7b21750b2c1bfd986
BLAKE2b-256 a4c6aff6c363810db1ce66b5b60e90f7cea58da6a904b067cd56a95ea730cc89

See more details on using hashes here.

Provenance

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