Skip to main content

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 a convenient Python API, an IPython cell magic, and the exhash/lnhashview CLI commands:

pip install exhash

lnhash format

We refer to an lnhash as a tag of the form lineno|hash|, where hash is the low 12 bits of CRC-32 (IEEE) over the line's UTF-8 content, encoded as two Base64url characters (A–Z, a–z, 0–9, -, _), high six bits first.

Address forms:

  • lineno|hash|: hash-verified address
  • $: last line (no hash)
  • %: whole file (1,$, no hashes)
  • lineno: line number with no hash. Only p accepts it, including as the subcommand of g, g!, or v.

CLI

The exhash and lnhashview commands are Python console scripts over the native Rust extension, installed into your PATH by 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

If end is past EOF, lnhashview returns through the last available line instead of failing.

Edit

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

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

# Change one line with inline text (spaces after c are literal text)
exhash file.txt '12|vN|c    replacement line'

# Append multiline text from stdin (read through EOF; every line is literal)
exhash file.txt '12|vN|a' <<'EOF'
new line 1
new line 2
EOF

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

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

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

# Print lines 12 to 20: p accepts line numbers with no hashes
exhash file.txt '12,20p'

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

# Create a missing file by treating it as empty input
exhash new.txt '0|AA|a' <<'EOF'
first line
EOF

Substitute and global commands use fancy-regex syntax, including lookbehind ((?<=...), (?<!...)), lookahead ((?=...), (?!...)), and pattern backreferences (\1):

  • Replacement syntax uses $1, $0, ${name}, and $$ for a literal dollar sign; \1 in a replacement stays literal. See fancy_regex::Regex::try_replacen.
  • \/ 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/
  • For example, s/// accepts newlines in pattern/replacement; replacement newlines split one line into multiple lines.
  • Transliteration uses y/src/dst/ and requires source/destination to have equal character counts
  • A substitute whose pattern matches nothing in its addressed range fails (nothing is written), so a typo cannot silently no-op; substitutes running inside g/g!/v payloads stay lenient, since not every selected line need match
  • Matching errors, including exceeding the engine's default backtracking limit, abort the command set before any files are written

When passing multiple commands, each command's lnhashes are verified immediately before it runs. A single-line address may match either the line's current hash or its call-start hash, so commands can stack on one line. Range addresses remain strict, and structural changes invalidate call-start records at and below their topmost affected line.

For CLI multiline a/i/c commands, omit inline text and provide the text block on stdin:

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

If the file does not exist and the command set is valid on empty input, exhash treats it as an empty file and writes the result. For example, 0|AA|a can create a new file. Creating a file also creates its missing parent directories.

Stdin filter mode

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

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

Notebook cells

lnhashview-cell and exhash-cell apply the same workflow to notebook cells, addressed by exact or unique ID prefixes. Pass comma-separated IDs to view several cells together; the output adds a # cell <id> header to each group:

lnhashview-cell nbs/00_core.ipynb ab12cd34
lnhashview-cell nbs/00_core.ipynb ab12cd34,ef56ab78
exhash-cell nbs/00_core.ipynb ab12cd34 '3|7v|s/old/new/'
exhash-cell --dry-run nbs/00_core.ipynb ab12cd34 '3|7v|d'
printf 'replacement line\n' | exhash-cell nbs/00_core.ipynb ab12cd34 '3|7v|c'

Document outlines

exhash-open opens Markdown, source code, notebooks, URLs, or stdin as a verified section tree. Its default output is the immediate outline; pass the section number and first line address that start a row to read that section:

exhash-open README.md
exhash-open README.md '1.2.|21|Z1|'
exhash-open README.md --paths --depth 2
exhash-open README.md --search 'CLI|console'
exhash-open README.md --lnhashs
exhash-open https://example.com/llms.txt --links

Python API

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

Viewing

text = "foo\nbar\n"
view = lnhashview(text)                        # ["1|Gy|foo", "2|PU|bar"]
view = lnhashview_file("f.py", start=1, end=260) # end past EOF is clamped

lnhashview/lnhashview_file return a list subclass whose repr shows the rows verbatim, one per line, so a bare call in IPython displays a ready-to-copy view.

Editing

exhash(text, cmds, sw=4) takes the text and a required iterable of tuple command specs (use [] for no-op). Raw command strings are rejected by the Python API. sw controls how far < and > shift.

A command is usually (addr, op) or (addr, op, payload). addr is an lnhash address string from lnhash(...)/lnhashview(...); put ranges in that same string, e.g. f"{a1},{a2}". Substitute uses (addr, "s", pattern, replacement[, flags]), so patterns and replacements can contain / without delimiter escaping.

Text fields can contain newlines. That covers multiline a/i/c payloads and substitute pattern/replacement. An a/i/c payload splits into lines at each newline: "x\ny" gives two lines, "x\n" gives x and then a blank line, and "" gives one blank line. Commands such as d, m, and sort do not take text.

addr = lnhash(1, "foo")  # "1|Gy|"
res = exhash(text, [(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, [(a1, "s", "foo", "FOO"), (a2, "s", "bar", "BAR")])

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

# Change one line; leading spaces are part of the replacement
res = exhash(text, [(addr, "c", "    replacement line")])

# Append multiline text in one tuple payload (no dot terminator)
res = exhash(text, [(addr, "a", "new line 1\nnew line 2")])

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

# Also wrong: do not split the inserted text into separate cmds entries
# res = exhash(text, [(addr, "a"), "new line 1", "new line 2"])

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

# Literal / needs no delimiter escaping in tuple substitute fields
res = exhash("a/b\n", [(lnhash(1, "a/b"), "s", "a/b", "c/d")])

# Literal newlines in replacement split one line into multiple lines
res = exhash("foo\n", [(lnhash(1, "foo"), "s", "foo", "bar\nbaz")])
print(res["lines"])  # ["bar", "baz"]

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

# Global commands take a pattern plus a nested subcommand tuple (no address)
res = exhash("keep\nTODO x\n", [("%", "g", "TODO", ("d",))])

# Transliterate takes source/dest fields (equal character counts)
res = exhash("abc\n", [(lnhash(1, "abc"), "y", "abc", "ABC")])

File helpers

lnhashview_file reads directly from one file path. All file paths, including file-qualified addresses, expand a leading ~ to your home directory. file_exhash(path, *cmds, sw=4, inplace=True) uses path as the default file context for unqualified addresses. Pass each command as its own tuple argument. Put file-qualified source and m/t destination addresses in the address/destination tuple fields:

view = lnhashview_file("file.py")

# By default, writes changed files after every command succeeds
# and returns the combined diff string.
diff = file_exhash("file.py", (addr, "s", "foo", "bar"))

# With inplace=False, files are unchanged and a FileSetEditResult is returned.
res = file_exhash("file.py", (addr, "s", "foo", "bar"), inplace=False)
print(res.changed)          # ["file.py"]
print(res["file.py"].lines)
print(res.format_diff())    # includes --- file.py / +++ file.py headers

# Missing files are treated as empty only when the command is valid on empty input.
diff = file_exhash("new.py", ("0|AA|", "a", "print('hi')"))

# File-qualified addresses can edit or transfer lines across files.
diff = file_exhash("src/a.py",
    ("src/a.py:24|8S|,38|De|", "m", "src/b.py:$"),
    (r"src/a.py:5|Gq|", "s", r"from \.b import old", r"from \.b import helper"))

A file prefix is separated from the address with :. Escape literal colons in filenames as \: and literal backslashes as \\. Two spellings of one file, such as a relative path and an absolute one, name one target. Results report it by its first spelling.

file_exhash(..., inplace=False) returns a FileSetEditResult:

  • res.files: dict of path to FileEditResult
  • res.changed: changed paths, in first-touch order
  • res.printed: paths with lines addressed by p (res[path].printed gives the line numbers)
  • res.default_path: the default path passed to file_exhash
  • res[path]: shorthand for res.files[path]
  • res.format_diff(context=1): combined diff of the changed targets, with --- path / +++ path headers. It never includes printed lines.
  • str(res): each target's diff, followed by its printed lines (see EditResult)

Notebook cells

lnhashview_cell(path, cell_id, ...) returns a normal lnhash view for one cell. lnhashview_cells(path, *cell_ids, ...) returns the requested cells in order, using # cell <id> headers before each cell's normal lineno|hash|content lines. cell_exhash(path, cell_id, *cmds, sw=4, inplace=True) edits one cell; pass each command as its own tuple argument. Like file_exhash it writes and returns a diff by default, and inplace=False previews the EditResult without touching the file.

Edit journal

When the EXHASH_JOURNAL environment variable names a directory, every write by file_exhash, cell_exhash, the %%exhash magic and the CLI first appends a record of each changed file or cell to that directory. EXHASH_AUTHOR names the writer, unknown by default. Each author appends to its own <author>.jsonl. Concurrent writers, such as several agents editing one tree, never share a file. A record holds time in seconds since the epoch, author, the absolute path, the cell id or null, the added and removed diff row counts, and the full diff with no context lines. An edit whose record can't be written fails before it writes anything.

journal(path, author=None, since=None) reads the records back, oldest first, from the directory or from one author's file:

from exhash import journal
j = journal("/tmp/edits", author="agent-1")
j.stat()   # added, removed and net rows for each file, with their total
j.diff()   # every recorded hunk in full

The %%exhash cell magic

Importing exhash.skill under IPython or Jupyter registers the %%exhash cell magic - the standard way to apply a/i/c payload commands interactively. The magic line is %%exhash <path> [<cell_id>] <address> <a|i|c>; the payload is everything below it, taken verbatim. Nothing in the payload is parsed as Python, so there is no quoting or escaping at all:

%%exhash notes.txt 2|7v|a
new line 1
new line 2
  • %%exhash new.py 0|AA| a creates a missing file.
  • %%exhash f.py % c replaces the whole file (% needs no hashes). With a cell id, %%exhash nb.ipynb ab12 % c replaces that notebook cell's source.
  • %%exhash f.py 12|Py|,15|HD| c replaces just that range, both addresses from one lnhashview_file view.
  • The payload has one line for each line of the cell.
  • Each magic cell applies one command and returns the diff.

Tuple a/i/c payloads (as in the examples above) remain for scripts and tests, where magics don't exist. Interactively, prefer the magic: a Python string layer invites quoting mistakes.

Pyskill

The package registers exhash.skill as a pyskill exposing the primary Python APIs with LLM-oriented workflow docs. Use doc(exhash.skill) after importing it through a pyskills host.

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)
  • printed: 1-based line numbers explicitly addressed by p

Lines addressed by p are not part of the diff. res.format_printed() returns them as a bare lnhashview with no headers. It never caps or truncates its rows. str(res) and the repr show the diff, then a # printed header, then the printed lines. A result that changed nothing shows the printed lines alone, with no header. file_exhash and cell_exhash return the same output. A p-only call writes nothing and returns the bare view. A call that changes and prints nothing returns none: No changes., as fastcore's editors do. When more than one target is reported, a target with only printed lines is headed by # file <path> or # cell <id>.

res.format_diff(context=1) returns a unified-diff-style summary showing only changed lines with context. A line that a command moved, as m and sort do, shows as removed where it stood and added where it stands. Each contiguous changed block shows all removed lines before all added lines:

res = exhash(text, [(addr, "s", "foo", "baz")])
print(res.format_diff())
# --- original
# +++ modified
# -1|Gy|foo
# +1|PU|baz
#  2|X2|bar

format_diff(maxlen=n) caps each diff row at n chars plus a closing …. Where a run of changed rows holds as many - rows as + rows, the nth - row pairs with the nth + row. A capped row of a pair starts 20 chars before the pair's first difference, with … after its address. Every other capped row keeps its start. The result reprs and the diffs that file_exhash and cell_exhash return use maxlen=180. truncate_diff then keeps up to 15 diff lines, sharing each changed block's remaining budget between removals and additions. Elision markers appear at each omitted run, in addition to those 15 lines.

All diff strings returned by format_diff, file_exhash, and cell_exhash are fastcore PrettyStrings, and the result objects' reprs show the diff too - so in IPython, ending a cell with the bare call displays the diff verbatim, no print needed.

Document outlines

open_doc opens a file (fname=, or a Path as src; recorded for refresh() and edits), a URL (an https?:// str, fetched), or any other str as text, and returns a Section tree: Markdown sections from headings, code sections (py, js, ts, tsx, rs, zig, swift) from tree-sitter definitions, and .ipynb sections from md-heading cells over cells. The bare repr is a fixed-width outline, one row per section. It lists the section itself and the two levels below it, not counting a level that holds only one section, such as a single H1:

1.6.|56|lq|,78|74| Release [725] Publishing is handled by GitHub Actions in `.github/workflows/ci.yml`…

The leading token is a verified address: the dotted addr (trailing dot; the root's is .) followed by the addresses of the section's first and last lines. at() and view() need only the first part, such as 1.2.|12|Py|, and fail if that line's hash is stale. The whole token is a file_exhash range for the section. find(title), search(pat), paths(depth), and numeric indexing (d[1][6]) traverse the live tree; links(pat) lists inline links numbered document-wide, and open(n) opens link n as a new tree (fetched or read relative to base). Previews join lines with and render links as [text][n], so no URL is ever displayed. A markdown row's preview starts under its heading; a code row has no title, and its preview opens with the def line itself, signature included. view() returns a section's rendered text the same way (.src is the raw source); view(*tokens) returns the live sections at those verified addresses, displayed under # token headers when more than one; nums=/lnhashs= switch any view to stored lines with edit-ready addresses. Notebook section tokens carry the heading cell id, and view(lnhashs=True) emits cellid:lineno|hash| rows ready for cell_exhash.

search(pat) returns one hit per matching source line, not one row per section. Each 180-character row shows the containing section's verified token, the matching line's hash address, and a preview starting at that line and continuing across newlines as , up to the section's end. Section tokens repeat when several lines in the same section match: copy the section token to view() to read the whole section, or use the line address to edit the hit. Notebook line addresses include the cell ID. Each hit exposes .section, .address, and .preview; slicing the results preserves their display format.

Tests

pytest -q

Metadata

Release files for exhash 0.4.22

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for exhash 0.4.22
File Size Uploaded Provenance Actions
exhash-0.4.22.tar.gz 93.8 kB With provenance Details for exhash-0.4.22.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for exhash 0.4.22
File Actions
exhash-0.4.22-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ x86-64 Details for exhash-0.4.22-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
exhash-0.4.22-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.13 CPython 3.13 Linux glibc 2.17+ ARM64 Details for exhash-0.4.22-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
exhash-0.4.22-cp313-cp313-macosx_11_0_arm64.whl CPython 3.13 CPython 3.13 macOS 11.0+ ARM64 Details for exhash-0.4.22-cp313-cp313-macosx_11_0_arm64.whl
exhash-0.4.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ x86-64 Details for exhash-0.4.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
exhash-0.4.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.12 CPython 3.12 Linux glibc 2.17+ ARM64 Details for exhash-0.4.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
exhash-0.4.22-cp312-cp312-macosx_11_0_arm64.whl CPython 3.12 CPython 3.12 macOS 11.0+ ARM64 Details for exhash-0.4.22-cp312-cp312-macosx_11_0_arm64.whl
exhash-0.4.22-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ x86-64 Details for exhash-0.4.22-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
exhash-0.4.22-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.11 CPython 3.11 Linux glibc 2.17+ ARM64 Details for exhash-0.4.22-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
exhash-0.4.22-cp311-cp311-macosx_11_0_arm64.whl CPython 3.11 CPython 3.11 macOS 11.0+ ARM64 Details for exhash-0.4.22-cp311-cp311-macosx_11_0_arm64.whl
exhash-0.4.22-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ x86-64 Details for exhash-0.4.22-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
exhash-0.4.22-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.10 CPython 3.10 Linux glibc 2.17+ ARM64 Details for exhash-0.4.22-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
exhash-0.4.22-cp310-cp310-macosx_11_0_arm64.whl CPython 3.10 CPython 3.10 macOS 11.0+ ARM64 Details for exhash-0.4.22-cp310-cp310-macosx_11_0_arm64.whl

Total release size: 27.1 MB

Release files / exhash-0.4.22.tar.gz

Download URL exhash-0.4.22.tar.gz
Size 93.8 kB
Tags Source
SHA-256 checksum
How to use checksums
be34dc7e50644964d26fd73773db37e078a56c0bcbce92a5e5960b2dbdc37aca
BLAKE2b-256 checksum
How to use checksums
5f2ba1d64972a63dadb451d6ab4f980e4c99dbfd86a8ccb1061503785f882f72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL exhash-0.4.22-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 2.3 MB
Tags CPython 3.13 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
ab68816b2fb9945be2e6b71e6c46622457d01324fe5098a49f6531339a2b4816
BLAKE2b-256 checksum
How to use checksums
a1b6b2e6933454cf3b5971bf927e5135902d09bdd9fad834a327b8e2c6d0acf7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL exhash-0.4.22-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 2.2 MB
Tags CPython 3.13 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
4d5a202e6f62c574b6b4d9f62ad970329b2d233938e35649af221f66777061fc
BLAKE2b-256 checksum
How to use checksums
928255d26932703ae5213bcea3dbc33ae59b8b67a85b0d26c9fc71ba1cb5aa4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp313-cp313-macosx_11_0_arm64.whl

Download URL exhash-0.4.22-cp313-cp313-macosx_11_0_arm64.whl
Size 2.2 MB
Tags CPython 3.13 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
543ce6834bc751bf31ad2485d3c6e697f108a3dab64cdb6fd58f798fc4153032
BLAKE2b-256 checksum
How to use checksums
09bc408872ded6effcbba0aea7377e21563782b85bba6fd9a9646ddde9f1d0a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL exhash-0.4.22-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 2.3 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
e0d952f44966df6fdfac38d604fa5e68787abb70f54f340457cf6f8512d1784a
BLAKE2b-256 checksum
How to use checksums
543b4327c1cdfe97789d36cfb7e6ea63490acd1da176f71af74b71528130083e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL exhash-0.4.22-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 2.2 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
05b6875dfad76e10bd12b153b5c66a21ecb143ebc2ba7370e273f285f721f922
BLAKE2b-256 checksum
How to use checksums
0c0a255974e36510bef932963c573e0a0a044387c90a353fe61b7da5293b03df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp312-cp312-macosx_11_0_arm64.whl

Download URL exhash-0.4.22-cp312-cp312-macosx_11_0_arm64.whl
Size 2.2 MB
Tags CPython 3.12 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
840ebe3c76ffb9149fff7c46a623260f4bce8a13f4d4c9d7114828250a7a15ce
BLAKE2b-256 checksum
How to use checksums
729de70fa723e10910b9289acd223e89327a150fc2851ed101c765b91326afc7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL exhash-0.4.22-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 2.3 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
929b817414283e1e1f2d84223b1b6e54f76f2375f4adbc135bb7cf9679753a0c
BLAKE2b-256 checksum
How to use checksums
9535d2af76de4cd4d7f072de1a7cbafbf41591aa1153b76dd0595cfff688c387
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL exhash-0.4.22-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 2.2 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
deb79799e1d699e225a1d96a4f89f0d50c05b1caa7f2b66f6e8dc788caf7d9f5
BLAKE2b-256 checksum
How to use checksums
b45737c966bf35c22edb54af7158d8ff7b9b3e048520ef145ab44265572772f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp311-cp311-macosx_11_0_arm64.whl

Download URL exhash-0.4.22-cp311-cp311-macosx_11_0_arm64.whl
Size 2.2 MB
Tags CPython 3.11 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
82b588bb2e0200f36d902f81e5ff875003040121b7fc8f9932df08ad2f9f80cc
BLAKE2b-256 checksum
How to use checksums
be41ec9847411da44f6f2138aee7aaab98102f4c0d82131e42af883b8de21d88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL exhash-0.4.22-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 2.3 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
84a60b788780579dd6f2e04430d677b64088c2e46de9519e830a4e7a5a6629dd
BLAKE2b-256 checksum
How to use checksums
c42cdd47b96d90f0b5ce4a2b02708ecc9cc4ae315061d2a32f90556984cd5366
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL exhash-0.4.22-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 2.2 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
ee8d50ef672dd9037a965971a8142bbf0d8d6fd20debed4c97221ecf10730e75
BLAKE2b-256 checksum
How to use checksums
b0e383f9e84a7e4ee3a2b2ea4905da3408141f929e1d3f8cbfffcb0c8e0ccfc1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / exhash-0.4.22-cp310-cp310-macosx_11_0_arm64.whl

Download URL exhash-0.4.22-cp310-cp310-macosx_11_0_arm64.whl
Size 2.2 MB
Tags CPython 3.10 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
2f90abc8b737805392623dc0ba0d6a9ccfda9dd730a885bd6e45762e2b6c9406
BLAKE2b-256 checksum
How to use checksums
89736e41225bf276863875fcac059cc95eea4d9e89916b85dbecb7adfd9192ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for exhash 0.4.22

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 13 files in this release have been uploaded with provenance metadata.

Select from 13 files

Attestation for exhash-0.4.22.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.4.22 This release

13 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page