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), in the spirit of orjson for JSON or polars for dataframes.

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; and 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 either way.

Install

pip install tors

Building from source (a Rust toolchain and maturin):

pip install maturin
maturin develop --release

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 (CPython compiled to WebAssembly) as a PEP 783 pyemscripten wheel — the abi3-py310 story carries over unchanged, 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.

What's in it

67 functions plus two small helper classes, grouped by what they do. Each entry is a one-line description; full signatures, argument contracts, and edge cases are in the API reference.

Unicode normalization & forms: clean up messy extracted text, or apply a single normalization form directly.

  • normalize: NFC + CRLF folding + blank-line collapsing + strip, the common PDF/OCR-extraction cleanup pipeline
  • finalize: normalize plus a SHA-256 of the result, in one pass
  • nfc / nfd / nfkc / nfkd: the four Unicode normalization forms standalone
  • html_unescape: html.unescape, full HTML5 entity table
  • strip_controls: every C0/DEL control run becomes one space (model-output scrub)

UTF-8 / UTF-16 / base64 codecs: validate and decode bytes without holding the GIL for the whole buffer.

  • decode_utf8 / finalize_utf8: UTF-8 decode, and decode+normalize+hash fused
  • utf8_is_valid: SIMD UTF-8 validity check, no str materialized, no exception flow
  • decode_utf16 / utf16_is_valid: the same pair for UTF-16, with BOM sniffing
  • b64_encode_bytes / b64_decode: RFC 4648 base64, byte-exact base64 module parity
  • detect_encoding: heuristic legacy-encoding guesser (chardetng, Firefox's detector)

Text segmentation: Unicode-correct boundaries the stdlib has no segmenter for at all.

  • grapheme_count: extended grapheme cluster count (UAX #29)
  • word_bounds / word_bounds_iter / word_count: word boundaries, list, iterator, and count forms
  • sentence_bounds / sentence_bounds_iter / sentence_count: sentence boundaries, same three forms

Diffing: difflib-compatible opcodes at native speed.

  • diff_opcodes: character-level SequenceMatcher.get_opcodes() shape
  • diff_opcodes_lines: the line-level spelling for document/version diffs

Fuzzy string matching & phonetic matching: edit-distance and sound-alike matching, none of which the stdlib ships.

  • similarity_ratio / get_close_matches: difflib's ratio and closest-match search
  • levenshtein / jaro / jaro_winkler: classic edit-distance and similarity metrics
  • soundex / metaphone / double_metaphone / nysiis / daitch_mokotoff / refined_soundex: classic English/Latin-script phonetic codes, five distinct mapping tables for the same name-matching/dedup lane

Multi-pattern search & redaction: leftmost-longest search and simultaneous replace, a combination no stdlib or maintained GIL-free binding offers.

  • find_patterns / find_patterns_iter / count_matches: multi-pattern search, list, iterator, and count forms
  • replace_many: simultaneous multi-pattern replace, one pass, no re-scanning
  • replace_many_masked: the same, length-preserving, for offset-safe redaction
  • CompiledPatterns: a build-once handle for a fixed pattern list, reused across calls

Markdown / code-fence extraction: pull structured content out of model output.

  • extract_code_blocks: every fenced code block, per CommonMark's fence grammar
  • strip_code_fences: unwrap a whole response wrapped in exactly one fence
  • dedent: textwrap.dedent, byte-exact

Truncation & lexical grounding: fit text to a budget, or sanity-check a claim against its source.

  • truncate_to_bounds: cut to a character budget at a word/sentence boundary, never mid-grapheme
  • truncate_ellipsis: hard cut to a character budget plus a marker, never mid-grapheme (the DB-column shape)
  • is_grounded: exact or fuzzy substring check of a claim against its source

URL encoding: urllib.parse's percent-encoding quartet, GIL-released.

  • quote / quote_plus / unquote / unquote_plus

Text chunking: split text or bytes for embedding, indexing, or context-window packing.

  • chunk_cdc: FastCDC content-defined byte chunking (edit-local, for dedup/sync)
  • chunk_text / chunk_text_iter: character-budget chunking, word/sentence-boundary aware, optional overlap
  • chunk_by_words / chunk_by_words_iter: fixed word-count chunks
  • chunk_by_sentences / chunk_by_sentences_iter: fixed sentence-count chunks
  • chunk_by_paragraphs: fixed paragraph-count chunks (blank-line heuristic)
  • chunk_hierarchical: priority-ordered fallback chunking (RecursiveCharacterTextSplitter pattern)

Information retrieval: lexical/statistical primitives for small-corpus search and integrity, without an embeddings dependency.

  • tf_idf: stateless TF-IDF term scoring per document
  • bm25_rank: Okapi BM25 reranking of a corpus against a query
  • simhash64 / simhash128: SimHash near-duplicate fingerprints
  • merkle_root / merkle_diff: domain-separated Merkle root and per-index chunk diff

Text-processing pipelines: batch preprocessing without a stateful pipeline object.

  • apply_pipeline: fused NFD/lowercase/accent-fold/stem/lemma/whitespace-collapse pass over a whole text list
  • CompiledLemmaDict: a build-once handle for a large lemma_dict, reused across calls

Examples

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')
text = (
    "This is sentence one. This is sentence two. "
    "This is sentence three. This is sentence four."
)
chunks = tors.chunk_by_sentences(text, 2)
# [(0, 44), (44, 90)]

[text[s:e] for s, e in chunks]
# ['This is sentence one. This is sentence two. ', 'This is sentence three. This is sentence four.']
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 three run against the built extension; the output above is what they actually return. For every function's full argument contract, error behavior, and more examples, see:

Design philosophy and non-goals

tors is a stateless library: every call does its own work from scratch, with nothing cached or built up across calls. That keeps every function simple to reason about and safe to call from anywhere: no handle to manage, no invalidation to think about, no surprise from a stale cache.

CompiledLemmaDict is the one narrow exception, and it's worth being precise about why. tf_idf, bm25_rank, and apply_pipeline accept an optional caller-supplied lemma_dict: a word -> lemma map. A raw dict[str, str] is re-materialized into a Rust HashMap on every call, and for a realistic multi-thousand- entry lemma table that cost is measured, not theoretical: roughly 1.4ms per call on a 20,000-entry map, independent of how much text the call actually processes, which can make repeated small calls slower than the equivalent pure-Python loop. CompiledLemmaDict builds that HashMap once and hands back an immutable, cheaply cloned handle, the same re.compile() shape the stdlib already uses for a comparable problem. It exists for exactly this one measured cost and does not reopen the case for a general pipeline object: nothing else in this library gets a persistent handle.

The scope cuts below are decisions, not oversights:

  • General regex. find_patterns/replace_many are leftmost-longest multi-pattern literal search, not a regex engine; chunk_hierarchical's separators are literal strings, not patterns.
  • Schema-aware JSON/YAML coercion. Out of scope for the same reason lemmatization is: it needs a schema or model, not an algorithm.
  • A bundled lemma dictionary. apply_pipeline/tf_idf/bm25_rank apply a caller-supplied lemma map; tors ships no lemma data of its own, because full lemmatization needs a per-language dataset or a POS-tagging model, not an algorithm; that is outside a text-operations library's job.
  • A persistent search index. bm25_rank recomputes corpus statistics from scratch on every call: the right shape for reranking a small, already-retrieved candidate set, the wrong shape for querying a large corpus repeatedly. Reach for a real search engine (tantivy, in Rust) for that; tors does not build or expose index objects.
  • A bespoke coroutine API. Every function already releases the GIL for its native pass, so the async surface is one thread dispatch per call (tors.aio, below) rather than a purpose-built event-loop integration.
  • A general persistent pipeline object. apply_pipeline re-describes and re-applies its steps on every call rather than compiling a reusable pipeline handle: see CompiledLemmaDict above for the one measured exception.

Beyond scope, a few limitations are worth stating plainly rather than glossing over:

  • SimHash is not cryptographic. It's a fast, uniformly-spreading voting hash, not a security primitive: two unrelated documents can coincidentally land close together, especially on short text, and there's no universal "near-duplicate" distance threshold; calibrate per deployment.
  • soundex/metaphone are English/Latin-script-oriented. Both pre-filter input to ASCII letters; accented and non-Latin characters are dropped, not encoded.
  • Chunking makes no retrieval-quality promise. Every chunker guarantees a mechanical contract (correct boundaries, genuine overlap when requested); none of them promises a particular chunk size or strategy helps any downstream model.
  • Segmentation is rule-based UAX #29 only. No dictionary segmentation for spaceless scripts (Thai, Khmer, Burmese, Japanese word breaks are a different, dictionary-based problem).
  • tf_idf/bm25_rank make no relevance claim. Both are correctly implemented, well-specified ranking formulas; neither promises retrieval quality for any particular corpus or query.

Async use

Releasing the GIL is not the same as not blocking: a native pass called directly from a coroutine still occupies that coroutine's own turn on the event loop for the call's full wall-clock duration. tors.aio is the pre-wired fix for the functions where that matters: await tors.aio.tf_idf(corpus) runs the native pass in a worker thread via asyncio.to_thread, and the event loop stays responsive for its whole duration.

It covers only the large-input-shaped functions (the chunking family, tf_idf, bm25_rank, diff_opcodes, diff_opcodes_lines, apply_pipeline, the normalize/finalize pipeline pair, the decode_utf8/finalize_utf8/ decode_utf16/b64_encode_bytes/b64_decode byte codecs, and truncate_ellipsis/strip_controls), not all of tors. Thread dispatch costs on the order of tens of microseconds: noise next to a millisecond-or-slower native pass over a real corpus or document, real overhead next to a microsecond-scale call over a short string. Wrapping every export would make the small, common calls slower through this module than through the plain sync spelling, for no benefit, so the rest of tors keeps exactly one spelling: call it directly from a coroutine when the input is small enough that the whole thing finishes in microseconds.

There is no size-based branch inside any wrapper, and there never will be: a function that sometimes runs inline and sometimes hops to a thread depending on its input is unpredictable from the caller's side and can silently block the loop when the heuristic misjudges. Every function in tors.aio always dispatches through asyncio.to_thread, unconditionally; the choice between the sync spelling and tors.aio is the caller's, made once at the call site, not a runtime guess. tests/test_aio.py pins this structurally (no branch in the wrapper body) as well as behaviorally (a heartbeat coroutine keeps ticking with worst gaps well under the call's own wall during a large diff_opcodes await).

The streaming iterator constructors (word_bounds_iter and siblings, including the chunking family's own chunk_text_iter/chunk_by_words_iter/chunk_by_sentences_iter) have no async twin: an iterator is not an awaitable shape, and draining one to a list inside a worker thread is exactly what the already-covered list-returning sibling does. The eager construction pass is the GIL-released part anyway, so the manual await asyncio.to_thread(lambda: list(tors.word_bounds_iter(text))) covers the streaming shape when it is genuinely needed. Signatures are identical to the sync spellings, pinned by tests/test_aio.py; the stub aio.pyi is generated by tools/gen_aio_stub.py.

Performance

Full measured tables (GIL heartbeat gaps under asyncio, wall-time races against the stdlib and difflib, and criterion throughput) live in the test suite itself (tests/test_gil_release.py, tests/test_performance.py) so every number stays reproducible and re-runnable: the summary here gives the shape, and the test files are the ledger.

The GIL release is the headline. Running tors.finalize on 12 MiB of prose in a background thread holds the asyncio event loop's worst heartbeat gap to 10–14 ms; the equivalent pure-Python pipeline (unicodedata.normalize + str.replace + re.sub + hashlib.sha256) holds it for 92–108 ms in the same thread placement: roughly 6–12× worse, and it blows past this project's own CI budget in every sample. At 32 MiB the gap widens further (17–21 ms vs 250–272 ms).

Already-normalized input is close to free. A quick-check fast path means normalize/finalize/nfc/nfkd on already-clean 12 MiB text return the original object with only a SIMD sentinel scan: 2.4–7.7 ms where a full pass costs 100+ ms (28–45× faster).

Where no stdlib equivalent exists, the comparison is against the real alternative. diff_opcodes diffs 256 KiB of mutated prose in ~4 ms against difflib's ~3 seconds (~750×); get_close_matches against 13,900 candidates runs in ~65–72 ms against difflib's ~3.4 s (~50×); find_patterns fills the gap left by pyahocorasick, which holds the GIL for its entire scan by design (no ALLOW_THREADS anywhere in its scan iterator); utf8_is_valid has no stdlib boolean primitive to race at all, so its record is absolute throughput: up to ~95 GiB/s at 12 MiB, memory-bound above L3 cache.

List-returning functions have a real, disclosed cost at scale. word_bounds on 12 MiB of prose (3.67M segments) holds the GIL for 428–497 ms just marshalling the returned list: a genuine cost of the list shape, not a bug. The _iter twins (word_bounds_iter, chunk_text_iter, find_patterns_iter, and friends) exist for exactly this: the same sequence, streamed, with each __next__ holding the GIL for one tuple instead of the whole list at once, and 2.1× faster in wall time as well, in the measured case.

Every number above traces to a specific measured cell; see the linked test files for methodology, corpus construction, and the full per-function tables.

Dependencies and licensing

The license gate is cargo deny check licenses advisories bans (make deny; the same three checks run in CI's lint job). The allowlist in deny.toml is permissive-only: MIT, Apache-2.0, BSD-2/3-Clause, ISC, Zlib, plus three documented additions, all permissive grants inside the gate's intent: Apache-2.0 WITH LLVM-exception (target-lexicon, a pyo3 build dependency: the LLVM-exception removes attribution obligations from Apache-2.0), Unicode-3.0 (unicode-ident, the permissive license the Unicode Consortium publishes the UCD data under), and 0BSD (enum-iterator/enum-iterator-derive, soundex/metaphone's rphonetic dependency's own dependencies: the BSD Zero Clause License is OSI-approved and public-domain-equivalent, strictly more permissive than plain MIT). No GPL/LGPL/AGPL/MPL, no unlicensed. Dual/tri-licensed crates are consumed via an allowed branch: notably r-efi (a getrandom dependency, dev tree only) offers LGPL-2.1-or-later as one branch of MIT OR Apache-2.0 OR LGPL-2.1-or-later; tors consumes it under MIT/Apache and the LGPL branch is never elected, which is exactly what cargo-deny's SPDX expression evaluation verifies.

Direct dependencies (the full transitive closure is machine-checked by the gate; the dev tree, criterion and friends, is included in the check):

crate version license role
pyo3 0.29.2 MIT OR Apache-2.0 the CPython extension layer (abi3-py310)
unicode-normalization 0.1.25 MIT OR Apache-2.0 NFC/NFD/NFKC/NFKD tables (Unicode 16.0.0)
unicode-segmentation 1.13.3 MIT OR Apache-2.0 UAX #29 grapheme/word tables (Unicode 17.0.0)
sha2 0.10.9 MIT OR Apache-2.0 finalize's SHA-256
const-hex 1.19.1 MIT OR Apache-2.0 digest hex encoding
base64 0.23.1 MIT OR Apache-2.0 RFC 4648 encode/decode core, simd-unsafe feature enabled (the crate's own AVX2/NEON kernels, runtime-detected with a scalar fallback: already-shipped, widely-exercised unsafe code upstream, not written in tors)
memchr 2.8.3 Unlicense OR MIT SIMD sentinel scans
simdutf8 0.1.5 MIT OR Apache-2.0 SIMD UTF-8 validity scan
similar 3.2.0 Apache-2.0 Myers diff engine for diff_opcodes
aho-corasick 1.1.5 Unlicense OR MIT leftmost-longest multi-pattern search engine
rs_merkle 1.5.0 Apache-2.0 OR MIT merkle_root/merkle_diff's tree structure (domain-separated SHA-256 Hasher supplied by tors, see merkle_impl.rs)
fastcdc 5.0.0 MIT chunk_cdc's FastCDC 2020 content-defined chunking
chardetng 1.0.0 Apache-2.0 OR MIT detect_encoding's guesser (the engine Firefox ships)
encoding_rs 0.8.35 (Apache-2.0 OR MIT) AND BSD-3-Clause the Encoding type chardetng's guess returns: already pulled in transitively by chardetng; named directly only to call .name() on it, no new package in the tree (the BSD-3-Clause conjunct is the WHATWG Encoding Standard data files' grant, inside the gate's BSD-3 allowance)
rust-stemmers 1.2.0 MIT OR BSD-3-Clause tf_idf's/bm25_rank's opt-in Snowball stemmer= knob (18 languages): pulls in serde/serde_derive as a non-optional dependency (an Algorithm enum derive, unused by tors's own call sites); recorded here because it is the one real transitive-weight addition in this table
rphonetic 4.0.0 Apache-2.0 soundex/metaphone's phonetic-code algorithms (an Apache Commons Codec port): pulls in enum-iterator/enum-iterator-derive (0BSD, the license-gate addition noted above), nom, and thiserror; tors pre-filters every input to ASCII letters before calling into it, working around a real, verified panic in the crate's own Soundex/DoubleMetaphone encoders on ordinary accented input (see the API docs)
criterion (dev) 0.5.1 Apache-2.0 OR MIT the benchmark harness
strsim (dev) 0.11.1 MIT differential oracle for levenshtein/jaro/jaro_winkler tests

Maintenance note (the spec's module decision): simdutf8's release line has been quiet since 2024-09-22 (0.1.5) while the repository itself stays active (commits into 2026-06): a mature, zero-dependency implementation of an algorithm that does not churn (UTF-8 validation), picked with that fact known and disclosed.

Maintenance note on similar (the same decision): Apache-2.0 only, actively maintained by mitsuhiko (the Flask author), and the diff engine behind insta, a crate with a large existing consumer base; no Python binding for it exists, so tors binds it directly for diff_opcodes.

Maintenance note on aho-corasick (the same decision): dual Unlicense OR MIT: the MIT branch is elected and recorded here (cargo-deny's SPDX expression evaluation verifies exactly that election, the same mechanism that handles memchr's Unlicense OR MIT, the same spelling). By BurntSushi, and the Aho-Corasick engine inside Rust's own regex crate: the most battle-tested implementation of this exact algorithm in the Rust ecosystem; it was already in the lock as a transitive dependency (criterion's regex) before find_patterns made it direct, so the dependency tree grew by zero packages.

Transitive closure at the last lock-state count (120 Cargo.lock entries including tors itself, i.e. 119 dependency packages incl. dev, re-derived with cargo metadata over the current lock): 74 MIT OR Apache-2.0, 12 MIT (fastcdc and strsim among them), 6 Apache-2.0 OR MIT, 5 Apache-2.0 (rphonetic, soundex/metaphone's crate, among them), 3 MIT/Apache-2.0 (criterion-plot, itertools, version_check), 3 Unlicense OR MIT (aho-corasick, memchr, winapi-util), 2 0BSD (enum-iterator/enum-iterator-derive, rphonetic's own dependencies: the license-gate addition above), 2 Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT (wasip2, wit-bindgen), 2 BSD-2-Clause OR Apache-2.0 OR MIT (zerocopy), 2 Unlicense/MIT (same-file, walkdir), 1 (Apache-2.0 OR MIT) AND BSD-3-Clause (encoding_rs, a direct dependency), 1 Zlib OR Apache-2.0 OR MIT (tinyvec), 1 MIT OR Apache-2.0 OR Zlib (tinyvec_macros), 1 Apache-2.0 WITH LLVM-exception (target-lexicon), 1 (MIT OR Apache-2.0) AND Unicode-3.0 (unicode-ident), 1 MIT OR Apache-2.0 OR LGPL-2.1-or-later (r-efi, the tri-license noted above), 1 Apache-2.0/MIT (rs_merkle: the closure's third spelling of a dual grant), and 1 MIT/BSD-3-Clause (rust-stemmers, a fourth spelling of the same dual-grant idea). Every one satisfies the allowlist.

Development

The fuzz/ crate drives the *_impl.rs cores with raw adversarial bytes via cargo-fuzz (libFuzzer): a bug class the hypothesis-based Python tests cannot reach, since they shape input around documented contracts rather than raw bytes. cargo fuzz run <target> -- -max_total_time=30 runs one target briefly (nightly toolchain and cargo install cargo-fuzz --locked required); targets assert the same invariants the Python gates pin, at raw-byte depth; crashes are minimized with cargo fuzz tmin. make fuzz-quick runs every target for 30s each; the weekly fuzz workflow runs the same set in CI. See CONTRIBUTING.md for the full setup.

Contributing

See CONTRIBUTING.md for setup, the pre-PR checklist (tests, clippy, fmt, ruff, the license gate), and commit-message conventions.

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.3.1.tar.gz (687.4 kB view details)

Uploaded Source

Built Distributions

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

tors-0.3.1-cp310-abi3-win_arm64.whl (976.5 kB view details)

Uploaded CPython 3.10+Windows ARM64

tors-0.3.1-cp310-abi3-win_amd64.whl (1.0 MB view details)

Uploaded CPython 3.10+Windows x86-64

tors-0.3.1-cp310-abi3-musllinux_1_2_x86_64.whl (1.5 MB view details)

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

tors-0.3.1-cp310-abi3-musllinux_1_2_aarch64.whl (1.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

tors-0.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB view details)

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

tors-0.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

tors-0.3.1-cp310-abi3-macosx_11_0_arm64.whl (1.1 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

tors-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

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

File metadata

  • Download URL: tors-0.3.1.tar.gz
  • Upload date:
  • Size: 687.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1.tar.gz
Algorithm Hash digest
SHA256 6c467b47a59e527a10c894632d18d1fea1527faadd23d16db15cf935a0393929
MD5 1ed8872610c1a15c8b511e660270fcc4
BLAKE2b-256 6bc36aa3dad6dc71b5a05cef0649ed59f24a7c5f7a873cdc692440b26c907783

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-win_arm64.whl
  • Upload date:
  • Size: 976.5 kB
  • Tags: CPython 3.10+, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 8d1a85a13d6209712c4c09a2baf8174853fdf2d9b2d25d69beac48dd4057d1ed
MD5 d3a1fbf5bd9c3e1e4245265b07243901
BLAKE2b-256 9ea9b01938ba6b5b1100017c2a5627f8f5cd2d0d78e87c59e0816d4dcd6d341e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.0 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 bc3e50117dcf7b103f5ea33b74250702a51b64225cf121fc05f14af4378bbf18
MD5 3b209dd8e0ab836223245a75fa7389ff
BLAKE2b-256 5e3435b37ae11e52d9d31d233e1ffd1a469995374cf4caad00abb5a738931096

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 edc084c451571196761b66877287fb9cef2bd60fb89b77cc351802bdc64380c7
MD5 d2cb195574e6ceb2d07799929e12b4cc
BLAKE2b-256 636d1ecbf6283396db1ebb5489d163f0c38450885709a0844d76547c75e26411

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 1856b473c80bf73b19eadad20fd242ce8816bab2571c45b4ca354339a8e146e3
MD5 2a55a3628c3a2b7653e0ed5838fb836a
BLAKE2b-256 44666c210634dcc051cc511b7e1fcf424248bb6b95ce7ab2800d261d39002b5f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 fc5f8fe42ffc0a4097c494a3c9761f8fdaabec933dcb20f478ae24aa1799fe54
MD5 4f93934feb47e48631dcd1c5e67c4e27
BLAKE2b-256 896f4ae2a45ee9720312b4aa558bbd4227e1482496b9adeb39d122b4af4b4b04

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 fc45eccb11f1907f1bafd8c36fda4ea4a7b853b90ec263e485737618654c8cb4
MD5 178b8cdd20783638f19cd272641a1ac7
BLAKE2b-256 551990722ab8383c528ab42f06333dbd2af14c687a40c6ee91c96e0f72d1a654

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c64a14f46db8a299cd8ccb56b1d1cc22486ea6d562053cdb00d550f88b2f4d67
MD5 d97d29757e9cb8984531ce2138d6ea33
BLAKE2b-256 d03be396e19a5a8e8850c86b6df06d855372d406dfaf6e6c419bb6442a01ec5e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tors-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.3.1-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d5e9707b31d4a15bc505ce1013dbe5fab170149fde936d337705ea33cd39b8e6
MD5 cb17b8c58510821294c7b73414bd327b
BLAKE2b-256 3a9aaa4c0610914f79d53bec63b90429428216d5ea894193ef9fab930c38de07

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.1

9 files

0.6.0

9 files

0.5.0

9 files

0.4.1

9 files

0.4.0

9 files

This release

0.3.1 This release

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