python: Consistently use from…import for urllib.parse.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-12-05 12:14:17 -08:00 committed by Tim Abbott
parent e5d71fe5ac
commit 3853fa875a
27 changed files with 154 additions and 210 deletions

View File

@ -1,9 +1,8 @@
import urllib
from contextlib import suppress from contextlib import suppress
from datetime import timedelta from datetime import timedelta
from decimal import Decimal from decimal import Decimal
from typing import Any, Dict, Iterable, List, Optional, Union from typing import Any, Dict, Iterable, List, Optional, Union
from urllib.parse import urlencode from urllib.parse import urlencode, urlparse
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -273,7 +272,7 @@ def support(
for key_word in key_words: for key_word in key_words:
try: try:
URLValidator()(key_word) URLValidator()(key_word)
parse_result = urllib.parse.urlparse(key_word) parse_result = urlparse(key_word)
hostname = parse_result.hostname hostname = parse_result.hostname
assert hostname is not None assert hostname is not None
if parse_result.port: if parse_result.port:

View File

@ -5,12 +5,12 @@ import http.client
import json import json
import logging import logging
import time import time
import urllib.parse from urllib.parse import urlencode
def fetch_metric(metric_id: str, query: str) -> float: def fetch_metric(metric_id: str, query: str) -> float:
logging.info("Fetching %s", metric_id) logging.info("Fetching %s", metric_id)
params = urllib.parse.urlencode({"query": query}) params = urlencode({"query": query})
conn = http.client.HTTPConnection("localhost:9090") conn = http.client.HTTPConnection("localhost:9090")
conn.request("GET", "/api/v1/query?" + params) conn.request("GET", "/api/v1/query?" + params)
response = conn.getresponse() response = conn.getresponse()

View File

@ -1,6 +1,5 @@
import base64 import base64
import logging import logging
import urllib
from datetime import datetime from datetime import datetime
from functools import wraps from functools import wraps
from io import BytesIO from io import BytesIO
@ -16,6 +15,7 @@ from typing import (
cast, cast,
overload, overload,
) )
from urllib.parse import urlparse
import django_otp import django_otp
from django.conf import settings from django.conf import settings
@ -393,8 +393,8 @@ def zulip_redirect_to_login(
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL) resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
# If the login URL is the same scheme and net location then just # If the login URL is the same scheme and net location then just
# use the path as the "next" url. # use the path as the "next" url.
login_scheme, login_netloc = urllib.parse.urlparse(resolved_login_url)[:2] login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urllib.parse.urlparse(path)[:2] current_scheme, current_netloc = urlparse(path)[:2]
if (not login_scheme or login_scheme == current_scheme) and ( if (not login_scheme or login_scheme == current_scheme) and (
not login_netloc or login_netloc == current_netloc not login_netloc or login_netloc == current_netloc
): ):

View File

@ -1,5 +1,5 @@
import urllib
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
from urllib.parse import urljoin
from django.conf import settings from django.conf import settings
from django.contrib.staticfiles.storage import staticfiles_storage from django.contrib.staticfiles.storage import staticfiles_storage
@ -135,7 +135,7 @@ def absolute_avatar_url(user_profile: UserProfile) -> str:
avatar = avatar_url(user_profile) avatar = avatar_url(user_profile)
# avatar_url can return None if client_gravatar=True, however here we use the default value of False # avatar_url can return None if client_gravatar=True, however here we use the default value of False
assert avatar is not None assert avatar is not None
return urllib.parse.urljoin(user_profile.realm.uri, avatar) return urljoin(user_profile.realm.uri, avatar)
def is_avatar_new(ldap_avatar: bytes, user_profile: UserProfile) -> bool: def is_avatar_new(ldap_avatar: bytes, user_profile: UserProfile) -> bool:

View File

