From 5428c5f296bbd6785a586dc23bf509ddcbb1b628 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Sat, 18 Apr 2020 15:59:12 -0700 Subject: [PATCH] typing: Convert function type annotations to Python 3 style. Generated by com2ann (slightly patched to avoid also converting assignment type annotations, which require Python 3.6), followed by some manual whitespace adjustment, and two fixes for use-before-define issues: - def set_zulip_client(self, zulipToJabberClient: ZulipToJabberBot) -> None: + def set_zulip_client(self, zulipToJabberClient: 'ZulipToJabberBot') -> None: -def init_from_options(options: Any, client: Optional[str] = None) -> Client: +def init_from_options(options: Any, client: Optional[str] = None) -> 'Client': Signed-off-by: Anders Kaseorg --- tools/lint | 12 +- tools/pep8.py | 6 +- tools/review | 24 +- .../bridge_with_irc/irc_mirror_backend.py | 35 +-- .../bridge_with_matrix/matrix_bridge.py | 47 ++- .../bridge_with_matrix/test_matrix.py | 30 +- .../codebase/zulip_codebase_mirror | 18 +- zulip/integrations/git/post-receive | 9 +- zulip/integrations/git/zulip_git_config.py | 6 +- .../google/get-google-credentials | 3 +- zulip/integrations/google/google-calendar | 9 +- zulip/integrations/hg/zulip_changegroup.py | 15 +- zulip/integrations/jabber/jabber_mirror.py | 3 +- .../jabber/jabber_mirror_backend.py | 69 ++--- zulip/integrations/log2zulip/log2zulip | 12 +- zulip/integrations/openshift/post_deploy | 6 +- .../openshift/zulip_openshift_config.py | 6 +- .../perforce/zulip_perforce_config.py | 3 +- zulip/integrations/rss/rss-bot | 30 +- zulip/integrations/svn/zulip_svn_config.py | 3 +- zulip/integrations/trac/zulip_trac.py | 24 +- zulip/integrations/twitter/twitter-bot | 3 +- zulip/integrations/zephyr/check-mirroring | 21 +- zulip/integrations/zephyr/sync-public-streams | 3 +- zulip/integrations/zephyr/zephyr_mirror.py | 6 +- .../zephyr/zephyr_mirror_backend.py | 108 +++---- zulip/setup.py | 6 +- zulip/tests/test_default_arguments.py | 6 +- zulip/zulip/__init__.py | 284 +++++++----------- zulip/zulip/api_examples.py | 3 +- zulip/zulip/examples/edit-stream | 3 +- zulip/zulip/examples/print-events | 3 +- zulip/zulip/examples/print-messages | 3 +- zulip/zulip/examples/welcome-message | 12 +- zulip/zulip/send.py | 6 +- zulip_bots/setup.py | 3 +- zulip_bots/zulip_bots/provision.py | 12 +- zulip_bots/zulip_bots/request_test_lib.py | 15 +- zulip_bots/zulip_bots/test_file_utils.py | 6 +- zulip_bots/zulip_bots/test_lib.py | 9 +- zulip_bots/zulip_bots/tests/test_run.py | 3 +- zulip_botserver/setup.py | 3 +- 42 files changed, 311 insertions(+), 577 deletions(-) diff --git a/tools/lint b/tools/lint index 38bd50d..4b9d873 100755 --- a/tools/lint +++ b/tools/lint @@ -12,8 +12,7 @@ EXCLUDED_FILES = [ 'zulip/integrations/perforce/git_p4.py', ] -def run(): - # type: () -> None +def run() -> None: parser = argparse.ArgumentParser() add_default_linter_arguments(parser) args = parser.parse_args() @@ -27,15 +26,13 @@ def run(): description="Static type checker for Python (config: mypy.ini)") @linter_config.lint - def custom_py(): - # type: () -> int + def custom_py() -> int: """Runs custom checks for python files (config: tools/linter_lib/custom_check.py)""" failed = python_rules.check(by_lang, verbose=args.verbose) return 1 if failed else 0 @linter_config.lint - def custom_nonpy(): - # type: () -> int + def custom_nonpy() -> int: """Runs custom checks for non-python files (config: tools/linter_lib/custom_check.py)""" failed = False for rule in non_py_rules: @@ -43,8 +40,7 @@ def run(): return 1 if failed else 0 @linter_config.lint - def pep8(): - # type: () -> int + def pep8() -> int: """Standard Python style linter on 50% of files (config: tools/linter_lib/pep8.py)""" failed = check_pep8(by_lang['py']) return 1 if failed else 0 diff --git a/tools/pep8.py b/tools/pep8.py index 75f0a4e..4373590 100644 --- a/tools/pep8.py +++ b/tools/pep8.py @@ -8,11 +8,9 @@ from zulint.printer import print_err, colors from typing import List -def check_pep8(files): - # type: (List[str]) -> bool +def check_pep8(files: List[str]) -> bool: - def run_pycodestyle(files, ignored_rules): - # type: (List[str], List[str]) -> bool + def run_pycodestyle(files: List[str], ignored_rules: List[str]) -> bool: failed = False color = next(colors) pep8 = subprocess.Popen( diff --git a/tools/review b/tools/review index 8201945..8ac489d 100755 --- a/tools/review +++ b/tools/review @@ -3,36 +3,30 @@ import subprocess import sys -def exit(message): - # type: (str) -> None +def exit(message: str) -> None: print('PROBLEM!') print(message) sys.exit(1) -def run(command): - # type: (str) -> None +def run(command: str) -> None: print('\n>>> ' + command) subprocess.check_call(command.split()) -def check_output(command): - # type: (str) -> str +def check_output(command: str) -> str: return subprocess.check_output(command.split()).decode('ascii') -def get_git_branch(): - # type: () -> str +def get_git_branch() -> str: command = 'git rev-parse --abbrev-ref HEAD' output = check_output(command) return output.strip() -def check_git_pristine(): - # type: () -> None +def check_git_pristine() -> None: command = 'git status --porcelain' output = check_output(command) if output.strip(): exit('Git is not pristine:\n' + output) -def ensure_on_clean_master(): - # type: () -> None +def ensure_on_clean_master() -> None: branch = get_git_branch() if branch != 'master': exit('You are still on a feature branch: %s' % (branch,)) @@ -40,8 +34,7 @@ def ensure_on_clean_master(): run('git fetch upstream master') run('git rebase upstream/master') -def create_pull_branch(pull_id): - # type: (int) -> None +def create_pull_branch(pull_id: int) -> None: run('git fetch upstream pull/%d/head' % (pull_id,)) run('git checkout -B review-%s FETCH_HEAD' % (pull_id,)) run('git rebase upstream/master') @@ -53,8 +46,7 @@ def create_pull_branch(pull_id): print(subprocess.check_output(['git', 'log', 'HEAD~..', '--pretty=format:Author: %an'])) -def review_pr(): - # type: () -> None +def review_pr() -> None: try: pull_id = int(sys.argv[1]) except Exception: diff --git a/zulip/integrations/bridge_with_irc/irc_mirror_backend.py b/zulip/integrations/bridge_with_irc/irc_mirror_backend.py index f87274c..5dfb3e5 100644 --- a/zulip/integrations/bridge_with_irc/irc_mirror_backend.py +++ b/zulip/integrations/bridge_with_irc/irc_mirror_backend.py @@ -9,9 +9,8 @@ from typing import Any, Dict class IRCBot(irc.bot.SingleServerIRCBot): reactor_class = AioReactor - def __init__(self, zulip_client, stream, topic, channel, - nickname, server, nickserv_password='', port=6667): - # type: (Any, str, str, irc.bot.Channel, str, str, str, int) -> None + def __init__(self, zulip_client: Any, stream: str, topic: str, channel: irc.bot.Channel, + nickname: str, server: str, nickserv_password: str = '', port: int = 6667) -> None: self.channel = channel # type: irc.bot.Channel self.zulip_client = zulip_client self.stream = stream @@ -23,13 +22,11 @@ class IRCBot(irc.bot.SingleServerIRCBot): # Initialize IRC bot after proper connection to Zulip server has been confirmed. irc.bot.SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname) - def zulip_sender(self, sender_string): - # type: (str) -> str + def zulip_sender(self, sender_string: str) -> str: nick = sender_string.split("!")[0] return nick + "@" + self.IRC_DOMAIN - def connect(self, *args, **kwargs): - # type: (*Any, **Any) -> None + def connect(self, *args: Any, **kwargs: Any) -> None: # Taken from # https://github.com/jaraco/irc/blob/master/irc/client_aio.py, # in particular the method of AioSimpleIRCClient @@ -38,8 +35,7 @@ class IRCBot(irc.bot.SingleServerIRCBot): ) print("Listening now. Please send an IRC message to verify operation") - def check_subscription_or_die(self): - # type: () -> None + def check_subscription_or_die(self) -> None: resp = self.zulip_client.list_subscriptions() if resp["result"] != "success": print("ERROR: %s" % (resp["msg"],)) @@ -49,19 +45,16 @@ class IRCBot(irc.bot.SingleServerIRCBot): print("The bot is not yet subscribed to stream '%s'. Please subscribe the bot to the stream first." % (self.stream,)) exit(1) - def on_nicknameinuse(self, c, e): - # type: (ServerConnection, Event) -> None + def on_nicknameinuse(self, c: ServerConnection, e: Event) -> None: c.nick(c.get_nickname().replace("_zulip", "__zulip")) - def on_welcome(self, c, e): - # type: (ServerConnection, Event) -> None + def on_welcome(self, c: ServerConnection, e: Event) -> None: if len(self.nickserv_password) > 0: msg = 'identify %s' % (self.nickserv_password,) c.privmsg('NickServ', msg) c.join(self.channel) - def forward_to_irc(msg): - # type: (Dict[str, Any]) -> None + def forward_to_irc(msg: Dict[str, Any]) -> None: not_from_zulip_bot = msg["sender_email"] != self.zulip_client.email if not not_from_zulip_bot: # Do not forward echo @@ -88,8 +81,7 @@ class IRCBot(irc.bot.SingleServerIRCBot): z2i = mp.Process(target=self.zulip_client.call_on_each_message, args=(forward_to_irc,)) z2i.start() - def on_privmsg(self, c, e): - # type: (ServerConnection, Event) -> None + def on_privmsg(self, c: ServerConnection, e: Event) -> None: content = e.arguments[0] sender = self.zulip_sender(e.source) if sender.endswith("_zulip@" + self.IRC_DOMAIN): @@ -103,8 +95,7 @@ class IRCBot(irc.bot.SingleServerIRCBot): "content": content, })) - def on_pubmsg(self, c, e): - # type: (ServerConnection, Event) -> None + def on_pubmsg(self, c: ServerConnection, e: Event) -> None: content = e.arguments[0] sender = self.zulip_sender(e.source) if sender.endswith("_zulip@" + self.IRC_DOMAIN): @@ -119,12 +110,10 @@ class IRCBot(irc.bot.SingleServerIRCBot): "content": "**{}**: {}".format(sender, content), })) - def on_dccmsg(self, c, e): - # type: (ServerConnection, Event) -> None + def on_dccmsg(self, c: ServerConnection, e: Event) -> None: c.privmsg("You said: " + e.arguments[0]) - def on_dccchat(self, c, e): - # type: (ServerConnection, Event) -> None + def on_dccchat(self, c: ServerConnection, e: Event) -> None: if len(e.arguments) != 2: return args = e.arguments[1].split() diff --git a/zulip/integrations/bridge_with_matrix/matrix_bridge.py b/zulip/integrations/bridge_with_matrix/matrix_bridge.py index bb0ba77..4ddee2d 100644 --- a/zulip/integrations/bridge_with_matrix/matrix_bridge.py +++ b/zulip/integrations/bridge_with_matrix/matrix_bridge.py @@ -34,8 +34,7 @@ class Bridge_FatalMatrixException(Exception): class Bridge_ZulipFatalException(Exception): pass -def matrix_login(matrix_client, matrix_config): - # type: (Any, Dict[str, Any]) -> None +def matrix_login(matrix_client: Any, matrix_config: Dict[str, Any]) -> None: try: matrix_client.login_with_password(matrix_config["username"], matrix_config["password"]) @@ -47,8 +46,7 @@ def matrix_login(matrix_client, matrix_config): except MissingSchema as exception: raise Bridge_FatalMatrixException("Bad URL format.") -def matrix_join_room(matrix_client, matrix_config): - # type: (Any, Dict[str, Any]) -> Any +def matrix_join_room(matrix_client: Any, matrix_config: Dict[str, Any]) -> Any: try: room = matrix_client.join_room(matrix_config["room_id"]) return room @@ -58,15 +56,17 @@ def matrix_join_room(matrix_client, matrix_config): else: raise Bridge_FatalMatrixException("Couldn't find room.") -def die(signal, frame): - # type: (int, FrameType) -> None +def die(signal: int, frame: FrameType) -> None: # We actually want to exit, so run os._exit (so as not to be caught and restarted) os._exit(1) -def matrix_to_zulip(zulip_client, zulip_config, matrix_config, no_noise): - # type: (zulip.Client, Dict[str, Any], Dict[str, Any], bool) -> Callable[[Any, Dict[str, Any]], None] - def _matrix_to_zulip(room, event): - # type: (Any, Dict[str, Any]) -> None +def matrix_to_zulip( + zulip_client: zulip.Client, + zulip_config: Dict[str, Any], + matrix_config: Dict[str, Any], + no_noise: bool, +) -> Callable[[Any, Dict[str, Any]], None]: + def _matrix_to_zulip(room: Any, event: Dict[str, Any]) -> None: """ Matrix -> Zulip """ @@ -95,8 +95,7 @@ def matrix_to_zulip(zulip_client, zulip_config, matrix_config, no_noise): return _matrix_to_zulip -def get_message_content_from_event(event, no_noise): - # type: (Dict[str, Any], bool) -> Optional[str] +def get_message_content_from_event(event: Dict[str, Any], no_noise: bool) -> Optional[str]: irc_nick = shorten_irc_nick(event['sender']) if event['type'] == "m.room.member": if no_noise: @@ -117,8 +116,7 @@ def get_message_content_from_event(event, no_noise): content = event['type'] return content -def shorten_irc_nick(nick): - # type: (str) -> str +def shorten_irc_nick(nick: str) -> str: """ Add nick shortner functions for specific IRC networks Eg: For freenode change '@freenode_user:matrix.org' to 'user' @@ -134,11 +132,9 @@ def shorten_irc_nick(nick): return match.group(1) return nick -def zulip_to_matrix(config, room): - # type: (Dict[str, Any], Any) -> Callable[[Dict[str, Any]], None] +def zulip_to_matrix(config: Dict[str, Any], room: Any) -> Callable[[Dict[str, Any]], None]: - def _zulip_to_matrix(msg): - # type: (Dict[str, Any]) -> None + def _zulip_to_matrix(msg: Dict[str, Any]) -> None: """ Zulip -> Matrix """ @@ -151,8 +147,7 @@ def zulip_to_matrix(config, room): room.send_text(matrix_text) return _zulip_to_matrix -def check_zulip_message_validity(msg, config): - # type: (Dict[str, Any], Dict[str, Any]) -> bool +def check_zulip_message_validity(msg: Dict[str, Any], config: Dict[str, Any]) -> bool: is_a_stream = msg["type"] == "stream" in_the_specified_stream = msg["display_recipient"] == config["stream"] at_the_specified_subject = msg["subject"] == config["topic"] @@ -164,8 +159,7 @@ def check_zulip_message_validity(msg, config): return True return False -def generate_parser(): - # type: () -> argparse.ArgumentParser +def generate_parser() -> argparse.ArgumentParser: description = """ Script to bridge between a topic in a Zulip stream, and a Matrix channel. @@ -190,8 +184,7 @@ def generate_parser(): help="Enable IRC join/leave events.") return parser -def read_configuration(config_file): - # type: (str) -> Dict[str, Dict[str, str]] +def read_configuration(config_file: str) -> Dict[str, Dict[str, str]]: config = configparser.ConfigParser() try: @@ -206,8 +199,7 @@ def read_configuration(config_file): return {section: dict(config[section]) for section in config.sections()} -def write_sample_config(target_path, zuliprc): - # type: (str, Optional[str]) -> None +def write_sample_config(target_path: str, zuliprc: Optional[str]) -> None: if os.path.exists(target_path): raise Bridge_ConfigException("Path '{}' exists; not overwriting existing file.".format(target_path)) @@ -248,8 +240,7 @@ def write_sample_config(target_path, zuliprc): with open(target_path, 'w') as target: sample.write(target) -def main(): - # type: () -> None +def main() -> None: signal.signal(signal.SIGINT, die) logging.basicConfig(level=logging.WARNING) diff --git a/zulip/integrations/bridge_with_matrix/test_matrix.py b/zulip/integrations/bridge_with_matrix/test_matrix.py index 6a0135f..11e74cf 100644 --- a/zulip/integrations/bridge_with_matrix/test_matrix.py +++ b/zulip/integrations/bridge_with_matrix/test_matrix.py @@ -36,20 +36,17 @@ topic = matrix """ @contextmanager -def new_temp_dir(): - # type: () -> Iterator[str] +def new_temp_dir() -> Iterator[str]: path = mkdtemp() yield path shutil.rmtree(path) class MatrixBridgeScriptTests(TestCase): - def output_from_script(self, options): - # type: (List[str]) -> List[str] + def output_from_script(self, options: List[str]) -> List[str]: popen = Popen(["python", script] + options, stdin=PIPE, stdout=PIPE, universal_newlines=True) return popen.communicate()[0].strip().split("\n") - def test_no_args(self): - # type: () -> None + def test_no_args(self) -> None: output_lines = self.output_from_script([]) expected_lines = [ "Options required: -c or --config to run, OR --write-sample-config.", @@ -58,8 +55,7 @@ class MatrixBridgeScriptTests(TestCase): for expected, output in zip(expected_lines, output_lines): self.assertIn(expected, output) - def test_help_usage_and_description(self): - # type: () -> None + def test_help_usage_and_description(self) -> None: output_lines = self.output_from_script(["-h"]) usage = "usage: {} [-h]".format(script_file) description = "Script to bridge" @@ -72,8 +68,7 @@ class MatrixBridgeScriptTests(TestCase): # Minimal description should be in the first line of the 2nd "paragraph" self.assertIn(description, output_lines[blank_lines[0] + 1]) - def test_write_sample_config(self): - # type: () -> None + def test_write_sample_config(self) -> None: with new_temp_dir() as tempdir: path = os.path.join(tempdir, sample_config_path) output_lines = self.output_from_script(["--write-sample-config", path]) @@ -82,8 +77,7 @@ class MatrixBridgeScriptTests(TestCase): with open(path) as sample_file: self.assertEqual(sample_file.read(), sample_config_text) - def test_write_sample_config_from_zuliprc(self): - # type: () -> None + def test_write_sample_config_from_zuliprc(self) -> None: zuliprc_template = ["[api]", "email={email}", "key={key}", "site={site}"] zulip_params = {'email': 'foo@bar', 'key': 'some_api_key', @@ -107,8 +101,7 @@ class MatrixBridgeScriptTests(TestCase): expected_lines[9] = 'site = {}'.format(zulip_params['site']) self.assertEqual(sample_lines, expected_lines[:-1]) - def test_detect_zuliprc_does_not_exist(self): - # type: () -> None + def test_detect_zuliprc_does_not_exist(self) -> None: with new_temp_dir() as tempdir: path = os.path.join(tempdir, sample_config_path) zuliprc_path = os.path.join(tempdir, "zuliprc") @@ -132,8 +125,7 @@ class MatrixBridgeZulipToMatrixTests(TestCase): subject=valid_zulip_config['topic'] ) - def test_zulip_message_validity_success(self): - # type: () -> None + def test_zulip_message_validity_success(self) -> None: zulip_config = self.valid_zulip_config msg = self.valid_msg # Ensure the test inputs are valid for success @@ -141,8 +133,7 @@ class MatrixBridgeZulipToMatrixTests(TestCase): self.assertTrue(check_zulip_message_validity(msg, zulip_config)) - def test_zulip_message_validity_failure(self): - # type: () -> None + def test_zulip_message_validity_failure(self) -> None: zulip_config = self.valid_zulip_config msg_wrong_stream = dict(self.valid_msg, display_recipient='foo') @@ -157,8 +148,7 @@ class MatrixBridgeZulipToMatrixTests(TestCase): msg_from_bot = dict(self.valid_msg, sender_email=zulip_config['email']) self.assertFalse(check_zulip_message_validity(msg_from_bot, zulip_config)) - def test_zulip_to_matrix(self): - # type: () -> None + def test_zulip_to_matrix(self) -> None: room = mock.MagicMock() zulip_config = self.valid_zulip_config send_msg = zulip_to_matrix(zulip_config, room) diff --git a/zulip/integrations/codebase/zulip_codebase_mirror b/zulip/integrations/codebase/zulip_codebase_mirror index 7aadbd8..469e814 100755 --- a/zulip/integrations/codebase/zulip_codebase_mirror +++ b/zulip/integrations/codebase/zulip_codebase_mirror @@ -50,8 +50,7 @@ while len(json_implementations): except ImportError: continue -def make_api_call(path): - # type: (str) -> Optional[List[Dict[str, Any]]] +def make_api_call(path: str) -> Optional[List[Dict[str, Any]]]: response = requests.get("https://api3.codebasehq.com/%s" % (path,), auth=(config.CODEBASE_API_USERNAME, config.CODEBASE_API_KEY), params={'raw': 'True'}, @@ -71,12 +70,10 @@ def make_api_call(path): logging.warn("Found non-success response status code: %s %s" % (response.status_code, response.text)) return None -def make_url(path): - # type: (str) -> str +def make_url(path: str) -> str: return "%s/%s" % (config.CODEBASE_ROOT_URL, path) -def handle_event(event): - # type: (Dict[str, Any]) -> None +def handle_event(event: Dict[str, Any]) -> None: event = event['event'] event_type = event['type'] actor_name = event['actor_name'] @@ -240,12 +237,10 @@ def handle_event(event): # the main run loop for this mirror script -def run_mirror(): - # type: () -> None +def run_mirror() -> None: # we should have the right (write) permissions on the resume file, as seen # in check_permissions, but it may still be empty or corrupted - def default_since(): - # type: () -> datetime + def default_since() -> datetime: return datetime.now(tz=pytz.utc) - timedelta(hours=config.CODEBASE_INITIAL_HISTORY_HOURS) try: @@ -282,8 +277,7 @@ def run_mirror(): logging.info("Shutting down Codebase mirror") # void function that checks the permissions of the files this script needs. -def check_permissions(): - # type: () -> None +def check_permissions() -> None: # check that the log file can be written if config.LOG_FILE: try: diff --git a/zulip/integrations/git/post-receive b/zulip/integrations/git/post-receive index c26d365..150c311 100755 --- a/zulip/integrations/git/post-receive +++ b/zulip/integrations/git/post-receive @@ -29,16 +29,14 @@ client = zulip.Client( api_key=config.ZULIP_API_KEY, client="ZulipGit/" + VERSION) -def git_repository_name(): - # type: () -> Text +def git_repository_name() -> Text: output = subprocess.check_output(["git", "rev-parse", "--is-bare-repository"]) if output.strip() == "true": return os.path.basename(os.getcwd())[:-len(".git")] else: return os.path.basename(os.path.dirname(os.getcwd())) -def git_commit_range(oldrev, newrev): - # type: (str, str) -> str +def git_commit_range(oldrev: str, newrev: str) -> str: log_cmd = ["git", "log", "--reverse", "--pretty=%aE %H %s", "%s..%s" % (oldrev, newrev)] commits = '' @@ -50,8 +48,7 @@ def git_commit_range(oldrev, newrev): commits += '!avatar(%s) %s\n' % (author_email, subject) return commits -def send_bot_message(oldrev, newrev, refname): - # type: (str, str, str) -> None +def send_bot_message(oldrev: str, newrev: str, refname: str) -> None: repo_name = git_repository_name() branch = refname.replace('refs/heads/', '') destination = config.commit_notice_destination(repo_name, branch, newrev) diff --git a/zulip/integrations/git/zulip_git_config.py b/zulip/integrations/git/zulip_git_config.py index 2d31266..15a9383 100644 --- a/zulip/integrations/git/zulip_git_config.py +++ b/zulip/integrations/git/zulip_git_config.py @@ -23,8 +23,7 @@ ZULIP_API_KEY = "0123456789abcdef0123456789abcdef" # * stream "commits" # * topic "master" # And similarly for branch "test-post-receive" (for use when testing). -def commit_notice_destination(repo, branch, commit): - # type: (Text, Text, Text) -> Optional[Dict[Text, Text]] +def commit_notice_destination(repo: Text, branch: Text, commit: Text) -> Optional[Dict[Text, Text]]: if branch in ["master", "test-post-receive"]: return dict(stream = STREAM_NAME, subject = "%s" % (branch,)) @@ -37,8 +36,7 @@ def commit_notice_destination(repo, branch, commit): # graphical repository viewer, e.g. # # return '!avatar(%s) [%s](https://example.com/commits/%s)\n' % (author, subject, commit_id) -def format_commit_message(author, subject, commit_id): - # type: (Text, Text, Text) -> Text +def format_commit_message(author: Text, subject: Text, commit_id: Text) -> Text: return '!avatar(%s) %s\n' % (author, subject) ## If properly installed, the Zulip API should be in your import diff --git a/zulip/integrations/google/get-google-credentials b/zulip/integrations/google/get-google-credentials index e04cb18..e17becb 100644 --- a/zulip/integrations/google/get-google-credentials +++ b/zulip/integrations/google/get-google-credentials @@ -26,8 +26,7 @@ CLIENT_SECRET_FILE = 'client_secret.json' APPLICATION_NAME = 'Zulip Calendar Bot' HOME_DIR = os.path.expanduser('~') -def get_credentials(): - # type: () -> client.Credentials +def get_credentials() -> client.Credentials: """Gets valid user credentials from storage. If nothing has been stored, or if the stored credentials are invalid, diff --git a/zulip/integrations/google/google-calendar b/zulip/integrations/google/google-calendar index cf6ec3c..72f2508 100755 --- a/zulip/integrations/google/google-calendar +++ b/zulip/integrations/google/google-calendar @@ -79,8 +79,7 @@ if not (options.zulip_email): zulip_client = zulip.init_from_options(options) -def get_credentials(): - # type: () -> client.Credentials +def get_credentials() -> client.Credentials: """Gets valid user credentials from storage. If nothing has been stored, or if the stored credentials are invalid, @@ -104,8 +103,7 @@ def get_credentials(): logging.error("Run the get-google-credentials script from this directory first.") -def populate_events(): - # type: () -> Optional[None] +def populate_events() -> Optional[None]: global events credentials = get_credentials() @@ -145,8 +143,7 @@ def populate_events(): events.append((event["id"], start, "(No Title)")) -def send_reminders(): - # type: () -> Optional[None] +def send_reminders() -> Optional[None]: global sent messages = [] diff --git a/zulip/integrations/hg/zulip_changegroup.py b/zulip/integrations/hg/zulip_changegroup.py index 91845e6..4f6f357 100755 --- a/zulip/integrations/hg/zulip_changegroup.py +++ b/zulip/integrations/hg/zulip_changegroup.py @@ -12,8 +12,7 @@ from mercurial import ui, repository as repo VERSION = "0.9" -def format_summary_line(web_url, user, base, tip, branch, node): - # type: (str, str, int, int, str, Text) -> Text +def format_summary_line(web_url: str, user: str, base: int, tip: int, branch: str, node: Text) -> Text: """ Format the first line of the message, which contains summary information about the changeset and links to the changelog if a @@ -38,8 +37,7 @@ def format_summary_line(web_url, user, base, tip, branch, node): user=user, commits=formatted_commit_count, branch=branch, tip=tip, node=node[:12]) -def format_commit_lines(web_url, repo, base, tip): - # type: (str, repo, int, int) -> str +def format_commit_lines(web_url: str, repo: repo, base: int, tip: int) -> str: """ Format the per-commit information for the message, including the one-line commit summary and a link to the diff if a web URL has been configured: @@ -64,8 +62,7 @@ def format_commit_lines(web_url, repo, base, tip): return "\n".join(summary for summary in commit_summaries) -def send_zulip(email, api_key, site, stream, subject, content): - # type: (str, str, str, str, str, Text) -> None +def send_zulip(email: str, api_key: str, site: str, stream: str, subject: str, content: Text) -> None: """ Send a message to Zulip using the provided credentials, which should be for a bot in most cases. @@ -83,8 +80,7 @@ def send_zulip(email, api_key, site, stream, subject, content): client.send_message(message_data) -def get_config(ui, item): - # type: (ui, str) -> str +def get_config(ui: ui, item: str) -> str: try: # config returns configuration value. return ui.config('zulip', item) @@ -92,8 +88,7 @@ def get_config(ui, item): ui.warn("Zulip: Could not find required item {} in hg config.".format(item)) sys.exit(1) -def hook(ui, repo, **kwargs): - # type: (ui, repo, **Text) -> None +def hook(ui: ui, repo: repo, **kwargs: Text) -> None: """ Invoked by configuring a [hook] entry in .hg/hgrc. """ diff --git a/zulip/integrations/jabber/jabber_mirror.py b/zulip/integrations/jabber/jabber_mirror.py index eb59e6d..1fdb7f3 100755 --- a/zulip/integrations/jabber/jabber_mirror.py +++ b/zulip/integrations/jabber/jabber_mirror.py @@ -9,8 +9,7 @@ from types import FrameType from typing import Any from zulip import RandomExponentialBackoff -def die(signal, frame): - # type: (int, FrameType) -> None +def die(signal: int, frame: FrameType) -> None: """We actually want to exit, so run os._exit (so as not to be caught and restarted)""" os._exit(1) diff --git a/zulip/integrations/jabber/jabber_mirror_backend.py b/zulip/integrations/jabber/jabber_mirror_backend.py index f5d5d50..8981ee3 100755 --- a/zulip/integrations/jabber/jabber_mirror_backend.py +++ b/zulip/integrations/jabber/jabber_mirror_backend.py @@ -57,23 +57,19 @@ from typing import Any, Callable __version__ = "1.1" -def room_to_stream(room): - # type: (str) -> str +def room_to_stream(room: str) -> str: return room + "/xmpp" -def stream_to_room(stream): - # type: (str) -> str +def stream_to_room(stream: str) -> str: return stream.lower().rpartition("/xmpp")[0] -def jid_to_zulip(jid): - # type: (JID) -> str +def jid_to_zulip(jid: JID) -> str: suffix = '' if not jid.username.endswith("-bot"): suffix = options.zulip_email_suffix return "%s%s@%s" % (jid.username, suffix, options.zulip_domain) -def zulip_to_jid(email, jabber_domain): - # type: (str, str) -> JID +def zulip_to_jid(email: str, jabber_domain: str) -> JID: jid = JID(email, domain=jabber_domain) if (options.zulip_email_suffix and options.zulip_email_suffix in jid.username and @@ -82,8 +78,7 @@ def zulip_to_jid(email, jabber_domain): return jid class JabberToZulipBot(ClientXMPP): - def __init__(self, jid, password, rooms): - # type: (JID, str, List[str]) -> None + def __init__(self, jid: JID, password: str, rooms: List[str]) -> None: if jid.resource: self.nick = jid.resource else: @@ -100,19 +95,16 @@ class JabberToZulipBot(ClientXMPP): self.register_plugin('xep_0045') # Jabber chatrooms self.register_plugin('xep_0199') # XMPP Ping - def set_zulip_client(self, zulipToJabberClient): - # type: (ZulipToJabberBot) -> None + def set_zulip_client(self, zulipToJabberClient: 'ZulipToJabberBot') -> None: self.zulipToJabber = zulipToJabberClient - def session_start(self, event): - # type: (Dict[str, Any]) -> None + def session_start(self, event: Dict[str, Any]) -> None: self.get_roster() self.send_presence() for room in self.rooms_to_join: self.join_muc(room) - def join_muc(self, room): - # type: (str) -> None + def join_muc(self, room: str) -> None: if room in self.rooms: return logging.debug("Joining " + room) @@ -137,8 +129,7 @@ class JabberToZulipBot(ClientXMPP): else: logging.error("Could not configure room: " + str(muc_jid)) - def leave_muc(self, room): - # type: (str) -> None + def leave_muc(self, room: str) -> None: if room not in self.rooms: return logging.debug("Leaving " + room) @@ -146,8 +137,7 @@ class JabberToZulipBot(ClientXMPP): muc_jid = JID(local=room, domain=options.conference_domain) self.plugin['xep_0045'].leaveMUC(muc_jid, self.nick) - def message(self, msg): - # type: (JabberMessage) -> Any + def message(self, msg: JabberMessage) -> Any: try: if msg["type"] == "groupchat": return self.group(msg) @@ -159,8 +149,7 @@ class JabberToZulipBot(ClientXMPP): except Exception: logging.exception("Error forwarding Jabber => Zulip") - def private(self, msg): - # type: (JabberMessage) -> None + def private(self, msg: JabberMessage) -> None: if options.mode == 'public' or msg['thread'] == '\u1FFFE': return sender = jid_to_zulip(msg["from"]) @@ -176,8 +165,7 @@ class JabberToZulipBot(ClientXMPP): if ret.get("result") != "success": logging.error(str(ret)) - def group(self, msg): - # type: (JabberMessage) -> None + def group(self, msg: JabberMessage) -> None: if options.mode == 'personal' or msg["thread"] == '\u1FFFE': return @@ -204,8 +192,7 @@ class JabberToZulipBot(ClientXMPP): if ret.get("result") != "success": logging.error(str(ret)) - def nickname_to_jid(self, room, nick): - # type: (str, str) -> JID + def nickname_to_jid(self, room: str, nick: str) -> JID: jid = self.plugin['xep_0045'].getJidProperty(room, nick, "jid") if (jid is None or jid == ''): return JID(local=nick.replace(' ', ''), domain=self.boundjid.domain) @@ -213,17 +200,14 @@ class JabberToZulipBot(ClientXMPP): return jid class ZulipToJabberBot: - def __init__(self, zulip_client): - # type: (Client) -> None + def __init__(self, zulip_client: Client) -> None: self.client = zulip_client self.jabber = None # type: Optional[JabberToZulipBot] - def set_jabber_client(self, client): - # type: (JabberToZulipBot) -> None + def set_jabber_client(self, client: JabberToZulipBot) -> None: self.jabber = client - def process_event(self, event): - # type: (Dict[str, Any]) -> None + def process_event(self, event: Dict[str, Any]) -> None: if event['type'] == 'message': message = event["message"] if message['sender_email'] != self.client.email: @@ -241,8 +225,7 @@ class ZulipToJabberBot: elif event['type'] == 'stream': self.process_stream(event) - def stream_message(self, msg): - # type: (Dict[str, str]) -> None + def stream_message(self, msg: Dict[str, str]) -> None: assert(self.jabber is not None) stream = msg['display_recipient'] if not stream.endswith("/xmpp"): @@ -257,8 +240,7 @@ class ZulipToJabberBot: outgoing['thread'] = '\u1FFFE' outgoing.send() - def private_message(self, msg): - # type: (Dict[str, Any]) -> None + def private_message(self, msg: Dict[str, Any]) -> None: assert(self.jabber is not None) for recipient in msg['display_recipient']: if recipient["email"] == self.client.email: @@ -274,8 +256,7 @@ class ZulipToJabberBot: outgoing['thread'] = '\u1FFFE' outgoing.send() - def process_subscription(self, event): - # type: (Dict[str, Any]) -> None + def process_subscription(self, event: Dict[str, Any]) -> None: assert(self.jabber is not None) if event['op'] == 'add': streams = [s['name'].lower() for s in event['subscriptions']] @@ -288,8 +269,7 @@ class ZulipToJabberBot: for stream in streams: self.jabber.leave_muc(stream_to_room(stream)) - def process_stream(self, event): - # type: (Dict[str, Any]) -> None + def process_stream(self, event: Dict[str, Any]) -> None: assert(self.jabber is not None) if event['op'] == 'occupy': streams = [s['name'].lower() for s in event['streams']] @@ -302,10 +282,8 @@ class ZulipToJabberBot: for stream in streams: self.jabber.leave_muc(stream_to_room(stream)) -def get_rooms(zulipToJabber): - # type: (ZulipToJabberBot) -> List[str] - def get_stream_infos(key, method): - # type: (str, Callable[[], Dict[str, Any]]) -> Any +def get_rooms(zulipToJabber: ZulipToJabberBot) -> List[str]: + def get_stream_infos(key: str, method: Callable[[], Dict[str, Any]]) -> Any: ret = method() if ret.get("result") != "success": logging.error(str(ret)) @@ -324,8 +302,7 @@ def get_rooms(zulipToJabber): rooms.append(stream_to_room(stream)) return rooms -def config_error(msg): - # type: (str) -> None +def config_error(msg: str) -> None: sys.stderr.write("%s\n" % (msg,)) sys.exit(2) diff --git a/zulip/integrations/log2zulip/log2zulip b/zulip/integrations/log2zulip/log2zulip index 1a5c184..166a933 100755 --- a/zulip/integrations/log2zulip/log2zulip +++ b/zulip/integrations/log2zulip/log2zulip @@ -31,8 +31,7 @@ from typing import List temp_dir = "/var/tmp/" if os.name == "posix" else tempfile.gettempdir() -def mkdir_p(path): - # type: (str) -> None +def mkdir_p(path: str) -> None: # Python doesn't have an analog to `mkdir -p` < Python 3.2. try: os.makedirs(path) @@ -42,8 +41,7 @@ def mkdir_p(path): else: raise -def send_log_zulip(file_name, count, lines, extra=""): - # type: (str, int, List[str], str) -> None +def send_log_zulip(file_name: str, count: int, lines: List[str], extra: str = "") -> None: content = "%s new errors%s:\n```\n%s\n```" % (count, extra, "\n".join(lines)) zulip_client.send_message({ "type": "stream", @@ -52,8 +50,7 @@ def send_log_zulip(file_name, count, lines, extra=""): "content": content, }) -def process_lines(raw_lines, file_name): - # type: (List[str], str) -> None +def process_lines(raw_lines: List[str], file_name: str) -> None: lines = [] for line in raw_lines: # Add any filtering or modification code here @@ -68,8 +65,7 @@ def process_lines(raw_lines, file_name): else: send_log_zulip(file_name, len(lines), lines) -def process_logs(): - # type: () -> None +def process_logs() -> None: data_file_path = os.path.join(temp_dir, "log2zulip.state") mkdir_p(os.path.dirname(data_file_path)) if not os.path.exists(data_file_path): diff --git a/zulip/integrations/openshift/post_deploy b/zulip/integrations/openshift/post_deploy index cbeea7a..6458d81 100755 --- a/zulip/integrations/openshift/post_deploy +++ b/zulip/integrations/openshift/post_deploy @@ -21,8 +21,7 @@ client = zulip.Client( api_key=config.ZULIP_API_KEY, client='ZulipOpenShift/' + VERSION) -def get_deployment_details(): - # type: () -> Dict[str, str] +def get_deployment_details() -> Dict[str, str]: # "gear deployments" output example: # Activation time - Deployment ID - Git Ref - Git SHA1 # 2017-01-07 15:40:30 -0500 - 9e2b7143 - master - b9ce57c - ACTIVE @@ -34,8 +33,7 @@ def get_deployment_details(): branch=splits[2], commit_id=splits[3]) -def send_bot_message(deployment): - # type: (Dict[str, str]) -> None +def send_bot_message(deployment: Dict[str, str]) -> None: destination = config.deployment_notice_destination(deployment['branch']) if destination is None: # No message should be sent diff --git a/zulip/integrations/openshift/zulip_openshift_config.py b/zulip/integrations/openshift/zulip_openshift_config.py index 0b86321..2de32c5 100755 --- a/zulip/integrations/openshift/zulip_openshift_config.py +++ b/zulip/integrations/openshift/zulip_openshift_config.py @@ -19,8 +19,7 @@ ZULIP_API_KEY = '0123456789abcdef0123456789abcdef' # * stream "deployments" # * topic "master" # And similarly for branch "test-post-receive" (for use when testing). -def deployment_notice_destination(branch): - # type: (str) -> Optional[Dict[str, Text]] +def deployment_notice_destination(branch: str) -> Optional[Dict[str, Text]]: if branch in ['master', 'test-post-receive']: return dict(stream = 'deployments', subject = '%s' % (branch,)) @@ -40,8 +39,7 @@ def deployment_notice_destination(branch): # * dep_id = deployment id # * dep_time = deployment timestamp def format_deployment_message( - app_name='', url='', branch='', commit_id='', dep_id='', dep_time=''): - # type: (str, str, str, str, str, str) -> str + app_name: str = '', url: str = '', branch: str = '', commit_id: str = '', dep_id: str = '', dep_time: str = '') -> str: return 'Deployed commit `%s` (%s) in [%s](%s)' % ( commit_id, branch, app_name, url) diff --git a/zulip/integrations/perforce/zulip_perforce_config.py b/zulip/integrations/perforce/zulip_perforce_config.py index 22884d8..cb02ab8 100644 --- a/zulip/integrations/perforce/zulip_perforce_config.py +++ b/zulip/integrations/perforce/zulip_perforce_config.py @@ -28,8 +28,7 @@ P4_WEB = None # "master-plan" and "secret" subdirectories of //depot/ to: # * stream "depot_subdirectory-commits" # * subject "change_root" -def commit_notice_destination(path, changelist): - # type: (Text, int) -> Optional[Dict[Text, Text]] +def commit_notice_destination(path: Text, changelist: int) -> Optional[Dict[Text, Text]]: dirs = path.split('/') if len(dirs) >= 4 and dirs[3] not in ("*", "..."): directory = dirs[3] diff --git a/zulip/integrations/rss/rss-bot b/zulip/integrations/rss/rss-bot index 3dc1128..b417d20 100755 --- a/zulip/integrations/rss/rss-bot +++ b/zulip/integrations/rss/rss-bot @@ -75,8 +75,7 @@ parser.add_argument('--math', opts = parser.parse_args() # type: Any -def mkdir_p(path): - # type: (str) -> None +def mkdir_p(path: str) -> None: # Python doesn't have an analog to `mkdir -p` < Python 3.2. try: os.makedirs(path) @@ -105,53 +104,44 @@ logger = logging.getLogger(__name__) # type: logging.Logger logger.setLevel(logging.DEBUG) logger.addHandler(file_handler) -def log_error_and_exit(error): - # type: (str) -> None +def log_error_and_exit(error: str) -> None: logger.error(error) logger.error(usage) exit(1) class MLStripper(HTMLParser): - def __init__(self): - # type: () -> None + def __init__(self) -> None: self.reset() self.fed = [] # type: List[str] - def handle_data(self, data): - # type: (str) -> None + def handle_data(self, data: str) -> None: self.fed.append(data) - def get_data(self): - # type: () -> str + def get_data(self) -> str: return ''.join(self.fed) -def strip_tags(html): - # type: (str) -> str +def strip_tags(html: str) -> str: stripper = MLStripper() stripper.feed(html) return stripper.get_data() -def compute_entry_hash(entry): - # type: (Dict[str, Any]) -> str +def compute_entry_hash(entry: Dict[str, Any]) -> str: entry_time = entry.get("published", entry.get("updated")) entry_id = entry.get("id", entry.get("link")) return hashlib.md5(entry_id + str(entry_time)).hexdigest() -def unwrap_text(body): - # type: (str) -> str +def unwrap_text(body: str) -> str: # Replace \n by space if it is preceded and followed by a non-\n. # Example: '\na\nb\nc\n\nd\n' -> '\na b c\n\nd\n' return re.sub('(?<=[^\n])\n(?=[^\n])', ' ', body) -def elide_subject(subject): - # type: (str) -> str +def elide_subject(subject: str) -> str: MAX_TOPIC_LENGTH = 60 if len(subject) > MAX_TOPIC_LENGTH: subject = subject[:MAX_TOPIC_LENGTH - 3].rstrip() + '...' return subject -def send_zulip(entry, feed_name): - # type: (Any, str) -> Dict[str, Any] +def send_zulip(entry: Any, feed_name: str) -> Dict[str, Any]: body = entry.summary # type: str if opts.unwrap: body = unwrap_text(body) diff --git a/zulip/integrations/svn/zulip_svn_config.py b/zulip/integrations/svn/zulip_svn_config.py index ffd88cf..804d5f0 100644 --- a/zulip/integrations/svn/zulip_svn_config.py +++ b/zulip/integrations/svn/zulip_svn_config.py @@ -18,8 +18,7 @@ ZULIP_API_KEY = "0123456789abcdef0123456789abcdef" # and "my-super-secret-repository" repos to # * stream "commits" # * topic "branch_name" -def commit_notice_destination(path, commit): - # type: (Text, Text) -> Optional[Dict[Text, Text]] +def commit_notice_destination(path: Text, commit: Text) -> Optional[Dict[Text, Text]]: repo = path.split('/')[-1] if repo not in ["evil-master-plan", "my-super-secret-repository"]: return dict(stream = "commits", diff --git a/zulip/integrations/trac/zulip_trac.py b/zulip/integrations/trac/zulip_trac.py index 9d2d4cc..6afa913 100644 --- a/zulip/integrations/trac/zulip_trac.py +++ b/zulip/integrations/trac/zulip_trac.py @@ -31,26 +31,21 @@ client = zulip.Client( api_key=config.ZULIP_API_KEY, client="ZulipTrac/" + VERSION) -def markdown_ticket_url(ticket, heading="ticket"): - # type: (Any, str) -> str +def markdown_ticket_url(ticket: Any, heading: str = "ticket") -> str: return "[%s #%s](%s/%s)" % (heading, ticket.id, config.TRAC_BASE_TICKET_URL, ticket.id) -def markdown_block(desc): - # type: (str) -> str +def markdown_block(desc: str) -> str: return "\n\n>" + "\n> ".join(desc.split("\n")) + "\n" -def truncate(string, length): - # type: (str, int) -> str +def truncate(string: str, length: int) -> str: if len(string) <= length: return string return string[:length - 3] + "..." -def trac_subject(ticket): - # type: (Any) -> str +def trac_subject(ticket: Any) -> str: return truncate("#%s: %s" % (ticket.id, ticket.values.get("summary")), 60) -def send_update(ticket, content): - # type: (Any, str) -> None +def send_update(ticket: Any, content: str) -> None: client.send_message({ "type": "stream", "to": config.STREAM_FOR_NOTIFICATIONS, @@ -61,8 +56,7 @@ def send_update(ticket, content): class ZulipPlugin(Component): implements(ITicketChangeListener) - def ticket_created(self, ticket): - # type: (Any) -> None + def ticket_created(self, ticket: Any) -> None: """Called when a ticket is created.""" content = "%s created %s in component **%s**, priority **%s**:\n" % \ (ticket.values.get("reporter"), markdown_ticket_url(ticket), @@ -74,8 +68,7 @@ class ZulipPlugin(Component): content += "%s" % (markdown_block(ticket.values.get("description")),) send_update(ticket, content) - def ticket_changed(self, ticket, comment, author, old_values): - # type: (Any, str, str, Dict[str, Any]) -> None + def ticket_changed(self, ticket: Any, comment: str, author: str, old_values: Dict[str, Any]) -> None: """Called when a ticket is modified. `old_values` is a dictionary containing the previous values of the @@ -106,8 +99,7 @@ class ZulipPlugin(Component): send_update(ticket, content) - def ticket_deleted(self, ticket): - # type: (Any) -> None + def ticket_deleted(self, ticket: Any) -> None: """Called when a ticket is deleted.""" content = "%s was deleted." % markdown_ticket_url(ticket, heading="Ticket") send_update(ticket, content) diff --git a/zulip/integrations/twitter/twitter-bot b/zulip/integrations/twitter/twitter-bot index eb04ec5..21f094b 100755 --- a/zulip/integrations/twitter/twitter-bot +++ b/zulip/integrations/twitter/twitter-bot @@ -66,8 +66,7 @@ Make sure to go the application you created and click "create my access token" as well. Fill in the values displayed. """ -def write_config(config, configfile_path): - # type: (ConfigParser, str) -> None +def write_config(config: ConfigParser, configfile_path: str) -> None: with open(configfile_path, 'w') as configfile: config.write(configfile) diff --git a/zulip/integrations/zephyr/check-mirroring b/zulip/integrations/zephyr/check-mirroring index a1d3449..315cbd8 100755 --- a/zulip/integrations/zephyr/check-mirroring +++ b/zulip/integrations/zephyr/check-mirroring @@ -82,8 +82,7 @@ else: ("tabbott-nagios-test", "a"), ] -def print_status_and_exit(status): - # type: (int) -> None +def print_status_and_exit(status: int) -> None: # The output of this script is used by Nagios. Various outputs, # e.g. true success and punting due to a SERVNAK, result in a @@ -92,8 +91,7 @@ def print_status_and_exit(status): print(status) sys.exit(status) -def send_zulip(message): - # type: (Dict[str, str]) -> None +def send_zulip(message: Dict[str, str]) -> None: result = zulip_client.send_message(message) if result["result"] != "success": logger.error("Error sending zulip, args were:") @@ -102,8 +100,7 @@ def send_zulip(message): print_status_and_exit(1) # Returns True if and only if we "Detected server failure" sending the zephyr. -def send_zephyr(zwrite_args, content): - # type: (List[str], str) -> bool +def send_zephyr(zwrite_args: List[str], content: str) -> bool: p = subprocess.Popen(zwrite_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate(input=content.encode("utf-8")) @@ -167,16 +164,14 @@ if not actually_subscribed: # Prepare keys zhkeys = {} # type: Dict[str, Tuple[str, str]] hzkeys = {} # type: Dict[str, Tuple[str, str]] -def gen_key(key_dict): - # type: (Dict[str, Any]) -> str +def gen_key(key_dict: Dict[str, Any]) -> str: bits = str(random.getrandbits(32)) while bits in key_dict: # Avoid the unlikely event that we get the same bits twice bits = str(random.getrandbits(32)) return bits -def gen_keys(key_dict): - # type: (Dict[str, Tuple[str, str]]) -> None +def gen_keys(key_dict: Dict[str, Tuple[str, str]]) -> None: for (stream, test) in test_streams: key_dict[gen_key(key_dict)] = (stream, test) @@ -188,8 +183,7 @@ notices = [] # We check for new zephyrs multiple times, to avoid filling the zephyr # receive queue with 30+ messages, which might result in messages # being dropped. -def receive_zephyrs(): - # type: () -> None +def receive_zephyrs() -> None: while True: try: notice = zephyr.receive(block=False) @@ -271,8 +265,7 @@ receive_zephyrs() logger.info("Finished receiving Zephyr messages!") all_keys = set(list(zhkeys.keys()) + list(hzkeys.keys())) -def process_keys(content_list): - # type: (List[str]) -> Tuple[Dict[str, int], Set[str], Set[str], bool, bool] +def process_keys(content_list: List[str]) -> Tuple[Dict[str, int], Set[str], Set[str], bool, bool]: # Start by filtering out any keys that might have come from # concurrent check-mirroring processes diff --git a/zulip/integrations/zephyr/sync-public-streams b/zulip/integrations/zephyr/sync-public-streams index d1e4eb9..ac96cad 100755 --- a/zulip/integrations/zephyr/sync-public-streams +++ b/zulip/integrations/zephyr/sync-public-streams @@ -13,8 +13,7 @@ import zulip from typing import Set, Optional -def fetch_public_streams(): - # type: () -> Optional[Set[bytes]] +def fetch_public_streams() -> Optional[Set[bytes]]: public_streams = set() try: diff --git a/zulip/integrations/zephyr/zephyr_mirror.py b/zulip/integrations/zephyr/zephyr_mirror.py index e806ba7..0fceaad 100755 --- a/zulip/integrations/zephyr/zephyr_mirror.py +++ b/zulip/integrations/zephyr/zephyr_mirror.py @@ -14,8 +14,7 @@ from zephyr_mirror_backend import parse_args from types import FrameType from typing import Any -def die(signal, frame): - # type: (int, FrameType) -> None +def die(signal: int, frame: FrameType) -> None: # We actually want to exit, so run os._exit (so as not to be caught and restarted) os._exit(1) @@ -40,8 +39,7 @@ if options.forward_class_messages and not options.noshard: print("Starting parallel zephyr class mirroring bot") jobs = list("0123456789abcdef") - def run_job(shard): - # type: (str) -> int + def run_job(shard: str) -> int: subprocess.call(args + ["--shard=%s" % (shard,)]) return 0 for (status, job) in run_parallel(run_job, jobs, threads=16): diff --git a/zulip/integrations/zephyr/zephyr_mirror_backend.py b/zulip/integrations/zephyr/zephyr_mirror_backend.py index b25598e..1e62854 100755 --- a/zulip/integrations/zephyr/zephyr_mirror_backend.py +++ b/zulip/integrations/zephyr/zephyr_mirror_backend.py @@ -26,8 +26,7 @@ CURRENT_STATE = States.Startup logger = cast(logging.Logger, None) # type: logging.Logger # FIXME cast should not be needed? -def to_zulip_username(zephyr_username): - # type: (str) -> str +def to_zulip_username(zephyr_username: str) -> str: if "@" in zephyr_username: (user, realm) = zephyr_username.split("@") else: @@ -39,8 +38,7 @@ def to_zulip_username(zephyr_username): return user.lower() + "@mit.edu" return user.lower() + "|" + realm.upper() + "@mit.edu" -def to_zephyr_username(zulip_username): - # type: (str) -> str +def to_zephyr_username(zulip_username: str) -> str: (user, realm) = zulip_username.split("@") if "|" not in user: # Hack to make ctl's fake username setup work :) @@ -62,8 +60,7 @@ def to_zephyr_username(zulip_username): # characters (our assumed minimum linewrapping threshold for Zephyr) # or (3) the first word of the next line is longer than this entire # line. -def different_paragraph(line, next_line): - # type: (str, str) -> bool +def different_paragraph(line: str, next_line: str) -> bool: words = next_line.split() return (len(line + " " + words[0]) < len(next_line) * 0.8 or len(line + " " + words[0]) < 50 or @@ -71,8 +68,7 @@ def different_paragraph(line, next_line): # Linewrapping algorithm based on: # http://gcbenison.wordpress.com/2011/07/03/a-program-to-intelligently-remove-carriage-returns-so-you-can-paste-text-without-having-it-look-awful/ #ignorelongline -def unwrap_lines(body): - # type: (str) -> str +def unwrap_lines(body: str) -> str: lines = body.split("\n") result = "" previous_line = lines[0] @@ -95,8 +91,7 @@ def unwrap_lines(body): result += previous_line return result -def send_zulip(zeph): - # type: (Dict[str, str]) -> Dict[str, str] +def send_zulip(zeph: Dict[str, str]) -> Dict[str, str]: message = {} if options.forward_class_messages: message["forged"] = "yes" @@ -128,8 +123,7 @@ def send_zulip(zeph): return zulip_client.send_message(message) -def send_error_zulip(error_msg): - # type: (str) -> None +def send_error_zulip(error_msg: str) -> None: message = {"type": "private", "sender": zulip_account_email, "to": zulip_account_email, @@ -138,8 +132,7 @@ def send_error_zulip(error_msg): zulip_client.send_message(message) current_zephyr_subs = set() -def zephyr_bulk_subscribe(subs): - # type: (List[Tuple[str, str, str]]) -> None +def zephyr_bulk_subscribe(subs: List[Tuple[str, str, str]]) -> None: try: zephyr._z.subAll(subs) except OSError: @@ -174,8 +167,7 @@ def zephyr_bulk_subscribe(subs): else: current_zephyr_subs.add(cls) -def update_subscriptions(): - # type: () -> None +def update_subscriptions() -> None: try: f = open(options.stream_file_path) public_streams = json.loads(f.read()) @@ -198,8 +190,7 @@ def update_subscriptions(): if len(classes_to_subscribe) > 0: zephyr_bulk_subscribe(list(classes_to_subscribe)) -def maybe_kill_child(): - # type: () -> None +def maybe_kill_child() -> None: try: if child_pid is not None: os.kill(child_pid, signal.SIGTERM) @@ -207,8 +198,7 @@ def maybe_kill_child(): # We don't care if the child process no longer exists, so just log the error logger.exception("") -def maybe_restart_mirroring_script(): - # type: () -> None +def maybe_restart_mirroring_script() -> None: if os.stat(os.path.join(options.stamp_path, "stamps", "restart_stamp")).st_mtime > start_time or \ ((options.user == "tabbott" or options.user == "tabbott/extra") and os.stat(os.path.join(options.stamp_path, "stamps", "tabbott_stamp")).st_mtime > start_time): @@ -227,8 +217,7 @@ def maybe_restart_mirroring_script(): logger.exception("Error restarting mirroring script; trying again... Traceback:") time.sleep(1) -def process_loop(log): - # type: (Optional[IO[Any]]) -> None +def process_loop(log: Optional[IO[Any]]) -> None: restart_check_count = 0 last_check_time = time.time() while True: @@ -267,8 +256,7 @@ def process_loop(log): except Exception: logger.exception("Error updating subscriptions from Zulip:") -def parse_zephyr_body(zephyr_data, notice_format): - # type: (str, str) -> Tuple[str, str] +def parse_zephyr_body(zephyr_data: str, notice_format: str) -> Tuple[str, str]: try: (zsig, body) = zephyr_data.split("\x00", 1) if (notice_format == 'New transaction [$1] entered in $2\nFrom: $3 ($5)\nSubject: $4' or @@ -284,8 +272,7 @@ def parse_zephyr_body(zephyr_data, notice_format): body = body.replace('\x00', '') return (zsig, body) -def parse_crypt_table(zephyr_class, instance): - # type: (Text, str) -> Optional[str] +def parse_crypt_table(zephyr_class: Text, instance: str) -> Optional[str]: try: crypt_table = open(os.path.join(os.environ["HOME"], ".crypt-table")) except OSError: @@ -306,8 +293,7 @@ def parse_crypt_table(zephyr_class, instance): return groups["keypath"] return None -def decrypt_zephyr(zephyr_class, instance, body): - # type: (Text, str, str) -> str +def decrypt_zephyr(zephyr_class: Text, instance: str, body: str) -> str: keypath = parse_crypt_table(zephyr_class, instance) if keypath is None: # We can't decrypt it, so we just return the original body @@ -337,8 +323,7 @@ def decrypt_zephyr(zephyr_class, instance, body): signal.signal(signal.SIGCHLD, signal.SIG_IGN) return decrypted # type: ignore # bytes, expecting str -def process_notice(notice, log): - # type: (Any, Optional[IO[Any]]) -> None +def process_notice(notice: Any, log: Optional[IO[Any]]) -> None: (zsig, body) = parse_zephyr_body(notice.message, notice.format) is_personal = False is_huddle = False @@ -436,8 +421,7 @@ def process_notice(notice, log): finally: os._exit(0) -def decode_unicode_byte_strings(zeph): - # type: (Dict[str, Any]) -> Dict[str, str] +def decode_unicode_byte_strings(zeph: Dict[str, Any]) -> Dict[str, str]: # 'Any' can be of any type of text that is converted to str. for field in zeph.keys(): if isinstance(zeph[field], str): @@ -448,14 +432,12 @@ def decode_unicode_byte_strings(zeph): zeph[field] = decoded return zeph -def quit_failed_initialization(message): - # type: (str) -> str +def quit_failed_initialization(message: str) -> str: logger.error(message) maybe_kill_child() sys.exit(1) -def zephyr_init_autoretry(): - # type: () -> None +def zephyr_init_autoretry() -> None: backoff = zulip.RandomExponentialBackoff() while backoff.keep_going(): try: @@ -470,8 +452,7 @@ def zephyr_init_autoretry(): quit_failed_initialization("Could not initialize Zephyr library, quitting!") -def zephyr_load_session_autoretry(session_path): - # type: (str) -> None +def zephyr_load_session_autoretry(session_path: str) -> None: backoff = zulip.RandomExponentialBackoff() while backoff.keep_going(): try: @@ -486,8 +467,7 @@ def zephyr_load_session_autoretry(session_path): quit_failed_initialization("Could not load saved Zephyr session, quitting!") -def zephyr_subscribe_autoretry(sub): - # type: (Tuple[str, str, str]) -> None +def zephyr_subscribe_autoretry(sub: Tuple[str, str, str]) -> None: backoff = zulip.RandomExponentialBackoff() while backoff.keep_going(): try: @@ -502,8 +482,7 @@ def zephyr_subscribe_autoretry(sub): quit_failed_initialization("Could not subscribe to personals, quitting!") -def zephyr_to_zulip(options): - # type: (Any) -> None +def zephyr_to_zulip(options: Any) -> None: if options.use_sessions and os.path.exists(options.session_path): logger.info("Loading old session") zephyr_load_session_autoretry(options.session_path) @@ -554,8 +533,7 @@ def zephyr_to_zulip(options): else: process_loop(None) -def send_zephyr(zwrite_args, content): - # type: (List[str], str) -> Tuple[int, str] +def send_zephyr(zwrite_args: List[str], content: str) -> Tuple[int, str]: p = subprocess.Popen(zwrite_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate(input=content.encode("utf-8")) @@ -571,16 +549,13 @@ def send_zephyr(zwrite_args, content): logger.warning("stderr: " + stderr) # type: ignore # str + bytes return (p.returncode, stderr) # type: ignore # bytes vs str -def send_authed_zephyr(zwrite_args, content): - # type: (List[str], str) -> Tuple[int, str] +def send_authed_zephyr(zwrite_args: List[str], content: str) -> Tuple[int, str]: return send_zephyr(zwrite_args, content) -def send_unauthed_zephyr(zwrite_args, content): - # type: (List[str], str) -> Tuple[int, str] +def send_unauthed_zephyr(zwrite_args: List[str], content: str) -> Tuple[int, str]: return send_zephyr(zwrite_args + ["-d"], content) -def zcrypt_encrypt_content(zephyr_class, instance, content): - # type: (str, str, str) -> Optional[str] +def zcrypt_encrypt_content(zephyr_class: str, instance: str, content: str) -> Optional[str]: keypath = parse_crypt_table(zephyr_class, instance) if keypath is None: return None @@ -605,8 +580,7 @@ def zcrypt_encrypt_content(zephyr_class, instance, content): encrypted, _ = p.communicate(input=content) # type: ignore # Optional[bytes] vs string return encrypted # type: ignore # bytes, expecting Optional[str] -def forward_to_zephyr(message): - # type: (Dict[str, Any]) -> None +def forward_to_zephyr(message: Dict[str, Any]) -> None: # 'Any' can be of any type of text support_heading = "Hi there! This is an automated message from Zulip." support_closing = """If you have any questions, please be in touch through the \ @@ -735,8 +709,7 @@ received it, Zephyr users did not. The error message from zwrite was: %s""" % (support_heading, stderr, support_closing)) return -def maybe_forward_to_zephyr(message): - # type: (Dict[str, Any]) -> None +def maybe_forward_to_zephyr(message: Dict[str, Any]) -> None: # The key string can be used to direct any type of text. if (message["sender_email"] == zulip_account_email): if not ((message["type"] == "stream") or @@ -758,8 +731,7 @@ def maybe_forward_to_zephyr(message): # whole process logger.exception("Error forwarding message:") -def zulip_to_zephyr(options): - # type: (int) -> None +def zulip_to_zephyr(options: int) -> None: # Sync messages from zulip to zephyr logger.info("Starting syncing messages.") while True: @@ -769,8 +741,7 @@ def zulip_to_zephyr(options): logger.exception("Error syncing messages:") time.sleep(1) -def subscribed_to_mail_messages(): - # type: () -> bool +def subscribed_to_mail_messages() -> bool: # In case we have lost our AFS tokens and those won't be able to # parse the Zephyr subs file, first try reading in result of this # query from the environment so we can avoid the filesystem read. @@ -784,8 +755,7 @@ def subscribed_to_mail_messages(): os.environ["HUMBUG_FORWARD_MAIL_ZEPHYRS"] = "False" return False -def add_zulip_subscriptions(verbose): - # type: (bool) -> None +def add_zulip_subscriptions(verbose: bool) -> None: zephyr_subscriptions = set() skipped = set() for (cls, instance, recipient) in parse_zephyr_subs(verbose=verbose): @@ -871,12 +841,10 @@ to these .zephyrs.subs lines, please do so via the Zulip web interface. """)) + "\n") -def valid_stream_name(name): - # type: (str) -> bool +def valid_stream_name(name: str) -> bool: return name != "" -def parse_zephyr_subs(verbose=False): - # type: (bool) -> Set[Tuple[str, str, str]] +def parse_zephyr_subs(verbose: bool = False) -> Set[Tuple[str, str, str]]: zephyr_subscriptions = set() # type: Set[Tuple[str, str, str]] subs_file = os.path.join(os.environ["HOME"], ".zephyr.subs") if not os.path.exists(subs_file): @@ -904,8 +872,7 @@ def parse_zephyr_subs(verbose=False): zephyr_subscriptions.add((cls.strip(), instance.strip(), recipient.strip())) return zephyr_subscriptions -def open_logger(): - # type: () -> logging.Logger +def open_logger() -> logging.Logger: if options.log_path is not None: log_file = options.log_path elif options.forward_class_messages: @@ -930,8 +897,7 @@ def open_logger(): logger.addHandler(file_handler) return logger -def configure_logger(logger, direction_name): - # type: (logging.Logger, Optional[str]) -> None +def configure_logger(logger: logging.Logger, direction_name: Optional[str]) -> None: if direction_name is None: log_format = "%(message)s" else: @@ -945,8 +911,7 @@ def configure_logger(logger, direction_name): for handler in root_logger.handlers: handler.setFormatter(formatter) -def parse_args(): - # type: () -> Tuple[Any, ...] +def parse_args() -> Tuple[Any, ...]: parser = optparse.OptionParser() parser.add_option('--forward-class-messages', default=False, @@ -1029,8 +994,7 @@ def parse_args(): default=os.path.join(os.environ["HOME"], "Private", ".humbug-api-key")) return parser.parse_args() -def die_gracefully(signal, frame): - # type: (int, FrameType) -> None +def die_gracefully(signal: int, frame: FrameType) -> None: if CURRENT_STATE == States.ZulipToZephyr or CURRENT_STATE == States.ChildSending: # this is a child process, so we want os._exit (no clean-up necessary) os._exit(1) diff --git a/zulip/setup.py b/zulip/setup.py index 4ebde55..eafd7c1 100755 --- a/zulip/setup.py +++ b/zulip/setup.py @@ -10,8 +10,7 @@ import itertools with open("README.md") as fh: long_description = fh.read() -def version(): - # type: () -> str +def version() -> str: version_py = os.path.join(os.path.dirname(__file__), "zulip", "__init__.py") with open(version_py) as in_handle: version_line = next(itertools.dropwhile(lambda x: not x.startswith("__version__"), @@ -19,8 +18,7 @@ def version(): version = version_line.split('=')[-1].strip().replace('"', '') return version -def recur_expand(target_root, dir): - # type: (Any, Any) -> Generator[Tuple[str, List[str]], None, None] +def recur_expand(target_root: Any, dir: Any) -> Generator[Tuple[str, List[str]], None, None]: for root, _, files in os.walk(dir): paths = [os.path.join(root, f) for f in files] if len(paths): diff --git a/zulip/tests/test_default_arguments.py b/zulip/tests/test_default_arguments.py index 6263d3a..8a698a5 100755 --- a/zulip/tests/test_default_arguments.py +++ b/zulip/tests/test_default_arguments.py @@ -12,8 +12,7 @@ from unittest.mock import patch class TestDefaultArguments(TestCase): - def test_invalid_arguments(self): - # type: () -> None + def test_invalid_arguments(self) -> None: parser = zulip.add_default_arguments(argparse.ArgumentParser(usage="lorem ipsum")) with self.assertRaises(SystemExit) as cm: # type: ignore # error: "assertRaises" doesn't match argument types with patch('sys.stderr', new=io.StringIO()) as mock_stderr: @@ -30,8 +29,7 @@ Zulip API configuration: """)) @patch('os.path.exists', return_value=False) - def test_config_path_with_tilde(self, mock_os_path_exists): - # type: (bool) -> None + def test_config_path_with_tilde(self, mock_os_path_exists: bool) -> None: parser = zulip.add_default_arguments(argparse.ArgumentParser(usage="lorem ipsum")) test_path = '~/zuliprc' args = parser.parse_args(['--config-file', test_path]) diff --git a/zulip/zulip/__init__.py b/zulip/zulip/__init__.py index 9a0124e..12aa92e 100644 --- a/zulip/zulip/__init__.py +++ b/zulip/zulip/__init__.py @@ -33,41 +33,35 @@ requests_json_is_function = callable(requests.Response.json) API_VERSTRING = "v1/" class CountingBackoff: - def __init__(self, maximum_retries=10, timeout_success_equivalent=None, delay_cap=90.0): - # type: (int, Optional[float], float) -> None + def __init__(self, maximum_retries: int = 10, timeout_success_equivalent: Optional[float] = None, delay_cap: float = 90.0) -> None: self.number_of_retries = 0 self.maximum_retries = maximum_retries self.timeout_success_equivalent = timeout_success_equivalent self.last_attempt_time = 0.0 self.delay_cap = delay_cap - def keep_going(self): - # type: () -> bool + def keep_going(self) -> bool: self._check_success_timeout() return self.number_of_retries < self.maximum_retries - def succeed(self): - # type: () -> None + def succeed(self) -> None: self.number_of_retries = 0 self.last_attempt_time = time.time() - def fail(self): - # type: () -> None + def fail(self) -> None: self._check_success_timeout() self.number_of_retries = min(self.number_of_retries + 1, self.maximum_retries) self.last_attempt_time = time.time() - def _check_success_timeout(self): - # type: () -> None + def _check_success_timeout(self) -> None: if (self.timeout_success_equivalent is not None and self.last_attempt_time != 0 and time.time() - self.last_attempt_time > self.timeout_success_equivalent): self.number_of_retries = 0 class RandomExponentialBackoff(CountingBackoff): - def fail(self): - # type: () -> None + def fail(self) -> None: super().fail() # Exponential growth with ratio sqrt(2); compute random delay # between x and 2x where x is growing exponentially @@ -80,16 +74,17 @@ class RandomExponentialBackoff(CountingBackoff): print(message) time.sleep(delay) -def _default_client(): - # type: () -> str +def _default_client() -> str: return "ZulipPython/" + __version__ -def add_default_arguments(parser, patch_error_handling=True, allow_provisioning=False): - # type: (argparse.ArgumentParser, bool, bool) -> argparse.ArgumentParser +def add_default_arguments( + parser: argparse.ArgumentParser, + patch_error_handling: bool = True, + allow_provisioning: bool = False, +) -> argparse.ArgumentParser: if patch_error_handling: - def custom_error_handling(self, message): - # type: (argparse.ArgumentParser, str) -> None + def custom_error_handling(self: argparse.ArgumentParser, message: str) -> None: self.print_help(sys.stderr) self.exit(2, '{}: error: {}\n'.format(self.prog, message)) parser.error = types.MethodType(custom_error_handling, parser) # type: ignore # patching function @@ -154,8 +149,7 @@ def add_default_arguments(parser, patch_error_handling=True, allow_provisioning= # except for the fact that is uses the deprecated `optparse` module. # We still keep it for legacy support of out-of-tree bots and integrations # depending on it. -def generate_option_group(parser, prefix=''): - # type: (optparse.OptionParser, str) -> optparse.OptionGroup +def generate_option_group(parser: optparse.OptionParser, prefix: str = '') -> optparse.OptionGroup: logging.warning("""zulip.generate_option_group is based on optparse, which is now deprecated. We recommend migrating to argparse and using zulip.add_default_arguments instead.""") @@ -209,8 +203,7 @@ def generate_option_group(parser, prefix=''): file).''') return group -def init_from_options(options, client=None): - # type: (Any, Optional[str]) -> Client +def init_from_options(options: Any, client: Optional[str] = None) -> 'Client': if getattr(options, 'provision', False): requirements_path = os.path.abspath(os.path.join(sys.path[0], 'requirements.txt')) @@ -238,8 +231,7 @@ def init_from_options(options, client=None): client_cert=options.client_cert, client_cert_key=options.client_cert_key) -def get_default_config_filename(): - # type: () -> Optional[str] +def get_default_config_filename() -> Optional[str]: if os.environ.get("HOME") is None: return None @@ -250,8 +242,7 @@ def get_default_config_filename(): " mv ~/.humbugrc ~/.zuliprc\n") return config_file -def validate_boolean_field(field): - # type: (Optional[Text]) -> Union[bool, None] +def validate_boolean_field(field: Optional[Text]) -> Union[bool, None]: if not isinstance(field, str): return None @@ -277,12 +268,11 @@ class UnrecoverableNetworkError(ZulipError): pass class Client: - def __init__(self, email=None, api_key=None, config_file=None, - verbose=False, retry_on_errors=True, - site=None, client=None, - cert_bundle=None, insecure=None, - client_cert=None, client_cert_key=None): - # type: (Optional[str], Optional[str], Optional[str], bool, bool, Optional[str], Optional[str], Optional[str], Optional[bool], Optional[str], Optional[str]) -> None + def __init__(self, email: Optional[str] = None, api_key: Optional[str] = None, config_file: Optional[str] = None, + verbose: bool = False, retry_on_errors: bool = True, + site: Optional[str] = None, client: Optional[str] = None, + cert_bundle: Optional[str] = None, insecure: Optional[bool] = None, + client_cert: Optional[str] = None, client_cert_key: Optional[str] = None) -> None: if client is None: client = _default_client() @@ -406,8 +396,7 @@ class Client: self.has_connected = False - def ensure_session(self): - # type: () -> None + def ensure_session(self) -> None: # Check if the session has been created already, and return # immediately if so. @@ -429,8 +418,7 @@ class Client: session.headers.update({"User-agent": self.get_user_agent()}) self.session = session - def get_user_agent(self): - # type: () -> str + def get_user_agent(self) -> str: vendor = '' vendor_version = '' try: @@ -454,9 +442,8 @@ class Client: vendor_version=vendor_version, ) - def do_api_query(self, orig_request, url, method="POST", - longpolling=False, files=None, timeout=None): - # type: (Mapping[str, Any], str, str, bool, Optional[List[IO[Any]]], Optional[float]) -> Dict[str, Any] + def do_api_query(self, orig_request: Mapping[str, Any], url: str, method: str = "POST", + longpolling: bool = False, files: Optional[List[IO[Any]]] = None, timeout: Optional[float] = None) -> Dict[str, Any]: if files is None: files = [] @@ -490,8 +477,7 @@ class Client: 'failures': 0, } # type: Dict[str, Any] - def error_retry(error_string): - # type: (str) -> bool + def error_retry(error_string: str) -> bool: if not self.retry_on_errors or query_state["failures"] >= 10: return False if self.verbose: @@ -507,8 +493,7 @@ class Client: query_state["failures"] += 1 return True - def end_error_retry(succeeded): - # type: (bool) -> None + def end_error_retry(succeeded: bool) -> None: if query_state["had_error_retry"] and self.verbose: if succeeded: print("Success!") @@ -589,9 +574,8 @@ class Client: return {'msg': "Unexpected error from the server", "result": "http-error", "status_code": res.status_code} - def call_endpoint(self, url=None, method="POST", request=None, - longpolling=False, files=None, timeout=None): - # type: (Optional[str], str, Optional[Dict[str, Any]], bool, Optional[List[IO[Any]]], Optional[float]) -> Dict[str, Any] + def call_endpoint(self, url: Optional[str] = None, method: str = "POST", request: Optional[Dict[str, Any]] = None, + longpolling: bool = False, files: Optional[List[IO[Any]]] = None, timeout: Optional[float] = None) -> Dict[str, Any]: if request is None: request = dict() marshalled_request = {} @@ -602,13 +586,16 @@ class Client: return self.do_api_query(marshalled_request, versioned_url, method=method, longpolling=longpolling, files=files, timeout=timeout) - def call_on_each_event(self, callback, event_types=None, narrow=None): - # type: (Callable[[Dict[str, Any]], None], Optional[List[str]], Optional[List[List[str]]]) -> None + def call_on_each_event( + self, + callback: Callable[[Dict[str, Any]], None], + event_types: Optional[List[str]] = None, + narrow: Optional[List[List[str]]] = None, + ) -> None: if narrow is None: narrow = [] - def do_register(): - # type: () -> Tuple[str, int] + def do_register() -> Tuple[str, int]: while True: if event_types is None: res = self.register() @@ -666,16 +653,13 @@ class Client: last_event_id = max(last_event_id, int(event['id'])) callback(event) - def call_on_each_message(self, callback): - # type: (Callable[[Dict[str, Any]], None]) -> None - def event_callback(event): - # type: (Dict[str, Any]) -> None + def call_on_each_message(self, callback: Callable[[Dict[str, Any]], None]) -> None: + def event_callback(event: Dict[str, Any]) -> None: if event['type'] == 'message': callback(event['message']) self.call_on_each_event(event_callback, ['message']) - def get_messages(self, message_filters): - # type: (Dict[str, Any]) -> Dict[str, Any] + def get_messages(self, message_filters: Dict[str, Any]) -> Dict[str, Any]: ''' See examples/get-messages for example usage ''' @@ -685,8 +669,7 @@ class Client: request=message_filters ) - def check_messages_match_narrow(self, **request): - # type: (Dict[str, Any]) -> Dict[str, Any] + def check_messages_match_narrow(self, **request: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: @@ -702,8 +685,7 @@ class Client: request=request ) - def get_raw_message(self, message_id): - # type: (int) -> Dict[str, str] + def get_raw_message(self, message_id: int) -> Dict[str, str]: ''' See examples/get-raw-message for example usage ''' @@ -712,8 +694,7 @@ class Client: method='GET' ) - def send_message(self, message_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def send_message(self, message_data: Dict[str, Any]) -> Dict[str, Any]: ''' See examples/send-message for example usage. ''' @@ -722,8 +703,7 @@ class Client: request=message_data, ) - def upload_file(self, file): - # type: (IO[Any]) -> Dict[str, Any] + def upload_file(self, file: IO[Any]) -> Dict[str, Any]: ''' See examples/upload-file for example usage. ''' @@ -732,8 +712,7 @@ class Client: files=[file] ) - def get_attachments(self): - # type: () -> Dict[str, Any] + def get_attachments(self) -> Dict[str, Any]: ''' Example usage: @@ -745,8 +724,7 @@ class Client: method='GET' ) - def update_message(self, message_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_message(self, message_data: Dict[str, Any]) -> Dict[str, Any]: ''' See examples/edit-message for example usage. ''' @@ -756,8 +734,7 @@ class Client: request=message_data, ) - def delete_message(self, message_id): - # type: (int) -> Dict[str, Any] + def delete_message(self, message_id: int) -> Dict[str, Any]: ''' See examples/delete-message for example usage. ''' @@ -766,8 +743,7 @@ class Client: method='DELETE' ) - def update_message_flags(self, update_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_message_flags(self, update_data: Dict[str, Any]) -> Dict[str, Any]: ''' See examples/update-flags for example usage. ''' @@ -777,8 +753,7 @@ class Client: request=update_data ) - def mark_all_as_read(self): - # type: () -> Dict[str, Any] + def mark_all_as_read(self) -> Dict[str, Any]: ''' Example usage: @@ -790,8 +765,7 @@ class Client: method='POST', ) - def mark_stream_as_read(self, stream_id): - # type: (int) -> Dict[str, Any] + def mark_stream_as_read(self, stream_id: int) -> Dict[str, Any]: ''' Example usage: @@ -804,8 +778,7 @@ class Client: request={'stream_id': stream_id}, ) - def mark_topic_as_read(self, stream_id, topic_name): - # type: (int, str) -> Dict[str, Any] + def mark_topic_as_read(self, stream_id: int, topic_name: str) -> Dict[str, Any]: ''' Example usage: @@ -821,8 +794,7 @@ class Client: }, ) - def get_message_history(self, message_id): - # type: (int) -> Dict[str, Any] + def get_message_history(self, message_id: int) -> Dict[str, Any]: ''' See examples/message-history for example usage. ''' @@ -831,8 +803,7 @@ class Client: method='GET' ) - def add_reaction(self, reaction_data): - # type: (Dict[str, str]) -> Dict[str, Any] + def add_reaction(self, reaction_data: Dict[str, str]) -> Dict[str, Any]: ''' Example usage: @@ -850,8 +821,7 @@ class Client: request=reaction_data, ) - def remove_reaction(self, reaction_data): - # type: (Dict[str, str]) -> Dict[str, Any] + def remove_reaction(self, reaction_data: Dict[str, str]) -> Dict[str, Any]: ''' Example usage: @@ -869,8 +839,7 @@ class Client: request=reaction_data, ) - def get_realm_emoji(self): - # type: () -> Dict[str, Any] + def get_realm_emoji(self) -> Dict[str, Any]: ''' See examples/realm-emoji for example usage. ''' @@ -879,8 +848,7 @@ class Client: method='GET' ) - def upload_custom_emoji(self, emoji_name, file_obj): - # type: (str, IO[Any]) -> Dict[str, Any] + def upload_custom_emoji(self, emoji_name: str, file_obj: IO[Any]) -> Dict[str, Any]: ''' Example usage: @@ -893,8 +861,7 @@ class Client: files=[file_obj] ) - def get_realm_filters(self): - # type: () -> Dict[str, Any] + def get_realm_filters(self) -> Dict[str, Any]: ''' Example usage: @@ -906,8 +873,7 @@ class Client: method='GET', ) - def add_realm_filter(self, pattern, url_format_string): - # type: (str, str) -> Dict[str, Any] + def add_realm_filter(self, pattern: str, url_format_string: str) -> Dict[str, Any]: ''' Example usage: @@ -923,8 +889,7 @@ class Client: }, ) - def remove_realm_filter(self, filter_id): - # type: (int) -> Dict[str, Any] + def remove_realm_filter(self, filter_id: int) -> Dict[str, Any]: ''' Example usage: @@ -936,8 +901,7 @@ class Client: method='DELETE', ) - def get_server_settings(self): - # type: () -> Dict[str, Any] + def get_server_settings(self) -> Dict[str, Any]: ''' Example usage: @@ -949,8 +913,7 @@ class Client: method='GET', ) - def get_events(self, **request): - # type: (**Any) -> Dict[str, Any] + def get_events(self, **request: Any) -> Dict[str, Any]: ''' See the register() method for example usage. ''' @@ -961,8 +924,12 @@ class Client: request=request, ) - def register(self, event_types=None, narrow=None, **kwargs): - # type: (Optional[Iterable[str]], Optional[List[List[str]]], **Any) -> Dict[str, Any] + def register( + self, + event_types: Optional[Iterable[str]] = None, + narrow: Optional[List[List[str]]] = None, + **kwargs: Any + ) -> Dict[str, Any]: ''' Example usage: @@ -986,8 +953,7 @@ class Client: request=request, ) - def deregister(self, queue_id, timeout=None): - # type: (str, Optional[float]) -> Dict[str, Any] + def deregister(self, queue_id: str, timeout: Optional[float] = None) -> Dict[str, Any]: ''' Example usage: @@ -1005,8 +971,7 @@ class Client: timeout=timeout, ) - def get_profile(self, request=None): - # type: (Optional[Dict[str, Any]]) -> Dict[str, Any] + def get_profile(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ''' Example usage: @@ -1019,8 +984,7 @@ class Client: request=request, ) - def get_user_presence(self, email): - # type: (str) -> Dict[str, Any] + def get_user_presence(self, email: str) -> Dict[str, Any]: ''' Example usage: @@ -1032,8 +996,7 @@ class Client: method='GET', ) - def get_realm_presence(self): - # type: () -> Dict[str, Any] + def get_realm_presence(self) -> Dict[str, Any]: ''' Example usage: @@ -1045,8 +1008,7 @@ class Client: method='GET', ) - def update_presence(self, request): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_presence(self, request: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: @@ -1063,8 +1025,7 @@ class Client: request=request, ) - def get_streams(self, **request): - # type: (**Any) -> Dict[str, Any] + def get_streams(self, **request: Any) -> Dict[str, Any]: ''' See examples/get-public-streams for example usage. ''' @@ -1074,8 +1035,7 @@ class Client: request=request, ) - def update_stream(self, stream_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_stream(self, stream_data: Dict[str, Any]) -> Dict[str, Any]: ''' See examples/edit-stream for example usage. ''' @@ -1086,8 +1046,7 @@ class Client: request=stream_data, ) - def delete_stream(self, stream_id): - # type: (int) -> Dict[str, Any] + def delete_stream(self, stream_id: int) -> Dict[str, Any]: ''' See examples/delete-stream for example usage. ''' @@ -1096,8 +1055,7 @@ class Client: method='DELETE', ) - def add_default_stream(self, stream_id): - # type: (int) -> Dict[str, Any] + def add_default_stream(self, stream_id: int) -> Dict[str, Any]: ''' Example usage: @@ -1111,8 +1069,7 @@ class Client: request={'stream_id': stream_id}, ) - def get_user_by_id(self, user_id, **request): - # type: (int, **Any) -> Dict[str, Any] + def get_user_by_id(self, user_id: int, **request: Any) -> Dict[str, Any]: ''' Example usage: @@ -1126,8 +1083,7 @@ class Client: request=request, ) - def deactivate_user_by_id(self, user_id): - # type: (int) -> Dict[str, Any] + def deactivate_user_by_id(self, user_id: int) -> Dict[str, Any]: ''' Example usage: @@ -1140,8 +1096,7 @@ class Client: method='DELETE', ) - def reactivate_user_by_id(self, user_id): - # type: (int) -> Dict[str, Any] + def reactivate_user_by_id(self, user_id: int) -> Dict[str, Any]: ''' Example usage: @@ -1154,8 +1109,7 @@ class Client: method='POST', ) - def update_user_by_id(self, user_id, **request): - # type: (int, **Any) -> Dict[str, Any] + def update_user_by_id(self, user_id: int, **request: Any) -> Dict[str, Any]: ''' Example usage: @@ -1173,8 +1127,7 @@ class Client: request=request ) - def get_members(self, request=None): - # type: (Optional[Dict[str, Any]]) -> Dict[str, Any] + def get_members(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ''' See examples/list-members for example usage. ''' @@ -1184,8 +1137,7 @@ class Client: request=request, ) - def get_alert_words(self): - # type: () -> Dict[str, Any] + def get_alert_words(self) -> Dict[str, Any]: ''' See examples/alert-words for example usage. ''' @@ -1194,8 +1146,7 @@ class Client: method='GET' ) - def add_alert_words(self, alert_words): - # type: (List[str]) -> Dict[str, Any] + def add_alert_words(self, alert_words: List[str]) -> Dict[str, Any]: ''' See examples/alert-words for example usage. ''' @@ -1207,8 +1158,7 @@ class Client: } ) - def remove_alert_words(self, alert_words): - # type: (List[str]) -> Dict[str, Any] + def remove_alert_words(self, alert_words: List[str]) -> Dict[str, Any]: ''' See examples/alert-words for example usage. ''' @@ -1220,8 +1170,7 @@ class Client: } ) - def list_subscriptions(self, request=None): - # type: (Optional[Dict[str, Any]]) -> Dict[str, Any] + def list_subscriptions(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ''' See examples/list-subscriptions for example usage. ''' @@ -1231,8 +1180,7 @@ class Client: request=request, ) - def add_subscriptions(self, streams, **kwargs): - # type: (Iterable[Dict[str, Any]], **Any) -> Dict[str, Any] + def add_subscriptions(self, streams: Iterable[Dict[str, Any]], **kwargs: Any) -> Dict[str, Any]: ''' See examples/subscribe for example usage. ''' @@ -1246,8 +1194,7 @@ class Client: request=request, ) - def remove_subscriptions(self, streams, principals=None): - # type: (Iterable[str], Optional[Iterable[str]]) -> Dict[str, Any] + def remove_subscriptions(self, streams: Iterable[str], principals: Optional[Iterable[str]] = None) -> Dict[str, Any]: ''' See examples/unsubscribe for example usage. ''' @@ -1264,8 +1211,7 @@ class Client: request=request, ) - def mute_topic(self, request): - # type: (Dict[str, Any]) -> Dict[str, Any] + def mute_topic(self, request: Dict[str, Any]) -> Dict[str, Any]: ''' See examples/mute-topic for example usage. ''' @@ -1275,8 +1221,7 @@ class Client: request=request ) - def update_subscription_settings(self, subscription_data): - # type: (List[Dict[str, Any]]) -> Dict[str, Any] + def update_subscription_settings(self, subscription_data: List[Dict[str, Any]]) -> Dict[str, Any]: ''' Example usage: @@ -1298,8 +1243,7 @@ class Client: request={'subscription_data': subscription_data} ) - def update_notification_settings(self, notification_settings): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_notification_settings(self, notification_settings: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: @@ -1315,8 +1259,7 @@ class Client: request=notification_settings, ) - def get_stream_id(self, stream): - # type: (str) -> Dict[str, Any] + def get_stream_id(self, stream: str) -> Dict[str, Any]: ''' Example usage: client.get_stream_id('devel') ''' @@ -1328,8 +1271,7 @@ class Client: request=None, ) - def get_stream_topics(self, stream_id): - # type: (int) -> Dict[str, Any] + def get_stream_topics(self, stream_id: int) -> Dict[str, Any]: ''' See examples/get-stream-topics for example usage. ''' @@ -1338,8 +1280,7 @@ class Client: method='GET' ) - def get_user_groups(self): - # type: () -> Dict[str, Any] + def get_user_groups(self) -> Dict[str, Any]: ''' Example usage: >>> client.get_user_groups() @@ -1350,8 +1291,7 @@ class Client: method='GET', ) - def create_user_group(self, group_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def create_user_group(self, group_data: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: >>> client.create_user_group({ @@ -1367,8 +1307,7 @@ class Client: request=group_data, ) - def update_user_group(self, group_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_user_group(self, group_data: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: @@ -1385,8 +1324,7 @@ class Client: request=group_data, ) - def remove_user_group(self, group_id): - # type: (int) -> Dict[str, Any] + def remove_user_group(self, group_id: int) -> Dict[str, Any]: ''' Example usage: @@ -1398,8 +1336,7 @@ class Client: method='DELETE', ) - def update_user_group_members(self, group_data): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_user_group_members(self, group_data: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: @@ -1415,8 +1352,7 @@ class Client: request=group_data, ) - def get_subscribers(self, **request): - # type: (**Any) -> Dict[str, Any] + def get_subscribers(self, **request: Any) -> Dict[str, Any]: ''' Example usage: client.get_subscribers(stream='devel') ''' @@ -1432,8 +1368,7 @@ class Client: request=request, ) - def render_message(self, request=None): - # type: (Optional[Dict[str, Any]]) -> Dict[str, Any] + def render_message(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ''' Example usage: @@ -1446,8 +1381,7 @@ class Client: request=request, ) - def create_user(self, request=None): - # type: (Optional[Dict[str, Any]]) -> Dict[str, Any] + def create_user(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ''' See examples/create-user for example usage. ''' @@ -1457,8 +1391,7 @@ class Client: request=request, ) - def update_storage(self, request): - # type: (Dict[str, Any]) -> Dict[str, Any] + def update_storage(self, request: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: @@ -1472,8 +1405,7 @@ class Client: request=request, ) - def get_storage(self, request=None): - # type: (Optional[Dict[str, Any]]) -> Dict[str, Any] + def get_storage(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: ''' Example usage: @@ -1489,8 +1421,7 @@ class Client: request=request, ) - def set_typing_status(self, request): - # type: (Dict[str, Any]) -> Dict[str, Any] + def set_typing_status(self, request: Dict[str, Any]) -> Dict[str, Any]: ''' Example usage: >>> client.set_typing_status({ @@ -1510,21 +1441,18 @@ class ZulipStream: A Zulip stream-like object """ - def __init__(self, type, to, subject, **kwargs): - # type: (str, str, str, **Any) -> None + def __init__(self, type: str, to: str, subject: str, **kwargs: Any) -> None: self.client = Client(**kwargs) self.type = type self.to = to self.subject = subject - def write(self, content): - # type: (str) -> None + def write(self, content: str) -> None: message = {"type": self.type, "to": self.to, "subject": self.subject, "content": content} self.client.send_message(message) - def flush(self): - # type: () -> None + def flush(self) -> None: pass diff --git a/zulip/zulip/api_examples.py b/zulip/zulip/api_examples.py index 4b4665b..0d67c7c 100644 --- a/zulip/zulip/api_examples.py +++ b/zulip/zulip/api_examples.py @@ -4,8 +4,7 @@ import zulip import argparse -def main(): - # type: () -> None +def main() -> None: usage = """zulip-api-examples [script_name] Prints the path to the Zulip API example scripts.""" diff --git a/zulip/zulip/examples/edit-stream b/zulip/zulip/examples/edit-stream index 06a7be7..7e88aad 100755 --- a/zulip/zulip/examples/edit-stream +++ b/zulip/zulip/examples/edit-stream @@ -14,8 +14,7 @@ Example: edit-stream --stream-id=3 --history-public-to-subscribers """ -def quote(string): - # type: (str) -> str +def quote(string: str) -> str: return '"{}"'.format(string) diff --git a/zulip/zulip/examples/print-events b/zulip/zulip/examples/print-events index 70c80c4..859e0d3 100755 --- a/zulip/zulip/examples/print-events +++ b/zulip/zulip/examples/print-events @@ -20,8 +20,7 @@ options = parser.parse_args() client = zulip.init_from_options(options) -def print_event(event): - # type: (Dict[str, Any]) -> None +def print_event(event: Dict[str, Any]) -> None: print(event) # This is a blocking call, and will continuously poll for new events diff --git a/zulip/zulip/examples/print-messages b/zulip/zulip/examples/print-messages index e17891b..7f0b0f8 100755 --- a/zulip/zulip/examples/print-messages +++ b/zulip/zulip/examples/print-messages @@ -20,8 +20,7 @@ options = parser.parse_args() client = zulip.init_from_options(options) -def print_message(message): - # type: (Dict[str, Any]) -> None +def print_message(message: Dict[str, Any]) -> None: print(message) # This is a blocking call, and will continuously poll for new messages diff --git a/zulip/zulip/examples/welcome-message b/zulip/zulip/examples/welcome-message index 7421a1c..6140c4d 100755 --- a/zulip/zulip/examples/welcome-message +++ b/zulip/zulip/examples/welcome-message @@ -51,17 +51,14 @@ streams_to_watch = ['new members'] # These streams will cause anyone who sends a message there to be removed from the watchlist streams_to_cancel = ['development help'] -def get_watchlist(): - # type: () -> List[Any] +def get_watchlist() -> List[Any]: storage = client.get_storage() return list(storage['storage'].values()) -def set_watchlist(watchlist): - # type: (List[str]) -> None +def set_watchlist(watchlist: List[str]) -> None: client.update_storage({'storage': dict(enumerate(watchlist))}) -def handle_event(event): - # type: (Dict[str, Any]) -> None +def handle_event(event: Dict[str, Any]) -> None: try: if event['type'] == 'realm_user' and event['op'] == 'add': watchlist = get_watchlist() @@ -87,8 +84,7 @@ def handle_event(event): print(err) -def start_event_handler(): - # type: () -> None +def start_event_handler() -> None: print("Starting event handler...") client.call_on_each_event(handle_event, event_types=['realm_user', 'message']) diff --git a/zulip/zulip/send.py b/zulip/zulip/send.py index c8562d4..48ff881 100755 --- a/zulip/zulip/send.py +++ b/zulip/zulip/send.py @@ -13,8 +13,7 @@ logging.basicConfig() log = logging.getLogger('zulip-send') -def do_send_message(client, message_data): - # type: (zulip.Client, Dict[str, Any]) -> bool +def do_send_message(client: zulip.Client, message_data: Dict[str, Any]) -> bool: '''Sends a message and optionally prints status about the same.''' if message_data['type'] == 'stream': @@ -30,8 +29,7 @@ def do_send_message(client, message_data): log.error(response['msg']) return False -def main(): - # type: () -> int +def main() -> int: usage = """zulip-send [options] [recipient...] Sends a message to specified recipients. diff --git a/zulip_bots/setup.py b/zulip_bots/setup.py index b170ca3..385b6f7 100755 --- a/zulip_bots/setup.py +++ b/zulip_bots/setup.py @@ -78,8 +78,7 @@ except ImportError: from importlib import import_module # Manual dependency check - def check_dependency_manually(module_name, version=None): - # type: (str, Optional[str]) -> None + def check_dependency_manually(module_name: str, version: Optional[str] = None) -> None: try: module = import_module(module_name) # type: Any if version is not None: diff --git a/zulip_bots/zulip_bots/provision.py b/zulip_bots/zulip_bots/provision.py index 2112e00..55bdecf 100755 --- a/zulip_bots/zulip_bots/provision.py +++ b/zulip_bots/zulip_bots/provision.py @@ -9,16 +9,14 @@ import glob import pip from typing import Iterator -def get_bot_paths(): - # type: () -> Iterator[str] +def get_bot_paths() -> Iterator[str]: current_dir = os.path.dirname(os.path.abspath(__file__)) bots_dir = os.path.join(current_dir, "bots") bots_subdirs = map(lambda d: os.path.abspath(d), glob.glob(bots_dir + '/*')) paths = filter(lambda d: os.path.isdir(d), bots_subdirs) return paths -def provision_bot(path_to_bot, force): - # type: (str, bool) -> None +def provision_bot(path_to_bot: str, force: bool) -> None: req_path = os.path.join(path_to_bot, 'requirements.txt') if os.path.isfile(req_path): bot_name = os.path.basename(path_to_bot) @@ -36,8 +34,7 @@ def provision_bot(path_to_bot, force): logging.info('Installed dependencies successfully.') -def parse_args(available_bots): - # type: (Iterator[str]) -> argparse.Namespace +def parse_args(available_bots: Iterator[str]) -> argparse.Namespace: usage = """ Installs dependencies of bots in the bots/ directories. Add a requirements.txt file in a bot's folder @@ -71,8 +68,7 @@ Example: ./provision.py helloworld xkcd wikipedia return parser.parse_args() -def main(): - # type: () -> None +def main() -> None: options = parse_args(available_bots=get_bot_paths()) if not options.quiet: diff --git a/zulip_bots/zulip_bots/request_test_lib.py b/zulip_bots/zulip_bots/request_test_lib.py index e9520b0..4096364 100644 --- a/zulip_bots/zulip_bots/request_test_lib.py +++ b/zulip_bots/zulip_bots/request_test_lib.py @@ -7,8 +7,7 @@ from unittest.mock import patch from typing import Any, Dict, List @contextmanager -def mock_http_conversation(http_data): - # type: (Dict[str, Any]) -> Any +def mock_http_conversation(http_data: Dict[str, Any]) -> Any: """ Use this context manager to mock and verify a bot's HTTP requests to the third-party API (and provide the correct @@ -18,8 +17,7 @@ def mock_http_conversation(http_data): http_data should be fixtures data formatted like the data in zulip_bots/zulip_bots/bots/giphy/fixtures/test_normal.json """ - def get_response(http_response, http_headers, is_raw_response): - # type: (Dict[str, Any], Dict[str, Any], bool) -> Any + def get_response(http_response: Dict[str, Any], http_headers: Dict[str, Any], is_raw_response: bool) -> Any: """Creates a fake `requests` Response with a desired HTTP response and response headers. """ @@ -31,8 +29,7 @@ def mock_http_conversation(http_data): mock_result.status_code = http_headers.get('status', 200) return mock_result - def assert_called_with_fields(mock_result, http_request, fields, meta): - # type: (Any, Dict[str, Any], List[str], Dict[str, Any]) -> None + def assert_called_with_fields(mock_result: Any, http_request: Dict[str, Any], fields: List[str], meta: Dict[str, Any]) -> None: """Calls `assert_called_with` on a mock object using an HTTP request. Uses `fields` to determine which keys to look for in HTTP request and to test; if a key is in `fields`, e.g., 'headers', it will be used in @@ -101,10 +98,8 @@ def mock_http_conversation(http_data): ) @contextmanager -def mock_request_exception(): - # type: () -> Any - def assert_mock_called(mock_result): - # type: (Any) -> None +def mock_request_exception() -> Any: + def assert_mock_called(mock_result: Any) -> None: assert mock_result.called with patch('requests.get') as mock_get: diff --git a/zulip_bots/zulip_bots/test_file_utils.py b/zulip_bots/zulip_bots/test_file_utils.py index a005db5..1fb04e4 100644 --- a/zulip_bots/zulip_bots/test_file_utils.py +++ b/zulip_bots/zulip_bots/test_file_utils.py @@ -15,8 +15,7 @@ directory structure is currently: fixtures/ ''' -def get_bot_message_handler(bot_name): - # type: (str) -> Any +def get_bot_message_handler(bot_name: str) -> Any: # message_handler is of type 'Any', since it can contain any bot's # handler class. Eventually, we want bot's handler classes to # inherit from a common prototype specifying the handle_message @@ -24,8 +23,7 @@ def get_bot_message_handler(bot_name): lib_module = import_module('zulip_bots.bots.{bot}.{bot}'.format(bot=bot_name)) # type: Any return lib_module.handler_class() -def read_bot_fixture_data(bot_name, test_name): - # type: (str, str) -> Dict[str, Any] +def read_bot_fixture_data(bot_name: str, test_name: str) -> Dict[str, Any]: base_path = os.path.realpath(os.path.join(os.path.dirname( os.path.abspath(__file__)), 'bots', bot_name, 'fixtures')) http_data_path = os.path.join(base_path, '{}.json'.format(test_name)) diff --git a/zulip_bots/zulip_bots/test_lib.py b/zulip_bots/zulip_bots/test_lib.py index acbd6ea..4476748 100755 --- a/zulip_bots/zulip_bots/test_lib.py +++ b/zulip_bots/zulip_bots/test_lib.py @@ -53,13 +53,11 @@ class StubBotHandler: def update_message(self, message: Dict[str, Any]) -> None: self.message_server.update(message) - def upload_file_from_path(self, file_path): - # type: (str) -> Dict[str, Any] + def upload_file_from_path(self, file_path: str) -> Dict[str, Any]: with open(file_path, 'rb') as file: return self.message_server.upload_file(file) - def upload_file(self, file): - # type: (IO[Any]) -> Dict[str, Any] + def upload_file(self, file: IO[Any]) -> Dict[str, Any]: return self.message_server.upload_file(file) class BotQuitException(Exception): @@ -132,8 +130,7 @@ class BotTestCase(unittest.TestCase): return bot, bot_handler - def get_response(self, message): - # type: (Dict[str, Any]) -> Dict[str, Any] + def get_response(self, message: Dict[str, Any]) -> Dict[str, Any]: bot, bot_handler = self._get_handlers() bot_handler.reset_transcript() bot.handle_message(message, bot_handler) diff --git a/zulip_bots/zulip_bots/tests/test_run.py b/zulip_bots/zulip_bots/tests/test_run.py index 7dd59bd..fff76c3 100644 --- a/zulip_bots/zulip_bots/tests/test_run.py +++ b/zulip_bots/zulip_bots/tests/test_run.py @@ -56,8 +56,7 @@ class TestDefaultArguments(TestCase): expected_bot_dir_path = '/path/to' self._test_adding_bot_parent_dir_to_sys_path(bot_qualifier=bot_path, bot_dir_path=expected_bot_dir_path) - def _test_adding_bot_parent_dir_to_sys_path(self, bot_qualifier, bot_dir_path): - # type: (str, str) -> None + def _test_adding_bot_parent_dir_to_sys_path(self, bot_qualifier: str, bot_dir_path: str) -> None: with patch('sys.argv', ['zulip-run-bot', bot_qualifier, '--config-file', '/path/to/config']): with patch('zulip_bots.finder.import_module_from_source', return_value=mock.Mock()): with patch('zulip_bots.run.run_message_handler_for_bot'): diff --git a/zulip_botserver/setup.py b/zulip_botserver/setup.py index c4a2628..ad1e157 100755 --- a/zulip_botserver/setup.py +++ b/zulip_botserver/setup.py @@ -59,8 +59,7 @@ except ImportError: from importlib import import_module # Manual dependency check - def check_dependency_manually(module_name, version=None): - # type: (str, Optional[str]) -> None + def check_dependency_manually(module_name: str, version: Optional[str] = None) -> None: try: module = import_module(module_name) # type: Any if version is not None: