Two questions, both answered from the bytes on disk right now:
- What breaks if I change this symbol? The live blast radius, with no precomputed graph, so a file someone saved thirty seconds ago counts.
- Where did this pasted text come from? Quote attribution re-verified against each source's current bytes, so a citation you get back is one you can actually follow.
pip install blast-searchThe distribution is blast-search; the import stays blast. The bare name on
PyPI belongs to an unrelated author, so this is the same split bs4, PIL, and
cv2 already ship.
We bundle the native blast executable in the wheel and install it on PATH.
The bindings resolve that bundled binary directly; $BLAST_BIN can select
a custom build.
import blast
radius = blast.blast("AcmeStore")
print(radius.symbol, radius.kind)
for site in radius.definitions:
print("defined", site.path, site.line)
for ref in radius.dependents:
print("used by", ref.path, ref.line, ref.enclosing)The report separates what it knows from what it suspects, and never fuses them
into one number. definitions, dependents, and dependencies are exact;
twins (compression kin of the seed's file, so co-edit risk) and ripple
(second-hop callers in the same language) are statistical. comments catches
the mentions in prose, because that is where the stale docs and the TODOs live.
That split is a property you can read off directly, which is usually what a program wants:
radius.paths # the whole edit set, most load-bearing first
radius.exact_paths # only files that provably name the symbolScope is the whole corpus by default, since a blast radius that stopped at a
directory would lie. Narrow it with roots when you know the change is
contained, and use budget as a soft cap that trims the lowest-priority tail
into stats.omitted rather than silently truncating.
radius = blast.blast("AcmeStore", roots=["services/backend"], budget=4000)
if radius.stats.omitted:
print("trimmed", radius.stats.omitted)for cite in blast.provenance("const fd = std.posix.openat(std.posix.AT.FDCWD"):
print(cite.source, cite.line, cite.verified, cite.text)verified is the whole point. relate quote
attributes phrases off the codex shelf, which is a snapshot and can therefore
name a line that no longer exists. blast re-reads the file and re-finds the
phrase, so verified=False is drift being reported rather than hidden.
Python 3.12 or later; the wheel supplies the engine. provenance reads
relate's codex shelf, built by relate.atlas_index(shelf=True).
Piping an exact search into a similarity tool throws the match information away between the steps and then pays whole-corpus statistical noise: a changelog that never matched still ranks high on coverage. Composing keeps the exact and the statistical evidence in separate fields on one answer.
Composition-as-narrowing lives next door as the matching argument on
relate.similar / relate.families / relate.pack. What is here is the pair
of questions that need current bytes instead of a narrowing.
| Package | Question |
|---|---|
gist-search |
where is this exact pattern? |
relate-search |
what resembles this, and what repeats? |
blast-search |
what breaks if I change this symbol? |
irregex |
the linear-time regex engine underneath all three |
uv sync --group dev
uv run pytestDepends on irregex and relate-search (path sources for local checkouts;
PyPI for published wheels). Behavioral tests also pull gist-search as a dev
oracle.