Claude Code for python-slugify: Python URL Slug Generation — Claude Skills 360 Blog
Blog / AI / Claude Code for python-slugify: Python URL Slug Generation
AI

Claude Code for python-slugify: Python URL Slug Generation

Published: March 1, 2028
Read time: 5 min read
By: Claude Skills 360

python-slugify converts text to URL-safe slugs with Unicode transliteration. pip install python-slugify. Basic: from slugify import slugify; slugify("Hello World!") → “hello-world”. Unicode: slugify("Héllo Wörld") → “hello-world”. CJK: slugify("今日は") → transliterated pinyin. slugify("München") → “munchen”. Separator: slugify("Hello World", separator="_") → “hello_world”. separator="" → “helloworld”. Max length: slugify("Long title here...", max_length=20) → truncated. max_length=50, word_boundary=True — truncate at word boundary. Lowercase: slugify("HELLO WORLD") → “hello-world” (lowercase by default). lowercase=False → preserve case. Stopwords: slugify("The quick brown fox", stopwords=["the","a","an"]) → “quick-brown-fox”. Regex: slugify("a!@#$b", regex_pattern=r"[^a-z0-9]+") → “a-b”. allow_unicode: slugify("münchen", allow_unicode=True) → “münchen” (preserves umlauts). Replacements: slugify("C++ language", replacements=[["C++","cpp"],["#","sharp"]]) → “cpp-language”. Pre/post: slugify(text, pre_process_list=[("&","and")], post_process_text=str.upper). Unique: append -2 -3 for duplicates. Django: from django.utils.text import slugify (built-in). AutoSlugField in django-autoslug. Jinja2: env.filters["slugify"] = slugify. Claude Code generates python-slugify URL builders, title normalizers, and slug uniqueness pipelines.

CLAUDE.md for python-slugify

## python-slugify Stack
- Version: python-slugify >= 8.0 | pip install python-slugify
- Basic: slugify("Hello World!") → "hello-world" — strips punctuation, lowercases
- Unicode: slugify("München") → "munchen" | allow_unicode=True to keep ä/ü/ö
- Options: separator="_" | max_length=50, word_boundary=True | lowercase=False
- Stopwords: slugify(title, stopwords=["the","a","an","of"]) — removes function words
- Replacements: replacements=[["C++","cpp"],["&","and"]] — apply before slugifying
- Jinja2: env.filters["slugify"] = slugify | {{ title | slugify }}

python-slugify URL Slug Pipeline

# app/slugs.py — python-slugify generation, uniqueness, and URL builders
from __future__ import annotations

import re
import unicodedata
from typing import Callable

from slugify import slugify


# ─────────────────────────────────────────────────────────────────────────────
# 1. Core slug helpers
# ─────────────────────────────────────────────────────────────────────────────

# Standard stopwords for title slugs
_STOPWORDS = [
    "a", "an", "the", "and", "or", "but", "in", "on", "at", "to",
    "for", "of", "with", "by", "from", "as", "is", "are", "was", "were",
]

# Common symbol replacements before slugification
_REPLACEMENTS = [
    ["&",  "and"],
    ["+",  "plus"],
    ["@",  "at"],
    ["#",  "number"],
    ["C++", "cpp"],
    ["C#",  "csharp"],
    ["f#",  "fsharp"],
]


def make_slug(
    text: str,
    max_length: int = 80,
    separator: str = "-",
    lowercase: bool = True,
    word_boundary: bool = True,
    stopwords: list[str] | None = None,
    allow_unicode: bool = False,
    replacements: list[list[str]] | None = None,
) -> str:
    """
    Generate a URL-safe slug from arbitrary text.
    max_length + word_boundary=True truncates at a whole word.
    allow_unicode=True keeps non-ASCII characters (é, ü, 日) in the slug.
    replacements: pre-process substitutions applied before transliteration.
    """
    return slugify(
        text,
        separator=separator,
        max_length=max_length,
        word_boundary=word_boundary,
        lowercase=lowercase,
        stopwords=stopwords or [],
        allow_unicode=allow_unicode,
        replacements=replacements or _REPLACEMENTS,
    )


