Metadata-Version: 2.5
Name: prosemeter
Version: 0.1.1
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: Built to power, https://laspi.pro
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)](LICENSE)
[![Python 3.10–3.13](https://img.shields.io/badge/python-3.10%E2%80%933.13-blue)](https://github.com/GradeBuilderSL/prosemeter/actions/workflows/ci.yml)
<!-- These go live once the package is on PyPI (Release v0.1.0) / on Zenodo:
[![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/)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXXX.svg)](https://doi.org/10.5281/zenodo.XXXXXXX)
-->

**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">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="assets/laspi-logo-dark.png">
    <img src="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)** — a marketing employee in a small-business
owner's phone. Laspi keeps a living memory of the business (services, prices,
cases — every fact confirmed by the owner), writes platform-native posts,
images and short vertical videos from it, publishes researched articles, and
works on the business's visibility in AI answers: a machine-readable public
page that assistants can read and cite, plus regular checks of what ChatGPT,
Perplexity and Gemini actually recommend. Content ships in whatever language
the business sells in. These metrics are part of how Laspi keeps all that
writing sounding human.

The measuring is open source. The writing is the product:
**[start a free trial at laspi.pro](https://laspi.pro)** — no card needed.

## License

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