Metadata-Version: 2.5
Name: prosemeter
Version: 0.1.2
Summary: Descriptive texture profiling for prose in Russian, English, Spanish and Italian — sentence rhythm, lexical texture and AI-tell descriptive markers, dependency-free
Project-URL: Homepage, https://github.com/GradeBuilderSL/prosemeter
Project-URL: Source, https://github.com/GradeBuilderSL/prosemeter
Project-URL: Issues, https://github.com/GradeBuilderSL/prosemeter/issues
Project-URL: Built by, https://laspi.pro/en
Author-email: "GradeBuilder S.L." <org@gradebuilder.tech>
License: MIT
License-File: LICENSE
Keywords: multilingual,prose,readability,rhythm,style,text-analysis
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Natural Language :: Italian
Classifier: Natural Language :: Russian
Classifier: Natural Language :: Spanish
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# prosemeter

[![CI](https://github.com/GradeBuilderSL/prosemeter/actions/workflows/ci.yml/badge.svg)](https://github.com/GradeBuilderSL/prosemeter/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/GradeBuilderSL/prosemeter/blob/main/LICENSE)
[![PyPI](https://img.shields.io/pypi/v/prosemeter)](https://pypi.org/project/prosemeter/)
[![Python](https://img.shields.io/pypi/pyversions/prosemeter)](https://pypi.org/project/prosemeter/)

**A ruler for prose texture** in Russian, English, Spanish and Italian.
Pure stdlib, zero dependencies.

`prosemeter` measures the surface texture of a text — sentence rhythm,
punctuation habits, lexical texture, voice — and hands you a flat dict of
numbers. That's the whole product.

It is explicitly **not an AI detector** and **not a quality metric**:
*it describes, it does not pass judgment*. Some of the features it reports (filler
density, contrast frames, opener repetition) happen to be popular
tells of generated or padded prose, but `prosemeter` attaches no verdict to
any value. What counts as "too much" depends on your corpus, your genre and
your taste — that's your call, on your data.

## Install

```
pip install prosemeter
```

## Usage

```python
from prosemeter import profile

text = (
    "The kettle clicked off and the kitchen went quiet. I had been rewriting "
    "the same paragraph for an hour, and it still read like a brochure. So I "
    "did what editors do: cut the openers, moved the numbers up front, and "
    "read it aloud. Better. Not perfect, but better."
)

print(profile(text))          # lang="auto" detects English here
print(profile(text, lang="en"))  # or say it explicitly
```

Real output (English adds five extra keys on top of the common core):

```python
{
  "words": 49, "sentences": 5,
  "mean_sentence_len": 9.8, "cv_sentence_len": 0.69,
  "pct_short": 0.4, "pct_long": 0.0, "staccato_share": 0.4,
  "anaphora_top_share": 0.2,
  "dash_per_100w": 0.0, "guillemet_per_100w": 0.0, "questions_per_sent": 0.0,
  "contrast_frames_per_100w": 0.0,
  "address_per_1k": 0.0, "first_person_per_1k": 40.8,
  "digit_density_per_1k": 0.0,
  "filler_per_100w": 0.0, "nominal_per_100w": 0.0,
  "mean_word_len": 4.14, "ttr": 0.8,
  "syllables_per_word": 1.33, "flesch_reading_ease": 84.7,
  "polysyllable_per_100w": 0.0, "passive_per_100sent": 0.0,
  "ly_adverbs_per_100w": 0.0
}
```

The public API is three names: `profile(text, lang="auto")`,
`SUPPORTED_LANGS` (`("ru", "en", "es", "it")`) and `__version__`.
`lang="auto"` uses a cheap script/function-word heuristic that is reliable
on a paragraph of ordinary prose; pass the language explicitly for short
fragments or mixed-language text.

## Features

Counts are ints; every other value is a rate, so profiles of texts of
different lengths are comparable. Rates are per word (`per_100w` /
`per_1k`), per sentence (`per_sent` / `per_100sent`) or a share in `0..1`.

| Key | What it measures | Languages |
| --- | --- | --- |
| `words` | Word count. | all |
| `sentences` | Sentence count (regex splitter — see *Honest limits*). | all |
| `mean_sentence_len` | Mean sentence length in words. | all |
| `cv_sentence_len` | Coefficient of variation (std/mean) of sentence length — the classic dispersion measure used as a rhythm signal. Metronomic prose sits low; prose alternating long and short sentences sits higher. | all |
| `pct_short` | Share of sentences with ≤ 6 words. | all |
| `pct_long` | Share of sentences with ≥ 25 words. | all |
| `staccato_share` | Share of stub sentences (≤ 4 words) — the "Short. Punchy. Fragments." register. | all |
| `anaphora_top_share` | Opener repetition: share of sentences beginning with the single most frequent opening word ("This… This… This…"). English baselines are naturally noisy (articles, pronouns). | all |
| `dash_per_100w` | Em/en dashes (plus the spaced hyphen used as a dash) per 100 words. Dash-for-everything punctuation vs. dash-as-rare-accent. | all |
| `guillemet_per_100w` | Guillemets («») per 100 words — quote/dialogue-heavy vs. plain expository prose. | all |
| `questions_per_sent` | Question marks per sentence — rhetorical-question density. | all |
| `contrast_frames_per_100w` | Density of the "it's not X, it's Y" contrast frame (per-language patterns). One is rhetoric; a high rate is a texture of its own. | all |
| `address_per_1k` | Second-person reader-address words per 1000 words. | all |
| `first_person_per_1k` | First-person words per 1000 words — personal voice. | all |
| `digit_density_per_1k` | Numeric tokens per 1000 words — concreteness of the text's factual grounding. | all |
| `filler_per_100w` | Hits from a per-language filler/officialese phrase list per 100 words ("it is important to note", «важно отметить», "cabe destacar", "va sottolineato"). The Russian list follows the officialese-hunting tradition of Nora Gal. | all |
| `nominal_per_100w` | Abstract nominalizations per 100 words, matched by derivational suffix ("-tion", "-ность", "-ción", "-zione") — Helen Sword's "zombie nouns", Nora Gal's hidden verbs. | all |
| `mean_word_len` | Mean word length in characters. | all |
| `ttr` | Type-token ratio — lexical diversity. Length-sensitive: only compare texts of similar length. | all |
| `syllables_per_word` | Mean syllables per word (heuristic counter). | en |
| `flesch_reading_ease` | Flesch Reading Ease (Rudolf Flesch, 1948): higher = plainer. | en |
| `polysyllable_per_100w` | Words with ≥ 4 syllables per 100 words. | en |
| `passive_per_100sent` | Passive constructions ("to be" + past participle, regex approximation) per 100 sentences — the Orwell / Strunk & White signal. | en |
| `ly_adverbs_per_100w` | `-ly` adverbs per 100 words — the modifier crutch Stephen King warns about. | en |

## Honest limits

Tokenization is regex-only: sentences split on runs of `.!?`, words on
per-language character classes. Abbreviations, decimals and ellipses can
over- or under-split. Over a paragraph or an article these errors average
out; do not trust the numbers on a single sentence, and do not compare
`ttr` across texts of very different lengths.

## Scope is frozen

This package does one thing: it measures. Bug reports (wrong counts, crash
on valid input, broken language detection) are very welcome. Feature
requests — new languages, new metrics, verdicts of any
kind — are out of scope for now.

## Built to power Laspi

<a href="https://laspi.pro/en">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/GradeBuilderSL/prosemeter/main/assets/laspi-logo-dark.png">
    <img src="https://raw.githubusercontent.com/GradeBuilderSL/prosemeter/main/assets/laspi-logo-light.png" alt="Laspi" width="220">
  </picture>
</a>

`prosemeter` is the measuring half of the writing engine behind
**[Laspi](https://laspi.pro/en)** — a marketing employee in a small-business
owner's phone. Laspi keeps a living memory of the business (every fact
confirmed by the owner), writes platform-native posts, images and short
videos from it, and looks after the business's visibility in AI answers —
in whatever language the business sells in. These metrics are part of how
Laspi keeps that writing sounding human.

The measuring is open source. The writing is the product: **[laspi.pro](https://laspi.pro/en)**.

## License

MIT © 2026 [GradeBuilder S.L.](https://laspi.pro)