@ -6,8 +6,6 @@ import logging
import mimetypes import mimetypes
import re import re
import time import time
import urllib
import urllib.parse
from collections import deque from collections import deque
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime, timezone
@ -28,7 +26,7 @@ from typing import (
Union, Union,
cast, cast,
) )
from urllib.parse import parse_qs, urlencode, urljoin, urlsplit from urllib.parse import parse_qs, quote, urlencode, urljoin, urlparse, urlsplit, urlunparse
from xml.etree.ElementTree import Element, SubElement from xml.etree.ElementTree import Element, SubElement
import ahocorasick import ahocorasick
@ -488,7 +486,7 @@ def fetch_open_graph_image(url: str) -> Optional[Dict[str, Any]]:
def get_tweet_id(url: str) -> Optional[str]: def get_tweet_id(url: str) -> Optional[str]:
parsed_url = urllib.parse.urlparse(url) parsed_url = urlparse(url)
if not (parsed_url.netloc == "twitter.com" or parsed_url.netloc.endswith(".twitter.com")): if not (parsed_url.netloc == "twitter.com" or parsed_url.netloc.endswith(".twitter.com")):
return None return None
to_match = parsed_url.path to_match = parsed_url.path
@ -717,13 +715,13 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
def get_actual_image_url(self, url: str) -> str: def get_actual_image_url(self, url: str) -> str:
# Add specific per-site cases to convert image-preview URLs to image URLs. # Add specific per-site cases to convert image-preview URLs to image URLs.
# See https://github.com/zulip/zulip/issues/4658 for more information # See https://github.com/zulip/zulip/issues/4658 for more information
parsed_url = urllib.parse.urlparse(url) parsed_url = urlparse(url)
if parsed_url.netloc == "github.com" or parsed_url.netloc.endswith(".github.com"): if parsed_url.netloc == "github.com" or parsed_url.netloc.endswith(".github.com"):
# https://github.com/zulip/zulip/blob/main/static/images/logo/zulip-icon-128x128.png -> # https://github.com/zulip/zulip/blob/main/static/images/logo/zulip-icon-128x128.png ->
# https://raw.githubusercontent.com/zulip/zulip/main/static/images/logo/zulip-icon-128x128.png # https://raw.githubusercontent.com/zulip/zulip/main/static/images/logo/zulip-icon-128x128.png
split_path = parsed_url.path.split("/") split_path = parsed_url.path.split("/")
if len(split_path) > 3 and split_path[3] == "blob": if len(split_path) > 3 and split_path[3] == "blob":
return urllib.parse.urljoin( return urljoin(
"https://raw.githubusercontent.com", "/".join(split_path[0:3] + split_path[4:]) "https://raw.githubusercontent.com", "/".join(split_path[0:3] + split_path[4:])
) )
@ -732,7 +730,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
def is_image(self, url: str) -> bool: def is_image(self, url: str) -> bool:
if not self.zmd.image_preview_enabled: if not self.zmd.image_preview_enabled:
return False return False
parsed_url = urllib.parse.urlparse(url) parsed_url = urlparse(url)
# remove HTML URLs which end with image extensions that cannot be shorted # remove HTML URLs which end with image extensions that cannot be shorted
if parsed_url.netloc == "pasteboard.co": if parsed_url.netloc == "pasteboard.co":
return False return False
@ -744,7 +742,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
# wikipedia.org to point to the actual image URL. It's # wikipedia.org to point to the actual image URL. It's
# structurally very similar to dropbox_image, and possibly # structurally very similar to dropbox_image, and possibly
# should be rewritten to use open graph, but has some value. # should be rewritten to use open graph, but has some value.
parsed_url = urllib.parse.urlparse(url) parsed_url = urlparse(url)
if parsed_url.netloc.lower().endswith(".wikipedia.org") and parsed_url.path.startswith( if parsed_url.netloc.lower().endswith(".wikipedia.org") and parsed_url.path.startswith(
"/wiki/File:" "/wiki/File:"
): ):
@ -759,7 +757,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
def dropbox_image(self, url: str) -> Optional[Dict[str, Any]]: def dropbox_image(self, url: str) -> Optional[Dict[str, Any]]:
# TODO: The returned Dict could possibly be a TypedDict in future. # TODO: The returned Dict could possibly be a TypedDict in future.
parsed_url = urllib.parse.urlparse(url) parsed_url = urlparse(url)
if parsed_url.netloc == "dropbox.com" or parsed_url.netloc.endswith(".dropbox.com"): if parsed_url.netloc == "dropbox.com" or parsed_url.netloc.endswith(".dropbox.com"):
is_album = parsed_url.path.startswith("/sc/") or parsed_url.path.startswith("/photos/") is_album = parsed_url.path.startswith("/sc/") or parsed_url.path.startswith("/photos/")
# Only allow preview Dropbox shared links # Only allow preview Dropbox shared links
@ -911,7 +909,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
"type": "mention", "type": "mention",
"start": match.start(), "start": match.start(),
"end": match.end(), "end": match.end(),
"url": "https://twitter.com/" + urllib.parse.quote(screen_name), "url": "https://twitter.com/" + quote(screen_name),
"text": mention_string, "text": mention_string,
} }
for match in re.finditer(re.escape(mention_string), text, re.IGNORECASE) for match in re.finditer(re.escape(mention_string), text, re.IGNORECASE)
@ -1260,7 +1258,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
# `/user_uploads` and beginning with `user_uploads`. # `/user_uploads` and beginning with `user_uploads`.
# This urllib construction converts the latter into # This urllib construction converts the latter into
# the former. # the former.
parsed_url = urllib.parse.urlsplit(urllib.parse.urljoin("/", url)) parsed_url = urlsplit(urljoin("/", url))
host = parsed_url.netloc host = parsed_url.netloc
if host != "" and ( if host != "" and (
@ -1568,7 +1566,7 @@ def sanitize_url(url: str) -> Optional[str]:
See the docstring on markdown.inlinepatterns.LinkPattern.sanitize_url. See the docstring on markdown.inlinepatterns.LinkPattern.sanitize_url.
""" """
try: try:
parts = urllib.parse.urlparse(url.replace(" ", "%20")) parts = urlparse(url.replace(" ", "%20"))
scheme, netloc, path, params, query, fragment = parts scheme, netloc, path, params, query, fragment = parts
except ValueError: except ValueError:
# Bad URL - so bad it couldn't be parsed. # Bad URL - so bad it couldn't be parsed.
@ -1580,10 +1578,10 @@ def sanitize_url(url: str) -> Optional[str]:
scheme = "mailto" scheme = "mailto"
elif scheme == "" and netloc == "" and len(path) > 0 and path[0] == "/": elif scheme == "" and netloc == "" and len(path) > 0 and path[0] == "/":
# Allow domain-relative links # Allow domain-relative links
return urllib.parse.urlunparse(("", "", path, params, query, fragment)) return urlunparse(("", "", path, params, query, fragment))
elif (scheme, netloc, path, params, query) == ("", "", "", "", "") and len(fragment) > 0: elif (scheme, netloc, path, params, query) == ("", "", "", "", "") and len(fragment) > 0:
# Allow fragment links # Allow fragment links
return urllib.parse.urlunparse(("", "", "", "", "", fragment)) return urlunparse(("", "", "", "", "", fragment))
# Zulip modification: If scheme is not specified, assume http:// # Zulip modification: If scheme is not specified, assume http://
# We re-enter sanitize_url because netloc etc. need to be re-parsed. # We re-enter sanitize_url because netloc etc. need to be re-parsed.
@ -1608,7 +1606,7 @@ def sanitize_url(url: str) -> Optional[str]:
# the colon check, which would also forbid a lot of legitimate URLs. # the colon check, which would also forbid a lot of legitimate URLs.
# URL passes all tests. Return URL as-is. # URL passes all tests. Return URL as-is.
return urllib.parse.urlunparse((scheme, netloc, path, params, query, fragment)) return urlunparse((scheme, netloc, path, params, query, fragment))
def url_to_a( def url_to_a(

View File

@ -1,6 +1,6 @@
import logging import logging
import urllib
from typing import Any, Dict, List, Mapping, Optional, Tuple, Union from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
from urllib.parse import urljoin
import orjson import orjson
import requests import requests
@ -88,9 +88,7 @@ def send_to_push_bouncer(
assert settings.PUSH_NOTIFICATION_BOUNCER_URL is not None assert settings.PUSH_NOTIFICATION_BOUNCER_URL is not None
assert settings.ZULIP_ORG_ID is not None assert settings.ZULIP_ORG_ID is not None
assert settings.ZULIP_ORG_KEY is not None assert settings.ZULIP_ORG_KEY is not None
url = urllib.parse.urljoin( url = urljoin(settings.PUSH_NOTIFICATION_BOUNCER_URL, "/api/v1/remotes/" + endpoint)
settings.PUSH_NOTIFICATION_BOUNCER_URL, "/api/v1/remotes/" + endpoint
)
api_auth = requests.auth.HTTPBasicAuth(settings.ZULIP_ORG_ID, settings.ZULIP_ORG_KEY) api_auth = requests.auth.HTTPBasicAuth(settings.ZULIP_ORG_ID, settings.ZULIP_ORG_KEY)
headers = {"User-agent": f"ZulipServer/{ZULIP_VERSION}"} headers = {"User-agent": f"ZulipServer/{ZULIP_VERSION}"}

View File

@ -1,6 +1,6 @@
import re import re
import urllib
from typing import Optional from typing import Optional
from urllib.parse import urlsplit
from django.conf import settings from django.conf import settings
from django.http import HttpRequest from django.http import HttpRequest
@ -57,8 +57,8 @@ def is_root_domain_available() -> bool:
def is_static_or_current_realm_url(url: str, realm: Optional[Realm]) -> bool: def is_static_or_current_realm_url(url: str, realm: Optional[Realm]) -> bool:
assert settings.STATIC_URL is not None assert settings.STATIC_URL is not None
split_url = urllib.parse.urlsplit(url) split_url = urlsplit(url)
split_static_url = urllib.parse.urlsplit(settings.STATIC_URL) split_static_url = urlsplit(settings.STATIC_URL)
# The netloc check here is important to correctness if STATIC_URL # The netloc check here is important to correctness if STATIC_URL
# does not contain a `/`; see the tests for why. # does not contain a `/`; see the tests for why.

View File

@ -4,7 +4,6 @@ import re
import shutil import shutil
import subprocess import subprocess
import tempfile import tempfile
import urllib
from contextlib import contextmanager from contextlib import contextmanager
from datetime import timedelta from datetime import timedelta
from typing import ( from typing import (
@ -25,6 +24,7 @@ from typing import (
cast, cast,
) )
from unittest import TestResult, mock, skipUnless from unittest import TestResult, mock, skipUnless
from urllib.parse import parse_qs, quote, urlencode
import lxml.html import lxml.html
import orjson import orjson
@ -278,7 +278,7 @@ Output:
url_split = url.split("?") url_split = url.split("?")
data = {} data = {}
if len(url_split) == 2: if len(url_split) == 2:
data = urllib.parse.parse_qs(url_split[1]) data = parse_qs(url_split[1])
url = url_split[0] url = url_split[0]
url = url.replace("/json/", "/").replace("/api/v1/", "/") url = url.replace("/json/", "/").replace("/api/v1/", "/")
return (url, data) return (url, data)
@ -342,7 +342,7 @@ Output:
""" """
We need to urlencode, since Django's function won't do it for us. We need to urlencode, since Django's function won't do it for us.
""" """
encoded = urllib.parse.urlencode(info) encoded = urlencode(info)
extra["content_type"] = "application/x-www-form-urlencoded" extra["content_type"] = "application/x-www-form-urlencoded"
django_client = self.client # see WRAPPER_COMMENT django_client = self.client # see WRAPPER_COMMENT
self.set_http_headers(extra, skip_user_agent) self.set_http_headers(extra, skip_user_agent)
@ -434,7 +434,7 @@ Output:
headers: Optional[Mapping[str, Any]] = None, headers: Optional[Mapping[str, Any]] = None,
**extra: str, **extra: str,
) -> "TestHttpResponse": ) -> "TestHttpResponse":
encoded = urllib.parse.urlencode(info) encoded = urlencode(info)
extra["content_type"] = "application/x-www-form-urlencoded" extra["content_type"] = "application/x-www-form-urlencoded"
django_client = self.client # see WRAPPER_COMMENT django_client = self.client # see WRAPPER_COMMENT
self.set_http_headers(extra, skip_user_agent) self.set_http_headers(extra, skip_user_agent)
@ -477,7 +477,7 @@ Output:
intentionally_undocumented: bool = False, intentionally_undocumented: bool = False,
**extra: str, **extra: str,
) -> "TestHttpResponse": ) -> "TestHttpResponse":
encoded = urllib.parse.urlencode(info) encoded = urlencode(info)
extra["content_type"] = "application/x-www-form-urlencoded" extra["content_type"] = "application/x-www-form-urlencoded"
django_client = self.client # see WRAPPER_COMMENT django_client = self.client # see WRAPPER_COMMENT
self.set_http_headers(extra, skip_user_agent) self.set_http_headers(extra, skip_user_agent)
@ -807,9 +807,7 @@ Output:
def register(self, email: str, password: str, subdomain: str = DEFAULT_SUBDOMAIN) -> None: def register(self, email: str, password: str, subdomain: str = DEFAULT_SUBDOMAIN) -> None:
response = self.client_post("/accounts/home/", {"email": email}, subdomain=subdomain) response = self.client_post("/accounts/home/", {"email": email}, subdomain=subdomain)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual( self.assertEqual(response["Location"], f"/accounts/send_confirm/?email={quote(email)}")
response["Location"], f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
response = self.submit_reg_form_for_user(email, password, subdomain=subdomain) response = self.submit_reg_form_for_user(email, password, subdomain=subdomain)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response["Location"], f"http://{Realm.host_for_subdomain(subdomain)}/") self.assertEqual(response["Location"], f"http://{Realm.host_for_subdomain(subdomain)}/")
@ -2408,7 +2406,7 @@ class BouncerTestCase(ZulipTestCase):
kwargs = dict(content_type="application/json") kwargs = dict(content_type="application/json")
else: else:
assert isinstance(request.body, str) or request.body is None assert isinstance(request.body, str) or request.body is None
params: Dict[str, List[str]] = urllib.parse.parse_qs(request.body) params: Dict[str, List[str]] = parse_qs(request.body)
# In Python 3, the values of the dict from `parse_qs` are # In Python 3, the values of the dict from `parse_qs` are
# in a list, because there might be multiple values. # in a list, because there might be multiple values.
# But since we are sending values with no same keys, hence # But since we are sending values with no same keys, hence

View File

@ -1,10 +1,9 @@
import io import io
import logging import logging
import urllib
from datetime import datetime from datetime import datetime
from mimetypes import guess_type from mimetypes import guess_type
from typing import IO, Any, BinaryIO, Callable, Iterator, List, Optional, Tuple, Union from typing import IO, Any, BinaryIO, Callable, Iterator, List, Optional, Tuple, Union
from urllib.parse import urljoin from urllib.parse import unquote, urljoin
from django.conf import settings from django.conf import settings
from django.core.files.uploadedfile import UploadedFile from django.core.files.uploadedfile import UploadedFile
@ -47,7 +46,7 @@ def get_file_info(user_file: UploadedFile) -> Tuple[str, str]:
# different content-type from the filename. # different content-type from the filename.
content_type = "application/octet-stream" content_type = "application/octet-stream"
uploaded_file_name = urllib.parse.unquote(uploaded_file_name) uploaded_file_name = unquote(uploaded_file_name)
return uploaded_file_name, content_type return uploaded_file_name, content_type

View File

@ -1,10 +1,10 @@
import logging import logging
import os import os
import secrets import secrets
import urllib
from datetime import datetime from datetime import datetime
from mimetypes import guess_type from mimetypes import guess_type
from typing import IO, Any, BinaryIO, Callable, Iterator, List, Literal, Optional, Tuple from typing import IO, Any, BinaryIO, Callable, Iterator, List, Literal, Optional, Tuple
from urllib.parse import urljoin, urlsplit, urlunsplit
import boto3 import boto3
import botocore import botocore
@ -188,10 +188,10 @@ class S3UploadBackend(ZulipUploadBackend):
}, },
ExpiresIn=0, ExpiresIn=0,
) )
split_url = urllib.parse.urlsplit(foo_url) split_url = urlsplit(foo_url)
assert split_url.path.endswith(f"/{DUMMY_KEY}") assert split_url.path.endswith(f"/{DUMMY_KEY}")
return urllib.parse.urlunsplit( return urlunsplit(
(split_url.scheme, split_url.netloc, split_url.path[: -len(DUMMY_KEY)], "", "") (split_url.scheme, split_url.netloc, split_url.path[: -len(DUMMY_KEY)], "", "")
) )
@ -204,7 +204,7 @@ class S3UploadBackend(ZulipUploadBackend):
key: str, key: str,
) -> str: ) -> str:
assert not key.startswith("/") assert not key.startswith("/")
return urllib.parse.urljoin(self.public_upload_url_base, key) return urljoin(self.public_upload_url_base, key)
@override @override
def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str: def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str:

View File

@ -5,7 +5,6 @@ import os
import re import re
import secrets import secrets
import time import time
import urllib
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from contextlib import contextmanager from contextlib import contextmanager
from datetime import timedelta from datetime import timedelta
@ -25,7 +24,7 @@ from typing import (
Type, Type,
) )
from unittest import mock from unittest import mock
from urllib.parse import urlencode from urllib.parse import parse_qs, urlencode, urlparse
import jwt import jwt
import ldap import ldap
@ -978,8 +977,8 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
headers: Any, headers: Any,
**extra_data: Any, **extra_data: Any,
) -> "TestHttpResponse": ) -> "TestHttpResponse":
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
csrf_state = urllib.parse.parse_qs(parsed_url.query)["state"] csrf_state = parse_qs(parsed_url.query)["state"]
result = self.client_get(self.AUTH_FINISH_URL, dict(state=csrf_state), **headers) result = self.client_get(self.AUTH_FINISH_URL, dict(state=csrf_state), **headers)
return result return result
@ -1158,7 +1157,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -1182,7 +1181,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -1308,8 +1307,8 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
redirect_url = result["Location"] redirect_url = result["Location"]
parsed_url = urllib.parse.urlparse(redirect_url) parsed_url = urlparse(redirect_url)
query_params = urllib.parse.parse_qs(parsed_url.query) query_params = parse_qs(parsed_url.query)
self.assertEqual(parsed_url.scheme, "zulip") self.assertEqual(parsed_url.scheme, "zulip")
self.assertEqual(query_params["realm"], ["http://zulip.testserver"]) self.assertEqual(query_params["realm"], ["http://zulip.testserver"])
self.assertEqual(query_params["email"], [hamlet.delivery_email]) self.assertEqual(query_params["email"], [hamlet.delivery_email])
@ -1404,7 +1403,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
self.assertEqual(data["full_name"], self.example_user("hamlet").full_name) self.assertEqual(data["full_name"], self.example_user("hamlet").full_name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")
@ -1430,7 +1429,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
self.assertEqual(data["full_name"], name) self.assertEqual(data["full_name"], name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -1473,8 +1472,8 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
if mobile_flow_otp: if mobile_flow_otp:
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
redirect_url = result["Location"] redirect_url = result["Location"]
parsed_url = urllib.parse.urlparse(redirect_url) parsed_url = urlparse(redirect_url)
query_params = urllib.parse.parse_qs(parsed_url.query) query_params = parse_qs(parsed_url.query)
self.assertEqual(parsed_url.scheme, "zulip") self.assertEqual(parsed_url.scheme, "zulip")
self.assertEqual(query_params["realm"], ["http://zulip.testserver"]) self.assertEqual(query_params["realm"], ["http://zulip.testserver"])
self.assertEqual(query_params["email"], [email]) self.assertEqual(query_params["email"], [email])
@ -1716,7 +1715,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
self.assertEqual(data["full_name"], name) self.assertEqual(data["full_name"], name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -1740,7 +1739,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
self.assertEqual(data["full_name"], name) self.assertEqual(data["full_name"], name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -1996,8 +1995,8 @@ class SAMLAuthBackendTest(SocialAuthBase):
assert "samlrequest" in result["Location"].lower() assert "samlrequest" in result["Location"].lower()
self.client.cookies = result.cookies self.client.cookies = result.cookies
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
relay_state = urllib.parse.parse_qs(parsed_url.query)["RelayState"][0] relay_state = parse_qs(parsed_url.query)["RelayState"][0]
# Make sure params are getting encoded into RelayState: # Make sure params are getting encoded into RelayState:
data = SAMLAuthBackend.get_data_from_redis(orjson.loads(relay_state)["state_token"]) data = SAMLAuthBackend.get_data_from_redis(orjson.loads(relay_state)["state_token"])
assert data is not None assert data is not None
@ -2149,7 +2148,7 @@ class SAMLAuthBackendTest(SocialAuthBase):
# Verify the redirect has the correct form - a LogoutRequest for hamlet # Verify the redirect has the correct form - a LogoutRequest for hamlet
# is delivered to the IdP in the SAMLRequest param. # is delivered to the IdP in the SAMLRequest param.
query_dict = urllib.parse.parse_qs(urllib.parse.urlparse(result["Location"]).query) query_dict = parse_qs(urlparse(result["Location"]).query)
saml_request_encoded = query_dict["SAMLRequest"][0] saml_request_encoded = query_dict["SAMLRequest"][0]
saml_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(saml_request_encoded).decode() saml_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(saml_request_encoded).decode()
self.assertIn("<samlp:LogoutRequest", saml_request) self.assertIn("<samlp:LogoutRequest", saml_request)
@ -2331,8 +2330,8 @@ class SAMLAuthBackendTest(SocialAuthBase):
redirect_to = result["Location"] redirect_to = result["Location"]
self.assertIn(settings.SOCIAL_AUTH_SAML_ENABLED_IDPS["test_idp"]["slo_url"], redirect_to) self.assertIn(settings.SOCIAL_AUTH_SAML_ENABLED_IDPS["test_idp"]["slo_url"], redirect_to)
parsed = urllib.parse.urlparse(redirect_to) parsed = urlparse(redirect_to)
query_dict = urllib.parse.parse_qs(parsed.query) query_dict = parse_qs(parsed.query)
self.assertIn("SAMLResponse", query_dict) self.assertIn("SAMLResponse", query_dict)
# Do some very basic parsing of the SAMLResponse to verify it's a success response. # Do some very basic parsing of the SAMLResponse to verify it's a success response.
@ -3015,7 +3014,7 @@ class SAMLAuthBackendTest(SocialAuthBase):
self.assertEqual(data["full_name"], self.name) self.assertEqual(data["full_name"], self.name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -3050,7 +3049,7 @@ class SAMLAuthBackendTest(SocialAuthBase):
self.assertEqual(data["full_name"], self.name) self.assertEqual(data["full_name"], self.name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -3071,7 +3070,7 @@ class SAMLAuthBackendTest(SocialAuthBase):
self.assertEqual(data["full_name"], self.name) self.assertEqual(data["full_name"], self.name)
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -3345,8 +3344,8 @@ class AppleIdAuthBackendTest(AppleAuthMixin, SocialAuthBase):
headers: Any, headers: Any,
**extra_data: Any, **extra_data: Any,
) -> "TestHttpResponse": ) -> "TestHttpResponse":
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
state = urllib.parse.parse_qs(parsed_url.query)["state"] state = parse_qs(parsed_url.query)["state"]
user_param = json.dumps(account_data_dict) user_param = json.dumps(account_data_dict)
self.client.session.flush() self.client.session.flush()
result = self.client_post( result = self.client_post(
@ -3924,8 +3923,8 @@ class GitHubAuthBackendTest(SocialAuthBase):
expect_noreply_email_allowed: bool = False, expect_noreply_email_allowed: bool = False,
**extra_data: Any, **extra_data: Any,
) -> "TestHttpResponse": ) -> "TestHttpResponse":
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
csrf_state = urllib.parse.parse_qs(parsed_url.query)["state"] csrf_state = parse_qs(parsed_url.query)["state"]
result = self.client_get(self.AUTH_FINISH_URL, dict(state=csrf_state), **headers) result = self.client_get(self.AUTH_FINISH_URL, dict(state=csrf_state), **headers)
if expect_choose_email_screen: if expect_choose_email_screen:
@ -4121,7 +4120,7 @@ class GitHubAuthBackendTest(SocialAuthBase):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -4146,7 +4145,7 @@ class GitHubAuthBackendTest(SocialAuthBase):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -4175,7 +4174,7 @@ class GitHubAuthBackendTest(SocialAuthBase):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -4204,7 +4203,7 @@ class GitHubAuthBackendTest(SocialAuthBase):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -4265,7 +4264,7 @@ class GitHubAuthBackendTest(SocialAuthBase):
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(data["redirect_to"], "/user_uploads/image") self.assertEqual(data["redirect_to"], "/user_uploads/image")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -4348,7 +4347,7 @@ class GitHubAuthBackendTest(SocialAuthBase):
self.assertEqual(data["full_name"], account_data_dict["name"]) self.assertEqual(data["full_name"], account_data_dict["name"])
self.assertEqual(data["subdomain"], "zulip") self.assertEqual(data["subdomain"], "zulip")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
parsed_url = urllib.parse.urlparse(result["Location"]) parsed_url = urlparse(result["Location"])
url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}" url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/")) self.assertTrue(url.startswith("http://zulip.testserver/accounts/login/subdomain/"))
@ -4487,8 +4486,8 @@ class GoogleAuthBackendTest(SocialAuthBase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
redirect_url = result["Location"] redirect_url = result["Location"]
parsed_url = urllib.parse.urlparse(redirect_url) parsed_url = urlparse(redirect_url)
query_params = urllib.parse.parse_qs(parsed_url.query) query_params = parse_qs(parsed_url.query)
self.assertEqual(parsed_url.scheme, "zulip") self.assertEqual(parsed_url.scheme, "zulip")
self.assertEqual(query_params["realm"], ["http://zulip-mobile.testserver"]) self.assertEqual(query_params["realm"], ["http://zulip-mobile.testserver"])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])
@ -4532,8 +4531,8 @@ class GoogleAuthBackendTest(SocialAuthBase):
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
redirect_url = result["Location"] redirect_url = result["Location"]
parsed_url = urllib.parse.urlparse(redirect_url) parsed_url = urlparse(redirect_url)
query_params = urllib.parse.parse_qs(parsed_url.query) query_params = parse_qs(parsed_url.query)
self.assertEqual(parsed_url.scheme, "zulip") self.assertEqual(parsed_url.scheme, "zulip")
self.assertEqual(query_params["realm"], ["http://zulip.testserver"]) self.assertEqual(query_params["realm"], ["http://zulip.testserver"])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])
@ -5530,7 +5529,7 @@ class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
url = result["Location"] url = result["Location"]
parsed_url = urllib.parse.urlparse(url) parsed_url = urlparse(url)
self.assertEqual(parsed_url.path, "/accounts/login/sso/") self.assertEqual(parsed_url.path, "/accounts/login/sso/")
self.assertEqual(parsed_url.query, "param1=value1&params=value2") self.assertEqual(parsed_url.query, "param1=value1&params=value2")
@ -5674,8 +5673,8 @@ class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase):
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
redirect_url = result["Location"] redirect_url = result["Location"]
parsed_url = urllib.parse.urlparse(redirect_url) parsed_url = urlparse(redirect_url)
query_params = urllib.parse.parse_qs(parsed_url.query) query_params = parse_qs(parsed_url.query)
self.assertEqual(parsed_url.scheme, "zulip") self.assertEqual(parsed_url.scheme, "zulip")
self.assertEqual(query_params["realm"], ["http://zulip.testserver"]) self.assertEqual(query_params["realm"], ["http://zulip.testserver"])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])
@ -5723,8 +5722,8 @@ class TestZulipRemoteUserBackend(DesktopFlowTestingLib, ZulipTestCase):
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
redirect_url = result["Location"] redirect_url = result["Location"]
parsed_url = urllib.parse.urlparse(redirect_url) parsed_url = urlparse(redirect_url)
query_params = urllib.parse.parse_qs(parsed_url.query) query_params = parse_qs(parsed_url.query)
self.assertEqual(parsed_url.scheme, "zulip") self.assertEqual(parsed_url.scheme, "zulip")
self.assertEqual(query_params["realm"], ["http://zulip.testserver"]) self.assertEqual(query_params["realm"], ["http://zulip.testserver"])
self.assertEqual(query_params["email"], [self.example_email("hamlet")]) self.assertEqual(query_params["email"], [self.example_email("hamlet")])

