mirror of https://github.com/zulip/zulip.git
mypy: Convert several directories to use typing.Text.
Specifically, these directories are converted: [analytics/, scripts/, tools/, zerver/management/, zilencer/, zproject/]
This commit is contained in:
parent
2288120155
commit
beaa62cafa
|
@ -8,8 +8,7 @@ from analytics.models import InstallationCount, RealmCount, \
|
||||||
from zerver.models import Realm, UserProfile, Message, Stream, models
|
from zerver.models import Realm, UserProfile, Message, Stream, models
|
||||||
from zerver.lib.timestamp import floor_to_day
|
from zerver.lib.timestamp import floor_to_day
|
||||||
|
|
||||||
from typing import Any, Optional, Type, Tuple
|
from typing import Any, Optional, Type, Tuple, Text
|
||||||
from six import text_type
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
@ -37,7 +36,7 @@ class CountStat(object):
|
||||||
GAUGE = 'gauge'
|
GAUGE = 'gauge'
|
||||||
|
|
||||||
def __init__(self, property, zerver_count_query, filter_args, group_by, frequency, is_gauge):
|
def __init__(self, property, zerver_count_query, filter_args, group_by, frequency, is_gauge):
|
||||||
# type: (text_type, ZerverCountQuery, Dict[str, bool], Optional[Tuple[models.Model, str]], str, bool) -> None
|
# type: (Text, ZerverCountQuery, Dict[str, bool], Optional[Tuple[models.Model, str]], str, bool) -> None
|
||||||
self.property = property
|
self.property = property
|
||||||
self.zerver_count_query = zerver_count_query
|
self.zerver_count_query = zerver_count_query
|
||||||
# might have to do something different for bitfields
|
# might have to do something different for bitfields
|
||||||
|
@ -50,7 +49,7 @@ class CountStat(object):
|
||||||
|
|
||||||
class ZerverCountQuery(object):
|
class ZerverCountQuery(object):
|
||||||
def __init__(self, zerver_table, analytics_table, query):
|
def __init__(self, zerver_table, analytics_table, query):
|
||||||
# type: (Type[models.Model], Type[BaseCount], text_type) -> None
|
# type: (Type[models.Model], Type[BaseCount], Text) -> None
|
||||||
self.zerver_table = zerver_table
|
self.zerver_table = zerver_table
|
||||||
self.analytics_table = analytics_table
|
self.analytics_table = analytics_table
|
||||||
self.query = query
|
self.query = query
|
||||||
|
|
|
@ -7,11 +7,10 @@ from zerver.lib.timestamp import datetime_to_UTC, floor_to_day
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from six import text_type
|
from typing import Optional, Tuple, Union, Dict, Any, Text
|
||||||
from typing import Optional, Tuple, Union, Dict, Any
|
|
||||||
|
|
||||||
class FillState(ModelReprMixin, models.Model):
|
class FillState(ModelReprMixin, models.Model):
|
||||||
property = models.CharField(max_length=40, unique=True) # type: text_type
|
property = models.CharField(max_length=40, unique=True) # type: Text
|
||||||
end_time = models.DateTimeField() # type: datetime.datetime
|
end_time = models.DateTimeField() # type: datetime.datetime
|
||||||
|
|
||||||
# Valid states are {DONE, STARTED}
|
# Valid states are {DONE, STARTED}
|
||||||
|
@ -22,11 +21,11 @@ class FillState(ModelReprMixin, models.Model):
|
||||||
last_modified = models.DateTimeField(auto_now=True) # type: datetime.datetime
|
last_modified = models.DateTimeField(auto_now=True) # type: datetime.datetime
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
return u"<FillState: %s %s %s>" % (self.property, self.end_time, self.state)
|
return u"<FillState: %s %s %s>" % (self.property, self.end_time, self.state)
|
||||||
|
|
||||||
def get_fill_state(property):
|
def get_fill_state(property):
|
||||||
# type: (text_type) -> Optional[Dict[str, Any]]
|
# type: (Text) -> Optional[Dict[str, Any]]
|
||||||
try:
|
try:
|
||||||
return FillState.objects.filter(property = property).values('end_time', 'state')[0]
|
return FillState.objects.filter(property = property).values('end_time', 'state')[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
@ -41,20 +40,20 @@ def installation_epoch():
|
||||||
|
|
||||||
# would only ever make entries here by hand
|
# would only ever make entries here by hand
|
||||||
class Anomaly(ModelReprMixin, models.Model):
|
class Anomaly(ModelReprMixin, models.Model):
|
||||||
info = models.CharField(max_length=1000) # type: text_type
|
info = models.CharField(max_length=1000) # type: Text
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
return u"<Anomaly: %s... %s>" % (self.info, self.id)
|
return u"<Anomaly: %s... %s>" % (self.info, self.id)
|
||||||
|
|
||||||
class BaseCount(ModelReprMixin, models.Model):
|
class BaseCount(ModelReprMixin, models.Model):
|
||||||
# Note: When inheriting from BaseCount, you may want to rearrange
|
# Note: When inheriting from BaseCount, you may want to rearrange
|
||||||
# the order of the columns in the migration to make sure they
|
# the order of the columns in the migration to make sure they
|
||||||
# match how you'd like the table to be arranged.
|
# match how you'd like the table to be arranged.
|
||||||
property = models.CharField(max_length=32) # type: text_type
|
property = models.CharField(max_length=32) # type: Text
|
||||||
subgroup = models.CharField(max_length=16, null=True) # type: text_type
|
subgroup = models.CharField(max_length=16, null=True) # type: Text
|
||||||
end_time = models.DateTimeField() # type: datetime.datetime
|
end_time = models.DateTimeField() # type: datetime.datetime
|
||||||
interval = models.CharField(max_length=8) # type: text_type
|
interval = models.CharField(max_length=8) # type: Text
|
||||||
value = models.BigIntegerField() # type: int
|
value = models.BigIntegerField() # type: int
|
||||||
anomaly = models.ForeignKey(Anomaly, null=True) # type: Optional[Anomaly]
|
anomaly = models.ForeignKey(Anomaly, null=True) # type: Optional[Anomaly]
|
||||||
|
|
||||||
|
@ -87,7 +86,7 @@ class InstallationCount(BaseCount):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
return u"<InstallationCount: %s %s>" % (self.property, self.value)
|
return u"<InstallationCount: %s %s>" % (self.property, self.value)
|
||||||
|
|
||||||
class RealmCount(BaseCount):
|
class RealmCount(BaseCount):
|
||||||
|
@ -107,7 +106,7 @@ class RealmCount(BaseCount):
|
||||||
return Realm
|
return Realm
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
return u"<RealmCount: %s %s %s>" % (self.realm, self.property, self.value)
|
return u"<RealmCount: %s %s %s>" % (self.realm, self.property, self.value)
|
||||||
|
|
||||||
class UserCount(BaseCount):
|
class UserCount(BaseCount):
|
||||||
|
@ -128,7 +127,7 @@ class UserCount(BaseCount):
|
||||||
return UserProfile
|
return UserProfile
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
return u"<UserCount: %s %s %s>" % (self.user, self.property, self.value)
|
return u"<UserCount: %s %s %s>" % (self.user, self.property, self.value)
|
||||||
|
|
||||||
class StreamCount(BaseCount):
|
class StreamCount(BaseCount):
|
||||||
|
@ -149,5 +148,5 @@ class StreamCount(BaseCount):
|
||||||
return Stream
|
return Stream
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
return u"<StreamCount: %s %s %s %s>" % (self.stream, self.property, self.value, self.id)
|
return u"<StreamCount: %s %s %s %s>" % (self.stream, self.property, self.value, self.id)
|
||||||
|
|
|
@ -14,8 +14,7 @@ from zerver.models import Realm, UserProfile, Message, Stream, Recipient, \
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from typing import Any, Type, Optional
|
from typing import Any, Type, Optional, Text
|
||||||
from six import text_type
|
|
||||||
|
|
||||||
class AnalyticsTestCase(TestCase):
|
class AnalyticsTestCase(TestCase):
|
||||||
MINUTE = timedelta(seconds = 60)
|
MINUTE = timedelta(seconds = 60)
|
||||||
|
@ -71,7 +70,7 @@ class AnalyticsTestCase(TestCase):
|
||||||
# kwargs should only ever be a UserProfile or Stream.
|
# kwargs should only ever be a UserProfile or Stream.
|
||||||
def assertCountEquals(self, table, property, value, end_time = TIME_ZERO, interval = CountStat.HOUR,
|
def assertCountEquals(self, table, property, value, end_time = TIME_ZERO, interval = CountStat.HOUR,
|
||||||
realm = None, **kwargs):
|
realm = None, **kwargs):
|
||||||
# type: (Type[BaseCount], text_type, int, datetime, str, Optional[Realm], **models.Model) -> None
|
# type: (Type[BaseCount], Text, int, datetime, str, Optional[Realm], **models.Model) -> None
|
||||||
if realm is None:
|
if realm is None:
|
||||||
realm = self.default_realm
|
realm = self.default_realm
|
||||||
self.assertEqual(table.objects.filter(realm=realm,
|
self.assertEqual(table.objects.filter(realm=realm,
|
||||||
|
@ -134,7 +133,7 @@ class TestProcessCountStat(AnalyticsTestCase):
|
||||||
return count_stat
|
return count_stat
|
||||||
|
|
||||||
def assertFillStateEquals(self, end_time, state = FillState.DONE, property = None):
|
def assertFillStateEquals(self, end_time, state = FillState.DONE, property = None):
|
||||||
# type: (datetime, int, Optional[text_type]) -> None
|
# type: (datetime, int, Optional[Text]) -> None
|
||||||
count_stat = self.make_dummy_count_stat(end_time)
|
count_stat = self.make_dummy_count_stat(end_time)
|
||||||
if property is None:
|
if property is None:
|
||||||
property = count_stat.property
|
property = count_stat.property
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
from six import text_type
|
from typing import Any, Dict, List, Tuple, Optional, Sequence, Callable, Union, Text
|
||||||
from typing import Any, Dict, List, Tuple, Optional, Sequence, Callable, Union
|
|
||||||
|
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
@ -789,7 +788,7 @@ def user_activity_summary_table(user_summary):
|
||||||
return make_table(title, cols, rows)
|
return make_table(title, cols, rows)
|
||||||
|
|
||||||
def realm_user_summary_table(all_records, admin_emails):
|
def realm_user_summary_table(all_records, admin_emails):
|
||||||
# type: (List[QuerySet], Set[text_type]) -> Tuple[Dict[str, Dict[str, Any]], str]
|
# type: (List[QuerySet], Set[Text]) -> Tuple[Dict[str, Dict[str, Any]], str]
|
||||||
user_records = {}
|
user_records = {}
|
||||||
|
|
||||||
def by_email(record):
|
def by_email(record):
|
||||||
|
|
|
@ -5,7 +5,7 @@ from __future__ import print_function
|
||||||
import sys, os, os.path
|
import sys, os, os.path
|
||||||
from os.path import dirname, abspath
|
from os.path import dirname, abspath
|
||||||
if False:
|
if False:
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional, Text
|
||||||
|
|
||||||
BASE_DIR = dirname(dirname(dirname(abspath(__file__))))
|
BASE_DIR = dirname(dirname(dirname(abspath(__file__))))
|
||||||
sys.path.append(BASE_DIR)
|
sys.path.append(BASE_DIR)
|
||||||
|
@ -14,7 +14,6 @@ import scripts.lib.setup_path_on_import
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'zproject.settings'
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'zproject.settings'
|
||||||
|
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from six import text_type
|
|
||||||
import six
|
import six
|
||||||
import argparse
|
import argparse
|
||||||
from zerver.lib.str_utils import force_str
|
from zerver.lib.str_utils import force_str
|
||||||
|
@ -30,7 +29,7 @@ AUTOGENERATED_SETTINGS = ['shared_secret', 'avatar_salt', 'rabbitmq_password', '
|
||||||
# TODO: We can eliminate this function if we refactor the install
|
# TODO: We can eliminate this function if we refactor the install
|
||||||
# script to run generate_secrets before zulip-puppet-apply.
|
# script to run generate_secrets before zulip-puppet-apply.
|
||||||
def generate_camo_config_file(camo_key):
|
def generate_camo_config_file(camo_key):
|
||||||
# type: (text_type) -> None
|
# type: (Text) -> None
|
||||||
camo_config = """ENABLED=yes
|
camo_config = """ENABLED=yes
|
||||||
PORT=9292
|
PORT=9292
|
||||||
CAMO_KEY=%s
|
CAMO_KEY=%s
|
||||||
|
@ -40,13 +39,13 @@ CAMO_KEY=%s
|
||||||
print("Generated Camo config file %s" % (CAMO_CONFIG_FILENAME,))
|
print("Generated Camo config file %s" % (CAMO_CONFIG_FILENAME,))
|
||||||
|
|
||||||
def generate_django_secretkey():
|
def generate_django_secretkey():
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
"""Secret key generation taken from Django's startproject.py"""
|
"""Secret key generation taken from Django's startproject.py"""
|
||||||
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
|
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
|
||||||
return get_random_string(50, chars)
|
return get_random_string(50, chars)
|
||||||
|
|
||||||
def get_old_conf(output_filename):
|
def get_old_conf(output_filename):
|
||||||
# type: (text_type) -> Dict[str, text_type]
|
# type: (Text) -> Dict[str, Text]
|
||||||
if not os.path.exists(output_filename):
|
if not os.path.exists(output_filename):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ def get_old_conf(output_filename):
|
||||||
secrets_file.read(output_filename)
|
secrets_file.read(output_filename)
|
||||||
|
|
||||||
def get_secret(key):
|
def get_secret(key):
|
||||||
# type: (text_type) -> Optional[text_type]
|
# type: (Text) -> Optional[Text]
|
||||||
if secrets_file.has_option('secrets', key):
|
if secrets_file.has_option('secrets', key):
|
||||||
return secrets_file.get('secrets', key)
|
return secrets_file.get('secrets', key)
|
||||||
return None
|
return None
|
||||||
|
@ -72,7 +71,7 @@ def generate_secrets(development=False):
|
||||||
lines = [u'[secrets]\n']
|
lines = [u'[secrets]\n']
|
||||||
|
|
||||||
def config_line(var, value):
|
def config_line(var, value):
|
||||||
# type: (text_type, text_type) -> text_type
|
# type: (Text, Text) -> Text
|
||||||
return "%s = %s\n" % (var, value)
|
return "%s = %s\n" % (var, value)
|
||||||
|
|
||||||
old_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME)
|
old_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME)
|
||||||
|
|
|
@ -8,8 +8,8 @@ import json
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import hashlib
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from six import unichr, text_type
|
from six import unichr
|
||||||
from typing import Union
|
from typing import Union, Text
|
||||||
from os.path import dirname
|
from os.path import dirname
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class MissingGlyphError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def color_font(name, code_point, code_point_to_fname_map):
|
def color_font(name, code_point, code_point_to_fname_map):
|
||||||
# type: (str, str, Dict[int, Union[text_type, bytes]]) -> None
|
# type: (str, str, Dict[int, Union[Text, bytes]]) -> None
|
||||||
glyph_name = code_point_to_fname_map[int(code_point, 16)]
|
glyph_name = code_point_to_fname_map[int(code_point, 16)]
|
||||||
|
|
||||||
in_name = 'bitmaps/strike0/{}.png'.format(glyph_name)
|
in_name = 'bitmaps/strike0/{}.png'.format(glyph_name)
|
||||||
|
@ -75,11 +75,11 @@ def bw_font(name, code_point):
|
||||||
)
|
)
|
||||||
|
|
||||||
def code_point_to_file_name_map(ttx):
|
def code_point_to_file_name_map(ttx):
|
||||||
# type: (str) -> Dict[int, Union[text_type, bytes]]
|
# type: (str) -> Dict[int, Union[Text, bytes]]
|
||||||
"""Given the NotoColorEmoji.ttx file, parse it to generate a map from
|
"""Given the NotoColorEmoji.ttx file, parse it to generate a map from
|
||||||
codepoint to filename (a la glyph0****.png)
|
codepoint to filename (a la glyph0****.png)
|
||||||
"""
|
"""
|
||||||
result = {} # type: Dict[int, Union[text_type, bytes]]
|
result = {} # type: Dict[int, Union[Text, bytes]]
|
||||||
xml = ET.parse(ttx)
|
xml = ET.parse(ttx)
|
||||||
for elem in xml.find("*cmap_format_12"):
|
for elem in xml.find("*cmap_format_12"):
|
||||||
code_point = int(elem.attrib["code"], 16)
|
code_point = int(elem.attrib["code"], 16)
|
||||||
|
|
|
@ -4,8 +4,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import ujson
|
import ujson
|
||||||
|
|
||||||
from six import text_type
|
from typing import Any, Dict, List, Text
|
||||||
from typing import Any, Dict, List
|
|
||||||
|
|
||||||
from django.core.management.commands import compilemessages
|
from django.core.management.commands import compilemessages
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -20,12 +19,12 @@ class Command(compilemessages.Command):
|
||||||
self.extract_language_options()
|
self.extract_language_options()
|
||||||
|
|
||||||
def get_po_filename(self, locale_path, locale):
|
def get_po_filename(self, locale_path, locale):
|
||||||
# type: (text_type, text_type) -> text_type
|
# type: (Text, Text) -> Text
|
||||||
po_template = '{}/{}/LC_MESSAGES/django.po'
|
po_template = '{}/{}/LC_MESSAGES/django.po'
|
||||||
return po_template.format(locale_path, locale)
|
return po_template.format(locale_path, locale)
|
||||||
|
|
||||||
def get_json_filename(self, locale_path, locale):
|
def get_json_filename(self, locale_path, locale):
|
||||||
# type: (text_type, text_type) -> text_type
|
# type: (Text, Text) -> Text
|
||||||
return "{}/{}/translations.json".format(locale_path, locale)
|
return "{}/{}/translations.json".format(locale_path, locale)
|
||||||
|
|
||||||
def extract_language_options(self):
|
def extract_language_options(self):
|
||||||
|
@ -81,7 +80,7 @@ class Command(compilemessages.Command):
|
||||||
ujson.dump(data, writer, indent=2)
|
ujson.dump(data, writer, indent=2)
|
||||||
|
|
||||||
def get_translation_percentage(self, locale_path, locale):
|
def get_translation_percentage(self, locale_path, locale):
|
||||||
# type: (text_type, text_type) -> int
|
# type: (Text, Text) -> int
|
||||||
|
|
||||||
# backend stats
|
# backend stats
|
||||||
po = polib.pofile(self.get_po_filename(locale_path, locale))
|
po = polib.pofile(self.get_po_filename(locale_path, locale))
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from six import text_type
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from typing import Any, Iterable, Tuple
|
from typing import Any, Iterable, Tuple, Text
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
@ -12,12 +12,11 @@ from zerver.lib.bulk_create import bulk_create_users
|
||||||
from zerver.lib.actions import set_default_streams, do_create_realm
|
from zerver.lib.actions import set_default_streams, do_create_realm
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from six import text_type
|
|
||||||
|
|
||||||
settings.TORNADO_SERVER = None
|
settings.TORNADO_SERVER = None
|
||||||
|
|
||||||
def create_users(name_list, bot_type=None):
|
def create_users(name_list, bot_type=None):
|
||||||
# type: (Iterable[Tuple[text_type, text_type]], int) -> None
|
# type: (Iterable[Tuple[Text, Text]], int) -> None
|
||||||
realms = {}
|
realms = {}
|
||||||
for realm in Realm.objects.all():
|
for realm in Realm.objects.all():
|
||||||
realms[realm.domain] = realm
|
realms[realm.domain] = realm
|
||||||
|
|
|
@ -30,14 +30,13 @@ http://stackoverflow.com/questions/2090717/getting-translation-strings-for-jinja
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from typing import Any, Dict, Iterable, Optional, Mapping, Set, Tuple
|
from typing import Any, Dict, Iterable, Optional, Mapping, Set, Tuple, Text
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
from six import text_type
|
|
||||||
from six.moves import filter
|
from six.moves import filter
|
||||||
from six.moves import map
|
from six.moves import map
|
||||||
from six.moves import zip
|
from six.moves import zip
|
||||||
|
@ -65,7 +64,7 @@ multiline_js_comment = re.compile("/\*.*?\*/", re.DOTALL)
|
||||||
singleline_js_comment = re.compile("//.*?\n")
|
singleline_js_comment = re.compile("//.*?\n")
|
||||||
|
|
||||||
def strip_whitespaces(src):
|
def strip_whitespaces(src):
|
||||||
# type: (text_type) -> text_type
|
# type: (Text) -> Text
|
||||||
src = strip_whitespace_left.sub(u'\\1', src)
|
src = strip_whitespace_left.sub(u'\\1', src)
|
||||||
src = strip_whitespace_right.sub(u'\\1', src)
|
src = strip_whitespace_right.sub(u'\\1', src)
|
||||||
return src
|
return src
|
||||||
|
@ -120,7 +119,7 @@ class Command(makemessages.Command):
|
||||||
trans_real.constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?')).*\)""")
|
trans_real.constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?')).*\)""")
|
||||||
|
|
||||||
def my_templatize(src, origin=None):
|
def my_templatize(src, origin=None):
|
||||||
# type: (text_type, Optional[text_type]) -> text_type
|
# type: (Text, Optional[Text]) -> Text
|
||||||
new_src = strip_whitespaces(src)
|
new_src = strip_whitespaces(src)
|
||||||
return old_templatize(new_src, origin)
|
return old_templatize(new_src, origin)
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,14 @@ from zilencer.models import Deployment
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
from six import text_type
|
|
||||||
from six.moves import range
|
from six.moves import range
|
||||||
from typing import Any, Callable, Dict, List, Iterable, Mapping, Sequence, Set, Tuple
|
from typing import Any, Callable, Dict, List, Iterable, Mapping, Sequence, Set, Tuple, Text
|
||||||
|
|
||||||
settings.TORNADO_SERVER = None
|
settings.TORNADO_SERVER = None
|
||||||
|
|
||||||
def create_users(realms, name_list, bot_type=None):
|
def create_users(realms, name_list, bot_type=None):
|
||||||
# type: (Mapping[text_type, Realm], Iterable[Tuple[text_type, text_type]], int) -> None
|
# type: (Mapping[Text, Realm], Iterable[Tuple[Text, Text]], int) -> None
|
||||||
user_set = set() # type: Set[Tuple[text_type, text_type, text_type, bool]]
|
user_set = set() # type: Set[Tuple[Text, Text, Text, bool]]
|
||||||
for full_name, email in name_list:
|
for full_name, email in name_list:
|
||||||
short_name = email_to_username(email)
|
short_name = email_to_username(email)
|
||||||
user_set.add((email, full_name, short_name, True))
|
user_set.add((email, full_name, short_name, True))
|
||||||
|
@ -35,8 +34,8 @@ def create_users(realms, name_list, bot_type=None):
|
||||||
bulk_create_users(realms, user_set, bot_type=bot_type, tos_version=tos_version)
|
bulk_create_users(realms, user_set, bot_type=bot_type, tos_version=tos_version)
|
||||||
|
|
||||||
def create_streams(realms, realm, stream_list):
|
def create_streams(realms, realm, stream_list):
|
||||||
# type: (Mapping[text_type, Realm], Realm, Iterable[text_type]) -> None
|
# type: (Mapping[Text, Realm], Realm, Iterable[Text]) -> None
|
||||||
stream_set = set() # type: Set[Tuple[text_type, text_type]]
|
stream_set = set() # type: Set[Tuple[Text, Text]]
|
||||||
for stream_name in stream_list:
|
for stream_name in stream_list:
|
||||||
stream_set.add((realm.domain, stream_name))
|
stream_set.add((realm.domain, stream_name))
|
||||||
bulk_create_streams(realms, stream_set)
|
bulk_create_streams(realms, stream_set)
|
||||||
|
@ -127,7 +126,7 @@ class Command(BaseCommand):
|
||||||
string_id="mit", name="MIT", restricted_to_domain=True,
|
string_id="mit", name="MIT", restricted_to_domain=True,
|
||||||
invite_required=False, org_type=Realm.CORPORATE, domain="mit.edu")
|
invite_required=False, org_type=Realm.CORPORATE, domain="mit.edu")
|
||||||
RealmAlias.objects.create(realm=mit_realm, domain="mit.edu")
|
RealmAlias.objects.create(realm=mit_realm, domain="mit.edu")
|
||||||
realms = {} # type: Dict[text_type, Realm]
|
realms = {} # type: Dict[Text, Realm]
|
||||||
for realm in Realm.objects.all():
|
for realm in Realm.objects.all():
|
||||||
realms[realm.domain] = realm
|
realms[realm.domain] = realm
|
||||||
|
|
||||||
|
@ -363,7 +362,7 @@ def send_messages(data):
|
||||||
# Pick a random subscriber to the stream
|
# Pick a random subscriber to the stream
|
||||||
message.sender = random.choice(Subscription.objects.filter(
|
message.sender = random.choice(Subscription.objects.filter(
|
||||||
recipient=message.recipient)).user_profile
|
recipient=message.recipient)).user_profile
|
||||||
message.subject = stream.name + text_type(random.randint(1, 3))
|
message.subject = stream.name + Text(random.randint(1, 3))
|
||||||
saved_data['subject'] = message.subject
|
saved_data['subject'] = message.subject
|
||||||
|
|
||||||
message.pub_date = now()
|
message.pub_date = now()
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Manager
|
from django.db.models import Manager
|
||||||
from six import text_type
|
from typing import Text
|
||||||
|
|
||||||
import zerver.models
|
import zerver.models
|
||||||
|
|
||||||
def get_deployment_by_domain(domain):
|
def get_deployment_by_domain(domain):
|
||||||
# type: (text_type) -> Deployment
|
# type: (Text) -> Deployment
|
||||||
return Deployment.objects.get(realms__domain=domain)
|
return Deployment.objects.get(realms__domain=domain)
|
||||||
|
|
||||||
class Deployment(models.Model):
|
class Deployment(models.Model):
|
||||||
|
@ -15,19 +15,19 @@ class Deployment(models.Model):
|
||||||
|
|
||||||
# TODO: This should really become the public portion of a keypair, and
|
# TODO: This should really become the public portion of a keypair, and
|
||||||
# it should be settable only with an initial bearer "activation key"
|
# it should be settable only with an initial bearer "activation key"
|
||||||
api_key = models.CharField(max_length=32, null=True) # type: text_type
|
api_key = models.CharField(max_length=32, null=True) # type: Text
|
||||||
|
|
||||||
base_api_url = models.CharField(max_length=128) # type: text_type
|
base_api_url = models.CharField(max_length=128) # type: Text
|
||||||
base_site_url = models.CharField(max_length=128) # type: text_type
|
base_site_url = models.CharField(max_length=128) # type: Text
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def endpoints(self):
|
def endpoints(self):
|
||||||
# type: () -> Dict[str, text_type]
|
# type: () -> Dict[str, Text]
|
||||||
return {'base_api_url': self.base_api_url, 'base_site_url': self.base_site_url}
|
return {'base_api_url': self.base_api_url, 'base_site_url': self.base_site_url}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
# type: () -> text_type
|
# type: () -> Text
|
||||||
|
|
||||||
# TODO: This only does the right thing for prod because prod authenticates to
|
# TODO: This only does the right thing for prod because prod authenticates to
|
||||||
# staging with the zulip.com deployment key, while staging is technically the
|
# staging with the zulip.com deployment key, while staging is technically the
|
||||||
|
|
|
@ -19,13 +19,12 @@ from .error_notify import notify_server_error, notify_browser_error
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from six import text_type
|
from typing import Dict, Optional, Any, Text
|
||||||
from typing import Dict, Optional, Any
|
|
||||||
|
|
||||||
client = get_redis_client()
|
client = get_redis_client()
|
||||||
|
|
||||||
def has_enough_time_expired_since_last_message(sender_email, min_delay):
|
def has_enough_time_expired_since_last_message(sender_email, min_delay):
|
||||||
# type: (text_type, float) -> bool
|
# type: (Text, float) -> bool
|
||||||
# This function returns a boolean, but it also has the side effect
|
# This function returns a boolean, but it also has the side effect
|
||||||
# of noting that a new message was received.
|
# of noting that a new message was received.
|
||||||
key = 'zilencer:feedback:%s' % (sender_email,)
|
key = 'zilencer:feedback:%s' % (sender_email,)
|
||||||
|
@ -48,7 +47,7 @@ def get_ticket_number():
|
||||||
|
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def submit_feedback(request, deployment, message=REQ(validator=check_dict([]))):
|
def submit_feedback(request, deployment, message=REQ(validator=check_dict([]))):
|
||||||
# type: (HttpRequest, Deployment, Dict[str, text_type]) -> HttpResponse
|
# type: (HttpRequest, Deployment, Dict[str, Text]) -> HttpResponse
|
||||||
domainish = message["sender_domain"]
|
domainish = message["sender_domain"]
|
||||||
if get_realm_by_string_id("zulip") not in deployment.realms.all():
|
if get_realm_by_string_id("zulip") not in deployment.realms.all():
|
||||||
domainish += u" via " + deployment.name
|
domainish += u" via " + deployment.name
|
||||||
|
@ -84,11 +83,11 @@ def submit_feedback(request, deployment, message=REQ(validator=check_dict([]))):
|
||||||
|
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def report_error(request, deployment, type=REQ(), report=REQ(validator=check_dict([]))):
|
def report_error(request, deployment, type=REQ(), report=REQ(validator=check_dict([]))):
|
||||||
# type: (HttpRequest, Deployment, text_type, Dict[str, Any]) -> HttpResponse
|
# type: (HttpRequest, Deployment, Text, Dict[str, Any]) -> HttpResponse
|
||||||
return do_report_error(deployment.name, type, report)
|
return do_report_error(deployment.name, type, report)
|
||||||
|
|
||||||
def do_report_error(deployment_name, type, report):
|
def do_report_error(deployment_name, type, report):
|
||||||
# type: (text_type, text_type, Dict[str, Any]) -> HttpResponse
|
# type: (Text, Text, 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)
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Set, Tuple, Optional
|
from typing import Any, Set, Tuple, Optional, Text
|
||||||
from six import text_type
|
|
||||||
|
|
||||||
from django.contrib.auth.backends import RemoteUserBackend
|
from django.contrib.auth.backends import RemoteUserBackend
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -25,7 +24,7 @@ from django.contrib.auth import authenticate
|
||||||
from zerver.lib.utils import check_subdomain, get_subdomain
|
from zerver.lib.utils import check_subdomain, get_subdomain
|
||||||
|
|
||||||
def pad_method_dict(method_dict):
|
def pad_method_dict(method_dict):
|
||||||
# type: (Dict[text_type, bool]) -> Dict[text_type, bool]
|
# type: (Dict[Text, bool]) -> Dict[Text, bool]
|
||||||
"""Pads an authentication methods dict to contain all auth backends
|
"""Pads an authentication methods dict to contain all auth backends
|
||||||
supported by the software, regardless of whether they are
|
supported by the software, regardless of whether they are
|
||||||
configured on this server"""
|
configured on this server"""
|
||||||
|
@ -35,7 +34,7 @@ def pad_method_dict(method_dict):
|
||||||
return method_dict
|
return method_dict
|
||||||
|
|
||||||
def auth_enabled_helper(backends_to_check, realm):
|
def auth_enabled_helper(backends_to_check, realm):
|
||||||
# type: (List[text_type], Optional[Realm]) -> bool
|
# type: (List[Text], Optional[Realm]) -> bool
|
||||||
if realm is not None:
|
if realm is not None:
|
||||||
enabled_method_dict = realm.authentication_methods_dict()
|
enabled_method_dict = realm.authentication_methods_dict()
|
||||||
pad_method_dict(enabled_method_dict)
|
pad_method_dict(enabled_method_dict)
|
||||||
|
@ -74,7 +73,7 @@ def github_auth_enabled(realm=None):
|
||||||
return auth_enabled_helper([u'GitHub'], realm)
|
return auth_enabled_helper([u'GitHub'], realm)
|
||||||
|
|
||||||
def common_get_active_user_by_email(email, return_data=None):
|
def common_get_active_user_by_email(email, return_data=None):
|
||||||
# type: (text_type, Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
# type: (Text, Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
||||||
try:
|
try:
|
||||||
user_profile = get_user_profile_by_email(email)
|
user_profile = get_user_profile_by_email(email)
|
||||||
except UserProfile.DoesNotExist:
|
except UserProfile.DoesNotExist:
|
||||||
|
@ -99,14 +98,14 @@ class ZulipAuthMixin(object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class SocialAuthMixin(ZulipAuthMixin):
|
class SocialAuthMixin(ZulipAuthMixin):
|
||||||
auth_backend_name = None # type: text_type
|
auth_backend_name = None # type: Text
|
||||||
|
|
||||||
def get_email_address(self, *args, **kwargs):
|
def get_email_address(self, *args, **kwargs):
|
||||||
# type: (*Any, **Any) -> text_type
|
# type: (*Any, **Any) -> Text
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_full_name(self, *args, **kwargs):
|
def get_full_name(self, *args, **kwargs):
|
||||||
# type: (*Any, **Any) -> text_type
|
# type: (*Any, **Any) -> Text
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def authenticate(self, *args, **kwargs):
|
def authenticate(self, *args, **kwargs):
|
||||||
|
@ -172,7 +171,7 @@ class ZulipDummyBackend(ZulipAuthMixin):
|
||||||
|
|
||||||
def authenticate(self, username=None, realm_subdomain=None, use_dummy_backend=False,
|
def authenticate(self, username=None, realm_subdomain=None, use_dummy_backend=False,
|
||||||
return_data=None):
|
return_data=None):
|
||||||
# type: (Optional[text_type], Optional[text_type], bool, Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
# type: (Optional[Text], Optional[Text], bool, Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
||||||
if use_dummy_backend:
|
if use_dummy_backend:
|
||||||
user_profile = common_get_active_user_by_email(username)
|
user_profile = common_get_active_user_by_email(username)
|
||||||
if user_profile is None:
|
if user_profile is None:
|
||||||
|
@ -192,7 +191,7 @@ class EmailAuthBackend(ZulipAuthMixin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def authenticate(self, username=None, password=None, realm_subdomain=None, return_data=None):
|
def authenticate(self, username=None, password=None, realm_subdomain=None, return_data=None):
|
||||||
# type: (Optional[text_type], Optional[str], Optional[text_type], Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
# type: (Optional[Text], Optional[str], Optional[Text], Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
||||||
""" Authenticate a user based on email address as the user name. """
|
""" Authenticate a user based on email address as the user name. """
|
||||||
if username is None or password is None:
|
if username is None or password is None:
|
||||||
# Return immediately. Otherwise we will look for a SQL row with
|
# Return immediately. Otherwise we will look for a SQL row with
|
||||||
|
@ -231,7 +230,7 @@ class GoogleMobileOauth2Backend(ZulipAuthMixin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def authenticate(self, google_oauth2_token=None, realm_subdomain=None, return_data={}):
|
def authenticate(self, google_oauth2_token=None, realm_subdomain=None, return_data={}):
|
||||||
# type: (Optional[str], Optional[text_type], Dict[str, Any]) -> Optional[UserProfile]
|
# type: (Optional[str], Optional[Text], Dict[str, Any]) -> Optional[UserProfile]
|
||||||
try:
|
try:
|
||||||
token_payload = googleapiclient.verify_id_token(google_oauth2_token, settings.GOOGLE_CLIENT_ID)
|
token_payload = googleapiclient.verify_id_token(google_oauth2_token, settings.GOOGLE_CLIENT_ID)
|
||||||
except AppIdentityError:
|
except AppIdentityError:
|
||||||
|
@ -262,7 +261,7 @@ class ZulipRemoteUserBackend(RemoteUserBackend):
|
||||||
create_unknown_user = False
|
create_unknown_user = False
|
||||||
|
|
||||||
def authenticate(self, remote_user, realm_subdomain=None):
|
def authenticate(self, remote_user, realm_subdomain=None):
|
||||||
# type: (str, Optional[text_type]) -> Optional[UserProfile]
|
# type: (str, Optional[Text]) -> Optional[UserProfile]
|
||||||
if not remote_user:
|
if not remote_user:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -304,7 +303,7 @@ class ZulipLDAPAuthBackendBase(ZulipAuthMixin, LDAPBackend):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
def django_to_ldap_username(self, username):
|
def django_to_ldap_username(self, username):
|
||||||
# type: (text_type) -> text_type
|
# type: (Text) -> Text
|
||||||
if settings.LDAP_APPEND_DOMAIN:
|
if settings.LDAP_APPEND_DOMAIN:
|
||||||
if not username.endswith("@" + settings.LDAP_APPEND_DOMAIN):
|
if not username.endswith("@" + settings.LDAP_APPEND_DOMAIN):
|
||||||
raise ZulipLDAPException("Username does not match LDAP domain.")
|
raise ZulipLDAPException("Username does not match LDAP domain.")
|
||||||
|
@ -319,7 +318,7 @@ class ZulipLDAPAuthBackendBase(ZulipAuthMixin, LDAPBackend):
|
||||||
|
|
||||||
class ZulipLDAPAuthBackend(ZulipLDAPAuthBackendBase):
|
class ZulipLDAPAuthBackend(ZulipLDAPAuthBackendBase):
|
||||||
def authenticate(self, username, password, realm_subdomain=None, return_data=None):
|
def authenticate(self, username, password, realm_subdomain=None, return_data=None):
|
||||||
# type: (text_type, str, Optional[text_type], Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
# type: (Text, str, Optional[Text], Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
||||||
try:
|
try:
|
||||||
username = self.django_to_ldap_username(username)
|
username = self.django_to_ldap_username(username)
|
||||||
user_profile = ZulipLDAPAuthBackendBase.authenticate(self, username, password)
|
user_profile = ZulipLDAPAuthBackendBase.authenticate(self, username, password)
|
||||||
|
@ -360,14 +359,14 @@ class ZulipLDAPAuthBackend(ZulipLDAPAuthBackendBase):
|
||||||
# Just like ZulipLDAPAuthBackend, but doesn't let you log in.
|
# Just like ZulipLDAPAuthBackend, but doesn't let you log in.
|
||||||
class ZulipLDAPUserPopulator(ZulipLDAPAuthBackendBase):
|
class ZulipLDAPUserPopulator(ZulipLDAPAuthBackendBase):
|
||||||
def authenticate(self, username, password, realm_subdomain=None):
|
def authenticate(self, username, password, realm_subdomain=None):
|
||||||
# type: (text_type, str, Optional[text_type]) -> None
|
# type: (Text, str, Optional[Text]) -> None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class DevAuthBackend(ZulipAuthMixin):
|
class DevAuthBackend(ZulipAuthMixin):
|
||||||
# Allow logging in as any user without a password.
|
# Allow logging in as any user without a password.
|
||||||
# This is used for convenience when developing Zulip.
|
# This is used for convenience when developing Zulip.
|
||||||
def authenticate(self, username, realm_subdomain=None, return_data=None):
|
def authenticate(self, username, realm_subdomain=None, return_data=None):
|
||||||
# type: (text_type, Optional[text_type], Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
# type: (Text, Optional[Text], Optional[Dict[str, Any]]) -> Optional[UserProfile]
|
||||||
user_profile = common_get_active_user_by_email(username, return_data=return_data)
|
user_profile = common_get_active_user_by_email(username, return_data=return_data)
|
||||||
if user_profile is None:
|
if user_profile is None:
|
||||||
return None
|
return None
|
||||||
|
@ -379,14 +378,14 @@ class GitHubAuthBackend(SocialAuthMixin, GithubOAuth2):
|
||||||
auth_backend_name = u"GitHub"
|
auth_backend_name = u"GitHub"
|
||||||
|
|
||||||
def get_email_address(self, *args, **kwargs):
|
def get_email_address(self, *args, **kwargs):
|
||||||
# type: (*Any, **Any) -> Optional[text_type]
|
# type: (*Any, **Any) -> Optional[Text]
|
||||||
try:
|
try:
|
||||||
return kwargs['response']['email']
|
return kwargs['response']['email']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_full_name(self, *args, **kwargs):
|
def get_full_name(self, *args, **kwargs):
|
||||||
# type: (*Any, **Any) -> text_type
|
# type: (*Any, **Any) -> Text
|
||||||
try:
|
try:
|
||||||
return kwargs['response']['name']
|
return kwargs['response']['name']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -432,4 +431,4 @@ AUTH_BACKEND_NAME_MAP = {
|
||||||
u'Google': GoogleMobileOauth2Backend,
|
u'Google': GoogleMobileOauth2Backend,
|
||||||
u'LDAP': ZulipLDAPAuthBackend,
|
u'LDAP': ZulipLDAPAuthBackend,
|
||||||
u'RemoteUser': ZulipRemoteUserBackend,
|
u'RemoteUser': ZulipRemoteUserBackend,
|
||||||
} # type: Dict[text_type, Any]
|
} # type: Dict[Text, Any]
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union, Text
|
||||||
from six import text_type
|
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -60,7 +59,7 @@ class Template(django_jinja2.Template):
|
||||||
super(Template, self).__init__(template, *args, **kwargs)
|
super(Template, self).__init__(template, *args, **kwargs)
|
||||||
|
|
||||||
def render(self, context=None, request=None):
|
def render(self, context=None, request=None):
|
||||||
# type: (Optional[Union[Dict[str, Any], Context]], Optional[HttpRequest]) -> text_type
|
# type: (Optional[Union[Dict[str, Any], Context]], Optional[HttpRequest]) -> Text
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import # Python 2 only
|
from __future__ import absolute_import # Python 2 only
|
||||||
|
|
||||||
from six import text_type
|
from typing import Text
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template import TemplateSyntaxError
|
from django.template import TemplateSyntaxError
|
||||||
|
@ -12,7 +12,7 @@ from zerver.templatetags.minified_js import MinifiedJSNode
|
||||||
|
|
||||||
|
|
||||||
def minified_js(sourcefile):
|
def minified_js(sourcefile):
|
||||||
# type: (str) -> text_type
|
# type: (str) -> Text
|
||||||
if sourcefile not in settings.JS_SPECS:
|
if sourcefile not in settings.JS_SPECS:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
"Invalid argument: no JS file %s".format(sourcefile))
|
"Invalid argument: no JS file %s".format(sourcefile))
|
||||||
|
|
Loading…
Reference in New Issue