python_arango_ogm.utils.str_util

  1from copy import copy
  2import re
  3import textwrap
  4
  5from enum import StrEnum, auto
  6from typing import Iterable
  7
  8CR = '\n'
  9LF = '\n'
 10EMPTY = ''
 11INDENT = '  '
 12QUOTE = '"'
 13SPACE = ' '
 14SCORE = '_'
 15
 16RE_COMBINE_WHITESPACE = re.compile(r"\s+")
 17RE_ENCLOSE_WS_BEFORE = re.compile(r"(\s+)([\{\[\(])")
 18RE_ENCLOSE_NO_WS_BEFORE = re.compile(r"(\S)([\}\]\)])")
 19RE_ENCLOSE_NO_WS_AFTER = re.compile(r"([\{\[\(\}\]\)])(\S)")
 20RE_ENCLOSEEMPTY = re.compile(r"([\{\[\(])(\s+)([\}\]\)])")
 21RE_TOSNAKECASE = re.compile(r"(?:(?<=[a-z])(?=[A-Z]))|[^a-zA-Z]")
 22RE_CAPITALIZED = re.compile(r"(\s+)([A-Z])")
 23RE_CAPITAL = re.compile(r"([A-Z]+)")
 24RE_NOT_ALPHNUM_SCORE = re.compile(r"[^\w+]")
 25
 26RE_NOT_ALPHNUM = re.compile(r"[^\w]+")
 27RE_NOT_ALPHNUM_SPACE = re.compile(r"[^a-zA-Z0-9\s]+")
 28RE_NOT_ALPHNUM_STRICT = re.compile(r"[^a-zA-Z0-9]+")
 29
 30
 31class QuoteEnum(StrEnum):
 32    """ Quote Enum """
 33    SINGLE = auto()
 34    DOUBLE = auto()
 35    TRIPLE = auto()
 36
 37
 38QUOTE_TYPES = {
 39    QuoteEnum.SINGLE: "'",
 40    QuoteEnum.DOUBLE: '"',
 41    QuoteEnum.TRIPLE: '"""',
 42}
 43
 44
 45class SurroundEnum(StrEnum):
 46    QUOTE_SINGLE = auto()
 47    QUOTE_DOUBLE = auto()
 48    QUOTE_TRIPLE = auto()
 49    BRACE = auto()
 50    BRACKET = auto()
 51    PARENS = auto()
 52
 53
 54SURROUND_TYPES = {
 55    SurroundEnum.QUOTE_SINGLE: ("'", "'"),
 56    SurroundEnum.QUOTE_DOUBLE: ('"', '"'),
 57    SurroundEnum.QUOTE_TRIPLE: ('"""', '"""'),
 58    SurroundEnum.BRACE: ('{', '}'),
 59    SurroundEnum.BRACKET: ('[', ']'),
 60    SurroundEnum.PARENS: ('(', ')'),
 61}
 62
 63
 64def indent(text, amount=4, pre=''):
 65    txt = f"{pre} {text}" if pre else str(text)
 66    return textwrap.indent(txt, INDENT * amount)
 67
 68
 69def prefix(text, pre):
 70    return f"{pre}{text}" if text else copy(EMPTY)
 71
 72
 73def pluralize(val):
 74    s = str(val).strip()
 75
 76    if s:
 77        last_char = s[-1]
 78        if last_char == 'y':
 79            p = s[:-1] + 'ies'
 80        elif last_char == 's':
 81            p = s + 'es'
 82        else:
 83            p = s + 's'
 84    else:
 85        p = copy(EMPTY)
 86
 87    return p
 88
 89
 90def collapse(ary, join_char=LF):
 91    return join_char.join(ary)
 92
 93
 94def surround(text: str, surround_with: SurroundEnum = SurroundEnum.PARENS):
 95    if not text:
 96        return copy(EMPTY)
 97
 98    stype = SURROUND_TYPES[surround_with]
 99    return "{o}{text}{c}".format(o=stype[0], c=stype[1], text=text)
