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    )