Case Converter — Python source
Convert text between UPPER, lower, Title, Sentence, camelCase, PascalCase, snake_case, kebab-case, CONSTANT_CASE and slug. Live, one-click copy.
This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.
"""case-converter — Python polyglot showcase port.
Pure, deterministic text-case transformations: split any input into word
tokens and re-emit it in upper / lower / title / sentence / camel / pascal /
snake / kebab / constant / slug case.
Ported from src/lib/case.ts (the canonical TypeScript that powers the live
Case Converter tool on dev.cosmolabs.org). Functionally equivalent — same
inputs produce the same outputs, including edge cases.
This is display source — part of CosmoDev's polyglot tool pages (one tool,
six language references). stdlib only, no third-party dependencies.
"""
from __future__ import annotations
import re
from typing import Callable, Dict, List, Optional
# --- Compiled patterns -------------------------------------------------------
# Each mirrors a regex from the TypeScript reference.
# A camelCase seam: a lowercase letter or digit immediately followed by an
# uppercase letter. We splice a space in here before splitting.
_CAMEL_BOUNDARY = re.compile(r"([a-z0-9])([A-Z])")
# Word delimiters: any run of whitespace, underscore, hyphen, or dot.
_SEPARATORS = re.compile(r"[\s_\-.]+")
# Anything that is not an ASCII letter or digit — stripped from each token.
_NON_ALNUM = re.compile(r"[^a-zA-Z0-9]")
# A title "word": a maximal run that starts with an ASCII word character and
# continues with non-whitespace. `re.ASCII` pins \w to [A-Za-z0-9_] so this
# matches JavaScript's /\w\S*/g (JS \w is ASCII-only) exactly.
_TITLE_WORD = re.compile(r"\w\S*", re.ASCII)
def words(s: str) -> List[str]:
"""Tokenize *s* into lowercase-friendly word units.
The pipeline reproduces the TypeScript reference step for step:
1. Insert a space at every camelCase boundary.
2. Split on runs of whitespace / underscore / hyphen / dot.
3. Strip remaining non-alphanumerics and drop empty tokens.
"""
spaced = _CAMEL_BOUNDARY.sub(r"\1 \2", s)
tokens: List[str] = []
for part in _SEPARATORS.split(spaced):
cleaned = _NON_ALNUM.sub("", part)
if cleaned: # filter(Boolean) in the TS source
tokens.append(cleaned)
return tokens
def cap(s: str) -> str:
"""Capitalize the first character and lowercase the rest."""
return s[:1].upper() + s[1:].lower()
def _title(s: str) -> str:
# Capitalize the first character of each maximal word-run. Characters
# between runs (spacing, punctuation) are left untouched.
return _TITLE_WORD.sub(lambda m: cap(m.group(0)), s)
def _sentence(s: str) -> str:
# Lowercase + trim the whole string, then uppercase only the first
# character. Empty input yields empty output.
t = s.lower().strip()
return t[:1].upper() + t[1:] if t else ""
# The case-transform registry: CaseId -> pure function. Keys mirror the
# TypeScript `caseTransforms` object 1:1.
case_transforms: Dict[str, Callable[[str], str]] = {
"upper": lambda s: s.upper(),
"lower": lambda s: s.lower(),
"title": _title,
"sentence": _sentence,
# camelCase: first word lowercase, every later word capitalized.
"camel": lambda s: "".join(
w.lower() if i == 0 else cap(w) for i, w in enumerate(words(s))
),
"pascal": lambda s: "".join(cap(w) for w in words(s)),
"snake": lambda s: "_".join(words(s)).lower(),
"kebab": lambda s: "-".join(words(s)).lower(),
"constant": lambda s: "_".join(words(s)).upper(),
"slug": lambda s: "-".join(words(s)).lower(),
}
# Valid case identifiers — the Python equivalent of the TS `CaseId` union.
CASE_IDS = tuple(case_transforms.keys())
def convert(case_id: str, s: str) -> str:
"""Apply a named case transform.
Convenience dispatcher over `case_transforms`; the registry itself remains
the primary public surface, matching the TypeScript reference.
Raises:
ValueError: if *case_id* is not a known case.
"""
try:
return case_transforms[case_id](s)
except KeyError as exc:
raise ValueError(f"Unknown case: {case_id}") from exc
def try_convert(case_id: str, s: str) -> Optional[str]:
"""Like `convert` but returns ``None`` for an unknown case id."""
fn = case_transforms.get(case_id)
return None if fn is None else fn(s)
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →