TextUtils: Text Utilities#
- class my.utils.TextUtils.TextUtils#
Methods that clean, search, split, and otherwise interact with strings.
Parts of this class overlap in scope with
RegexStore(namelyregex_dict()), but ultimately present a much more lightweight interface for simple (or dependency-sensitive…) regex tasks. As with the store, all regex compiled through these methods supports theregexmodule’s extended regex syntax (see themy.regexdocs).
I Manipulation & Regex#
- static TextUtils.replace(string: str, *args: tuple[str | Pattern, str | Callable[[Match[str]], str]]) str#
Apply multiple regex replacements sequentially to a string.
- Parameters:
string – Input string to transform.
*args – Tuples of (pattern, replacement) for sequential application.
- Returns:
String with all replacements applied in order.
Examples
Chain several substitutions in one pass:
>>> from my import ut >>> ut.replace('a1b2', (r'\d', '#'), ('b', 'B')) 'a#B#'
- static TextUtils.split_into(text: str, pattern: str | Pattern, n: int = 2, rhs: bool = True) list[str]#
Split string using regex into exactly n parts, padding as needed.
- Parameters:
text – String to split.
pattern – Regex pattern to split by.
n – Exact number of parts to return (must be > 1).
rhs – If True, pad on right; if False, pad on left (default: True).
- Returns:
List of exactly n strings, padded with empty strings if needed.
- Raises:
AssertionError – If n <= 1 or split operation fails.
Examples
Split into exactly
nparts, padding when short:>>> from my import ut >>> ut.split_into('a:b:c', ':', 2) ['a', 'b:c'] >>> ut.split_into('a', ':', 3) ['a', '', '']
- static TextUtils.regex_dict(expressions: ~collections.abc.Mapping[K, V | ~_regex.Pattern] | None = None, compile_function: ~collections.abc.Callable[[V], ~_regex.Pattern] = <function compile>, sep: str = '', **kwargs: V | ~_regex.Pattern) dict[K, Pattern]#
Compile the expression strings in the given dictionary, mapping names to Patterns.
A string value may reference an earlier entry in the same call with
{{.name}}; the reference is substituted with that entry’s (already-compiled) pattern text before compiling. A non-string, non-Pattern value is treated as an iterable of strings and joined withsepbefore compiling (and before reference substitution).- Parameters:
expressions – A mapping of string names to regular expressions (compiled or otherwise).
compile_function – Function to compile patterns (default: re.compile).
sep – Separator used to join a list-of-strings value before compiling.
**kwargs – Additional named patterns to include.
- Returns:
The expressions mapping with all values now compiled.
- Raises:
AssertionError – If a
{{.name}}reference names a group not yet defined.ValueError – If a pattern fails to compile; the message reports the offending key and, where possible, the exact column.
Examples
Compile a named batch of patterns:
>>> from my import ut >>> rgxs = ut.regex_dict(word=r'\w+') >>> rgxs['word'].findall('a b') ['a', 'b']
Reference an earlier entry, and join a list of alternatives with
sep:>>> rgxs = ut.regex_dict(digit=r'\d', pair=r'{{.digit}}{{.digit}}') >>> bool(rgxs['pair'].fullmatch('42')) True >>> rgxs = ut.regex_dict(sep='|', either=['aa', 'bb']) >>> bool(rgxs['either'].fullmatch('bb')) True
- classmethod TextUtils.safe_compile(key: str, expr: str, fn: Callable[[str], Pattern[str]]) Pattern[str]#
Compile a regex pattern, falling back to a matches-nothing-useful pattern on failure.
Unlike
regex_dict(), a compilation failure here is reported (printed) rather than raised, so a caller assembling many independent patterns can keep going.- Parameters:
key – Name of the regex pattern (used only for the printed diagnostic).
expr – Regular expression to compile.
fn – Function to compile the pattern (e.g.
re.compile).
- Returns:
The compiled pattern, or a pattern compiled from the literal text
'ERROR'ifexprfailed to compile.
Examples
A valid pattern compiles and matches normally:
>>> import regex as re >>> from my import ut >>> bool(ut.safe_compile('ok', r'\w+', re.compile).fullmatch('abc')) True
- static TextUtils.regex_array(*args: tuple[str | Pattern, str]) list[tuple[Pattern, str]]#
- static TextUtils.regex_array(*args: tuple[str | Pattern, V], compile_function: Callable[[...], Pattern] = re.compile) list[tuple[Pattern, V]]
Compile the expressions in a list of two-tuples, mapping Patterns to strings.
- Parameters:
*args – (pattern, replacement) tuples to compile.
compile_function – Function to compile patterns (default: re.compile).
- Returns:
List of (compiled_pattern, replacement) tuples.
Examples
Compile substitution pairs ready for
replace():>>> from my import ut >>> pairs = ut.regex_array((r'\d+', 'N'), (r'\s+', '_')) >>> [(p.pattern, repl) for p, repl in pairs] [('\\d+', 'N'), ('\\s+', '_')]
- static TextUtils.multi_rgx(*expressions: str | list[str], branching: bool = False, sep: str = ' ?', pre: str = '', suf: str = '') str#
Combine expression clauses into a single alternating group that matches any of them.
- Parameters:
*expressions – Regex patterns to be combined.
branching – If True, use a branch-reset group (resets group names b/w branches).
sep – Separator for joining list patterns (default: ` ?`).
pre – Prefix to add before combined pattern (default: empty).
suf – Suffix to add after combined pattern (default: empty).
- Returns:
Combined regex pattern in group format
(?|...)or(?:...).
Examples
Combine alternatives into one non-capturing group:
>>> from my import ut >>> ut.multi_rgx('cat', 'dog') '(?:cat|dog)' >>> ut.multi_rgx(['a', 'b'], 'c') '(?:a ?b|c)'
II Formatting#
- static TextUtils.wrap(line: str, prefix: str = '', char: str = '-', width: int = 2) str#
Wrap a line of text with decorative borders.
- Parameters:
line – Text to wrap.
prefix – Prefix for each line (default: empty).
char – Character for border (default: ‘-‘).
width – Padding width on each side (default: 2).
- Returns:
Multi-line string with text wrapped in decorative borders.
Examples
Frame a headline:
>>> from my import ut >>> print(ut.wrap('Stage 1').strip()) ------------- -- Stage 1 -- -------------
- static TextUtils.indent(text: str, n: int = 4) str#
Indent all lines in text by n spaces.
- Parameters:
text – Text to indent.
n – Number of spaces to indent (default: 4).
- Returns:
Indented text, or original if n is 0.
Examples
Indent every line:
>>> from my import ut >>> print(ut.indent('a\nb', 2)) a b
- static TextUtils.unindent(text: str, n: int = 4) str#
Remove up to n*4 leading spaces from each line.
- Parameters:
text – Text to unindent.
n – Number of indent levels to remove (default: 4, removes up to 16 spaces).
- Returns:
Unindented text.
Examples
Peel off up to
nlevels of 4-space indentation:>>> from my import ut >>> print(ut.unindent(' a\n b', 1)) a b
- static TextUtils.strip_quotes(string: str) str#
Remove surrounding matched quotes and emphasis markers (
'"*_) from the string.- Parameters:
string – The text content to strip.
- Returns:
String with surrounding quotes/emphasis removed.
Examples
Strip matched quoting and emphasis pairs:
>>> from my import ut >>> ut.strip_quotes('"hello"') 'hello' >>> ut.strip_quotes("*'nested'*") 'nested'
- classmethod TextUtils.clean_string(string: str, case: 'lower' | 'none' | 'upper' = 'lower') str#
Fully clean and normalize a string for use as identifier or slug.
Applies unidecode, strips whitespace, cleans non-words, and applies case conversion.
- Parameters:
string – String to clean.
case – Case conversion - ‘lower’, ‘upper’, or ‘none’ (default: ‘lower’).
- Returns:
Cleaned and normalized string suitable for identifiers.
- Raises:
ImportError – If the optional
unidecodedependency is not installed.
Examples
Slugify arbitrary text:
>>> from my import ut >>> ut.clean_string('Héllo, World!') 'hello_world' >>> ut.clean_string("Zoë's Café", case='none') 'Zoes-Cafe'
- class my.utils.TextUtils.TextUtils.TextCase(*values)#
Enumeration of common text case styles, each able to
apply()itself to a string.- apply(text: str, _from: TextCase | None = None) str#
Apply this case style to
text, first splitting it into words.- Parameters:
text – Text to reformat.
_from – The text’s current case, when it isn’t plain whitespace-separated – required to split
KEBAB/SNAKE/PASCAL/CAMELinput correctly.
- Returns:
textreformatted to this case, or''iftextis blank.
- classmethod TextUtils.recase(string: str, to: TextCase | str = TextCase.SNAKE, _from: TextCase | str | None = None, clean: bool = True) str#
Convert a string between case styles (snake_case, camelCase, kebab-case, …).
- Parameters:
string – String to convert.
to – The target case (default:
SNAKE); a case name string is also accepted._from – The string’s current case – required for
KEBAB/SNAKE/PASCAL/CAMELinput, since those don’t split on whitespace.clean – Whether to also apply
_clean_nonwords()to the result (default: True).
- Returns:
stringreformatted to thetocase.
Examples
Convert between styles, with and without an explicit source case:
>>> from my import ut >>> ut.recase('hello world', to='kebab') 'hello-world' >>> ut.recase('helloWorld', to=ut.TextCase.SNAKE, _from=ut.TextCase.CAMEL) 'hello_world'
- classmethod TextUtils.from_pascal(string: str) str#
Convert a PascalCase string to snake_case.
Examples
>>> from my import ut >>> ut.from_pascal('MyClassName') 'my_class_name'
- classmethod TextUtils.to_pascal(string: str) str#
Convert a snake_case string to PascalCase.
Examples
>>> from my import ut >>> ut.to_pascal('my_class_name') 'MyClassName'
- classmethod TextUtils.to_words(text: str) list[str]#
Extract all words from text using regex word boundary matching.
- Parameters:
text – Text to extract words from.
- Returns:
List of word strings.
Examples
Pull out just the words:
>>> from my import ut >>> ut.to_words('Hello, world - again!') ['Hello', 'world', 'again']
- static TextUtils.line_num(article: str, pos: int | str) int#
Calculate line number from character position or substring.
- Parameters:
article – Text to search within.
pos – Character position (int) or substring to find (str).
- Returns:
Line number (1-indexed).
Examples
Locate by offset or by substring:
>>> from my import ut >>> ut.line_num('ab\ncd\nef', 4) 2 >>> ut.line_num('ab\ncd\nef', 'ef') 3
- static TextUtils.parse_domain(url: str, default: str = '') str#
Extract domain name from URL, removing ‘www.’ prefix.
- Parameters:
url – URL string to parse.
default – Default value if parsing fails (default: ‘’).
- Returns:
Domain name without ‘www.’ prefix, or default if parsing fails.
Examples
Extract the bare domain:
>>> from my import ut >>> ut.parse_domain('https://www.example.com/page?q=1') 'example.com' >>> ut.parse_domain('not a url', default='n/a') 'n/a'
- static TextUtils.wrap_paragraphs(text: str, width: int = 100) str#
Wrap text to specified width, breaking on whitespace.
- Parameters:
text – Text to wrap.
width – Maximum line width (default: 100).
- Returns:
Wrapped text.
Examples
Hard-wrap prose at a fixed width:
>>> from my import ut >>> ut.wrap_paragraphs('one two three four', width=9) 'one two\nthree\nfour'
- classmethod TextUtils.unwrap_paragraphs(text: str) str#
Unwrap and normalize paragraph text, joining wrapped lines intelligently.
Handles hyphenated line breaks, prose detection, and comment prefixes.
- Parameters:
text – Text with potentially wrapped paragraphs.
- Returns:
Unwrapped text with proper spacing and line breaks.
Examples
Rejoin hard-wrapped prose while preserving non-prose lines:
>>> from my import ut >>> ut.unwrap_paragraphs('This line was\nhard-wrapped by an\neditor.\n\n- a bullet') 'This line was hard-wrapped by an editor.\n\n- a bullet'