1from__future__importannotations 2 3importbase64 4importunicodedata 5frombinasciiimportErrorasBinasciiError 6 7fromplain.internalimportinternalcode 8 910@internalcode11defurlsafe_base64_encode(s:bytes)->str:12"""13 Encode a bytestring to a base64 string for use in URLs. Strip any trailing14 equal signs.15 """16returnbase64.urlsafe_b64encode(s).rstrip(b"\n=").decode("ascii")171819@internalcode20defurlsafe_base64_decode(s:str)->bytes:21"""22 Decode a base64 encoded string. Add back any trailing equal signs that23 might have been stripped.24 """25s_bytes=s.encode()26try:27returnbase64.urlsafe_b64decode(28s_bytes.ljust(len(s_bytes)+len(s_bytes)%4,b"=")29)30except(LookupError,BinasciiError)ase:31raiseValueError(e)323334@internalcode35defunicode_ci_compare(s1:str,s2:str)->bool:36"""37 Perform case-insensitive comparison of two identifiers, using the38 recommended algorithm from Unicode Technical Report 36, section39 2.11.2(B)(2).40 """41return(42unicodedata.normalize("NFKC",s1).casefold()43==unicodedata.normalize("NFKC",s2).casefold()44)