def title_slug(title: str, max_length: int = 60) -> str:
    """
    Blog/post slug: strip stopwords, limit to ~60 chars at a word boundary.
    "The Quick Brown Fox Jumps Over The Lazy Dog"
    → "quick-brown-fox-jumps-over-lazy-dog"
    """
    return slugify(
        title,
        max_length=max_length,
        word_boundary=True,
        stopwords=_STOPWORDS,
        replacements=_REPLACEMENTS,
    )


def filename_slug(text: str) -> str:
    """
    Safe filename base (no extension): replaces spaces and special chars.
    "My Report (2024) — Final" → "my-report-2024-final"
    """
    return slugify(text, separator="-", max_length=120, word_boundary=True)


def username_slug(text: str) -> str:
    """
    Username-safe slug: lowercase, no separators, alphanumeric only + dash.
    "John Doe" → "john-doe"
    """
    return slugify(text, separator="-", max_length=40, regex_pattern=r"[^a-z0-9\-]+")


def tag_slug(tag: str) -> str:
    """Normalize a tag: "Python 3.x" → "python-3-x"."""
    return slugify(tag, max_length=50, word_boundary=False)


def category_slug(category: str) -> str:
    """Category slug: consistent lowercase with hyphens."""
    return slugify(category, max_length=60, word_boundary=True)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Unique slug generator
# ─────────────────────────────────────────────────────────────────────────────

def unique_slug(
    text: str,
    existing: set[str] | list[str],
    max_length: int = 60,
) -> str:
    """
    Generate a unique slug by appending -2, -3, ... until unique.
    existing: already-used slugs (from database or in-memory set).

    "python-guide" (exists) → "python-guide-2"
    "python-guide-2" (exists) → "python-guide-3"
    """
    existing_set = set(existing)
    base    = slugify(text, max_length=max_length - 4, word_boundary=True)
    slug    = base
    counter = 2
    while slug in existing_set:
        slug = f"{base}-{counter}"
        counter += 1
    return slug


class SlugRegistry:
    """
    In-memory unique slug registry.
    Use in bulk-import or export pipelines where you want unique slugs
    without hitting the database for every item.
    """

    def __init__(self) -> None:
        self._used: set[str] = set()

    def register(self, text: str, max_length: int = 60) -> str:
        """Register text and return a unique slug."""
        slug = unique_slug(text, self._used, max_length=max_length)
        self._used.add(slug)
        return slug

    def all(self) -> set[str]:
        return set(self._used)


# ─────────────────────────────────────────────────────────────────────────────
# 3. URL builders
# ─────────────────────────────────────────────────────────────────────────────

def build_post_url(title: str, post_id: int | str, base: str = "") -> str:
    """
    Build a blog post URL: /posts/{id}-{slug}
    e.g. /posts/42-quick-brown-fox
    """
    slug = title_slug(title)
    return f"{base}/posts/{post_id}-{slug}"


def build_product_url(name: str, sku: str, base: str = "") -> str:
    """Build a product URL: /products/{slug}-{sku}"""
    slug = make_slug(name, max_length=60)
    return f"{base}/products/{slug}-{sku.lower()}"


def build_breadcrumb_path(*parts: str) -> str:
    """
    Build a URL path from a sequence of strings.
    build_breadcrumb_path("Blog", "Technology", "Python") → "/blog/technology/python"
    """
    slugs = [slugify(p, max_length=40) for p in parts if p.strip()]
    return "/" + "/".join(slugs)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Slug validation
# ─────────────────────────────────────────────────────────────────────────────

_VALID_SLUG_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")


def is_valid_slug(slug: str) -> bool:
    """Return True if the string matches slug format (lowercase, hyphens only)."""
    return bool(_VALID_SLUG_RE.match(slug))


