types: Define UserDisplayRecipient type using TypedDict.

Since the display_recipients dictionaries corresponding to users are
always dictionaries with keys email, full_name, short_name, id,
is_mirror_dummy - instead of using the overly general Dict[str, Any]
type, we can define a UserDisplayRecipient type,
using an appropriate TypedDict.

The type definitions are moved from display_recipient.py to types.py, so
that they can be imported in models.py.

Appropriate type adjustments are made in various places in the code
where we operate on display_recipients.
This commit is contained in:
Mateusz Mandera
2019-08-18 00:24:46 +02:00
committed by Tim Abbott
parent c779bb1959
commit 3ba0a37a92
7 changed files with 33 additions and 18 deletions

View File

@@ -1,10 +1,10 @@
from typing import Any, Dict, List, Optional, Set, Tuple, Union
from typing import Dict, List, Optional, Set, Tuple
from zerver.lib.types import DisplayRecipientCacheT, UserDisplayRecipient
from zerver.lib.cache import cache_with_key, display_recipient_cache_key, generic_bulk_cached_fetch, \
display_recipient_bulk_get_users_by_id_cache_key
from zerver.models import Recipient, Stream, UserProfile, bulk_get_huddle_user_ids
DisplayRecipientCacheT = Union[str, List[Dict[str, Any]]]
@cache_with_key(lambda *args: display_recipient_cache_key(args[0]),
timeout=3600*24*7)
def get_display_recipient_remote_cache(recipient_id: int, recipient_type: int,
@@ -27,7 +27,7 @@ def get_display_recipient_remote_cache(recipient_id: int, recipient_type: int,
.order_by('id'))
return [user_profile_to_display_recipient_dict(user_profile) for user_profile in user_profile_list]
def user_profile_to_display_recipient_dict(user_profile: 'UserProfile') -> Dict[str, Any]:
def user_profile_to_display_recipient_dict(user_profile: 'UserProfile') -> UserDisplayRecipient:
return {'email': user_profile.email,
'full_name': user_profile.full_name,
'short_name': user_profile.short_name,
@@ -133,7 +133,7 @@ def bulk_fetch_display_recipients(recipient_tuples: Set[Tuple[int, int, int]]
return result
def personal_and_huddle_cache_transformer(db_object: Tuple[int, List[UserProfile]]
) -> List[Dict[str, Any]]:
) -> List[UserDisplayRecipient]:
"""
Takes an element of the list returned by the query_function, maps it to the final
display_recipient list.
@@ -149,7 +149,7 @@ def bulk_fetch_display_recipients(recipient_tuples: Set[Tuple[int, int, int]]
return db_object[0]
# ItemT = Tuple[int, List[UserProfile]] (recipient_id, list of corresponding users)
# CacheItemT = List[Dict[str, Any]] (display_recipient list)
# CacheItemT = List[UserDisplayRecipient] (display_recipient list)
# ObjKT = int (recipient_id)
personal_and_huddle_display_recipients = generic_bulk_cached_fetch(
cache_key_function=display_recipient_cache_key,

View File

@@ -1,4 +1,5 @@
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
from zerver.lib.types import UserDisplayRecipient
from confirmation.models import one_click_unsubscribe_link
from django.conf import settings
@@ -250,7 +251,7 @@ def build_message_list(user_profile: UserProfile, messages: List[Message]) -> Li
return messages_to_render
def get_narrow_url(user_profile: UserProfile, message: Message,
display_recipient: Optional[Union[str, List[Dict[str, Any]]]]=None,
display_recipient: Optional[Union[str, List[UserDisplayRecipient]]]=None,
stream: Optional[Stream]=None) -> str:
"""The display_recipient and stream arguments are optional. If not
provided, we'll compute them from the message; they exist as a

View File

@@ -18,7 +18,8 @@ from zerver.lib.cache import (
to_dict_cache_key,
to_dict_cache_key_id,
)
from zerver.lib.display_recipient import DisplayRecipientCacheT, bulk_fetch_display_recipients
from zerver.lib.display_recipient import UserDisplayRecipient, DisplayRecipientCacheT, \
bulk_fetch_display_recipients
from zerver.lib.request import JsonableError
from zerver.lib.stream_subscription import (
get_stream_subscriptions_for_user,
@@ -448,7 +449,7 @@ class MessageDict:
'full_name': sender_full_name,
'short_name': sender_short_name,
'id': sender_id,
'is_mirror_dummy': sender_is_mirror_dummy}
'is_mirror_dummy': sender_is_mirror_dummy} # type: UserDisplayRecipient
if recip['email'] < display_recipient[0]['email']:
display_recipient = [recip, display_recipient[0]]
elif recip['email'] > display_recipient[0]['email']:
@@ -666,7 +667,7 @@ def do_render_markdown(message: Message,
def huddle_users(recipient_id: int) -> str:
display_recipient = get_display_recipient_by_id(recipient_id,
Recipient.HUDDLE,
None) # type: Union[str, List[Dict[str, Any]]]
None) # type: Union[str, List[UserDisplayRecipient]]
# str is for streams.
assert not isinstance(display_recipient, str)

View File

@@ -27,3 +27,7 @@ ExtendedFieldElement = Tuple[int, str, ExtendedValidator, Callable[[Any], Any],
UserFieldElement = Tuple[int, str, RealmUserValidator, Callable[[Any], Any], str]
ProfileFieldData = Dict[str, Union[Dict[str, str], str]]
UserDisplayRecipient = TypedDict('UserDisplayRecipient', {'email': str, 'full_name': str, 'short_name': str,
'id': int, 'is_mirror_dummy': bool})
DisplayRecipientCacheT = Union[str, List[UserDisplayRecipient]]

View File

@@ -36,7 +36,8 @@ from zerver.lib.validator import check_int, \
from zerver.lib.name_restrictions import is_disposable_domain
from zerver.lib.types import Validator, ExtendedValidator, \
ProfileDataElement, ProfileData, RealmUserValidator, \
ExtendedFieldElement, UserFieldElement, FieldElement
ExtendedFieldElement, UserFieldElement, FieldElement, \
UserDisplayRecipient
from bitfield import BitField
from bitfield.types import BitHandler
@@ -80,9 +81,9 @@ def query_for_ids(query: QuerySet, user_ids: List[int], field: str) -> QuerySet:
# could be replaced with smarter bulk-fetching logic that deduplicates
# queries for the same recipient; this is just a convenient way to
# write that code.
per_request_display_recipient_cache = {} # type: Dict[int, Union[str, List[Dict[str, Any]]]]
per_request_display_recipient_cache = {} # type: Dict[int, Union[str, List[UserDisplayRecipient]]]
def get_display_recipient_by_id(recipient_id: int, recipient_type: int,
recipient_type_id: Optional[int]) -> Union[str, List[Dict[str, Any]]]:
recipient_type_id: Optional[int]) -> Union[str, List[UserDisplayRecipient]]:
"""
returns: an object describing the recipient (using a cache).
If the type is a stream, the type_id must be an int; a string is returned.
@@ -96,7 +97,7 @@ def get_display_recipient_by_id(recipient_id: int, recipient_type: int,
per_request_display_recipient_cache[recipient_id] = result
return per_request_display_recipient_cache[recipient_id]
def get_display_recipient(recipient: 'Recipient') -> Union[str, List[Dict[str, Any]]]:
def get_display_recipient(recipient: 'Recipient') -> Union[str, List[UserDisplayRecipient]]:
return get_display_recipient_by_id(
recipient.id,
recipient.type,

View File

@@ -74,6 +74,7 @@ from zerver.lib.topic import (
DB_TOPIC_NAME,
)
from zerver.lib.types import UserDisplayRecipient
from zerver.lib.soft_deactivation import (
add_missing_messages,
do_soft_activate_users,
@@ -4207,8 +4208,11 @@ class MessageHydrationTest(ZulipTestCase):
dict(
email='aaron@example.com',
full_name='Aaron Smith',
short_name='Aaron',
id=999,
is_mirror_dummy=False
),
]
] # type: List[UserDisplayRecipient]
obj = dict(
recipient_type=Recipient.PERSONAL,
@@ -4228,6 +4232,9 @@ class MessageHydrationTest(ZulipTestCase):
dict(
email='aaron@example.com',
full_name='Aaron Smith',
short_name='Aaron',
id=999,
is_mirror_dummy=False
),
dict(
email=cordelia.email,
@@ -4328,7 +4335,7 @@ class MessageHydrationTest(ZulipTestCase):
self.assertEqual(cordelia_display_recipient['email'], cordelia_new_email)
class TestMessageForIdsDisplayRecipientFetching(ZulipTestCase):
def _verify_display_recipient(self, display_recipient: Union[str, List[Dict[str, Any]]],
def _verify_display_recipient(self, display_recipient: Union[str, List[UserDisplayRecipient]],
expected_recipient_objects: Union[Stream, List[UserProfile]]) -> None:
if isinstance(expected_recipient_objects, Stream):
self.assertEqual(display_recipient, expected_recipient_objects.name)
@@ -4339,7 +4346,7 @@ class TestMessageForIdsDisplayRecipientFetching(ZulipTestCase):
'full_name': user_profile.full_name,
'short_name': user_profile.short_name,
'id': user_profile.id,
'is_mirror_dummy': user_profile.is_mirror_dummy}
'is_mirror_dummy': user_profile.is_mirror_dummy} # type: UserDisplayRecipient
self.assertTrue(recipient_dict in display_recipient)
def test_display_recipient_personal(self) -> None:

View File

@@ -37,6 +37,7 @@ from zerver.lib.topic import (
from zerver.lib.topic_mutes import (
set_topic_mutes,
)
from zerver.lib.types import UserDisplayRecipient
from zerver.views.messages import (
exclude_muting_conditions,
get_messages_backend, ok_to_include_history,
@@ -1068,11 +1069,11 @@ class GetOldMessagesTest(ZulipTestCase):
"""
me = self.example_email('hamlet')
def dr_emails(dr: Union[str, List[Dict[str, Any]]]) -> str:
def dr_emails(dr: Union[str, List[UserDisplayRecipient]]) -> str:
assert isinstance(dr, list)
return ','.join(sorted(set([r['email'] for r in dr] + [me])))
def dr_ids(dr: Union[str, List[Dict[str, Any]]]) -> List[int]:
def dr_ids(dr: Union[str, List[UserDisplayRecipient]]) -> List[int]:
assert isinstance(dr, list)
return list(sorted(set([r['id'] for r in dr] + [self.example_user('hamlet').id])))