View File

@ -1,8 +1,8 @@
import calendar import calendar
import urllib
from datetime import timedelta, timezone from datetime import timedelta, timezone
from typing import TYPE_CHECKING, Any, Dict from typing import TYPE_CHECKING, Any, Dict
from unittest.mock import patch from unittest.mock import patch
from urllib.parse import urlparse
import orjson import orjson
import time_machine import time_machine
@ -1011,7 +1011,7 @@ class HomeTest(ZulipTestCase):
self.assertTrue(result["Location"].endswith("/desktop_home/")) self.assertTrue(result["Location"].endswith("/desktop_home/"))
result = self.client_get("/desktop_home/") result = self.client_get("/desktop_home/")
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
path = urllib.parse.urlparse(result["Location"]).path path = urlparse(result["Location"]).path
self.assertEqual(path, "/") self.assertEqual(path, "/")
@override_settings(SERVER_UPGRADE_NAG_DEADLINE_DAYS=365) @override_settings(SERVER_UPGRADE_NAG_DEADLINE_DAYS=365)

View File

@ -1,9 +1,8 @@
import re import re
import urllib
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import TYPE_CHECKING, List, Optional, Sequence, Union from typing import TYPE_CHECKING, List, Optional, Sequence, Union
from unittest.mock import patch from unittest.mock import patch
from urllib.parse import urlencode from urllib.parse import quote, urlencode
import orjson import orjson
import time_machine import time_machine
@ -2294,9 +2293,7 @@ class MultiuseInviteTest(ZulipTestCase):
result = self.client_post(invite_link, {"email": email}) result = self.client_post(invite_link, {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)

View File

@ -1,10 +1,10 @@
import os import os
import re import re
import urllib
from datetime import timedelta from datetime import timedelta
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from unittest import mock, skipUnless from unittest import mock, skipUnless
from unittest.mock import MagicMock, call, patch from unittest.mock import MagicMock, call, patch
from urllib.parse import quote, quote_plus
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
@ -344,7 +344,7 @@ class TestGenerateRealmCreationLink(ZulipTestCase):
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertEqual( self.assertEqual(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}", f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}",
result["Location"], result["Location"],
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])