100
101
102def surround_all(iterable: Iterable, surround_with: SurroundEnum = SurroundEnum.PARENS) -> Iterable:
103    return [surround(t, surround_with=surround_with) for t in iterable]
104
105
106def squish_text(text, format_braces=True):
107    squished = RE_COMBINE_WHITESPACE.sub(SPACE, text).strip()
108    if format_braces:
109        squished = RE_ENCLOSE_WS_BEFORE.sub(r'\g<2>', squished)
110        squished = RE_ENCLOSE_NO_WS_BEFORE.sub(r'\g<1> \g<2>', squished)
111        squished = RE_ENCLOSE_NO_WS_AFTER.sub(r'\g<1> \g<2>', squished)
112        squished = RE_ENCLOSEEMPTY.sub(r'\g<1>\g<3>', squished)
113        squished = RE_COMBINE_WHITESPACE.sub(SPACE, squished).strip()
114
115    return squished
116
117
118def score_text(text):
119    return RE_COMBINE_WHITESPACE.sub('_', text.strip()).lower()
120
121
122def combine_space(text):
123    return RE_COMBINE_WHITESPACE.sub(SPACE, text.strip())
124
125
126def snake_text(text, lower: bool = True):
127    def score_cap(txt: str, position:int):
128        return txt if not (txt.isupper() and position) else f"_{txt}"
129    # Separate capitalized words:
130    terms = RE_CAPITAL.split(text)
131    terms.remove('')
132
133    snaked = [score_cap(t, i) for i, t in enumerate(terms) if t]
134    txt = "".join(snaked)
135    return txt.lower() if lower else txt
136
137
138def capitalize_text(text):
139    txt = str(text).strip()
140    return txt if txt.isupper() else txt.capitalize()
141
142
143def title_text(text):
144    txt = RE_CAPITAL.sub(r" \1", text)
145    terms = RE_NOT_ALPHNUM_STRICT.split(txt)
146    clean_terms = filter(None, terms)
147
148    words = [capitalize_text(t) for t in clean_terms]
149    return SPACE.join(words)
CR = '\n'
LF = '\n'
EMPTY = ''
INDENT = ' '
QUOTE = '"'
SPACE = ' '
SCORE = '_'
RE_COMBINE_WHITESPACE = re.compile('\\s+')
RE_ENCLOSE_WS_BEFORE = re.compile('(\\s+)([\\{\\[\\(])')
RE_ENCLOSE_NO_WS_BEFORE = re.compile('(\\S)([\\}\\]\\)])')
RE_ENCLOSE_NO_WS_AFTER = re.compile('([\\{\\[\\(\\}\\]\\)])(\\S)')
RE_ENCLOSEEMPTY = re.compile('([\\{\\[\\(])(\\s+)([\\}\\]\\)])')
RE_TOSNAKECASE = re.compile('(?:(?<=[a-z])(?=[A-Z]))|[^a-zA-Z]')
RE_CAPITALIZED = re.compile('(\\s+)([A-Z])')
RE_CAPITAL = re.compile('([A-Z]+)')
RE_NOT_ALPHNUM_SCORE = re.compile('[^\\w+]')
RE_NOT_ALPHNUM = re.compile('[^\\w]+')
RE_NOT_ALPHNUM_SPACE = re.compile('[^a-zA-Z0-9\\s]+')
RE_NOT_ALPHNUM_STRICT = re.compile('[^a-zA-Z0-9]+')
class QuoteEnum(enum.StrEnum):
32class QuoteEnum(StrEnum):
33    """ Quote Enum """
34    SINGLE = auto()
35    DOUBLE = auto()
36    TRIPLE = auto()

Quote Enum

SINGLE = <QuoteEnum.SINGLE: 'single'>
DOUBLE = <QuoteEnum.DOUBLE: 'double'>
TRIPLE = <QuoteEnum.TRIPLE: 'triple'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
QUOTE_TYPES = {<QuoteEnum.SINGLE: 'single'>: "'", <QuoteEnum.DOUBLE: 'double'>: '"', <QuoteEnum.TRIPLE: 'triple'>: '"""'}
class SurroundEnum(enum.StrEnum):
46class SurroundEnum(StrEnum):
47    QUOTE_SINGLE = auto()
48    QUOTE_DOUBLE = auto()
49    QUOTE_TRIPLE = auto()
50    BRACE = auto()
51    BRACKET = auto()
52    PARENS = auto()

Enum where members are also (and must be) strings

