python: Replace None defaults with empty collections where appropriate.

Use read-only types (List ↦ Sequence, Dict ↦ Mapping, Set ↦
AbstractSet) to guard against accidental mutation of the default
value.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2020-06-12 18:34:01 -07:00 committed by Tim Abbott
parent cfcbf58cd1
commit 91a86c24f5
24 changed files with 101 additions and 119 deletions

View File

@ -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))

View File

@ -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(

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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])

View File

@ -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.'],
}

View File

@ -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:

View File

@ -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),

View File

@ -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:

View File

@ -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]:

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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',

View File

@ -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)

View File

@ -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')))

View File

@ -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))

View File

@ -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(

View File

@ -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(

View File

@ -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:

View File

@ -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)

View File

@ -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)