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.0.tar.gz (1.2 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.0-cp310-abi3-win_arm64.whl (2.7 MB view details)

Uploaded CPython 3.10+Windows ARM64

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

Uploaded CPython 3.10+Windows x86-64

tors-0.6.0-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.0-cp310-abi3-musllinux_1_2_aarch64.whl (3.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

tors-0.6.0-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.0-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.0-cp310-abi3-macosx_11_0_arm64.whl (3.0 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

tors-0.6.0-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.0.tar.gz.

File metadata

  • Download URL: tors-0.6.0.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","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.0.tar.gz
Algorithm Hash digest
SHA256 caf1da3f18703b5802fe93534fbd9cb65b7c48f8c32d2367885715b1af20a7da
MD5 169c5d4cf6356c97347fa810a2867292
BLAKE2b-256 f3f8cdf585b1a26b8c188eb13ff4e12488414caea9796b45933710d329312184

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 02678ed365f01b06a0a60c035575969498580d6d827bcba2ea7db858a8e89fb3
MD5 500af26258f696d42e884bd525bcbdba
BLAKE2b-256 6489c4fdafd1ddffeed0842d9553b90de72d3c8e9b5d583e00f3a2f7a2016a13

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 adf03cee720d9f8a06c59df195b6e3d2a7beed220d7f7cb23b5557d471a0957f
MD5 eb69c591e24e4fe23acb732f45c14757
BLAKE2b-256 f7366cb1d971e604b08b05465a18df254fcdb2f2bf7a7080b99efba45c7b2284

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 bda790c6617688a011dcf153c1c3db4db25020841e58dd408203a0b022669472
MD5 11492791aa8fff1c7c3288e4d43d7a7d
BLAKE2b-256 f3e9281bf69bc1e220d8219b0eff051f619d9448883ece61c29ce6dd77ada138

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 5b202dcd32cf37de056499d596c4621934a8c9e25e840290331069907a629e02
MD5 00224df5c11b7a25f0543b4e3c0d794c
BLAKE2b-256 ff310be4902ad11825322e8bab1f9a082a297639eeeba9ede7eba01cdd591b07

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ae18080c187c5d4fa08efe2fd95927cc97ae8805181809cf24d96b3b96ab1c30
MD5 3c919d70733d5758ed61c66ea2dce1b0
BLAKE2b-256 f4bf6efedfdbc1cb2b47bce5ec862a49b28cae5620ba57d2f1474c60a7977eeb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e38f148d4d281e917eca55cf9ce1189ba98d89b24e9abd987a7326b2f1075c60
MD5 af9ed20c144a4147131e1fb74f4caad9
BLAKE2b-256 85aef29d523018e035c265a2cc66ccb3cd8fd5dc1daca2fa0aee292036edc2ce

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e684ce24594dab578ef4502d1aa9dfc7ca4c2423cde2d9c27886d3e295c64688
MD5 8a05ce5ba5405d4cf03c7266042c8f87
BLAKE2b-256 3438e4aff46774af7636825dd95c933aa1c5737893e026cd521c6f7add3846b5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.6.0-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.12 {"installer":{"name":"uv","version":"0.12.12","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.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d39f925f5cf8866a9371a446bc6d3074595e9669ed7e42537a5aa4b77631582b
MD5 e9704fd26c8f762fa532dd7eb36bdf3b
BLAKE2b-256 3242e2b6d0ea5c13f9d687a61425aef2b88ba670fafb3bec942b3a934d91e411

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.1

9 files

This release

0.6.0 This release

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