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