python: Modernize legacy Python 2 syntax with pyupgrade.

Generated by `pyupgrade --py3-plus --keep-percent-format` on all our
Python code except `zthumbor` and `zulip-ec2-configure-interfaces`,
followed by manual indentation fixes.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
This commit is contained in:
Anders Kaseorg 2020-04-09 12:51:58 -07:00 committed by Tim Abbott
parent fff2d3958a
commit c734bbd95d
567 changed files with 1877 additions and 2564 deletions

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -90,22 +89,22 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='usercount', name='usercount',
unique_together=set([('user', 'property', 'end_time', 'interval')]), unique_together={('user', 'property', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='streamcount', name='streamcount',
unique_together=set([('stream', 'property', 'end_time', 'interval')]), unique_together={('stream', 'property', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='realmcount', name='realmcount',
unique_together=set([('realm', 'property', 'end_time', 'interval')]), unique_together={('realm', 'property', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='installationcount', name='installationcount',
unique_together=set([('property', 'end_time', 'interval')]), unique_together={('property', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='huddlecount', name='huddlecount',
unique_together=set([('huddle', 'property', 'end_time', 'interval')]), unique_together={('huddle', 'property', 'end_time', 'interval')},
), ),
] ]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations from django.db import migrations
@ -11,7 +10,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='huddlecount', name='huddlecount',
unique_together=set([]), unique_together=set(),
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='huddlecount', model_name='huddlecount',

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations from django.db import migrations
@ -11,18 +10,18 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='installationcount', name='installationcount',
unique_together=set([('property', 'subgroup', 'end_time', 'interval')]), unique_together={('property', 'subgroup', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='realmcount', name='realmcount',
unique_together=set([('realm', 'property', 'subgroup', 'end_time', 'interval')]), unique_together={('realm', 'property', 'subgroup', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='streamcount', name='streamcount',
unique_together=set([('stream', 'property', 'subgroup', 'end_time', 'interval')]), unique_together={('stream', 'property', 'subgroup', 'end_time', 'interval')},
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='usercount', name='usercount',
unique_together=set([('user', 'property', 'subgroup', 'end_time', 'interval')]), unique_together={('user', 'property', 'subgroup', 'end_time', 'interval')},
), ),
] ]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2017-01-16 20:50 # Generated by Django 1.10.4 on 2017-01-16 20:50
from django.db import migrations from django.db import migrations
@ -12,7 +11,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='installationcount', name='installationcount',
unique_together=set([('property', 'subgroup', 'end_time')]), unique_together={('property', 'subgroup', 'end_time')},
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='installationcount', model_name='installationcount',
@ -20,7 +19,7 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='realmcount', name='realmcount',
unique_together=set([('realm', 'property', 'subgroup', 'end_time')]), unique_together={('realm', 'property', 'subgroup', 'end_time')},
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='realmcount', model_name='realmcount',
@ -28,7 +27,7 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='streamcount', name='streamcount',
unique_together=set([('stream', 'property', 'subgroup', 'end_time')]), unique_together={('stream', 'property', 'subgroup', 'end_time')},
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='streamcount', model_name='streamcount',
@ -36,7 +35,7 @@ class Migration(migrations.Migration):
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='usercount', name='usercount',
unique_together=set([('user', 'property', 'subgroup', 'end_time')]), unique_together={('user', 'property', 'subgroup', 'end_time')},
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='usercount', model_name='usercount',

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-01 22:28 # Generated by Django 1.10.5 on 2017-02-01 22:28
from django.db import migrations from django.db import migrations
@ -13,14 +12,14 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterIndexTogether( migrations.AlterIndexTogether(
name='realmcount', name='realmcount',
index_together=set([('property', 'end_time')]), index_together={('property', 'end_time')},
), ),
migrations.AlterIndexTogether( migrations.AlterIndexTogether(
name='streamcount', name='streamcount',
index_together=set([('property', 'realm', 'end_time')]), index_together={('property', 'realm', 'end_time')},
), ),
migrations.AlterIndexTogether( migrations.AlterIndexTogether(
name='usercount', name='usercount',
index_together=set([('property', 'realm', 'end_time')]), index_together={('property', 'realm', 'end_time')},
), ),
] ]

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations from django.db import migrations
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps from django.db.migrations.state import StateApps

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations from django.db import migrations
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps from django.db.migrations.state import StateApps

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations from django.db import migrations
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps from django.db.migrations.state import StateApps

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-01-29 08:14 # Generated by Django 1.11.6 on 2018-01-29 08:14
from __future__ import unicode_literals
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-02-02 02:47 # Generated by Django 1.11.18 on 2019-02-02 02:47
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.26 on 2020-01-27 04:32 # Generated by Django 1.11.26 on 2020-01-27 04:32
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations from django.db import migrations
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps from django.db.migrations.state import StateApps

View File

@ -261,7 +261,7 @@ def get_chart_data(request: HttpRequest, user_profile: UserProfile, chart_name:
"analytics cron job running?" % (realm.string_id, start, end)) "analytics cron job running?" % (realm.string_id, start, end))
raise JsonableError(_("No analytics data available. Please contact your server administrator.")) raise JsonableError(_("No analytics data available. Please contact your server administrator."))
assert len(set([stat.frequency for stat in stats])) == 1 assert len({stat.frequency for stat in stats}) == 1
end_times = time_range(start, end, stats[0].frequency, min_length) end_times = time_range(start, end, stats[0].frequency, min_length)
data = {'end_times': end_times, 'frequency': stats[0].frequency} # type: Dict[str, Any] data = {'end_times': end_times, 'frequency': stats[0].frequency} # type: Dict[str, Any]

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com> # Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com>
# Permission is hereby granted, free of charge, to any person obtaining a # Permission is hereby granted, free of charge, to any person obtaining a

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import models, migrations from django.db import models, migrations
import django.db.models.deletion import django.db.models.deletion

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import models, migrations from django.db import models, migrations
import django.utils.timezone import django.utils.timezone

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2017-01-17 09:16 # Generated by Django 1.10.4 on 2017-01-17 09:16
from django.db import migrations from django.db import migrations

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-07-08 04:23 # Generated by Django 1.11.2 on 2017-07-08 04:23
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-30 00:13 # Generated by Django 1.11.6 on 2017-11-30 00:13
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2018-01-29 18:39 # Generated by Django 1.11.6 on 2018-01-29 18:39
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com> # Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com>
__revision__ = '$Id: models.py 28 2009-10-22 15:03:02Z jarek.zgoda $' __revision__ = '$Id: models.py 28 2009-10-22 15:03:02Z jarek.zgoda $'

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com> # Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com>
__revision__ = '$Id: settings.py 12 2008-11-23 19:38:52Z jarek.zgoda $' __revision__ = '$Id: settings.py 12 2008-11-23 19:38:52Z jarek.zgoda $'

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.14 on 2018-09-25 12:02 # Generated by Django 1.11.14 on 2018-09-25 12:02
from __future__ import unicode_literals
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-12 20:19 # Generated by Django 1.11.16 on 2018-12-12 20:19
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-22 21:05 # Generated by Django 1.11.16 on 2018-12-22 21:05
from __future__ import unicode_literals
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-01-19 05:01 # Generated by Django 1.11.18 on 2019-01-19 05:01
from __future__ import unicode_literals
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-01-28 13:04 # Generated by Django 1.11.18 on 2019-01-28 13:04
from __future__ import unicode_literals
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-01-29 01:46 # Generated by Django 1.11.18 on 2019-01-29 01:46
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-01-31 22:16 # Generated by Django 1.11.18 on 2019-01-31 22:16
from __future__ import unicode_literals
from django.db import migrations from django.db import migrations

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-11 00:45 # Generated by Django 1.11.20 on 2019-04-11 00:45
from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models

View File

@ -105,7 +105,7 @@ def read_stripe_fixture(decorated_function_name: str,
def _read_stripe_fixture(*args: Any, **kwargs: Any) -> Any: def _read_stripe_fixture(*args: Any, **kwargs: Any) -> Any:
mock = operator.attrgetter(mocked_function_name)(sys.modules[__name__]) mock = operator.attrgetter(mocked_function_name)(sys.modules[__name__])
fixture_path = stripe_fixture_path(decorated_function_name, mocked_function_name, mock.call_count) fixture_path = stripe_fixture_path(decorated_function_name, mocked_function_name, mock.call_count)
fixture = ujson.load(open(fixture_path, 'r')) fixture = ujson.load(open(fixture_path))
# Check for StripeError fixtures # Check for StripeError fixtures
if "json_body" in fixture: if "json_body" in fixture:
requestor = stripe.api_requestor.APIRequestor() requestor = stripe.api_requestor.APIRequestor()
@ -151,7 +151,7 @@ def normalize_fixture_data(decorated_function: CallableT,
normalized_values = {pattern: {} normalized_values = {pattern: {}
for pattern in pattern_translations.keys()} # type: Dict[str, Dict[str, str]] for pattern in pattern_translations.keys()} # type: Dict[str, Dict[str, str]]
for fixture_file in fixture_files_for_function(decorated_function): for fixture_file in fixture_files_for_function(decorated_function):
with open(fixture_file, "r") as f: with open(fixture_file) as f:
file_content = f.read() file_content = f.read()
for pattern, translation in pattern_translations.items(): for pattern, translation in pattern_translations.items():
for match in re.findall(pattern, file_content): for match in re.findall(pattern, file_content):
@ -214,7 +214,7 @@ def mock_stripe(tested_timestamp_fields: List[str]=[],
# A Kandra is a fictional character that can become anything. Used as a # A Kandra is a fictional character that can become anything. Used as a
# wildcard when testing for equality. # wildcard when testing for equality.
class Kandra(object): # nocoverage: TODO class Kandra: # nocoverage: TODO
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
return True return True
@ -288,7 +288,7 @@ class StripeTestCase(ZulipTestCase):
# Upgrade without talking to Stripe # Upgrade without talking to Stripe
def local_upgrade(self, *args: Any) -> None: def local_upgrade(self, *args: Any) -> None:
class StripeMock(object): class StripeMock:
def __init__(self, depth: int=1): def __init__(self, depth: int=1):
self.id = 'id' self.id = 'id'
self.created = '1000' self.created = '1000'
@ -1045,8 +1045,8 @@ class RequiresBillingAccessTest(ZulipTestCase):
# Make sure that we are testing all the JSON endpoints # Make sure that we are testing all the JSON endpoints
# Quite a hack, but probably fine for now # Quite a hack, but probably fine for now
string_with_all_endpoints = str(get_resolver('corporate.urls').reverse_dict) string_with_all_endpoints = str(get_resolver('corporate.urls').reverse_dict)
json_endpoints = set([word.strip("\"'()[],$") for word in string_with_all_endpoints.split() json_endpoints = {word.strip("\"'()[],$") for word in string_with_all_endpoints.split()
if 'json' in word]) if 'json' in word}
# No need to test upgrade endpoint as it only requires user to be logged in. # No need to test upgrade endpoint as it only requires user to be logged in.
json_endpoints.remove("json/billing/upgrade") json_endpoints.remove("json/billing/upgrade")

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# zulip-contributor-docs documentation build configuration file, created by # zulip-contributor-docs documentation build configuration file, created by
# sphinx-quickstart on Mon Aug 17 16:24:04 2015. # sphinx-quickstart on Mon Aug 17 16:24:04 2015.

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import (print_function)
import os import os
import sys import sys
import configparser import configparser

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.conf import settings from django.conf import settings
from django.db import migrations from django.db import migrations

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import connection, migrations from django.db import connection, migrations
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps from django.db.migrations.state import StateApps

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.conf import settings from django.conf import settings
from django.db import migrations from django.db import migrations

View File

@ -15,7 +15,7 @@ def nagios_from_file(results_file: str, max_time_diff: int=60 * 2) -> 'Tuple[int
This file is created by various nagios checking cron jobs such as This file is created by various nagios checking cron jobs such as
check-rabbitmq-queues and check-rabbitmq-consumers""" check-rabbitmq-queues and check-rabbitmq-consumers"""
with open(results_file, 'r') as f: with open(results_file) as f:
data = f.read().strip() data = f.read().strip()
pieces = data.split('|') pieces = data.split('|')

View File

@ -113,7 +113,7 @@ def get_zulips():
last_event_id = max(last_event_id, int(event['id'])) last_event_id = max(last_event_id, int(event['id']))
# If we get a heartbeat event, that means we've been hanging for # If we get a heartbeat event, that means we've been hanging for
# 40s, and we should bail. # 40s, and we should bail.
if 'heartbeat' in set(event['type'] for event in res['events']): if 'heartbeat' in {event['type'] for event in res['events']}:
report("CRITICAL", msg="Got heartbeat waiting for Zulip, which means get_events is hanging") report("CRITICAL", msg="Got heartbeat waiting for Zulip, which means get_events is hanging")
return [event['message'] for event in res['events']] return [event['message'] for event in res['events']]

View File

@ -24,9 +24,9 @@ if subprocess.check_output(['psql', '-v', 'ON_ERROR_STOP=1',
report('OK', 'this is not the primary') report('OK', 'this is not the primary')
try: try:
with open('/var/lib/nagios_state/last_postgres_backup', 'r') as f: with open('/var/lib/nagios_state/last_postgres_backup') as f:
last_backup = dateutil.parser.parse(f.read()) last_backup = dateutil.parser.parse(f.read())
except IOError: except OSError:
report('UNKNOWN', 'could not determine completion time of last Postgres backup') report('UNKNOWN', 'could not determine completion time of last Postgres backup')
if datetime.now(tz=pytz.utc) - last_backup > timedelta(hours=25): if datetime.now(tz=pytz.utc) - last_backup > timedelta(hours=25):

View File

@ -31,7 +31,7 @@ down_count = 0
for results_file_name in os.listdir(RESULTS_DIR): for results_file_name in os.listdir(RESULTS_DIR):
this_state = "OK" this_state = "OK"
results_file = os.path.join(RESULTS_DIR, results_file_name) results_file = os.path.join(RESULTS_DIR, results_file_name)
with open(results_file, 'r') as f: with open(results_file) as f:
data = f.read().strip() data = f.read().strip()
last_check = os.stat(results_file).st_mtime last_check = os.stat(results_file).st_mtime
time_since_last_check = time.time() - last_check time_since_last_check = time.time() - last_check

View File

@ -29,7 +29,7 @@ def report(state, data, last_check):
data)) data))
exit(states[state]) exit(states[state])
with open(RESULTS_FILE, 'r') as f: with open(RESULTS_FILE) as f:
data = f.read().strip() data = f.read().strip()
if data.split("\n")[-1].strip() == "0": if data.split("\n")[-1].strip() == "0":
state = "OK" state = "OK"

View File

@ -18,7 +18,7 @@ if ENV == "travis":
def get_caches_in_use(threshold_days): def get_caches_in_use(threshold_days):
# type: (int) -> Set[str] # type: (int) -> Set[str]
setups_to_check = set([ZULIP_PATH, ]) setups_to_check = {ZULIP_PATH}
caches_in_use = set() caches_in_use = set()
if ENV == "prod": if ENV == "prod":

View File

@ -25,7 +25,7 @@ if ENV == "travis":
def get_caches_in_use(threshold_days): def get_caches_in_use(threshold_days):
# type: (int) -> Set[str] # type: (int) -> Set[str]
setups_to_check = set([ZULIP_PATH, ]) setups_to_check = {ZULIP_PATH}
caches_in_use = set() caches_in_use = set()
if ENV == "prod": if ENV == "prod":

View File

@ -20,7 +20,7 @@ if ENV == "travis":
def get_caches_in_use(threshold_days): def get_caches_in_use(threshold_days):
# type: (int) -> Set[str] # type: (int) -> Set[str]
setups_to_check = set([ZULIP_PATH, ]) setups_to_check = {ZULIP_PATH}
caches_in_use = set() caches_in_use = set()
def add_current_venv_cache(venv_name: str) -> None: def add_current_venv_cache(venv_name: str) -> None:

View File

@ -39,7 +39,7 @@ def generate_sha1sum_node_modules(setup_dir=None, production=DEFAULT_PRODUCTION)
if os.path.exists(YARN_LOCK_FILE_PATH): if os.path.exists(YARN_LOCK_FILE_PATH):
# For backwards compatibility, we can't assume yarn.lock exists # For backwards compatibility, we can't assume yarn.lock exists
sha1sum.update(subprocess_text_output(['cat', YARN_LOCK_FILE_PATH]).encode('utf8')) sha1sum.update(subprocess_text_output(['cat', YARN_LOCK_FILE_PATH]).encode('utf8'))
with open(YARN_PACKAGE_JSON, "r") as f: with open(YARN_PACKAGE_JSON) as f:
yarn_version = json.load(f)['version'] yarn_version = json.load(f)['version']
sha1sum.update(yarn_version.encode("utf8")) sha1sum.update(yarn_version.encode("utf8"))
sha1sum.update(subprocess_text_output(['node', '--version']).encode('utf8')) sha1sum.update(subprocess_text_output(['node', '--version']).encode('utf8'))

View File

@ -171,7 +171,7 @@ def get_venv_packages(venv_path):
package index file. package index file.
""" """
with open(get_index_filename(venv_path)) as reader: with open(get_index_filename(venv_path)) as reader:
return set(p.strip() for p in reader.read().split('\n') if p.strip()) return {p.strip() for p in reader.read().split('\n') if p.strip()}
def try_to_copy_venv(venv_path, new_packages): def try_to_copy_venv(venv_path, new_packages):
# type: (str, Set[str]) -> bool # type: (str, Set[str]) -> bool
@ -281,7 +281,7 @@ def do_patch_activate_script(venv_path):
# venv_path should be what we want to have in VIRTUAL_ENV after patching # venv_path should be what we want to have in VIRTUAL_ENV after patching
script_path = os.path.join(venv_path, "bin", "activate") script_path = os.path.join(venv_path, "bin", "activate")
with open(script_path, 'r') as f: with open(script_path) as f:
lines = f.readlines() lines = f.readlines()
for i, line in enumerate(lines): for i, line in enumerate(lines):
if line.startswith('VIRTUAL_ENV='): if line.startswith('VIRTUAL_ENV='):

View File

@ -65,7 +65,7 @@ if not args.skip_puppet:
subprocess.check_call(["apt-get", "update"]) subprocess.check_call(["apt-get", "update"])
subprocess.check_call(["apt-get", "-y", "upgrade"]) subprocess.check_call(["apt-get", "-y", "upgrade"])
if not os.path.exists((os.path.join(deploy_path, "zproject/prod_settings.py"))): if not os.path.exists(os.path.join(deploy_path, "zproject/prod_settings.py")):
# This is normally done in unpack-zulip, but for upgrading from # This is normally done in unpack-zulip, but for upgrading from
# zulip<1.4.0, we need to do it. See discussion in commit 586b23637. # zulip<1.4.0, we need to do it. See discussion in commit 586b23637.
os.symlink("/etc/zulip/settings.py", os.symlink("/etc/zulip/settings.py",

View File

@ -289,7 +289,7 @@ def get_caches_to_be_purged(caches_dir, caches_in_use, threshold_days):
def purge_unused_caches(caches_dir, caches_in_use, cache_type, args): def purge_unused_caches(caches_dir, caches_in_use, cache_type, args):
# type: (str, Set[str], str, argparse.Namespace) -> None # type: (str, Set[str], str, argparse.Namespace) -> None
all_caches = set([os.path.join(caches_dir, cache) for cache in os.listdir(caches_dir)]) all_caches = {os.path.join(caches_dir, cache) for cache in os.listdir(caches_dir)}
caches_to_purge = get_caches_to_be_purged(caches_dir, caches_in_use, args.threshold_days) caches_to_purge = get_caches_to_be_purged(caches_dir, caches_in_use, args.threshold_days)
caches_to_keep = all_caches - caches_to_purge caches_to_keep = all_caches - caches_to_purge
@ -313,7 +313,7 @@ def generate_sha1sum_emoji(zulip_path):
# Take into account the version of `emoji-datasource-google` package # Take into account the version of `emoji-datasource-google` package
# while generating success stamp. # while generating success stamp.
PACKAGE_FILE_PATH = os.path.join(zulip_path, 'package.json') PACKAGE_FILE_PATH = os.path.join(zulip_path, 'package.json')
with open(PACKAGE_FILE_PATH, 'r') as fp: with open(PACKAGE_FILE_PATH) as fp:
parsed_package_file = json.load(fp) parsed_package_file = json.load(fp)
dependency_data = parsed_package_file['dependencies'] dependency_data = parsed_package_file['dependencies']
@ -366,7 +366,7 @@ def parse_os_release():
we avoid using it, as it is not available on RHEL-based platforms. we avoid using it, as it is not available on RHEL-based platforms.
""" """
distro_info = {} # type: Dict[str, str] distro_info = {} # type: Dict[str, str]
with open('/etc/os-release', 'r') as fp: with open('/etc/os-release') as fp:
for line in fp: for line in fp:
line = line.strip() line = line.strip()
if not line or line.startswith('#'): if not line or line.startswith('#'):

View File

@ -36,8 +36,8 @@ def parse_args():
def get_deployments_to_be_purged(recent_deployments): def get_deployments_to_be_purged(recent_deployments):
# type: (Set[str]) -> Set[str] # type: (Set[str]) -> Set[str]
all_deployments = set([os.path.join(DEPLOYMENTS_DIR, deployment) all_deployments = {os.path.join(DEPLOYMENTS_DIR, deployment)
for deployment in os.listdir(DEPLOYMENTS_DIR)]) for deployment in os.listdir(DEPLOYMENTS_DIR)}
deployments_to_purge = set() deployments_to_purge = set()
for deployment in all_deployments: for deployment in all_deployments:
# Deployments whose name is not in the format of a timestamp are # Deployments whose name is not in the format of a timestamp are

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Scrapy settings for documentation_crawler project # Scrapy settings for documentation_crawler project
# #
# For simplicity, this file contains only settings considered important or # For simplicity, this file contains only settings considered important or

View File

@ -41,8 +41,8 @@ def get_config():
def user_exists(username): def user_exists(username):
# type: (str) -> bool # type: (str) -> bool
print("Checking to see if GitHub user {0} exists...".format(username)) print("Checking to see if GitHub user {} exists...".format(username))
user_api_url = "https://api.github.com/users/{0}".format(username) user_api_url = "https://api.github.com/users/{}".format(username)
try: try:
response = urllib.request.urlopen(user_api_url) response = urllib.request.urlopen(user_api_url)
json.load(response) json.load(response)
@ -50,30 +50,30 @@ def user_exists(username):
return True return True
except urllib.error.HTTPError as err: except urllib.error.HTTPError as err:
print(err) print(err)
print("Does the github user {0} exist?".format(username)) print("Does the github user {} exist?".format(username))
sys.exit(1) sys.exit(1)
def get_keys(username): def get_keys(username):
# type: (str) -> List[Dict[str, Any]] # type: (str) -> List[Dict[str, Any]]
print("Checking to see that GitHub user has available public keys...") print("Checking to see that GitHub user has available public keys...")
apiurl_keys = "https://api.github.com/users/{0}/keys".format(username) apiurl_keys = "https://api.github.com/users/{}/keys".format(username)
try: try:
response = urllib.request.urlopen(apiurl_keys) response = urllib.request.urlopen(apiurl_keys)
userkeys = json.load(response) userkeys = json.load(response)
if not userkeys: if not userkeys:
print("No keys found. Has user {0} added ssh keys to their github account?".format(username)) print("No keys found. Has user {} added ssh keys to their github account?".format(username))
sys.exit(1) sys.exit(1)
print("...public keys found!") print("...public keys found!")
return userkeys return userkeys
except urllib.error.HTTPError as err: except urllib.error.HTTPError as err:
print(err) print(err)
print("Has user {0} added ssh keys to their github account?".format(username)) print("Has user {} added ssh keys to their github account?".format(username))
sys.exit(1) sys.exit(1)
def fork_exists(username): def fork_exists(username):
# type: (str) -> bool # type: (str) -> bool
print("Checking to see GitHub user has forked zulip/zulip...") print("Checking to see GitHub user has forked zulip/zulip...")
apiurl_fork = "https://api.github.com/repos/{0}/zulip".format(username) apiurl_fork = "https://api.github.com/repos/{}/zulip".format(username)
try: try:
response = urllib.request.urlopen(apiurl_fork) response = urllib.request.urlopen(apiurl_fork)
json.load(response) json.load(response)
@ -81,21 +81,21 @@ def fork_exists(username):
return True return True
except urllib.error.HTTPError as err: except urllib.error.HTTPError as err:
print(err) print(err)
print("Has user {0} forked zulip/zulip?".format(username)) print("Has user {} forked zulip/zulip?".format(username))
sys.exit(1) sys.exit(1)
def exit_if_droplet_exists(my_token: str, username: str, recreate: bool) -> None: def exit_if_droplet_exists(my_token: str, username: str, recreate: bool) -> None:
print("Checking to see if droplet for {0} already exists...".format(username)) print("Checking to see if droplet for {} already exists...".format(username))
manager = digitalocean.Manager(token=my_token) manager = digitalocean.Manager(token=my_token)
my_droplets = manager.get_all_droplets() my_droplets = manager.get_all_droplets()
for droplet in my_droplets: for droplet in my_droplets:
if droplet.name == "{0}.zulipdev.org".format(username): if droplet.name == "{}.zulipdev.org".format(username):
if not recreate: if not recreate:
print("Droplet for user {0} already exists. Pass --recreate if you " print("Droplet for user {} already exists. Pass --recreate if you "
"need to recreate the droplet.".format(username)) "need to recreate the droplet.".format(username))
sys.exit(1) sys.exit(1)
else: else:
print("Deleting existing droplet for {0}.".format(username)) print("Deleting existing droplet for {}.".format(username))
droplet.destroy() droplet.destroy()
return return
print("...No droplet found...proceeding.") print("...No droplet found...proceeding.")
@ -108,7 +108,7 @@ def set_user_data(username, userkeys):
# spaces here are important here - these need to be properly indented under # spaces here are important here - these need to be properly indented under
# ssh_authorized_keys: # ssh_authorized_keys:
for key in userkeys: for key in userkeys:
ssh_authorized_keys += "\n - {0}".format(key['key']) ssh_authorized_keys += "\n - {}".format(key['key'])
# print(ssh_authorized_keys) # print(ssh_authorized_keys)
setup_repo = """\ setup_repo = """\
@ -121,11 +121,11 @@ cd /home/zulipdev/{1} && git remote add origin https://github.com/{0}/{1}.git &&
#cloud-config #cloud-config
users: users:
- name: zulipdev - name: zulipdev
ssh_authorized_keys:{0} ssh_authorized_keys:{}
runcmd: runcmd:
- su -c '{1}' zulipdev - su -c '{}' zulipdev
- su -c 'git clean -f' zulipdev - su -c 'git clean -f' zulipdev
- su -c '{2}' zulipdev - su -c '{}' zulipdev
- su -c 'git clean -f' zulipdev - su -c 'git clean -f' zulipdev
- su -c 'git config --global core.editor nano' zulipdev - su -c 'git config --global core.editor nano' zulipdev
- su -c 'git config --global pull.rebase true' zulipdev - su -c 'git config --global pull.rebase true' zulipdev
@ -141,7 +141,7 @@ def create_droplet(my_token, template_id, username, tags, user_data):
# type: (str, str, str, List[str], str) -> str # type: (str, str, str, List[str], str) -> str
droplet = digitalocean.Droplet( droplet = digitalocean.Droplet(
token=my_token, token=my_token,
name='{0}.zulipdev.org'.format(username), name='{}.zulipdev.org'.format(username),
region='nyc3', region='nyc3',
image=template_id, image=template_id,
size_slug='2gb', size_slug='2gb',
@ -157,7 +157,7 @@ def create_droplet(my_token, template_id, username, tags, user_data):
actions = droplet.get_actions() actions = droplet.get_actions()
for action in actions: for action in actions:
action.load() action.load()
print("...[{0}]: {1}".format(action.type, action.status)) print("...[{}]: {}".format(action.type, action.status))
if action.type == 'create' and action.status == 'completed': if action.type == 'create' and action.status == 'completed':
incomplete = False incomplete = False
break break
@ -165,7 +165,7 @@ def create_droplet(my_token, template_id, username, tags, user_data):
time.sleep(15) time.sleep(15)
print("...droplet created!") print("...droplet created!")
droplet.load() droplet.load()
print("...ip address for new droplet is: {0}.".format(droplet.ip_address)) print("...ip address for new droplet is: {}.".format(droplet.ip_address))
return droplet.ip_address return droplet.ip_address
def delete_existing_records(records: List[digitalocean.Record], record_name: str) -> None: def delete_existing_records(records: List[digitalocean.Record], record_name: str) -> None:
@ -175,7 +175,7 @@ def delete_existing_records(records: List[digitalocean.Record], record_name: str
record.destroy() record.destroy()
count = count + 1 count = count + 1
if count: if count:
print("Deleted {0} existing A records for {1}.zulipdev.org.".format(count, record_name)) print("Deleted {} existing A records for {}.zulipdev.org.".format(count, record_name))
def create_dns_record(my_token, username, ip_address): def create_dns_record(my_token, username, ip_address):
# type: (str, str, str) -> None # type: (str, str, str) -> None
@ -187,9 +187,9 @@ def create_dns_record(my_token, username, ip_address):
wildcard_name = "*." + username wildcard_name = "*." + username
delete_existing_records(records, wildcard_name) delete_existing_records(records, wildcard_name)
print("Creating new A record for {0}.zulipdev.org that points to {1}.".format(username, ip_address)) print("Creating new A record for {}.zulipdev.org that points to {}.".format(username, ip_address))
domain.create_new_domain_record(type='A', name=username, data=ip_address) domain.create_new_domain_record(type='A', name=username, data=ip_address)
print("Creating new A record for *.{0}.zulipdev.org that points to {1}.".format(username, ip_address)) print("Creating new A record for *.{}.zulipdev.org that points to {}.".format(username, ip_address))
domain.create_new_domain_record(type='A', name=wildcard_name, data=ip_address) domain.create_new_domain_record(type='A', name=wildcard_name, data=ip_address)
def print_completion(username): def print_completion(username):
@ -231,7 +231,7 @@ if __name__ == '__main__':
# get command line arguments # get command line arguments
args = parser.parse_args() args = parser.parse_args()
print("Creating Zulip developer environment for GitHub user {0}...".format(args.username)) print("Creating Zulip developer environment for GitHub user {}...".format(args.username))
# get config details # get config details
config = get_config() config = get_config()

View File

@ -54,7 +54,7 @@ def write_to_disk(json_data: ContributorsJSON, out_file: str) -> None:
with open(out_file, 'w') as f: with open(out_file, 'w') as f:
try: try:
f.write("{}\n".format(json.dumps(json_data, indent=2, sort_keys=True))) f.write("{}\n".format(json.dumps(json_data, indent=2, sort_keys=True)))
except IOError as e: except OSError as e:
logger.warning(e) logger.warning(e)
sys.exit(1) sys.exit(1)
@ -126,7 +126,7 @@ def update_contributor_data_file() -> None:
# remove duplicate contributions count # remove duplicate contributions count
# find commits at the time of split and subtract from zulip-server # find commits at the time of split and subtract from zulip-server
with open(duplicate_commits_file, 'r') as f: with open(duplicate_commits_file) as f:
duplicate_commits = json.load(f) duplicate_commits = json.load(f)
for committer in duplicate_commits: for committer in duplicate_commits:
if committer in contribs_list and contribs_list[committer].get('server'): if committer in contribs_list and contribs_list[committer].get('server'):

View File

@ -15,7 +15,7 @@ def debug(obj):
def parse_file(fn): def parse_file(fn):
# type: (str) -> Dict[str, Any] # type: (str) -> Dict[str, Any]
with open(fn, 'r') as f: with open(fn) as f:
text = f.read() text = f.read()
tags = re.findall(r'{+\s*(.*?)\s*}+', text) tags = re.findall(r'{+\s*(.*?)\s*}+', text)
root = {} # type: Dict[str, Any] root = {} # type: Dict[str, Any]

View File

@ -1,6 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import absolute_import
from __future__ import print_function
import configparser import configparser
from hashlib import md5 from hashlib import md5

View File

@ -159,7 +159,7 @@ class TitleMatchRegexAllowException(LineRule):
regex = self.options['regex'].value regex = self.options['regex'].value
pattern = re.compile(regex, re.UNICODE) pattern = re.compile(regex, re.UNICODE)
if not pattern.search(title) and not title.startswith("Revert \""): if not pattern.search(title) and not title.startswith("Revert \""):
violation_msg = u"Title does not match regex ({0})".format(regex) violation_msg = "Title does not match regex ({})".format(regex)
return [RuleViolation(self.id, violation_msg, title)] return [RuleViolation(self.id, violation_msg, title)]
return [] return []

View File

@ -119,7 +119,7 @@ def make_dot_file(graph):
def test(): def test():
# type: () -> None # type: () -> None
graph = Graph(set([ graph = Graph({
('x', 'a'), ('x', 'a'),
('a', 'b'), ('a', 'b'),
('b', 'c'), ('b', 'c'),
@ -128,7 +128,7 @@ def test():
('d', 'e'), ('d', 'e'),
('e', 'f'), ('e', 'f'),
('e', 'g'), ('e', 'g'),
])) })
graph.remove_exterior_nodes() graph.remove_exterior_nodes()
s = make_dot_file(graph) s = make_dot_file(graph)

View File

@ -193,7 +193,7 @@ def build_id_dict(templates):
template_id_dict = defaultdict(list) # type: (Dict[str, List[str]]) template_id_dict = defaultdict(list) # type: (Dict[str, List[str]])
for fn in templates: for fn in templates:
with open(fn, 'r') as f: with open(fn) as f:
text = f.read() text = f.read()
list_tags = tokenize(text) list_tags = tokenize(text)

View File

@ -7,7 +7,7 @@ def show_all_branches(fns):
# type: (List[str]) -> None # type: (List[str]) -> None
for fn in fns: for fn in fns:
print(fn) print(fn)
with open(fn, 'r') as f: with open(fn) as f:
text = f.read() text = f.read()
branches = html_branches(text, fn=fn) branches = html_branches(text, fn=fn)
for branch in branches: for branch in branches:
@ -26,7 +26,7 @@ class Grepper:
all_branches = [] # type: List[HtmlTreeBranch] all_branches = [] # type: List[HtmlTreeBranch]
for fn in fns: for fn in fns:
with open(fn, 'r') as f: with open(fn) as f:
text = f.read() text = f.read()
branches = html_branches(text, fn=fn) branches = html_branches(text, fn=fn)
all_branches += branches all_branches += branches

View File

@ -193,7 +193,7 @@ def pretty_print_html(html, num_spaces=4):
def validate_indent_html(fn, fix): def validate_indent_html(fn, fix):
# type: (str, bool) -> int # type: (str, bool) -> int
with open(fn, 'r') as f: with open(fn) as f:
html = f.read() html = f.read()
phtml = pretty_print_html(html) phtml = pretty_print_html(html)
if not html.split('\n') == phtml.split('\n'): if not html.split('\n') == phtml.split('\n'):

View File

@ -41,7 +41,7 @@ def setup_shell_profile(shell_profile):
def write_command(command): def write_command(command):
# type: (str) -> None # type: (str) -> None
if os.path.exists(shell_profile_path): if os.path.exists(shell_profile_path):
with open(shell_profile_path, 'r') as shell_profile_file: with open(shell_profile_path) as shell_profile_file:
lines = [line.strip() for line in shell_profile_file.readlines()] lines = [line.strip() for line in shell_profile_file.readlines()]
if command not in lines: if command not in lines:
with open(shell_profile_path, 'a+') as shell_profile_file: with open(shell_profile_path, 'a+') as shell_profile_file:
@ -77,7 +77,7 @@ def setup_bash_profile() -> None:
if os.path.exists(DOT_PROFILE): if os.path.exists(DOT_PROFILE):
try: try:
with open(BASH_PROFILE, "r") as f: with open(BASH_PROFILE) as f:
profile_contents = f.read() profile_contents = f.read()
if profile_contents == OLD_PROFILE_TEXT: if profile_contents == OLD_PROFILE_TEXT:
os.unlink(BASH_PROFILE) os.unlink(BASH_PROFILE)

View File

@ -214,7 +214,7 @@ def validate(fn=None, text=None, check_indent=True):
fn = '<in memory file>' fn = '<in memory file>'
if text is None: if text is None:
with open(fn, 'r') as f: with open(fn) as f:
text = f.read() text = f.read()
tokens = tokenize(text) tokens = tokenize(text)

View File

@ -56,7 +56,7 @@ def get_provisioning_status():
# their own dependencies and not running provision. # their own dependencies and not running provision.
return True, None return True, None
with open(version_file, 'r') as f: with open(version_file) as f:
version = f.read().strip() version = f.read().strip()
# Normal path for people that provision--we're all good! # Normal path for people that provision--we're all good!

View File

@ -1,6 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import print_function
from __future__ import absolute_import
import os import os
import sys import sys
import argparse import argparse

View File

@ -1,8 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import absolute_import
from typing import List, TYPE_CHECKING from typing import List, TYPE_CHECKING
from zulint.custom_rules import RuleList from zulint.custom_rules import RuleList
@ -57,7 +52,7 @@ FILES_WITH_LEGACY_SUBJECT = {
shebang_rules = [ shebang_rules = [
{'pattern': '^#!', {'pattern': '^#!',
'description': "zerver library code shouldn't have a shebang line.", 'description': "zerver library code shouldn't have a shebang line.",
'include_only': set(['zerver/'])}, 'include_only': {'zerver/'}},
# /bin/sh and /usr/bin/env are the only two binaries # /bin/sh and /usr/bin/env are the only two binaries
# that NixOS provides at a fixed path (outside a # that NixOS provides at a fixed path (outside a
# buildFHSUserEnv sandbox). # buildFHSUserEnv sandbox).
@ -81,12 +76,12 @@ whitespace_rules = [
}, },
{'pattern': '\t', {'pattern': '\t',
'strip': '\n', 'strip': '\n',
'exclude': set(['tools/ci/success-http-headers.txt']), 'exclude': {'tools/ci/success-http-headers.txt'},
'description': 'Fix tab-based whitespace'}, 'description': 'Fix tab-based whitespace'},
] # type: List[Rule] ] # type: List[Rule]
comma_whitespace_rule = [ comma_whitespace_rule = [
{'pattern': ', {2,}[^#/ ]', {'pattern': ', {2,}[^#/ ]',
'exclude': set(['zerver/tests', 'frontend_tests/node_tests', 'corporate/tests']), 'exclude': {'zerver/tests', 'frontend_tests/node_tests', 'corporate/tests'},
'description': "Remove multiple whitespaces after ','", 'description': "Remove multiple whitespaces after ','",
'good_lines': ['foo(1, 2, 3)', 'foo = bar # some inline comment'], 'good_lines': ['foo(1, 2, 3)', 'foo = bar # some inline comment'],
'bad_lines': ['foo(1, 2, 3)', 'foo(1, 2, 3)']}, 'bad_lines': ['foo(1, 2, 3)', 'foo(1, 2, 3)']},
@ -110,8 +105,8 @@ js_rules = RuleList(
langs=['js'], langs=['js'],
rules=[ rules=[
{'pattern': 'subject|SUBJECT', {'pattern': 'subject|SUBJECT',
'exclude': set(['static/js/util.js', 'exclude': {'static/js/util.js',
'frontend_tests/']), 'frontend_tests/'},
'exclude_pattern': 'emails', 'exclude_pattern': 'emails',
'description': 'avoid subject in JS code', 'description': 'avoid subject in JS code',
'good_lines': ['topic_name'], 'good_lines': ['topic_name'],
@ -155,7 +150,7 @@ js_rules = RuleList(
'description': 'Write JS else statements on same line as }'}, 'description': 'Write JS else statements on same line as }'},
{'pattern': r'''[.]text\(["'][a-zA-Z]''', {'pattern': r'''[.]text\(["'][a-zA-Z]''',
'description': 'Strings passed to $().text should be wrapped in i18n.t() for internationalization', 'description': 'Strings passed to $().text should be wrapped in i18n.t() for internationalization',
'exclude': set(['frontend_tests/node_tests/'])}, 'exclude': {'frontend_tests/node_tests/'}},
{'pattern': r'''compose_error\(["']''', {'pattern': r'''compose_error\(["']''',
'description': 'Argument to compose_error should be a literal string enclosed ' 'description': 'Argument to compose_error should be a literal string enclosed '
'by i18n.t()'}, 'by i18n.t()'},
@ -177,7 +172,7 @@ js_rules = RuleList(
'bad_lines': ['$(document).ready(function () {foo();}']}, 'bad_lines': ['$(document).ready(function () {foo();}']},
{'pattern': '[$][.](get|post|patch|delete|ajax)[(]', {'pattern': '[$][.](get|post|patch|delete|ajax)[(]',
'description': "Use channel module for AJAX calls", 'description': "Use channel module for AJAX calls",
'exclude': set([ 'exclude': {
# Internal modules can do direct network calls # Internal modules can do direct network calls
'static/js/blueslip.js', 'static/js/blueslip.js',
'static/js/channel.js', 'static/js/channel.js',
@ -185,17 +180,17 @@ js_rules = RuleList(
'static/js/stats/', 'static/js/stats/',
'static/js/portico/', 'static/js/portico/',
'static/js/billing/', 'static/js/billing/',
]), },
'good_lines': ['channel.get(...)'], 'good_lines': ['channel.get(...)'],
'bad_lines': ['$.get()', '$.post()', '$.ajax()']}, 'bad_lines': ['$.get()', '$.post()', '$.ajax()']},
{'pattern': 'style ?=', {'pattern': 'style ?=',
'description': "Avoid using the `style=` attribute; we prefer styling in CSS files", 'description': "Avoid using the `style=` attribute; we prefer styling in CSS files",
'exclude': set([ 'exclude': {
'frontend_tests/node_tests/copy_and_paste.js', 'frontend_tests/node_tests/copy_and_paste.js',
'frontend_tests/node_tests/upload.js', 'frontend_tests/node_tests/upload.js',
'static/js/upload.js', 'static/js/upload.js',
'static/js/stream_color.js', 'static/js/stream_color.js',
]), },
'good_lines': ['#my-style {color: blue;}'], 'good_lines': ['#my-style {color: blue;}'],
'bad_lines': ['<p style="color: blue;">Foo</p>', 'style = "color: blue;"']}, 'bad_lines': ['<p style="color: blue;">Foo</p>', 'style = "color: blue;"']},
*whitespace_rules, *whitespace_rules,
@ -212,14 +207,14 @@ python_rules = RuleList(
'good_lines': ['topic_name'], 'good_lines': ['topic_name'],
'bad_lines': ['subject="foo"', ' MAX_SUBJECT_LEN'], 'bad_lines': ['subject="foo"', ' MAX_SUBJECT_LEN'],
'exclude': FILES_WITH_LEGACY_SUBJECT, 'exclude': FILES_WITH_LEGACY_SUBJECT,
'include_only': set([ 'include_only': {
'zerver/data_import/', 'zerver/data_import/',
'zerver/lib/', 'zerver/lib/',
'zerver/tests/', 'zerver/tests/',
'zerver/views/'])}, 'zerver/views/'}},
{'pattern': 'msgid|MSGID', {'pattern': 'msgid|MSGID',
'exclude': set(['tools/check-capitalization', 'exclude': {'tools/check-capitalization',
'tools/i18n/tagmessages']), 'tools/i18n/tagmessages'},
'description': 'Avoid using "msgid" as a variable name; use "message_id" instead.'}, 'description': 'Avoid using "msgid" as a variable name; use "message_id" instead.'},
{'pattern': '^(?!#)@login_required', {'pattern': '^(?!#)@login_required',
'description': '@login_required is unsupported; use @zulip_login_required', 'description': '@login_required is unsupported; use @zulip_login_required',
@ -227,10 +222,10 @@ python_rules = RuleList(
'bad_lines': ['@login_required', ' @login_required']}, 'bad_lines': ['@login_required', ' @login_required']},
{'pattern': '^user_profile[.]save[(][)]', {'pattern': '^user_profile[.]save[(][)]',
'description': 'Always pass update_fields when saving user_profile objects', 'description': 'Always pass update_fields when saving user_profile objects',
'exclude_line': set([ 'exclude_line': {
('zerver/lib/actions.py', "user_profile.save() # Can't use update_fields because of how the foreign key works."), ('zerver/lib/actions.py', "user_profile.save() # Can't use update_fields because of how the foreign key works."),
]), },
'exclude': set(['zerver/tests', 'zerver/lib/create_user.py']), 'exclude': {'zerver/tests', 'zerver/lib/create_user.py'},
'good_lines': ['user_profile.save(update_fields=["pointer"])'], 'good_lines': ['user_profile.save(update_fields=["pointer"])'],
'bad_lines': ['user_profile.save()']}, 'bad_lines': ['user_profile.save()']},
{'pattern': r'^[^"]*"[^"]*"%\(', {'pattern': r'^[^"]*"[^"]*"%\(',
@ -252,7 +247,7 @@ python_rules = RuleList(
'bad_lines': ['a =b', 'asdf =42']}, 'bad_lines': ['a =b', 'asdf =42']},
{'pattern': r'":\w[^"]*$', {'pattern': r'":\w[^"]*$',
'description': 'Missing whitespace after ":"', 'description': 'Missing whitespace after ":"',
'exclude': set(['zerver/tests/test_push_notifications.py']), 'exclude': {'zerver/tests/test_push_notifications.py'},
'good_lines': ['"foo": bar', '"some:string:with:colons"'], 'good_lines': ['"foo": bar', '"some:string:with:colons"'],
'bad_lines': ['"foo":bar', '"foo":1']}, 'bad_lines': ['"foo":bar', '"foo":1']},
{'pattern': r"':\w[^']*$", {'pattern': r"':\w[^']*$",
@ -261,7 +256,7 @@ python_rules = RuleList(
'bad_lines': ["'foo':bar", "'foo':1"]}, 'bad_lines': ["'foo':bar", "'foo':1"]},
{'pattern': r"^\s+#\w", {'pattern': r"^\s+#\w",
'strip': '\n', 'strip': '\n',
'exclude': set(['tools/droplets/create.py']), 'exclude': {'tools/droplets/create.py'},
'description': 'Missing whitespace after "#"', 'description': 'Missing whitespace after "#"',
'good_lines': ['a = b # some operation', '1+2 # 3 is the result'], 'good_lines': ['a = b # some operation', '1+2 # 3 is the result'],
'bad_lines': [' #some operation', ' #not valid!!!']}, 'bad_lines': [' #some operation', ' #not valid!!!']},
@ -278,9 +273,9 @@ python_rules = RuleList(
'good_lines': ['# type: (Any, Any)', 'colon:separated:string:containing:type:as:keyword'], 'good_lines': ['# type: (Any, Any)', 'colon:separated:string:containing:type:as:keyword'],
'bad_lines': ['# type:(Any, Any)']}, 'bad_lines': ['# type:(Any, Any)']},
{'pattern': "type: ignore$", {'pattern': "type: ignore$",
'exclude': set(['tools/tests', 'exclude': {'tools/tests',
'zerver/lib/test_runner.py', 'zerver/lib/test_runner.py',
'zerver/tests']), 'zerver/tests'},
'description': '"type: ignore" should always end with "# type: ignore # explanation for why"', 'description': '"type: ignore" should always end with "# type: ignore # explanation for why"',
'good_lines': ['foo = bar # type: ignore # explanation'], 'good_lines': ['foo = bar # type: ignore # explanation'],
'bad_lines': ['foo = bar # type: ignore']}, 'bad_lines': ['foo = bar # type: ignore']},
@ -318,17 +313,17 @@ python_rules = RuleList(
'good_lines': ['"foo %s bar" % ("baz",)"'], 'good_lines': ['"foo %s bar" % ("baz",)"'],
'bad_lines': ['"foo %s bar" % ("baz")']}, 'bad_lines': ['"foo %s bar" % ("baz")']},
{'pattern': 'sudo', {'pattern': 'sudo',
'include_only': set(['scripts/']), 'include_only': {'scripts/'},
'exclude': set(['scripts/lib/setup_venv.py']), 'exclude': {'scripts/lib/setup_venv.py'},
'exclude_line': set([ 'exclude_line': {
('scripts/lib/zulip_tools.py', 'sudo_args = kwargs.pop(\'sudo_args\', [])'), ('scripts/lib/zulip_tools.py', 'sudo_args = kwargs.pop(\'sudo_args\', [])'),
('scripts/lib/zulip_tools.py', 'args = [\'sudo\'] + sudo_args + [\'--\'] + args'), ('scripts/lib/zulip_tools.py', 'args = [\'sudo\'] + sudo_args + [\'--\'] + args'),
]), },
'description': 'Most scripts are intended to run on systems without sudo.', 'description': 'Most scripts are intended to run on systems without sudo.',
'good_lines': ['subprocess.check_call(["ls"])'], 'good_lines': ['subprocess.check_call(["ls"])'],
'bad_lines': ['subprocess.check_call(["sudo", "ls"])']}, 'bad_lines': ['subprocess.check_call(["sudo", "ls"])']},
{'pattern': 'django.utils.translation', {'pattern': 'django.utils.translation',
'include_only': set(['test/', 'zerver/views/development/']), 'include_only': {'test/', 'zerver/views/development/'},
'description': 'Test strings should not be tagged for translation', 'description': 'Test strings should not be tagged for translation',
'good_lines': [''], 'good_lines': [''],
'bad_lines': ['django.utils.translation']}, 'bad_lines': ['django.utils.translation']},
@ -341,25 +336,23 @@ python_rules = RuleList(
'good_lines': ['return json_success()'], 'good_lines': ['return json_success()'],
'bad_lines': ['return json_success({})']}, 'bad_lines': ['return json_success({})']},
{'pattern': r'\Wjson_error\(_\(?\w+\)', {'pattern': r'\Wjson_error\(_\(?\w+\)',
'exclude': set(['zerver/tests', 'zerver/views/development/']), 'exclude': {'zerver/tests', 'zerver/views/development/'},
'description': 'Argument to json_error should be a literal string enclosed by _()', 'description': 'Argument to json_error should be a literal string enclosed by _()',
'good_lines': ['return json_error(_("string"))'], 'good_lines': ['return json_error(_("string"))'],
'bad_lines': ['return json_error(_variable)', 'return json_error(_(variable))']}, 'bad_lines': ['return json_error(_variable)', 'return json_error(_(variable))']},
{'pattern': r'''\Wjson_error\(['"].+[),]$''', {'pattern': r'''\Wjson_error\(['"].+[),]$''',
'exclude': set(['zerver/tests']), 'exclude': {'zerver/tests'},
'description': 'Argument to json_error should a literal string enclosed by _()'}, 'description': 'Argument to json_error should a literal string enclosed by _()'},
# To avoid JsonableError(_variable) and JsonableError(_(variable)) # To avoid JsonableError(_variable) and JsonableError(_(variable))
{'pattern': r'\WJsonableError\(_\(?\w.+\)', {'pattern': r'\WJsonableError\(_\(?\w.+\)',
'exclude': set(['zerver/tests', 'zerver/views/development/']), 'exclude': {'zerver/tests', 'zerver/views/development/'},
'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, 'description': 'Argument to JsonableError should be a literal string enclosed by _()'},
{'pattern': r'''\WJsonableError\(["'].+\)''', {'pattern': r'''\WJsonableError\(["'].+\)''',
'exclude': set(['zerver/tests', 'zerver/views/development/']), 'exclude': {'zerver/tests', 'zerver/views/development/'},
'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, 'description': 'Argument to JsonableError should be a literal string enclosed by _()'},
{'pattern': r"""\b_\((?:\s|{}|{})*[^\s'")]""".format(PYSQ, PYDQ), {'pattern': r"""\b_\((?:\s|{}|{})*[^\s'")]""".format(PYSQ, PYDQ),
'description': 'Called _() on a computed string', 'description': 'Called _() on a computed string',
'exclude_line': set([ 'exclude_line': {('zerver/lib/i18n.py', 'result = _(string)')},
('zerver/lib/i18n.py', 'result = _(string)'),
]),
'good_lines': ["return json_error(_('No presence data for %s') % (target.email,))"], 'good_lines': ["return json_error(_('No presence data for %s') % (target.email,))"],
'bad_lines': ["return json_error(_('No presence data for %s' % (target.email,)))"]}, 'bad_lines': ["return json_error(_('No presence data for %s' % (target.email,)))"]},
{'pattern': r'''([a-zA-Z0-9_]+)=REQ\(['"]\1['"]''', {'pattern': r'''([a-zA-Z0-9_]+)=REQ\(['"]\1['"]''',
@ -371,35 +364,35 @@ python_rules = RuleList(
'''}, '''},
# Directly fetching Message objects in e.g. views code is often a security bug. # Directly fetching Message objects in e.g. views code is often a security bug.
{'pattern': '[^r]Message.objects.get', {'pattern': '[^r]Message.objects.get',
'exclude': set(["zerver/tests", 'exclude': {"zerver/tests",
"zerver/lib/onboarding.py", "zerver/lib/onboarding.py",
"zilencer/management/commands/add_mock_conversation.py", "zilencer/management/commands/add_mock_conversation.py",
"zerver/worker/queue_processors.py", "zerver/worker/queue_processors.py",
"zerver/management/commands/export.py", "zerver/management/commands/export.py",
"zerver/lib/export.py"]), "zerver/lib/export.py"},
'description': 'Please use access_message() to fetch Message objects', 'description': 'Please use access_message() to fetch Message objects',
}, },
{'pattern': 'Stream.objects.get', {'pattern': 'Stream.objects.get',
'include_only': set(["zerver/views/"]), 'include_only': {"zerver/views/"},
'description': 'Please use access_stream_by_*() to fetch Stream objects', 'description': 'Please use access_stream_by_*() to fetch Stream objects',
}, },
{'pattern': 'get_stream[(]', {'pattern': 'get_stream[(]',
'include_only': set(["zerver/views/", "zerver/lib/actions.py"]), 'include_only': {"zerver/views/", "zerver/lib/actions.py"},
'exclude_line': set([ 'exclude_line': {
# This one in check_message is kinda terrible, since it's # This one in check_message is kinda terrible, since it's
# how most instances are written, but better to exclude something than nothing # how most instances are written, but better to exclude something than nothing
('zerver/lib/actions.py', 'stream = get_stream(stream_name, realm)'), ('zerver/lib/actions.py', 'stream = get_stream(stream_name, realm)'),
('zerver/lib/actions.py', 'return get_stream("signups", realm)'), ('zerver/lib/actions.py', 'return get_stream("signups", realm)'),
]), },
'description': 'Please use access_stream_by_*() to fetch Stream objects', 'description': 'Please use access_stream_by_*() to fetch Stream objects',
}, },
{'pattern': 'Stream.objects.filter', {'pattern': 'Stream.objects.filter',
'include_only': set(["zerver/views/"]), 'include_only': {"zerver/views/"},
'description': 'Please use access_stream_by_*() to fetch Stream objects', 'description': 'Please use access_stream_by_*() to fetch Stream objects',
}, },
{'pattern': '^from (zerver|analytics|confirmation)', {'pattern': '^from (zerver|analytics|confirmation)',
'include_only': set(["/migrations/"]), 'include_only': {"/migrations/"},
'exclude': set([ 'exclude': {
'zerver/migrations/0032_verify_all_medium_avatar_images.py', 'zerver/migrations/0032_verify_all_medium_avatar_images.py',
'zerver/migrations/0060_move_avatars_to_be_uid_based.py', 'zerver/migrations/0060_move_avatars_to_be_uid_based.py',
'zerver/migrations/0104_fix_unreads.py', 'zerver/migrations/0104_fix_unreads.py',
@ -407,11 +400,11 @@ python_rules = RuleList(
'zerver/migrations/0209_user_profile_no_empty_password.py', 'zerver/migrations/0209_user_profile_no_empty_password.py',
'zerver/migrations/0260_missed_message_addresses_from_redis_to_db.py', 'zerver/migrations/0260_missed_message_addresses_from_redis_to_db.py',
'pgroonga/migrations/0002_html_escape_subject.py', 'pgroonga/migrations/0002_html_escape_subject.py',
]), },
'description': "Don't import models or other code in migrations; see docs/subsystems/schema-migrations.md", 'description': "Don't import models or other code in migrations; see docs/subsystems/schema-migrations.md",
}, },
{'pattern': 'datetime[.](now|utcnow)', {'pattern': 'datetime[.](now|utcnow)',
'include_only': set(["zerver/", "analytics/"]), 'include_only': {"zerver/", "analytics/"},
'description': "Don't use datetime in backend code.\n" 'description': "Don't use datetime in backend code.\n"
"See https://zulip.readthedocs.io/en/latest/contributing/code-style.html#naive-datetime-objects", "See https://zulip.readthedocs.io/en/latest/contributing/code-style.html#naive-datetime-objects",
}, },
@ -431,7 +424,7 @@ python_rules = RuleList(
'good_lines': ['if my_django_model.id == 42', 'self.user_profile._meta.pk'], 'good_lines': ['if my_django_model.id == 42', 'self.user_profile._meta.pk'],
'bad_lines': ['if my_django_model.pk == 42']}, 'bad_lines': ['if my_django_model.pk == 42']},
{'pattern': r'^[ ]*# type: \(', {'pattern': r'^[ ]*# type: \(',
'exclude': set([ 'exclude': {
# These directories, especially scripts/ and puppet/, # These directories, especially scripts/ and puppet/,
# have tools that need to run before a Zulip environment # have tools that need to run before a Zulip environment
# is provisioned; in some of those, the `typing` module # is provisioned; in some of those, the `typing` module
@ -446,7 +439,7 @@ python_rules = RuleList(
'zerver/views/streams.py', 'zerver/views/streams.py',
# thumbor is (currently) python2 only # thumbor is (currently) python2 only
'zthumbor/', 'zthumbor/',
]), },
'description': 'Comment-style function type annotation. Use Python3 style annotations instead.', 'description': 'Comment-style function type annotation. Use Python3 style annotations instead.',
}, },
{'pattern': r' = models[.].*null=True.*\) # type: (?!Optional)', {'pattern': r' = models[.].*null=True.*\) # type: (?!Optional)',
@ -471,7 +464,7 @@ python_rules = RuleList(
'stream = models.ForeignKey(Stream, on_delete=CASCADE) # type: Optional[Stream]'], 'stream = models.ForeignKey(Stream, on_delete=CASCADE) # type: Optional[Stream]'],
}, },
{'pattern': r'[\s([]Text([^\s\w]|$)', {'pattern': r'[\s([]Text([^\s\w]|$)',
'exclude': set([ 'exclude': {
# We are likely to want to keep these dirs Python 2+3 compatible, # We are likely to want to keep these dirs Python 2+3 compatible,
# since the plan includes extracting them to a separate project eventually. # since the plan includes extracting them to a separate project eventually.
'tools/lib', 'tools/lib',
@ -479,26 +472,26 @@ python_rules = RuleList(
'zerver/migrations/', 'zerver/migrations/',
# thumbor is (currently) python2 only # thumbor is (currently) python2 only
'zthumbor/', 'zthumbor/',
]), },
'description': "Now that we're a Python 3 only codebase, we don't need to use typing.Text. Please use str instead.", 'description': "Now that we're a Python 3 only codebase, we don't need to use typing.Text. Please use str instead.",
}, },
{'pattern': 'exit[(]1[)]', {'pattern': 'exit[(]1[)]',
'include_only': set(["/management/commands/"]), 'include_only': {"/management/commands/"},
'description': 'Raise CommandError to exit with failure in management commands', 'description': 'Raise CommandError to exit with failure in management commands',
}, },
{'pattern': '.is_realm_admin =', {'pattern': '.is_realm_admin =',
'description': 'Use do_change_is_admin function rather than setting UserProfile\'s is_realm_admin attribute directly.', 'description': 'Use do_change_is_admin function rather than setting UserProfile\'s is_realm_admin attribute directly.',
'exclude': set([ 'exclude': {
'zerver/migrations/0248_userprofile_role_start.py', 'zerver/migrations/0248_userprofile_role_start.py',
'zerver/tests/test_users.py', 'zerver/tests/test_users.py',
]), },
}, },
{'pattern': '.is_guest =', {'pattern': '.is_guest =',
'description': 'Use do_change_is_guest function rather than setting UserProfile\'s is_guest attribute directly.', 'description': 'Use do_change_is_guest function rather than setting UserProfile\'s is_guest attribute directly.',
'exclude': set([ 'exclude': {
'zerver/migrations/0248_userprofile_role_start.py', 'zerver/migrations/0248_userprofile_role_start.py',
'zerver/tests/test_users.py', 'zerver/tests/test_users.py',
]), },
}, },
*whitespace_rules, *whitespace_rules,
*comma_whitespace_rule, *comma_whitespace_rule,
@ -515,11 +508,11 @@ bash_rules = RuleList(
' to set -x|set -e'}, ' to set -x|set -e'},
{'pattern': 'sudo', {'pattern': 'sudo',
'description': 'Most scripts are intended to work on systems without sudo', 'description': 'Most scripts are intended to work on systems without sudo',
'include_only': set(['scripts/']), 'include_only': {'scripts/'},
'exclude': set([ 'exclude': {
'scripts/lib/install', 'scripts/lib/install',
'scripts/setup/configure-rabbitmq' 'scripts/setup/configure-rabbitmq'
]), }, }, },
*whitespace_rules[0:1], *whitespace_rules[0:1],
], ],
shebang_rules=shebang_rules, shebang_rules=shebang_rules,
@ -576,7 +569,7 @@ css_rules = RuleList(
prose_style_rules = [ prose_style_rules = [
{'pattern': r'[^\/\#\-"]([jJ]avascript)', # exclude usage in hrefs/divs {'pattern': r'[^\/\#\-"]([jJ]avascript)', # exclude usage in hrefs/divs
'exclude': set(["docs/documentation/api.md"]), 'exclude': {"docs/documentation/api.md"},
'description': "javascript should be spelled JavaScript"}, 'description': "javascript should be spelled JavaScript"},
{'pattern': r'''[^\/\-\."'\_\=\>]([gG]ithub)[^\.\-\_"\<]''', # exclude usage in hrefs/divs {'pattern': r'''[^\/\-\."'\_\=\>]([gG]ithub)[^\.\-\_"\<]''', # exclude usage in hrefs/divs
'description': "github should be spelled GitHub"}, 'description': "github should be spelled GitHub"},
@ -593,7 +586,7 @@ prose_style_rules = [
] # type: List[Rule] ] # type: List[Rule]
html_rules = whitespace_rules + prose_style_rules + [ html_rules = whitespace_rules + prose_style_rules + [
{'pattern': 'subject|SUBJECT', {'pattern': 'subject|SUBJECT',
'exclude': set(['templates/zerver/email.html']), 'exclude': {'templates/zerver/email.html'},
'exclude_pattern': 'email subject', 'exclude_pattern': 'email subject',
'description': 'avoid subject in templates', 'description': 'avoid subject in templates',
'good_lines': ['topic_name'], 'good_lines': ['topic_name'],
@ -602,7 +595,7 @@ html_rules = whitespace_rules + prose_style_rules + [
'description': "`placeholder` value should be translatable.", 'description': "`placeholder` value should be translatable.",
'exclude_line': {('templates/zerver/register.html', 'placeholder="acme"'), 'exclude_line': {('templates/zerver/register.html', 'placeholder="acme"'),
('templates/zerver/register.html', 'placeholder="Acme or Ακμή"')}, ('templates/zerver/register.html', 'placeholder="Acme or Ακμή"')},
'exclude': set(["templates/analytics/support.html"]), 'exclude': {"templates/analytics/support.html"},
'good_lines': ['<input class="stream-list-filter" type="text" placeholder="{{ _(\'Search streams\') }}" />'], 'good_lines': ['<input class="stream-list-filter" type="text" placeholder="{{ _(\'Search streams\') }}" />'],
'bad_lines': ['<input placeholder="foo">']}, 'bad_lines': ['<input placeholder="foo">']},
{'pattern': "placeholder='[^{]", {'pattern': "placeholder='[^{]",
@ -619,8 +612,8 @@ html_rules = whitespace_rules + prose_style_rules + [
'bad_lines': ['<button aria-label="foo"></button>']}, 'bad_lines': ['<button aria-label="foo"></button>']},
{'pattern': 'script src="http', {'pattern': 'script src="http',
'description': "Don't directly load dependencies from CDNs. See docs/subsystems/html-css.md", 'description': "Don't directly load dependencies from CDNs. See docs/subsystems/html-css.md",
'exclude': set(["templates/corporate/billing.html", "templates/zerver/hello.html", 'exclude': {"templates/corporate/billing.html", "templates/zerver/hello.html",
"templates/corporate/upgrade.html"]), "templates/corporate/upgrade.html"},
'good_lines': ["{{ render_entrypoint('landing-page') }}"], 'good_lines': ["{{ render_entrypoint('landing-page') }}"],
'bad_lines': ['<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>']}, 'bad_lines': ['<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>']},
{'pattern': "title='[^{]", {'pattern': "title='[^{]",
@ -628,17 +621,17 @@ html_rules = whitespace_rules + prose_style_rules + [
'good_lines': ['<link rel="author" title="{{ _(\'About these documents\') }}" />'], 'good_lines': ['<link rel="author" title="{{ _(\'About these documents\') }}" />'],
'bad_lines': ["<p title='foo'></p>"]}, 'bad_lines': ["<p title='foo'></p>"]},
{'pattern': r'title="[^{\:]', {'pattern': r'title="[^{\:]',
'exclude_line': set([ 'exclude_line': {
('templates/zerver/app/markdown_help.html', ('templates/zerver/app/markdown_help.html',
'<td class="rendered_markdown"><img alt=":heart:" class="emoji" src="/static/generated/emoji/images/emoji/heart.png" title=":heart:" /></td>') '<td class="rendered_markdown"><img alt=":heart:" class="emoji" src="/static/generated/emoji/images/emoji/heart.png" title=":heart:" /></td>')
]), },
'exclude': set(["templates/zerver/emails", "templates/analytics/realm_details.html", "templates/analytics/support.html"]), 'exclude': {"templates/zerver/emails", "templates/analytics/realm_details.html", "templates/analytics/support.html"},
'description': "`title` value should be translatable."}, 'description': "`title` value should be translatable."},
{'pattern': r'''\Walt=["'][^{"']''', {'pattern': r'''\Walt=["'][^{"']''',
'description': "alt argument should be enclosed by _() or it should be an empty string.", 'description': "alt argument should be enclosed by _() or it should be an empty string.",
'exclude': set(['static/templates/settings/display_settings.hbs', 'exclude': {'static/templates/settings/display_settings.hbs',
'templates/zerver/app/keyboard_shortcuts.html', 'templates/zerver/app/keyboard_shortcuts.html',
'templates/zerver/app/markdown_help.html']), 'templates/zerver/app/markdown_help.html'},
'good_lines': ['<img src="{{source_url}}" alt="{{ _(name) }}" />', '<img alg="" />'], 'good_lines': ['<img src="{{source_url}}" alt="{{ _(name) }}" />', '<img alg="" />'],
'bad_lines': ['<img alt="Foo Image" />']}, 'bad_lines': ['<img alt="Foo Image" />']},
{'pattern': r'''\Walt=["']{{ ?["']''', {'pattern': r'''\Walt=["']{{ ?["']''',
@ -649,13 +642,13 @@ html_rules = whitespace_rules + prose_style_rules + [
'description': "Don't use inline event handlers (onclick=, etc. attributes) in HTML. Instead," 'description': "Don't use inline event handlers (onclick=, etc. attributes) in HTML. Instead,"
"attach a jQuery event handler ($('#foo').on('click', function () {...})) when " "attach a jQuery event handler ($('#foo').on('click', function () {...})) when "
"the DOM is ready (inside a $(function () {...}) block).", "the DOM is ready (inside a $(function () {...}) block).",
'exclude': set(['templates/zerver/dev_login.html', 'templates/corporate/upgrade.html']), 'exclude': {'templates/zerver/dev_login.html', 'templates/corporate/upgrade.html'},
'good_lines': ["($('#foo').on('click', function () {}"], 'good_lines': ["($('#foo').on('click', function () {}"],
'bad_lines': ["<button id='foo' onclick='myFunction()'>Foo</button>", "<input onchange='myFunction()'>"]}, 'bad_lines': ["<button id='foo' onclick='myFunction()'>Foo</button>", "<input onchange='myFunction()'>"]},
{'pattern': 'style ?=', {'pattern': 'style ?=',
'description': "Avoid using the `style=` attribute; we prefer styling in CSS files", 'description': "Avoid using the `style=` attribute; we prefer styling in CSS files",
'exclude_pattern': r'.*style ?=["' + "'" + '](display: ?none|background: {{|color: {{|background-color: {{).*', 'exclude_pattern': r'.*style ?=["' + "'" + '](display: ?none|background: {{|color: {{|background-color: {{).*',
'exclude': set([ 'exclude': {
# KaTeX output uses style attribute # KaTeX output uses style attribute
'templates/zerver/app/markdown_help.html', 'templates/zerver/app/markdown_help.html',
# 5xx page doesn't have external CSS # 5xx page doesn't have external CSS
@ -714,7 +707,7 @@ html_rules = whitespace_rules + prose_style_rules + [
'templates/analytics/realm_summary_table.html', 'templates/analytics/realm_summary_table.html',
'templates/corporate/zephyr.html', 'templates/corporate/zephyr.html',
'templates/corporate/zephyr-mirror.html', 'templates/corporate/zephyr-mirror.html',
]), },
'good_lines': ['#my-style {color: blue;}', 'style="display: none"', "style='display: none"], 'good_lines': ['#my-style {color: blue;}', 'style="display: none"', "style='display: none"],
'bad_lines': ['<p style="color: blue;">Foo</p>', 'style = "color: blue;"']}, 'bad_lines': ['<p style="color: blue;">Foo</p>', 'style = "color: blue;"']},
] # type: List[Rule] ] # type: List[Rule]
@ -764,10 +757,10 @@ json_rules = RuleList(
trailing_whitespace_rule, trailing_whitespace_rule,
{'pattern': '\t', {'pattern': '\t',
'strip': '\n', 'strip': '\n',
'exclude': set(['zerver/webhooks/']), 'exclude': {'zerver/webhooks/'},
'description': 'Fix tab-based whitespace'}, 'description': 'Fix tab-based whitespace'},
{'pattern': r'":["\[\{]', {'pattern': r'":["\[\{]',
'exclude': set(['zerver/webhooks/', 'zerver/tests/fixtures/']), 'exclude': {'zerver/webhooks/', 'zerver/tests/fixtures/'},
'description': 'Require space after : in JSON'}, 'description': 'Require space after : in JSON'},
] ]
) )
@ -801,15 +794,15 @@ markdown_rules = RuleList(
'description': 'Linkified markdown URLs should use cleaner <http://example.com> syntax.'}, 'description': 'Linkified markdown URLs should use cleaner <http://example.com> syntax.'},
{'pattern': 'https://zulip.readthedocs.io/en/latest/[a-zA-Z0-9]', {'pattern': 'https://zulip.readthedocs.io/en/latest/[a-zA-Z0-9]',
'exclude': {'docs/overview/contributing.md', 'docs/overview/readme.md', 'docs/README.md'}, 'exclude': {'docs/overview/contributing.md', 'docs/overview/readme.md', 'docs/README.md'},
'include_only': set(['docs/']), 'include_only': {'docs/'},
'description': "Use relative links (../foo/bar.html) to other documents in docs/", 'description': "Use relative links (../foo/bar.html) to other documents in docs/",
}, },
{'pattern': "su zulip -c [^']", {'pattern': "su zulip -c [^']",
'include_only': set(['docs/']), 'include_only': {'docs/'},
'description': "Always quote arguments using `su zulip -c '` to avoid confusion about how su works.", 'description': "Always quote arguments using `su zulip -c '` to avoid confusion about how su works.",
}, },
{'pattern': r'\][(][^#h]', {'pattern': r'\][(][^#h]',
'include_only': set(['README.md', 'CONTRIBUTING.md']), 'include_only': {'README.md', 'CONTRIBUTING.md'},
'description': "Use absolute links from docs served by GitHub", 'description': "Use absolute links from docs served by GitHub",
}, },
], ],
@ -823,10 +816,10 @@ help_markdown_rules = RuleList(
rules=markdown_rules.rules + [ rules=markdown_rules.rules + [
{'pattern': '[a-z][.][A-Z]', {'pattern': '[a-z][.][A-Z]',
'description': "Likely missing space after end of sentence", 'description': "Likely missing space after end of sentence",
'include_only': set(['templates/zerver/help/']), 'include_only': {'templates/zerver/help/'},
}, },
{'pattern': r'\b[rR]ealm[s]?\b', {'pattern': r'\b[rR]ealm[s]?\b',
'include_only': set(['templates/zerver/help/']), 'include_only': {'templates/zerver/help/'},
'good_lines': ['Organization', 'deactivate_realm', 'realm_filter'], 'good_lines': ['Organization', 'deactivate_realm', 'realm_filter'],
'bad_lines': ['Users are in a realm', 'Realm is the best model'], 'bad_lines': ['Users are in a realm', 'Realm is the best model'],
'description': "Realms are referred to as Organizations in user-facing docs."}, 'description': "Realms are referred to as Organizations in user-facing docs."},

View File

@ -1,6 +1,3 @@
from __future__ import print_function
from __future__ import absolute_import
from zulint.linters import run_pycodestyle from zulint.linters import run_pycodestyle
from typing import List from typing import List

View File

@ -1,6 +1,3 @@
from __future__ import print_function
from __future__ import absolute_import
import argparse import argparse
from typing import List from typing import List

View File

@ -7,7 +7,7 @@ def clean_html(filenames):
# type: (List[str]) -> None # type: (List[str]) -> None
for fn in filenames: for fn in filenames:
print('Prettifying: %s' % (fn,)) print('Prettifying: %s' % (fn,))
with open(fn, 'r') as f: with open(fn) as f:
html = f.read() html = f.read()
phtml = pretty_print_html(html) phtml = pretty_print_html(html)
with open(fn, 'w') as f: with open(fn, 'w') as f:

View File

@ -8,7 +8,7 @@ os.chdir(os.path.join(os.path.dirname(__file__), '..'))
pid_file_path = os.path.join(os.path.join(os.getcwd(), 'var/run/run_dev.pid')) pid_file_path = os.path.join(os.path.join(os.getcwd(), 'var/run/run_dev.pid'))
try: try:
with open(pid_file_path, 'r') as pid_file: with open(pid_file_path) as pid_file:
try: try:
pid = int(pid_file.read()) pid = int(pid_file.read())
except ValueError: except ValueError:

View File

@ -143,9 +143,9 @@ FAILED_TEST_PATH = 'var/last_test_failure.json'
def get_failed_tests() -> List[str]: def get_failed_tests() -> List[str]:
try: try:
with open(FAILED_TEST_PATH, 'r') as f: with open(FAILED_TEST_PATH) as f:
return ujson.load(f) return ujson.load(f)
except IOError: except OSError:
print("var/last_test_failure.json doesn't exist; running all tests.") print("var/last_test_failure.json doesn't exist; running all tests.")
return [] return []

View File

@ -244,9 +244,9 @@ def check_line_coverage(fn, line_coverage, line_mapping, log=True):
def read_coverage() -> Any: def read_coverage() -> Any:
coverage_json = None coverage_json = None
try: try:
with open(NODE_COVERAGE_PATH, 'r') as f: with open(NODE_COVERAGE_PATH) as f:
coverage_json = ujson.load(f) coverage_json = ujson.load(f)
except IOError: except OSError:
print(NODE_COVERAGE_PATH + " doesn't exist. Cannot enforce fully covered files.") print(NODE_COVERAGE_PATH + " doesn't exist. Cannot enforce fully covered files.")
raise raise
return coverage_json return coverage_json

View File

@ -28,7 +28,7 @@ for zuliprc_path in zuliprc_paths_list:
zuliprc = configparser.ConfigParser() zuliprc = configparser.ConfigParser()
result = '' result = ''
try: try:
with open(zuliprc_path, 'r') as f: with open(zuliprc_path) as f:
zuliprc.read_file(f, zuliprc_path) zuliprc.read_file(f, zuliprc_path)
api_details = zuliprc['api'] api_details = zuliprc['api']
email = api_details['email'] email = api_details['email']
@ -66,7 +66,7 @@ for zuliprc_path in zuliprc_paths_list:
zuliprc.write(w) zuliprc.write(w)
result = 'SUCCESS' result = 'SUCCESS'
reason = 'API field updated for user %s' % (email,) reason = 'API field updated for user %s' % (email,)
except (IOError, OSError): except OSError:
result = 'FAILURE' result = 'FAILURE'
reason = 'Writing to file unsuccessful' reason = 'Writing to file unsuccessful'
else: else:

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © 2014 Dropbox, Inc. # Copyright © 2014 Dropbox, Inc.
# #

View File

@ -238,7 +238,7 @@ def do_convert_data(gitter_data_file: str, output_dir: str, threads: int=6) -> N
raise Exception("Output directory should be empty!") raise Exception("Output directory should be empty!")
# Read data from the gitter file # Read data from the gitter file
with open(gitter_data_file, "r") as fp: with open(gitter_data_file) as fp:
gitter_data = ujson.load(fp) gitter_data = ujson.load(fp)
realm, avatar_list, user_map = gitter_workspace_to_realm( realm, avatar_list, user_map = gitter_workspace_to_realm(

View File

@ -84,7 +84,7 @@ def untar_input_file(tar_file: str) -> str:
def read_user_data(data_dir: str) -> List[ZerverFieldsT]: def read_user_data(data_dir: str) -> List[ZerverFieldsT]:
fn = 'users.json' fn = 'users.json'
data_file = os.path.join(data_dir, fn) data_file = os.path.join(data_dir, fn)
with open(data_file, "r") as fp: with open(data_file) as fp:
return ujson.load(fp) return ujson.load(fp)
def convert_user_data(user_handler: UserHandler, def convert_user_data(user_handler: UserHandler,
@ -257,7 +257,7 @@ def convert_room_data(raw_data: List[ZerverFieldsT],
users = set() users = set()
if api_token is not None: if api_token is not None:
hc = hypchat.HypChat(api_token) hc = hypchat.HypChat(api_token)
room_data = hc.fromurl('{0}/v2/room/{1}/member'.format(hc.endpoint, in_dict['id'])) room_data = hc.fromurl('{}/v2/room/{}/member'.format(hc.endpoint, in_dict['id']))
for item in room_data['items']: for item in room_data['items']:
hipchat_user_id = item['id'] hipchat_user_id = item['id']

View File

@ -681,7 +681,7 @@ def mattermost_data_file_to_dict(mattermost_data_file: str) -> Dict[str, Any]:
mattermost_data["emoji"] = [] mattermost_data["emoji"] = []
mattermost_data["direct_channel"] = [] mattermost_data["direct_channel"] = []
with open(mattermost_data_file, "r") as fp: with open(mattermost_data_file) as fp:
for line in fp: for line in fp:
row = ujson.loads(line.rstrip("\n")) row = ujson.loads(line.rstrip("\n"))
data_type = row["type"] data_type = row["type"]

View File

@ -205,7 +205,7 @@ def users_to_zerver_userprofile(slack_data_dir: str, users: List[ZerverFieldsT],
if not user.get('is_primary_owner', False): if not user.get('is_primary_owner', False):
user_id_count += 1 user_id_count += 1
logging.info(u"{} -> {}".format(user['name'], userprofile_dict['email'])) logging.info("{} -> {}".format(user['name'], userprofile_dict['email']))
process_customprofilefields(zerver_customprofilefield, zerver_customprofilefield_values) process_customprofilefields(zerver_customprofilefield, zerver_customprofilefield_values)
logging.info('######### IMPORTING USERS FINISHED #########\n') logging.info('######### IMPORTING USERS FINISHED #########\n')
@ -397,7 +397,7 @@ def channels_to_zerver_stream(slack_data_dir: str, realm_id: int,
stream_id_count += 1 stream_id_count += 1
recipient_id_count += 1 recipient_id_count += 1
logging.info(u"{} -> created".format(channel['name'])) logging.info("{} -> created".format(channel['name']))
# TODO map Slack's pins to Zulip's stars # TODO map Slack's pins to Zulip's stars
# There is the security model that Slack's pins are known to the team owner # There is the security model that Slack's pins are known to the team owner
@ -443,7 +443,7 @@ def channels_to_zerver_stream(slack_data_dir: str, realm_id: int,
huddle_id_count += 1 huddle_id_count += 1
recipient_id_count += 1 recipient_id_count += 1
logging.info(u"{} -> created".format(mpim['name'])) logging.info("{} -> created".format(mpim['name']))
try: try:
mpims = get_data_file(slack_data_dir + '/mpims.json') mpims = get_data_file(slack_data_dir + '/mpims.json')
@ -649,8 +649,7 @@ def get_messages_iterator(slack_data_dir: str, added_channels: Dict[str, Any],
# we sort the messages according to the timestamp to show messages with # we sort the messages according to the timestamp to show messages with
# the proper date order # the proper date order
for message in sorted(messages_for_one_day, key=lambda m: m['ts']): yield from sorted(messages_for_one_day, key=lambda m: m['ts'])
yield message
def channel_message_to_zerver_message(realm_id: int, def channel_message_to_zerver_message(realm_id: int,
users: List[ZerverFieldsT], users: List[ZerverFieldsT],
@ -1095,7 +1094,7 @@ def do_convert_data(slack_zip_file: str, output_dir: str, token: str, threads: i
logging.info("Zulip data dump created at %s" % (output_dir,)) logging.info("Zulip data dump created at %s" % (output_dir,))
def get_data_file(path: str) -> Any: def get_data_file(path: str) -> Any:
with open(path, "r") as fp: with open(path) as fp:
data = ujson.load(fp) data = ujson.load(fp)
return data return data

View File

@ -37,16 +37,16 @@ from typing import Any, List, Optional, Dict, Tuple
from two_factor.forms import AuthenticationTokenForm as TwoFactorAuthenticationTokenForm from two_factor.forms import AuthenticationTokenForm as TwoFactorAuthenticationTokenForm
from two_factor.utils import totp_digits from two_factor.utils import totp_digits
MIT_VALIDATION_ERROR = u'That user does not exist at MIT or is a ' + \ MIT_VALIDATION_ERROR = 'That user does not exist at MIT or is a ' + \
u'<a href="https://ist.mit.edu/email-lists">mailing list</a>. ' + \ '<a href="https://ist.mit.edu/email-lists">mailing list</a>. ' + \
u'If you want to sign up an alias for Zulip, ' + \ 'If you want to sign up an alias for Zulip, ' + \
u'<a href="mailto:support@zulipchat.com">contact us</a>.' '<a href="mailto:support@zulipchat.com">contact us</a>.'
WRONG_SUBDOMAIN_ERROR = "Your Zulip account is not a member of the " + \ WRONG_SUBDOMAIN_ERROR = "Your Zulip account is not a member of the " + \
"organization associated with this subdomain. " + \ "organization associated with this subdomain. " + \
"Please contact your organization administrator with any questions." "Please contact your organization administrator with any questions."
DEACTIVATED_ACCOUNT_ERROR = u"Your account is no longer active. " + \ DEACTIVATED_ACCOUNT_ERROR = "Your account is no longer active. " + \
u"Please contact your organization administrator to reactivate it." "Please contact your organization administrator to reactivate it."
PASSWORD_TOO_WEAK_ERROR = u"The password is too weak." PASSWORD_TOO_WEAK_ERROR = "The password is too weak."
AUTHENTICATION_RATE_LIMITED_ERROR = "You're making too many attempts to sign in. " + \ AUTHENTICATION_RATE_LIMITED_ERROR = "You're making too many attempts to sign in. " + \
"Try again in %s seconds or contact your organization administrator " + \ "Try again in %s seconds or contact your organization administrator " + \
"for help." "for help."

View File

@ -1158,11 +1158,11 @@ def get_recipient_info(recipient: Recipient,
# direct recipient or were mentioned; for now, we're just making # direct recipient or were mentioned; for now, we're just making
# sure we have the data we need for that without extra database # sure we have the data we need for that without extra database
# queries. # queries.
default_bot_user_ids = set([ default_bot_user_ids = {
row['id'] row['id']
for row in rows for row in rows
if row['is_bot'] and row['bot_type'] == UserProfile.DEFAULT_BOT if row['is_bot'] and row['bot_type'] == UserProfile.DEFAULT_BOT
]) }
service_bot_tuples = [ service_bot_tuples = [
(row['id'], row['bot_type']) (row['id'], row['bot_type'])
@ -1946,7 +1946,7 @@ def get_recipient_from_user_profiles(recipient_profiles: Sequence[UserProfile],
# Otherwise, we need a huddle. Make sure the sender is included in huddle messages # Otherwise, we need a huddle. Make sure the sender is included in huddle messages
recipient_profiles_map[sender.id] = sender recipient_profiles_map[sender.id] = sender
user_ids = set([user_id for user_id in recipient_profiles_map]) # type: Set[int] user_ids = {user_id for user_id in recipient_profiles_map} # type: Set[int]
return get_huddle_recipient(user_ids) return get_huddle_recipient(user_ids)
def validate_recipient_user_profiles(user_profiles: Sequence[UserProfile], def validate_recipient_user_profiles(user_profiles: Sequence[UserProfile],
@ -2680,7 +2680,7 @@ def bulk_get_subscriber_user_ids(stream_dicts: Iterable[Mapping[str, Any]],
for stream_id in stream_ids for stream_id in stream_ids
]) ])
result = dict((stream["id"], []) for stream in stream_dicts) # type: Dict[int, List[int]] result = {stream["id"]: [] for stream in stream_dicts} # type: Dict[int, List[int]]
if not recipient_ids: if not recipient_ids:
return result return result
@ -2857,7 +2857,7 @@ def bulk_add_subscriptions(streams: Iterable[Stream],
acting_user: Optional[UserProfile]=None) -> SubT: acting_user: Optional[UserProfile]=None) -> SubT:
users = list(users) users = list(users)
recipients_map = dict((stream.id, stream.recipient_id) for stream in streams) # type: Dict[int, int] recipients_map = {stream.id: stream.recipient_id for stream in streams} # type: Dict[int, int]
recipient_ids = [recipient_id for recipient_id in recipients_map.values()] # type: List[int] recipient_ids = [recipient_id for recipient_id in recipients_map.values()] # type: List[int]
stream_map = {} # type: Dict[int, Stream] stream_map = {} # type: Dict[int, Stream]
@ -4877,7 +4877,7 @@ def gather_subscriptions_helper(user_profile: UserProfile,
# Deactivated streams aren't in stream_hash. # Deactivated streams aren't in stream_hash.
streams = [stream_hash[sub["stream_id"]] for sub in sub_dicts streams = [stream_hash[sub["stream_id"]] for sub in sub_dicts
if sub["stream_id"] in stream_hash] if sub["stream_id"] in stream_hash]
streams_subscribed_map = dict((sub["stream_id"], sub["active"]) for sub in sub_dicts) streams_subscribed_map = {sub["stream_id"]: sub["active"] for sub in sub_dicts}
# Add never subscribed streams to streams_subscribed_map # Add never subscribed streams to streams_subscribed_map
streams_subscribed_map.update({stream['id']: False for stream in all_streams if stream not in streams}) streams_subscribed_map.update({stream['id']: False for stream in all_streams if stream not in streams})
@ -5577,7 +5577,7 @@ def check_attachment_reference_change(message: Message) -> bool:
# For a unsaved message edit (message.* has been updated, but not # For a unsaved message edit (message.* has been updated, but not
# saved to the database), adjusts Attachment data to correspond to # saved to the database), adjusts Attachment data to correspond to
# the new content. # the new content.
prev_attachments = set([a.path_id for a in message.attachment_set.all()]) prev_attachments = {a.path_id for a in message.attachment_set.all()}
new_attachments = set(message.potential_attachment_path_ids) new_attachments = set(message.potential_attachment_path_ids)
if new_attachments == prev_attachments: if new_attachments == prev_attachments:
@ -5652,7 +5652,7 @@ def try_update_realm_custom_profile_field(realm: Realm, field: CustomProfileFiel
notify_realm_custom_profile_fields(realm, 'update') notify_realm_custom_profile_fields(realm, 'update')
def try_reorder_realm_custom_profile_fields(realm: Realm, order: List[int]) -> None: def try_reorder_realm_custom_profile_fields(realm: Realm, order: List[int]) -> None:
order_mapping = dict((_[1], _[0]) for _ in enumerate(order)) order_mapping = {_[1]: _[0] for _ in enumerate(order)}
fields = CustomProfileField.objects.filter(realm=realm) fields = CustomProfileField.objects.filter(realm=realm)
for field in fields: for field in fields:
if field.id not in order_mapping: if field.id not in order_mapping:

View File

@ -10,8 +10,8 @@ from typing import Dict, Iterable, List
def alert_words_in_realm(realm: Realm) -> Dict[int, List[str]]: 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 = {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 = {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
@cache_with_key(realm_alert_words_automaton_cache_key, timeout=3600*24) @cache_with_key(realm_alert_words_automaton_cache_key, timeout=3600*24)
@ -25,7 +25,7 @@ def get_alert_word_automaton(realm: Realm) -> ahocorasick.Automaton:
(key, user_ids_for_alert_word) = alert_word_automaton.get(alert_word_lower) (key, user_ids_for_alert_word) = alert_word_automaton.get(alert_word_lower)
user_ids_for_alert_word.add(user_id) user_ids_for_alert_word.add(user_id)
else: else:
alert_word_automaton.add_word(alert_word_lower, (alert_word_lower, set([user_id]))) alert_word_automaton.add_word(alert_word_lower, (alert_word_lower, {user_id}))
alert_word_automaton.make_automaton() alert_word_automaton.make_automaton()
# If the kind is not AHOCORASICK after calling make_automaton, it means there is no key present # If the kind is not AHOCORASICK after calling make_automaton, it means there is no key present
# and hence we cannot call items on the automaton yet. To avoid it we return None for such cases # and hence we cannot call items on the automaton yet. To avoid it we return None for such cases

View File

@ -263,7 +263,7 @@ def list_of_tlds() -> List[str]:
# tlds-alpha-by-domain.txt comes from https://data.iana.org/TLD/tlds-alpha-by-domain.txt # tlds-alpha-by-domain.txt comes from https://data.iana.org/TLD/tlds-alpha-by-domain.txt
tlds_file = os.path.join(os.path.dirname(__file__), 'tlds-alpha-by-domain.txt') tlds_file = os.path.join(os.path.dirname(__file__), 'tlds-alpha-by-domain.txt')
tlds = [tld.lower().strip() for tld in open(tlds_file, 'r') tlds = [tld.lower().strip() for tld in open(tlds_file)
if tld not in blacklist and not tld[0].startswith('#')] if tld not in blacklist and not tld[0].startswith('#')]
tlds.sort(key=len, reverse=True) tlds.sort(key=len, reverse=True)
return tlds return tlds
@ -563,10 +563,10 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
# We strip leading '/' from relative URLs here to ensure # We strip leading '/' from relative URLs here to ensure
# consistency in what gets passed to /thumbnail # consistency in what gets passed to /thumbnail
url = url.lstrip('/') url = url.lstrip('/')
img.set("src", "/thumbnail?url={0}&size=thumbnail".format( img.set("src", "/thumbnail?url={}&size=thumbnail".format(
urllib.parse.quote(url, safe='') urllib.parse.quote(url, safe='')
)) ))
img.set('data-src-fullsize', "/thumbnail?url={0}&size=full".format( img.set('data-src-fullsize', "/thumbnail?url={}&size=full".format(
urllib.parse.quote(url, safe='') urllib.parse.quote(url, safe='')
)) ))
else: else:
@ -1187,7 +1187,7 @@ class Avatar(markdown.inlinepatterns.Pattern):
profile_id = user_dict['id'] profile_id = user_dict['id']
img.set('class', 'message_body_gravatar') img.set('class', 'message_body_gravatar')
img.set('src', '/avatar/{0}?s=30'.format(profile_id or email)) img.set('src', '/avatar/{}?s=30'.format(profile_id or email))
img.set('title', email) img.set('title', email)
img.set('alt', email) img.set('alt', email)
return img return img
@ -1713,9 +1713,9 @@ def possible_linked_stream_names(content: str) -> Set[str]:
class AlertWordsNotificationProcessor(markdown.preprocessors.Preprocessor): class AlertWordsNotificationProcessor(markdown.preprocessors.Preprocessor):
allowed_before_punctuation = set([' ', '\n', '(', '"', '.', ',', '\'', ';', '[', '*', '`', '>']) allowed_before_punctuation = {' ', '\n', '(', '"', '.', ',', '\'', ';', '[', '*', '`', '>'}
allowed_after_punctuation = set([' ', '\n', ')', '",', '?', ':', '.', ',', '\'', ';', ']', '!', allowed_after_punctuation = {' ', '\n', ')', '",', '?', ':', '.', ',', '\'', ';', ']', '!',
'*', '`']) '*', '`'}
def check_valid_start_position(self, content: str, index: int) -> bool: def check_valid_start_position(self, content: str, index: int) -> bool:
if index <= 0 or content[index] in self.allowed_before_punctuation: if index <= 0 or content[index] in self.allowed_before_punctuation:

View File

@ -30,7 +30,7 @@ class MarkdownArgumentsTableGenerator(Extension):
class APIArgumentsTablePreprocessor(Preprocessor): class APIArgumentsTablePreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None:
super(APIArgumentsTablePreprocessor, self).__init__(md) super().__init__(md)
self.base_path = config['base_path'] self.base_path = config['base_path']
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
@ -66,7 +66,7 @@ class APIArgumentsTablePreprocessor(Preprocessor):
if e.args != ('parameters',): if e.args != ('parameters',):
raise e raise e
else: else:
with open(filename, 'r') as fp: with open(filename) as fp:
json_obj = json.load(fp) json_obj = json.load(fp)
arguments = json_obj[doc_name] arguments = json_obj[doc_name]

View File

@ -278,7 +278,7 @@ class APICodeExamplesGenerator(Extension):
class APICodeExamplesPreprocessor(Preprocessor): class APICodeExamplesPreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None:
super(APICodeExamplesPreprocessor, self).__init__(md) super().__init__(md)
self.api_url = config['api_url'] self.api_url = config['api_url']
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:

View File

@ -1,4 +1,3 @@
from __future__ import print_function
import re import re
import os import os
from typing import Any, Dict, List from typing import Any, Dict, List
@ -42,7 +41,7 @@ class IncludeCustomPreprocessor(IncludePreprocessor):
os.path.join(self.base_path, filename) os.path.join(self.base_path, filename)
) )
try: try:
with open(filename, 'r', encoding=self.encoding) as r: with open(filename, encoding=self.encoding) as r:
text = r.readlines() text = r.readlines()
except Exception as e: except Exception as e:
print('Warning: could not find file {}. Error: {}'.format(filename, e)) print('Warning: could not find file {}. Error: {}'.format(filename, e))

View File

@ -15,7 +15,7 @@ class NestedCodeBlocksRenderer(Extension):
class NestedCodeBlocksRendererTreeProcessor(markdown.treeprocessors.Treeprocessor): class NestedCodeBlocksRendererTreeProcessor(markdown.treeprocessors.Treeprocessor):
def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None:
super(NestedCodeBlocksRendererTreeProcessor, self).__init__(md) super().__init__(md)
def run(self, root: Element) -> None: def run(self, root: Element) -> None:
code_tags = walk_tree_with_family(root, self.get_code_tags) code_tags = walk_tree_with_family(root, self.get_code_tags)

View File

@ -84,7 +84,7 @@ class TabbedSectionsGenerator(Extension):
class TabbedSectionsPreprocessor(Preprocessor): class TabbedSectionsPreprocessor(Preprocessor):
def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None: def __init__(self, md: markdown.Markdown, config: Dict[str, Any]) -> None:
super(TabbedSectionsPreprocessor, self).__init__(md) super().__init__(md)
def run(self, lines: List[str]) -> List[str]: def run(self, lines: List[str]) -> List[str]:
tab_section = self.parse_tabs(lines) tab_section = self.parse_tabs(lines)

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
import ujson import ujson

View File

@ -88,7 +88,7 @@ def bulk_set_users_or_streams_recipient_fields(model: Model,
object_ids = [obj.id for obj in objects] object_ids = [obj.id for obj in objects]
recipients = Recipient.objects.filter(type=recipient_type, type_id__in=object_ids) recipients = Recipient.objects.filter(type=recipient_type, type_id__in=object_ids)
objects_dict = dict((obj.id, obj) for obj in objects) objects_dict = {obj.id: obj for obj in objects}
for recipient in recipients: for recipient in recipients:
assert recipient.type == recipient_type assert recipient.type == recipient_type

View File

@ -87,7 +87,7 @@ def get_or_create_key_prefix() -> str:
# The file already exists # The file already exists
tries = 1 tries = 1
while tries < 10: while tries < 10:
with open(filename, 'r') as f: with open(filename) as f:
prefix = f.readline()[:-1] prefix = f.readline()[:-1]
if len(prefix) == 33: if len(prefix) == 33:
break break
@ -244,7 +244,7 @@ def cache_get_many(keys: List[str], cache_name: Optional[str]=None) -> Dict[str,
remote_cache_stats_start() remote_cache_stats_start()
ret = get_cache_backend(cache_name).get_many(keys) ret = get_cache_backend(cache_name).get_many(keys)
remote_cache_stats_finish() remote_cache_stats_finish()
return dict([(key[len(KEY_PREFIX):], value) for key, value in ret.items()]) return {key[len(KEY_PREFIX):]: value for key, value in ret.items()}
def safe_cache_get_many(keys: List[str], cache_name: Optional[str]=None) -> Dict[str, Any]: def safe_cache_get_many(keys: List[str], cache_name: Optional[str]=None) -> Dict[str, Any]:
"""Variant of cache_get_many that drops any keys that fail """Variant of cache_get_many that drops any keys that fail
@ -290,7 +290,7 @@ def safe_cache_set_many(items: Dict[str, Any], cache_name: Optional[str]=None,
good_keys, bad_keys = filter_good_and_bad_keys(list(items.keys())) good_keys, bad_keys = filter_good_and_bad_keys(list(items.keys()))
log_invalid_cache_keys(stack_trace, bad_keys) log_invalid_cache_keys(stack_trace, bad_keys)
good_items = dict((key, items[key]) for key in good_keys) good_items = {key: items[key] for key in good_keys}
return cache_set_many(good_items, cache_name, timeout) return cache_set_many(good_items, cache_name, timeout)
def cache_delete(key: str, cache_name: Optional[str]=None) -> None: def cache_delete(key: str, cache_name: Optional[str]=None) -> None:
@ -404,8 +404,8 @@ def generic_bulk_cached_fetch(
cached_objects[key] = item cached_objects[key] = item
if len(items_for_remote_cache) > 0: if len(items_for_remote_cache) > 0:
safe_cache_set_many(items_for_remote_cache) safe_cache_set_many(items_for_remote_cache)
return dict((object_id, cached_objects[cache_keys[object_id]]) for object_id in object_ids return {object_id: cached_objects[cache_keys[object_id]] for object_id in object_ids
if cache_keys[object_id] in cached_objects) if cache_keys[object_id] in cached_objects}
def preview_url_cache_key(url: str) -> str: def preview_url_cache_key(url: str) -> str:
return "preview_url:%s" % (make_safe_digest(url),) return "preview_url:%s" % (make_safe_digest(url),)
@ -425,7 +425,7 @@ def user_profile_by_email_cache_key(email: str) -> str:
return 'user_profile_by_email:%s' % (make_safe_digest(email.strip()),) return 'user_profile_by_email:%s' % (make_safe_digest(email.strip()),)
def user_profile_cache_key_id(email: str, realm_id: int) -> str: def user_profile_cache_key_id(email: str, realm_id: int) -> str:
return u"user_profile:%s:%s" % (make_safe_digest(email.strip()), realm_id,) return "user_profile:%s:%s" % (make_safe_digest(email.strip()), realm_id,)
def user_profile_cache_key(email: str, realm: 'Realm') -> str: def user_profile_cache_key(email: str, realm: 'Realm') -> str:
return user_profile_cache_key_id(email, realm.id) return user_profile_cache_key_id(email, realm.id)
@ -451,7 +451,7 @@ def realm_user_dicts_cache_key(realm_id: int) -> str:
return "realm_user_dicts:%s" % (realm_id,) return "realm_user_dicts:%s" % (realm_id,)
def get_realm_used_upload_space_cache_key(realm: 'Realm') -> str: def get_realm_used_upload_space_cache_key(realm: 'Realm') -> str:
return u'realm_used_upload_space:%s' % (realm.id,) return 'realm_used_upload_space:%s' % (realm.id,)
def active_user_ids_cache_key(realm_id: int) -> str: def active_user_ids_cache_key(realm_id: int) -> str:
return "active_user_ids:%s" % (realm_id,) return "active_user_ids:%s" % (realm_id,)

View File

@ -74,9 +74,9 @@ def bulk_fetch_display_recipients(recipient_tuples: Set[Tuple[int, int, int]]
for recipient in recipient_tuples for recipient in recipient_tuples
} }
stream_recipients = set( stream_recipients = {
recipient for recipient in recipient_tuples if recipient[1] == Recipient.STREAM recipient for recipient in recipient_tuples if recipient[1] == Recipient.STREAM
) }
personal_and_huddle_recipients = recipient_tuples - stream_recipients personal_and_huddle_recipients = recipient_tuples - stream_recipients
def stream_query_function(recipient_ids: List[int]) -> List[TinyStreamResult]: def stream_query_function(recipient_ids: List[int]) -> List[TinyStreamResult]:

View File

@ -309,7 +309,7 @@ def do_send_missedmessage_events_reply_in_zulip(user_profile: UserProfile,
if not user_profile.enable_offline_email_notifications: if not user_profile.enable_offline_email_notifications:
return return
recipients = set((msg['message'].recipient_id, msg['message'].topic_name()) for msg in missed_messages) recipients = {(msg['message'].recipient_id, msg['message'].topic_name()) for msg in missed_messages}
if len(recipients) != 1: if len(recipients) != 1:
raise ValueError( raise ValueError(
'All missed_messages must have the same recipient and topic %r' % 'All missed_messages must have the same recipient and topic %r' %
@ -359,7 +359,7 @@ def do_send_missedmessage_events_reply_in_zulip(user_profile: UserProfile,
'narrow_url': narrow_url, 'narrow_url': narrow_url,
}) })
senders = list(set(m['message'].sender for m in missed_messages)) senders = list({m['message'].sender for m in missed_messages})
if (missed_messages[0]['message'].recipient.type == Recipient.HUDDLE): if (missed_messages[0]['message'].recipient.type == Recipient.HUDDLE):
display_recipient = get_display_recipient(missed_messages[0]['message'].recipient) display_recipient = get_display_recipient(missed_messages[0]['message'].recipient)
# Make sure that this is a list of strings, not a string. # Make sure that this is a list of strings, not a string.
@ -383,9 +383,9 @@ def do_send_missedmessage_events_reply_in_zulip(user_profile: UserProfile,
elif (context['mention'] or context['stream_email_notify']): elif (context['mention'] or context['stream_email_notify']):
# Keep only the senders who actually mentioned the user # Keep only the senders who actually mentioned the user
if context['mention']: if context['mention']:
senders = list(set(m['message'].sender for m in missed_messages senders = list({m['message'].sender for m in missed_messages
if m['trigger'] == 'mentioned' or if m['trigger'] == 'mentioned' or
m['trigger'] == 'wildcard_mentioned')) m['trigger'] == 'wildcard_mentioned'})
message = missed_messages[0]['message'] message = missed_messages[0]['message']
stream = Stream.objects.only('id', 'name').get(id=message.recipient.type_id) stream = Stream.objects.only('id', 'name').get(id=message.recipient.type_id)
stream_header = "%s > %s" % (stream.name, message.topic_name()) stream_header = "%s > %s" % (stream.name, message.topic_name())

View File

@ -21,9 +21,9 @@ EMOTICON_CONVERSIONS = emoji_codes["emoticon_conversions"]
possible_emoticons = EMOTICON_CONVERSIONS.keys() possible_emoticons = EMOTICON_CONVERSIONS.keys()
possible_emoticon_regexes = (re.escape(emoticon) for emoticon in possible_emoticons) possible_emoticon_regexes = (re.escape(emoticon) for emoticon in possible_emoticons)
terminal_symbols = ',.;?!()\\[\\] "\'\\n\\t' # from composebox_typeahead.js terminal_symbols = ',.;?!()\\[\\] "\'\\n\\t' # from composebox_typeahead.js
emoticon_regex = ('(?<![^{0}])(?P<emoticon>('.format(terminal_symbols) emoticon_regex = ('(?<![^{}])(?P<emoticon>('.format(terminal_symbols)
+ ')|('.join(possible_emoticon_regexes) + ')|('.join(possible_emoticon_regexes)
+ '))(?![^{0}])'.format(terminal_symbols)) + '))(?![^{}])'.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: str) -> str: def translate_emoticons(text: str) -> str:

View File

@ -274,7 +274,7 @@ def sanity_check_output(data: TableData) -> None:
list(apps.get_app_config('two_factor').get_models(include_auto_created=True)) + list(apps.get_app_config('two_factor').get_models(include_auto_created=True)) +
list(apps.get_app_config('zerver').get_models(include_auto_created=True)) list(apps.get_app_config('zerver').get_models(include_auto_created=True))
) )
all_tables_db = set(model._meta.db_table for model in target_models) all_tables_db = {model._meta.db_table for model in target_models}
# These assertion statements will fire when we add a new database # These assertion statements will fire when we add a new database
# table that is not included in Zulip's data exports. Generally, # table that is not included in Zulip's data exports. Generally,
@ -783,9 +783,9 @@ def sanity_check_stream_data(response: TableData, config: Config, context: Conte
# complex to have a sanity check. # complex to have a sanity check.
return return
actual_streams = set([stream.name for stream in Stream.objects.filter( actual_streams = {stream.name for stream in Stream.objects.filter(
realm=response["zerver_realm"][0]['id'])]) realm=response["zerver_realm"][0]['id'])}
streams_in_response = set([stream['name'] for stream in response['zerver_stream']]) streams_in_response = {stream['name'] for stream in response['zerver_stream']}
if len(streams_in_response - actual_streams) > 0: if len(streams_in_response - actual_streams) > 0:
print("Error: Streams not present in the realm were exported:") print("Error: Streams not present in the realm were exported:")
@ -893,12 +893,12 @@ def fetch_huddle_objects(response: TableData, config: Config, context: Context)
realm = context['realm'] realm = context['realm']
assert config.parent is not None assert config.parent is not None
assert config.parent.table is not None assert config.parent.table is not None
user_profile_ids = set(r['id'] for r in response[config.parent.table]) user_profile_ids = {r['id'] for r in response[config.parent.table]}
# First we get all huddles involving someone in the realm. # First we get all huddles involving someone in the realm.
realm_huddle_subs = Subscription.objects.select_related("recipient").filter( realm_huddle_subs = Subscription.objects.select_related("recipient").filter(
recipient__type=Recipient.HUDDLE, user_profile__in=user_profile_ids) recipient__type=Recipient.HUDDLE, user_profile__in=user_profile_ids)
realm_huddle_recipient_ids = set(sub.recipient_id for sub in realm_huddle_subs) realm_huddle_recipient_ids = {sub.recipient_id for sub in realm_huddle_subs}
# Mark all Huddles whose recipient ID contains a cross-realm user. # Mark all Huddles whose recipient ID contains a cross-realm user.
unsafe_huddle_recipient_ids = set() unsafe_huddle_recipient_ids = set()
@ -914,8 +914,8 @@ def fetch_huddle_objects(response: TableData, config: Config, context: Context)
# exporting the users from this realm), at the cost of losing # exporting the users from this realm), at the cost of losing
# some of these cross-realm messages. # some of these cross-realm messages.
huddle_subs = [sub for sub in realm_huddle_subs if sub.recipient_id not in unsafe_huddle_recipient_ids] huddle_subs = [sub for sub in realm_huddle_subs if sub.recipient_id not in unsafe_huddle_recipient_ids]
huddle_recipient_ids = set(sub.recipient_id for sub in huddle_subs) huddle_recipient_ids = {sub.recipient_id for sub in huddle_subs}
huddle_ids = set(sub.recipient.type_id for sub in huddle_subs) huddle_ids = {sub.recipient.type_id for sub in huddle_subs}
huddle_subscription_dicts = make_raw(huddle_subs) huddle_subscription_dicts = make_raw(huddle_subs)
huddle_recipients = make_raw(Recipient.objects.filter(id__in=huddle_recipient_ids)) huddle_recipients = make_raw(Recipient.objects.filter(id__in=huddle_recipient_ids))
@ -953,7 +953,7 @@ def export_usermessages_batch(input_path: Path, output_path: Path,
batch of Message objects and adds the corresponding UserMessage batch of Message objects and adds the corresponding UserMessage
objects. (This is called by the export_usermessage_batch objects. (This is called by the export_usermessage_batch
management command).""" management command)."""
with open(input_path, "r") as input_file: with open(input_path) as input_file:
output = ujson.load(input_file) output = ujson.load(input_file)
message_ids = [item['id'] for item in output['zerver_message']] message_ids = [item['id'] for item in output['zerver_message']]
user_profile_ids = set(output['zerver_userprofile_ids']) user_profile_ids = set(output['zerver_userprofile_ids'])
@ -979,7 +979,7 @@ def export_partial_message_files(realm: Realm,
output_dir = tempfile.mkdtemp(prefix="zulip-export") output_dir = tempfile.mkdtemp(prefix="zulip-export")
def get_ids(records: List[Record]) -> Set[int]: def get_ids(records: List[Record]) -> Set[int]:
return set(x['id'] for x in records) return {x['id'] for x in records}
# Basic security rule: You can export everything either... # Basic security rule: You can export everything either...
# - sent by someone in your exportable_user_ids # - sent by someone in your exportable_user_ids
@ -1095,7 +1095,7 @@ def write_message_partial_for_query(realm: Realm, message_query: Any, dump_file_
while True: while True:
actual_query = message_query.filter(id__gt=min_id)[0:chunk_size] actual_query = message_query.filter(id__gt=min_id)[0:chunk_size]
message_chunk = make_raw(actual_query) message_chunk = make_raw(actual_query)
message_ids = set(m['id'] for m in message_chunk) message_ids = {m['id'] for m in message_chunk}
assert len(message_ids.intersection(all_message_ids)) == 0 assert len(message_ids.intersection(all_message_ids)) == 0
all_message_ids.update(message_ids) all_message_ids.update(message_ids)
@ -1431,7 +1431,7 @@ def do_write_stats_file_for_realm_export(output_dir: Path) -> None:
with open(stats_file, 'w') as f: with open(stats_file, 'w') as f:
for fn in fns: for fn in fns:
f.write(os.path.basename(fn) + '\n') f.write(os.path.basename(fn) + '\n')
with open(fn, 'r') as filename: with open(fn) as filename:
data = ujson.load(filename) data = ujson.load(filename)
for k in sorted(data): for k in sorted(data):
f.write('%5d %s\n' % (len(data[k]), k)) f.write('%5d %s\n' % (len(data[k]), k))
@ -1442,7 +1442,7 @@ def do_write_stats_file_for_realm_export(output_dir: Path) -> None:
for fn in [avatar_file, uploads_file]: for fn in [avatar_file, uploads_file]:
f.write(fn+'\n') f.write(fn+'\n')
with open(fn, 'r') as filename: with open(fn) as filename:
data = ujson.load(filename) data = ujson.load(filename)
f.write('%5d records\n' % (len(data),)) f.write('%5d records\n' % (len(data),))
f.write('\n') f.write('\n')
@ -1652,7 +1652,7 @@ def export_messages_single_user(user_profile: UserProfile, output_dir: Path,
actual_query = user_message_query.select_related( actual_query = user_message_query.select_related(
"message", "message__sending_client").filter(id__gt=min_id)[0:chunk_size] "message", "message__sending_client").filter(id__gt=min_id)[0:chunk_size]
user_message_chunk = [um for um in actual_query] user_message_chunk = [um for um in actual_query]
user_message_ids = set(um.id for um in user_message_chunk) user_message_ids = {um.id for um in user_message_chunk}
if len(user_message_chunk) == 0: if len(user_message_chunk) == 0:
break break

View File

@ -7,7 +7,7 @@ import os
from scripts.lib.zulip_tools import get_or_create_dev_uuid_var_path from scripts.lib.zulip_tools import get_or_create_dev_uuid_var_path
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") as infile:
config = ujson.load(infile) config = ujson.load(infile)
return config return config
@ -47,7 +47,7 @@ def parse_file(config: Dict[str, Any], gens: Dict[str, Any], corpus_file: str) -
paragraphs = [] # type: List[str] paragraphs = [] # type: List[str]
with open(corpus_file, "r") as infile: with open(corpus_file) as infile:
# OUR DATA: we need to separate the person talking and what they say # OUR DATA: we need to separate the person talking and what they say
paragraphs = remove_line_breaks(infile) paragraphs = remove_line_breaks(infile)
paragraphs = add_flair(paragraphs, gens) paragraphs = add_flair(paragraphs, gens)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import operator import operator
from django.conf import settings from django.conf import settings
@ -27,7 +26,7 @@ def with_language(string: str, language: str) -> str:
@lru_cache() @lru_cache()
def get_language_list() -> List[Dict[str, Any]]: def get_language_list() -> List[Dict[str, Any]]:
path = os.path.join(settings.DEPLOY_ROOT, 'locale', 'language_name_map.json') path = os.path.join(settings.DEPLOY_ROOT, 'locale', 'language_name_map.json')
with open(path, 'r') as reader: with open(path) as reader:
languages = ujson.load(reader) languages = ujson.load(reader)
return languages['name_map'] return languages['name_map']
@ -90,7 +89,7 @@ def get_language_translation_data(language: str) -> Dict[str, str]:
language = 'id_ID' language = 'id_ID'
path = os.path.join(settings.DEPLOY_ROOT, 'locale', language, 'translations.json') path = os.path.join(settings.DEPLOY_ROOT, 'locale', language, 'translations.json')
try: try:
with open(path, 'r') as reader: with open(path) as reader:
return ujson.load(reader) return ujson.load(reader)
except FileNotFoundError: except FileNotFoundError:
print('Translation for {} not found at {}'.format(language, path)) print('Translation for {} not found at {}'.format(language, path))

View File

@ -112,7 +112,7 @@ You can use the command list_realms to find ID of the realms in this server."""
if options["users"] is None: if options["users"] is None:
return [] return []
emails = set([email.strip() for email in options["users"].split(",")]) emails = {email.strip() for email in options["users"].split(",")}
user_profiles = [] user_profiles = []
for email in emails: for email in emails:
user_profiles.append(self.get_user(email, realm)) user_profiles.append(self.get_user(email, realm))

View File

@ -505,13 +505,13 @@ class MessageDict:
@staticmethod @staticmethod
def bulk_hydrate_recipient_info(objs: List[Dict[str, Any]]) -> None: def bulk_hydrate_recipient_info(objs: List[Dict[str, Any]]) -> None:
recipient_tuples = set( # We use set to eliminate duplicate tuples. recipient_tuples = { # We use set to eliminate duplicate tuples.
( (
obj['recipient_id'], obj['recipient_id'],
obj['recipient_type'], obj['recipient_type'],
obj['recipient_type_id'] obj['recipient_type_id']
) for obj in objs ) for obj in objs
) }
display_recipients = bulk_fetch_display_recipients(recipient_tuples) display_recipients = bulk_fetch_display_recipients(recipient_tuples)
for obj in objs: for obj in objs:

View File

@ -14,7 +14,7 @@ def read_stop_words() -> List[str]:
global stop_words_list global stop_words_list
if stop_words_list is None: if stop_words_list is None:
file_path = os.path.join(settings.DEPLOY_ROOT, "puppet/zulip/files/postgresql/zulip_english.stop") file_path = os.path.join(settings.DEPLOY_ROOT, "puppet/zulip/files/postgresql/zulip_english.stop")
with open(file_path, 'r') as f: with open(file_path) as f:
stop_words_list = f.read().splitlines() stop_words_list = f.read().splitlines()
return stop_words_list return stop_words_list

Some files were not shown because too many files have changed in this diff Show More