View File

@ -1,10 +1,9 @@
import re import re
import time import time
import urllib
from datetime import timedelta from datetime import timedelta
from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from urllib.parse import urlencode from urllib.parse import quote, quote_plus, urlencode, urlparse
import orjson import orjson
from django.conf import settings from django.conf import settings
@ -1096,7 +1095,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
# An unknown message type "fake" produces an error. # An unknown message type "fake" produces an error.
user_profile = self.example_user("hamlet") user_profile = self.example_user("hamlet")
unsubscribe_link = one_click_unsubscribe_link(user_profile, "fake") unsubscribe_link = one_click_unsubscribe_link(user_profile, "fake")
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) result = self.client_get(urlparse(unsubscribe_link).path)
self.assert_in_response("Unknown email unsubscribe request", result) self.assert_in_response("Unknown email unsubscribe request", result)
def test_message_notification_emails_unsubscribe(self) -> None: def test_message_notification_emails_unsubscribe(self) -> None:
@ -1110,7 +1109,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
user_profile.save() user_profile.save()
unsubscribe_link = one_click_unsubscribe_link(user_profile, "missed_messages") unsubscribe_link = one_click_unsubscribe_link(user_profile, "missed_messages")
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) result = self.client_get(urlparse(unsubscribe_link).path)
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
@ -1129,7 +1128,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
# Simulate unsubscribing from the welcome e-mails. # Simulate unsubscribing from the welcome e-mails.
unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome") unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome")
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) result = self.client_get(urlparse(unsubscribe_link).path)
# The welcome email jobs are no longer scheduled. # The welcome email jobs are no longer scheduled.
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
@ -1167,7 +1166,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
# Simulate unsubscribing from digest e-mails. # Simulate unsubscribing from digest e-mails.
unsubscribe_link = one_click_unsubscribe_link(user_profile, "digest") unsubscribe_link = one_click_unsubscribe_link(user_profile, "digest")
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) result = self.client_get(urlparse(unsubscribe_link).path)
# The setting is toggled off, and scheduled jobs have been removed. # The setting is toggled off, and scheduled jobs have been removed.
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
@ -1188,7 +1187,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
user_profile.save() user_profile.save()
unsubscribe_link = one_click_unsubscribe_link(user_profile, "login") unsubscribe_link = one_click_unsubscribe_link(user_profile, "login")
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) result = self.client_get(urlparse(unsubscribe_link).path)
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
@ -1205,7 +1204,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
# Simulate unsubscribing from marketing e-mails. # Simulate unsubscribing from marketing e-mails.
unsubscribe_link = one_click_unsubscribe_link(user_profile, "marketing") unsubscribe_link = one_click_unsubscribe_link(user_profile, "marketing")
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) result = self.client_get(urlparse(unsubscribe_link).path)
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
# Circumvent user_profile caching. # Circumvent user_profile caching.
@ -1224,9 +1223,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
# Simulate unsubscribing from marketing e-mails. # Simulate unsubscribing from marketing e-mails.
unsubscribe_link = one_click_unsubscribe_link(user_profile, "marketing") unsubscribe_link = one_click_unsubscribe_link(user_profile, "marketing")
client = Client(enforce_csrf_checks=True) client = Client(enforce_csrf_checks=True)
result = client.post( result = client.post(urlparse(unsubscribe_link).path, {"List-Unsubscribe": "One-Click"})
urllib.parse.urlparse(unsubscribe_link).path, {"List-Unsubscribe": "One-Click"}
)
self.assertEqual(result.status_code, 200) self.assertEqual(result.status_code, 200)
# Circumvent user_profile caching. # Circumvent user_profile caching.
@ -1255,7 +1252,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(org_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(org_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1398,7 +1395,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1446,7 +1443,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1495,7 +1492,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1555,7 +1552,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1600,7 +1597,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1651,7 +1648,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=35&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=35&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1708,7 +1705,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language={realm_language}&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language={realm_language}&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1774,7 +1771,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1830,7 +1827,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(first_realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={first_string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(first_realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={first_string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1846,7 +1843,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(second_realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={second_string_id}" f"/accounts/new/send_confirm/?email={quote(email)}&realm_name={quote_plus(second_realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={second_string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -2118,9 +2115,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}, **client_kwargs) result = self.client_post("/accounts/home/", {"email": email}, **client_kwargs)
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"], **client_kwargs) result = self.client_get(result["Location"], **client_kwargs)
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2261,9 +2256,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}) result = self.client_post("/accounts/home/", {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2296,9 +2289,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}) result = self.client_post("/accounts/home/", {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2334,9 +2325,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}) result = self.client_post("/accounts/home/", {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2367,9 +2356,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}) result = self.client_post("/accounts/home/", {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2472,9 +2459,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}) result = self.client_post("/accounts/home/", {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2521,7 +2506,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/login/?email={urllib.parse.quote(email)}&already_registered=1" f"/accounts/login/?email={quote(email)}&already_registered=1"
) )
) )
@ -2790,9 +2775,7 @@ class UserSignUpTest(ZulipTestCase):
result = self.client_post("/accounts/home/", {"email": email}) result = self.client_post("/accounts/home/", {"email": email})
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -2974,9 +2957,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3062,9 +3043,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3146,9 +3125,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3223,9 +3200,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3299,9 +3274,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3449,9 +3422,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3504,9 +3475,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3630,9 +3599,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3675,9 +3642,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3837,9 +3802,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3870,9 +3833,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)
@ -3936,9 +3897,7 @@ class UserSignUpTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(f"/accounts/send_confirm/?email={quote(email)}")
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
)
) )
result = self.client_get(result["Location"], subdomain="zephyr") result = self.client_get(result["Location"], subdomain="zephyr")
self.assert_in_response("check your email", result) self.assert_in_response("check your email", result)

