import re # Word delimiters and symbols that will not be preserved when re-casing. # language=PythonRegExp SYMBOLS = "[^a-zA-Z0-9]*" # Optionally capitalized word. # language=PythonRegExp WORD = "[A-Z]*[a-z]*[0-9]*" # Uppercase word, not followed by lowercase letters. # language=PythonRegExp WORD_UPPER = "[A-Z]+(?![a-z])[0-9]*" def safe_snake_case(value: str) -> str: """Snake case a value taking into account Python keywords.""" value = snake_case(value) if value in [ "and", "as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield", ]: # https://www.python.org/dev/peps/pep-0008/#descriptive-naming-styles value += "_" return value def snake_case(value: str, strict: bool = True): """ Join words with an underscore into lowercase and remove symbols. @param value: value to convert @param strict: force single underscores """ def substitute_word(symbols, word, is_start): if not word: return "" if strict: delimiter_count = 0 if is_start else 1 # Single underscore if strict. elif is_start: delimiter_count = len(symbols) elif word.isupper() or word.islower(): delimiter_count = max( 1, len(symbols) ) # Preserve all delimiters if not strict. else: delimiter_count = len(symbols) + 1 # Extra underscore for leading capital. return ("_" * delimiter_count) + word.lower() snake = re.sub( f"(^)?({SYMBOLS})({WORD_UPPER}|{WORD})", lambda groups: substitute_word(groups[2], groups[3], groups[1] is not None), value, ) return snake def pascal_case(value: str, strict: bool = True): """ Capitalize each word and remove symbols. @param value: value to convert @param strict: output only alphanumeric characters """ def substitute_word(symbols, word): if strict: return word.capitalize() # Remove all delimiters if word.islower(): delimiter_length = len(symbols[:-1]) # Lose one delimiter else: delimiter_length = len(symbols) # Preserve all delimiters return ("_" * delimiter_length) + word.capitalize() return re.sub( f"({SYMBOLS})({WORD_UPPER}|{WORD})", lambda groups: substitute_word(groups[1], groups[2]), value, ) def camel_case(value: str, strict: bool = True): """ Capitalize all words except first and remove symbols. """ return lowercase_first(pascal_case(value, strict=strict)) def lowercase_first(value: str): return value[0:1].lower() + value[1:]