QUOTE_SINGLE = <SurroundEnum.QUOTE_SINGLE: 'quote_single'>
QUOTE_DOUBLE = <SurroundEnum.QUOTE_DOUBLE: 'quote_double'>
QUOTE_TRIPLE = <SurroundEnum.QUOTE_TRIPLE: 'quote_triple'>
BRACE = <SurroundEnum.BRACE: 'brace'>
BRACKET = <SurroundEnum.BRACKET: 'bracket'>
PARENS = <SurroundEnum.PARENS: 'parens'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
SURROUND_TYPES = {<SurroundEnum.QUOTE_SINGLE: 'quote_single'>: ("'", "'"), <SurroundEnum.QUOTE_DOUBLE: 'quote_double'>: ('"', '"'), <SurroundEnum.QUOTE_TRIPLE: 'quote_triple'>: ('"""', '"""'), <SurroundEnum.BRACE: 'brace'>: ('{', '}'), <SurroundEnum.BRACKET: 'bracket'>: ('[', ']'), <SurroundEnum.PARENS: 'parens'>: ('(', ')')}
def indent(text, amount=4, pre=''):
65def indent(text, amount=4, pre=''):
66    txt = f"{pre} {text}" if pre else str(text)
67    return textwrap.indent(txt, INDENT * amount)
def prefix(text, pre):
70def prefix(text, pre):
71    return f"{pre}{text}" if text else copy(EMPTY)
def pluralize(val):
74def pluralize(val):
75    s = str(val).strip()
76
77    if s:
78        last_char = s[-1]
79        if last_char == 'y':
80            p = s[:-1] + 'ies'
81        elif last_char == 's':
82            p = s + 'es'
83        else:
84            p = s + 's'
85    else:
86        p = copy(EMPTY)
87
88    return p
def collapse(ary, join_char='\n'):
91def collapse(ary, join_char=LF):
92    return join_char.join(ary)
def surround( text: str, surround_with: SurroundEnum = <SurroundEnum.PARENS: 'parens'>):
 95def surround(text: str, surround_with: SurroundEnum = SurroundEnum.PARENS):
 96    if not text:
 97        return copy(EMPTY)
 98
 99    stype = SURROUND_TYPES[surround_with]
100    return "{o}{text}{c}".format(o=stype[0], c=stype[1], text=text)
def surround_all( iterable: Iterable, surround_with: SurroundEnum = <SurroundEnum.PARENS: 'parens'>) -> Iterable:
103def surround_all(iterable: Iterable, surround_with: SurroundEnum = SurroundEnum.PARENS) -> Iterable:
104    return [surround(t, surround_with=surround_with) for t in iterable]
def squish_text(text, format_braces=True):
107def squish_text(text, format_braces=True):
108    squished = RE_COMBINE_WHITESPACE.sub(SPACE, text).strip()
109    if format_braces:
110        squished = RE_ENCLOSE_WS_BEFORE.sub(r'\g<2>', squished)
111        squished = RE_ENCLOSE_NO_WS_BEFORE.sub(r'\g<1> \g<2>', squished)
112        squished = RE_ENCLOSE_NO_WS_AFTER.sub(r'\g<1> \g<2>', squished)
113        squished = RE_ENCLOSEEMPTY.sub(r'\g<1>\g<3>', squished)
114        squished = RE_COMBINE_WHITESPACE.sub(SPACE, squished).strip()
115
116    return squished
def score_text(text):
119def score_text(text):
120    return RE_COMBINE_WHITESPACE.sub('_', text.strip()).lower()
def combine_space(text):
123def combine_space(text):
124    return RE_COMBINE_WHITESPACE.sub(SPACE, text.strip())
def snake_text(text, lower: bool = True):
127def snake_text(text, lower: bool = True):
128    def score_cap(txt: str, position:int):
129        return txt if not (txt.isupper() and position) else f"_{txt}"
130    # Separate capitalized words:
131    terms = RE_CAPITAL.split(text)
132    terms.remove('')
133
134    snaked = [score_cap(t, i) for i, t in enumerate(terms) if t]
135    txt = "".join(snaked)
136    return txt.lower() if lower else txt
def capitalize_text(text):
139def capitalize_text(text):
140    txt = str(text).strip()
141    return txt if txt.isupper() else txt.capitalize()
def title_text(text):
144def title_text(text):
145    txt = RE_CAPITAL.sub(r" \1", text)
146    terms = RE_NOT_ALPHNUM_STRICT.split(txt)
147    clean_terms = filter(None, terms)
148
149    words = [capitalize_text(t) for t in clean_terms]
150    return SPACE.join(words)