Skip to main content

thread-safety-claim-audit

Offline audit of documented thread-safety claims against Python source.

Version 0.1.0. A standard-library-only CLI for comparing documentation promises about concurrent safety with static synchronisation evidence. No audited code is executed, no network is used, and .env contents are never opened.

Usage

Requires Python 3.9 or later. Install and run:

python3 -m pip install .
thread-safety-claim-audit path/to/project
python3 -m thread_safety_claim_audit path/to/project --json
thread-safety-claim-audit . --verbose --ignore 'vendor/*' --fail-on HIGH
python3 -m thread_safety_claim_audit --version

The path defaults to the current directory. --ignore GLOB is repeatable and matches relative paths. --verbose includes evidence and verified families. --json emits an object with findings and verified_claims arrays. Findings contain rule, severity, family, claims, and evidence; claim records contain a path, line, sentence, and family. Evidence records contain a path, line, and kind, except the no-source sentinel. Verified records contain a family, claims, and evidence. Exit 0 means PASS; exit 2 means findings at or above the selected threshold; exit 1 means a usage, reading, or parsing error. The default threshold is MEDIUM. HIGH leaves MEDIUM findings visible while allowing exit 0.

Claims

Only root README.md and docs/**/*.md supply claims. Fenced code blocks are excluded; inline code counts as prose. Sentences are lowercased, n't becomes not, hyphens become spaces, and whitespace collapses. Exact phrases, with word boundaries, are matched; there is no optional-word expansion.

The complete normalized phrase vocabulary is below:

THREADSAFE:
thread safe
safe for concurrent use
safe to call from multiple threads
safe to use from multiple threads
safe to use concurrently
safe across threads
safe for multithreaded use
safe for multi threaded use
thread safe by design

NOTSAFE:
not thread safe
not safe for concurrent use
not safe to call from multiple threads
not safe to use from multiple threads
not safe to use concurrently
not safe across threads
not safe for multithreaded use
not safe for multi threaded use
no thread safety
must not be called from multiple threads
must not be used from multiple threads

NOTSAFE spans are matched and masked first to prevent substring overlap. Negation words (no, not, never, without, including normalized contractions) suppress THREADSAFE for the whole sentence; listed NOTSAFE phrases still count. This asymmetry means the following examples behave as shown:

is not thread-safe       => notsafe only
isn't thread-safe        => notsafe only
never thread safe       => no claim
does not claim to be thread safe => no claim
thread safety           => no claim (bare noun)
threadsafe              => no claim (one word)

Rules

Each family is evaluated independently, including contradictory documentation. Each finding lists all family claim sentences and all applicable evidence.

| Rule | Severity | Trigger |
| --- | --- | --- |
| `documented-thread-safe-claim-false` | HIGH | THREADSAFE with no production evidence |
| `documented-thread-safe-claim-false-test-only` | MEDIUM | THREADSAFE with evidence only in tests/fixtures |
| `documented-not-thread-safe-claim-false` | HIGH | NOTSAFE with production evidence |
| `documented-not-thread-safe-claim-false-test-only` | MEDIUM | NOTSAFE with evidence only in tests/fixtures |

No production Python source produces one HIGH finding per claimed family with no-source evidence. THREADSAFE with production evidence is verified; NOTSAFE with no evidence anywhere is verified. No claim yields PASS regardless of source evidence. There are no other reverse rules.

Named evidence

Kind Recognized syntax
lock-constructor Calls to threading Lock, RLock, Semaphore, BoundedSemaphore, Condition; multiprocessing Lock or RLock
thread-local Call to threading.local
acquire Attribute call .acquire(...) on any receiver
with-lock With context Name or Attribute whose name contains lock, case-insensitively

Module and from-import aliases are tracked per file. Bare constructor calls need a matching import from threading or multiprocessing (multiprocessing bare imports count only for Lock and RLock, matching the attribute form). Evidence is deduplicated by file, line, and kind. Production excludes tests directories, test filename patterns, fixtures, docs, examples, and symlinked files. Test filename patterns and tests/fixtures are collected separately; docs and examples never supply evidence. Symlinked directories are not traversed. Build outputs, virtual environments, and Git metadata are skipped. Ignored files supply neither claims nor evidence.

Differentiation

  • concurrency-claim-audit checks existence of parallel execution, not synchronisation.
  • determinism-claim-audit checks run-to-run stability, not concurrent safety.
  • security-claim-audit checks eval, shell, and pickle hazards, not locks.
  • bandit and ruff inspect code issues without comparing documentation promises.
  • The two PyPI neighbours named below are multi-language concurrency issue linters. This tool compares docs promises with source evidence.
thread-safety / thread-safe-check
  • Python threading documentation describes primitives rather than auditing documentation against implementation.

Honest limitations

This is a static heuristic, not a race detector or proof. Dead code counts: constructors in uncalled code still supply evidence. Receiver types, import shadowing, guard coverage, correct lock use, and actual shared-state mutation are not inferred. Even .acquire on a clearly unrelated object counts. A constructor does not prove that every shared access is guarded.

Bare threading imports and Thread imports provide no evidence. Thread, Pool, Executor creation and concurrent.futures use concern execution, not protection. asyncio.Lock is excluded because it is a single-threaded coroutine primitive. queue.Queue is excluded because its internal protection says nothing about the surrounding object. A bare Lock name, import, annotation, or attribute access without a call does not count. threading.Barrier and threading.Event are coordination primitives rather than mutual exclusion and are excluded. Dynamic imports and custom guard wrappers may be missed. Exact-phrase claims miss paraphrases. Markdown and sentence parsing are deliberately lightweight; claim line numbers identify the start of the containing prose paragraph. Invalid source or unreadable input results in exit 1 rather than a silent PASS.

Development

PYTHONPATH=src python3 -m unittest discover -s tests -v
python3 -m build

The sdist includes the regression fixtures. Runtime dependencies: none.

Metadata

Release files for thread-safety-claim-audit 0.1.0

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

Source distribution (sdist)

Source distribution for thread-safety-claim-audit 0.1.0
File Size Uploaded Provenance Actions
thread_safety_claim_audit-0.1.0.tar.gz 14.1 kB Without provenance Details for thread_safety_claim_audit-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for thread-safety-claim-audit 0.1.0
File Interpreter ABI Platform Actions
thread_safety_claim_audit-0.1.0-py3-none-any.whl Python 3 none any Details for thread_safety_claim_audit-0.1.0-py3-none-any.whl

Total release size: 23.0 kB

Release files / thread_safety_claim_audit-0.1.0.tar.gz

Download URL thread_safety_claim_audit-0.1.0.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5abba90db9cb3f3aa31fe4796fa8f10dfadebd852e3e82de51307e6b7896d38c
BLAKE2b-256 checksum
How to use checksums
2f8926f18b7f03ac5fd1983e785cb4a1c2cebfcbeab408eb4511bf1925777799
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / thread_safety_claim_audit-0.1.0-py3-none-any.whl

Download URL thread_safety_claim_audit-0.1.0-py3-none-any.whl
Size 8.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b5baa1f71a32d16b17751b88474c798cc88e4d20ac58b7d8f1057d9ab7bf648c
BLAKE2b-256 checksum
How to use checksums
dc66ffeb9d48685bfd03e38555228513a32b5b16d1190664a6ef1338e205c5f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for thread-safety-claim-audit 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.1.0 This release

2 release files No 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