Move betterproto → src/betterproto
This change avoids some nasty import issues and also ensures that the right code is tested and arbitrary code is not included when packaging.
This commit is contained in:
committed by
Bouke Versteegh
parent
cebf9176a3
commit
459d12b24d
122
src/betterproto/casing.py
Normal file
122
src/betterproto/casing.py
Normal file
@@ -0,0 +1,122 @@
|
||||
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",
|
||||
"async",
|
||||
"await",
|
||||
"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:]
|
||||
Reference in New Issue
Block a user