Metadata-Version: 2.4
Name: textly
Version: 1.1.0
Summary: Text Analytics - Calculate statistical features from text
Home-page: https://github.com/rajacsp/textly
Author: Raja CSP Raman
Author-email: raja.csp@gmail.com
License: MIT
Project-URL: Documentation, https://github.com/rajacsp/textly
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Text Processing
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: appdirs
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# textly — Text Analytics for Python

[![PyPI version](https://img.shields.io/pypi/v/textly.svg)](https://pypi.org/project/textly/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)

`textly` is a Python library for computing readability scores, grade levels, and text complexity metrics for English text. It works with any prose but is particularly tuned for scientific and technical writing.

> This library is built upon the foundation of the [`scireadability`](https://github.com/robert-roth/scireadability) Python library, but behaves differently.

## Installation

```bash
pip install textly
```

## Quick start

```python
import textly

text = (
    "Within the heterogeneous canopy of the Amazonian rainforest, "
    "a fascinating interspecies interaction manifests between "
    "Cephalotes atratus, a species of arboreal ant, and Epiphytes "
    "dendrobii, a genus of epiphytic orchids."
)

textly.flesch_reading_ease(text)
textly.flesch_kincaid_grade(text)
textly.smog_index(text)
textly.coleman_liau_index(text)
textly.automated_readability_index(text)
textly.dale_chall_readability_score(text)
textly.gunning_fog(text)
textly.text_standard(text)          # consensus grade across all formulas
textly.reading_time(text)           # in seconds (default 200 WPM)
```

## What makes textly different

- **CMUdict-driven syllable counting** — uses the Carnegie Mellon Pronouncing Dictionary, taking the minimum syllable count when multiple pronunciations exist.
- **Custom syllable dictionary** — user-editable overrides for domain-specific terms, jargon, and species names.
- **Regex fallback** — a refined heuristic counter tuned for scientific suffixes, agreeing with CMUdict ~91% of the time.
- **Token-based difficult word rates** — formulas like Dale–Chall and SPACHE use per-token analysis as the original research intended.
- **Consistent tokenization** — letter and character counting that works correctly with Coleman–Liau and similar formulas.

> English only. For multilingual support, consider [`textstat`](https://github.com/shivam5992/textstat).

## Readability formulas

| Function | What it returns |
|---|---|
| `textly.flesch_reading_ease(text)` | Score (higher = easier, up to ~121; negatives possible) |
| `textly.flesch_kincaid_grade(text)` | U.S. grade level from sentence length and syllables per word |
| `textly.gunning_fog(text)` | Grade level from sentence length and polysyllabic word percentage |
| `textly.smog_index(text)` | Grade level (best with ~30 sentences; returns 0.0 if < 3) |
| `textly.automated_readability_index(text)` | Grade level from characters per word and words per sentence |
| `textly.coleman_liau_index(text)` | Grade level from letters per word and sentences per word |
| `textly.linsear_write_formula(text)` | Grade level using the first 100 words |
| `textly.dale_chall_readability_score(text)` | Score based on difficult word percentage (see grade table below) |
| `textly.forcast(text)` | Grade level from single-syllable counts in a 150-word sample |
| `textly.spache_readability(text)` | Grade level for young readers |
| `textly.mcalpine_eflaw(text)` | Score for EFL (English as a Foreign Language) materials |
| `textly.lix(text)` | Swedish readability score (not grade-mapped) |
| `textly.rix(text)` | Grade level from long-word-to-sentence ratio |
| `textly.text_standard(text)` | Consensus grade from all formulas |
| `textly.reading_time(text, wpm=200.0)` | Estimated reading time in seconds |

### Dale–Chall grade bands

| Score | Reader level |
|---|---|
| ≤ 4.9 | 4th grade or below |
| 5.0–5.9 | 5th–6th grade |
| 6.0–6.9 | 7th–8th grade |
| 7.0–7.9 | 9th–10th grade |
| 8.0–8.9 | 11th–12th grade |
| ≥ 9.0 | College level |

### LIX score interpretation

| Score | Difficulty |
|---|---|
| < 30 | Very easy |
| 30–40 | Easy |
| 40–50 | Standard |
| 50–60 | Difficult |
| > 60 | Very difficult |

## Text statistics

```python
textly.syllable_count(text)                  # total syllables
textly.lexicon_count(text, removepunct=True)  # word count
textly.sentence_count(text)                   # sentence count
textly.char_count(text, ignore_spaces=True)   # character count
textly.letter_count(text, ignore_spaces=True) # alphabetic characters only
textly.polysyllabcount(text)                  # words with ≥3 syllables
textly.monosyllabcount(text)                  # words with 1 syllable

# Averages
textly.avg_sentence_length(text)
textly.avg_syllables_per_word(text)
textly.avg_character_per_word(text)
textly.avg_letter_per_word(text)
textly.avg_sentence_per_word(text)

# Difficult word analysis
textly.difficult_words(text)        # count
textly.difficult_words_list(text)   # list of tokens
textly.is_difficult_word("synapse") # True/False
textly.is_easy_word("dog")          # True/False
```

## Configuration

### Rounding

By default, scores are returned unrounded. You can control this globally or per call.

```python
# Global: round all subsequent calls to 2 decimal places
textly.set_rounding(True, points=2)

# Per-call: overrides the global setting for this call only
textly.flesch_kincaid_grade(text, rounding=True, points=1)
textly.flesch_reading_ease(text, rounding=False)  # unrounded regardless of global
```

### Apostrophe handling

```python
# Default: False (preserves apostrophes in contractions like don't, it's)
textly.set_rm_apostrophe(True)  # strips all apostrophes with other punctuation
```

Set this once at the start of your script — it affects all subsequent calls.

## Custom syllable dictionary

Override syllable counts for specialized vocabulary.

```python
textly.add_word_to_dictionary("pterodactyl", 4)
textly.add_words_from_file_to_dictionary("my_terms.json")
textly.overwrite_dictionary("full_replacement.json")
textly.revert_dictionary_to_default()
textly.print_dictionary()
```

Dictionary files use this JSON format:

```json
{
  "CUSTOM_SYLLABLE_DICT": {
    "pterodactyl": 4,
    "crispr": 2
  }
}
```

## Limitations

- SMOG requires ~30 sentences for reliable results; fewer than 3 returns 0.0.
- Short text snippets produce unstable scores across most formulas.
- Novel jargon may need custom dictionary entries for accurate syllable counts.
- The regex fallback is approximate by nature (~91% agreement with CMUdict).
- English only.

## Contributing

Found a bug or have an idea? Open an [issue](https://github.com/rajacsp/textly/issues).
Want to contribute code? Submit a [pull request](https://github.com/rajacsp/textly/pulls).

1. Fork the repo and create a branch.
2. Add tests for your changes.
3. Open a PR.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for release history.

## License

MIT
