Skip to main content

tors

Fast, GIL-free text and document operations for Python, backed by Rust: normalization, Unicode segmentation, diffing, fuzzy and phonetic matching, multi-pattern search and redaction, chunking, and lightweight retrieval (TF-IDF, BM25, SimHash, Merkle integrity). An optional extra adds cross-format document extraction (PDF, Office, RTF/ODF/EPUB, CSV, HTML to markdown or plain text). In the spirit of orjson for JSON or polars for dataframes.

Full documentation lives at azx-pbc-oss.github.io/tors and in docs/ in this repo: the API reference, Documents, Performance, Async use, Design and scope, Dependencies and licensing, and three worked recipes.

Why

Python's re module and str methods never release the GIL, no matter how large the input is: a multi-megabyte text transform runs as one long GIL-held call that stalls every other thread and the asyncio event loop for its whole duration. tors does the same kind of transform as a single native Rust pass, wrapped in py.detach (PyO3's GIL-release call) for the entire computation, so the GIL is free for the rest of your program while it runs.

That covers the functions with a stdlib equivalent (normalize's pipeline mirrors unicodedata.normalize + a few re.sub calls; quote/unquote mirror urllib.parse; b64_encode_bytes/b64_decode mirror base64). Where the stdlib has no equivalent at all (Unicode text segmentation: grapheme clusters, word and sentence boundaries; leftmost-longest multi-pattern search; edit-distance and phonetic matching; content-defined chunking; SimHash near-duplicate detection; Merkle tree integrity; encoding detection), tors supplies it over maintained Rust crates, GIL-released the same way, so async services and threaded pipelines don't pay a blocking tax for text work.

Install

pip install tors

Building from source (a Rust toolchain and maturin):

pip install maturin
maturin develop --release

The document-extraction surface is a second wheel behind an extra: pip install "tors[documents]". From a checkout, uv sync --locked --extra documents builds and installs the payload wheel from this tree. See Documents.

The underlying Rust crate is also on crates.io, published separately as tors-core (the plain tors name belongs to an unrelated, dormant crate). cargo add tors-core, then use tors::... in code: [lib] name in Cargo.toml keeps the importable crate name tors regardless of the published package name.

Pyodide / WebAssembly

The Rust cores are OS-free, so tors also builds for Pyodide as a PEP 783 pyemscripten wheel: the abi3-py310 story carries over unchanged, so one wheel covers every Pyodide Python >= 3.10. The WASM workflow builds it on every push (artifact only; publishing to PyPI's Emscripten platform is not wired up). To build one locally:

rustup target add wasm32-unknown-emscripten
uvx --python 3.14 --from pyodide-build pyodide build . -o dist

The driving interpreter matters: Python 3.14 targets Pyodide 314.x / pyemscripten_2026_0 with a stable Rust toolchain; driving from 3.13 pins a Rust nightly older than this crate's MSRV. SIMD-dependent dependencies (base64, simdutf8, memchr, aho-corasick) compile their scalar fallbacks for wasm; the extension has been smoke-tested (import + representative calls across every core family) in Pyodide under node.

Quickstart

import tors

tors.normalize("line one  \n\n\n\nline two\r\n")
# 'line one\n\nline two'

tors.finalize("line one  \n\n\n\nline two\r\n")
# ('line one\n\nline two', 'e986ba083c7c1a9361143d2d8ccd8477d1d5eeef8b94b67c6ad4693f8f7b942a')

tors.diff_opcodes_lines("l1\nl2\nl3\n", "l1\nX\nl3\nl4\n")
# [("equal", 0, 1, 0, 1), ("replace", 1, 2, 1, 2), ("equal", 2, 3, 2, 3),
#  ("insert", 3, 3, 3, 4)]
thread = (
    "Ana: kickoff at nine.\n"
    "Ben: We briefed the U.S. team on the numbers. They asked for a follow-up.\n"
    "Ana: done."
)
chunks = tors.chunk_hierarchical(thread, 40, ["\n", None])
# [(0, 21), (22, 59), (59, 95), (96, 106)]

[thread[s:e] for s, e in chunks]
# ['Ana: kickoff at nine.',
#  'Ben: We briefed the U.S. team on the ',
#  'numbers. They asked for a follow-up.',
#  'Ana: done.']
corpus = [
    "the quick brown fox jumps over the lazy dog",
    "a lazy cat sleeps all day",
    "the fox and the dog are friends",
]
tors.bm25_rank("quick fox", corpus)
# [(0, 1.3162195220480066), (2, 0.4798180901812613), (1, 0.0)]

All of these run against the built extension; the output above is what they actually return.

What's inside

73 functions plus two small helper classes, grouped by what they do; the documents extra adds seven document-extraction functions and its own helper types. Full signatures, argument contracts, and edge cases are in the API reference.

family functions
Unicode normalization & forms normalize, finalize, nfc/nfd/nfkc/nfkd, html_unescape, strip_controls
UTF-8 / UTF-16 / base64 codecs decode_utf8, finalize_utf8, utf8_is_valid, decode_utf16, utf16_is_valid, b64_encode_bytes, b64_decode, detect_encoding
Text segmentation (UAX #29) grapheme_count, word_bounds(+_iter), word_count, sentence_bounds(+_iter), sentence_count
Diffing (difflib-compatible) diff_opcodes, diff_opcodes_lines
Fuzzy & phonetic matching similarity_ratio, get_close_matches, levenshtein, jaro, jaro_winkler, soundex, metaphone, double_metaphone, nysiis, daitch_mokotoff, refined_soundex
Multi-pattern search & redaction find_patterns(+_iter), count_matches, replace_many, replace_many_masked, CompiledPatterns
Markdown / code-fence extraction extract_code_blocks, strip_code_fences, dedent
JSON repair (json_repair port) repair_json, repair_json_loads, repair_json_diagnostics
Truncation & lexical grounding truncate_to_bounds, truncate_ellipsis, is_grounded
URL encoding quote, quote_plus, unquote, unquote_plus
Text chunking chunk_cdc, chunk_text(+_iter), chunk_by_words/_sentences/_paragraphs/_lines(+_iter), chunk_hierarchical
Information retrieval & integrity tf_idf, bm25_rank, simhash64, simhash128, merkle_root, merkle_diff
Text-processing pipelines apply_pipeline, CompiledLemmaDict
Document-format extraction (tors.documents) to_markdown, to_text, sniff, pdf_extract, pdf_page_count, pdf_classify, pdf_link_uris

Highlights

  • GIL release on every call. 12 MiB of prose through tors.finalize in a background thread holds the event loop's worst heartbeat gap to 10-14 ms; the pure-Python equivalent holds it for 92-108 ms. Full measured tables: Performance.
  • Async where it matters. tors.aio wraps exactly the large-input functions in asyncio.to_thread, unconditionally, with no size-based branch; everything else keeps one sync spelling. Details: Async use.
  • Stateless by design. No pipeline objects, no caches, no handles to manage; the build-once CompiledLemmaDict and CompiledPatterns handles are the measured exceptions, and the scope cuts (no regex engine, no bundled lemma data, no search index) are decisions, not oversights. Details: Design and scope.
  • Permissive-only dependency tree, machine-checked by cargo deny on every push. The full table and license accounting: Dependencies and licensing.

Contributing

See CONTRIBUTING.md for setup, the pre-PR checklist (tests, clippy, fmt, ruff, the license gate), fuzzing, release process, and commit-message conventions. For security vulnerabilities, see SECURITY.md instead of filing a public issue.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tors-0.6.1.tar.gz (1.3 MB view details)

Uploaded Source

Built Distributions

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

tors-0.6.1-cp310-abi3-win_arm64.whl (2.7 MB view details)

Uploaded CPython 3.10+Windows ARM64

tors-0.6.1-cp310-abi3-win_amd64.whl (2.9 MB view details)

Uploaded CPython 3.10+Windows x86-64

tors-0.6.1-cp310-abi3-musllinux_1_2_x86_64.whl (3.7 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

tors-0.6.1-cp310-abi3-musllinux_1_2_aarch64.whl (3.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

tors-0.6.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.4 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

tors-0.6.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

tors-0.6.1-cp310-abi3-macosx_11_0_arm64.whl (3.0 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

tors-0.6.1-cp310-abi3-macosx_10_12_x86_64.whl (3.2 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file tors-0.6.1.tar.gz.

File metadata

  • Download URL: tors-0.6.1.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1.tar.gz
Algorithm Hash digest
SHA256 1fdc318e315cc33551cc78c3a2b3b70eeff79d58e5d4220827eee39bcfc5e03a
MD5 c6249764ab931d24d552f36dad8fd26f
BLAKE2b-256 4c02cdb096f316a95a2bed49999c0b36e67f545cf0871d7337b4f3970a790421

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-win_arm64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-win_arm64.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: CPython 3.10+, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 621e46a72cf055f4950688a536c6aa4d7f8e9fbeffb42480240bffe064f0adae
MD5 85ae53219257eab66220e683aea29ee6
BLAKE2b-256 26361c66d2a7a1bfb8d8aba97602eba2f5a41e0578a6badbdb9c96456eb09dd6

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 2.9 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 f4cbd3a78cc69b62de635961ec0cf73a3f1245cda394a53ae199b9b50808e0f8
MD5 353718687112055ff206a204275d721c
BLAKE2b-256 cfd4a5357ad0a964fd7b5bef9eceb7873ed39ad6458b0287266806941fce992b

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 b5c93285cb2faf159f912297d88c7469eff4b44dbd198263be51c2eba6c5e344
MD5 f6e84eb9c44d917bb17bb84de5c64e01
BLAKE2b-256 54500f71b51b217f58e72325c534dd75e1ad3ab35ef6dce5fe1882359a8d6a43

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 3.5 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 8dfde2f21c1d2a725f273a8cb1f8629c253e9f52f80fadb27622b663f78d9c70
MD5 2f8480e7c381d9a55dafe164de0a731a
BLAKE2b-256 caef65fdcb36a7c1259f5121f0b6debf02761507feebed310e9b065d6de97c40

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 3.4 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7574e87f3a04ef7807f89893ad80f135349c36d0a048b4f279d01ffac3d2bb96
MD5 6ddeddeb50ad38ddbdf9a622bcd15c87
BLAKE2b-256 c0463330d54e3fc8659feb123cef1a48b4f530fe08731a4afdbc97ccb2807977

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 3.3 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 bd9840cf43b664c3725d48b6e140d010e61ddaa405b03bf98cce6f4738864e8a
MD5 88e36556ffefdb1d7ebbbaa44ee96da3
BLAKE2b-256 5b505a1b8aaf31fa0590d2a2def4b11256279ef05a9eee40fa57835be09c1f26

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 3.0 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7d4706e420cea9f01b886300e331e62e9f33b0fb4ab2ca2ad18b2a279a3c3c9e
MD5 b6c7c766debb9ad6b79e0c792dd09792
BLAKE2b-256 d686b9cecc9e116b8451221ea372152eb11044b8e4b82f61f7f7217f13f2aaea

See more details on using hashes here.

File details

Details for the file tors-0.6.1-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: tors-0.6.1-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 3.2 MB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tors-0.6.1-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 95cfee4263d84e47f3591545767913a4366a165f235a94bf7324edf6620f77c9
MD5 410c259c9b7db7c0727e1b1693d35f10
BLAKE2b-256 38c913573094ea7f723e058f98079279d9c510c2f8d79318f873aba008a436b7

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.6.1 This release

9 files

0.6.0

9 files

0.5.0

9 files

0.4.1

9 files

0.4.0

9 files

0.3.1

9 files

0.3.0

9 files

0.2.0

9 files

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