zerver/lib: Change use of typing.Text to str.

This commit is contained in:
Aditya Bansal
2018-05-10 22:43:36 +05:30
committed by Tim Abbott
parent 2f3b2fbf59
commit 1f9244e060
43 changed files with 233 additions and 241 deletions

View File

@@ -1,5 +1,5 @@
from typing import Iterable, List, Optional, Sequence, Text from typing import Iterable, List, Optional, Sequence
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@@ -11,7 +11,7 @@ from zerver.models import (
get_user_including_cross_realm, get_user_including_cross_realm,
) )
def user_profiles_from_unvalidated_emails(emails: Iterable[Text], realm: Realm) -> List[UserProfile]: def user_profiles_from_unvalidated_emails(emails: Iterable[str], realm: Realm) -> List[UserProfile]:
user_profiles = [] # type: List[UserProfile] user_profiles = [] # type: List[UserProfile]
for email in emails: for email in emails:
try: try:
@@ -21,7 +21,7 @@ def user_profiles_from_unvalidated_emails(emails: Iterable[Text], realm: Realm)
user_profiles.append(user_profile) user_profiles.append(user_profile)
return user_profiles return user_profiles
def get_user_profiles(emails: Iterable[Text], realm: Realm) -> List[UserProfile]: def get_user_profiles(emails: Iterable[str], realm: Realm) -> List[UserProfile]:
try: try:
return user_profiles_from_unvalidated_emails(emails, realm) return user_profiles_from_unvalidated_emails(emails, realm)
except ValidationError as e: except ValidationError as e:
@@ -42,8 +42,8 @@ class Addressee:
# This should be treated as an immutable class. # This should be treated as an immutable class.
def __init__(self, msg_type: str, def __init__(self, msg_type: str,
user_profiles: Optional[Sequence[UserProfile]]=None, user_profiles: Optional[Sequence[UserProfile]]=None,
stream_name: Optional[Text]=None, stream_name: Optional[str]=None,
topic: Optional[Text]=None) -> None: topic: Optional[str]=None) -> None:
assert(msg_type in ['stream', 'private']) assert(msg_type in ['stream', 'private'])
self._msg_type = msg_type self._msg_type = msg_type
self._user_profiles = user_profiles self._user_profiles = user_profiles
@@ -63,21 +63,21 @@ class Addressee:
assert(self.is_private()) assert(self.is_private())
return self._user_profiles # type: ignore # assertion protects us return self._user_profiles # type: ignore # assertion protects us
def stream_name(self) -> Text: def stream_name(self) -> str:
assert(self.is_stream()) assert(self.is_stream())
assert(self._stream_name is not None) assert(self._stream_name is not None)
return self._stream_name return self._stream_name
def topic(self) -> Text: def topic(self) -> str:
assert(self.is_stream()) assert(self.is_stream())
assert(self._topic is not None) assert(self._topic is not None)
return self._topic return self._topic
@staticmethod @staticmethod
def legacy_build(sender: UserProfile, def legacy_build(sender: UserProfile,
message_type_name: Text, message_type_name: str,
message_to: Sequence[Text], message_to: Sequence[str],
topic_name: Text, topic_name: str,
realm: Optional[Realm]=None) -> 'Addressee': realm: Optional[Realm]=None) -> 'Addressee':
# For legacy reason message_to used to be either a list of # For legacy reason message_to used to be either a list of
@@ -110,7 +110,7 @@ class Addressee:
raise JsonableError(_("Invalid message type")) raise JsonableError(_("Invalid message type"))
@staticmethod @staticmethod
def for_stream(stream_name: Text, topic: Text) -> 'Addressee': def for_stream(stream_name: str, topic: str) -> 'Addressee':
if topic is None: if topic is None:
raise JsonableError(_("Missing topic")) raise JsonableError(_("Missing topic"))
topic = topic.strip() topic = topic.strip()
@@ -123,7 +123,7 @@ class Addressee:
) )
@staticmethod @staticmethod
def for_private(emails: Sequence[Text], realm: Realm) -> 'Addressee': def for_private(emails: Sequence[str], realm: Realm) -> 'Addressee':
user_profiles = get_user_profiles(emails, realm) user_profiles = get_user_profiles(emails, realm)
return Addressee( return Addressee(
msg_type='private', msg_type='private',

View File

@@ -3,20 +3,20 @@ from django.db.models import Q
from zerver.models import UserProfile, Realm from zerver.models import UserProfile, Realm
from zerver.lib.cache import cache_with_key, realm_alert_words_cache_key from zerver.lib.cache import cache_with_key, realm_alert_words_cache_key
import ujson import ujson
from typing import Dict, Iterable, List, Text from typing import Dict, Iterable, List
@cache_with_key(realm_alert_words_cache_key, timeout=3600*24) @cache_with_key(realm_alert_words_cache_key, timeout=3600*24)
def alert_words_in_realm(realm: Realm) -> Dict[int, List[Text]]: def alert_words_in_realm(realm: Realm) -> Dict[int, List[str]]:
users_query = UserProfile.objects.filter(realm=realm, is_active=True) users_query = UserProfile.objects.filter(realm=realm, is_active=True)
alert_word_data = users_query.filter(~Q(alert_words=ujson.dumps([]))).values('id', 'alert_words') alert_word_data = users_query.filter(~Q(alert_words=ujson.dumps([]))).values('id', 'alert_words')
all_user_words = dict((elt['id'], ujson.loads(elt['alert_words'])) for elt in alert_word_data) all_user_words = dict((elt['id'], ujson.loads(elt['alert_words'])) for elt in alert_word_data)
user_ids_with_words = dict((user_id, w) for (user_id, w) in all_user_words.items() if len(w)) user_ids_with_words = dict((user_id, w) for (user_id, w) in all_user_words.items() if len(w))
return user_ids_with_words return user_ids_with_words
def user_alert_words(user_profile: UserProfile) -> List[Text]: def user_alert_words(user_profile: UserProfile) -> List[str]:
return ujson.loads(user_profile.alert_words) return ujson.loads(user_profile.alert_words)
def add_user_alert_words(user_profile: UserProfile, alert_words: Iterable[Text]) -> List[Text]: def add_user_alert_words(user_profile: UserProfile, alert_words: Iterable[str]) -> List[str]:
words = user_alert_words(user_profile) words = user_alert_words(user_profile)
new_words = [w for w in alert_words if w not in words] new_words = [w for w in alert_words if w not in words]
@@ -26,7 +26,7 @@ def add_user_alert_words(user_profile: UserProfile, alert_words: Iterable[Text])
return words return words
def remove_user_alert_words(user_profile: UserProfile, alert_words: Iterable[Text]) -> List[Text]: def remove_user_alert_words(user_profile: UserProfile, alert_words: Iterable[str]) -> List[str]:
words = user_alert_words(user_profile) words = user_alert_words(user_profile)
words = [w for w in words if w not in alert_words] words = [w for w in words if w not in alert_words]
@@ -34,6 +34,6 @@ def remove_user_alert_words(user_profile: UserProfile, alert_words: Iterable[Tex
return words return words
def set_user_alert_words(user_profile: UserProfile, alert_words: List[Text]) -> None: def set_user_alert_words(user_profile: UserProfile, alert_words: List[str]) -> None:
user_profile.alert_words = ujson.dumps(alert_words) user_profile.alert_words = ujson.dumps(alert_words)
user_profile.save(update_fields=['alert_words']) user_profile.save(update_fields=['alert_words'])

View File

@@ -3,14 +3,14 @@ from django.conf import settings
if False: if False:
from zerver.models import UserProfile from zerver.models import UserProfile
from typing import Any, Dict, Optional, Text from typing import Any, Dict, Optional
from zerver.lib.avatar_hash import gravatar_hash, user_avatar_path_from_ids from zerver.lib.avatar_hash import gravatar_hash, user_avatar_path_from_ids
from zerver.lib.upload import upload_backend, MEDIUM_AVATAR_SIZE from zerver.lib.upload import upload_backend, MEDIUM_AVATAR_SIZE
from zerver.models import UserProfile from zerver.models import UserProfile
import urllib import urllib
def avatar_url(user_profile: UserProfile, medium: bool=False, client_gravatar: bool=False) -> Text: def avatar_url(user_profile: UserProfile, medium: bool=False, client_gravatar: bool=False) -> str:
return get_avatar_field( return get_avatar_field(
user_id=user_profile.id, user_id=user_profile.id,
@@ -22,7 +22,7 @@ def avatar_url(user_profile: UserProfile, medium: bool=False, client_gravatar: b
client_gravatar=client_gravatar, client_gravatar=client_gravatar,
) )
def avatar_url_from_dict(userdict: Dict[str, Any], medium: bool=False) -> Text: def avatar_url_from_dict(userdict: Dict[str, Any], medium: bool=False) -> str:
''' '''
DEPRECATED: We should start using DEPRECATED: We should start using
get_avatar_field to populate users, get_avatar_field to populate users,
@@ -41,11 +41,11 @@ def avatar_url_from_dict(userdict: Dict[str, Any], medium: bool=False) -> Text:
def get_avatar_field(user_id: int, def get_avatar_field(user_id: int,
realm_id: int, realm_id: int,
email: Text, email: str,
avatar_source: Text, avatar_source: str,
avatar_version: int, avatar_version: int,
medium: bool, medium: bool,
client_gravatar: bool) -> Optional[Text]: client_gravatar: bool) -> Optional[str]:
''' '''
Most of the parameters to this function map to fields Most of the parameters to this function map to fields
by the same name in UserProfile (avatar_source, realm_id, by the same name in UserProfile (avatar_source, realm_id,
@@ -88,12 +88,12 @@ def get_avatar_field(user_id: int,
url += '&version=%d' % (avatar_version,) url += '&version=%d' % (avatar_version,)
return url return url
def get_gravatar_url(email: Text, avatar_version: int, medium: bool=False) -> Text: def get_gravatar_url(email: str, avatar_version: int, medium: bool=False) -> str:
url = _get_unversioned_gravatar_url(email, medium) url = _get_unversioned_gravatar_url(email, medium)
url += '&version=%d' % (avatar_version,) url += '&version=%d' % (avatar_version,)
return url return url
def _get_unversioned_gravatar_url(email: Text, medium: bool) -> Text: def _get_unversioned_gravatar_url(email: str, medium: bool) -> str:
if settings.ENABLE_GRAVATAR: if settings.ENABLE_GRAVATAR:
gravitar_query_suffix = "&s=%s" % (MEDIUM_AVATAR_SIZE,) if medium else "" gravitar_query_suffix = "&s=%s" % (MEDIUM_AVATAR_SIZE,) if medium else ""
hash_key = gravatar_hash(email) hash_key = gravatar_hash(email)
@@ -101,17 +101,17 @@ def _get_unversioned_gravatar_url(email: Text, medium: bool) -> Text:
return settings.DEFAULT_AVATAR_URI+'?x=x' return settings.DEFAULT_AVATAR_URI+'?x=x'
def _get_unversioned_avatar_url(user_profile_id: int, def _get_unversioned_avatar_url(user_profile_id: int,
avatar_source: Text, avatar_source: str,
realm_id: int, realm_id: int,
email: Optional[Text]=None, email: Optional[str]=None,
medium: bool=False) -> Text: medium: bool=False) -> str:
if avatar_source == 'U': if avatar_source == 'U':
hash_key = user_avatar_path_from_ids(user_profile_id, realm_id) hash_key = user_avatar_path_from_ids(user_profile_id, realm_id)
return upload_backend.get_avatar_url(hash_key, medium=medium) return upload_backend.get_avatar_url(hash_key, medium=medium)
assert email is not None assert email is not None
return _get_unversioned_gravatar_url(email, medium) return _get_unversioned_gravatar_url(email, medium)
def absolute_avatar_url(user_profile: UserProfile) -> Text: def absolute_avatar_url(user_profile: UserProfile) -> str:
"""Absolute URLs are used to simplify logic for applications that """Absolute URLs are used to simplify logic for applications that
won't be served by browsers, such as rendering GCM notifications.""" won't be served by browsers, such as rendering GCM notifications."""
return urllib.parse.urljoin(user_profile.realm.uri, avatar_url(user_profile)) return urllib.parse.urljoin(user_profile.realm.uri, avatar_url(user_profile))

View File

@@ -1,6 +1,5 @@
from django.conf import settings from django.conf import settings
from typing import Text
from zerver.lib.utils import make_safe_digest from zerver.lib.utils import make_safe_digest
@@ -8,7 +7,7 @@ from zerver.models import UserProfile
import hashlib import hashlib
def gravatar_hash(email: Text) -> Text: def gravatar_hash(email: str) -> str:
"""Compute the Gravatar hash for an email address.""" """Compute the Gravatar hash for an email address."""
# Non-ASCII characters aren't permitted by the currently active e-mail # Non-ASCII characters aren't permitted by the currently active e-mail
# RFCs. However, the IETF has published https://tools.ietf.org/html/rfc4952, # RFCs. However, the IETF has published https://tools.ietf.org/html/rfc4952,
@@ -17,7 +16,7 @@ def gravatar_hash(email: Text) -> Text:
# not error out on it. # not error out on it.
return make_safe_digest(email.lower(), hashlib.md5) return make_safe_digest(email.lower(), hashlib.md5)
def user_avatar_hash(uid: Text) -> Text: def user_avatar_hash(uid: str) -> str:
# WARNING: If this method is changed, you may need to do a migration # WARNING: If this method is changed, you may need to do a migration
# similar to zerver/migrations/0060_move_avatars_to_be_uid_based.py . # similar to zerver/migrations/0060_move_avatars_to_be_uid_based.py .
@@ -28,12 +27,12 @@ def user_avatar_hash(uid: Text) -> Text:
user_key = uid + settings.AVATAR_SALT user_key = uid + settings.AVATAR_SALT
return make_safe_digest(user_key, hashlib.sha1) return make_safe_digest(user_key, hashlib.sha1)
def user_avatar_path(user_profile: UserProfile) -> Text: def user_avatar_path(user_profile: UserProfile) -> str:
# WARNING: If this method is changed, you may need to do a migration # WARNING: If this method is changed, you may need to do a migration
# similar to zerver/migrations/0060_move_avatars_to_be_uid_based.py . # similar to zerver/migrations/0060_move_avatars_to_be_uid_based.py .
return user_avatar_path_from_ids(user_profile.id, user_profile.realm_id) return user_avatar_path_from_ids(user_profile.id, user_profile.realm_id)
def user_avatar_path_from_ids(user_profile_id: int, realm_id: int) -> Text: def user_avatar_path_from_ids(user_profile_id: int, realm_id: int) -> str:
user_id_hash = user_avatar_hash(str(user_profile_id)) user_id_hash = user_avatar_hash(str(user_profile_id))
return '%s/%s' % (str(realm_id), user_id_hash) return '%s/%s' % (str(realm_id), user_id_hash)

View File

@@ -4,7 +4,7 @@ from django.db.models.query import F
from django.db.models.functions import Length from django.db.models.functions import Length
from zerver.models import BotConfigData, UserProfile from zerver.models import BotConfigData, UserProfile
from typing import Text, List, Dict, Optional from typing import List, Dict, Optional
from collections import defaultdict from collections import defaultdict
@@ -16,22 +16,22 @@ import importlib
class ConfigError(Exception): class ConfigError(Exception):
pass pass
def get_bot_config(bot_profile: UserProfile) -> Dict[Text, Text]: def get_bot_config(bot_profile: UserProfile) -> Dict[str, str]:
entries = BotConfigData.objects.filter(bot_profile=bot_profile) entries = BotConfigData.objects.filter(bot_profile=bot_profile)
if not entries: if not entries:
raise ConfigError("No config data available.") raise ConfigError("No config data available.")
return {entry.key: entry.value for entry in entries} return {entry.key: entry.value for entry in entries}
def get_bot_configs(bot_profile_ids: List[int]) -> Dict[int, Dict[Text, Text]]: def get_bot_configs(bot_profile_ids: List[int]) -> Dict[int, Dict[str, str]]:
if not bot_profile_ids: if not bot_profile_ids:
return {} return {}
entries = BotConfigData.objects.filter(bot_profile_id__in=bot_profile_ids) entries = BotConfigData.objects.filter(bot_profile_id__in=bot_profile_ids)
entries_by_uid = defaultdict(dict) # type: Dict[int, Dict[Text, Text]] entries_by_uid = defaultdict(dict) # type: Dict[int, Dict[str, str]]
for entry in entries: for entry in entries:
entries_by_uid[entry.bot_profile_id].update({entry.key: entry.value}) entries_by_uid[entry.bot_profile_id].update({entry.key: entry.value})
return entries_by_uid return entries_by_uid
def get_bot_config_size(bot_profile: UserProfile, key: Optional[Text]=None) -> int: def get_bot_config_size(bot_profile: UserProfile, key: Optional[str]=None) -> int:
if key is None: if key is None:
return BotConfigData.objects.filter(bot_profile=bot_profile) \ return BotConfigData.objects.filter(bot_profile=bot_profile) \
.annotate(key_size=Length('key'), value_size=Length('value')) \ .annotate(key_size=Length('key'), value_size=Length('value')) \
@@ -42,7 +42,7 @@ def get_bot_config_size(bot_profile: UserProfile, key: Optional[Text]=None) -> i
except BotConfigData.DoesNotExist: except BotConfigData.DoesNotExist:
return 0 return 0
def set_bot_config(bot_profile: UserProfile, key: Text, value: Text) -> None: def set_bot_config(bot_profile: UserProfile, key: str, value: str) -> None:
config_size_limit = settings.BOT_CONFIG_SIZE_LIMIT config_size_limit = settings.BOT_CONFIG_SIZE_LIMIT
old_entry_size = get_bot_config_size(bot_profile, key) old_entry_size = get_bot_config_size(bot_profile, key)
new_entry_size = len(key) + len(value) new_entry_size = len(key) + len(value)

View File

@@ -18,7 +18,7 @@ import configparser
if False: if False:
from mypy_extensions import NoReturn from mypy_extensions import NoReturn
from typing import Any, Optional, List, Dict, Text from typing import Any, Optional, List, Dict
from types import ModuleType from types import ModuleType
our_dir = os.path.dirname(os.path.abspath(__file__)) our_dir = os.path.dirname(os.path.abspath(__file__))
@@ -45,16 +45,16 @@ class StateHandler:
self.marshal = lambda obj: json.dumps(obj) self.marshal = lambda obj: json.dumps(obj)
self.demarshal = lambda obj: json.loads(obj) self.demarshal = lambda obj: json.loads(obj)
def get(self, key: Text) -> Text: def get(self, key: str) -> str:
return self.demarshal(get_bot_storage(self.user_profile, key)) return self.demarshal(get_bot_storage(self.user_profile, key))
def put(self, key: Text, value: Text) -> None: def put(self, key: str, value: str) -> None:
set_bot_storage(self.user_profile, [(key, self.marshal(value))]) set_bot_storage(self.user_profile, [(key, self.marshal(value))])
def remove(self, key: Text) -> None: def remove(self, key: str) -> None:
remove_bot_storage(self.user_profile, [key]) remove_bot_storage(self.user_profile, [key])
def contains(self, key: Text) -> bool: def contains(self, key: str) -> bool:
return is_key_in_bot_storage(self.user_profile, key) return is_key_in_bot_storage(self.user_profile, key)
class EmbeddedBotQuitException(Exception): class EmbeddedBotQuitException(Exception):
@@ -109,7 +109,7 @@ class EmbeddedBotHandler:
)) ))
# The bot_name argument exists only to comply with ExternalBotHandler.get_config_info(). # The bot_name argument exists only to comply with ExternalBotHandler.get_config_info().
def get_config_info(self, bot_name: str, optional: bool=False) -> Dict[Text, Text]: def get_config_info(self, bot_name: str, optional: bool=False) -> Dict[str, str]:
try: try:
return get_bot_config(self.user_profile) return get_bot_config(self.user_profile)
except ConfigError: except ConfigError:

View File

@@ -4,18 +4,18 @@ from django.db.models.query import F
from django.db.models.functions import Length from django.db.models.functions import Length
from zerver.models import BotStorageData, UserProfile, Length from zerver.models import BotStorageData, UserProfile, Length
from typing import Text, Optional, List, Tuple from typing import Optional, List, Tuple
class StateError(Exception): class StateError(Exception):
pass pass
def get_bot_storage(bot_profile: UserProfile, key: Text) -> Text: def get_bot_storage(bot_profile: UserProfile, key: str) -> str:
try: try:
return BotStorageData.objects.get(bot_profile=bot_profile, key=key).value return BotStorageData.objects.get(bot_profile=bot_profile, key=key).value
except BotStorageData.DoesNotExist: except BotStorageData.DoesNotExist:
raise StateError("Key does not exist.") raise StateError("Key does not exist.")
def get_bot_storage_size(bot_profile: UserProfile, key: Optional[Text]=None) -> int: def get_bot_storage_size(bot_profile: UserProfile, key: Optional[str]=None) -> int:
if key is None: if key is None:
return BotStorageData.objects.filter(bot_profile=bot_profile) \ return BotStorageData.objects.filter(bot_profile=bot_profile) \
.annotate(key_size=Length('key'), value_size=Length('value')) \ .annotate(key_size=Length('key'), value_size=Length('value')) \
@@ -44,14 +44,14 @@ def set_bot_storage(bot_profile: UserProfile, entries: List[Tuple[str, str]]) ->
BotStorageData.objects.update_or_create(bot_profile=bot_profile, key=key, BotStorageData.objects.update_or_create(bot_profile=bot_profile, key=key,
defaults={'value': value}) defaults={'value': value})
def remove_bot_storage(bot_profile: UserProfile, keys: List[Text]) -> None: def remove_bot_storage(bot_profile: UserProfile, keys: List[str]) -> None:
queryset = BotStorageData.objects.filter(bot_profile=bot_profile, key__in=keys) queryset = BotStorageData.objects.filter(bot_profile=bot_profile, key__in=keys)
if len(queryset) < len(keys): if len(queryset) < len(keys):
raise StateError("Key does not exist.") raise StateError("Key does not exist.")
queryset.delete() queryset.delete()
def is_key_in_bot_storage(bot_profile: UserProfile, key: Text) -> bool: def is_key_in_bot_storage(bot_profile: UserProfile, key: str) -> bool:
return BotStorageData.objects.filter(bot_profile=bot_profile, key=key).exists() return BotStorageData.objects.filter(bot_profile=bot_profile, key=key).exists()
def get_keys_in_bot_storage(bot_profile: UserProfile) -> List[Text]: def get_keys_in_bot_storage(bot_profile: UserProfile) -> List[str]:
return list(BotStorageData.objects.filter(bot_profile=bot_profile).values_list('key', flat=True)) return list(BotStorageData.objects.filter(bot_profile=bot_profile).values_list('key', flat=True))

View File

@@ -82,7 +82,7 @@ import markdown
from django.utils.html import escape from django.utils.html import escape
from markdown.extensions.codehilite import CodeHilite, CodeHiliteExtension from markdown.extensions.codehilite import CodeHilite, CodeHiliteExtension
from zerver.lib.tex import render_tex from zerver.lib.tex import render_tex
from typing import Any, Dict, Iterable, List, MutableSequence, Optional, Tuple, Union, Text from typing import Any, Dict, Iterable, List, MutableSequence, Optional, Tuple, Union
# Global vars # Global vars
FENCE_RE = re.compile(""" FENCE_RE = re.compile("""
@@ -132,13 +132,13 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
self.checked_for_codehilite = False self.checked_for_codehilite = False
self.codehilite_conf = {} # type: Dict[str, List[Any]] self.codehilite_conf = {} # type: Dict[str, List[Any]]
def run(self, lines: Iterable[Text]) -> List[Text]: def run(self, lines: Iterable[str]) -> List[str]:
""" Match and store Fenced Code Blocks in the HtmlStash. """ """ Match and store Fenced Code Blocks in the HtmlStash. """
output = [] # type: List[Text] output = [] # type: List[str]
class BaseHandler: class BaseHandler:
def handle_line(self, line: Text) -> None: def handle_line(self, line: str) -> None:
raise NotImplementedError() raise NotImplementedError()
def done(self) -> None: def done(self) -> None:
@@ -153,7 +153,7 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
def pop() -> None: def pop() -> None:
handlers.pop() handlers.pop()
def check_for_new_fence(output: MutableSequence[Text], line: Text) -> None: def check_for_new_fence(output: MutableSequence[str], line: str) -> None:
m = FENCE_RE.match(line) m = FENCE_RE.match(line)
if m: if m:
fence = m.group('fence') fence = m.group('fence')
@@ -164,16 +164,16 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
output.append(line) output.append(line)
class OuterHandler(BaseHandler): class OuterHandler(BaseHandler):
def __init__(self, output: MutableSequence[Text]) -> None: def __init__(self, output: MutableSequence[str]) -> None:
self.output = output self.output = output
def handle_line(self, line: Text) -> None: def handle_line(self, line: str) -> None:
check_for_new_fence(self.output, line) check_for_new_fence(self.output, line)
def done(self) -> None: def done(self) -> None:
pop() pop()
def generic_handler(output: MutableSequence[Text], fence: Text, lang: Text) -> BaseHandler: def generic_handler(output: MutableSequence[str], fence: str, lang: str) -> BaseHandler:
if lang in ('quote', 'quoted'): if lang in ('quote', 'quoted'):
return QuoteHandler(output, fence) return QuoteHandler(output, fence)
elif lang in ('math', 'tex', 'latex'): elif lang in ('math', 'tex', 'latex'):
@@ -182,13 +182,13 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
return CodeHandler(output, fence, lang) return CodeHandler(output, fence, lang)
class CodeHandler(BaseHandler): class CodeHandler(BaseHandler):
def __init__(self, output: MutableSequence[Text], fence: Text, lang: Text) -> None: def __init__(self, output: MutableSequence[str], fence: str, lang: str) -> None:
self.output = output self.output = output
self.fence = fence self.fence = fence
self.lang = lang self.lang = lang
self.lines = [] # type: List[Text] self.lines = [] # type: List[str]
def handle_line(self, line: Text) -> None: def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence: if line.rstrip() == self.fence:
self.done() self.done()
else: else:
@@ -205,12 +205,12 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
pop() pop()
class QuoteHandler(BaseHandler): class QuoteHandler(BaseHandler):
def __init__(self, output: MutableSequence[Text], fence: Text) -> None: def __init__(self, output: MutableSequence[str], fence: str) -> None:
self.output = output self.output = output
self.fence = fence self.fence = fence
self.lines = [] # type: List[Text] self.lines = [] # type: List[str]
def handle_line(self, line: Text) -> None: def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence: if line.rstrip() == self.fence:
self.done() self.done()
else: else:
@@ -226,12 +226,12 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
pop() pop()
class TexHandler(BaseHandler): class TexHandler(BaseHandler):
def __init__(self, output: MutableSequence[Text], fence: Text) -> None: def __init__(self, output: MutableSequence[str], fence: str) -> None:
self.output = output self.output = output
self.fence = fence self.fence = fence
self.lines = [] # type: List[Text] self.lines = [] # type: List[str]
def handle_line(self, line: Text) -> None: def handle_line(self, line: str) -> None:
if line.rstrip() == self.fence: if line.rstrip() == self.fence:
self.done() self.done()
else: else:
@@ -263,7 +263,7 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
output.append('') output.append('')
return output return output
def format_code(self, lang: Text, text: Text) -> Text: def format_code(self, lang: str, text: str) -> str:
if lang: if lang:
langclass = LANG_TAG % (lang,) langclass = LANG_TAG % (lang,)
else: else:
@@ -296,7 +296,7 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
return code return code
def format_quote(self, text: Text) -> Text: def format_quote(self, text: str) -> str:
paragraphs = text.split("\n\n") paragraphs = text.split("\n\n")
quoted_paragraphs = [] quoted_paragraphs = []
for paragraph in paragraphs: for paragraph in paragraphs:
@@ -304,7 +304,7 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
quoted_paragraphs.append("\n".join("> " + line for line in lines if line != '')) quoted_paragraphs.append("\n".join("> " + line for line in lines if line != ''))
return "\n\n".join(quoted_paragraphs) return "\n\n".join(quoted_paragraphs)
def format_tex(self, text: Text) -> Text: def format_tex(self, text: str) -> str:
paragraphs = text.split("\n\n") paragraphs = text.split("\n\n")
tex_paragraphs = [] tex_paragraphs = []
for paragraph in paragraphs: for paragraph in paragraphs:
@@ -316,10 +316,10 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
escape(paragraph) + '</span>') escape(paragraph) + '</span>')
return "\n\n".join(tex_paragraphs) return "\n\n".join(tex_paragraphs)
def placeholder(self, code: Text) -> Text: def placeholder(self, code: str) -> str:
return self.markdown.htmlStash.store(code, safe=True) return self.markdown.htmlStash.store(code, safe=True)
def _escape(self, txt: Text) -> Text: def _escape(self, txt: str) -> str:
""" basic html escaping """ """ basic html escaping """
txt = txt.replace('&', '&amp;') txt = txt.replace('&', '&amp;')
txt = txt.replace('<', '&lt;') txt = txt.replace('<', '&lt;')

View File

@@ -1,6 +1,6 @@
import re import re
import markdown import markdown
from typing import Any, Dict, List, Optional, Union, Text from typing import Any, Dict, List, Optional, Union
from typing.re import Match from typing.re import Match
from markdown.preprocessors import Preprocessor from markdown.preprocessors import Preprocessor
@@ -91,7 +91,7 @@ class Setting(Preprocessor):
done = True done = True
return lines return lines
def handleMatch(self, match: Match[Text]) -> Text: def handleMatch(self, match: Match[str]) -> str:
setting_identifier = match.group('setting_identifier') setting_identifier = match.group('setting_identifier')
setting_type_name = link_mapping[setting_identifier][0] setting_type_name = link_mapping[setting_identifier][0]
setting_name = link_mapping[setting_identifier][1] setting_name = link_mapping[setting_identifier][1]

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Any, Dict, Optional, Text from typing import Any, Dict, Optional
import ujson import ujson
@@ -221,7 +221,7 @@ EMOJI_TWEET = """{
] ]
}""" }"""
def twitter(tweet_id: Text) -> Optional[Dict[Text, Any]]: def twitter(tweet_id: str) -> Optional[Dict[str, Any]]:
if tweet_id in ["112652479837110273", "287977969287315456", "287977969287315457"]: if tweet_id in ["112652479837110273", "287977969287315456", "287977969287315457"]:
return ujson.loads(NORMAL_TWEET) return ujson.loads(NORMAL_TWEET)
elif tweet_id == "287977969287315458": elif tweet_id == "287977969287315458":

View File

@@ -3,11 +3,9 @@ import codecs
import hashlib import hashlib
import hmac import hmac
from typing import Text
# Encodes the provided URL using the same algorithm used by the camo # Encodes the provided URL using the same algorithm used by the camo
# caching https image proxy # caching https image proxy
def get_camo_url(url: Text) -> Text: def get_camo_url(url: str) -> str:
# Only encode the url if Camo is enabled # Only encode the url if Camo is enabled
if settings.CAMO_URI == '': if settings.CAMO_URI == '':
return url return url

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Optional, Text from typing import Any, Dict, List, Optional
# This file is adapted from samples/shellinabox/ssh-krb-wrapper in # This file is adapted from samples/shellinabox/ssh-krb-wrapper in
# https://github.com/davidben/webathena, which has the following # https://github.com/davidben/webathena, which has the following
@@ -82,8 +82,8 @@ def der_encode_uint32(val: int) -> bytes:
raise ValueError("Bad value") raise ValueError("Bad value")
return der_encode_integer(val) return der_encode_integer(val)
def der_encode_string(val: Text) -> bytes: def der_encode_string(val: str) -> bytes:
if not isinstance(val, Text): if not isinstance(val, str):
raise TypeError("unicode") raise TypeError("unicode")
return der_encode_tlv(0x1b, val.encode("utf-8")) return der_encode_tlv(0x1b, val.encode("utf-8"))

View File

@@ -3,11 +3,11 @@ import time
from psycopg2.extensions import cursor, connection from psycopg2.extensions import cursor, connection
from typing import Callable, Optional, Iterable, Any, Dict, List, Union, TypeVar, \ from typing import Callable, Optional, Iterable, Any, Dict, List, Union, TypeVar, \
Mapping, Text Mapping
from zerver.lib.str_utils import NonBinaryStr from zerver.lib.str_utils import NonBinaryStr
CursorObj = TypeVar('CursorObj', bound=cursor) CursorObj = TypeVar('CursorObj', bound=cursor)
ParamsT = Union[Iterable[Any], Mapping[Text, Any]] ParamsT = Union[Iterable[Any], Mapping[str, Any]]
# Similar to the tracking done in Django's CursorDebugWrapper, but done at the # Similar to the tracking done in Django's CursorDebugWrapper, but done at the
# psycopg2 cursor level so it works with SQLAlchemy. # psycopg2 cursor level so it works with SQLAlchemy.

View File

@@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, Iterable, List, Set, Tuple, Text from typing import Any, Callable, Dict, Iterable, List, Set, Tuple
from collections import defaultdict from collections import defaultdict
import datetime import datetime
@@ -88,8 +88,8 @@ def gather_hot_conversations(user_profile: UserProfile, stream_messages: QuerySe
# Returns a list of dictionaries containing the templating # Returns a list of dictionaries containing the templating
# information for each hot conversation. # information for each hot conversation.
conversation_length = defaultdict(int) # type: Dict[Tuple[int, Text], int] conversation_length = defaultdict(int) # type: Dict[Tuple[int, str], int]
conversation_diversity = defaultdict(set) # type: Dict[Tuple[int, Text], Set[Text]] conversation_diversity = defaultdict(set) # type: Dict[Tuple[int, str], Set[str]]
for user_message in stream_messages: for user_message in stream_messages:
if not user_message.message.sent_by_human(): if not user_message.message.sent_by_human():
# Don't include automated messages in the count. # Don't include automated messages in the count.
@@ -143,7 +143,7 @@ def gather_hot_conversations(user_profile: UserProfile, stream_messages: QuerySe
hot_conversation_render_payloads.append(teaser_data) hot_conversation_render_payloads.append(teaser_data)
return hot_conversation_render_payloads return hot_conversation_render_payloads
def gather_new_users(user_profile: UserProfile, threshold: datetime.datetime) -> Tuple[int, List[Text]]: def gather_new_users(user_profile: UserProfile, threshold: datetime.datetime) -> Tuple[int, List[str]]:
# Gather information on users in the realm who have recently # Gather information on users in the realm who have recently
# joined. # joined.
if user_profile.realm.is_zephyr_mirror_realm: if user_profile.realm.is_zephyr_mirror_realm:
@@ -157,7 +157,7 @@ def gather_new_users(user_profile: UserProfile, threshold: datetime.datetime) ->
return len(user_names), user_names return len(user_names), user_names
def gather_new_streams(user_profile: UserProfile, def gather_new_streams(user_profile: UserProfile,
threshold: datetime.datetime) -> Tuple[int, Dict[str, List[Text]]]: threshold: datetime.datetime) -> Tuple[int, Dict[str, List[str]]]:
if user_profile.can_access_public_streams(): if user_profile.can_access_public_streams():
new_streams = list(get_active_streams(user_profile.realm).filter( new_streams = list(get_active_streams(user_profile.realm).filter(
invite_only=False, date_created__gt=threshold)) invite_only=False, date_created__gt=threshold))
@@ -177,7 +177,7 @@ def gather_new_streams(user_profile: UserProfile,
return len(new_streams), {"html": streams_html, "plain": streams_plain} return len(new_streams), {"html": streams_html, "plain": streams_plain}
def enough_traffic(unread_pms: Text, hot_conversations: Text, new_streams: int, new_users: int) -> bool: def enough_traffic(unread_pms: str, hot_conversations: str, new_streams: int, new_users: int) -> bool:
if unread_pms or hot_conversations: if unread_pms or hot_conversations:
# If you have any unread traffic, good enough. # If you have any unread traffic, good enough.
return True return True

View File

@@ -2,9 +2,8 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
import re import re
from typing import Text
def validate_domain(domain: Text) -> None: def validate_domain(domain: str) -> None:
if domain is None or len(domain) == 0: if domain is None or len(domain) == 0:
raise ValidationError(_("Domain can't be empty.")) raise ValidationError(_("Domain can't be empty."))
if '.' not in domain: if '.' not in domain:

View File

@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Optional, Text, Union from typing import Any, Dict, List, Optional, Union
import logging import logging
import re import re
@@ -28,7 +28,7 @@ talon.init()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def redact_stream(error_message: Text) -> Text: def redact_stream(error_message: str) -> str:
domain = settings.EMAIL_GATEWAY_PATTERN.rsplit('@')[-1] domain = settings.EMAIL_GATEWAY_PATTERN.rsplit('@')[-1]
stream_match = re.search('\\b(.*?)@' + domain, error_message) stream_match = re.search('\\b(.*?)@' + domain, error_message)
if stream_match: if stream_match:
@@ -36,7 +36,7 @@ def redact_stream(error_message: Text) -> Text:
return error_message.replace(stream_name, "X" * len(stream_name)) return error_message.replace(stream_name, "X" * len(stream_name))
return error_message return error_message
def report_to_zulip(error_message: Text) -> None: def report_to_zulip(error_message: str) -> None:
if settings.ERROR_BOT is None: if settings.ERROR_BOT is None:
return return
error_bot = get_system_bot(settings.ERROR_BOT) error_bot = get_system_bot(settings.ERROR_BOT)
@@ -44,7 +44,7 @@ def report_to_zulip(error_message: Text) -> None:
send_zulip(settings.ERROR_BOT, error_stream, "email mirror error", send_zulip(settings.ERROR_BOT, error_stream, "email mirror error",
"""~~~\n%s\n~~~""" % (error_message,)) """~~~\n%s\n~~~""" % (error_message,))
def log_and_report(email_message: message.Message, error_message: Text, debug_info: Dict[str, Any]) -> None: def log_and_report(email_message: message.Message, error_message: str, debug_info: Dict[str, Any]) -> None:
scrubbed_error = u"Sender: %s\n%s" % (email_message.get("From"), scrubbed_error = u"Sender: %s\n%s" % (email_message.get("From"),
redact_stream(error_message)) redact_stream(error_message))
@@ -65,22 +65,22 @@ def log_and_report(email_message: message.Message, error_message: Text, debug_in
redis_client = get_redis_client() redis_client = get_redis_client()
def missed_message_redis_key(token: Text) -> Text: def missed_message_redis_key(token: str) -> str:
return 'missed_message:' + token return 'missed_message:' + token
def is_missed_message_address(address: Text) -> bool: def is_missed_message_address(address: str) -> bool:
msg_string = get_email_gateway_message_string_from_address(address) msg_string = get_email_gateway_message_string_from_address(address)
return is_mm_32_format(msg_string) return is_mm_32_format(msg_string)
def is_mm_32_format(msg_string: Optional[Text]) -> bool: def is_mm_32_format(msg_string: Optional[str]) -> bool:
''' '''
Missed message strings are formatted with a little "mm" prefix Missed message strings are formatted with a little "mm" prefix
followed by a randomly generated 32-character string. followed by a randomly generated 32-character string.
''' '''
return msg_string is not None and msg_string.startswith('mm') and len(msg_string) == 34 return msg_string is not None and msg_string.startswith('mm') and len(msg_string) == 34
def get_missed_message_token_from_address(address: Text) -> Text: def get_missed_message_token_from_address(address: str) -> str:
msg_string = get_email_gateway_message_string_from_address(address) msg_string = get_email_gateway_message_string_from_address(address)
if msg_string is None: if msg_string is None:
@@ -125,7 +125,7 @@ def create_missed_message_address(user_profile: UserProfile, message: Message) -
return settings.EMAIL_GATEWAY_PATTERN % (address,) return settings.EMAIL_GATEWAY_PATTERN % (address,)
def mark_missed_message_address_as_used(address: Text) -> None: def mark_missed_message_address_as_used(address: str) -> None:
token = get_missed_message_token_from_address(address) token = get_missed_message_token_from_address(address)
key = missed_message_redis_key(token) key = missed_message_redis_key(token)
with redis_client.pipeline() as pipeline: with redis_client.pipeline() as pipeline:
@@ -136,7 +136,7 @@ def mark_missed_message_address_as_used(address: Text) -> None:
redis_client.delete(key) redis_client.delete(key)
raise ZulipEmailForwardError('Missed message address has already been used') raise ZulipEmailForwardError('Missed message address has already been used')
def construct_zulip_body(message: message.Message, realm: Realm) -> Text: def construct_zulip_body(message: message.Message, realm: Realm) -> str:
body = extract_body(message) body = extract_body(message)
# Remove null characters, since Zulip will reject # Remove null characters, since Zulip will reject
body = body.replace("\x00", "") body = body.replace("\x00", "")
@@ -147,7 +147,7 @@ def construct_zulip_body(message: message.Message, realm: Realm) -> Text:
body = '(No email body)' body = '(No email body)'
return body return body
def send_to_missed_message_address(address: Text, message: message.Message) -> None: def send_to_missed_message_address(address: str, message: message.Message) -> None:
token = get_missed_message_token_from_address(address) token = get_missed_message_token_from_address(address)
key = missed_message_redis_key(token) key = missed_message_redis_key(token)
result = redis_client.hmget(key, 'user_profile_id', 'recipient_id', 'subject') result = redis_client.hmget(key, 'user_profile_id', 'recipient_id', 'subject')
@@ -189,7 +189,7 @@ def send_to_missed_message_address(address: Text, message: message.Message) -> N
class ZulipEmailForwardError(Exception): class ZulipEmailForwardError(Exception):
pass pass
def send_zulip(sender: Text, stream: Stream, topic: Text, content: Text) -> None: def send_zulip(sender: str, stream: Stream, topic: str, content: str) -> None:
internal_send_message( internal_send_message(
stream.realm, stream.realm,
sender, sender,
@@ -199,14 +199,14 @@ def send_zulip(sender: Text, stream: Stream, topic: Text, content: Text) -> None
content[:2000], content[:2000],
email_gateway=True) email_gateway=True)
def valid_stream(stream_name: Text, token: Text) -> bool: def valid_stream(stream_name: str, token: str) -> bool:
try: try:
stream = Stream.objects.get(email_token=token) stream = Stream.objects.get(email_token=token)
return stream.name.lower() == stream_name.lower() return stream.name.lower() == stream_name.lower()
except Stream.DoesNotExist: except Stream.DoesNotExist:
return False return False
def get_message_part_by_type(message: message.Message, content_type: Text) -> Optional[Text]: def get_message_part_by_type(message: message.Message, content_type: str) -> Optional[str]:
charsets = message.get_charsets() charsets = message.get_charsets()
for idx, part in enumerate(message.walk()): for idx, part in enumerate(message.walk()):
@@ -217,7 +217,7 @@ def get_message_part_by_type(message: message.Message, content_type: Text) -> Op
return content.decode(charsets[idx], errors="ignore") return content.decode(charsets[idx], errors="ignore")
return None return None
def extract_body(message: message.Message) -> Text: def extract_body(message: message.Message) -> str:
# If the message contains a plaintext version of the body, use # If the message contains a plaintext version of the body, use
# that. # that.
plaintext_content = get_message_part_by_type(message, "text/plain") plaintext_content = get_message_part_by_type(message, "text/plain")
@@ -231,7 +231,7 @@ def extract_body(message: message.Message) -> Text:
raise ZulipEmailForwardError("Unable to find plaintext or HTML message body") raise ZulipEmailForwardError("Unable to find plaintext or HTML message body")
def filter_footer(text: Text) -> Text: def filter_footer(text: str) -> str:
# Try to filter out obvious footers. # Try to filter out obvious footers.
possible_footers = [line for line in text.split("\n") if line.strip().startswith("--")] possible_footers = [line for line in text.split("\n") if line.strip().startswith("--")]
if len(possible_footers) != 1: if len(possible_footers) != 1:
@@ -241,7 +241,7 @@ def filter_footer(text: Text) -> Text:
return text.partition("--")[0].strip() return text.partition("--")[0].strip()
def extract_and_upload_attachments(message: message.Message, realm: Realm) -> Text: def extract_and_upload_attachments(message: message.Message, realm: Realm) -> str:
user_profile = get_system_bot(settings.EMAIL_GATEWAY_BOT) user_profile = get_system_bot(settings.EMAIL_GATEWAY_BOT)
attachment_links = [] attachment_links = []
@@ -268,7 +268,7 @@ def extract_and_upload_attachments(message: message.Message, realm: Realm) -> Te
return "\n".join(attachment_links) return "\n".join(attachment_links)
def extract_and_validate(email: Text) -> Stream: def extract_and_validate(email: str) -> Stream:
temp = decode_email_address(email) temp = decode_email_address(email)
if temp is None: if temp is None:
raise ZulipEmailForwardError("Malformed email recipient " + email) raise ZulipEmailForwardError("Malformed email recipient " + email)
@@ -279,12 +279,12 @@ def extract_and_validate(email: Text) -> Stream:
return Stream.objects.get(email_token=token) return Stream.objects.get(email_token=token)
def find_emailgateway_recipient(message: message.Message) -> Text: def find_emailgateway_recipient(message: message.Message) -> str:
# We can't use Delivered-To; if there is a X-Gm-Original-To # We can't use Delivered-To; if there is a X-Gm-Original-To
# it is more accurate, so try to find the most-accurate # it is more accurate, so try to find the most-accurate
# recipient list in descending priority order # recipient list in descending priority order
recipient_headers = ["X-Gm-Original-To", "Delivered-To", "To"] recipient_headers = ["X-Gm-Original-To", "Delivered-To", "To"]
recipients = [] # type: List[Union[Text, Header]] recipients = [] # type: List[Union[str, Header]]
for recipient_header in recipient_headers: for recipient_header in recipient_headers:
r = message.get_all(recipient_header, None) r = message.get_all(recipient_header, None)
if r: if r:
@@ -299,7 +299,7 @@ def find_emailgateway_recipient(message: message.Message) -> Text:
raise ZulipEmailForwardError("Missing recipient in mirror email") raise ZulipEmailForwardError("Missing recipient in mirror email")
def process_stream_message(to: Text, subject: Text, message: message.Message, def process_stream_message(to: str, subject: str, message: message.Message,
debug_info: Dict[str, Any]) -> None: debug_info: Dict[str, Any]) -> None:
stream = extract_and_validate(to) stream = extract_and_validate(to)
body = construct_zulip_body(message, stream.realm) body = construct_zulip_body(message, stream.realm)
@@ -308,12 +308,12 @@ def process_stream_message(to: Text, subject: Text, message: message.Message,
logger.info("Successfully processed email to %s (%s)" % ( logger.info("Successfully processed email to %s (%s)" % (
stream.name, stream.realm.string_id)) stream.name, stream.realm.string_id))
def process_missed_message(to: Text, message: message.Message, pre_checked: bool) -> None: def process_missed_message(to: str, message: message.Message, pre_checked: bool) -> None:
if not pre_checked: if not pre_checked:
mark_missed_message_address_as_used(to) mark_missed_message_address_as_used(to)
send_to_missed_message_address(to, message) send_to_missed_message_address(to, message)
def process_message(message: message.Message, rcpt_to: Optional[Text]=None, pre_checked: bool=False) -> None: def process_message(message: message.Message, rcpt_to: Optional[str]=None, pre_checked: bool=False) -> None:
subject_header = str(message.get("Subject", "")).strip() subject_header = str(message.get("Subject", "")).strip()
if subject_header == "": if subject_header == "":
subject_header = "(no topic)" subject_header = "(no topic)"
@@ -344,7 +344,7 @@ def process_message(message: message.Message, rcpt_to: Optional[Text]=None, pre_
log_and_report(message, str(e), debug_info) log_and_report(message, str(e), debug_info)
def mirror_email_message(data: Dict[Text, Text]) -> Dict[str, str]: def mirror_email_message(data: Dict[str, str]) -> Dict[str, str]:
rcpt_to = data['recipient'] rcpt_to = data['recipient']
if is_missed_message_address(rcpt_to): if is_missed_message_address(rcpt_to):
try: try:

View File

@@ -5,7 +5,7 @@ import ujson
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from typing import Optional, Text, Tuple from typing import Optional, Tuple
from zerver.lib.request import JsonableError from zerver.lib.request import JsonableError
from zerver.lib.upload import upload_backend from zerver.lib.upload import upload_backend
@@ -34,7 +34,7 @@ emoticon_regex = ('(?<![^{0}])(?P<emoticon>('.format(terminal_symbols)
+ '))(?![^{0}])'.format(terminal_symbols)) + '))(?![^{0}])'.format(terminal_symbols))
# Translates emoticons to their colon syntax, e.g. `:smiley:`. # Translates emoticons to their colon syntax, e.g. `:smiley:`.
def translate_emoticons(text: Text) -> Text: def translate_emoticons(text: str) -> str:
translated = text translated = text
for emoticon in EMOTICON_CONVERSIONS: for emoticon in EMOTICON_CONVERSIONS:
@@ -48,7 +48,7 @@ with open(NAME_TO_CODEPOINT_PATH) as fp:
with open(CODEPOINT_TO_NAME_PATH) as fp: with open(CODEPOINT_TO_NAME_PATH) as fp:
codepoint_to_name = ujson.load(fp) codepoint_to_name = ujson.load(fp)
def emoji_name_to_emoji_code(realm: Realm, emoji_name: Text) -> Tuple[Text, Text]: def emoji_name_to_emoji_code(realm: Realm, emoji_name: str) -> Tuple[str, str]:
realm_emojis = realm.get_active_emoji() realm_emojis = realm.get_active_emoji()
realm_emoji = realm_emojis.get(emoji_name) realm_emoji = realm_emojis.get(emoji_name)
if realm_emoji is not None: if realm_emoji is not None:
@@ -59,7 +59,7 @@ def emoji_name_to_emoji_code(realm: Realm, emoji_name: Text) -> Tuple[Text, Text
return name_to_codepoint[emoji_name], Reaction.UNICODE_EMOJI return name_to_codepoint[emoji_name], Reaction.UNICODE_EMOJI
raise JsonableError(_("Emoji '%s' does not exist" % (emoji_name,))) raise JsonableError(_("Emoji '%s' does not exist" % (emoji_name,)))
def check_valid_emoji(realm: Realm, emoji_name: Text) -> None: def check_valid_emoji(realm: Realm, emoji_name: str) -> None:
emoji_name_to_emoji_code(realm, emoji_name) emoji_name_to_emoji_code(realm, emoji_name)
def check_emoji_request(realm: Realm, emoji_name: str, emoji_code: str, def check_emoji_request(realm: Realm, emoji_name: str, emoji_code: str,
@@ -89,7 +89,7 @@ def check_emoji_request(realm: Realm, emoji_name: str, emoji_code: str,
# The above are the only valid emoji types # The above are the only valid emoji types
raise JsonableError(_("Invalid emoji type.")) raise JsonableError(_("Invalid emoji type."))
def check_emoji_admin(user_profile: UserProfile, emoji_name: Optional[Text]=None) -> None: def check_emoji_admin(user_profile: UserProfile, emoji_name: Optional[str]=None) -> None:
"""Raises an exception if the user cannot administer the target realm """Raises an exception if the user cannot administer the target realm
emoji name in their organization.""" emoji name in their organization."""
@@ -113,15 +113,15 @@ def check_emoji_admin(user_profile: UserProfile, emoji_name: Optional[Text]=None
if not user_profile.is_realm_admin and not current_user_is_author: if not user_profile.is_realm_admin and not current_user_is_author:
raise JsonableError(_("Must be an organization administrator or emoji author")) raise JsonableError(_("Must be an organization administrator or emoji author"))
def check_valid_emoji_name(emoji_name: Text) -> None: def check_valid_emoji_name(emoji_name: str) -> None:
if re.match('^[0-9a-z.\-_]+(?<![.\-_])$', emoji_name): if re.match('^[0-9a-z.\-_]+(?<![.\-_])$', emoji_name):
return return
raise JsonableError(_("Invalid characters in emoji name")) raise JsonableError(_("Invalid characters in emoji name"))
def get_emoji_url(emoji_file_name: Text, realm_id: int) -> Text: def get_emoji_url(emoji_file_name: str, realm_id: int) -> str:
return upload_backend.get_emoji_url(emoji_file_name, realm_id) return upload_backend.get_emoji_url(emoji_file_name, realm_id)
def get_emoji_file_name(emoji_file_name: Text, emoji_id: int) -> Text: def get_emoji_file_name(emoji_file_name: str, emoji_id: int) -> str:
_, image_ext = os.path.splitext(emoji_file_name) _, image_ext = os.path.splitext(emoji_file_name)
return ''.join((str(emoji_id), image_ext)) return ''.join((str(emoji_id), image_ext))

View File

@@ -7,7 +7,7 @@ from django.conf import settings
from django.core.mail import mail_admins from django.core.mail import mail_admins
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from typing import Any, Dict, Optional, Text from typing import Any, Dict, Optional
from zerver.models import get_system_bot from zerver.models import get_system_bot
from zerver.lib.actions import internal_send_message from zerver.lib.actions import internal_send_message
@@ -136,7 +136,7 @@ def email_server_error(report: Dict[str, Any]) -> None:
mail_admins(format_subject(subject), message, fail_silently=True) mail_admins(format_subject(subject), message, fail_silently=True)
def do_report_error(deployment_name: Text, type: Text, report: Dict[str, Any]) -> HttpResponse: def do_report_error(deployment_name: str, type: str, report: Dict[str, Any]) -> HttpResponse:
report['deployment'] = deployment_name report['deployment'] = deployment_name
if type == 'browser': if type == 'browser':
notify_browser_error(report) notify_browser_error(report)

View File

@@ -2,7 +2,7 @@
import time import time
import logging import logging
from typing import Callable, List, TypeVar, Text from typing import Callable, List, TypeVar
from psycopg2.extensions import cursor from psycopg2.extensions import cursor
CursorObj = TypeVar('CursorObj', bound=cursor) CursorObj = TypeVar('CursorObj', bound=cursor)
@@ -20,7 +20,7 @@ migration runs.
logger = logging.getLogger('zulip.fix_unreads') logger = logging.getLogger('zulip.fix_unreads')
logger.setLevel(logging.WARNING) logger.setLevel(logging.WARNING)
def build_topic_mute_checker(cursor: CursorObj, user_profile: UserProfile) -> Callable[[int, Text], bool]: def build_topic_mute_checker(cursor: CursorObj, user_profile: UserProfile) -> Callable[[int, str], bool]:
''' '''
This function is similar to the function of the same name This function is similar to the function of the same name
in zerver/lib/topic_mutes.py, but it works without the ORM, in zerver/lib/topic_mutes.py, but it works without the ORM,
@@ -43,7 +43,7 @@ def build_topic_mute_checker(cursor: CursorObj, user_profile: UserProfile) -> Ca
for (recipient_id, topic_name) in rows for (recipient_id, topic_name) in rows
} }
def is_muted(recipient_id: int, topic: Text) -> bool: def is_muted(recipient_id: int, topic: str) -> bool:
return (recipient_id, topic.lower()) in tups return (recipient_id, topic.lower()) in tups
return is_muted return is_muted

View File

@@ -1,7 +1,7 @@
import itertools import itertools
import ujson import ujson
import random import random
from typing import List, Dict, Any, Text, Optional from typing import List, Dict, Any, Optional
def load_config() -> Dict[str, Any]: def load_config() -> Dict[str, Any]:
with open("zerver/tests/fixtures/config.generate_data.json", "r") as infile: with open("zerver/tests/fixtures/config.generate_data.json", "r") as infile:

View File

@@ -3,7 +3,7 @@ from django.utils.translation import ugettext as _
from zerver.models import UserProfile, UserHotspot from zerver.models import UserProfile, UserHotspot
from typing import List, Text, Dict from typing import List, Dict
ALL_HOTSPOTS = { ALL_HOTSPOTS = {
'intro_reply': { 'intro_reply': {
@@ -26,7 +26,7 @@ ALL_HOTSPOTS = {
'description': _('Click here to start a new conversation. Pick a topic ' 'description': _('Click here to start a new conversation. Pick a topic '
'(2-3 words is best), and give it a go!'), '(2-3 words is best), and give it a go!'),
}, },
} # type: Dict[str, Dict[str, Text]] } # type: Dict[str, Dict[str, str]]
def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]: def get_next_hotspots(user: UserProfile) -> List[Dict[str, object]]:
# For manual testing, it can be convenient to set # For manual testing, it can be convenient to set

View File

@@ -4,10 +4,10 @@ from django.conf import settings
import hashlib import hashlib
import base64 import base64
from typing import Optional, Text from typing import Optional
def initial_password(email: Text) -> Optional[Text]: def initial_password(email: str) -> Optional[str]:
"""Given an email address, returns the initial password for that account, as """Given an email address, returns the initial password for that account, as
created by populate_db.""" created by populate_db."""

View File

@@ -1,10 +1,10 @@
from typing import Any, Callable, Dict, List, Tuple, Text from typing import Any, Callable, Dict, List, Tuple
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
import re import re
import time import time
def create_index_if_not_exist(index_name: Text, table_name: Text, column_string: Text, def create_index_if_not_exist(index_name: str, table_name: str, column_string: str,
where_clause: Text) -> Text: where_clause: str) -> str:
# #
# FUTURE TODO: When we no longer need to support postgres 9.3 for Trusty, # FUTURE TODO: When we no longer need to support postgres 9.3 for Trusty,
# we can use "IF NOT EXISTS", which is part of postgres 9.5 # we can use "IF NOT EXISTS", which is part of postgres 9.5

View File

@@ -1,7 +1,6 @@
from typing import Text
from disposable_email_domains import blacklist from disposable_email_domains import blacklist
def is_reserved_subdomain(subdomain: Text) -> bool: def is_reserved_subdomain(subdomain: str) -> bool:
if subdomain in ZULIP_RESERVED_SUBDOMAINS: if subdomain in ZULIP_RESERVED_SUBDOMAINS:
return True return True
if subdomain[-1] == 's' and subdomain[:-1] in ZULIP_RESERVED_SUBDOMAINS: if subdomain[-1] == 's' and subdomain[:-1] in ZULIP_RESERVED_SUBDOMAINS:
@@ -12,7 +11,7 @@ def is_reserved_subdomain(subdomain: Text) -> bool:
return True return True
return False return False
def is_disposable_domain(domain: Text) -> bool: def is_disposable_domain(domain: str) -> bool:
return domain.lower() in DISPOSABLE_DOMAINS return domain.lower() in DISPOSABLE_DOMAINS
ZULIP_RESERVED_SUBDOMAINS = frozenset([ ZULIP_RESERVED_SUBDOMAINS = frozenset([

View File

@@ -1,16 +1,16 @@
from zerver.lib.request import JsonableError from zerver.lib.request import JsonableError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from typing import Any, Callable, Iterable, Mapping, Sequence, Text from typing import Any, Callable, Iterable, Mapping, Sequence
def check_supported_events_narrow_filter(narrow: Iterable[Sequence[Text]]) -> None: def check_supported_events_narrow_filter(narrow: Iterable[Sequence[str]]) -> None:
for element in narrow: for element in narrow:
operator = element[0] operator = element[0]
if operator not in ["stream", "topic", "sender", "is"]: if operator not in ["stream", "topic", "sender", "is"]:
raise JsonableError(_("Operator %s not supported.") % (operator,)) raise JsonableError(_("Operator %s not supported.") % (operator,))
def build_narrow_filter(narrow: Iterable[Sequence[Text]]) -> Callable[[Mapping[str, Any]], bool]: def build_narrow_filter(narrow: Iterable[Sequence[str]]) -> Callable[[Mapping[str, Any]], bool]:
"""Changes to this function should come with corresponding changes to """Changes to this function should come with corresponding changes to
BuildNarrowFilterTest.""" BuildNarrowFilterTest."""
check_supported_events_narrow_filter(narrow) check_supported_events_narrow_filter(narrow)

View File

@@ -1,15 +1,13 @@
from django.conf import settings from django.conf import settings
from typing import Text
from zerver.lib.avatar_hash import gravatar_hash, user_avatar_hash from zerver.lib.avatar_hash import gravatar_hash, user_avatar_hash
from zerver.lib.upload import upload_backend from zerver.lib.upload import upload_backend
from zerver.models import Realm from zerver.models import Realm
def realm_icon_url(realm: Realm) -> Text: def realm_icon_url(realm: Realm) -> str:
return get_realm_icon_url(realm) return get_realm_icon_url(realm)
def get_realm_icon_url(realm: Realm) -> Text: def get_realm_icon_url(realm: Realm) -> str:
if realm.icon_source == 'U': if realm.icon_source == 'U':
return upload_backend.get_realm_icon_url(realm.id, realm.icon_version) return upload_backend.get_realm_icon_url(realm.id, realm.icon_version)
elif settings.ENABLE_GRAVATAR: elif settings.ENABLE_GRAVATAR:

View File

@@ -2,13 +2,13 @@
from django.http import HttpResponse, HttpResponseNotAllowed from django.http import HttpResponse, HttpResponseNotAllowed
import ujson import ujson
from typing import Optional, Any, Dict, List, Text from typing import Optional, Any, Dict, List
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
class HttpResponseUnauthorized(HttpResponse): class HttpResponseUnauthorized(HttpResponse):
status_code = 401 status_code = 401
def __init__(self, realm: Text, www_authenticate: Optional[Text]=None) -> None: def __init__(self, realm: str, www_authenticate: Optional[str]=None) -> None:
HttpResponse.__init__(self) HttpResponse.__init__(self)
if www_authenticate is None: if www_authenticate is None:
self["WWW-Authenticate"] = 'Basic realm="%s"' % (realm,) self["WWW-Authenticate"] = 'Basic realm="%s"' % (realm,)
@@ -17,21 +17,21 @@ class HttpResponseUnauthorized(HttpResponse):
else: else:
raise AssertionError("Invalid www_authenticate value!") raise AssertionError("Invalid www_authenticate value!")
def json_unauthorized(message: Text, www_authenticate: Optional[Text]=None) -> HttpResponse: def json_unauthorized(message: str, www_authenticate: Optional[str]=None) -> HttpResponse:
resp = HttpResponseUnauthorized("zulip", www_authenticate=www_authenticate) resp = HttpResponseUnauthorized("zulip", www_authenticate=www_authenticate)
resp.content = (ujson.dumps({"result": "error", resp.content = (ujson.dumps({"result": "error",
"msg": message}) + "\n").encode() "msg": message}) + "\n").encode()
return resp return resp
def json_method_not_allowed(methods: List[Text]) -> HttpResponseNotAllowed: def json_method_not_allowed(methods: List[str]) -> HttpResponseNotAllowed:
resp = HttpResponseNotAllowed(methods) resp = HttpResponseNotAllowed(methods)
resp.content = ujson.dumps({"result": "error", resp.content = ujson.dumps({"result": "error",
"msg": "Method Not Allowed", "msg": "Method Not Allowed",
"allowed_methods": methods}).encode() "allowed_methods": methods}).encode()
return resp return resp
def json_response(res_type: Text="success", def json_response(res_type: str="success",
msg: Text="", msg: str="",
data: Optional[Dict[str, Any]]=None, data: Optional[Dict[str, Any]]=None,
status: int=200) -> HttpResponse: status: int=200) -> HttpResponse:
content = {"result": res_type, "msg": msg} content = {"result": res_type, "msg": msg}
@@ -56,5 +56,5 @@ def json_response_from_error(exception: JsonableError) -> HttpResponse:
data=exception.data, data=exception.data,
status=exception.http_status_code) status=exception.http_status_code)
def json_error(msg: Text, data: Optional[Dict[str, Any]]=None, status: int=400) -> HttpResponse: def json_error(msg: str, data: Optional[Dict[str, Any]]=None, status: int=400) -> HttpResponse:
return json_response(res_type="error", msg=msg, data=data, status=status) return json_response(res_type="error", msg=msg, data=data, status=status)

View File

@@ -6,13 +6,13 @@ from django.contrib.auth import SESSION_KEY, get_user_model
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from importlib import import_module from importlib import import_module
from typing import List, Mapping, Optional, Text from typing import List, Mapping, Optional
from zerver.models import Realm, UserProfile, get_user_profile_by_id from zerver.models import Realm, UserProfile, get_user_profile_by_id
session_engine = import_module(settings.SESSION_ENGINE) session_engine = import_module(settings.SESSION_ENGINE)
def get_session_dict_user(session_dict: Mapping[Text, int]) -> Optional[int]: def get_session_dict_user(session_dict: Mapping[str, int]) -> Optional[int]:
# Compare django.contrib.auth._get_user_session_key # Compare django.contrib.auth._get_user_session_key
try: try:
return get_user_model()._meta.pk.to_python(session_dict[SESSION_KEY]) return get_user_model()._meta.pk.to_python(session_dict[SESSION_KEY])

View File

@@ -8,7 +8,7 @@ Currently we have strings of 3 semantic types:
1. text strings: These strings are used to represent all textual data, 1. text strings: These strings are used to represent all textual data,
like people's names, stream names, content of messages, etc. like people's names, stream names, content of messages, etc.
These strings can contain non-ASCII characters, so its type should be These strings can contain non-ASCII characters, so its type should be
typing.Text (which is `str` in python 3 and `unicode` in python 2). typing.str (which is `str` in python 3 and `unicode` in python 2).
2. binary strings: These strings are used to represent binary data. 2. binary strings: These strings are used to represent binary data.
This should be of type `bytes` This should be of type `bytes`
@@ -29,34 +29,34 @@ force_text and force_bytes.
It is recommended to use the utility functions for other string conversions. It is recommended to use the utility functions for other string conversions.
""" """
from typing import Any, Dict, Mapping, Union, TypeVar, Text from typing import Any, Dict, Mapping, Union, TypeVar
NonBinaryStr = TypeVar('NonBinaryStr', str, Text) NonBinaryStr = TypeVar('NonBinaryStr', str, str)
# This is used to represent text or native strings # This is used to represent text or native strings
def force_text(s: Union[Text, bytes], encoding: str='utf-8') -> Text: def force_text(s: Union[str, bytes], encoding: str='utf-8') -> str:
"""converts a string to a text string""" """converts a string to a text string"""
if isinstance(s, Text): if isinstance(s, str):
return s return s
elif isinstance(s, bytes): elif isinstance(s, bytes):
return s.decode(encoding) return s.decode(encoding)
else: else:
raise TypeError("force_text expects a string type") raise TypeError("force_text expects a string type")
def force_bytes(s: Union[Text, bytes], encoding: str='utf-8') -> bytes: def force_bytes(s: Union[str, bytes], encoding: str='utf-8') -> bytes:
"""converts a string to binary string""" """converts a string to binary string"""
if isinstance(s, bytes): if isinstance(s, bytes):
return s return s
elif isinstance(s, Text): elif isinstance(s, str):
return s.encode(encoding) return s.encode(encoding)
else: else:
raise TypeError("force_bytes expects a string type") raise TypeError("force_bytes expects a string type")
def force_str(s: Union[Text, bytes], encoding: str='utf-8') -> str: def force_str(s: Union[str, bytes], encoding: str='utf-8') -> str:
"""converts a string to a native string""" """converts a string to a native string"""
if isinstance(s, str): if isinstance(s, str):
return s return s
elif isinstance(s, Text): elif isinstance(s, str):
return s.encode(encoding) return s.encode(encoding)
elif isinstance(s, bytes): elif isinstance(s, bytes):
return s.decode(encoding) return s.decode(encoding)
@@ -67,11 +67,11 @@ class ModelReprMixin:
""" """
This mixin provides a python 2 and 3 compatible way of handling string representation of a model. This mixin provides a python 2 and 3 compatible way of handling string representation of a model.
When declaring a model, inherit this mixin before django.db.models.Model. When declaring a model, inherit this mixin before django.db.models.Model.
Define __unicode__ on your model which returns a typing.Text object. Define __unicode__ on your model which returns a str object.
This mixin will automatically define __str__ and __repr__. This mixin will automatically define __str__ and __repr__.
""" """
def __unicode__(self) -> Text: def __unicode__(self) -> str:
# Originally raised an exception, but Django (e.g. the ./manage.py shell) # Originally raised an exception, but Django (e.g. the ./manage.py shell)
# was catching the exception and not displaying any sort of error # was catching the exception and not displaying any sort of error
return "Implement __unicode__ in your subclass of ModelReprMixin!" return "Implement __unicode__ in your subclass of ModelReprMixin!"

View File

@@ -3,11 +3,11 @@
from django.conf import settings from django.conf import settings
from django.http import HttpRequest from django.http import HttpRequest
import re import re
from typing import Optional, Text from typing import Optional
from zerver.models import get_realm, Realm, UserProfile from zerver.models import get_realm, Realm, UserProfile
def get_subdomain(request: HttpRequest) -> Text: def get_subdomain(request: HttpRequest) -> str:
# The HTTP spec allows, but doesn't require, a client to omit the # The HTTP spec allows, but doesn't require, a client to omit the
# port in the `Host` header if it's "the default port for the # port in the `Host` header if it's "the default port for the
@@ -41,7 +41,7 @@ def get_subdomain(request: HttpRequest) -> Text:
def is_subdomain_root_or_alias(request: HttpRequest) -> bool: def is_subdomain_root_or_alias(request: HttpRequest) -> bool:
return get_subdomain(request) == Realm.SUBDOMAIN_FOR_ROOT_DOMAIN return get_subdomain(request) == Realm.SUBDOMAIN_FOR_ROOT_DOMAIN
def user_matches_subdomain(realm_subdomain: Optional[Text], user_profile: UserProfile) -> bool: def user_matches_subdomain(realm_subdomain: Optional[str], user_profile: UserProfile) -> bool:
if realm_subdomain is None: if realm_subdomain is None:
return True # nocoverage # This state may no longer be possible. return True # nocoverage # This state may no longer be possible.
return user_profile.realm.subdomain == realm_subdomain return user_profile.realm.subdomain == realm_subdomain

View File

@@ -3,7 +3,7 @@ import os
import re import re
import hashlib import hashlib
import sys import sys
from typing import Any, List, Optional, Text from typing import Any, List, Optional
from importlib import import_module from importlib import import_module
from io import StringIO from io import StringIO
@@ -20,7 +20,7 @@ from scripts.lib.zulip_tools import get_dev_uuid_var_path
UUID_VAR_DIR = get_dev_uuid_var_path() UUID_VAR_DIR = get_dev_uuid_var_path()
FILENAME_SPLITTER = re.compile('[\W\-_]') FILENAME_SPLITTER = re.compile('[\W\-_]')
def database_exists(database_name: Text, **options: Any) -> bool: def database_exists(database_name: str, **options: Any) -> bool:
db = options.get('database', DEFAULT_DB_ALIAS) db = options.get('database', DEFAULT_DB_ALIAS)
try: try:
connection = connections[db] connection = connections[db]
@@ -59,7 +59,7 @@ def get_migration_status(**options: Any) -> str:
output = out.read() output = out.read()
return re.sub('\x1b\[(1|0)m', '', output) return re.sub('\x1b\[(1|0)m', '', output)
def are_migrations_the_same(migration_file: Text, **options: Any) -> bool: def are_migrations_the_same(migration_file: str, **options: Any) -> bool:
if not os.path.exists(migration_file): if not os.path.exists(migration_file):
return False return False

View File

@@ -3,9 +3,9 @@ import logging
import os import os
import subprocess import subprocess
from django.conf import settings from django.conf import settings
from typing import Optional, Text from typing import Optional
def render_tex(tex: Text, is_inline: bool=True) -> Optional[Text]: def render_tex(tex: str, is_inline: bool=True) -> Optional[str]:
"""Render a TeX string into HTML using KaTeX """Render a TeX string into HTML using KaTeX
Returns the HTML string, or None if there was some error in the TeX syntax Returns the HTML string, or None if there was some error in the TeX syntax

View File

@@ -1,10 +1,10 @@
from typing import Text, List from typing import List
import pytz import pytz
def get_all_timezones() -> List[Text]: def get_all_timezones() -> List[str]:
return sorted(pytz.all_timezones) return sorted(pytz.all_timezones)
def get_timezone(tz: Text) -> pytz.datetime.tzinfo: def get_timezone(tz: str) -> pytz.datetime.tzinfo:
return pytz.timezone(tz) return pytz.timezone(tz)

View File

@@ -2,7 +2,7 @@
import sys import sys
import functools import functools
from typing import Any, Callable, IO, Mapping, Sequence, TypeVar, Text from typing import Any, Callable, IO, Mapping, Sequence, TypeVar
def get_mapping_type_str(x: Mapping[Any, Any]) -> str: def get_mapping_type_str(x: Mapping[Any, Any]) -> str:
container_type = type(x).__name__ container_type = type(x).__name__
@@ -44,7 +44,7 @@ def get_sequence_type_str(x: Sequence[Any]) -> str:
else: else:
return '%s([%s, ...])' % (container_type, elem_type) return '%s([%s, ...])' % (container_type, elem_type)
expansion_blacklist = [Text, bytes] expansion_blacklist = [str, bytes]
def get_type_str(x: Any) -> str: def get_type_str(x: Any) -> str:
if x is None: if x is None:

View File

@@ -3,17 +3,17 @@ import re
import os import os
import sourcemap import sourcemap
from typing import Dict, List, Text from typing import Dict, List
class SourceMap: class SourceMap:
'''Map (line, column) pairs from generated to source file.''' '''Map (line, column) pairs from generated to source file.'''
def __init__(self, sourcemap_dirs: List[Text]) -> None: def __init__(self, sourcemap_dirs: List[str]) -> None:
self._dirs = sourcemap_dirs self._dirs = sourcemap_dirs
self._indices = {} # type: Dict[Text, sourcemap.SourceMapDecoder] self._indices = {} # type: Dict[str, sourcemap.SourceMapDecoder]
def _index_for(self, minified_src: Text) -> sourcemap.SourceMapDecoder: def _index_for(self, minified_src: str) -> sourcemap.SourceMapDecoder:
'''Return the source map index for minified_src, loading it if not '''Return the source map index for minified_src, loading it if not
already loaded.''' already loaded.'''
if minified_src not in self._indices: if minified_src not in self._indices:
@@ -26,8 +26,8 @@ class SourceMap:
return self._indices[minified_src] return self._indices[minified_src]
def annotate_stacktrace(self, stacktrace: Text) -> Text: def annotate_stacktrace(self, stacktrace: str) -> str:
out = '' # type: Text out = '' # type: str
for ln in stacktrace.splitlines(): for ln in stacktrace.splitlines():
out += ln + '\n' out += ln + '\n'
match = re.search(r'/static/(?:webpack-bundles|min)/(.+)(\.[\.0-9a-f]+)\.js:(\d+):(\d+)', ln) match = re.search(r'/static/(?:webpack-bundles|min)/(.+)(\.[\.0-9a-f]+)\.js:(\d+):(\d+)', ln)

View File

@@ -1,8 +1,8 @@
from typing import Optional, Text, Dict, Any from typing import Optional, Dict, Any
from pyoembed import oEmbed, PyOembedException from pyoembed import oEmbed, PyOembedException
def get_oembed_data(url: Text, def get_oembed_data(url: str,
maxwidth: Optional[int]=640, maxwidth: Optional[int]=640,
maxheight: Optional[int]=480) -> Optional[Dict[Any, Any]]: maxheight: Optional[int]=480) -> Optional[Dict[Any, Any]]:
try: try:

View File

@@ -1,9 +1,9 @@
from typing import Any, Text from typing import Any
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
class BaseParser: class BaseParser:
def __init__(self, html_source: Text) -> None: def __init__(self, html_source: str) -> None:
self._soup = BeautifulSoup(html_source, "lxml") self._soup = BeautifulSoup(html_source, "lxml")
def extract_data(self) -> Any: def extract_data(self) -> Any:

View File

@@ -1,15 +1,15 @@
from typing import Dict, Optional, Text from typing import Dict, Optional
from zerver.lib.url_preview.parsers.base import BaseParser from zerver.lib.url_preview.parsers.base import BaseParser
class GenericParser(BaseParser): class GenericParser(BaseParser):
def extract_data(self) -> Dict[str, Optional[Text]]: def extract_data(self) -> Dict[str, Optional[str]]:
return { return {
'title': self._get_title(), 'title': self._get_title(),
'description': self._get_description(), 'description': self._get_description(),
'image': self._get_image()} 'image': self._get_image()}
def _get_title(self) -> Optional[Text]: def _get_title(self) -> Optional[str]:
soup = self._soup soup = self._soup
if (soup.title and soup.title.text != ''): if (soup.title and soup.title.text != ''):
return soup.title.text return soup.title.text
@@ -17,7 +17,7 @@ class GenericParser(BaseParser):
return soup.h1.text return soup.h1.text
return None return None
def _get_description(self) -> Optional[Text]: def _get_description(self) -> Optional[str]:
soup = self._soup soup = self._soup
meta_description = soup.find('meta', attrs={'name': 'description'}) meta_description = soup.find('meta', attrs={'name': 'description'})
if (meta_description and meta_description['content'] != ''): if (meta_description and meta_description['content'] != ''):
@@ -32,7 +32,7 @@ class GenericParser(BaseParser):
return first_p.string return first_p.string
return None return None
def _get_image(self) -> Optional[Text]: def _get_image(self) -> Optional[str]:
""" """
Finding a first image after the h1 header. Finding a first image after the h1 header.
Presumably it will be the main image. Presumably it will be the main image.

View File

@@ -1,10 +1,10 @@
import re import re
from typing import Dict, Text from typing import Dict
from .base import BaseParser from .base import BaseParser
class OpenGraphParser(BaseParser): class OpenGraphParser(BaseParser):
def extract_data(self) -> Dict[str, Text]: def extract_data(self) -> Dict[str, str]:
meta = self._soup.findAll('meta') meta = self._soup.findAll('meta')
content = {} content = {}
for tag in meta: for tag in meta:

View File

@@ -1,7 +1,7 @@
import re import re
import logging import logging
import traceback import traceback
from typing import Any, Optional, Text, Dict from typing import Any, Optional, Dict
from typing.re import Match from typing.re import Match
import requests import requests
from zerver.lib.cache import cache_with_key, get_cache_with_key from zerver.lib.cache import cache_with_key, get_cache_with_key
@@ -20,16 +20,16 @@ link_regex = re.compile(
r'(?:/?|[/?]\S+)$', re.IGNORECASE) r'(?:/?|[/?]\S+)$', re.IGNORECASE)
def is_link(url: Text) -> Match[Text]: def is_link(url: str) -> Match[str]:
return link_regex.match(smart_text(url)) return link_regex.match(smart_text(url))
def cache_key_func(url: Text) -> Text: def cache_key_func(url: str) -> str:
return url return url
@cache_with_key(cache_key_func, cache_name=CACHE_NAME, with_statsd_key="urlpreview_data") @cache_with_key(cache_key_func, cache_name=CACHE_NAME, with_statsd_key="urlpreview_data")
def get_link_embed_data(url: Text, def get_link_embed_data(url: str,
maxwidth: Optional[int]=640, maxwidth: Optional[int]=640,
maxheight: Optional[int]=480) -> Optional[Dict[Any, Any]]: maxheight: Optional[int]=480) -> Optional[Dict[Any, Any]]:
if not is_link(url): if not is_link(url):
@@ -59,5 +59,5 @@ def get_link_embed_data(url: Text,
@get_cache_with_key(cache_key_func, cache_name=CACHE_NAME) @get_cache_with_key(cache_key_func, cache_name=CACHE_NAME)
def link_embed_data_from_cache(url: Text, maxwidth: Optional[int]=640, maxheight: Optional[int]=480) -> Any: def link_embed_data_from_cache(url: str, maxwidth: Optional[int]=640, maxheight: Optional[int]=480) -> Any:
return return

View File

@@ -5,7 +5,7 @@ from django.db import transaction
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
from zerver.models import UserProfile, Realm, UserGroupMembership, UserGroup from zerver.models import UserProfile, Realm, UserGroupMembership, UserGroup
from typing import Dict, Iterable, List, Text, Tuple, Any from typing import Dict, Iterable, List, Tuple, Any
def access_user_group_by_id(user_group_id: int, user_profile: UserProfile) -> UserGroup: def access_user_group_by_id(user_group_id: int, user_profile: UserProfile) -> UserGroup:
try: try:
@@ -22,7 +22,7 @@ def user_groups_in_realm(realm: Realm) -> List[UserGroup]:
user_groups = UserGroup.objects.filter(realm=realm) user_groups = UserGroup.objects.filter(realm=realm)
return list(user_groups) return list(user_groups)
def user_groups_in_realm_serialized(realm: Realm) -> List[Dict[Text, Any]]: def user_groups_in_realm_serialized(realm: Realm) -> List[Dict[str, Any]]:
"""This function is used in do_events_register code path so this code """This function is used in do_events_register code path so this code
should be performant. We need to do 2 database queries because should be performant. We need to do 2 database queries because
Django's ORM doesn't properly support the left join between Django's ORM doesn't properly support the left join between
@@ -67,8 +67,8 @@ def check_remove_user_from_user_group(user_profile: UserProfile, user_group: Use
except Exception: except Exception:
return False return False
def create_user_group(name: Text, members: List[UserProfile], realm: Realm, def create_user_group(name: str, members: List[UserProfile], realm: Realm,
description: Text='') -> UserGroup: description: str='') -> UserGroup:
with transaction.atomic(): with transaction.atomic():
user_group = UserGroup.objects.create(name=name, realm=realm, user_group = UserGroup.objects.create(name=name, realm=realm,
description=description) description=description)

View File

@@ -1,4 +1,4 @@
from typing import Optional, Any, Dict, List, Text, Tuple from typing import Optional, Any, Dict, List, Tuple
from collections import defaultdict from collections import defaultdict
SUBJECT_WITH_BRANCH_TEMPLATE = '{repo} / {branch}' SUBJECT_WITH_BRANCH_TEMPLATE = '{repo} / {branch}'
SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE = '{repo} / {type} #{id} {title}' SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE = '{repo} / {type} #{id} {title}'
@@ -49,10 +49,10 @@ TAG_WITH_URL_TEMPLATE = "[{tag_name}]({tag_url})"
TAG_WITHOUT_URL_TEMPLATE = "{tag_name}" TAG_WITHOUT_URL_TEMPLATE = "{tag_name}"
def get_push_commits_event_message(user_name: Text, compare_url: Optional[Text], def get_push_commits_event_message(user_name: str, compare_url: Optional[str],
branch_name: Text, commits_data: List[Dict[str, Any]], branch_name: str, commits_data: List[Dict[str, Any]],
is_truncated: Optional[bool]=False, is_truncated: Optional[bool]=False,
deleted: Optional[bool]=False) -> Text: deleted: Optional[bool]=False) -> str:
if not commits_data and deleted: if not commits_data and deleted:
return PUSH_DELETE_BRANCH_MESSAGE_TEMPLATE.format( return PUSH_DELETE_BRANCH_MESSAGE_TEMPLATE.format(
user_name=user_name, user_name=user_name,
@@ -99,7 +99,7 @@ def get_push_commits_event_message(user_name: Text, compare_url: Optional[Text],
commits_data=get_commits_content(commits_data, is_truncated), commits_data=get_commits_content(commits_data, is_truncated),
).rstrip() ).rstrip()
def get_force_push_commits_event_message(user_name: Text, url: Text, branch_name: Text, head: Text) -> Text: def get_force_push_commits_event_message(user_name: str, url: str, branch_name: str, head: str) -> str:
return FORCE_PUSH_COMMITS_MESSAGE_TEMPLATE.format( return FORCE_PUSH_COMMITS_MESSAGE_TEMPLATE.format(
user_name=user_name, user_name=user_name,
url=url, url=url,
@@ -107,23 +107,23 @@ def get_force_push_commits_event_message(user_name: Text, url: Text, branch_name
head=head head=head
) )
def get_create_branch_event_message(user_name: Text, url: Text, branch_name: Text) -> Text: def get_create_branch_event_message(user_name: str, url: str, branch_name: str) -> str:
return CREATE_BRANCH_MESSAGE_TEMPLATE.format( return CREATE_BRANCH_MESSAGE_TEMPLATE.format(
user_name=user_name, user_name=user_name,
url=url, url=url,
branch_name=branch_name, branch_name=branch_name,
) )
def get_remove_branch_event_message(user_name: Text, branch_name: Text) -> Text: def get_remove_branch_event_message(user_name: str, branch_name: str) -> str:
return REMOVE_BRANCH_MESSAGE_TEMPLATE.format( return REMOVE_BRANCH_MESSAGE_TEMPLATE.format(
user_name=user_name, user_name=user_name,
branch_name=branch_name, branch_name=branch_name,
) )
def get_pull_request_event_message(user_name: Text, action: Text, url: Text, number: Optional[int]=None, def get_pull_request_event_message(user_name: str, action: str, url: str, number: Optional[int]=None,
target_branch: Optional[Text]=None, base_branch: Optional[Text]=None, target_branch: Optional[str]=None, base_branch: Optional[str]=None,
message: Optional[Text]=None, assignee: Optional[Text]=None, message: Optional[str]=None, assignee: Optional[str]=None,
type: Optional[Text]='PR') -> Text: type: Optional[str]='PR') -> str:
main_message = PULL_REQUEST_OR_ISSUE_MESSAGE_TEMPLATE.format( main_message = PULL_REQUEST_OR_ISSUE_MESSAGE_TEMPLATE.format(
user_name=user_name, user_name=user_name,
action=action, action=action,
@@ -143,18 +143,18 @@ def get_pull_request_event_message(user_name: Text, action: Text, url: Text, num
main_message += '\n' + CONTENT_MESSAGE_TEMPLATE.format(message=message) main_message += '\n' + CONTENT_MESSAGE_TEMPLATE.format(message=message)
return main_message.rstrip() return main_message.rstrip()
def get_setup_webhook_message(integration: Text, user_name: Optional[Text]=None) -> Text: def get_setup_webhook_message(integration: str, user_name: Optional[str]=None) -> str:
content = SETUP_MESSAGE_TEMPLATE.format(integration=integration) content = SETUP_MESSAGE_TEMPLATE.format(integration=integration)
if user_name: if user_name:
content += SETUP_MESSAGE_USER_PART.format(user_name=user_name) content += SETUP_MESSAGE_USER_PART.format(user_name=user_name)
return content return content
def get_issue_event_message(user_name: Text, def get_issue_event_message(user_name: str,
action: Text, action: str,
url: Text, url: str,
number: Optional[int]=None, number: Optional[int]=None,
message: Optional[Text]=None, message: Optional[str]=None,
assignee: Optional[Text]=None) -> Text: assignee: Optional[str]=None) -> str:
return get_pull_request_event_message( return get_pull_request_event_message(
user_name, user_name,
action, action,
@@ -165,10 +165,10 @@ def get_issue_event_message(user_name: Text,
type='Issue' type='Issue'
) )
def get_push_tag_event_message(user_name: Text, def get_push_tag_event_message(user_name: str,
tag_name: Text, tag_name: str,
tag_url: Optional[Text]=None, tag_url: Optional[str]=None,
action: Optional[Text]='pushed') -> Text: action: Optional[str]='pushed') -> str:
if tag_url: if tag_url:
tag_part = TAG_WITH_URL_TEMPLATE.format(tag_name=tag_name, tag_url=tag_url) tag_part = TAG_WITH_URL_TEMPLATE.format(tag_name=tag_name, tag_url=tag_url)
else: else:
@@ -179,11 +179,11 @@ def get_push_tag_event_message(user_name: Text,
tag=tag_part tag=tag_part
) )
def get_commits_comment_action_message(user_name: Text, def get_commits_comment_action_message(user_name: str,
action: Text, action: str,
commit_url: Text, commit_url: str,
sha: Text, sha: str,
message: Optional[Text]=None) -> Text: message: Optional[str]=None) -> str:
content = COMMITS_COMMENT_MESSAGE_TEMPLATE.format( content = COMMITS_COMMENT_MESSAGE_TEMPLATE.format(
user_name=user_name, user_name=user_name,
action=action, action=action,
@@ -196,7 +196,7 @@ def get_commits_comment_action_message(user_name: Text,
) )
return content return content
def get_commits_content(commits_data: List[Dict[str, Any]], is_truncated: Optional[bool]=False) -> Text: def get_commits_content(commits_data: List[Dict[str, Any]], is_truncated: Optional[bool]=False) -> str:
commits_content = '' commits_content = ''
for commit in commits_data[:COMMITS_LIMIT]: for commit in commits_data[:COMMITS_LIMIT]:
commits_content += COMMIT_ROW_TEMPLATE.format( commits_content += COMMIT_ROW_TEMPLATE.format(
@@ -215,7 +215,7 @@ def get_commits_content(commits_data: List[Dict[str, Any]], is_truncated: Option
).replace(' ', ' ') ).replace(' ', ' ')
return commits_content.rstrip() return commits_content.rstrip()
def get_short_sha(sha: Text) -> Text: def get_short_sha(sha: str) -> str:
return sha[:7] return sha[:7]
def get_all_committers(commits_data: List[Dict[str, Any]]) -> List[Tuple[str, int]]: def get_all_committers(commits_data: List[Dict[str, Any]]) -> List[Tuple[str, int]]:

View File

@@ -1,4 +1,3 @@
from typing import Text
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@@ -8,9 +7,9 @@ class BadEventQueueIdError(JsonableError):
code = ErrorCode.BAD_EVENT_QUEUE_ID code = ErrorCode.BAD_EVENT_QUEUE_ID
data_fields = ['queue_id'] data_fields = ['queue_id']
def __init__(self, queue_id: Text) -> None: def __init__(self, queue_id: str) -> None:
self.queue_id = queue_id # type: Text self.queue_id = queue_id # type: str
@staticmethod @staticmethod
def msg_format() -> Text: def msg_format() -> str:
return _("Bad event queue id: {queue_id}") return _("Bad event queue id: {queue_id}")