def normalize_incoming_slug(slug: str) -> str:
    """
    Normalize a user-submitted slug: re-slugify to strip any invalid characters.
    Useful when accepting slugs in API inputs.
    """
    return make_slug(slug)


# ─────────────────────────────────────────────────────────────────────────────
# 5. Jinja2 filter registration
# ─────────────────────────────────────────────────────────────────────────────

def register_slug_filters(env) -> None:
    """
    Register slugify as Jinja2 template filters.
    Usage:
      {{ post.title | slugify }}
      {{ tag.name | tag_slug }}
      {{ post.id ~ "-" ~ post.title | slugify }}
    """
    env.filters["slugify"]  = title_slug
    env.filters["tag_slug"] = tag_slug
    env.filters["cat_slug"] = category_slug
    env.filters["filename"] = filename_slug


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== Basic slugs ===")
    samples = [
        "Hello World!",
        "The Quick Brown Fox Jumps Over The Lazy Dog",
        "C++ Programming Language",
        "München & Beyond",
        "100% Pure Python",
        "   extra   spaces   ",
        "UPPER CASE TITLE",
        "Héllo Wörld — 2024 Edition",
        "python-already-a-slug",
    ]
    for s in samples:
        print(f"  {s!r:45}{make_slug(s)!r}")

    print("\n=== Title slugs (with stopwords) ===")
    titles = [
        "The Art of Python Programming",
        "A Guide to Unit Testing",
        "How to Build a REST API with FastAPI",
        "Introduction to Machine Learning",
    ]
    for t in titles:
        print(f"  {t!r:50}{title_slug(t)!r}")

    print("\n=== Unicode ===")
    for text, allow_uni in [
        ("München",   False),
        ("München",   True),
        ("日本語テスト",  False),
        ("hello-世界", False),
        ("Привет мир", False),
    ]:
        print(f"  {text!r:20} unicode={allow_uni}{make_slug(text, allow_unicode=allow_uni)!r}")

    print("\n=== Unique slugs ===")
    registry = SlugRegistry()
    posts = [
        "Python Guide",
        "Python Guide",   # duplicate
        "Python Guide",   # third
        "JavaScript Tips",
        "Python Guide",   # fourth
    ]
    for title in posts:
        slug = registry.register(title)
        print(f"  {title!r:25}{slug!r}")

    print("\n=== URL builders ===")
    print(f"  Post:    {build_post_url('Hello World', 42)}")
    print(f"  Product: {build_product_url('Blue Running Shoes', 'SKU-1234')}")
    print(f"  Path:    {build_breadcrumb_path('Blog', 'Technology', 'Python Tips')}")

For the django.utils.text.slugify alternative — Django’s built-in slugify() works for English text, handles basic Unicode ASCII conversion with allow_unicode=True, but lacks stopword removal, custom replacements (C++cpp), word-boundary truncation, and separator customization; python-slugify wraps Unidecode for better transliteration of CJK/Arabic/Cyrillic characters and exposes a richer options API for all these use cases. For the re.sub(r"[^a-z0-9]+", "-", text.lower()) approach — a raw regex can build slugs but fails on accented characters (é → not stripped, not transliterated), CJK (becomes ”---”), and edge cases (double hyphens, leading/trailing hyphens); python-slugify handles all of these correctly using Unicode normalization and the optional Unidecode transliteration. The Claude Skills 360 bundle includes python-slugify skill sets covering slugify() with separator/max_length/word_boundary/lowercase, allow_unicode=True for preserving ä/ü/ö, stopwords for removing function words, replacements for symbol substitution (C++ → cpp), title_slug() with stopwords, filename_slug(), username_slug(), tag_slug(), unique_slug() with counter suffix, SlugRegistry in-memory registry, build_post_url/build_product_url/build_breadcrumb_path URL builders, is_valid_slug() validation, and Jinja2 filter registration. Start with the free tier to try URL slug generation code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free