stopslop
Measure a paper's prose against how a venue actually writes — before and after LLMs existed.
Two baselines, always both: fossil (published up to 2022, so it cannot have been machine-assisted) and current (the last full year plus this one). The fossil window is the target; the current window is context, never an excuse. See UNSLOP.md.
Install
The wheel carries the baselines for all seven venues — 105 venue-years of measurements — so there is nothing to crawl and nothing to configure.
pip install stopslop # pypdf, BSD-3-Clause, no copyleft
pip install "stopslop[pymupdf]" # adds PyMuPDF (AGPL-3.0, opt-in)
pip install -e . # editable, for hacking on it
Then, from any directory, in any project:
stopslop check paper.pdf --venue tmlr --out report.md
No system tools required. Everything else is stdlib re and statistics.
Extractors
The extractor is part of the measurement. Across 24 papers the four
supported extractors agree to within ~1% on prose metrics — first person
18.1–18.3, -ly adverbs 13.2–13.4, passives 10.3–10.5 — and disagree sharply on
layout-sensitive ones: numerals runs 43.4 under pymupdf against 61.4
under pypdf. So measurements are stored per extractor and never mixed, and
stopslop check refuses rather than compare across them.
| extra | licence | speed | notes |
|---|---|---|---|
(default) pypdf |
BSD-3-Clause | 0.82 s/paper | pure Python, installs anywhere |
[pymupdf] |
AGPL-3.0 or commercial | 0.12 s/paper | best quality; opt-in, never a default |
[pypdfium2] |
BSD-3 / Apache-2.0 | 0.10 s/paper | fastest; platform binary wheels |
[pdfminer] |
MIT | 2.13 s/paper | slowest |
Baselines for all of them ship in the base wheel, so an extra only decides
what you can measure with, never what you can read. Pick one with
--extractor or $UNSLOP_EXTRACTOR.
pip install stopslop carries no copyleft: PyMuPDF is AGPL and is opt-in, so
taking on that licence is always your deliberate choice.
Use
stopslop check paper.pdf --venue tmlr --out reports/paper.md # score one paper
stopslop trend tmlr --out reports/tmlr_trend.md # one venue's drift
stopslop crosstrend --out reports/total_trend_points.md # venues kept apart
stopslop pooltrend --out reports/total_trend.md # venues pooled
stopslop stats tmlr 2022 # one crawled year
stopslop crawl tmlr 2022 2025 2026 --limit 20 # fetch and measure
crawl is the only command that touches the network, and it needs no key or
account. Everything else is offline.
Where the baselines come from
Resolved at call time, most specific first:
--root DIRon any command$UNSLOP_MEASUREMENTS./datain the working directory, if it exists- the measurements bundled in the package
So a project that measures its own papers into ./data uses them
automatically, and everyone else gets the shipped ones. PDFs are cached
separately, under --pdf-root / $UNSLOP_PDFS / ./corpus.
Plots
--plot needs matplotlib, which is not a dependency and not an extra.
Plotting is an analytics concern and the measurement path has no use for it, so
it goes in a separate environment:
python3 -m venv .venv-analytics
.venv-analytics/bin/pip install -e . -r analytics-requirements.txt
.venv-analytics/bin/stopslop trend tmlr --plot --out reports/tmlr_trend.md
Every other command works without it.
Development
pip install -e ".[pymupdf,pypdfium2,pdfminer]" --group dev
pytest
stopslop remeasure --extractor pypdf --root src/stopslop/data # offline, no network
Measurements are generated artifacts and are written straight into the
package, so there is no snapshot to keep in sync. corpus/ is a pure PDF
cache: large, re-fetchable, and entirely gitignored. remeasure re-reads those
PDFs under a chosen extractor without touching the network — crawl would
re-query arXiv for a paper list we already have.
Layout
src/stopslop/
extractors/ pypdf | pymupdf | pypdfium2 | pdfminer
data/<extractor>/<venue>/<year>/stats.json
the measurements, shipped in the wheel
corpus/<venue>/<year>/*.pdf
PDF cache: 3.7 GB, gitignored, re-fetchable
reports/ generated trend reports and checked papers
tests/
Status
Pre-release. Published to PyPI as stopslop: the obvious names, unslop
and deslop, are both taken by humanizers, which do the opposite of what this
tool is for --- it measures and locates, it never edits your paper. Every
candidate that was checked is in "The rename" in UNSLOP.md, and the
remaining release blockers are in the Decision section of
COMPETITION.md.
MIT licensed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stopslop-0.1.0.tar.gz.
File metadata
- Download URL: stopslop-0.1.0.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7659f84a7430be5a61d15c415ac51335cc498b66e38b5fb4de8186967d737d90
|
|
| MD5 |
502df1b1c7350993af593157a4915c61
|
|
| BLAKE2b-256 |
b76d93d3c1d646c1295238bc15f1fd431d6c1f1d32608ccf42dbae67759f7ffb
|
File details
Details for the file stopslop-0.1.0-py3-none-any.whl.
File metadata
- Download URL: stopslop-0.1.0-py3-none-any.whl
- Upload date:
- Size: 1.6 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
623347128d7c216ab07ee34e64a07251890ac01f7e842d20b026924e29c9569f
|
|
| MD5 |
a6ce9258b6c3af4d735225c0a18ba3b3
|
|
| BLAKE2b-256 |
ce6bdbd257906e3fd5aa9024201ae8eac30a948b9146c74bac835d0db2fa7d9d
|