1from __future__ import annotations
2
3import base64
4import unicodedata
5from binascii import Error as BinasciiError
6
7
8def urlsafe_base64_encode(s: bytes) -> str:
9 """
10 Encode a bytestring to a base64 string for use in URLs. Strip any trailing
11 equal signs.
12 """
13 return base64.urlsafe_b64encode(s).rstrip(b"\n=").decode("ascii")
14
15
16def urlsafe_base64_decode(s: str) -> bytes:
17 """
18 Decode a base64 encoded string. Add back any trailing equal signs that
19 might have been stripped.
20 """
21 s_bytes = s.encode()
22 try:
23 return base64.urlsafe_b64decode(
24 s_bytes.ljust(len(s_bytes) + len(s_bytes) % 4, b"=")
25 )
26 except (LookupError, BinasciiError) as e:
27 raise ValueError(e)
28
29
30def unicode_ci_compare(s1: str, s2: str) -> bool:
31 """
32 Perform case-insensitive comparison of two identifiers, using the
33 recommended algorithm from Unicode Technical Report 36, section
34 2.11.2(B)(2).
35 """
36 return (
37 unicodedata.normalize("NFKC", s1).casefold()
38 == unicodedata.normalize("NFKC", s2).casefold()
39 )