Coverage for mcpgateway / utils / create_slug.py: 100%
13 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
1# -*- coding: utf-8 -*-
2"""Location: ./mcpgateway/utils/create_slug.py
3Copyright 2025
4SPDX-License-Identifier: Apache-2.0
5Authors: Manav Gupta
7Slug generation utilities for MCP Gateway.
8This module provides utilities for creating URL-friendly slugs from text.
9It handles Unicode normalization, special character replacement, and
10contraction processing to generate clean, readable slugs.
11"""
13# Standard
14import re
15from unicodedata import normalize
17# First-Party
18from mcpgateway.config import settings
20# Helper regex patterns
21CONTRACTION_PATTERN = re.compile(r"(\w)[''](\w)")
22NON_ALPHANUMERIC_PATTERN = re.compile(r"[\W_]+")
24# Special character replacements that normalize() doesn't handle well
25SPECIAL_CHAR_MAP = {
26 "æ": "ae",
27 "ß": "ss",
28 "ø": "o",
29}
32def slugify(text: str) -> str:
33 """Make an ASCII slug of text.
35 Args:
36 text(str): Input text
38 Returns:
39 str: Slugified text
41 Examples:
42 Basic slugification:
43 >>> slugify("Hello World")
44 'hello-world'
45 >>> slugify("Test-Case_123")
46 'test-case-123'
48 Handle special characters:
49 >>> slugify("Café & Restaurant")
50 'cafe-restaurant'
51 >>> slugify("user@example.com")
52 'user-example-com'
54 Handle contractions:
55 >>> slugify("Don't Stop")
56 'dont-stop'
57 >>> slugify("It's Working")
58 'its-working'
60 Edge cases:
61 >>> slugify("")
62 ''
63 >>> slugify(" ")
64 ''
65 >>> slugify("---test---")
66 'test'
67 >>> slugify("Multiple Spaces")
68 'multiple-spaces'
70 Unicode normalization:
71 >>> slugify("Naïve résumé")
72 'naive-resume'
73 >>> slugify("Zürich")
74 'zurich'
75 """
76 # Make lower case and delete apostrophes from contractions
77 slug = CONTRACTION_PATTERN.sub(r"\1\2", text.lower())
78 # Convert runs of non-alphanumeric characters to single hyphens, strip ends
79 slug = NON_ALPHANUMERIC_PATTERN.sub(settings.gateway_tool_name_separator, slug).strip(settings.gateway_tool_name_separator)
80 # Replace special characters from the map
81 for special_char, replacement in SPECIAL_CHAR_MAP.items():
82 slug = slug.replace(special_char, replacement)
83 # Normalize the non-ASCII text to ASCII
84 slug = normalize("NFKD", slug).encode("ascii", "ignore").decode()
85 return slug