Python’s string module provides character-set constants and Template/Formatter classes. import string. Constants: string.ascii_letters = ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’; string.ascii_lowercase; string.ascii_uppercase; string.digits = ‘0123456789’; string.hexdigits = ‘0123456789abcdefABCDEF’; string.octdigits = ‘01234567’; string.punctuation = ’!”#$%&’()*+,-./:;<=>?@[\]^_{|}~'; string.printable= digits + letters + punctuation + whitespace;string.whitespace= ' \t\n\r\x0b\x0c'. Template:t = string.Template(“Hello $name”)— uses$identifieror${identifier}syntax;t.substitute(name=“Alice”)→ 'Hello Alice' (raises KeyError on missing keys);t.safe_substitute(name=“Alice”)→ leaves missing$varunchanged. Custom delimiter: subclass Template and setdelimiter = ”@“. t.template→ original string. Formatter:string.Formatter()— implementsstr.format()protocol;.format(fmt, *args, **kw); .parse(fmt)→(literal_text, field_name, format_spec, conversion)tuples;.vformat(fmt, args, kwargs)for pre-parsed calls. capwords:string.capwords(“hello world”)` → ‘Hello World’ (splits on whitespace, strips). Claude Code generates config file renderers, code generators, email templates, and table formatters.
CLAUDE.md for string
## string Stack
- Stdlib: import string
- Const: string.ascii_letters string.digits string.punctuation
- Tmpl: t = string.Template("Dear $name,"); t.substitute(name="Bob")
- Safe: t.safe_substitute(name="Bob") # no KeyError on missing vars
- Fmt: string.Formatter().format("{x:.2f}", x=3.14)
- Cap: string.capwords("hello world") # "Hello World"
string Constants and Template Pipeline
# app/strutil.py — charset helpers, password gen, Template rendering, Formatter
from __future__ import annotations
import re
import secrets
import string
from dataclasses import dataclass
from typing import Any, Mapping
# ─────────────────────────────────────────────────────────────────────────────
# 1. Character-set helpers
# ─────────────────────────────────────────────────────────────────────────────
def is_ascii_printable(s: str) -> bool:
"""
Return True if every character in s is in string.printable.
Example:
is_ascii_printable("Hello!") # True
is_ascii_printable("Héllo") # False
"""
return all(c in string.printable for c in s)
def contains_only(s: str, charset: str) -> bool:
"""
Return True if every character in s is in charset.
Example:
contains_only("abc123", string.ascii_letters + string.digits) # True
contains_only("abc-123", string.ascii_letters + string.digits) # False
"""
return all(c in charset for c in s)
def filter_chars(s: str, keep: str) -> str:
"""
Return s with only characters in the `keep` charset preserved.
Example:
filter_chars("Hello, World! 123", string.ascii_letters) # "HelloWorld"
filter_chars("ph: +1-555-0100", string.digits) # "15550100"
"""
return "".join(c for c in s if c in keep)
def count_char_types(s: str) -> dict[str, int]:
"""
Count characters by type: upper, lower, digit, punctuation, whitespace, other.
Example:
count_char_types("Hello, World 99!")
# {'upper': 2, 'lower': 8, 'digit': 2, 'punctuation': 2, 'whitespace': 1, 'other': 0}
"""
return {
"upper": sum(1 for c in s if c in string.ascii_uppercase),
"lower": sum(1 for c in s if c in string.ascii_lowercase),
"digit": sum(1 for c in s if c in string.digits),
"punctuation": sum(1 for c in s if c in string.punctuation),
"whitespace": sum(1 for c in s if c in string.whitespace),
"other": sum(1 for c in s if c not in string.printable),
}
def make_charset(
upper: bool = True,
lower: bool = True,
digits: bool = True,
punctuation: bool = False,
extra: str = "",
) -> str:
"""
Build a character set string from flags.
Example:
cs = make_charset(punctuation=True, extra="@#")
gen_token(16, cs)
"""
parts = []
if upper:
parts.append(string.ascii_uppercase)
if lower:
parts.append(string.ascii_lowercase)
if digits:
parts.append(string.digits)
if punctuation:
parts.append(string.punctuation)
if extra:
parts.append(extra)
return "".join(dict.fromkeys("".join(parts))) # deduplicate, preserve order
# ─────────────────────────────────────────────────────────────────────────────
# 2. Secure token and password generation
# ─────────────────────────────────────────────────────────────────────────────
def gen_token(length: int = 32, charset: str | None = None) -> str:
"""
Generate a cryptographically secure random token from charset.
Defaults to URL-safe base62 (letters + digits).
Example:
gen_token(24) # e.g. "K7gXm2Nq8pBzVrTsWdCf3h4Y"
gen_token(16, string.digits) # 16-digit numeric code
"""
cs = charset or (string.ascii_letters + string.digits)
return "".join(secrets.choice(cs) for _ in range(length))
def gen_password(
length: int = 16,
require_upper: bool = True,
require_lower: bool = True,
require_digit: bool = True,
require_punct: bool = True,
) -> str:
"""
Generate a random password satisfying the given character-type requirements.
Example:
gen_password(20) # secure 20-char password with all types
"""
required: list[str] = []
if require_upper:
required.append(secrets.choice(string.ascii_uppercase))
if require_lower:
required.append(secrets.choice(string.ascii_lowercase))
if require_digit:
required.append(secrets.choice(string.digits))
if require_punct:
required.append(secrets.choice(string.punctuation))
charset = make_charset(
upper=require_upper,
lower=require_lower,
digits=require_digit,
punctuation=require_punct,
)
pool = [secrets.choice(charset) for _ in range(length - len(required))]
chars = required + pool
# Shuffle in-place using secrets-derived order
for i in range(len(chars) - 1, 0, -1):
j = secrets.randbelow(i + 1)
chars[i], chars[j] = chars[j], chars[i]
return "".join(chars)
def gen_slug(base: str, length: int = 8) -> str:
"""
Append a short random suffix to base to create a unique slug.
Example:
gen_slug("user") # "user-k7gx2mNq"
"""
suffix = gen_token(length, string.ascii_lowercase + string.digits)
safe_base = filter_chars(base.lower().replace(" ", "-"), string.ascii_lowercase + string.digits + "-")
return f"{safe_base}-{suffix}" if safe_base else suffix
# ─────────────────────────────────────────────────────────────────────────────
# 3. Template rendering
# ─────────────────────────────────────────────────────────────────────────────
def render_template(template_str: str, mapping: Mapping[str, Any], safe: bool = True) -> str:
"""
Render a string.Template with the given mapping.
safe=True uses safe_substitute (missing vars left as-is).
safe=False uses substitute (raises KeyError on missing vars).
Example:
render_template("Hello $name, you have $count messages.", {"name": "Alice", "count": 3})
# "Hello Alice, you have 3 messages."
"""
t = string.Template(template_str)
return t.safe_substitute(mapping) if safe else t.substitute(mapping)
def render_template_file(path: str, mapping: Mapping[str, Any], safe: bool = True) -> str:
"""
Read a template file and render it with the given mapping.
Example:
html = render_template_file("templates/email.txt", {"user": "Alice", "link": "..."})
"""
import pathlib
content = pathlib.Path(path).read_text(encoding="utf-8")
return render_template(content, mapping, safe=safe)
def extract_template_vars(template_str: str) -> list[str]:
"""
Return all variable names referenced in a string.Template instance.
Example:
extract_template_vars("Dear $name, your order $order_id is ready.")
# ['name', 'order_id']
"""
pattern = re.compile(r"\$\{?([_a-z][_a-z0-9]*)\}?", re.IGNORECASE)
return list(dict.fromkeys(pattern.findall(template_str)))
class AtTemplate(string.Template):
"""string.Template subclass using @ as the delimiter instead of $."""
delimiter = "@"
def render_at_template(template_str: str, mapping: Mapping[str, Any]) -> str:
"""
Render an @-delimited template (useful when $ conflicts with shell or currencies).
Example:
render_at_template("Price: @price USD", {"price": "9.99"})
# "Price: 9.99 USD"
"""
return AtTemplate(template_str).safe_substitute(mapping)
# ─────────────────────────────────────────────────────────────────────────────
# 4. Formatter utilities
# ─────────────────────────────────────────────────────────────────────────────
def parse_format_fields(fmt: str) -> list[str]:
"""
Return all field names referenced in a str.format()-style format string.
Example:
parse_format_fields("{name:20s} {count:5d} {pct:.2%}")
# ['name', 'count', 'pct']
"""
fmt_obj = string.Formatter()
return [field for _, field, _, _ in fmt_obj.parse(fmt) if field is not None]
def safe_format(fmt: str, **kwargs: Any) -> str:
"""
Format fmt using only the provided kwargs; missing keys render as '{key}'.
Example:
safe_format("Hello {name}, score={score}", name="Alice")
# "Hello Alice, score={score}"
"""
class SafeDict(dict):
def __missing__(self, key: str) -> str:
return "{" + key + "}"
return fmt.format_map(SafeDict(kwargs))
def render_table(
rows: list[dict[str, Any]],
columns: list[str],
widths: list[int] | None = None,
) -> str:
"""
Render a list of dicts as a fixed-width text table.
Example:
rows = [{"name": "Alice", "score": 95}, {"name": "Bob", "score": 87}]
print(render_table(rows, ["name", "score"], [20, 8]))
"""
if not rows:
return ""
col_widths = widths or [max(len(str(r.get(c, ""))) for r in rows + [{"name": c}]) for c in columns]
fmt = " ".join(f"{{:<{w}}}" for w in col_widths)
header = fmt.format(*[c.upper() for c in columns])
sep = " ".join("-" * w for w in col_widths)
lines = [header, sep]
for row in rows:
lines.append(fmt.format(*[str(row.get(c, "")) for c in columns]))
return "\n".join(lines)
# ─────────────────────────────────────────────────────────────────────────────
# 5. String transformation helpers
# ─────────────────────────────────────────────────────────────────────────────
def capwords(s: str, sep: str | None = None) -> str:
"""
Capitalize each word in s using string.capwords.
Example:
capwords("hello world") # "Hello World"
capwords("hello-world", "-") # "Hello-World"
"""
return string.capwords(s, sep)
def slugify(s: str, sep: str = "-") -> str:
"""
Convert s to a URL-safe slug: lowercase, strip non-alnum to sep.
Example:
slugify("Hello World!") # "hello-world"
slugify(" Leading Spaces ") # "leading-spaces"
"""
s = s.lower().strip()
s = re.sub(r"[^a-z0-9]+", sep, s)
return s.strip(sep)
def truncate(s: str, max_len: int, suffix: str = "…") -> str:
"""
Truncate s to max_len characters, appending suffix if truncated.
Example:
truncate("Hello World", 8) # "Hello W…"
truncate("Hi", 8) # "Hi"
"""
if len(s) <= max_len:
return s
return s[: max_len - len(suffix)] + suffix
def pad_center(s: str, width: int, fillchar: str = " ") -> str:
"""Center s in a field of `width` characters."""
return s.center(width, fillchar)
def indent_block(s: str, prefix: str = " ") -> str:
"""Add prefix to every non-empty line in s."""
return "\n".join(prefix + line if line.strip() else line for line in s.splitlines())
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== string demo ===")
print("\n--- character constants ---")
print(f" ascii_letters[:10] : {string.ascii_letters[:10]}")
print(f" digits : {string.digits}")
print(f" punctuation[:10] : {string.punctuation[:10]}")
print("\n--- contains_only / filter_chars ---")
print(f" contains_only('abc123', alnum): {contains_only('abc123', string.ascii_letters + string.digits)}")
print(f" filter_chars('ph: +1-555-0100', digits): {filter_chars('ph: +1-555-0100', string.digits)}")
print("\n--- count_char_types ---")
sample = "Hello, World 99!"
print(f" count_char_types({sample!r}): {count_char_types(sample)}")
print("\n--- gen_token / gen_password ---")
print(f" gen_token(16) : {gen_token(16)}")
print(f" gen_password(20) : {gen_password(20)}")
print(f" gen_slug('user') : {gen_slug('user')}")
print("\n--- Template rendering ---")
tmpl = "Dear $name,\n\nYour order $order_id is ready for pickup.\n\nRegards,\n$sender"
rendered = render_template(tmpl, {"name": "Alice", "order_id": "ORD-7842", "sender": "Acme"})
print(rendered)
print(f"\n extract_template_vars: {extract_template_vars(tmpl)}")
print(f" render_at_template: {render_at_template('Price: @price USD (was @old_price)', {'price': '9.99', 'old_price': '14.99'})}")
print("\n--- safe_format ---")
print(f" safe_format: {safe_format('Hello {name}, score={score}', name='Alice')}")
print("\n--- render_table ---")
rows = [
{"name": "Alice", "score": 95, "grade": "A"},
{"name": "Bob", "score": 87, "grade": "B+"},
{"name": "Charlie", "score": 72, "grade": "C"},
]
print(render_table(rows, ["name", "score", "grade"]))
print("\n--- capwords / slugify / truncate ---")
print(f" capwords('hello world') : {capwords('hello world')}")
print(f" slugify('Hello World! 2025') : {slugify('Hello World! 2025')}")
print(f" truncate('Hello World', 8) : {truncate('Hello World', 8)}")
print(f" indent_block('line1\\nline2'):")
print(indent_block("line1\nline2"))
print("\n=== done ===")
For the re alternative — Python’s re module provides full regular-expression pattern matching and substitution for complex string parsing and transformation; string provides character-set constants that serve as building blocks for regex character classes ([ + string.ascii_letters + ]) and identity predicates — use re for pattern extraction, validation with complex rules, and multi-step text transformations; use string constants to define and document character sets explicitly, and string.Template when you need $variable substitution without the security risks of str.format() on untrusted input. For the jinja2 alternative — jinja2 (PyPI) provides a full template engine with loops, conditionals, inheritance, autoescaping, and macros for rendering HTML, YAML, and other formats; string.Template supports only simple $identifier substitution with no logic — use jinja2 for web templates, configuration generation with conditional blocks, and any template that requires iteration or logic; use string.Template for lightweight variable substitution in internal tooling and code generators where introducing a templating dependency is not warranted. The Claude Skills 360 bundle includes string skill sets covering is_ascii_printable()/contains_only()/filter_chars()/count_char_types()/make_charset() character-set helpers, gen_token()/gen_password()/gen_slug() secure generation, render_template()/render_template_file()/extract_template_vars()/AtTemplate/render_at_template() Template utilities, parse_format_fields()/safe_format()/render_table() Formatter tools, and capwords()/slugify()/truncate()/indent_block() string transforms. Start with the free tier to try string template patterns and string module pipeline code generation.