View File

@ -1,9 +1,9 @@
import asyncio import asyncio
import socket import socket
import urllib.parse
from functools import wraps from functools import wraps
from typing import Any, Awaitable, Callable, Dict, Optional, TypeVar from typing import Any, Awaitable, Callable, Dict, Optional, TypeVar
from unittest import TestResult, mock from unittest import TestResult, mock
from urllib.parse import urlencode
import orjson import orjson
from asgiref.sync import async_to_sync, sync_to_async from asgiref.sync import async_to_sync, sync_to_async
@ -120,7 +120,7 @@ class EventsTestCase(TornadoWebTestCase):
"last_event_id": -1, "last_event_id": -1,
} }
path = f"/json/events?{urllib.parse.urlencode(data)}" path = f"/json/events?{urlencode(data)}"
def process_events() -> None: def process_events() -> None:
users = [user_profile.id] users = [user_profile.id]

View File

@ -2,10 +2,10 @@ import io
import os import os
import re import re
import time import time
import urllib
from io import StringIO from io import StringIO
from unittest import mock from unittest import mock
from unittest.mock import patch from unittest.mock import patch
from urllib.parse import quote
import orjson import orjson
from django.conf import settings from django.conf import settings
@ -572,7 +572,7 @@ class FileUploadTest(UploadSerializeMixin, ZulipTestCase):
self.login("hamlet") self.login("hamlet")
for expected in ["Здравейте.txt", "test"]: for expected in ["Здравейте.txt", "test"]:
fp = StringIO("bah!") fp = StringIO("bah!")
fp.name = urllib.parse.quote(expected) fp.name = quote(expected)
result = self.client_post("/json/user_uploads", {"f1": fp}) result = self.client_post("/json/user_uploads", {"f1": fp})
response_dict = self.assert_json_success(result) response_dict = self.assert_json_success(result)
@ -590,7 +590,7 @@ class FileUploadTest(UploadSerializeMixin, ZulipTestCase):
("**", "uploaded-file"), ("**", "uploaded-file"),
]: ]:
fp = StringIO("bah!") fp = StringIO("bah!")
fp.name = urllib.parse.quote(uploaded_filename) fp.name = quote(uploaded_filename)
result = self.client_post("/json/user_uploads", {"f1": fp}) result = self.client_post("/json/user_uploads", {"f1": fp})
response_dict = self.assert_json_success(result) response_dict = self.assert_json_success(result)

