From 717cf60edfbb0527570537740ca6fea6e21b4ab0 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Wed, 11 Jun 2025 16:56:58 -0700 Subject: [PATCH] =?UTF-8?q?python:=20Use=20Django=205.2=20reverse(?= =?UTF-8?q?=E2=80=A6,=20query=3D=E2=80=A6).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders Kaseorg --- corporate/lib/activity.py | 14 +++----------- corporate/lib/decorator.py | 8 ++++---- corporate/lib/stripe.py | 6 +----- corporate/views/portico.py | 3 +-- corporate/views/remote_billing_page.py | 2 +- corporate/views/support.py | 6 ++---- zerver/tests/test_invite.py | 4 ++-- zerver/views/auth.py | 23 +++++------------------ zerver/views/registration.py | 25 +++++++------------------ zproject/backends.py | 7 ++----- 10 files changed, 28 insertions(+), 70 deletions(-) diff --git a/corporate/lib/activity.py b/corporate/lib/activity.py index d2112f8146..4d9c200f72 100644 --- a/corporate/lib/activity.py +++ b/corporate/lib/activity.py @@ -4,7 +4,6 @@ from dataclasses import dataclass from datetime import datetime from decimal import Decimal from typing import Any -from urllib.parse import urlencode from django.conf import settings from django.db import connection @@ -19,7 +18,6 @@ from psycopg2.sql import Composable from corporate.models.licenses import LicenseLedger from corporate.models.plans import CustomerPlan from zerver.lib.pysa import mark_sanitized -from zerver.lib.url_encoding import append_url_query_string from zerver.models import Realm from zilencer.models import ( RemoteCustomerUserCount, @@ -139,16 +137,12 @@ def realm_stats_link(realm_str: str) -> Markup: def user_support_link(email: str) -> Markup: - support_url = reverse("support") - query = urlencode({"q": email}) - url = append_url_query_string(support_url, query) + url = reverse("support", query={"q": email}) return Markup('').format(url=url) def realm_support_link(realm_str: str) -> Markup: - support_url = reverse("support") - query = urlencode({"q": realm_str}) - url = append_url_query_string(support_url, query) + url = reverse("support", query={"q": realm_str}) return Markup('{realm}').format(url=url, realm=realm_str) @@ -166,9 +160,7 @@ def remote_installation_stats_link(server_id: int) -> Markup: def remote_installation_support_link(hostname: str) -> Markup: - support_url = reverse("remote_servers_support") - query = urlencode({"q": hostname}) - url = append_url_query_string(support_url, query) + url = reverse("remote_servers_support", query={"q": hostname}) return Markup('').format(url=url) diff --git a/corporate/lib/decorator.py b/corporate/lib/decorator.py index 93cacb8398..11dc89a940 100644 --- a/corporate/lib/decorator.py +++ b/corporate/lib/decorator.py @@ -203,11 +203,11 @@ def authenticated_remote_server_management_endpoint( # That means that we can do it universally whether the user has an expired # identity_dict, or just lacks any form of authentication info at all - there # are no security concerns since this is just a local redirect. - url = reverse("remote_billing_legacy_server_login") page_type = get_next_page_param_from_request_path(request) - if page_type is not None: - query = urlencode({"next_page": page_type}) - url = append_url_query_string(url, query) + url = reverse( + "remote_billing_legacy_server_login", + query=None if page_type is None else {"next_page": page_type}, + ) # Return error for AJAX requests with url. if ( diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index afeb36308a..eaccedaffc 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -52,7 +52,6 @@ from zerver.lib.send_email import ( send_email_to_users_with_billing_access_and_realm_owners, ) from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime -from zerver.lib.url_encoding import append_url_query_string from zerver.lib.utils import assert_is_not_none from zerver.models import Realm, RealmAuditLog, Stream, UserProfile from zerver.models.realm_audit_logs import AuditLogEventType @@ -378,10 +377,7 @@ def payment_method_string(stripe_customer: stripe.Customer) -> str: def build_support_url(support_view: str, query_text: str) -> str: support_realm_url = get_realm(settings.STAFF_SUBDOMAIN).url - support_url = urljoin(support_realm_url, reverse(support_view)) - query = urlencode({"q": query_text}) - support_url = append_url_query_string(support_url, query) - return support_url + return urljoin(support_realm_url, reverse(support_view, query={"q": query_text})) def get_configured_fixed_price_plan_offer( diff --git a/corporate/views/portico.py b/corporate/views/portico.py index 62c18fe853..cbcb0f6240 100644 --- a/corporate/views/portico.py +++ b/corporate/views/portico.py @@ -1,6 +1,5 @@ from dataclasses import asdict, dataclass from typing import TYPE_CHECKING -from urllib.parse import urlencode import orjson from django.conf import settings @@ -104,7 +103,7 @@ def plans_view(request: HttpRequest) -> HttpResponse: ) if is_subdomain_root_or_alias(request): # If we're on the root domain, we make this link first ask you which organization. - context.sponsorship_url = f"/accounts/go/?{urlencode({'next': context.sponsorship_url})}" + context.sponsorship_url = reverse("realm_redirect", query={"next": context.sponsorship_url}) if realm is not None: if realm.plan_type == Realm.PLAN_TYPE_SELF_HOSTED and settings.PRODUCTION: diff --git a/corporate/views/remote_billing_page.py b/corporate/views/remote_billing_page.py index 2cb1c29aa4..9fb407d337 100644 --- a/corporate/views/remote_billing_page.py +++ b/corporate/views/remote_billing_page.py @@ -611,7 +611,7 @@ def remote_billing_legacy_server_confirm_login( raise RemoteBillingAuthenticationError except (RemoteBillingIdentityExpiredError, RemoteBillingAuthenticationError): return HttpResponseRedirect( - reverse("remote_billing_legacy_server_login") + f"?next_page={next_page}" + reverse("remote_billing_legacy_server_login", query={"next_page": next_page}) ) rate_limit_error_response = check_rate_limits(request, remote_server) diff --git a/corporate/views/support.py b/corporate/views/support.py index 4cdd25f16d..e64a0d6762 100644 --- a/corporate/views/support.py +++ b/corporate/views/support.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from datetime import timedelta from operator import attrgetter from typing import Annotated, Any, Literal -from urllib.parse import urlencode, urlsplit +from urllib.parse import urlsplit from django import forms from django.conf import settings @@ -550,9 +550,7 @@ def support( request.session["success_message"] = ( f"Subdomain changed from {old_subdomain} to {new_subdomain}" ) - return HttpResponseRedirect( - reverse("support") + "?" + urlencode({"q": new_subdomain}) - ) + return HttpResponseRedirect(reverse("support", query={"q": new_subdomain})) elif status is not None: if status == "active": do_send_realm_reactivation_email(realm, acting_user=acting_user) diff --git a/zerver/tests/test_invite.py b/zerver/tests/test_invite.py index 4b6b6e6548..523aa398d3 100644 --- a/zerver/tests/test_invite.py +++ b/zerver/tests/test_invite.py @@ -3,7 +3,7 @@ from collections.abc import Sequence from datetime import datetime, timedelta from typing import TYPE_CHECKING from unittest.mock import patch -from urllib.parse import quote, urlencode +from urllib.parse import quote import orjson import time_machine @@ -2074,7 +2074,7 @@ so we didn't send them an invitation. We did send invitations to everyone else!" self.assertEqual(response.status_code, 302) self.assertEqual( response["Location"], - reverse("login") + "?" + urlencode({"email": email, "already_registered": 1}), + reverse("login", query={"email": email, "already_registered": 1}), ) def test_confirmation_key_cant_be_reused(self) -> None: diff --git a/zerver/views/auth.py b/zerver/views/auth.py index 1c40c36304..00a7b894a3 100644 --- a/zerver/views/auth.py +++ b/zerver/views/auth.py @@ -624,7 +624,6 @@ def oauth_redirect_to_root( url: str, sso_type: str, is_signup: bool, - extra_url_params: Mapping[str, str], # Protect the above parameters from being processed as kwargs # provided by @typed_endpoint by marking them as mandatory # positional parameters. @@ -662,8 +661,6 @@ def oauth_redirect_to_root( if next: params["next"] = next - params = {**params, **extra_url_params} - return redirect(append_url_query_string(main_site_url, urlencode(params))) @@ -691,8 +688,7 @@ def start_remote_user_sso(request: HttpRequest) -> HttpResponse: /accounts/login/sso may have Apache intercepting requests to it to do authentication, so we need this additional endpoint. """ - query = request.META["QUERY_STRING"] - return redirect(append_url_query_string(reverse(remote_user_sso), query)) + return redirect(reverse(remote_user_sso, query=request.GET)) @handle_desktop_flow @@ -701,7 +697,6 @@ def start_social_login( backend: str, extra_arg: str | None = None, ) -> HttpResponse: - backend_url = reverse("social:begin", args=[backend]) extra_url_params: dict[str, str] = {} if backend == "saml": if not SAMLAuthBackend.check_config(): @@ -730,10 +725,9 @@ def start_social_login( return oauth_redirect_to_root( request, - backend_url, + reverse("social:begin", args=[backend], query=extra_url_params), "social", False, - extra_url_params, ) @@ -743,7 +737,6 @@ def start_social_signup( backend: str, extra_arg: str | None = None, ) -> HttpResponse: - backend_url = reverse("social:begin", args=[backend]) extra_url_params: dict[str, str] = {} if backend == "saml": if not SAMLAuthBackend.check_config(): @@ -757,10 +750,9 @@ def start_social_signup( extra_url_params = {"idp": extra_arg} return oauth_redirect_to_root( request, - backend_url, + reverse("social:begin", args=[backend], query=extra_url_params), "social", True, - extra_url_params, ) @@ -922,10 +914,7 @@ def login_page( redirect_to = get_safe_redirect_to(next, request.user.realm.url) return HttpResponseRedirect(redirect_to) if is_subdomain_root_or_alias(request) and settings.ROOT_DOMAIN_LANDING_PAGE: - redirect_url = reverse("realm_redirect") - if request.GET: - redirect_url = append_url_query_string(redirect_url, request.GET.urlencode()) - return HttpResponseRedirect(redirect_url) + return HttpResponseRedirect(reverse("realm_redirect", query=request.GET)) realm = get_realm_from_request(request) if realm and realm.deactivated: @@ -1256,9 +1245,7 @@ def logout_view(request: HttpRequest) -> HttpResponse: def password_reset(request: HttpRequest) -> HttpResponse: if is_subdomain_root_or_alias(request) and settings.ROOT_DOMAIN_LANDING_PAGE: - redirect_url = append_url_query_string( - reverse("realm_redirect"), urlencode({"next": reverse("password_reset")}) - ) + redirect_url = reverse("realm_redirect", query={"next": reverse("password_reset")}) return HttpResponseRedirect(redirect_url) try: diff --git a/zerver/views/registration.py b/zerver/views/registration.py index 8b7637beca..9bda4dcd64 100644 --- a/zerver/views/registration.py +++ b/zerver/views/registration.py @@ -771,10 +771,7 @@ def registration_helper( # user-friendly error message, but it doesn't # particularly matter, because the registration form # is hidden for most users. - view_url = reverse("login") - query = urlencode({"email": email}) - redirect_url = append_url_query_string(view_url, query) - return HttpResponseRedirect(redirect_url) + return HttpResponseRedirect(reverse("login", query={"email": email})) else: assert isinstance(user, UserProfile) user_profile = user @@ -1032,11 +1029,7 @@ def send_confirm_registration_email( def redirect_to_email_login_url(email: str) -> HttpResponseRedirect: - login_url = reverse("login") - redirect_url = append_url_query_string( - login_url, urlencode({"email": email, "already_registered": 1}) - ) - return HttpResponseRedirect(redirect_url) + return HttpResponseRedirect(reverse("login", query={"email": email, "already_registered": 1})) @typed_endpoint @@ -1305,17 +1298,16 @@ def create_realm(request: HttpRequest, creation_key: str | None = None) -> HttpR if key_record is not None: key_record.delete() - new_realm_send_confirm_url = reverse("new_realm_send_confirm") - query = urlencode( - { + url = reverse( + "new_realm_send_confirm", + query={ "email": email, "realm_name": realm_name, "realm_type": realm_type, "realm_default_language": realm_default_language, "realm_subdomain": realm_subdomain, - } + }, ) - url = append_url_query_string(new_realm_send_confirm_url, query) return HttpResponseRedirect(url) else: default_language_code = get_browser_language_code(request) @@ -1468,10 +1460,7 @@ def accounts_home( if settings.CORPORATE_ENABLED: return render(request, "500.html", status=500) return config_error(request, "smtp") - signup_send_confirm_url = reverse("signup_send_confirm") - query = urlencode({"email": email}) - url = append_url_query_string(signup_send_confirm_url, query) - return HttpResponseRedirect(url) + return HttpResponseRedirect(reverse("signup_send_confirm", query={"email": email})) else: form = HomepageForm(realm=realm) diff --git a/zproject/backends.py b/zproject/backends.py index 3d7d302dc3..b581df0f1a 100644 --- a/zproject/backends.py +++ b/zproject/backends.py @@ -19,7 +19,6 @@ from abc import ABC, abstractmethod from collections.abc import Callable from email.headerregistry import Address from typing import Any, TypedDict, TypeVar, cast -from urllib.parse import urlencode import magic import orjson @@ -88,7 +87,6 @@ from zerver.lib.request import RequestNotes from zerver.lib.sessions import delete_user_sessions from zerver.lib.subdomains import get_subdomain from zerver.lib.types import ProfileDataElementUpdateDict -from zerver.lib.url_encoding import append_url_query_string from zerver.lib.users import check_full_name, validate_user_custom_profile_field from zerver.models import ( CustomProfileField, @@ -1637,9 +1635,8 @@ def redirect_to_login(realm: Realm) -> HttpResponseRedirect: def redirect_deactivated_user_to_login(realm: Realm, email: str) -> HttpResponseRedirect: # Specifying the template name makes sure that the user is not redirected to dev_login in case of # a deactivated account on a test server. - login_url = reverse("login_page", kwargs={"template_name": "zerver/login.html"}) - redirect_url = append_url_query_string( - realm.url + login_url, urlencode({"is_deactivated": email}) + redirect_url = realm.url + reverse( + "login_page", kwargs={"template_name": "zerver/login.html"}, query={"is_deactivated": email} ) return HttpResponseRedirect(redirect_url)