77import string
88import uuid
99import zlib
10+ from typing import TYPE_CHECKING , Any
1011
1112from localstack .config import DEFAULT_ENCODING
1213
14+ if TYPE_CHECKING :
15+ from localstack .utils .objects import ComplexType
16+
1317_unprintables = (
1418 range (0x00 , 0x09 ),
1519 range (0x0A , 0x0A ),
2731)
2832
2933
30- def to_str (obj : str | bytes , encoding : str = DEFAULT_ENCODING , errors = "strict" ) -> str :
34+ def to_str (obj : str | bytes , encoding : str = DEFAULT_ENCODING , errors : str = "strict" ) -> str :
3135 """If ``obj`` is an instance of ``binary_type``, return
3236 ``obj.decode(encoding, errors)``, otherwise return ``obj``"""
3337 return obj .decode (encoding , errors ) if isinstance (obj , bytes ) else obj
3438
3539
36- def to_bytes (obj : str | bytes , encoding : str = DEFAULT_ENCODING , errors = "strict" ) -> bytes :
40+ def to_bytes (obj : str | bytes , encoding : str = DEFAULT_ENCODING , errors : str = "strict" ) -> bytes :
3741 """If ``obj`` is an instance of ``text_type``, return
3842 ``obj.encode(encoding, errors)``, otherwise return ``obj``"""
3943 return obj .encode (encoding , errors ) if isinstance (obj , str ) else obj
@@ -44,7 +48,7 @@ def truncate(data: str, max_length: int = 100) -> str:
4448 return (f"{ data [:max_length ]} ..." ) if len (data ) > max_length else data
4549
4650
47- def is_string (s , include_unicode = True , exclude_binary = False ):
51+ def is_string (s : Any , include_unicode : bool = True , exclude_binary : bool = False ) -> bool :
4852 if isinstance (s , bytes ) and exclude_binary :
4953 return False
5054 if isinstance (s , str ):
@@ -54,13 +58,13 @@ def is_string(s, include_unicode=True, exclude_binary=False):
5458 return False
5559
5660
57- def is_string_or_bytes (s ) :
61+ def is_string_or_bytes (s : Any ) -> bool :
5862 return is_string (s ) or isinstance (s , str ) or isinstance (s , bytes )
5963
6064
61- def is_base64 (s ) :
65+ def is_base64 (s : Any ) -> bool :
6266 regex = r"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$"
63- return is_string (s ) and re .match (regex , s )
67+ return is_string (s ) and re .match (regex , s ) is not None
6468
6569
6670_re_camel_to_snake_case = re .compile ("((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))" )
@@ -85,22 +89,21 @@ def canonicalize_bool_to_str(val: bool) -> str:
8589 return "true" if str (val ).lower () == "true" else "false"
8690
8791
88- def convert_to_printable_chars (value : list | dict | str ) -> str :
92+ def convert_to_printable_chars (value : "str | ComplexType" ) -> "ComplexType" :
8993 """Removes all unprintable characters from the given string."""
9094 from localstack .utils .objects import recurse_object
9195
92- if isinstance (value , (dict , list )):
96+ if isinstance (value , str ):
97+ return REGEX_UNPRINTABLE_CHARS .sub ("" , value )
98+ else :
9399
94- def _convert (obj , ** kwargs ) :
100+ def _convert (obj : Any , ** kwargs : Any ) -> "ComplexType" :
95101 if isinstance (obj , str ):
96102 return convert_to_printable_chars (obj )
97103 return obj
98104
99105 return recurse_object (value , _convert )
100106
101- result = REGEX_UNPRINTABLE_CHARS .sub ("" , value )
102- return result
103-
104107
105108def first_char_to_lower (s : str ) -> str :
106109 return s and f"{ s [0 ].lower ()} { s [1 :]} "
@@ -110,20 +113,20 @@ def first_char_to_upper(s: str) -> str:
110113 return s and f"{ s [0 ].upper ()} { s [1 :]} "
111114
112115
113- def str_to_bool (value ) :
116+ def str_to_bool (value : Any ) -> Any :
114117 """Return the boolean value of the given string, or the verbatim value if it is not a string"""
115118 if isinstance (value , str ):
116119 true_strings = ["true" , "True" ]
117120 return value in true_strings
118121 return value
119122
120123
121- def str_insert (string , index , content ) :
124+ def str_insert (string : str , index : int , content : str ) -> str :
122125 """Insert a substring into an existing string at a certain index."""
123126 return f"{ string [:index ]} { content } { string [index :]} "
124127
125128
126- def str_remove (string , index , end_index = None ):
129+ def str_remove (string : str , index : int , end_index : int | None = None ) -> str :
127130 """Remove a substring from an existing string at a certain from-to index range."""
128131 end_index = end_index or (index + 1 )
129132 return f"{ string [:index ]} { string [end_index :]} "
@@ -159,7 +162,7 @@ def checksum_crc32(string: str | bytes) -> str:
159162 return base64 .b64encode (checksum .to_bytes (4 , "big" )).decode ()
160163
161164
162- def checksum_crc32c (string : str | bytes ):
165+ def checksum_crc32c (string : str | bytes ) -> str :
163166 # import botocore locally here to avoid a dependency of the CLI to botocore
164167 from botocore .httpchecksum import CrtCrc32cChecksum
165168
@@ -168,7 +171,7 @@ def checksum_crc32c(string: str | bytes):
168171 return base64 .b64encode (checksum .digest ()).decode ()
169172
170173
171- def checksum_crc64nvme (string : str | bytes ):
174+ def checksum_crc64nvme (string : str | bytes ) -> str :
172175 # import botocore locally here to avoid a dependency of the CLI to botocore
173176 from botocore .httpchecksum import CrtCrc64NvmeChecksum
174177
@@ -223,7 +226,9 @@ def prepend_with_slash(input: str) -> str:
223226 return input
224227
225228
226- def key_value_pairs_to_dict (pairs : str , delimiter : str = "," , separator : str = "=" ) -> dict :
229+ def key_value_pairs_to_dict (
230+ pairs : str , delimiter : str = "," , separator : str = "="
231+ ) -> dict [str , str ]:
227232 """
228233 Converts a string of key-value pairs to a dictionary.
229234
0 commit comments