View File

@ -1,6 +1,5 @@
import os import os
import re import re
import urllib
from io import BytesIO, StringIO from io import BytesIO, StringIO
from urllib.parse import urlparse from urllib.parse import urlparse
@ -258,5 +257,5 @@ class LocalStorageTest(UploadSerializeMixin, ZulipTestCase):
warn_log.output, warn_log.output,
["WARNING:root:not_a_file does not exist. Its entry in the database will be removed."], ["WARNING:root:not_a_file does not exist. Its entry in the database will be removed."],
) )
path_id = urllib.parse.urlparse(url).path path_id = urlparse(url).path
self.assertEqual(delete_export_tarball(path_id), path_id) self.assertEqual(delete_export_tarball(path_id), path_id)

View File

@ -1,9 +1,9 @@
import io import io
import os import os
import re import re
import urllib
from io import BytesIO, StringIO from io import BytesIO, StringIO
from unittest.mock import patch from unittest.mock import patch
from urllib.parse import urlparse
import botocore.exceptions import botocore.exceptions
from django.conf import settings from django.conf import settings
@ -191,7 +191,7 @@ class S3Test(ZulipTestCase):
# In development, this is just a redirect # In development, this is just a redirect
response = self.client_get(url) response = self.client_get(url)
redirect_url = response["Location"] redirect_url = response["Location"]
path = urllib.parse.urlparse(redirect_url).path path = urlparse(redirect_url).path
assert path.startswith("/") assert path.startswith("/")
key = path[len("/") :] key = path[len("/") :]
self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read()) self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read())
@ -200,7 +200,7 @@ class S3Test(ZulipTestCase):
with self.settings(DEVELOPMENT=False): with self.settings(DEVELOPMENT=False):
response = self.client_get(url) response = self.client_get(url)
redirect_url = response["X-Accel-Redirect"] redirect_url = response["X-Accel-Redirect"]
path = urllib.parse.urlparse(redirect_url).path path = urlparse(redirect_url).path
assert path.startswith(prefix) assert path.startswith(prefix)
key = path[len(prefix) :] key = path[len(prefix) :]
self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read()) self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read())
@ -210,7 +210,7 @@ class S3Test(ZulipTestCase):
with self.settings(DEVELOPMENT=False): with self.settings(DEVELOPMENT=False):
response = self.client_get(download_url) response = self.client_get(download_url)
redirect_url = response["X-Accel-Redirect"] redirect_url = response["X-Accel-Redirect"]
path = urllib.parse.urlparse(redirect_url).path path = urlparse(redirect_url).path
assert path.startswith(prefix) assert path.startswith(prefix)
key = path[len(prefix) :] key = path[len(prefix) :]
self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read()) self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read())
@ -230,7 +230,7 @@ class S3Test(ZulipTestCase):
with self.settings(DEVELOPMENT=False): with self.settings(DEVELOPMENT=False):
self.client_get(url_only_url) self.client_get(url_only_url)
redirect_url = response["X-Accel-Redirect"] redirect_url = response["X-Accel-Redirect"]
path = urllib.parse.urlparse(redirect_url).path path = urlparse(redirect_url).path
assert path.startswith(prefix) assert path.startswith(prefix)
key = path[len(prefix) :] key = path[len(prefix) :]
self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read()) self.assertEqual(b"zulip!", bucket.Object(key).get()["Body"].read())
@ -532,5 +532,5 @@ class S3Test(ZulipTestCase):
warn_log.output, warn_log.output,
["WARNING:root:not_a_file does not exist. Its entry in the database will be removed."], ["WARNING:root:not_a_file does not exist. Its entry in the database will be removed."],
) )
path_id = urllib.parse.urlparse(url).path path_id = urlparse(url).path
self.assertEqual(delete_export_tarball(path_id), path_id) self.assertEqual(delete_export_tarball(path_id), path_id)

View File

