Skip to main content

slugany

CI PyPI version Python License: MIT Coverage Tests mypy ruff

A multi-language slugify library with zero runtime dependencies. MIT-licensed, fully typed, and audited for idempotency — a clean alternative to python-slugify with no GPL baggage.

381 tests · 100% coverage · mypy --strict clean · ruff clean · 13,800+ randomized idempotency checks passed

Why slugany?

python-slugify is the de facto standard, but it drags in text-unidecode (GPL) and over 1,000 lines of code. slugany was built from scratch to be:

python-slugify slugany
Runtime deps text-unidecode (GPL) Zero
License GPL MIT
Languages Limited Built-in: es, pt, de, fr, it
Caching No lru_cache built-in (512)
Typing Partial Fully typed, py.typed marker
Core size ~1,000+ lines ~550 lines
Idempotency Not guaranteed Guaranteed & tested
CLI Separate package Built-in

Features

  • Zero runtime deps — only the Python standard library
  • Multi-language transliteration — Spanish, Portuguese, German, French, Italian
  • 8 case styles — kebab, snake, camel, pascal, dot, train, filename, url
  • Smart punctuation — normalizes curly quotes, em-dashes, NBSP, zero-width chars, bullets
  • HTML entity decoding&& before processing
  • Emoji handling — strip, keep, or convert to text
  • Confusable detection — Cyrillic homoglyphs → Latin equivalents
  • Stopwords removal — filter out common words per language
  • Custom replacements — pre- and post-pipeline string substitution
  • CSS-safe slugs — prefix digit-leading slugs with s-
  • Max length with word boundaries — truncate without breaking words
  • Unicode preservationallow_unicode=True keeps non-ASCII chars
  • Fallback for empty slugs — never get an empty string
  • Built-in lru_cache — results cached automatically (maxsize=512)
  • CLI with stdin support — pipe text directly: echo "text" | slugany
  • Idempotentslugify(slugify(x)) == slugify(x), guaranteed and tested
  • Fully typed — type hints on every public API, py.typed marker (PEP 561)
  • ~550 lines core — auditable, no bloat

Installation

pip install slugany

Requires Python 3.11+. No runtime dependencies.

Quickstart

from slugany import slugify

slugify("¡Hola Mundo!")            # "hola-mundo"
slugify("Café résumé naïve")       # "cafe-resume-naive"
slugify("Ñandú coração")           # "nandu-coracao"
slugify("Über Straße", lang="de")  # "ueber-strasse"
slugify("Hello 🎉 World")          # "hello-world"

CLI

# Basic usage
slugany "Hello World"
# hello-world

# Pipe from stdin
echo "Café" | slugany
# cafe

# Case styles
slugany "hello world" --style camel
# helloWorld

slugany "hello world" --style train
# Hello-World

# Truncation with word boundary
slugany "hello-world-foo-bar" --max-length 10 --word-boundary
# hello-world

# Batch mode (one slug per line)
slugany --batch < input.txt

# CSS-safe slugs
slugany "123 main st" --css-safe
# s-123-main-st

Case Styles

slugify("hello world", style="kebab")     # "hello-world"
slugify("hello world", style="snake")     # "hello_world"
slugify("hello world", style="camel")     # "helloWorld"
slugify("hello world", style="pascal")    # "HelloWorld"
slugify("hello world", style="dot")       # "hello.world"
slugify("hello world", style="train")     # "Hello-World"
slugify("hello world", style="filename")  # "Hello-World"
slugify("hello world", style="url")       # "hello-world"

Languages

Built-in transliteration tables for five languages:

slugify("España", lang="es")          # "espana"
slugify("Coração", lang="pt")         # "coracao"
slugify("Über Straße", lang="de")     # "ueber-strasse"
slugify("Cœur", lang="fr")            # "coeur"
slugify("Caffè", lang="it")           # "caffe"

Advanced

from slugany import slugify, slugify_batch, is_slug

# Stopwords — remove common words
slugify("the quick brown fox", stopwords=["the", "fox"])  # "quick-brown"

# Custom replacements — substitute before and after transliteration
slugify("hello world", replacements={"hello": "hi"})  # "hi-world"
slugify("Straße", replacements={"ß": "ss"})           # "strass"

# Emoji handling
slugify("Hello 🎉 World", emoji_mode="strip")  # "hello-world"
slugify("Hello 🎉 World", emoji_mode="keep")   # "hello-world"

# CSS-safe — prefix digit-leading slugs for CSS class names
slugify("123 main st", css_safe=True)  # "s-123-main-st"

# Fallback — never get an empty string
slugify("!!!", fallback="untitled")  # "untitled"

# Unicode preservation — keep non-ASCII characters
slugify("Ñandú", allow_unicode=True)  # "ñandú"

# Max length with word boundary — truncate without breaking words
slugify("hello world foo bar", max_length=15, word_boundary=True)  # "hello-world"

# Batch processing
slugify_batch(["Hello World", "Café Résumé"])  # ["hello-world", "cafe-resume"]

# Validation
is_slug("hello-world")              # True
is_slug("hello world")              # False
is_slug("hello_world", separator="_")  # True
is_slug("hello-wörld", allow_unicode=True)  # True

# Cache inspection
from slugany import slugify
slugify.cache_info()   # CacheInfo(hits=0, misses=1, maxsize=512, currsize=1)
slugify.cache_clear()  # Clear the cache

Migration from python-slugify

slugany is designed as a drop-in replacement. The main difference is that all arguments are keyword-only:

# python-slugify
from slugify import slugify
slugify("Hello World", "_")
slugify("Hello World", separator="_", stopwords=["the"])

# slugany
from slugany import slugify
slugify("Hello World", separator="_")
slugify("Hello World", separator="_", stopwords=["the"])

See the migration guide for full details.

Documentation

Full documentation at mathiaspaulenko.github.io/slugany

Contributing

Contributions are welcome! See CONTRIBUTING.md for development setup, PR process, and code style guidelines.

Please read our Code of Conduct before participating.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slugany-0.2.0.tar.gz (37.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

slugany-0.2.0-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file slugany-0.2.0.tar.gz.

File metadata

  • Download URL: slugany-0.2.0.tar.gz
  • Upload date:
  • Size: 37.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for slugany-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1fe2dbb80d72363ea08fdf92c7638ebc904bcf1d029e9517405781b0fc11d0be
MD5 3d920b1d5d821e2b9c71184a009c229d
BLAKE2b-256 5c1e0d7add03e1269e2a6d6de18d58f73518eb1f3dc696a89236d0bd2a89ceb5

See more details on using hashes here.

Provenance

The following attestation bundles were made for slugany-0.2.0.tar.gz:

Publisher: release.yml on MathiasPaulenko/slugany

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file slugany-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: slugany-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for slugany-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bbc9da70bf1bbab59d8d28b5a794c62e6f9bac431ba0e3ac323c23491e4c8612
MD5 7eab657f100a25bcf2405b2e6f5fe545
BLAKE2b-256 8ee538926016b9516f1dd03af539af35338367f2e2e5d74a292f5c94e60450c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for slugany-0.2.0-py3-none-any.whl:

Publisher: release.yml on MathiasPaulenko/slugany

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page