From 91a86c24f55a03f05c0b395218061c722710238b Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Fri, 12 Jun 2020 18:34:01 -0700 Subject: [PATCH] python: Replace None defaults with empty collections where appropriate. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use read-only types (List ↦ Sequence, Dict ↦ Mapping, Set ↦ AbstractSet) to guard against accidental mutation of the default value. Signed-off-by: Anders Kaseorg --- confirmation/models.py | 9 +++--- .../check_user_zephyr_mirror_liveness | 4 +-- tools/generate-integration-docs-screenshot | 6 ++-- tools/lib/html_branches.py | 11 +++---- tools/run-dev.py | 5 ++- zerver/data_import/import_util.py | 7 ++--- zerver/lib/actions.py | 21 ++++++------- .../bugdown/api_arguments_table_generator.py | 6 ++-- .../api_return_values_table_generator.py | 4 +-- zerver/lib/bugdown/fenced_code.py | 6 ++-- zerver/lib/html_to_text.py | 6 ++-- zerver/lib/integrations.py | 14 ++++----- zerver/lib/remote_server.py | 7 ++--- zerver/lib/request.py | 31 +++++++++++++------ zerver/lib/response.py | 11 +++---- zerver/lib/send_email.py | 10 +++--- zerver/openapi/python_examples.py | 8 ++--- zerver/templatetags/app_filters.py | 7 ++--- zerver/tests/test_auth_backends.py | 4 +-- zerver/tests/test_email_notifications.py | 9 +++--- zerver/tests/test_subs.py | 9 +++--- zerver/views/report.py | 7 ++--- zerver/webhooks/trello/view/board_actions.py | 9 +++--- zerver/webhooks/trello/view/card_actions.py | 9 +++--- 24 files changed, 101 insertions(+), 119 deletions(-) diff --git a/confirmation/models.py b/confirmation/models.py index 8471ed8940..27d7d53a07 100644 --- a/confirmation/models.py +++ b/confirmation/models.py @@ -4,7 +4,7 @@ __revision__ = '$Id: models.py 28 2009-10-22 15:03:02Z jarek.zgoda $' import datetime import string from random import SystemRandom -from typing import Dict, Optional, Union +from typing import Mapping, Optional, Union from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey @@ -65,7 +65,7 @@ def get_object_from_key(confirmation_key: str, def create_confirmation_link(obj: ContentType, host: str, confirmation_type: int, - url_args: Optional[Dict[str, str]]=None) -> str: + url_args: Mapping[str, str] = {}) -> str: key = generate_key() realm = None if hasattr(obj, 'realm'): @@ -79,9 +79,8 @@ def create_confirmation_link(obj: ContentType, host: str, def confirmation_url(confirmation_key: str, host: str, confirmation_type: int, - url_args: Optional[Dict[str, str]]=None) -> str: - if url_args is None: - url_args = {} + url_args: Mapping[str, str] = {}) -> str: + url_args = dict(url_args) url_args['confirmation_key'] = confirmation_key return '%s%s%s' % (settings.EXTERNAL_URI_SCHEME, host, reverse(_properties[confirmation_type].url_name, kwargs=url_args)) diff --git a/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness b/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness index 59c4cd4303..6bd60fe985 100755 --- a/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness +++ b/puppet/zulip_ops/files/nagios_plugins/zulip_zephyr_mirror/check_user_zephyr_mirror_liveness @@ -24,7 +24,7 @@ sys.path.append('/home/zulip/deployments/current/zerver') django.setup() -from typing import Any, Dict, Optional, Set +from typing import AbstractSet, Any, Dict from zerver.models import UserActivity @@ -35,7 +35,7 @@ states: Dict[str, int] = { "UNKNOWN": 3, } -def report(state: str, short_msg: str, too_old: Optional[Set[Any]] = None) -> None: +def report(state: str, short_msg: str, too_old: AbstractSet[Any] = set()) -> None: too_old_data = "" if too_old: too_old_data = "\nLast call to get_message for recently out of date mirrors:\n" + "\n".join( diff --git a/tools/generate-integration-docs-screenshot b/tools/generate-integration-docs-screenshot index 44aefa6fd8..02f89089e8 100755 --- a/tools/generate-integration-docs-screenshot +++ b/tools/generate-integration-docs-screenshot @@ -129,8 +129,7 @@ def send_bot_payload_message(bot: UserProfile, integration: WebhookIntegration, data = '' headers = get_requests_headers(integration.name, fixture_name) - if config.custom_headers: - headers.update(config.custom_headers) + headers.update(config.custom_headers) if config.use_basic_auth: credentials = base64.b64encode(f'{bot.email}:{bot.api_key}'.encode('utf8')).decode('utf8') auth = f'basic {credentials}' @@ -140,8 +139,7 @@ def send_bot_payload_message(bot: UserProfile, integration: WebhookIntegration, stream = integration.stream_name or 'devel' url = f"{bot.bot_owner.realm.uri}/{integration.url}" params = {'api_key': bot.api_key, 'stream': stream} - if config.extra_params: - params.update(config.extra_params) + params.update(config.extra_params) extra_args = {} if not json_fixture and data: diff --git a/tools/lib/html_branches.py b/tools/lib/html_branches.py index ceb588e46d..56d7311889 100644 --- a/tools/lib/html_branches.py +++ b/tools/lib/html_branches.py @@ -1,6 +1,6 @@ import re from collections import defaultdict -from typing import Dict, List, Optional, Set +from typing import Dict, List, Optional, Sequence, Set from .template_parser import FormattedException, Token, tokenize @@ -132,12 +132,9 @@ def html_branches(text: str, fn: Optional[str] = None) -> List[HtmlTreeBranch]: tree = html_tag_tree(text, fn) branches: List[HtmlTreeBranch] = [] - def walk(node: Node, tag_info_list: Optional[List[TagInfo]] = None) -> None: + def walk(node: Node, tag_info_list: Sequence[TagInfo] = []) -> None: info = get_tag_info(node.token) - if tag_info_list is None: - tag_info_list = [info] - else: - tag_info_list = tag_info_list[:] + [info] + tag_info_list = [*tag_info_list, info] if node.children: for child in node.children: @@ -147,7 +144,7 @@ def html_branches(text: str, fn: Optional[str] = None) -> List[HtmlTreeBranch]: branches.append(tree_branch) for node in tree.children: - walk(node, None) + walk(node, []) return branches diff --git a/tools/run-dev.py b/tools/run-dev.py index 8ca71c13c5..6b8b226255 100755 --- a/tools/run-dev.py +++ b/tools/run-dev.py @@ -6,6 +6,7 @@ import signal import subprocess import sys import traceback +from typing import Any, Callable, Generator, List, Sequence from urllib.parse import urlunparse # check for the venv @@ -18,7 +19,6 @@ from tornado.ioloop import IOLoop TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(TOOLS_DIR)) -from typing import Any, Callable, Generator, List, Optional from tools.lib.test_script import assert_provisioning_status_ok @@ -212,9 +212,8 @@ class BaseHandler(web.RequestHandler): target_port: int def _add_request_headers( - self, exclude_lower_headers_list: Optional[List[str]] = None, + self, exclude_lower_headers_list: Sequence[str] = [], ) -> httputil.HTTPHeaders: - exclude_lower_headers_list = exclude_lower_headers_list or [] headers = httputil.HTTPHeaders() for header, v in self.request.headers.get_all(): if header.lower() not in exclude_lower_headers_list: diff --git a/zerver/data_import/import_util.py b/zerver/data_import/import_util.py index 31f235df09..b0275822a8 100644 --- a/zerver/data_import/import_util.py +++ b/zerver/data_import/import_util.py @@ -3,7 +3,7 @@ import os import random import shutil import traceback -from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, TypeVar +from typing import AbstractSet, Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, TypeVar import requests import ujson @@ -363,12 +363,9 @@ def build_usermessages(zerver_usermessage: List[ZerverFieldsT], mentioned_user_ids: List[int], message_id: int, is_private: bool, - long_term_idle: Optional[Set[int]]=None) -> Tuple[int, int]: + long_term_idle: AbstractSet[int] = set()) -> Tuple[int, int]: user_ids = subscriber_map.get(recipient_id, set()) - if long_term_idle is None: - long_term_idle = set() - user_messages_created = 0 user_messages_skipped = 0 if user_ids: diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 2ef6c03221..d05c5d2088 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -1059,7 +1059,7 @@ class RecipientInfoResult(TypedDict): def get_recipient_info(recipient: Recipient, sender_id: int, stream_topic: Optional[StreamTopicTarget], - possibly_mentioned_user_ids: Optional[Set[int]]=None, + possibly_mentioned_user_ids: AbstractSet[int]=set(), possible_wildcard_mention: bool=True) -> RecipientInfoResult: stream_push_user_ids: Set[int] = set() stream_email_user_ids: Set[int] = set() @@ -1151,14 +1151,13 @@ def get_recipient_info(recipient: Recipient, message_to_user_id_set = set(message_to_user_ids) user_ids = set(message_to_user_id_set) - if possibly_mentioned_user_ids: - # Important note: Because we haven't rendered bugdown yet, we - # don't yet know which of these possibly-mentioned users was - # actually mentioned in the message (in other words, the - # mention syntax might have been in a code block or otherwise - # escaped). `get_ids_for` will filter these extra user rows - # for our data structures not related to bots - user_ids |= possibly_mentioned_user_ids + # Important note: Because we haven't rendered bugdown yet, we + # don't yet know which of these possibly-mentioned users was + # actually mentioned in the message (in other words, the + # mention syntax might have been in a code block or otherwise + # escaped). `get_ids_for` will filter these extra user rows + # for our data structures not related to bots + user_ids |= possibly_mentioned_user_ids if user_ids: query = UserProfile.objects.filter( @@ -2759,7 +2758,7 @@ def get_last_message_id() -> int: SubT = Tuple[List[Tuple[UserProfile, Stream]], List[Tuple[UserProfile, Stream]]] def bulk_add_subscriptions(streams: Iterable[Stream], users: Iterable[UserProfile], - color_map: Optional[Dict[str, str]]=None, + color_map: Mapping[str, str]={}, from_stream_creation: bool=False, acting_user: Optional[UserProfile]=None) -> SubT: users = list(users) @@ -2799,7 +2798,7 @@ def bulk_add_subscriptions(streams: Iterable[Stream], subs_to_add: List[Tuple[Subscription, Stream]] = [] for (user_profile, recipient_id, stream) in new_subs: - if color_map is not None and stream.name in color_map: + if stream.name in color_map: color = color_map[stream.name] else: color = pick_color(user_profile, subs_by_user[user_profile.id]) diff --git a/zerver/lib/bugdown/api_arguments_table_generator.py b/zerver/lib/bugdown/api_arguments_table_generator.py index 5aab676008..9793f91c7f 100644 --- a/zerver/lib/bugdown/api_arguments_table_generator.py +++ b/zerver/lib/bugdown/api_arguments_table_generator.py @@ -1,7 +1,7 @@ import json import os import re -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Mapping import markdown from django.utils.html import escape as escape_html @@ -14,9 +14,7 @@ REGEXP = re.compile(r'\{generate_api_arguments_table\|\s*(.+?)\s*\|\s*(.+)\s*\}' class MarkdownArgumentsTableGenerator(Extension): - def __init__(self, configs: Optional[Dict[str, Any]]=None) -> None: - if configs is None: - configs = {} + def __init__(self, configs: Mapping[str, Any] = {}) -> None: self.config = { 'base_path': ['.', 'Default location from which to evaluate relative paths for the JSON files.'], } diff --git a/zerver/lib/bugdown/api_return_values_table_generator.py b/zerver/lib/bugdown/api_return_values_table_generator.py index 9c56ac3790..e7c4286eab 100644 --- a/zerver/lib/bugdown/api_return_values_table_generator.py +++ b/zerver/lib/bugdown/api_return_values_table_generator.py @@ -1,5 +1,5 @@ import re -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Mapping, Optional import markdown from markdown.extensions import Extension @@ -11,7 +11,7 @@ REGEXP = re.compile(r'\{generate_return_values_table\|\s*(.+?)\s*\|\s*(.+)\s*\}' class MarkdownReturnValuesTableGenerator(Extension): - def __init__(self, configs: Optional[Dict[str, Any]]=None) -> None: + def __init__(self, configs: Mapping[str, Any] = {}) -> None: self.config: Dict[str, Any] = {} def extendMarkdown(self, md: markdown.Markdown, md_globals: Dict[str, Any]) -> None: diff --git a/zerver/lib/bugdown/fenced_code.py b/zerver/lib/bugdown/fenced_code.py index 226d3b8be0..4823d728d4 100644 --- a/zerver/lib/bugdown/fenced_code.py +++ b/zerver/lib/bugdown/fenced_code.py @@ -76,7 +76,7 @@ Dependencies: """ import re -from typing import Any, Dict, Iterable, List, MutableSequence, Optional +from typing import Any, Dict, Iterable, List, Mapping, MutableSequence, Optional import markdown from django.utils.html import escape @@ -128,9 +128,7 @@ CODE_VALIDATORS = { } class FencedCodeExtension(markdown.Extension): - def __init__(self, config: Optional[Dict[str, Any]]=None) -> None: - if config is None: - config = {} + def __init__(self, config: Mapping[str, Any] = {}) -> None: self.config = { 'run_content_validators': [ config.get('run_content_validators', False), diff --git a/zerver/lib/html_to_text.py b/zerver/lib/html_to_text.py index 9a450f5dc8..0cd1e9c819 100644 --- a/zerver/lib/html_to_text.py +++ b/zerver/lib/html_to_text.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Mapping from bs4 import BeautifulSoup from django.http import HttpRequest @@ -7,7 +7,7 @@ from django.utils.html import escape from zerver.lib.cache import cache_with_key, open_graph_description_cache_key -def html_to_text(content: str, tags: Optional[Dict[str, str]]=None) -> str: +def html_to_text(content: str, tags: Mapping[str, str] = {'p': ' | '}) -> str: bs = BeautifulSoup(content, features='lxml') # Skip any admonition (warning) blocks, since they're # usually something about users needing to be an @@ -21,8 +21,6 @@ def html_to_text(content: str, tags: Optional[Dict[str, str]]=None) -> str: tag.clear() text = '' - if tags is None: - tags = {'p': ' | '} for element in bs.find_all(tags.keys()): # Ignore empty elements if not element.text: diff --git a/zerver/lib/integrations.py b/zerver/lib/integrations.py index 8a1c81e1fd..bb289d88ea 100644 --- a/zerver/lib/integrations.py +++ b/zerver/lib/integrations.py @@ -1,6 +1,6 @@ import os -from dataclasses import dataclass -from typing import Any, Dict, List, Optional, Tuple +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional, Sequence, Tuple from django.conf.urls import url from django.contrib.staticfiles.storage import staticfiles_storage @@ -56,7 +56,7 @@ class Integration: logo: Optional[str]=None, secondary_line_text: Optional[str]=None, display_name: Optional[str]=None, doc: Optional[str]=None, stream_name: Optional[str]=None, legacy: bool=False, - config_options: Optional[List[Tuple[str, str, Validator]]]=None) -> None: + config_options: Sequence[Tuple[str, str, Validator]]=[]) -> None: self.name = name self.client_name = client_name self.secondary_line_text = secondary_line_text @@ -66,8 +66,6 @@ class Integration: # Note: Currently only incoming webhook type bots use this list for # defining how the bot's BotConfigData should be. Embedded bots follow # a different approach. - if config_options is None: - config_options = [] self.config_options = config_options for category in categories: @@ -160,7 +158,7 @@ class WebhookIntegration(Integration): function: Optional[str]=None, url: Optional[str]=None, display_name: Optional[str]=None, doc: Optional[str]=None, stream_name: Optional[str]=None, legacy: bool=False, - config_options: Optional[List[Tuple[str, str, Validator]]]=None) -> None: + config_options: Sequence[Tuple[str, str, Validator]]=[]) -> None: if client_name is None: client_name = self.DEFAULT_CLIENT_NAME.format(name=name.title()) super().__init__( @@ -210,9 +208,9 @@ class ScreenshotConfig: bot_name: Optional[str] = None payload_as_query_param: bool = False payload_param_name: str = 'payload' - extra_params: Optional[Dict[str, str]] = None + extra_params: Dict[str, str] = field(default_factory=dict) use_basic_auth: bool = False - custom_headers: Optional[Dict[str, str]] = None + custom_headers: Dict[str, str] = field(default_factory=dict) def get_fixture_and_image_paths(integration: WebhookIntegration, screenshot_config: ScreenshotConfig) -> Tuple[str, str]: diff --git a/zerver/lib/remote_server.py b/zerver/lib/remote_server.py index 7ed113fe44..cace1c9199 100644 --- a/zerver/lib/remote_server.py +++ b/zerver/lib/remote_server.py @@ -1,6 +1,6 @@ import logging import urllib -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Mapping, Tuple, Union import requests import ujson @@ -24,7 +24,7 @@ class PushNotificationBouncerRetryLaterError(JsonableError): def send_to_push_bouncer(method: str, endpoint: str, post_data: Union[str, Dict[str, Any]], - extra_headers: Optional[Dict[str, Any]]=None) -> Dict[str, Any]: + extra_headers: Mapping[str, Any] = {}) -> Dict[str, Any]: """While it does actually send the notice, this function has a lot of code and comments around error handling for the push notifications bouncer. There are several classes of failures, each with its own @@ -47,8 +47,7 @@ def send_to_push_bouncer(method: str, settings.ZULIP_ORG_KEY) headers = {"User-agent": f"ZulipServer/{ZULIP_VERSION}"} - if extra_headers is not None: - headers.update(extra_headers) + headers.update(extra_headers) try: res = requests.request(method, diff --git a/zerver/lib/request.py b/zerver/lib/request.py index d542445b57..5ab4f73e7e 100644 --- a/zerver/lib/request.py +++ b/zerver/lib/request.py @@ -1,7 +1,19 @@ from collections import defaultdict from functools import wraps from types import FunctionType -from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, Union, cast, overload +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Optional, + Sequence, + TypeVar, + Union, + cast, + overload, +) import ujson from django.http import HttpRequest, HttpResponse @@ -69,7 +81,7 @@ class _REQ(Generic[ResultT]): argument_type: Optional[str] = None, intentionally_undocumented: bool=False, documentation_pending: bool=False, - aliases: Optional[List[str]] = None, + aliases: Sequence[str] = [], path_only: bool=False ) -> None: """whence: the name of the request variable that should be used @@ -134,7 +146,7 @@ def REQ( default: ResultT = ..., intentionally_undocumented: bool = ..., documentation_pending: bool = ..., - aliases: Optional[List[str]] = ..., + aliases: Sequence[str] = ..., path_only: bool = ... ) -> ResultT: ... @@ -148,7 +160,7 @@ def REQ( validator: Validator, intentionally_undocumented: bool = ..., documentation_pending: bool = ..., - aliases: Optional[List[str]] = ..., + aliases: Sequence[str] = ..., path_only: bool = ... ) -> ResultT: ... @@ -162,7 +174,7 @@ def REQ( str_validator: Optional[Validator] = ..., intentionally_undocumented: bool = ..., documentation_pending: bool = ..., - aliases: Optional[List[str]] = ..., + aliases: Sequence[str] = ..., path_only: bool = ... ) -> str: ... @@ -176,7 +188,7 @@ def REQ( str_validator: Optional[Validator] = ..., intentionally_undocumented: bool = ..., documentation_pending: bool = ..., - aliases: Optional[List[str]] = ..., + aliases: Sequence[str] = ..., path_only: bool = ... ) -> Optional[str]: ... @@ -191,7 +203,7 @@ def REQ( argument_type: Literal["body"], intentionally_undocumented: bool = ..., documentation_pending: bool = ..., - aliases: Optional[List[str]] = ..., + aliases: Sequence[str] = ..., path_only: bool = ... ) -> ResultT: ... @@ -207,7 +219,7 @@ def REQ( argument_type: Optional[str] = None, intentionally_undocumented: bool=False, documentation_pending: bool=False, - aliases: Optional[List[str]] = None, + aliases: Sequence[str] = [], path_only: bool = False ) -> ResultT: return cast(ResultT, _REQ( @@ -295,8 +307,7 @@ def has_request_variables(view_func: ViewFuncT) -> ViewFuncT: raise Exception(_("Invalid argument type")) post_var_names = [param.post_var_name] - if param.aliases: - post_var_names += param.aliases + post_var_names += param.aliases default_assigned = False diff --git a/zerver/lib/response.py b/zerver/lib/response.py index a9e5516a76..33e6631fe3 100644 --- a/zerver/lib/response.py +++ b/zerver/lib/response.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, List, Mapping, Optional import ujson from django.http import HttpResponse, HttpResponseNotAllowed @@ -37,15 +37,14 @@ def json_method_not_allowed(methods: List[str]) -> HttpResponseNotAllowed: def json_response(res_type: str="success", msg: str="", - data: Optional[Dict[str, Any]]=None, + data: Mapping[str, Any]={}, status: int=200) -> HttpResponse: content = {"result": res_type, "msg": msg} - if data is not None: - content.update(data) + content.update(data) return HttpResponse(content=ujson.dumps(content) + "\n", content_type='application/json', status=status) -def json_success(data: Optional[Dict[str, Any]]=None) -> HttpResponse: +def json_success(data: Mapping[str, Any]={}) -> HttpResponse: return json_response(data=data) def json_response_from_error(exception: JsonableError) -> HttpResponse: @@ -61,5 +60,5 @@ def json_response_from_error(exception: JsonableError) -> HttpResponse: data=exception.data, status=exception.http_status_code) -def json_error(msg: str, data: Optional[Dict[str, Any]]=None, status: int=400) -> HttpResponse: +def json_error(msg: str, data: Mapping[str, Any]={}, status: int=400) -> HttpResponse: return json_response(res_type="error", msg=msg, data=data, status=status) diff --git a/zerver/lib/send_email.py b/zerver/lib/send_email.py index ee9a523ed9..c1c4ff8b1e 100644 --- a/zerver/lib/send_email.py +++ b/zerver/lib/send_email.py @@ -55,7 +55,7 @@ class FromAddress: def build_email(template_prefix: str, to_user_ids: Optional[List[int]]=None, to_emails: Optional[List[str]]=None, from_name: Optional[str]=None, from_address: Optional[str]=None, reply_to_email: Optional[str]=None, - language: Optional[str]=None, context: Optional[Dict[str, Any]]=None, + language: Optional[str]=None, context: Mapping[str, Any]={}, ) -> EmailMultiAlternatives: # Callers should pass exactly one of to_user_id and to_email. assert (to_user_ids is None) ^ (to_emails is None) @@ -63,14 +63,12 @@ def build_email(template_prefix: str, to_user_ids: Optional[List[int]]=None, to_users = [get_user_profile_by_id(to_user_id) for to_user_id in to_user_ids] to_emails = [formataddr((to_user.full_name, to_user.delivery_email)) for to_user in to_users] - if context is None: - context = {} - - context.update({ + context = { + **context, 'support_email': FromAddress.SUPPORT, 'email_images_base_uri': settings.ROOT_DOMAIN_URI + '/static/images/emails', 'physical_address': settings.PHYSICAL_ADDRESS, - }) + } def render_templates() -> Tuple[str, str, str]: email_subject = loader.render_to_string(template_prefix + '.subject.txt', diff --git a/zerver/openapi/python_examples.py b/zerver/openapi/python_examples.py index 885ff60594..5739b7f278 100644 --- a/zerver/openapi/python_examples.py +++ b/zerver/openapi/python_examples.py @@ -1058,18 +1058,18 @@ def test_invalid_stream_error(client: Client) -> None: # SETUP METHODS FOLLOW -def test_against_fixture(result: Dict[str, Any], fixture: Dict[str, Any], check_if_equal: Optional[Iterable[str]] = [], check_if_exists: Optional[Iterable[str]] = []) -> None: +def test_against_fixture(result: Dict[str, Any], fixture: Dict[str, Any], check_if_equal: Optional[Iterable[str]] = None, check_if_exists: Optional[Iterable[str]] = None) -> None: assertLength(result, fixture) - if not check_if_equal and not check_if_exists: + if check_if_equal is None and check_if_exists is None: for key, value in fixture.items(): assertEqual(key, result, fixture) - if check_if_equal: + if check_if_equal is not None: for key in check_if_equal: assertEqual(key, result, fixture) - if check_if_exists: + if check_if_exists is not None: for key in check_if_exists: assertIn(key, result) diff --git a/zerver/templatetags/app_filters.py b/zerver/templatetags/app_filters.py index 00b2503a25..d60b99e3dc 100644 --- a/zerver/templatetags/app_filters.py +++ b/zerver/templatetags/app_filters.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, List, Mapping, Optional import markdown import markdown.extensions.admonition @@ -75,7 +75,7 @@ docs_without_macros = [ @items_tuple_to_dict @register.filter(name='render_markdown_path', is_safe=True) def render_markdown_path(markdown_file_path: str, - context: Optional[Dict[Any, Any]]=None, + context: Mapping[str, Any]={}, pure_markdown: bool=False) -> str: """Given a path to a markdown file, return the rendered html. @@ -83,9 +83,6 @@ def render_markdown_path(markdown_file_path: str, trusted; it is intended to be used for documentation, not user data.""" - if context is None: - context = {} - # We set this global hackishly from zerver.lib.bugdown.help_settings_links import set_relative_settings_links set_relative_settings_links(bool(context.get('html_settings_links'))) diff --git a/zerver/tests/test_auth_backends.py b/zerver/tests/test_auth_backends.py index 6621cba44b..30f71d3e55 100644 --- a/zerver/tests/test_auth_backends.py +++ b/zerver/tests/test_auth_backends.py @@ -140,12 +140,10 @@ class AuthBackendTest(ZulipTestCase): return username - def verify_backend(self, backend: Any, good_kwargs: Optional[Dict[str, Any]]=None, bad_kwargs: Optional[Dict[str, Any]]=None) -> None: + def verify_backend(self, backend: Any, *, good_kwargs: Dict[str, Any], bad_kwargs: Optional[Dict[str, Any]]=None) -> None: clear_supported_auth_backends_cache() user_profile = self.example_user('hamlet') - assert good_kwargs is not None - # If bad_kwargs was specified, verify auth fails in that case if bad_kwargs is not None: self.assertIsNone(backend.authenticate(**bad_kwargs)) diff --git a/zerver/tests/test_email_notifications.py b/zerver/tests/test_email_notifications.py index bbb134c7cd..a23f04d7a8 100644 --- a/zerver/tests/test_email_notifications.py +++ b/zerver/tests/test_email_notifications.py @@ -1,7 +1,7 @@ import random import re from email.utils import formataddr -from typing import List, Optional +from typing import List, Sequence from unittest.mock import patch import ldap @@ -221,7 +221,7 @@ class TestMissedMessages(ZulipTestCase): def _test_cases(self, msg_id: int, verify_body_include: List[str], email_subject: str, send_as_user: bool, verify_html_body: bool=False, show_message_content: bool=True, - verify_body_does_not_include: Optional[List[str]]=None, + verify_body_does_not_include: Sequence[str]=[], trigger: str='') -> None: othello = self.example_user('othello') hamlet = self.example_user('hamlet') @@ -248,9 +248,8 @@ class TestMissedMessages(ZulipTestCase): else: for text in verify_body_include: self.assertIn(text, self.normalize_string(msg.body)) - if verify_body_does_not_include is not None: - for text in verify_body_does_not_include: - self.assertNotIn(text, self.normalize_string(msg.body)) + for text in verify_body_does_not_include: + self.assertNotIn(text, self.normalize_string(msg.body)) def _realm_name_in_missed_message_email_subject(self, realm_name_in_notifications: bool) -> None: msg_id = self.send_personal_message( diff --git a/zerver/tests/test_subs.py b/zerver/tests/test_subs.py index e835bbf123..030608d75d 100644 --- a/zerver/tests/test_subs.py +++ b/zerver/tests/test_subs.py @@ -1,6 +1,6 @@ import random from datetime import timedelta -from typing import Any, Dict, List, Mapping, Optional, Set, Union +from typing import Any, Dict, List, Mapping, Optional, Sequence, Set, Union from unittest import mock import ujson @@ -976,7 +976,7 @@ class StreamAdminTest(ZulipTestCase): def attempt_unsubscribe_of_principal(self, query_count: int, target_users: List[UserProfile], is_admin: bool=False, is_subbed: bool=True, invite_only: bool=False, target_users_subbed: bool=True, using_legacy_emails: bool=False, - other_sub_users: Optional[List[UserProfile]]=None) -> HttpResponse: + other_sub_users: Sequence[UserProfile]=[]) -> HttpResponse: # Set up the main user, who is in most cases an admin. if is_admin: @@ -1004,9 +1004,8 @@ class StreamAdminTest(ZulipTestCase): if target_users_subbed: for user in target_users: self.subscribe(user, stream_name) - if other_sub_users: - for user in other_sub_users: - self.subscribe(user, stream_name) + for user in other_sub_users: + self.subscribe(user, stream_name) with queries_captured() as queries: result = self.client_delete( diff --git a/zerver/views/report.py b/zerver/views/report.py index 3e665c58a9..cbe3e3972c 100644 --- a/zerver/views/report.py +++ b/zerver/views/report.py @@ -1,7 +1,7 @@ # System documented in https://zulip.readthedocs.io/en/latest/subsystems/logging.html import logging import subprocess -from typing import Any, Dict, Optional +from typing import Any, Dict, Mapping, Optional from django.conf import settings from django.http import HttpRequest, HttpResponse @@ -89,14 +89,13 @@ def report_unnarrow_times(request: HttpRequest, user_profile: UserProfile, def report_error(request: HttpRequest, user_profile: UserProfile, message: str=REQ(), stacktrace: str=REQ(), ui_message: bool=REQ(validator=check_bool), user_agent: str=REQ(), href: str=REQ(), log: str=REQ(), - more_info: Optional[Dict[str, Any]]=REQ(validator=check_dict([]), default=None), + more_info: Mapping[str, Any]=REQ(validator=check_dict([]), default={}), ) -> HttpResponse: """Accepts an error report and stores in a queue for processing. The actual error reports are later handled by do_report_error""" if not settings.BROWSER_ERROR_REPORTING: return json_success() - if more_info is None: - more_info = {} + more_info = dict(more_info) js_source_map = get_js_source_map() if js_source_map: diff --git a/zerver/webhooks/trello/view/board_actions.py b/zerver/webhooks/trello/view/board_actions.py index 36a457c790..b0b64ce39b 100644 --- a/zerver/webhooks/trello/view/board_actions.py +++ b/zerver/webhooks/trello/view/board_actions.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Mapping, Optional, Tuple +from typing import Any, Mapping, Optional, Tuple from zerver.lib.exceptions import UnexpectedWebhookEventType @@ -71,9 +71,10 @@ def get_change_name_body(payload: Mapping[str, Any], action_type: str) -> str: def fill_appropriate_message_content(payload: Mapping[str, Any], action_type: str, - data: Optional[Dict[str, Any]]=None) -> str: - data = {} if data is None else data - data['board_url_template'] = data.get('board_url_template', get_filled_board_url_template(payload)) + data: Mapping[str, Any] = {}) -> str: + data = dict(data) + if 'board_url_template' not in data: + data['board_url_template'] = get_filled_board_url_template(payload) message_body = get_message_body(action_type) return message_body.format(**data) diff --git a/zerver/webhooks/trello/view/card_actions.py b/zerver/webhooks/trello/view/card_actions.py index c15442bee6..b37c5f67ce 100644 --- a/zerver/webhooks/trello/view/card_actions.py +++ b/zerver/webhooks/trello/view/card_actions.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Mapping, Optional, Tuple +from typing import Any, Mapping, Optional, Tuple from zerver.lib.exceptions import UnexpectedWebhookEventType @@ -205,9 +205,10 @@ def get_body_by_action_type_without_data(payload: Mapping[str, Any], action_type def fill_appropriate_message_content(payload: Mapping[str, Any], action_type: str, - data: Optional[Dict[str, Any]]=None) -> str: - data = {} if data is None else data - data['card_url_template'] = data.get('card_url_template', get_filled_card_url_template(payload)) + data: Mapping[str, Any] = {}) -> str: + data = dict(data) + if 'card_url_template' not in data: + data['card_url_template'] = get_filled_card_url_template(payload) message_body = get_message_body(action_type) return message_body.format(**data)