def percent_encode(char: str) -> str:
"""
- Replace every character in a string with the percent-encoded representation.
+ Replace a single character with the percent-encoded representation.
Characters outside the ASCII range are represented with their a percent-encoded
representation of their UTF-8 byte sequence.
return "".join([f"%{byte:02x}" for byte in char.encode("utf-8")]).upper()
+def is_safe(string: str, safe: str = "/") -> bool:
+ """
+ Determine if a given string is already quote-safe.
+ """
+ NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe + "%"
+
+ # All characters must already be non-escaping or '%'
+ for char in string:
+ if char not in NON_ESCAPED_CHARS:
+ return False
+
+ # Any '%' characters must be valid '%xx' escape sequences.
+ return string.count("%") == len(PERCENT_ENCODED_REGEX.findall(string))
+
+
def quote(string: str, safe: str = "/") -> str:
- NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe
- if string.count("%") == len(PERCENT_ENCODED_REGEX.findall(string)):
- # If all occurances of '%' are valid '%xx' escapes, then treat
- # percent as a non-escaping character.
- NON_ESCAPED_CHARS += "%"
+ """
+ Use percent-encoding to quote a string if required.
+ """
+ if is_safe(string, safe=safe):
+ return string
+ NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe
return "".join(
[char if char in NON_ESCAPED_CHARS else percent_encode(char) for char in string]
)
assert url.path == "../abc"
+# Tests for optional percent encoding
+
+
+def test_param_requires_encoding():
+ url = httpx.URL("http://webservice", params={"u": "with spaces"})
+ assert str(url) == "http://webservice?u=with%20spaces"
+
+
+def test_param_does_not_require_encoding():
+ url = httpx.URL("http://webservice", params={"u": "with%20spaces"})
+ assert str(url) == "http://webservice?u=with%20spaces"
+
+
+def test_param_with_existing_escape_requires_encoding():
+ url = httpx.URL("http://webservice", params={"u": "http://example.com?q=foo%2Fa"})
+ assert str(url) == "http://webservice?u=http%3A//example.com%3Fq%3Dfoo%252Fa"
+
+
# Tests for invalid URLs