pilcrow

By Steph12
Scroll to open

pilcrow

Paste from Word, a PDF, an email client or a CMS and you get sentences broken by hard line breaks, runs of double spaces, non-breaking spaces where they do not belong, and smart quotes that survived three round trips and then did not. This library turns that into clean paragraphs. It does not do anything else, and that is the whole design.

Who This Is For

You maintain something that accepts text a human pasted in from somewhere else. A CMS field, a contact form, a note-taking app, an import pipeline. You have written the same three regexes at least twice. They were subtly wrong both times. The bug that made you write them the second time came from a French document, where the non-breaking space before a colon is correct and must be kept.

Install

0%

pip install pilcrow

Python 3.9 or newer

No dependencies, and none planned. There is nothing else to install

Optional: pip install "pilcrow[cli]" if you want the pilcrow command

The One Thing It Does

normalise() takes a string and returns a string. It joins lines broken mid-sentence, keeps the breaks that were deliberate, collapses whitespace runs without touching indentation inside fenced blocks, and leaves typographic characters alone unless you ask otherwise.

How It Decides

A break is deliberate if the line before it ends in sentence punctuation, is shorter than the running median line length, or starts a list. Three rules, in that order. They are wrong for poetry, and poetry is out of scope in the README rather than in a heuristic.

Public functions

3

Runtime dependencies

0

Python versions in CI

3.9 to 3.13

Median first response on issues

4 days

Contributors

11

What It Will Not Do

It does not detect language, spellcheck, strip HTML, convert Markdown or guess what you meant. Each of those has been requested and declined, and the reasoning sits in the issue thread rather than behind a wontfix label. If you need HTML stripping, use something that actually parses HTML. A regex that half understands HTML is how the second bug on this page started.

"

I deleted sixty lines of accumulated regex and replaced them with one function call. The French quotation marks finally survived the round trip.

"

from issue 88

Your first run

0%

Call normalise(text) and read the output before you configure anything

If a break you wanted was removed, pass keep_breaks=True

If a non-breaking space you needed was removed, pass locale="fr"

Run pilcrow --diff against a real file from your corpus, not a synthetic one

Only then decide whether you need the other two functions

Where To Go Next

The README is the documentation. There is no separate docs site, because there is not enough surface area to justify one and a docs site that lags the README is worse than none at all. The API reference fits on one page. If something reads as unclear, that is a documentation bug and it is worth an issue.