@ -1,7 +1,7 @@
import logging import logging
import urllib
from contextlib import suppress from contextlib import suppress
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from urllib.parse import unquote
import tornado.web import tornado.web
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
@ -108,7 +108,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler):
# HttpRequest object with the original Tornado request's HTTP # HttpRequest object with the original Tornado request's HTTP
# headers, parameters, etc. # headers, parameters, etc.
environ = fake_wsgi_container.environ(self.request) environ = fake_wsgi_container.environ(self.request)
environ["PATH_INFO"] = urllib.parse.unquote(environ["PATH_INFO"]) environ["PATH_INFO"] = unquote(environ["PATH_INFO"])
# Django WSGIRequest setup code that should match logic from # Django WSGIRequest setup code that should match logic from
# Django's WSGIHandler.__call__ before the call to # Django's WSGIHandler.__call__ before the call to

View File

@ -1,9 +1,8 @@
import logging import logging
import secrets import secrets
import urllib
from functools import wraps from functools import wraps
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, cast from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, cast
from urllib.parse import urlencode from urllib.parse import urlencode, urljoin
import jwt import jwt
import orjson import orjson
@ -115,7 +114,7 @@ def get_safe_redirect_to(url: str, redirect_host: str) -> str:
# Mark as safe to prevent Pysa from surfacing false positives for # Mark as safe to prevent Pysa from surfacing false positives for
# open redirects. In this branch, we have already checked that the URL # open redirects. In this branch, we have already checked that the URL
# points to the specified 'redirect_host', or is relative. # points to the specified 'redirect_host', or is relative.
return urllib.parse.urljoin(redirect_host, mark_sanitized(url)) return urljoin(redirect_host, mark_sanitized(url))
else: else:
return redirect_host return redirect_host
@ -478,7 +477,7 @@ def create_response_for_otp_flow(
} }
# We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs
response = HttpResponse(status=302) response = HttpResponse(status=302)
response["Location"] = append_url_query_string("zulip://login", urllib.parse.urlencode(params)) response["Location"] = append_url_query_string("zulip://login", urlencode(params))
return response return response
@ -628,7 +627,7 @@ def oauth_redirect_to_root(
params = {**params, **extra_url_params} params = {**params, **extra_url_params}
return redirect(append_url_query_string(main_site_url, urllib.parse.urlencode(params))) return redirect(append_url_query_string(main_site_url, urlencode(params)))
def handle_desktop_flow( def handle_desktop_flow(

View File

@ -1,7 +1,7 @@
import os import os
import urllib
from contextlib import suppress from contextlib import suppress
from typing import Optional from typing import Optional
from urllib.parse import urlencode
import orjson import orjson
from django.conf import settings from django.conf import settings
@ -121,7 +121,7 @@ def generate_all_emails(request: HttpRequest) -> HttpResponse:
# Verification for new email # Verification for new email
result = client.patch( result = client.patch(
"/json/settings", "/json/settings",
urllib.parse.urlencode({"email": "hamlets-new@zulip.com"}), urlencode({"email": "hamlets-new@zulip.com"}),
content_type="application/x-www-form-urlencoded", content_type="application/x-www-form-urlencoded",
HTTP_HOST=realm.host, HTTP_HOST=realm.host,
) )

View File

@ -1,5 +1,4 @@
import logging import logging
import urllib
from contextlib import suppress from contextlib import suppress
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode, urljoin
@ -1109,7 +1108,7 @@ def find_account(
# Note: Show all the emails in the result otherwise this # Note: Show all the emails in the result otherwise this
# feature can be used to ascertain which email addresses # feature can be used to ascertain which email addresses
# are associated with Zulip. # are associated with Zulip.
data = urllib.parse.urlencode({"emails": ",".join(emails)}) data = urlencode({"emails": ",".join(emails)})
return redirect(append_url_query_string(url, data)) return redirect(append_url_query_string(url, data))
else: else:
form = FindMyTeamForm() form = FindMyTeamForm()

View File

@ -1,7 +1,7 @@
import logging import logging
import urllib
from contextlib import suppress from contextlib import suppress
from typing import Type from typing import Type
from urllib.parse import urlparse
import orjson import orjson
from circuitbreaker import CircuitBreakerError, circuit from circuitbreaker import CircuitBreakerError, circuit
@ -34,7 +34,7 @@ def sentry_tunnel(
try: try:
envelope_header_line, envelope_items = request.body.split(b"\n", 1) envelope_header_line, envelope_items = request.body.split(b"\n", 1)
envelope_header = to_wild_value("envelope_header", envelope_header_line.decode("utf-8")) envelope_header = to_wild_value("envelope_header", envelope_header_line.decode("utf-8"))
dsn = urllib.parse.urlparse(envelope_header["dsn"].tame(check_url)) dsn = urlparse(envelope_header["dsn"].tame(check_url))
except Exception: except Exception:
raise JsonableError(_("Invalid request format")) raise JsonableError(_("Invalid request format"))

View File

@ -1,4 +1,4 @@
import urllib from urllib.parse import urlencode
from typing_extensions import override from typing_extensions import override
@ -15,7 +15,7 @@ class LibratoHookTests(WebhookTestCase):
def get_body(self, fixture_name: str) -> str: def get_body(self, fixture_name: str) -> str:
if self.IS_ATTACHMENT: if self.IS_ATTACHMENT:
return self.webhook_fixture_data("librato", fixture_name, file_type="json") return self.webhook_fixture_data("librato", fixture_name, file_type="json")
return urllib.parse.urlencode( return urlencode(
{"payload": self.webhook_fixture_data("librato", fixture_name, file_type="json")} {"payload": self.webhook_fixture_data("librato", fixture_name, file_type="json")}
) )

View File

@ -1,4 +1,4 @@
import urllib from urllib.parse import urlencode
from typing_extensions import override from typing_extensions import override
@ -131,6 +131,6 @@ one or more new messages.
@override @override
def get_body(self, fixture_name: str) -> str: def get_body(self, fixture_name: str) -> str:
return urllib.parse.urlencode( return urlencode(
{"payload": self.webhook_fixture_data("travis", fixture_name, file_type="json")} {"payload": self.webhook_fixture_data("travis", fixture_name, file_type="json")}
) )

View File

@ -10,7 +10,6 @@ import socket
import tempfile import tempfile
import threading import threading
import time import time
import urllib
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import defaultdict, deque from collections import defaultdict, deque
from datetime import timedelta from datetime import timedelta
@ -31,6 +30,7 @@ from typing import (
Type, Type,
TypeVar, TypeVar,
) )
from urllib.parse import urlparse
import orjson import orjson
import sentry_sdk import sentry_sdk
@ -1132,7 +1132,7 @@ class DeferredWorker(QueueProcessingWorker):
assert public_url is not None assert public_url is not None
# Update the extra_data field now that the export is complete. # Update the extra_data field now that the export is complete.
extra_data["export_path"] = urllib.parse.urlparse(public_url).path extra_data["export_path"] = urlparse(public_url).path
export_event.extra_data = extra_data export_event.extra_data = extra_data
export_event.save(update_fields=["extra_data"]) export_event.save(update_fields=["extra_data"])