@@ -8,7 +8,7 @@ def camelcase(string: str) -> str:
88 """Convert a string into camelCase.
99
1010 Args:
11- string (:obj:`str`):
11+ string (:obj:`str`):
1212 The string to convert to camelCase.
1313
1414 Returns:
@@ -45,27 +45,45 @@ def snakecase(string: str, keep_together: List[str] = None) -> str:
4545 string (:obj:`str`):
4646 The string to convert to snake_case.
4747 keep_together (:obj:`List[str]`, `optional`):
48- (Upper) characters to not split, e.g., "HTTP".
48+ Substrings to not split (case sensitive) , e.g., "HTTP".
4949
5050 Returns:
5151 :obj:`str`: The snake_cased string.
5252 """
5353 if not string :
5454 return ""
55+ if keep_together is None :
56+ keep_together = []
5557 leading_underscore : bool = string [0 ] == "_"
5658 trailing_underscore : bool = string [- 1 ] == "_"
57- # If all uppercase, turn into lowercase
59+ # If all uppercase, turn into lowercase, preserving the keep together in
60+ # upper case
5861 if string .isupper ():
59- string = string .lower ()
60- if keep_together :
6162 for keep in keep_together :
6263 string = string .replace (keep , f"_{ keep .lower ()} _" )
64+ string = string .swapcase ()
65+ if keep_together :
66+ # Here, we use "The Greatest Regex Trick Ever" here (see
67+ # https://www.rexegg.com/regex-best-trick.php)
68+ keep_pattern = rf"{ '|' .join (f'({ re .escape (keep )} )' for keep in keep_together )} |"
69+ capturing_groups = r"" .join (rf"\{ i } " for i in range (1 , len (keep_together ) + 2 ))
70+ else :
71+ keep_pattern = r""
72+ capturing_groups = r"\1"
6373 # Manage separators
6474 string = re .sub (r"[\W]" , "_" , string )
6575 # Manage capital letters and numbers
66- string = re .sub (r"([A-Z]|\d+)" , r"_\1" , string )
76+ string = re .sub (
77+ fr"{ keep_pattern } ([A-Z]|\d+)" ,
78+ fr"_{ capturing_groups } " ,
79+ string
80+ )
6781 # Add "_" after numbers
68- string = re .sub (r"(\d+)" , r"\1_" , string ).lower ()
82+ string = re .sub (
83+ fr"{ keep_pattern } (\d+)" ,
84+ fr"{ capturing_groups } _" ,
85+ string
86+ ).lower ()
6987 # Remove repeated "_"
7088 string = re .sub (r"[_]{2,}" , "_" , string )
7189 string = re .sub (r"^_" , "" , string ) if not leading_underscore else string
@@ -130,6 +148,8 @@ def separatorcase(
130148 The string to convert.
131149 separator (:obj:`str`):
132150 The separator to use.
151+ keep_together (:obj:`List[str]`, `optional`):
152+ Substrings to not split (case sensitive), e.g., "HTTP".
133153
134154 Returns:
135155 :obj:`str`: The separator cased string.
0 commit comments