pyupgrade: Reformat with --py36-plus.

This includes mainly fixes of string literals using f-strings or
.format(...), as well as unpacking of list comprehensions.
This commit is contained in:
PIG208 2021-05-28 19:19:40 +08:00 committed by Tim Abbott
parent e27ac0ddbe
commit 9ce7c52a10
78 changed files with 356 additions and 389 deletions

View file

@ -13,7 +13,7 @@ whitespace_rules = [
] # type: List[Rule] ] # type: List[Rule]
markdown_whitespace_rules = list( markdown_whitespace_rules = list(
[rule for rule in whitespace_rules if rule["pattern"] != r"\s+$"] rule for rule in whitespace_rules if rule["pattern"] != r"\s+$"
) + [ ) + [
# Two spaces trailing a line with other content is okay--it's a markdown line break. # Two spaces trailing a line with other content is okay--it's a markdown line break.
# This rule finds one space trailing a non-space, three or more trailing spaces, and # This rule finds one space trailing a non-space, three or more trailing spaces, and

View file

@ -31,14 +31,14 @@ def pack(options: argparse.Namespace) -> None:
print("tools/deploy: No main bot file specified.") print("tools/deploy: No main bot file specified.")
sys.exit(1) sys.exit(1)
if not os.path.isfile(options.config): if not os.path.isfile(options.config):
print("pack: Config file not found at path: {}.".format(options.config)) print(f"pack: Config file not found at path: {options.config}.")
sys.exit(1) sys.exit(1)
if not os.path.isdir(options.path): if not os.path.isdir(options.path):
print("pack: Bot folder not found at path: {}.".format(options.path)) print(f"pack: Bot folder not found at path: {options.path}.")
sys.exit(1) sys.exit(1)
main_path = os.path.join(options.path, options.main) main_path = os.path.join(options.path, options.main)
if not os.path.isfile(main_path): if not os.path.isfile(main_path):
print("pack: Bot main file not found at path: {}.".format(main_path)) print(f"pack: Bot main file not found at path: {main_path}.")
sys.exit(1) sys.exit(1)
# Main logic for packing the bot. # Main logic for packing the bot.
@ -65,7 +65,7 @@ def pack(options: argparse.Namespace) -> None:
) )
zip_file.writestr("config.ini", bot_config) zip_file.writestr("config.ini", bot_config)
zip_file.close() zip_file.close()
print("pack: Created zip file at: {}.".format(zip_file_path)) print(f"pack: Created zip file at: {zip_file_path}.")
def check_common_options(options: argparse.Namespace) -> None: def check_common_options(options: argparse.Namespace) -> None:
@ -83,7 +83,7 @@ def handle_common_response_without_data(
return handle_common_response( return handle_common_response(
response=response, response=response,
operation=operation, operation=operation,
success_handler=lambda r: print("{}: {}".format(operation, success_message)), success_handler=lambda r: print(f"{operation}: {success_message}"),
) )
@ -99,12 +99,12 @@ def handle_common_response(
print("{}: {}".format(operation, response_data["message"])) print("{}: {}".format(operation, response_data["message"]))
return False return False
else: else:
print("{}: Unexpected success response format".format(operation)) print(f"{operation}: Unexpected success response format")
return False return False
if response.status_code == requests.codes.unauthorized: if response.status_code == requests.codes.unauthorized:
print("{}: Authentication error with the server. Aborting.".format(operation)) print(f"{operation}: Authentication error with the server. Aborting.")
else: else:
print("{}: Error {}. Aborting.".format(operation, response.status_code)) print(f"{operation}: Error {response.status_code}. Aborting.")
return False return False
@ -112,7 +112,7 @@ def upload(options: argparse.Namespace) -> None:
check_common_options(options) check_common_options(options)
file_path = os.path.join(bots_dir, options.botname + ".zip") file_path = os.path.join(bots_dir, options.botname + ".zip")
if not os.path.exists(file_path): if not os.path.exists(file_path):
print("upload: Could not find bot package at {}.".format(file_path)) print(f"upload: Could not find bot package at {file_path}.")
sys.exit(1) sys.exit(1)
files = {"file": open(file_path, "rb")} files = {"file": open(file_path, "rb")}
headers = {"key": options.token} headers = {"key": options.token}
@ -129,9 +129,9 @@ def clean(options: argparse.Namespace) -> None:
file_path = os.path.join(bots_dir, options.botname + ".zip") file_path = os.path.join(bots_dir, options.botname + ".zip")
if os.path.exists(file_path): if os.path.exists(file_path):
os.remove(file_path) os.remove(file_path)
print("clean: Removed {}.".format(file_path)) print(f"clean: Removed {file_path}.")
else: else:
print("clean: File '{}' not found.".format(file_path)) print(f"clean: File '{file_path}' not found.")
def process(options: argparse.Namespace) -> None: def process(options: argparse.Namespace) -> None:
@ -341,7 +341,7 @@ To list user's bots, use:
if options.command in commands: if options.command in commands:
commands[options.command](options) commands[options.command](options)
else: else:
print("tools/deploy: No command '{}' found.".format(options.command)) print(f"tools/deploy: No command '{options.command}' found.")
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -41,7 +41,7 @@ the Python version this command is executed with."""
# The output has the format "Python 1.2.3" # The output has the format "Python 1.2.3"
py_version_list = py_version_output.split()[1].split(".") py_version_list = py_version_output.split()[1].split(".")
py_version = tuple(int(num) for num in py_version_list[0:2]) py_version = tuple(int(num) for num in py_version_list[0:2])
venv_name = "zulip-api-py{}-venv".format(py_version[0]) venv_name = f"zulip-api-py{py_version[0]}-venv"
if py_version <= (3, 1) and (not options.force): if py_version <= (3, 1) and (not options.force):
print( print(

View file

@ -62,11 +62,11 @@ def cleanup(package_dir):
build_dir = os.path.join(package_dir, "build") build_dir = os.path.join(package_dir, "build")
temp_dir = os.path.join(package_dir, "temp") temp_dir = os.path.join(package_dir, "temp")
dist_dir = os.path.join(package_dir, "dist") dist_dir = os.path.join(package_dir, "dist")
egg_info = os.path.join(package_dir, "{}.egg-info".format(os.path.basename(package_dir))) egg_info = os.path.join(package_dir, f"{os.path.basename(package_dir)}.egg-info")
def _rm_if_it_exists(directory): def _rm_if_it_exists(directory):
if os.path.isdir(directory): if os.path.isdir(directory):
print(crayons.green("Removing {}/*".format(directory), bold=True)) print(crayons.green(f"Removing {directory}/*", bold=True))
shutil.rmtree(directory) shutil.rmtree(directory)
_rm_if_it_exists(build_dir) _rm_if_it_exists(build_dir)
@ -91,7 +91,7 @@ def set_variable(fp, variable, value):
os.remove(fp) os.remove(fp)
shutil.move(temp_abs_path, fp) shutil.move(temp_abs_path, fp)
message = "Set {variable} in {fp} to {value}.".format(fp=fp, variable=variable, value=value) message = f"Set {variable} in {fp} to {value}."
print(crayons.white(message, bold=True)) print(crayons.white(message, bold=True))
@ -122,8 +122,8 @@ def update_requirements_in_zulip_repo(zulip_repo_dir, version, hash_or_tag):
_edit_reqs_file(prod, zulip_bots_line, zulip_line) _edit_reqs_file(prod, zulip_bots_line, zulip_line)
_edit_reqs_file(dev, zulip_bots_line, zulip_line) _edit_reqs_file(dev, zulip_bots_line, zulip_line)
editable_zulip = '-e "{}"\n'.format(url_zulip.rstrip()) editable_zulip = f'-e "{url_zulip.rstrip()}"\n'
editable_zulip_bots = '-e "{}"\n'.format(url_zulip_bots.rstrip()) editable_zulip_bots = f'-e "{url_zulip_bots.rstrip()}"\n'
_edit_reqs_file( _edit_reqs_file(
common, common,

View file

@ -35,7 +35,7 @@ def check_git_pristine() -> None:
def ensure_on_clean_master() -> None: def ensure_on_clean_master() -> None:
branch = get_git_branch() branch = get_git_branch()
if branch != "master": if branch != "master":
exit("You are still on a feature branch: %s" % (branch,)) exit(f"You are still on a feature branch: {branch}")
check_git_pristine() check_git_pristine()
run("git fetch upstream master") run("git fetch upstream master")
run("git rebase upstream/master") run("git rebase upstream/master")
@ -43,7 +43,7 @@ def ensure_on_clean_master() -> None:
def create_pull_branch(pull_id: int) -> None: def create_pull_branch(pull_id: int) -> None:
run("git fetch upstream pull/%d/head" % (pull_id,)) run("git fetch upstream pull/%d/head" % (pull_id,))
run("git checkout -B review-%s FETCH_HEAD" % (pull_id,)) run(f"git checkout -B review-{pull_id} FETCH_HEAD")
run("git rebase upstream/master") run("git rebase upstream/master")
run("git log upstream/master.. --oneline") run("git log upstream/master.. --oneline")
run("git diff upstream/master.. --name-status") run("git diff upstream/master.. --name-status")

View file

@ -207,7 +207,7 @@ if args.quick:
# run mypy # run mypy
status = 0 status = 0
for repo, python_files in repo_python_files.items(): for repo, python_files in repo_python_files.items():
print("Running mypy for `{}`.".format(repo), flush=True) print(f"Running mypy for `{repo}`.", flush=True)
if python_files: if python_files:
result = subprocess.call([mypy_command] + extra_args + python_files) result = subprocess.call([mypy_command] + extra_args + python_files)
if result != 0: if result != 0:

View file

@ -11,7 +11,7 @@ os.chdir(os.path.dirname(TOOLS_DIR))
def handle_input_and_run_tests_for_package(package_name, path_list): def handle_input_and_run_tests_for_package(package_name, path_list):
parser = argparse.ArgumentParser(description="Run tests for {}.".format(package_name)) parser = argparse.ArgumentParser(description=f"Run tests for {package_name}.")
parser.add_argument( parser.add_argument(
"--coverage", "--coverage",
nargs="?", nargs="?",
@ -31,7 +31,7 @@ def handle_input_and_run_tests_for_package(package_name, path_list):
) )
options = parser.parse_args() options = parser.parse_args()
test_session_title = " Running tests for {} ".format(package_name) test_session_title = f" Running tests for {package_name} "
header = test_session_title.center(shutil.get_terminal_size().columns, "#") header = test_session_title.center(shutil.get_terminal_size().columns, "#")
print(header) print(header)

View file

@ -97,7 +97,7 @@ def main():
if options.pytest: if options.pytest:
excluded_bots = ["merels"] excluded_bots = ["merels"]
pytest_bots_to_test = sorted([bot for bot in bots_to_test if bot not in excluded_bots]) pytest_bots_to_test = sorted(bot for bot in bots_to_test if bot not in excluded_bots)
pytest_options = [ pytest_options = [
"-s", # show output from tests; this hides the progress bar though "-s", # show output from tests; this hides the progress bar though
"-x", # stop on first test failure "-x", # stop on first test failure

View file

@ -46,7 +46,7 @@ class IRCBot(irc.bot.SingleServerIRCBot):
def check_subscription_or_die(self) -> None: def check_subscription_or_die(self) -> None:
resp = self.zulip_client.get_subscriptions() resp = self.zulip_client.get_subscriptions()
if resp["result"] != "success": if resp["result"] != "success":
print("ERROR: %s" % (resp["msg"],)) print("ERROR: {}".format(resp["msg"]))
exit(1) exit(1)
subs = [s["name"] for s in resp["subscriptions"]] subs = [s["name"] for s in resp["subscriptions"]]
if self.stream not in subs: if self.stream not in subs:
@ -61,7 +61,7 @@ class IRCBot(irc.bot.SingleServerIRCBot):
def on_welcome(self, c: ServerConnection, e: Event) -> None: def on_welcome(self, c: ServerConnection, e: Event) -> None:
if len(self.nickserv_password) > 0: if len(self.nickserv_password) > 0:
msg = "identify %s" % (self.nickserv_password,) msg = f"identify {self.nickserv_password}"
c.privmsg("NickServ", msg) c.privmsg("NickServ", msg)
c.join(self.channel) c.join(self.channel)
@ -75,7 +75,7 @@ class IRCBot(irc.bot.SingleServerIRCBot):
in_the_specified_stream = msg["display_recipient"] == self.stream in_the_specified_stream = msg["display_recipient"] == self.stream
at_the_specified_subject = msg["subject"].casefold() == self.topic.casefold() at_the_specified_subject = msg["subject"].casefold() == self.topic.casefold()
if in_the_specified_stream and at_the_specified_subject: if in_the_specified_stream and at_the_specified_subject:
msg["content"] = ("@**%s**: " % (msg["sender_full_name"],)) + msg["content"] msg["content"] = ("@**{}**: ".format(msg["sender_full_name"])) + msg["content"]
send = lambda x: self.c.privmsg(self.channel, x) send = lambda x: self.c.privmsg(self.channel, x)
else: else:
return return
@ -126,7 +126,7 @@ class IRCBot(irc.bot.SingleServerIRCBot):
"type": "stream", "type": "stream",
"to": self.stream, "to": self.stream,
"subject": self.topic, "subject": self.topic,
"content": "**{}**: {}".format(sender, content), "content": f"**{sender}**: {content}",
} }
) )
) )

View file

@ -77,7 +77,7 @@ def matrix_to_zulip(
""" """
content = get_message_content_from_event(event, no_noise) content = get_message_content_from_event(event, no_noise)
zulip_bot_user = "@%s:matrix.org" % (matrix_config["username"],) zulip_bot_user = "@{}:matrix.org".format(matrix_config["username"])
# We do this to identify the messages generated from Zulip -> Matrix # We do this to identify the messages generated from Zulip -> Matrix
# and we make sure we don't forward it again to the Zulip stream. # and we make sure we don't forward it again to the Zulip stream.
not_from_zulip_bot = event["sender"] != zulip_bot_user not_from_zulip_bot = event["sender"] != zulip_bot_user
@ -228,9 +228,7 @@ def read_configuration(config_file: str) -> Dict[str, Dict[str, str]]:
def write_sample_config(target_path: str, zuliprc: Optional[str]) -> None: def write_sample_config(target_path: str, zuliprc: Optional[str]) -> None:
if os.path.exists(target_path): if os.path.exists(target_path):
raise Bridge_ConfigException( raise Bridge_ConfigException(f"Path '{target_path}' exists; not overwriting existing file.")
"Path '{}' exists; not overwriting existing file.".format(target_path)
)
sample_dict = OrderedDict( sample_dict = OrderedDict(
( (
@ -262,7 +260,7 @@ def write_sample_config(target_path: str, zuliprc: Optional[str]) -> None:
if zuliprc is not None: if zuliprc is not None:
if not os.path.exists(zuliprc): if not os.path.exists(zuliprc):
raise Bridge_ConfigException("Zuliprc file '{}' does not exist.".format(zuliprc)) raise Bridge_ConfigException(f"Zuliprc file '{zuliprc}' does not exist.")
zuliprc_config = configparser.ConfigParser() zuliprc_config = configparser.ConfigParser()
try: try:
@ -293,10 +291,10 @@ def main() -> None:
try: try:
write_sample_config(options.sample_config, options.zuliprc) write_sample_config(options.sample_config, options.zuliprc)
except Bridge_ConfigException as exception: except Bridge_ConfigException as exception:
print("Could not write sample config: {}".format(exception)) print(f"Could not write sample config: {exception}")
sys.exit(1) sys.exit(1)
if options.zuliprc is None: if options.zuliprc is None:
print("Wrote sample configuration to '{}'".format(options.sample_config)) print(f"Wrote sample configuration to '{options.sample_config}'")
else: else:
print( print(
"Wrote sample configuration to '{}' using zuliprc file '{}'".format( "Wrote sample configuration to '{}' using zuliprc file '{}'".format(
@ -312,7 +310,7 @@ def main() -> None:
try: try:
config = read_configuration(options.config) config = read_configuration(options.config)
except Bridge_ConfigException as exception: except Bridge_ConfigException as exception:
print("Could not parse config file: {}".format(exception)) print(f"Could not parse config file: {exception}")
sys.exit(1) sys.exit(1)
# Get config for each client # Get config for each client
@ -347,11 +345,11 @@ def main() -> None:
zulip_client.call_on_each_message(zulip_to_matrix(zulip_config, room)) zulip_client.call_on_each_message(zulip_to_matrix(zulip_config, room))
except Bridge_FatalMatrixException as exception: except Bridge_FatalMatrixException as exception:
sys.exit("Matrix bridge error: {}".format(exception)) sys.exit(f"Matrix bridge error: {exception}")
except Bridge_ZulipFatalException as exception: except Bridge_ZulipFatalException as exception:
sys.exit("Zulip bridge error: {}".format(exception)) sys.exit(f"Zulip bridge error: {exception}")
except zulip.ZulipError as exception: except zulip.ZulipError as exception:
sys.exit("Zulip error: {}".format(exception)) sys.exit(f"Zulip error: {exception}")
except Exception: except Exception:
traceback.print_exc() traceback.print_exc()
backoff.fail() backoff.fail()

View file

@ -49,14 +49,14 @@ class MatrixBridgeScriptTests(TestCase):
output_lines = self.output_from_script([]) output_lines = self.output_from_script([])
expected_lines = [ expected_lines = [
"Options required: -c or --config to run, OR --write-sample-config.", "Options required: -c or --config to run, OR --write-sample-config.",
"usage: {} [-h]".format(script_file), f"usage: {script_file} [-h]",
] ]
for expected, output in zip(expected_lines, output_lines): for expected, output in zip(expected_lines, output_lines):
self.assertIn(expected, output) self.assertIn(expected, output)
def test_help_usage_and_description(self) -> None: def test_help_usage_and_description(self) -> None:
output_lines = self.output_from_script(["-h"]) output_lines = self.output_from_script(["-h"])
usage = "usage: {} [-h]".format(script_file) usage = f"usage: {script_file} [-h]"
description = "Script to bridge" description = "Script to bridge"
self.assertIn(usage, output_lines[0]) self.assertIn(usage, output_lines[0])
blank_lines = [num for num, line in enumerate(output_lines) if line == ""] blank_lines = [num for num, line in enumerate(output_lines) if line == ""]
@ -71,7 +71,7 @@ class MatrixBridgeScriptTests(TestCase):
with new_temp_dir() as tempdir: with new_temp_dir() as tempdir:
path = os.path.join(tempdir, sample_config_path) path = os.path.join(tempdir, sample_config_path)
output_lines = self.output_from_script(["--write-sample-config", path]) output_lines = self.output_from_script(["--write-sample-config", path])
self.assertEqual(output_lines, ["Wrote sample configuration to '{}'".format(path)]) self.assertEqual(output_lines, [f"Wrote sample configuration to '{path}'"])
with open(path) as sample_file: with open(path) as sample_file:
self.assertEqual(sample_file.read(), sample_config_text) self.assertEqual(sample_file.read(), sample_config_text)

View file

@ -1,5 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse import argparse
import os import os

View file

@ -56,7 +56,7 @@ while len(json_implementations):
def make_api_call(path: str) -> Optional[List[Dict[str, Any]]]: def make_api_call(path: str) -> Optional[List[Dict[str, Any]]]:
response = requests.get( response = requests.get(
"https://api3.codebasehq.com/%s" % (path,), f"https://api3.codebasehq.com/{path}",
auth=(config.CODEBASE_API_USERNAME, config.CODEBASE_API_KEY), auth=(config.CODEBASE_API_USERNAME, config.CODEBASE_API_KEY),
params={"raw": "True"}, params={"raw": "True"},
headers={ headers={
@ -76,13 +76,13 @@ def make_api_call(path: str) -> Optional[List[Dict[str, Any]]]:
sys.exit(-1) sys.exit(-1)
else: else:
logging.warn( logging.warn(
"Found non-success response status code: %s %s" % (response.status_code, response.text) f"Found non-success response status code: {response.status_code} {response.text}"
) )
return None return None
def make_url(path: str) -> str: def make_url(path: str) -> str:
return "%s/%s" % (config.CODEBASE_ROOT_URL, path) return f"{config.CODEBASE_ROOT_URL}/{path}"
def handle_event(event: Dict[str, Any]) -> None: def handle_event(event: Dict[str, Any]) -> None:
@ -102,11 +102,11 @@ def handle_event(event: Dict[str, Any]) -> None:
project_name = raw_props.get("name") project_name = raw_props.get("name")
project_repo_type = raw_props.get("scm_type") project_repo_type = raw_props.get("scm_type")
url = make_url("projects/%s" % (project_link,)) url = make_url(f"projects/{project_link}")
scm = "of type %s" % (project_repo_type,) if project_repo_type else "" scm = f"of type {project_repo_type}" if project_repo_type else ""
subject = "Repository %s Created" % (project_name,) subject = f"Repository {project_name} Created"
content = "%s created a new repository %s [%s](%s)" % (actor_name, scm, project_name, url) content = f"{actor_name} created a new repository {scm} [{project_name}]({url})"
elif event_type == "push": elif event_type == "push":
stream = config.ZULIP_COMMITS_STREAM_NAME stream = config.ZULIP_COMMITS_STREAM_NAME
@ -117,14 +117,14 @@ def handle_event(event: Dict[str, Any]) -> None:
deleted_ref = raw_props.get("deleted_ref") deleted_ref = raw_props.get("deleted_ref")
new_ref = raw_props.get("new_ref") new_ref = raw_props.get("new_ref")
subject = "Push to %s on %s" % (branch, project) subject = f"Push to {branch} on {project}"
if deleted_ref: if deleted_ref:
content = "%s deleted branch %s from %s" % (actor_name, branch, project) content = f"{actor_name} deleted branch {branch} from {project}"
else: else:
if new_ref: if new_ref:
branch = "new branch %s" % (branch,) branch = f"new branch {branch}"
content = "%s pushed %s commit(s) to %s in project %s:\n\n" % ( content = "{} pushed {} commit(s) to {} in project {}:\n\n".format(
actor_name, actor_name,
num_commits, num_commits,
branch, branch,
@ -132,11 +132,9 @@ def handle_event(event: Dict[str, Any]) -> None:
) )
for commit in raw_props.get("commits"): for commit in raw_props.get("commits"):
ref = commit.get("ref") ref = commit.get("ref")
url = make_url( url = make_url(f"projects/{project_link}/repositories/{repo_link}/commit/{ref}")
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, ref)
)
message = commit.get("message") message = commit.get("message")
content += "* [%s](%s): %s\n" % (ref, url, message) content += f"* [{ref}]({url}): {message}\n"
elif event_type == "ticketing_ticket": elif event_type == "ticketing_ticket":
stream = config.ZULIP_TICKETS_STREAM_NAME stream = config.ZULIP_TICKETS_STREAM_NAME
@ -144,11 +142,11 @@ def handle_event(event: Dict[str, Any]) -> None:
name = raw_props.get("subject") name = raw_props.get("subject")
assignee = raw_props.get("assignee") assignee = raw_props.get("assignee")
priority = raw_props.get("priority") priority = raw_props.get("priority")
url = make_url("projects/%s/tickets/%s" % (project_link, num)) url = make_url(f"projects/{project_link}/tickets/{num}")
if assignee is None: if assignee is None:
assignee = "no one" assignee = "no one"
subject = "#%s: %s" % (num, name) subject = f"#{num}: {name}"
content = ( content = (
"""%s created a new ticket [#%s](%s) priority **%s** assigned to %s:\n\n~~~ quote\n %s""" """%s created a new ticket [#%s](%s) priority **%s** assigned to %s:\n\n~~~ quote\n %s"""
% (actor_name, num, url, priority, assignee, name) % (actor_name, num, url, priority, assignee, name)
@ -161,12 +159,12 @@ def handle_event(event: Dict[str, Any]) -> None:
body = raw_props.get("content") body = raw_props.get("content")
changes = raw_props.get("changes") changes = raw_props.get("changes")
url = make_url("projects/%s/tickets/%s" % (project_link, num)) url = make_url(f"projects/{project_link}/tickets/{num}")
subject = "#%s: %s" % (num, name) subject = f"#{num}: {name}"
content = "" content = ""
if body is not None and len(body) > 0: if body is not None and len(body) > 0:
content = "%s added a comment to ticket [#%s](%s):\n\n~~~ quote\n%s\n\n" % ( content = "{} added a comment to ticket [#{}]({}):\n\n~~~ quote\n{}\n\n".format(
actor_name, actor_name,
num, num,
url, url,
@ -175,7 +173,7 @@ def handle_event(event: Dict[str, Any]) -> None:
if "status_id" in changes: if "status_id" in changes:
status_change = changes.get("status_id") status_change = changes.get("status_id")
content += "Status changed from **%s** to **%s**\n\n" % ( content += "Status changed from **{}** to **{}**\n\n".format(
status_change[0], status_change[0],
status_change[1], status_change[1],
) )
@ -184,10 +182,10 @@ def handle_event(event: Dict[str, Any]) -> None:
name = raw_props.get("name") name = raw_props.get("name")
identifier = raw_props.get("identifier") identifier = raw_props.get("identifier")
url = make_url("projects/%s/milestone/%s" % (project_link, identifier)) url = make_url(f"projects/{project_link}/milestone/{identifier}")
subject = name subject = name
content = "%s created a new milestone [%s](%s)" % (actor_name, name, url) content = f"{actor_name} created a new milestone [{name}]({url})"
elif event_type == "comment": elif event_type == "comment":
stream = config.ZULIP_COMMITS_STREAM_NAME stream = config.ZULIP_COMMITS_STREAM_NAME
@ -198,12 +196,10 @@ def handle_event(event: Dict[str, Any]) -> None:
if commit: if commit:
repo_link = raw_props.get("repository_permalink") repo_link = raw_props.get("repository_permalink")
url = make_url( url = make_url(f"projects/{project_link}/repositories/{repo_link}/commit/{commit}")
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, commit)
)
subject = "%s commented on %s" % (actor_name, commit) subject = f"{actor_name} commented on {commit}"
content = "%s commented on [%s](%s):\n\n~~~ quote\n%s" % ( content = "{} commented on [{}]({}):\n\n~~~ quote\n{}".format(
actor_name, actor_name,
commit, commit,
url, url,
@ -215,13 +211,13 @@ def handle_event(event: Dict[str, Any]) -> None:
category = raw_props.get("category") category = raw_props.get("category")
comment_content = raw_props.get("content") comment_content = raw_props.get("content")
subject = "Discussion: %s" % (subj,) subject = f"Discussion: {subj}"
if category: if category:
format_str = "%s started a new discussion in %s:\n\n~~~ quote\n%s\n~~~" format_str = "%s started a new discussion in %s:\n\n~~~ quote\n%s\n~~~"
content = format_str % (actor_name, category, comment_content) content = format_str % (actor_name, category, comment_content)
else: else:
content = "%s posted:\n\n~~~ quote\n%s\n~~~" % (actor_name, comment_content) content = f"{actor_name} posted:\n\n~~~ quote\n{comment_content}\n~~~"
elif event_type == "deployment": elif event_type == "deployment":
stream = config.ZULIP_COMMITS_STREAM_NAME stream = config.ZULIP_COMMITS_STREAM_NAME
@ -233,19 +229,17 @@ def handle_event(event: Dict[str, Any]) -> None:
repo_link = raw_props.get("repository_permalink") repo_link = raw_props.get("repository_permalink")
start_ref_url = make_url( start_ref_url = make_url(
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, start_ref) f"projects/{project_link}/repositories/{repo_link}/commit/{start_ref}"
)
end_ref_url = make_url(
"projects/%s/repositories/%s/commit/%s" % (project_link, repo_link, end_ref)
) )
end_ref_url = make_url(f"projects/{project_link}/repositories/{repo_link}/commit/{end_ref}")
between_url = make_url( between_url = make_url(
"projects/%s/repositories/%s/compare/%s...%s" "projects/%s/repositories/%s/compare/%s...%s"
% (project_link, repo_link, start_ref, end_ref) % (project_link, repo_link, start_ref, end_ref)
) )
subject = "Deployment to %s" % (environment,) subject = f"Deployment to {environment}"
content = "%s deployed [%s](%s) [through](%s) [%s](%s) to the **%s** environment." % ( content = "{} deployed [{}]({}) [through]({}) [{}]({}) to the **{}** environment.".format(
actor_name, actor_name,
start_ref, start_ref,
start_ref_url, start_ref_url,
@ -256,7 +250,7 @@ def handle_event(event: Dict[str, Any]) -> None:
) )
if servers is not None: if servers is not None:
content += "\n\nServers deployed to: %s" % ( content += "\n\nServers deployed to: %s" % (
", ".join(["`%s`" % (server,) for server in servers]) ", ".join(f"`{server}`" for server in servers)
) )
elif event_type == "named_tree": elif event_type == "named_tree":
@ -270,7 +264,7 @@ def handle_event(event: Dict[str, Any]) -> None:
elif event_type == "sprint_ended": elif event_type == "sprint_ended":
logging.warn("Sprint notifications not yet implemented") logging.warn("Sprint notifications not yet implemented")
else: else:
logging.info("Unknown event type %s, ignoring!" % (event_type,)) logging.info(f"Unknown event type {event_type}, ignoring!")
if subject and content: if subject and content:
if len(subject) > 60: if len(subject) > 60:
@ -280,9 +274,9 @@ def handle_event(event: Dict[str, Any]) -> None:
{"type": "stream", "to": stream, "subject": subject, "content": content} {"type": "stream", "to": stream, "subject": subject, "content": content}
) )
if res["result"] == "success": if res["result"] == "success":
logging.info("Successfully sent Zulip with id: %s" % (res["id"],)) logging.info("Successfully sent Zulip with id: {}".format(res["id"]))
else: else:
logging.warn("Failed to send Zulip: %s %s" % (res["result"], res["msg"])) logging.warn("Failed to send Zulip: {} {}".format(res["result"], res["msg"]))
# the main run loop for this mirror script # the main run loop for this mirror script
@ -300,7 +294,7 @@ def run_mirror() -> None:
else: else:
since = datetime.fromtimestamp(float(timestamp), tz=pytz.utc) since = datetime.fromtimestamp(float(timestamp), tz=pytz.utc)
except (ValueError, OSError) as e: except (ValueError, OSError) as e:
logging.warn("Could not open resume file: %s" % (str(e),)) logging.warn(f"Could not open resume file: {str(e)}")
since = default_since() since = default_since()
try: try:
@ -339,9 +333,7 @@ def check_permissions() -> None:
try: try:
open(config.RESUME_FILE, "a+") open(config.RESUME_FILE, "a+")
except OSError as e: except OSError as e:
sys.stderr.write( sys.stderr.write(f"Could not open up the file {config.RESUME_FILE} for reading and writing")
"Could not open up the file %s for reading and writing" % (config.RESUME_FILE,)
)
sys.stderr.write(str(e)) sys.stderr.write(str(e))

View file

@ -41,14 +41,14 @@ def git_repository_name() -> str:
def git_commit_range(oldrev: str, newrev: str) -> str: def git_commit_range(oldrev: str, newrev: str) -> str:
log_cmd = ["git", "log", "--reverse", "--pretty=%aE %H %s", "%s..%s" % (oldrev, newrev)] log_cmd = ["git", "log", "--reverse", "--pretty=%aE %H %s", f"{oldrev}..{newrev}"]
commits = "" commits = ""
for ln in subprocess.check_output(log_cmd, universal_newlines=True).splitlines(): for ln in subprocess.check_output(log_cmd, universal_newlines=True).splitlines():
author_email, commit_id, subject = ln.split(None, 2) author_email, commit_id, subject = ln.split(None, 2)
if hasattr(config, "format_commit_message"): if hasattr(config, "format_commit_message"):
commits += config.format_commit_message(author_email, subject, commit_id) commits += config.format_commit_message(author_email, subject, commit_id)
else: else:
commits += "!avatar(%s) %s\n" % (author_email, subject) commits += f"!avatar({author_email}) {subject}\n"
return commits return commits
@ -75,19 +75,19 @@ def send_bot_message(oldrev: str, newrev: str, refname: str) -> None:
removed = git_commit_range(newrev, oldrev) removed = git_commit_range(newrev, oldrev)
if oldrev == "0000000000000000000000000000000000000000": if oldrev == "0000000000000000000000000000000000000000":
message = "`%s` was pushed to new branch `%s`" % (new_head, branch) message = f"`{new_head}` was pushed to new branch `{branch}`"
elif newrev == "0000000000000000000000000000000000000000": elif newrev == "0000000000000000000000000000000000000000":
message = "branch `%s` was removed (was `%s`)" % (branch, old_head) message = f"branch `{branch}` was removed (was `{old_head}`)"
elif removed: elif removed:
message = "`%s` was pushed to `%s`, **REMOVING**:\n\n%s" % (new_head, branch, removed) message = f"`{new_head}` was pushed to `{branch}`, **REMOVING**:\n\n{removed}"
if added: if added:
message += "\n**and adding**:\n\n" + added message += "\n**and adding**:\n\n" + added
message += "\n**A HISTORY REWRITE HAS OCCURRED!**" message += "\n**A HISTORY REWRITE HAS OCCURRED!**"
message += "\n@everyone: Please check your local branches to deal with this." message += "\n@everyone: Please check your local branches to deal with this."
elif added: elif added:
message = "`%s` was deployed to `%s` with:\n\n%s" % (new_head, branch, added) message = f"`{new_head}` was deployed to `{branch}` with:\n\n{added}"
else: else:
message = "`%s` was pushed to `%s`... but nothing changed?" % (new_head, branch) message = f"`{new_head}` was pushed to `{branch}`... but nothing changed?"
message_data = { message_data = {
"type": "stream", "type": "stream",

View file

@ -25,7 +25,7 @@ ZULIP_API_KEY = "0123456789abcdef0123456789abcdef"
# And similarly for branch "test-post-receive" (for use when testing). # And similarly for branch "test-post-receive" (for use when testing).
def commit_notice_destination(repo: str, branch: str, commit: str) -> Optional[Dict[str, str]]: def commit_notice_destination(repo: str, branch: str, commit: str) -> Optional[Dict[str, str]]:
if branch in ["master", "test-post-receive"]: if branch in ["master", "test-post-receive"]:
return dict(stream=STREAM_NAME, subject="%s" % (branch,)) return dict(stream=STREAM_NAME, subject=f"{branch}")
# Return None for cases where you don't want a notice sent # Return None for cases where you don't want a notice sent
return None return None
@ -37,7 +37,7 @@ def commit_notice_destination(repo: str, branch: str, commit: str) -> Optional[D
# #
# return '!avatar(%s) [%s](https://example.com/commits/%s)\n' % (author, subject, commit_id) # return '!avatar(%s) [%s](https://example.com/commits/%s)\n' % (author, subject, commit_id)
def format_commit_message(author: str, subject: str, commit_id: str) -> str: def format_commit_message(author: str, subject: str, commit_id: str) -> str:
return "!avatar(%s) %s\n" % (author, subject) return f"!avatar({author}) {subject}\n"
## If properly installed, the Zulip API should be in your import ## If properly installed, the Zulip API should be in your import

View file

@ -174,9 +174,9 @@ def send_reminders() -> Optional[None]:
key = (id, start) key = (id, start)
if key not in sent: if key not in sent:
if start.hour == 0 and start.minute == 0: if start.hour == 0 and start.minute == 0:
line = "%s is today." % (summary,) line = f"{summary} is today."
else: else:
line = "%s starts at %s" % (summary, start.strftime("%H:%M")) line = "{} starts at {}".format(summary, start.strftime("%H:%M"))
print("Sending reminder:", line) print("Sending reminder:", line)
messages.append(line) messages.append(line)
keys.add(key) keys.add(key)

View file

@ -37,7 +37,7 @@ def format_summary_line(
revcount=revcount, s=plural, url=summary_url revcount=revcount, s=plural, url=summary_url
) )
else: else:
formatted_commit_count = "{revcount} commit{s}".format(revcount=revcount, s=plural) formatted_commit_count = f"{revcount} commit{plural}"
return "**{user}** pushed {commits} to **{branch}** (`{tip}:{node}`):\n\n".format( return "**{user}** pushed {commits} to **{branch}** (`{tip}:{node}`):\n\n".format(
user=user, commits=formatted_commit_count, branch=branch, tip=tip, node=node[:12] user=user, commits=formatted_commit_count, branch=branch, tip=tip, node=node[:12]
@ -60,9 +60,9 @@ def format_commit_lines(web_url: str, repo: repo, base: int, tip: int) -> str:
if web_url: if web_url:
summary_url = rev_base_url + str(rev_ctx) summary_url = rev_base_url + str(rev_ctx)
summary = "* [{summary}]({url})".format(summary=one_liner, url=summary_url) summary = f"* [{one_liner}]({summary_url})"
else: else:
summary = "* {summary}".format(summary=one_liner) summary = f"* {one_liner}"
commit_summaries.append(summary) commit_summaries.append(summary)
@ -95,7 +95,7 @@ def get_config(ui: ui, item: str) -> str:
# config returns configuration value. # config returns configuration value.
return ui.config("zulip", item) return ui.config("zulip", item)
except IndexError: except IndexError:
ui.warn("Zulip: Could not find required item {} in hg config.".format(item)) ui.warn(f"Zulip: Could not find required item {item} in hg config.")
sys.exit(1) sys.exit(1)
@ -106,10 +106,10 @@ def hook(ui: ui, repo: repo, **kwargs: str) -> None:
hooktype = kwargs["hooktype"] hooktype = kwargs["hooktype"]
node = kwargs["node"] node = kwargs["node"]
ui.debug("Zulip: received {hooktype} event\n".format(hooktype=hooktype)) ui.debug(f"Zulip: received {hooktype} event\n")
if hooktype != "changegroup": if hooktype != "changegroup":
ui.warn("Zulip: {hooktype} not supported\n".format(hooktype=hooktype)) ui.warn(f"Zulip: {hooktype} not supported\n")
sys.exit(1) sys.exit(1)
ctx = repo[node] ctx = repo[node]
@ -123,14 +123,14 @@ def hook(ui: ui, repo: repo, **kwargs: str) -> None:
# Only send notifications on branches we are watching. # Only send notifications on branches we are watching.
watched_branches = [b.lower().strip() for b in branch_whitelist.split(",")] watched_branches = [b.lower().strip() for b in branch_whitelist.split(",")]
if branch.lower() not in watched_branches: if branch.lower() not in watched_branches:
ui.debug("Zulip: ignoring event for {branch}\n".format(branch=branch)) ui.debug(f"Zulip: ignoring event for {branch}\n")
sys.exit(0) sys.exit(0)
if branch_blacklist: if branch_blacklist:
# Don't send notifications for branches we've ignored. # Don't send notifications for branches we've ignored.
ignored_branches = [b.lower().strip() for b in branch_blacklist.split(",")] ignored_branches = [b.lower().strip() for b in branch_blacklist.split(",")]
if branch.lower() in ignored_branches: if branch.lower() in ignored_branches:
ui.debug("Zulip: ignoring event for {branch}\n".format(branch=branch)) ui.debug(f"Zulip: ignoring event for {branch}\n")
sys.exit(0) sys.exit(0)
# The first and final commits in the changeset. # The first and final commits in the changeset.

View file

@ -65,7 +65,7 @@ def jid_to_zulip(jid: JID) -> str:
suffix = "" suffix = ""
if not jid.username.endswith("-bot"): if not jid.username.endswith("-bot"):
suffix = options.zulip_email_suffix suffix = options.zulip_email_suffix
return "%s%s@%s" % (jid.username, suffix, options.zulip_domain) return f"{jid.username}{suffix}@{options.zulip_domain}"
def zulip_to_jid(email: str, jabber_domain: str) -> JID: def zulip_to_jid(email: str, jabber_domain: str) -> JID:
@ -274,7 +274,7 @@ def get_rooms(zulipToJabber: ZulipToJabberBot) -> List[str]:
ret = method() ret = method()
if ret.get("result") != "success": if ret.get("result") != "success":
logging.error(str(ret)) logging.error(str(ret))
sys.exit("Could not get initial list of Zulip %s" % (key,)) sys.exit(f"Could not get initial list of Zulip {key}")
return ret[key] return ret[key]
if options.mode == "public": if options.mode == "public":
@ -291,7 +291,7 @@ def get_rooms(zulipToJabber: ZulipToJabberBot) -> List[str]:
def config_error(msg: str) -> None: def config_error(msg: str) -> None:
sys.stderr.write("%s\n" % (msg,)) sys.stderr.write(f"{msg}\n")
sys.exit(2) sys.exit(2)
@ -442,10 +442,10 @@ option does not affect login credentials.""".replace(
try: try:
jid = JID(options.jid) jid = JID(options.jid)
except InvalidJID as e: except InvalidJID as e:
config_error("Bad JID: %s: %s" % (options.jid, e.message)) config_error(f"Bad JID: {options.jid}: {e.message}")
if options.conference_domain is None: if options.conference_domain is None:
options.conference_domain = "conference.%s" % (jid.domain,) options.conference_domain = f"conference.{jid.domain}"
xmpp = JabberToZulipBot(jid, options.jabber_password, get_rooms(zulipToJabber)) xmpp = JabberToZulipBot(jid, options.jabber_password, get_rooms(zulipToJabber))

View file

@ -46,12 +46,12 @@ def mkdir_p(path: str) -> None:
def send_log_zulip(file_name: str, count: int, lines: List[str], extra: 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)) content = "{} new errors{}:\n```\n{}\n```".format(count, extra, "\n".join(lines))
zulip_client.send_message( zulip_client.send_message(
{ {
"type": "stream", "type": "stream",
"to": "logs", "to": "logs",
"subject": "%s on %s" % (file_name, platform.node()), "subject": f"{file_name} on {platform.node()}",
"content": content, "content": content,
} }
) )
@ -84,7 +84,7 @@ def process_logs() -> None:
file_data = last_data.get(log_file, {}) file_data = last_data.get(log_file, {})
if not os.path.exists(log_file): if not os.path.exists(log_file):
# If the file doesn't exist, log an error and then move on to the next file # If the file doesn't exist, log an error and then move on to the next file
print("Log file does not exist or could not stat log file: %s" % (log_file,)) print(f"Log file does not exist or could not stat log file: {log_file}")
continue continue
length = int(subprocess.check_output(["wc", "-l", log_file]).split()[0]) length = int(subprocess.check_output(["wc", "-l", log_file]).split()[0])
if file_data.get("last") is None: if file_data.get("last") is None:
@ -95,7 +95,7 @@ def process_logs() -> None:
# a log file ends up at the same line length as before # a log file ends up at the same line length as before
# immediately after rotation, this tool won't notice. # immediately after rotation, this tool won't notice.
file_data["last"] = 1 file_data["last"] = 1
output = subprocess.check_output(["tail", "-n+%s" % (file_data["last"],), log_file]) output = subprocess.check_output(["tail", "-n+{}".format(file_data["last"]), log_file])
new_lines = output.decode("utf-8", errors="replace").split("\n")[:-1] new_lines = output.decode("utf-8", errors="replace").split("\n")[:-1]
if len(new_lines) > 0: if len(new_lines) > 0:
process_lines(new_lines, log_file) process_lines(new_lines, log_file)
@ -124,7 +124,7 @@ if __name__ == "__main__":
try: try:
log_files = json.loads(open(args.control_path).read()) log_files = json.loads(open(args.control_path).read())
except (json.JSONDecodeError, OSError): except (json.JSONDecodeError, OSError):
print("Could not load control data from %s" % (args.control_path,)) print(f"Could not load control data from {args.control_path}")
traceback.print_exc() traceback.print_exc()
sys.exit(1) sys.exit(1)
process_logs() process_logs()

View file

@ -31,16 +31,16 @@ msg = dict(type="stream", to=opts.stream) # type: Dict[str, Any]
if opts.service is None: if opts.service is None:
# Host notification # Host notification
thing = "host" # type: Text thing = "host" # type: Text
msg["subject"] = "host %s" % (opts.host,) msg["subject"] = f"host {opts.host}"
else: else:
# Service notification # Service notification
thing = "service" thing = "service"
msg["subject"] = "service %s on %s" % (opts.service, opts.host) msg["subject"] = f"service {opts.service} on {opts.host}"
if len(msg["subject"]) > 60: if len(msg["subject"]) > 60:
msg["subject"] = msg["subject"][0:57].rstrip() + "..." msg["subject"] = msg["subject"][0:57].rstrip() + "..."
# e.g. **PROBLEM**: service is CRITICAL # e.g. **PROBLEM**: service is CRITICAL
msg["content"] = "**%s**: %s is %s" % (opts.type, thing, opts.state) msg["content"] = f"**{opts.type}**: {thing} is {opts.state}"
# The "long output" can contain newlines represented by "\n" escape sequences. # The "long output" can contain newlines represented by "\n" escape sequences.
# The Nagios mail command uses /usr/bin/printf "%b" to expand these. # The Nagios mail command uses /usr/bin/printf "%b" to expand these.

View file

@ -21,7 +21,7 @@ ZULIP_API_KEY = "0123456789abcdef0123456789abcdef"
# And similarly for branch "test-post-receive" (for use when testing). # And similarly for branch "test-post-receive" (for use when testing).
def deployment_notice_destination(branch: str) -> Optional[Dict[str, str]]: def deployment_notice_destination(branch: str) -> Optional[Dict[str, str]]:
if branch in ["master", "test-post-receive"]: if branch in ["master", "test-post-receive"]:
return dict(stream="deployments", subject="%s" % (branch,)) return dict(stream="deployments", subject=f"{branch}")
# Return None for cases where you don't want a notice sent # Return None for cases where you don't want a notice sent
return None return None
@ -46,7 +46,7 @@ def format_deployment_message(
dep_id: str = "", dep_id: str = "",
dep_time: str = "", dep_time: str = "",
) -> str: ) -> str:
return "Deployed commit `%s` (%s) in [%s](%s)" % (commit_id, branch, app_name, url) return f"Deployed commit `{commit_id}` ({branch}) in [{app_name}]({url})"
## If properly installed, the Zulip API should be in your import ## If properly installed, the Zulip API should be in your import

View file

@ -37,7 +37,7 @@ def commit_notice_destination(path: str, changelist: int) -> Optional[Dict[str,
directory = dirs[2] directory = dirs[2]
if directory not in ["evil-master-plan", "my-super-secret-repository"]: if directory not in ["evil-master-plan", "my-super-secret-repository"]:
return dict(stream="%s-commits" % (directory,), subject=path) return dict(stream=f"{directory}-commits", subject=path)
# Return None for cases where you don't want a notice sent # Return None for cases where you don't want a notice sent
return None return None

View file

@ -105,7 +105,7 @@ try:
mkdir_p(opts.data_dir) mkdir_p(opts.data_dir)
except OSError: except OSError:
# We can't write to the logfile, so just print and give up. # We can't write to the logfile, so just print and give up.
print("Unable to store RSS data at %s." % (opts.data_dir,), file=sys.stderr) print(f"Unable to store RSS data at {opts.data_dir}.", file=sys.stderr)
exit(1) exit(1)
log_file = os.path.join(opts.data_dir, "rss-bot.log") # type: str log_file = os.path.join(opts.data_dir, "rss-bot.log") # type: str
@ -170,7 +170,7 @@ def send_zulip(entry: Any, feed_name: str) -> Dict[str, Any]:
if opts.unwrap: if opts.unwrap:
body = unwrap_text(body) body = unwrap_text(body)
content = "**[%s](%s)**\n%s\n%s" % ( content = "**[{}]({})**\n{}\n{}".format(
entry.title, entry.title,
entry.link, entry.link,
strip_tags(body), strip_tags(body),
@ -194,7 +194,7 @@ try:
with open(opts.feed_file) as f: with open(opts.feed_file) as f:
feed_urls = [feed.strip() for feed in f.readlines()] # type: List[str] feed_urls = [feed.strip() for feed in f.readlines()] # type: List[str]
except OSError: except OSError:
log_error_and_exit("Unable to read feed file at %s." % (opts.feed_file,)) log_error_and_exit(f"Unable to read feed file at {opts.feed_file}.")
client = zulip.Client( client = zulip.Client(
email=opts.zulip_email, email=opts.zulip_email,
@ -245,7 +245,7 @@ for feed_url in feed_urls:
response = send_zulip(entry, feed_name) # type: Dict[str, Any] response = send_zulip(entry, feed_name) # type: Dict[str, Any]
if response["result"] != "success": if response["result"] != "success":
logger.error("Error processing %s" % (feed_url,)) logger.error(f"Error processing {feed_url}")
logger.error(str(response)) logger.error(str(response))
if first_message: if first_message:
# This is probably some fundamental problem like the stream not # This is probably some fundamental problem like the stream not

View file

@ -21,7 +21,7 @@ ZULIP_API_KEY = "0123456789abcdef0123456789abcdef"
def commit_notice_destination(path: str, commit: str) -> Optional[Dict[str, str]]: def commit_notice_destination(path: str, commit: str) -> Optional[Dict[str, str]]:
repo = path.split("/")[-1] repo = path.split("/")[-1]
if repo not in ["evil-master-plan", "my-super-secret-repository"]: if repo not in ["evil-master-plan", "my-super-secret-repository"]:
return dict(stream="commits", subject="%s" % (repo,)) return dict(stream="commits", subject=f"{repo}")
# Return None for cases where you don't want a notice sent # Return None for cases where you don't want a notice sent
return None return None

View file

@ -38,7 +38,7 @@ client = zulip.Client(
def markdown_ticket_url(ticket: Any, heading: str = "ticket") -> 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) return f"[{heading} #{ticket.id}]({config.TRAC_BASE_TICKET_URL}/{ticket.id})"
def markdown_block(desc: str) -> str: def markdown_block(desc: str) -> str:
@ -52,7 +52,7 @@ def truncate(string: str, length: int) -> str:
def trac_subject(ticket: Any) -> str: def trac_subject(ticket: Any) -> str:
return truncate("#%s: %s" % (ticket.id, ticket.values.get("summary")), 60) return truncate("#{}: {}".format(ticket.id, ticket.values.get("summary")), 60)
def send_update(ticket: Any, content: str) -> None: def send_update(ticket: Any, content: str) -> None:
@ -71,7 +71,7 @@ class ZulipPlugin(Component):
def ticket_created(self, ticket: Any) -> None: def ticket_created(self, ticket: Any) -> None:
"""Called when a ticket is created.""" """Called when a ticket is created."""
content = "%s created %s in component **%s**, priority **%s**:\n" % ( content = "{} created {} in component **{}**, priority **{}**:\n".format(
ticket.values.get("reporter"), ticket.values.get("reporter"),
markdown_ticket_url(ticket), markdown_ticket_url(ticket),
ticket.values.get("component"), ticket.values.get("component"),
@ -79,9 +79,9 @@ class ZulipPlugin(Component):
) )
# Include the full subject if it will be truncated # Include the full subject if it will be truncated
if len(ticket.values.get("summary")) > 60: if len(ticket.values.get("summary")) > 60:
content += "**%s**\n" % (ticket.values.get("summary"),) content += "**{}**\n".format(ticket.values.get("summary"))
if ticket.values.get("description") != "": if ticket.values.get("description") != "":
content += "%s" % (markdown_block(ticket.values.get("description")),) content += "{}".format(markdown_block(ticket.values.get("description")))
send_update(ticket, content) send_update(ticket, content)
def ticket_changed( def ticket_changed(
@ -98,26 +98,26 @@ class ZulipPlugin(Component):
): ):
return return
content = "%s updated %s" % (author, markdown_ticket_url(ticket)) content = f"{author} updated {markdown_ticket_url(ticket)}"
if comment: if comment:
content += " with comment: %s\n\n" % (markdown_block(comment),) content += f" with comment: {markdown_block(comment)}\n\n"
else: else:
content += ":\n\n" content += ":\n\n"
field_changes = [] field_changes = []
for key, value in old_values.items(): for key, value in old_values.items():
if key == "description": if key == "description":
content += "- Changed %s from %s\n\nto %s" % ( content += "- Changed {} from {}\n\nto {}".format(
key, key,
markdown_block(value), markdown_block(value),
markdown_block(ticket.values.get(key)), markdown_block(ticket.values.get(key)),
) )
elif old_values.get(key) == "": elif old_values.get(key) == "":
field_changes.append("%s: => **%s**" % (key, ticket.values.get(key))) field_changes.append(f"{key}: => **{ticket.values.get(key)}**")
elif ticket.values.get(key) == "": elif ticket.values.get(key) == "":
field_changes.append('%s: **%s** => ""' % (key, old_values.get(key))) field_changes.append(f'{key}: **{old_values.get(key)}** => ""')
else: else:
field_changes.append( field_changes.append(
"%s: **%s** => **%s**" % (key, old_values.get(key), ticket.values.get(key)) f"{key}: **{old_values.get(key)}** => **{ticket.values.get(key)}**"
) )
content += ", ".join(field_changes) content += ", ".join(field_changes)
@ -125,5 +125,5 @@ class ZulipPlugin(Component):
def ticket_deleted(self, ticket: Any) -> None: def ticket_deleted(self, ticket: Any) -> None:
"""Called when a ticket is deleted.""" """Called when a ticket is deleted."""
content = "%s was deleted." % (markdown_ticket_url(ticket, heading="Ticket"),) content = "{} was deleted.".format(markdown_ticket_url(ticket, heading="Ticket"))
send_update(ticket, content) send_update(ticket, content)

View file

@ -25,7 +25,7 @@ def get_model_id(options):
""" """
trello_api_url = "https://api.trello.com/1/board/{}".format(options.trello_board_id) trello_api_url = f"https://api.trello.com/1/board/{options.trello_board_id}"
params = { params = {
"key": options.trello_api_key, "key": options.trello_api_key,
@ -88,7 +88,7 @@ def create_webhook(options):
""" """
# first, we need to get the idModel # first, we need to get the idModel
print("Getting Trello idModel for the {} board...".format(options.trello_board_name)) print(f"Getting Trello idModel for the {options.trello_board_name} board...")
id_model = get_model_id(options) id_model = get_model_id(options)

View file

@ -202,8 +202,8 @@ for status in statuses[::-1][: opts.limit_tweets]:
continue # Continue with the loop for the next tweet continue # Continue with the loop for the next tweet
# https://twitter.com/eatevilpenguins/status/309995853408530432 # https://twitter.com/eatevilpenguins/status/309995853408530432
composed = "%s (%s)" % (status.user.name, status.user.screen_name) composed = f"{status.user.name} ({status.user.screen_name})"
url = "https://twitter.com/%s/status/%s" % (status.user.screen_name, status.id) url = f"https://twitter.com/{status.user.screen_name}/status/{status.id}"
# This contains all strings that could have caused the tweet to match our query. # This contains all strings that could have caused the tweet to match our query.
text_to_check = [status.text, status.user.screen_name] text_to_check = [status.text, status.user.screen_name]
text_to_check.extend(url.expanded_url for url in status.urls) text_to_check.extend(url.expanded_url for url in status.urls)

View file

@ -143,7 +143,7 @@ for tries in range(10):
missing = 0 missing = 0
for elt in zephyr_subs_to_add: for elt in zephyr_subs_to_add:
if elt not in zephyr_subs: if elt not in zephyr_subs:
logging.error("Failed to subscribe to %s" % (elt,)) logging.error(f"Failed to subscribe to {elt}")
missing += 1 missing += 1
if missing == 0: if missing == 0:
actually_subscribed = True actually_subscribed = True
@ -220,9 +220,7 @@ for key, (stream, test) in zhkeys.items():
) )
print_status_and_exit(1) print_status_and_exit(1)
else: else:
logging.warning( logging.warning(f"Replaced key {key} with {new_key} due to Zephyr server failure.")
"Replaced key %s with %s due to Zephyr server failure." % (key, new_key)
)
receive_zephyrs() receive_zephyrs()
receive_zephyrs() receive_zephyrs()

View file

@ -9,19 +9,19 @@ api_key = sys.argv[2]
ccache_data_encoded = sys.argv[3] ccache_data_encoded = sys.argv[3]
# Update the Kerberos ticket cache file # Update the Kerberos ticket cache file
program_name = "zmirror-%s" % (short_user,) program_name = f"zmirror-{short_user}"
with open("/home/zulip/ccache/%s" % (program_name,), "wb") as f: with open(f"/home/zulip/ccache/{program_name}", "wb") as f:
f.write(base64.b64decode(ccache_data_encoded)) f.write(base64.b64decode(ccache_data_encoded))
# Setup API key # Setup API key
api_key_path = "/home/zulip/api-keys/%s" % (program_name,) api_key_path = f"/home/zulip/api-keys/{program_name}"
open(api_key_path, "w").write(api_key + "\n") open(api_key_path, "w").write(api_key + "\n")
# Setup supervisord configuration # Setup supervisord configuration
supervisor_path = "/etc/supervisor/conf.d/zulip/%s.conf" % (program_name,) supervisor_path = f"/etc/supervisor/conf.d/zulip/{program_name}.conf"
template = os.path.join(os.path.dirname(__file__), "zmirror_private.conf.template") template = os.path.join(os.path.dirname(__file__), "zmirror_private.conf.template")
template_data = open(template).read() template_data = open(template).read()
session_path = "/home/zulip/zephyr_sessions/%s" % (program_name,) session_path = f"/home/zulip/zephyr_sessions/{program_name}"
# Preserve mail zephyrs forwarding setting across rewriting the config file # Preserve mail zephyrs forwarding setting across rewriting the config file

View file

@ -42,7 +42,7 @@ if options.forward_class_messages and not options.noshard:
jobs = list("0123456789abcdef") jobs = list("0123456789abcdef")
def run_job(shard: str) -> int: def run_job(shard: str) -> int:
subprocess.call(args + ["--shard=%s" % (shard,)]) subprocess.call(args + [f"--shard={shard}"])
return 0 return 0
for (status, job) in run_parallel(run_job, jobs, threads=16): for (status, job) in run_parallel(run_job, jobs, threads=16):

View file

@ -54,7 +54,7 @@ def to_zephyr_username(zulip_username: str) -> str:
return user.lower() + "@ATHENA.MIT.EDU" return user.lower() + "@ATHENA.MIT.EDU"
match_user = re.match(r"([a-zA-Z0-9_]+)\|(.+)", user) match_user = re.match(r"([a-zA-Z0-9_]+)\|(.+)", user)
if not match_user: if not match_user:
raise Exception("Could not parse Zephyr realm for cross-realm user %s" % (zulip_username,)) raise Exception(f"Could not parse Zephyr realm for cross-realm user {zulip_username}")
return match_user.group(1).lower() + "@" + match_user.group(2).upper() return match_user.group(1).lower() + "@" + match_user.group(2).upper()
@ -134,10 +134,10 @@ def send_zulip(zeph: ZephyrDict) -> Dict[str, Any]:
# Forward messages sent to -c foo -i bar to stream bar subject "instance" # Forward messages sent to -c foo -i bar to stream bar subject "instance"
if zeph["stream"] == "message": if zeph["stream"] == "message":
message["to"] = zeph["subject"].lower() message["to"] = zeph["subject"].lower()
message["subject"] = "instance %s" % (zeph["subject"],) message["subject"] = "instance {}".format(zeph["subject"])
elif zeph["stream"] == "tabbott-test5": elif zeph["stream"] == "tabbott-test5":
message["to"] = zeph["subject"].lower() message["to"] = zeph["subject"].lower()
message["subject"] = "test instance %s" % (zeph["subject"],) message["subject"] = "test instance {}".format(zeph["subject"])
else: else:
message["to"] = zeph["stream"] message["to"] = zeph["stream"]
else: else:
@ -145,7 +145,7 @@ def send_zulip(zeph: ZephyrDict) -> Dict[str, Any]:
message["content"] = unwrap_lines(zeph["content"]) message["content"] = unwrap_lines(zeph["content"])
if options.test_mode and options.site == DEFAULT_SITE: if options.test_mode and options.site == DEFAULT_SITE:
logger.debug("Message is: %s" % (str(message),)) logger.debug(f"Message is: {str(message)}")
return {"result": "success"} return {"result": "success"}
return zulip_client.send_message(message) return zulip_client.send_message(message)
@ -174,7 +174,7 @@ def zephyr_bulk_subscribe(subs: List[Tuple[str, str, str]]) -> None:
# retrying the next time the bot checks its subscriptions are # retrying the next time the bot checks its subscriptions are
# up to date. # up to date.
logger.exception("Error subscribing to streams (will retry automatically):") logger.exception("Error subscribing to streams (will retry automatically):")
logger.warning("Streams were: %s" % ([cls for cls, instance, recipient in subs],)) logger.warning(f"Streams were: {[cls for cls, instance, recipient in subs]}")
return return
try: try:
actual_zephyr_subs = [cls for (cls, _, _) in zephyr._z.getSubscriptions()] actual_zephyr_subs = [cls for (cls, _, _) in zephyr._z.getSubscriptions()]
@ -186,7 +186,7 @@ def zephyr_bulk_subscribe(subs: List[Tuple[str, str, str]]) -> None:
return return
for (cls, instance, recipient) in subs: for (cls, instance, recipient) in subs:
if cls not in actual_zephyr_subs: if cls not in actual_zephyr_subs:
logger.error("Zephyr failed to subscribe us to %s; will retry" % (cls,)) logger.error(f"Zephyr failed to subscribe us to {cls}; will retry")
try: try:
# We'll retry automatically when we next check for # We'll retry automatically when we next check for
# streams to subscribe to (within 15 seconds), but # streams to subscribe to (within 15 seconds), but
@ -317,7 +317,7 @@ def parse_zephyr_body(zephyr_data: str, notice_format: str) -> Tuple[str, str]:
# Logic based off of owl_zephyr_get_message in barnowl # Logic based off of owl_zephyr_get_message in barnowl
fields = body.split("\x00") fields = body.split("\x00")
if len(fields) == 5: if len(fields) == 5:
body = "New transaction [%s] entered in %s\nFrom: %s (%s)\nSubject: %s" % ( body = "New transaction [{}] entered in {}\nFrom: {} ({})\nSubject: {}".format(
fields[0], fields[0],
fields[1], fields[1],
fields[2], fields[2],
@ -419,7 +419,7 @@ def process_notice(notice: "zephyr.ZNotice", log: Optional[IO[str]]) -> None:
if is_personal and not options.forward_personals: if is_personal and not options.forward_personals:
return return
if (zephyr_class not in current_zephyr_subs) and not is_personal: if (zephyr_class not in current_zephyr_subs) and not is_personal:
logger.debug("Skipping ... %s/%s/%s" % (zephyr_class, notice.instance, is_personal)) logger.debug(f"Skipping ... {zephyr_class}/{notice.instance}/{is_personal}")
return return
if notice.format.startswith("Zephyr error: See") or notice.format.endswith("@(@color(blue))"): if notice.format.startswith("Zephyr error: See") or notice.format.endswith("@(@color(blue))"):
logger.debug("Skipping message we got from Zulip!") logger.debug("Skipping message we got from Zulip!")
@ -471,23 +471,21 @@ def process_notice(notice: "zephyr.ZNotice", log: Optional[IO[str]]) -> None:
if notice.instance.strip() != "": if notice.instance.strip() != "":
zeph["subject"] = notice.instance zeph["subject"] = notice.instance
else: else:
zeph["subject"] = '(instance "%s")' % (notice.instance,) zeph["subject"] = f'(instance "{notice.instance}")'
# Add instances in for instanced personals # Add instances in for instanced personals
if is_personal: if is_personal:
if notice.cls.lower() != "message" and notice.instance.lower != "personal": if notice.cls.lower() != "message" and notice.instance.lower != "personal":
heading = "[-c %s -i %s]\n" % (notice.cls, notice.instance) heading = f"[-c {notice.cls} -i {notice.instance}]\n"
elif notice.cls.lower() != "message": elif notice.cls.lower() != "message":
heading = "[-c %s]\n" % (notice.cls,) heading = f"[-c {notice.cls}]\n"
elif notice.instance.lower() != "personal": elif notice.instance.lower() != "personal":
heading = "[-i %s]\n" % (notice.instance,) heading = f"[-i {notice.instance}]\n"
else: else:
heading = "" heading = ""
zeph["content"] = heading + zeph["content"] zeph["content"] = heading + zeph["content"]
logger.info( logger.info(f"Received a message on {zephyr_class}/{notice.instance} from {notice.sender}...")
"Received a message on %s/%s from %s..." % (zephyr_class, notice.instance, notice.sender)
)
if log is not None: if log is not None:
log.write(json.dumps(zeph) + "\n") log.write(json.dumps(zeph) + "\n")
log.flush() log.flush()
@ -499,7 +497,7 @@ def process_notice(notice: "zephyr.ZNotice", log: Optional[IO[str]]) -> None:
try: try:
res = send_zulip(zeph) res = send_zulip(zeph)
if res.get("result") != "success": if res.get("result") != "success":
logger.error("Error relaying zephyr:\n%s\n%s" % (zeph, res)) logger.error(f"Error relaying zephyr:\n{zeph}\n{res}")
except Exception: except Exception:
logger.exception("Error relaying zephyr:") logger.exception("Error relaying zephyr:")
finally: finally:
@ -630,7 +628,7 @@ def send_zephyr(zwrite_args: List[str], content: str) -> Tuple[int, str]:
logger.info("stdout: " + stdout) logger.info("stdout: " + stdout)
elif stderr: elif stderr:
logger.warning( logger.warning(
"zwrite command '%s' printed the following warning:" % (" ".join(zwrite_args),) "zwrite command '{}' printed the following warning:".format(" ".join(zwrite_args))
) )
if stderr: if stderr:
logger.warning("stderr: " + stderr) logger.warning("stderr: " + stderr)
@ -712,7 +710,7 @@ Feedback button or at support@zulip.com."""
# Forward messages sent to '(instance "WHITESPACE")' back to the # Forward messages sent to '(instance "WHITESPACE")' back to the
# appropriate WHITESPACE instance for bidirectional mirroring # appropriate WHITESPACE instance for bidirectional mirroring
instance = match_whitespace_instance.group(1) instance = match_whitespace_instance.group(1)
elif instance == "instance %s" % (zephyr_class,) or instance == "test instance %s" % ( elif instance == f"instance {zephyr_class}" or instance == "test instance {}".format(
zephyr_class, zephyr_class,
): ):
# Forward messages to e.g. -c -i white-magic back from the # Forward messages to e.g. -c -i white-magic back from the
@ -724,7 +722,7 @@ Feedback button or at support@zulip.com."""
instance = zephyr_class instance = zephyr_class
zephyr_class = "message" zephyr_class = "message"
zwrite_args.extend(["-c", zephyr_class, "-i", instance]) zwrite_args.extend(["-c", zephyr_class, "-i", instance])
logger.info("Forwarding message to class %s, instance %s" % (zephyr_class, instance)) logger.info(f"Forwarding message to class {zephyr_class}, instance {instance}")
elif message["type"] == "private": elif message["type"] == "private":
if len(message["display_recipient"]) == 1: if len(message["display_recipient"]) == 1:
recipient = to_zephyr_username(message["display_recipient"][0]["email"]) recipient = to_zephyr_username(message["display_recipient"][0]["email"])
@ -744,7 +742,7 @@ Feedback button or at support@zulip.com."""
to_zephyr_username(user["email"]).replace("@ATHENA.MIT.EDU", "") to_zephyr_username(user["email"]).replace("@ATHENA.MIT.EDU", "")
for user in message["display_recipient"] for user in message["display_recipient"]
] ]
logger.info("Forwarding message to %s" % (recipients,)) logger.info(f"Forwarding message to {recipients}")
zwrite_args.extend(recipients) zwrite_args.extend(recipients)
if message.get("invite_only_stream"): if message.get("invite_only_stream"):
@ -769,7 +767,7 @@ Zulip users (like you) received it, Zephyr users did not.
zwrite_args.extend(["-O", "crypt"]) zwrite_args.extend(["-O", "crypt"])
if options.test_mode: if options.test_mode:
logger.debug("Would have forwarded: %s\n%s" % (zwrite_args, wrapped_content)) logger.debug(f"Would have forwarded: {zwrite_args}\n{wrapped_content}")
return return
(code, stderr) = send_authed_zephyr(zwrite_args, wrapped_content) (code, stderr) = send_authed_zephyr(zwrite_args, wrapped_content)
@ -849,7 +847,7 @@ def maybe_forward_to_zephyr(message: Dict[str, Any]) -> None:
timestamp_now = int(time.time()) timestamp_now = int(time.time())
if float(message["timestamp"]) < timestamp_now - 15: if float(message["timestamp"]) < timestamp_now - 15:
logger.warning( logger.warning(
"Skipping out of order message: %s < %s" % (message["timestamp"], timestamp_now) "Skipping out of order message: {} < {}".format(message["timestamp"], timestamp_now)
) )
return return
try: try:
@ -932,7 +930,7 @@ def add_zulip_subscriptions(verbose: bool) -> None:
authorization_errors_fatal=False, authorization_errors_fatal=False,
) )
if res.get("result") != "success": if res.get("result") != "success":
logger.error("Error subscribing to streams:\n%s" % (res["msg"],)) logger.error("Error subscribing to streams:\n{}".format(res["msg"]))
return return
already = res.get("already_subscribed") already = res.get("already_subscribed")
@ -940,10 +938,12 @@ def add_zulip_subscriptions(verbose: bool) -> None:
unauthorized = res.get("unauthorized") unauthorized = res.get("unauthorized")
if verbose: if verbose:
if already is not None and len(already) > 0: if already is not None and len(already) > 0:
logger.info("\nAlready subscribed to: %s" % (", ".join(list(already.values())[0]),)) logger.info(
"\nAlready subscribed to: {}".format(", ".join(list(already.values())[0]))
)
if new is not None and len(new) > 0: if new is not None and len(new) > 0:
logger.info( logger.info(
"\nSuccessfully subscribed to: %s" % (", ".join(list(new.values())[0]),) "\nSuccessfully subscribed to: {}".format(", ".join(list(new.values())[0]))
) )
if unauthorized is not None and len(unauthorized) > 0: if unauthorized is not None and len(unauthorized) > 0:
logger.info( logger.info(
@ -962,7 +962,7 @@ on these streams and already use Zulip. They can subscribe you to them via the
""" """
) )
) )
+ "\n\n %s" % (", ".join(unauthorized),) + "\n\n {}".format(", ".join(unauthorized))
) )
if len(skipped) > 0: if len(skipped) > 0:
@ -989,9 +989,9 @@ Zulip subscription to these lines in ~/.zephyr.subs:
for (cls, instance, recipient, reason) in skipped: for (cls, instance, recipient, reason) in skipped:
if verbose: if verbose:
if reason != "": if reason != "":
logger.info(" [%s,%s,%s] (%s)" % (cls, instance, recipient, reason)) logger.info(f" [{cls},{instance},{recipient}] ({reason})")
else: else:
logger.info(" [%s,%s,%s]" % (cls, instance, recipient)) logger.info(f" [{cls},{instance},{recipient}]")
if len(skipped) > 0: if len(skipped) > 0:
if verbose: if verbose:
logger.info( logger.info(
@ -1032,11 +1032,11 @@ def parse_zephyr_subs(verbose: bool = False) -> Set[Tuple[str, str, str]]:
recipient = recipient.replace("%me%", options.user) recipient = recipient.replace("%me%", options.user)
if not valid_stream_name(cls): if not valid_stream_name(cls):
if verbose: if verbose:
logger.error("Skipping subscription to unsupported class name: [%s]" % (line,)) logger.error(f"Skipping subscription to unsupported class name: [{line}]")
continue continue
except Exception: except Exception:
if verbose: if verbose:
logger.error("Couldn't parse ~/.zephyr.subs line: [%s]" % (line,)) logger.error(f"Couldn't parse ~/.zephyr.subs line: [{line}]")
continue continue
zephyr_subscriptions.add((cls.strip(), instance.strip(), recipient.strip())) zephyr_subscriptions.add((cls.strip(), instance.strip(), recipient.strip()))
return zephyr_subscriptions return zephyr_subscriptions
@ -1051,7 +1051,7 @@ def open_logger() -> logging.Logger:
else: else:
log_file = "/var/log/zulip/mirror-log" log_file = "/var/log/zulip/mirror-log"
else: else:
f = tempfile.NamedTemporaryFile(prefix="zulip-log.%s." % (options.user,), delete=False) f = tempfile.NamedTemporaryFile(prefix=f"zulip-log.{options.user}.", delete=False)
log_file = f.name log_file = f.name
# Close the file descriptor, since the logging system will # Close the file descriptor, since the logging system will
# reopen it anyway. # reopen it anyway.
@ -1230,10 +1230,10 @@ or specify the --api-key-file option."""
pgrep_query = "python.*zephyr_mirror" pgrep_query = "python.*zephyr_mirror"
if options.shard is not None: if options.shard is not None:
# sharded class mirror # sharded class mirror
pgrep_query = "%s.*--shard=%s" % (pgrep_query, options.shard) pgrep_query = f"{pgrep_query}.*--shard={options.shard}"
elif options.user is not None: elif options.user is not None:
# Personals mirror on behalf of another user. # Personals mirror on behalf of another user.
pgrep_query = "%s.*--user=%s" % (pgrep_query, options.user) pgrep_query = f"{pgrep_query}.*--user={options.user}"
proc = subprocess.Popen( proc = subprocess.Popen(
["pgrep", "-U", os.environ["USER"], "-f", pgrep_query], ["pgrep", "-U", os.environ["USER"], "-f", pgrep_query],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -1245,7 +1245,7 @@ or specify the --api-key-file option."""
continue continue
# Another copy of zephyr_mirror.py! Kill it. # Another copy of zephyr_mirror.py! Kill it.
logger.info("Killing duplicate zephyr_mirror process %s" % (pid,)) logger.info(f"Killing duplicate zephyr_mirror process {pid}")
try: try:
os.kill(pid, signal.SIGINT) os.kill(pid, signal.SIGINT)
except OSError: except OSError:
@ -1261,7 +1261,7 @@ or specify the --api-key-file option."""
options.forward_mail_zephyrs = subscribed_to_mail_messages() options.forward_mail_zephyrs = subscribed_to_mail_messages()
if options.session_path is None: if options.session_path is None:
options.session_path = "/var/tmp/%s" % (options.user,) options.session_path = f"/var/tmp/{options.user}"
if options.forward_from_zulip: if options.forward_from_zulip:
child_pid = os.fork() # type: Optional[int] child_pid = os.fork() # type: Optional[int]
@ -1278,7 +1278,7 @@ or specify the --api-key-file option."""
logger_name = "zephyr=>zulip" logger_name = "zephyr=>zulip"
if options.shard is not None: if options.shard is not None:
logger_name += "(%s)" % (options.shard,) logger_name += f"({options.shard})"
configure_logger(logger, logger_name) configure_logger(logger, logger_name)
# Have the kernel reap children for when we fork off processes to send Zulips # Have the kernel reap children for when we fork off processes to send Zulips
signal.signal(signal.SIGCHLD, signal.SIG_IGN) signal.signal(signal.SIGCHLD, signal.SIG_IGN)

View file

@ -102,7 +102,7 @@ class RandomExponentialBackoff(CountingBackoff):
# between x and 2x where x is growing exponentially # between x and 2x where x is growing exponentially
delay_scale = int(2 ** (self.number_of_retries / 2.0 - 1)) + 1 delay_scale = int(2 ** (self.number_of_retries / 2.0 - 1)) + 1
delay = min(delay_scale + random.randint(1, delay_scale), self.delay_cap) delay = min(delay_scale + random.randint(1, delay_scale), self.delay_cap)
message = "Sleeping for %ss [max %s] before retrying." % (delay, delay_scale * 2) message = f"Sleeping for {delay}s [max {delay_scale * 2}] before retrying."
try: try:
logger.warning(message) logger.warning(message)
except NameError: except NameError:
@ -124,7 +124,7 @@ def add_default_arguments(
def custom_error_handling(self: argparse.ArgumentParser, message: str) -> None: def custom_error_handling(self: argparse.ArgumentParser, message: str) -> None:
self.print_help(sys.stderr) self.print_help(sys.stderr)
self.exit(2, "{}: error: {}\n".format(self.prog, message)) self.exit(2, f"{self.prog}: error: {message}\n")
parser.error = types.MethodType(custom_error_handling, parser) # type: ignore # patching function parser.error = types.MethodType(custom_error_handling, parser) # type: ignore # patching function
@ -200,22 +200,22 @@ def generate_option_group(parser: optparse.OptionParser, prefix: str = "") -> op
) )
group = optparse.OptionGroup(parser, "Zulip API configuration") group = optparse.OptionGroup(parser, "Zulip API configuration")
group.add_option(f"--{prefix}site", dest="zulip_site", help="Zulip server URI", default=None)
group.add_option(f"--{prefix}api-key", dest="zulip_api_key", action="store")
group.add_option( group.add_option(
"--%ssite" % (prefix,), dest="zulip_site", help="Zulip server URI", default=None f"--{prefix}user",
) dest="zulip_email",
group.add_option("--%sapi-key" % (prefix,), dest="zulip_api_key", action="store") help="Email address of the calling bot or user.",
group.add_option(
"--%suser" % (prefix,), dest="zulip_email", help="Email address of the calling bot or user."
) )
group.add_option( group.add_option(
"--%sconfig-file" % (prefix,), f"--{prefix}config-file",
action="store", action="store",
dest="zulip_config_file", dest="zulip_config_file",
help="Location of an ini file containing the\nabove information. (default ~/.zuliprc)", help="Location of an ini file containing the\nabove information. (default ~/.zuliprc)",
) )
group.add_option("-v", "--verbose", action="store_true", help="Provide detailed output.") group.add_option("-v", "--verbose", action="store_true", help="Provide detailed output.")
group.add_option( group.add_option(
"--%sclient" % (prefix,), f"--{prefix}client",
action="store", action="store",
default=None, default=None,
dest="zulip_client", dest="zulip_client",
@ -428,7 +428,7 @@ class Client:
elif None in (api_key, email): elif None in (api_key, email):
raise ConfigNotFoundError( raise ConfigNotFoundError(
"api_key or email not specified and file %s does not exist" % (config_file,) f"api_key or email not specified and file {config_file} does not exist"
) )
assert api_key is not None and email is not None assert api_key is not None and email is not None
@ -461,7 +461,7 @@ class Client:
self.tls_verification = False # type: Union[bool, str] self.tls_verification = False # type: Union[bool, str]
elif cert_bundle is not None: elif cert_bundle is not None:
if not os.path.isfile(cert_bundle): if not os.path.isfile(cert_bundle):
raise ConfigNotFoundError("tls bundle '%s' does not exist" % (cert_bundle,)) raise ConfigNotFoundError(f"tls bundle '{cert_bundle}' does not exist")
self.tls_verification = cert_bundle self.tls_verification = cert_bundle
else: else:
# Default behavior: verify against system CA certificates # Default behavior: verify against system CA certificates
@ -475,12 +475,10 @@ class Client:
) )
else: # we have a client cert else: # we have a client cert
if not os.path.isfile(client_cert): if not os.path.isfile(client_cert):
raise ConfigNotFoundError("client cert '%s' does not exist" % (client_cert,)) raise ConfigNotFoundError(f"client cert '{client_cert}' does not exist")
if client_cert_key is not None: if client_cert_key is not None:
if not os.path.isfile(client_cert_key): if not os.path.isfile(client_cert_key):
raise ConfigNotFoundError( raise ConfigNotFoundError(f"client cert key '{client_cert_key}' does not exist")
"client cert key '%s' does not exist" % (client_cert_key,)
)
self.client_cert = client_cert self.client_cert = client_cert
self.client_cert_key = client_cert_key self.client_cert_key = client_cert_key
@ -631,7 +629,7 @@ class Client:
# On 50x errors, try again after a short sleep # On 50x errors, try again after a short sleep
if str(res.status_code).startswith("5"): if str(res.status_code).startswith("5"):
if error_retry(" (server %s)" % (res.status_code,)): if error_retry(f" (server {res.status_code})"):
continue continue
# Otherwise fall through and process the python-requests error normally # Otherwise fall through and process the python-requests error normally
except (requests.exceptions.Timeout, requests.exceptions.SSLError) as e: except (requests.exceptions.Timeout, requests.exceptions.SSLError) as e:
@ -650,7 +648,7 @@ class Client:
else: else:
end_error_retry(False) end_error_retry(False)
return { return {
"msg": "Connection error:\n%s" % (traceback.format_exc(),), "msg": f"Connection error:\n{traceback.format_exc()}",
"result": "connection-error", "result": "connection-error",
} }
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
@ -665,13 +663,13 @@ class Client:
continue continue
end_error_retry(False) end_error_retry(False)
return { return {
"msg": "Connection error:\n%s" % (traceback.format_exc(),), "msg": f"Connection error:\n{traceback.format_exc()}",
"result": "connection-error", "result": "connection-error",
} }
except Exception: except Exception:
# We'll split this out into more cases as we encounter new bugs. # We'll split this out into more cases as we encounter new bugs.
return { return {
"msg": "Unexpected error:\n%s" % (traceback.format_exc(),), "msg": f"Unexpected error:\n{traceback.format_exc()}",
"result": "unexpected-error", "result": "unexpected-error",
} }
@ -737,7 +735,7 @@ class Client:
res = self.register(event_types, narrow, **kwargs) res = self.register(event_types, narrow, **kwargs)
if "error" in res["result"]: if "error" in res["result"]:
if self.verbose: if self.verbose:
print("Server returned error:\n%s" % (res["msg"],)) print("Server returned error:\n{}".format(res["msg"]))
time.sleep(1) time.sleep(1)
else: else:
return (res["queue_id"], res["last_event_id"]) return (res["queue_id"], res["last_event_id"])
@ -762,7 +760,7 @@ class Client:
) )
else: else:
if self.verbose: if self.verbose:
print("Server returned error:\n%s" % (res["msg"],)) print("Server returned error:\n{}".format(res["msg"]))
# Eventually, we'll only want the # Eventually, we'll only want the
# BAD_EVENT_QUEUE_ID check, but we check for the # BAD_EVENT_QUEUE_ID check, but we check for the
# old string to support legacy Zulip servers. We # old string to support legacy Zulip servers. We
@ -821,7 +819,7 @@ class Client:
""" """
See examples/get-raw-message for example usage See examples/get-raw-message for example usage
""" """
return self.call_endpoint(url="messages/{}".format(message_id), method="GET") return self.call_endpoint(url=f"messages/{message_id}", method="GET")
def send_message(self, message_data: Dict[str, Any]) -> Dict[str, Any]: def send_message(self, message_data: Dict[str, Any]) -> Dict[str, Any]:
""" """
@ -861,7 +859,7 @@ class Client:
""" """
See examples/delete-message for example usage. See examples/delete-message for example usage.
""" """
return self.call_endpoint(url="messages/{}".format(message_id), method="DELETE") return self.call_endpoint(url=f"messages/{message_id}", method="DELETE")
def update_message_flags(self, update_data: Dict[str, Any]) -> Dict[str, Any]: def update_message_flags(self, update_data: Dict[str, Any]) -> Dict[str, Any]:
""" """
@ -914,7 +912,7 @@ class Client:
""" """
See examples/message-history for example usage. See examples/message-history for example usage.
""" """
return self.call_endpoint(url="messages/{}/history".format(message_id), method="GET") return self.call_endpoint(url=f"messages/{message_id}/history", method="GET")
def add_reaction(self, reaction_data: Dict[str, Any]) -> Dict[str, Any]: def add_reaction(self, reaction_data: Dict[str, Any]) -> Dict[str, Any]:
""" """
@ -965,9 +963,7 @@ class Client:
>>> client.upload_custom_emoji(emoji_name, file_obj) >>> client.upload_custom_emoji(emoji_name, file_obj)
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(f"realm/emoji/{emoji_name}", method="POST", files=[file_obj])
"realm/emoji/{}".format(emoji_name), method="POST", files=[file_obj]
)
def delete_custom_emoji(self, emoji_name: str) -> Dict[str, Any]: def delete_custom_emoji(self, emoji_name: str) -> Dict[str, Any]:
""" """
@ -977,7 +973,7 @@ class Client:
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="realm/emoji/{}".format(emoji_name), url=f"realm/emoji/{emoji_name}",
method="DELETE", method="DELETE",
) )
@ -1027,7 +1023,7 @@ class Client:
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="realm/filters/{}".format(filter_id), url=f"realm/filters/{filter_id}",
method="DELETE", method="DELETE",
) )
@ -1064,7 +1060,7 @@ class Client:
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="realm/profile_fields/{}".format(field_id), url=f"realm/profile_fields/{field_id}",
method="DELETE", method="DELETE",
) )
@ -1089,7 +1085,7 @@ class Client:
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="realm/profile_fields/{}".format(field_id), url=f"realm/profile_fields/{field_id}",
method="PATCH", method="PATCH",
request=request, request=request,
) )
@ -1181,7 +1177,7 @@ class Client:
{'presence': {'website': {'timestamp': 1486799122, 'status': 'active'}}, 'result': 'success', 'msg': ''} {'presence': {'website': {'timestamp': 1486799122, 'status': 'active'}}, 'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="users/%s/presence" % (email,), url=f"users/{email}/presence",
method="GET", method="GET",
) )
@ -1240,7 +1236,7 @@ class Client:
See examples/delete-stream for example usage. See examples/delete-stream for example usage.
""" """
return self.call_endpoint( return self.call_endpoint(
url="streams/{}".format(stream_id), url=f"streams/{stream_id}",
method="DELETE", method="DELETE",
) )
@ -1267,7 +1263,7 @@ class Client:
{'result': 'success', 'msg': '', 'user': [{...}, {...}]} {'result': 'success', 'msg': '', 'user': [{...}, {...}]}
""" """
return self.call_endpoint( return self.call_endpoint(
url="users/{}".format(user_id), url=f"users/{user_id}",
method="GET", method="GET",
request=request, request=request,
) )
@ -1281,7 +1277,7 @@ class Client:
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="users/{}".format(user_id), url=f"users/{user_id}",
method="DELETE", method="DELETE",
) )
@ -1294,7 +1290,7 @@ class Client:
{'result': 'success', 'msg': ''} {'result': 'success', 'msg': ''}
""" """
return self.call_endpoint( return self.call_endpoint(
url="users/{}/reactivate".format(user_id), url=f"users/{user_id}/reactivate",
method="POST", method="POST",
) )
@ -1310,7 +1306,7 @@ class Client:
for key, value in request.items(): for key, value in request.items():
request[key] = json.dumps(value) request[key] = json.dumps(value)
return self.call_endpoint(url="users/{}".format(user_id), method="PATCH", request=request) return self.call_endpoint(url=f"users/{user_id}", method="PATCH", request=request)
def get_users(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: def get_users(self, request: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
""" """
@ -1399,7 +1395,7 @@ class Client:
{'result': 'success', 'msg': '', 'is_subscribed': False} {'result': 'success', 'msg': '', 'is_subscribed': False}
""" """
return self.call_endpoint( return self.call_endpoint(
url="users/{}/subscriptions/{}".format(user_id, stream_id), url=f"users/{user_id}/subscriptions/{stream_id}",
method="GET", method="GET",
) )
@ -1456,7 +1452,7 @@ class Client:
Example usage: client.get_stream_id('devel') Example usage: client.get_stream_id('devel')
""" """
stream_encoded = urllib.parse.quote(stream, safe="") stream_encoded = urllib.parse.quote(stream, safe="")
url = "get_stream_id?stream=%s" % (stream_encoded,) url = f"get_stream_id?stream={stream_encoded}"
return self.call_endpoint( return self.call_endpoint(
url=url, url=url,
method="GET", method="GET",
@ -1467,7 +1463,7 @@ class Client:
""" """
See examples/get-stream-topics for example usage. See examples/get-stream-topics for example usage.
""" """
return self.call_endpoint(url="users/me/{}/topics".format(stream_id), method="GET") return self.call_endpoint(url=f"users/me/{stream_id}/topics", method="GET")
def get_user_groups(self) -> Dict[str, Any]: def get_user_groups(self) -> Dict[str, Any]:
""" """
@ -1521,7 +1517,7 @@ class Client:
{'msg': '', 'result': 'success'} {'msg': '', 'result': 'success'}
""" """
return self.call_endpoint( return self.call_endpoint(
url="user_groups/{}".format(group_id), url=f"user_groups/{group_id}",
method="DELETE", method="DELETE",
) )
@ -1538,7 +1534,7 @@ class Client:
{'msg': '', 'result': 'success'} {'msg': '', 'result': 'success'}
""" """
return self.call_endpoint( return self.call_endpoint(
url="user_groups/{}/members".format(user_group_id), url=f"user_groups/{user_group_id}/members",
method="POST", method="POST",
request=group_data, request=group_data,
) )
@ -1683,7 +1679,7 @@ class Client:
return result return result
if len(result["messages"]) <= 0: if len(result["messages"]) <= 0:
return {"result": "error", "msg": 'No messages found in topic: "{}"'.format(topic)} return {"result": "error", "msg": f'No messages found in topic: "{topic}"'}
message_id = result["messages"][0]["id"] message_id = result["messages"][0]["id"]
@ -1696,7 +1692,7 @@ class Client:
"send_notification_to_new_thread": notify_new_topic, "send_notification_to_new_thread": notify_new_topic,
} }
return self.call_endpoint( return self.call_endpoint(
url="messages/{}".format(message_id), url=f"messages/{message_id}",
method="PATCH", method="PATCH",
request=request, request=request,
) )

View file

@ -15,7 +15,7 @@ Example: edit-stream --stream-id=3 --history-public-to-subscribers
def quote(string: str) -> str: def quote(string: str) -> str:
return '"{}"'.format(string) return f'"{string}"'
parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage)) parser = zulip.add_default_arguments(argparse.ArgumentParser(usage=usage))

View file

@ -38,4 +38,4 @@ response = client.upload_file(file)
try: try:
print("File URI: {}".format(response["uri"])) print("File URI: {}".format(response["uri"]))
except KeyError: except KeyError:
print("Error! API response was: {}".format(response)) print(f"Error! API response was: {response}")

View file

@ -22,7 +22,7 @@ def do_send_message(client: zulip.Client, message_data: Dict[str, Any]) -> bool:
% (message_data["to"], message_data["subject"]) % (message_data["to"], message_data["subject"])
) )
else: else:
log.info("Sending message to %s... " % (message_data["to"],)) log.info("Sending message to {}... ".format(message_data["to"]))
response = client.send_message(message_data) response = client.send_message(message_data)
if response["result"] == "success": if response["result"] == "success":
log.info("Message sent.") log.info("Message sent.")

View file

@ -90,11 +90,11 @@ except ImportError:
except (ImportError, AssertionError): except (ImportError, AssertionError):
if version is not None: if version is not None:
print( print(
"{name}>={version} is not installed.".format(name=module_name, version=version), f"{module_name}>={version} is not installed.",
file=sys.stderr, file=sys.stderr,
) )
else: else:
print("{name} is not installed.".format(name=module_name), file=sys.stderr) print(f"{module_name} is not installed.", file=sys.stderr)
sys.exit(1) sys.exit(1)
check_dependency_manually("zulip") check_dependency_manually("zulip")

View file

@ -73,7 +73,7 @@ class BaremetricsHandler:
if content == ["list-commands"]: if content == ["list-commands"]:
response = "**Available Commands:** \n" response = "**Available Commands:** \n"
for command, description in zip(self.commands, self.descriptions): for command, description in zip(self.commands, self.descriptions):
response += " - {} : {}\n".format(command, description) response += f" - {command} : {description}\n"
bot_handler.send_reply(message, response) bot_handler.send_reply(message, response)
return return
@ -148,7 +148,7 @@ class BaremetricsHandler:
return response return response
def get_plans(self, source_id: str) -> str: def get_plans(self, source_id: str) -> str:
url = "https://api.baremetrics.com/v1/{}/plans".format(source_id) url = f"https://api.baremetrics.com/v1/{source_id}/plans"
plans_response = requests.get(url, headers=self.auth_header) plans_response = requests.get(url, headers=self.auth_header)
plans_data = plans_response.json() plans_data = plans_response.json()
@ -174,7 +174,7 @@ class BaremetricsHandler:
return "\n".join(response) return "\n".join(response)
def get_customers(self, source_id: str) -> str: def get_customers(self, source_id: str) -> str:
url = "https://api.baremetrics.com/v1/{}/customers".format(source_id) url = f"https://api.baremetrics.com/v1/{source_id}/customers"
customers_response = requests.get(url, headers=self.auth_header) customers_response = requests.get(url, headers=self.auth_header)
customers_data = customers_response.json() customers_data = customers_response.json()
@ -203,7 +203,7 @@ class BaremetricsHandler:
return "\n".join(response) return "\n".join(response)
def get_subscriptions(self, source_id: str) -> str: def get_subscriptions(self, source_id: str) -> str:
url = "https://api.baremetrics.com/v1/{}/subscriptions".format(source_id) url = f"https://api.baremetrics.com/v1/{source_id}/subscriptions"
subscriptions_response = requests.get(url, headers=self.auth_header) subscriptions_response = requests.get(url, headers=self.auth_header)
subscriptions_data = subscriptions_response.json() subscriptions_data = subscriptions_response.json()
@ -250,7 +250,7 @@ class BaremetricsHandler:
"interval_count": int(parameters[6]), "interval_count": int(parameters[6]),
} # type: Any } # type: Any
url = "https://api.baremetrics.com/v1/{}/plans".format(parameters[0]) url = f"https://api.baremetrics.com/v1/{parameters[0]}/plans"
create_plan_response = requests.post(url, data=data_header, headers=self.auth_header) create_plan_response = requests.post(url, data=data_header, headers=self.auth_header)
if "error" not in create_plan_response.json(): if "error" not in create_plan_response.json():
return "Plan Created." return "Plan Created."

View file

@ -70,7 +70,7 @@ at syntax by: @mention-botname help"
r.status_code r.status_code
) # Occures in case of unprocessable entity ) # Occures in case of unprocessable entity
else: else:
datapoint_link = "https://www.beeminder.com/{}/{}".format(username, goalname) datapoint_link = f"https://www.beeminder.com/{username}/{goalname}"
return "[Datapoint]({}) created.".format( return "[Datapoint]({}) created.".format(
datapoint_link datapoint_link
) # Handles the case of successful datapoint creation ) # Handles the case of successful datapoint creation

View file

@ -376,7 +376,7 @@ def make_draw_response(reason: str) -> str:
Returns: The draw response string. Returns: The draw response string.
""" """
return "It's a draw because of {}!".format(reason) return f"It's a draw because of {reason}!"
def make_loss_response(board: chess.Board, reason: str) -> str: def make_loss_response(board: chess.Board, reason: str) -> str:
@ -525,7 +525,7 @@ def make_str(board: chess.Board, is_white_on_bottom: bool) -> str:
replaced_and_guided_str if is_white_on_bottom else replaced_and_guided_str[::-1] replaced_and_guided_str if is_white_on_bottom else replaced_and_guided_str[::-1]
) )
trimmed_str = trim_whitespace_before_newline(properly_flipped_str) trimmed_str = trim_whitespace_before_newline(properly_flipped_str)
monospaced_str = "```\n{}\n```".format(trimmed_str) monospaced_str = f"```\n{trimmed_str}\n```"
return monospaced_str return monospaced_str

View file

@ -51,7 +51,7 @@ class DefineHandler:
if not to_define_lower: if not to_define_lower:
return self.EMPTY_WORD_REQUEST_ERROR_MESSAGE return self.EMPTY_WORD_REQUEST_ERROR_MESSAGE
else: else:
response = "**{}**:\n".format(to_define) response = f"**{to_define}**:\n"
try: try:
# Use OwlBot API to fetch definition. # Use OwlBot API to fetch definition.

View file

@ -36,7 +36,7 @@ def get_bot_result(message_content: str, config: Dict[str, str], sender_id: str)
return res_json["result"]["fulfillment"]["speech"] return res_json["result"]["fulfillment"]["speech"]
except Exception as e: except Exception as e:
logging.exception(str(e)) logging.exception(str(e))
return "Error. {}.".format(str(e)) return f"Error. {str(e)}."
class DialogFlowHandler: class DialogFlowHandler:

View file

@ -117,7 +117,7 @@ def syntax_help(cmd_name: str) -> str:
cmd = cmd_name + " " + arg_syntax cmd = cmd_name + " " + arg_syntax
else: else:
cmd = cmd_name cmd = cmd_name
return "syntax: {}".format(cmd) return f"syntax: {cmd}"
def dbx_help(client: Any, cmd_name: str) -> str: def dbx_help(client: Any, cmd_name: str) -> str:
@ -197,7 +197,7 @@ def dbx_read(client: Any, fn: str) -> str:
try: try:
result = client.files_download(fn) result = client.files_download(fn)
msg = "**{}** :\n{}".format(result[0].name, result[1].text) msg = f"**{result[0].name}** :\n{result[1].text}"
except Exception: except Exception:
msg = ( msg = (
"Please provide a correct file path\nUsage: `read <filename>` to read content of a file" "Please provide a correct file path\nUsage: `read <filename>` to read content of a file"

View file

@ -27,14 +27,14 @@ class FileUploaderHandler:
path = Path(os.path.expanduser(content)) path = Path(os.path.expanduser(content))
if not path.is_file(): if not path.is_file():
bot_handler.send_reply(message, "File `{}` not found".format(content)) bot_handler.send_reply(message, f"File `{content}` not found")
return return
path = path.resolve() path = path.resolve()
upload = bot_handler.upload_file_from_path(str(path)) upload = bot_handler.upload_file_from_path(str(path))
if upload["result"] != "success": if upload["result"] != "success":
msg = upload["msg"] msg = upload["msg"]
bot_handler.send_reply(message, "Failed to upload `{}` file: {}".format(path, msg)) bot_handler.send_reply(message, f"Failed to upload `{path}` file: {msg}")
return return
uploaded_file_reply = "[{}]({})".format(path.name, upload["uri"]) uploaded_file_reply = "[{}]({})".format(path.name, upload["uri"])

View file

@ -52,7 +52,7 @@ class FollowupHandler:
def get_bot_followup_response(self, message: Dict[str, str]) -> str: def get_bot_followup_response(self, message: Dict[str, str]) -> str:
original_content = message["content"] original_content = message["content"]
original_sender = message["sender_email"] original_sender = message["sender_email"]
temp_content = "from %s: " % (original_sender,) temp_content = f"from {original_sender}: "
new_content = temp_content + original_content new_content = temp_content + original_content
return new_content return new_content

View file

@ -35,7 +35,7 @@ class FrontHandler:
def help(self, bot_handler: BotHandler) -> str: def help(self, bot_handler: BotHandler) -> str:
response = "" response = ""
for command, description in self.COMMANDS: for command, description in self.COMMANDS:
response += "`{}` {}\n".format(command, description) response += f"`{command}` {description}\n"
return response return response

View file

@ -78,9 +78,9 @@ class TestFrontBot(BotTestCase, DefaultTests):
def _test_command_error(self, command_name: str, command_arg: Optional[str] = None) -> None: def _test_command_error(self, command_name: str, command_arg: Optional[str] = None) -> None:
bot_command = command_name bot_command = command_name
if command_arg: if command_arg:
bot_command += " {}".format(command_arg) bot_command += f" {command_arg}"
with self.mock_config_info({"api_key": "TEST"}): with self.mock_config_info({"api_key": "TEST"}):
with self.mock_http_conversation("{}_error".format(command_name)): with self.mock_http_conversation(f"{command_name}_error"):
self.verify_reply(bot_command, "Something went wrong.") self.verify_reply(bot_command, "Something went wrong.")

View file

@ -1,6 +1,5 @@
from typing import Any, Dict, List from typing import Any, Dict, List
from unittest.mock import patch
from mock import patch
from zulip_bots.game_handler import GameInstance from zulip_bots.game_handler import GameInstance
from zulip_bots.test_lib import BotTestCase, DefaultTests from zulip_bots.test_lib import BotTestCase, DefaultTests
@ -66,8 +65,8 @@ class TestGameHandlerBot(BotTestCase, DefaultTests):
if bot is None: if bot is None:
bot, bot_handler = self._get_handlers() bot, bot_handler = self._get_handlers()
message = { message = {
"sender_email": "{}@example.com".format(name), "sender_email": f"{name}@example.com",
"sender_full_name": "{}".format(name), "sender_full_name": f"{name}",
} }
bot.add_user_to_cache(message) bot.add_user_to_cache(message)
return bot return bot

View file

@ -42,8 +42,8 @@ class GithubHandler:
status = details["state"].title() status = details["state"].title()
message_string = ( message_string = (
"**[{owner}/{repo}#{id}]".format(owner=owner, repo=repo, id=number), f"**[{owner}/{repo}#{number}]",
"({link}) - {title}**\n".format(title=title, link=link), f"({link}) - {title}**\n",
"Created by **[{author}](https://github.com/{author})**\n".format(author=author), "Created by **[{author}](https://github.com/{author})**\n".format(author=author),
"Status - **{status}**\n```quote\n{description}\n```".format( "Status - **{status}**\n```quote\n{description}\n```".format(
status=status, description=description status=status, description=description

View file

@ -32,7 +32,7 @@ def google_search(keywords: str) -> List[Dict[str, str]]:
if a.text.strip() == "Cached" and "webcache.googleusercontent.com" in a["href"]: if a.text.strip() == "Cached" and "webcache.googleusercontent.com" in a["href"]:
continue continue
# a.text: The name of the page # a.text: The name of the page
result = {"url": "https://www.google.com{}".format(link), "name": a.text} result = {"url": f"https://www.google.com{link}", "name": a.text}
results.append(result) results.append(result)
return results return results
@ -61,7 +61,7 @@ def get_google_result(search_keywords: str) -> str:
return "Found Result: [{}]({})".format(results[0]["name"], results[0]["url"]) return "Found Result: [{}]({})".format(results[0]["name"], results[0]["url"])
except Exception as e: except Exception as e:
logging.exception(str(e)) logging.exception(str(e))
return "Error: Search failed. {}.".format(e) return f"Error: Search failed. {e}."
class GoogleSearchHandler: class GoogleSearchHandler:

View file

@ -107,12 +107,12 @@ def get_translate_bot_response(message_content, config_file, author, all_languag
text_to_translate, config_file["key"], target_language, source_language text_to_translate, config_file["key"], target_language, source_language
) )
except requests.exceptions.ConnectionError as conn_err: except requests.exceptions.ConnectionError as conn_err:
return "Could not connect to Google Translate. {}.".format(conn_err) return f"Could not connect to Google Translate. {conn_err}."
except TranslateError as tr_err: except TranslateError as tr_err:
return "Translate Error. {}.".format(tr_err) return f"Translate Error. {tr_err}."
except Exception as err: except Exception as err:
return "Error. {}.".format(err) return f"Error. {err}."
return "{} (from {})".format(translated_text, author) return f"{translated_text} (from {author})"
handler_class = GoogleTranslateHandler handler_class = GoogleTranslateHandler

View file

@ -64,12 +64,12 @@ def api_list_team() -> List[Dict[str, str]]:
def api_show_team(hash_id: str) -> Dict[str, str]: def api_show_team(hash_id: str) -> Dict[str, str]:
return make_API_request("/teams/{}".format(hash_id)) return make_API_request(f"/teams/{hash_id}")
# NOTE: This function is not currently used # NOTE: This function is not currently used
def api_show_users(hash_id: str) -> Any: def api_show_users(hash_id: str) -> Any:
return make_API_request("/teams/{}/members".format(hash_id)) return make_API_request(f"/teams/{hash_id}/members")
def api_list_entries(team_id: Optional[str] = None) -> List[Dict[str, Any]]: def api_list_entries(team_id: Optional[str] = None) -> List[Dict[str, Any]]:
@ -105,7 +105,7 @@ def team_info(team_name: str) -> str:
def entries_list(team_name: str) -> str: def entries_list(team_name: str) -> str:
if team_name: if team_name:
data = api_list_entries(get_team_hash(team_name)) data = api_list_entries(get_team_hash(team_name))
response = "Entries for {}:".format(team_name) response = f"Entries for {team_name}:"
else: else:
data = api_list_entries() data = api_list_entries()
response = "Entries for all teams:" response = "Entries for all teams:"

View file

@ -39,7 +39,7 @@ class IncidentHandler:
bot_response = "Invalid answer format" bot_response = "Invalid answer format"
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
return return
bot_response = "Incident %s\n status = %s" % (ticket_id, answer) bot_response = f"Incident {ticket_id}\n status = {answer}"
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
else: else:
bot_response = 'type "new <description>" for a new incident' bot_response = 'type "new <description>" for a new incident'
@ -125,15 +125,13 @@ def format_incident_for_widget(ticket_id: str, incident: Dict[str, Any]) -> str:
def format_incident_for_markdown(ticket_id: str, incident: Dict[str, Any]) -> str: def format_incident_for_markdown(ticket_id: str, incident: Dict[str, Any]) -> str:
answer_list = "\n".join( answer_list = "\n".join(
[
"* **{code}** {answer}".format( "* **{code}** {answer}".format(
code=code, code=code,
answer=ANSWERS[code], answer=ANSWERS[code],
) )
for code in "1234" for code in "1234"
]
) )
how_to_respond = """**reply**: answer {ticket_id} <code>""".format(ticket_id=ticket_id) how_to_respond = f"""**reply**: answer {ticket_id} <code>"""
content = """ content = """
Incident: {incident} Incident: {incident}

View file

@ -183,7 +183,7 @@ class JiraHandler:
UNKNOWN_VAL = "*unknown*" UNKNOWN_VAL = "*unknown*"
jira_response = requests.get( jira_response = requests.get(
self.domain_with_protocol self.domain_with_protocol
+ "/rest/api/2/search?jql={}&fields=key,summary,status".format(jql_query), + f"/rest/api/2/search?jql={jql_query}&fields=key,summary,status",
headers={"Authorization": self.auth}, headers={"Authorization": self.auth},
).json() ).json()
@ -194,7 +194,7 @@ class JiraHandler:
if errors: if errors:
response = "Oh no! Jira raised an error:\n > " + ", ".join(errors) response = "Oh no! Jira raised an error:\n > " + ", ".join(errors)
else: else:
response = "*Found {} results*\n\n".format(results) response = f"*Found {results} results*\n\n"
for issue in jira_response.get("issues", []): for issue in jira_response.get("issues", []):
fields = issue.get("fields", {}) fields = issue.get("fields", {})
summary = fields.get("summary", UNKNOWN_VAL) summary = fields.get("summary", UNKNOWN_VAL)
@ -314,12 +314,12 @@ class JiraHandler:
response = "Issue *" + key + "* was edited! " + url response = "Issue *" + key + "* was edited! " + url
elif search_match: elif search_match:
search_term = search_match.group("search_term") search_term = search_match.group("search_term")
search_results = self.jql_search("summary ~ {}".format(search_term)) search_results = self.jql_search(f"summary ~ {search_term}")
response = '**Search results for "{}"**\n\n{}'.format(search_term, search_results) response = f'**Search results for "{search_term}"**\n\n{search_results}'
elif jql_match: elif jql_match:
jql_query = jql_match.group("jql_query") jql_query = jql_match.group("jql_query")
search_results = self.jql_search(jql_query) search_results = self.jql_search(jql_query)
response = '**Search results for "{}"**\n\n{}'.format(jql_query, search_results) response = f'**Search results for "{jql_query}"**\n\n{search_results}'
elif help_match: elif help_match:
response = HELP_RESPONSE response = HELP_RESPONSE
else: else:

View file

@ -197,5 +197,5 @@ def check_win(topic_name, merels_storage):
win = mechanics.who_won(topic_name, merels_storage) win = mechanics.who_won(topic_name, merels_storage)
if win != "None": if win != "None":
merels.remove_game(topic_name) merels.remove_game(topic_name)
return "{} wins the game!".format(win) return f"{win} wins the game!"
return "" return ""

View file

@ -278,7 +278,7 @@ def create_room(topic_name, merels_storage):
if merels.create_new_game(topic_name): if merels.create_new_game(topic_name):
response = "" response = ""
response += "A room has been created in {0}. Starting game now.\n".format(topic_name) response += f"A room has been created in {topic_name}. Starting game now.\n"
response += display_game(topic_name, merels_storage) response += display_game(topic_name, merels_storage)
return response return response
@ -404,7 +404,7 @@ def put_man(topic_name, v, h, merels_storage):
data.hill_uid, data.hill_uid,
data.take_mode, data.take_mode,
) )
return "Put a man to ({}, {}) for {}.".format(v, h, data.turn) return f"Put a man to ({v}, {h}) for {data.turn}."
else: else:
raise BadMoveException("Failed: That's not a legal put. Please try again.") raise BadMoveException("Failed: That's not a legal put. Please try again.")
@ -448,7 +448,7 @@ def take_man(topic_name, v, h, merels_storage):
data.hill_uid, data.hill_uid,
data.take_mode, data.take_mode,
) )
return "Taken a man from ({}, {}) for {}.".format(v, h, data.turn) return f"Taken a man from ({v}, {h}) for {data.turn}."
else: else:
raise BadMoveException("Failed: That's not a legal take. Please try again.") raise BadMoveException("Failed: That's not a legal take. Please try again.")

View file

@ -44,7 +44,7 @@ class MerelsModel:
if self.storage.get(self.topic) == '["X", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]': if self.storage.get(self.topic) == '["X", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]':
self.storage.put( self.storage.put(
self.topic, self.topic,
'["{}", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]'.format(self.token[player_number]), f'["{self.token[player_number]}", 0, 0, "NNNNNNNNNNNNNNNNNNNNNNNN", "", 0]',
) )
self.current_board, same_player_move = game.beat(move, self.topic, self.storage) self.current_board, same_player_move = game.beat(move, self.topic, self.storage)
if same_player_move != "": if same_player_move != "":

View file

@ -60,8 +60,8 @@ class TestMerelsBot(BotTestCase, DefaultTests):
if bot is None: if bot is None:
bot, bot_handler = self._get_handlers() bot, bot_handler = self._get_handlers()
message = { message = {
"sender_email": "{}@example.com".format(name), "sender_email": f"{name}@example.com",
"sender_full_name": "{}".format(name), "sender_full_name": f"{name}",
} }
bot.add_user_to_cache(message) bot.add_user_to_cache(message)
return bot return bot

View file

@ -25,15 +25,15 @@ def compose(results: Dict) -> str:
response = "" response = ""
response += "{}\n".format(print_status(results)) response += f"{print_status(results)}\n"
if "success" in response.lower(): if "success" in response.lower():
response += "{}".format(print_test_id(results)) response += f"{print_test_id(results)}"
return response return response
response += "{}\n".format(print_enabled_checkers(results)) response += f"{print_enabled_checkers(results)}\n"
response += "{}\n".format(print_failures_checkers(results)) response += f"{print_failures_checkers(results)}\n"
response += "{}".format(print_more_info_url(results)) response += f"{print_more_info_url(results)}"
return response return response
@ -81,11 +81,11 @@ def print_failures_checkers(results: Dict) -> str:
] # [('seo', 3), ..] ] # [('seo', 3), ..]
failures_checkers_messages = [ failures_checkers_messages = [
"{} ({})".format(fail_checker[0], fail_checker[1]) for fail_checker in failures_checkers f"{fail_checker[0]} ({fail_checker[1]})" for fail_checker in failures_checkers
] ]
failures_checkers_message = ", ".join(failures_checkers_messages) failures_checkers_message = ", ".join(failures_checkers_messages)
return "Failures from checkers: {}".format(failures_checkers_message) return f"Failures from checkers: {failures_checkers_message}"
def get_enabled_checkers(results: Dict) -> List: def get_enabled_checkers(results: Dict) -> List:

View file

@ -57,17 +57,17 @@ def format_result(
output += "**[{}]({}{})**\n".format(record["Name"], login_url, record["Id"]) output += "**[{}]({}{})**\n".format(record["Name"], login_url, record["Id"])
for key, value in record.items(): for key, value in record.items():
if key not in exclude_keys: if key not in exclude_keys:
output += ">**{}**: {}\n".format(key, value) output += f">**{key}**: {value}\n"
else: else:
for i, record in enumerate(result["records"]): for i, record in enumerate(result["records"]):
if rank_output: if rank_output:
output += "{}) ".format(i + 1) output += f"{i + 1}) "
output += "**[{}]({}{})**\n".format(record["Name"], login_url, record["Id"]) output += "**[{}]({}{})**\n".format(record["Name"], login_url, record["Id"])
added_keys = False added_keys = False
for key, value in record.items(): for key, value in record.items():
if key in force_keys or (show_all_keys and key not in exclude_keys): if key in force_keys or (show_all_keys and key not in exclude_keys):
added_keys = True added_keys = True
output += ">**{}**: {}\n".format(key, value) output += f">**{key}**: {value}\n"
if added_keys: if added_keys:
output += "\n" output += "\n"
return output return output
@ -88,7 +88,7 @@ def query_salesforce(
limit = re_limit.search(raw_arg) limit = re_limit.search(raw_arg)
if limit: if limit:
limit_num = int(limit.group().rsplit(" ", 1)[1]) limit_num = int(limit.group().rsplit(" ", 1)[1])
logging.info("Searching with limit {}".format(limit_num)) logging.info(f"Searching with limit {limit_num}")
query = default_query query = default_query
if "query" in command.keys(): if "query" in command.keys():
query = command["query"] query = command["query"]
@ -170,14 +170,14 @@ class SalesforceHandler:
security_token=self.config_info["security_token"], security_token=self.config_info["security_token"],
) )
except simple_salesforce.exceptions.SalesforceAuthenticationFailed as err: except simple_salesforce.exceptions.SalesforceAuthenticationFailed as err:
bot_handler.quit("Failed to log in to Salesforce. {} {}".format(err.code, err.message)) bot_handler.quit(f"Failed to log in to Salesforce. {err.code} {err.message}")
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None:
try: try:
bot_response = self.get_salesforce_response(message["content"]) bot_response = self.get_salesforce_response(message["content"])
bot_handler.send_reply(message, bot_response) bot_handler.send_reply(message, bot_response)
except Exception as e: except Exception as e:
bot_handler.send_reply(message, "Error. {}.".format(e), bot_response) bot_handler.send_reply(message, f"Error. {e}.", bot_response)
handler_class = SalesforceHandler handler_class = SalesforceHandler

View file

@ -151,8 +151,8 @@ class TestTicTacToeBot(BotTestCase, DefaultTests):
if bot is None: if bot is None:
bot, bot_handler = self._get_handlers() bot, bot_handler = self._get_handlers()
message = { message = {
"sender_email": "{}@example.com".format(name), "sender_email": f"{name}@example.com",
"sender_full_name": "{}".format(name), "sender_full_name": f"{name}",
} }
bot.add_user_to_cache(message) bot.add_user_to_cache(message)
return bot return bot

View file

@ -241,14 +241,14 @@ class TicTacToeMessageHandler:
def parse_board(self, board: Any) -> str: def parse_board(self, board: Any) -> str:
"""Takes the board as a nested list and returns a nice version for the user.""" """Takes the board as a nested list and returns a nice version for the user."""
return "".join([self.parse_row(r, r_num) for r_num, r in enumerate(board)]) return "".join(self.parse_row(r, r_num) for r_num, r in enumerate(board))
def get_player_color(self, turn: int) -> str: def get_player_color(self, turn: int) -> str:
return self.tokens[turn] return self.tokens[turn]
def alert_move_message(self, original_player: str, move_info: str) -> str: def alert_move_message(self, original_player: str, move_info: str) -> str:
move_info = move_info.replace("move ", "") move_info = move_info.replace("move ", "")
return "{} put a token at {}".format(original_player, move_info) return f"{original_player} put a token at {move_info}"
def game_start_message(self) -> str: def game_start_message(self) -> str:
return ( return (
@ -299,7 +299,7 @@ def coords_from_command(cmd: str) -> str:
"""As there are various ways to input a coordinate (with/without parentheses, with/without spaces, etc.) the """As there are various ways to input a coordinate (with/without parentheses, with/without spaces, etc.) the
input is stripped to just the numbers before being used in the program.""" input is stripped to just the numbers before being used in the program."""
cmd_num = int(cmd.replace("move ", "")) - 1 cmd_num = int(cmd.replace("move ", "")) - 1
cmd = "{},{}".format((cmd_num % 3) + 1, (cmd_num // 3) + 1) cmd = f"{(cmd_num % 3) + 1},{(cmd_num // 3) + 1}"
return cmd return cmd

View file

@ -30,7 +30,7 @@ class TrelloHandler:
def check_access_token(self, bot_handler: BotHandler) -> None: def check_access_token(self, bot_handler: BotHandler) -> None:
test_query_response = requests.get( test_query_response = requests.get(
"https://api.trello.com/1/members/{}/".format(self.user_name), params=self.auth_params f"https://api.trello.com/1/members/{self.user_name}/", params=self.auth_params
) )
if test_query_response.text == "invalid key": if test_query_response.text == "invalid key":
@ -75,12 +75,12 @@ class TrelloHandler:
def get_all_supported_commands(self) -> str: def get_all_supported_commands(self) -> str:
bot_response = "**Commands:** \n" bot_response = "**Commands:** \n"
for index, (command, desc) in enumerate(supported_commands): for index, (command, desc) in enumerate(supported_commands):
bot_response += "{}. **{}**: {}\n".format(index + 1, command, desc) bot_response += f"{index + 1}. **{command}**: {desc}\n"
return bot_response return bot_response
def get_all_boards(self) -> str: def get_all_boards(self) -> str:
get_board_ids_url = "https://api.trello.com/1/members/{}/".format(self.user_name) get_board_ids_url = f"https://api.trello.com/1/members/{self.user_name}/"
board_ids_response = requests.get(get_board_ids_url, params=self.auth_params) board_ids_response = requests.get(get_board_ids_url, params=self.auth_params)
try: try:
@ -112,7 +112,7 @@ class TrelloHandler:
return INVALID_ARGUMENTS_ERROR_MESSAGE return INVALID_ARGUMENTS_ERROR_MESSAGE
board_id = content[1] board_id = content[1]
get_cards_url = "https://api.trello.com/1/boards/{}/cards".format(board_id) get_cards_url = f"https://api.trello.com/1/boards/{board_id}/cards"
cards_response = requests.get(get_cards_url, params=self.auth_params) cards_response = requests.get(get_cards_url, params=self.auth_params)
try: try:
@ -133,7 +133,7 @@ class TrelloHandler:
return INVALID_ARGUMENTS_ERROR_MESSAGE return INVALID_ARGUMENTS_ERROR_MESSAGE
card_id = content[1] card_id = content[1]
get_checklists_url = "https://api.trello.com/1/cards/{}/checklists/".format(card_id) get_checklists_url = f"https://api.trello.com/1/cards/{card_id}/checklists/"
checklists_response = requests.get(get_checklists_url, params=self.auth_params) checklists_response = requests.get(get_checklists_url, params=self.auth_params)
try: try:
@ -160,7 +160,7 @@ class TrelloHandler:
return INVALID_ARGUMENTS_ERROR_MESSAGE return INVALID_ARGUMENTS_ERROR_MESSAGE
board_id = content[1] board_id = content[1]
get_lists_url = "https://api.trello.com/1/boards/{}/lists".format(board_id) get_lists_url = f"https://api.trello.com/1/boards/{board_id}/lists"
lists_response = requests.get(get_lists_url, params=self.auth_params) lists_response = requests.get(get_lists_url, params=self.auth_params)
try: try:

View file

@ -191,15 +191,13 @@ def format_quiz_for_markdown(quiz_id: str, quiz: Dict[str, Any]) -> str:
question = quiz["question"] question = quiz["question"]
answers = quiz["answers"] answers = quiz["answers"]
answer_list = "\n".join( answer_list = "\n".join(
[
"* **{letter}** {answer}".format( "* **{letter}** {answer}".format(
letter=letter, letter=letter,
answer=answers[letter], answer=answers[letter],
) )
for letter in "ABCD" for letter in "ABCD"
]
) )
how_to_respond = """**reply**: answer {quiz_id} <letter>""".format(quiz_id=quiz_id) how_to_respond = f"""**reply**: answer {quiz_id} <letter>"""
content = """ content = """
Q: {question} Q: {question}

View file

@ -48,7 +48,7 @@ class TwitpostBot:
status = self.post(" ".join(content[1:])) status = self.post(" ".join(content[1:]))
screen_name = status["user"]["screen_name"] screen_name = status["user"]["screen_name"]
id_str = status["id_str"] id_str = status["id_str"]
bot_reply = "https://twitter.com/{}/status/{}".format(screen_name, id_str) bot_reply = f"https://twitter.com/{screen_name}/status/{id_str}"
bot_reply = "Tweet Posted\n" + bot_reply bot_reply = "Tweet Posted\n" + bot_reply
bot_handler.send_reply(message, bot_reply) bot_handler.send_reply(message, bot_reply)

View file

@ -25,7 +25,7 @@ class VirtualFsHandler:
recipient = message["display_recipient"] recipient = message["display_recipient"]
if isinstance(recipient, list): # If not a stream, then hash on list of emails if isinstance(recipient, list): # If not a stream, then hash on list of emails
recipient = " ".join([x["email"] for x in recipient]) recipient = " ".join(x["email"] for x in recipient)
storage = bot_handler.storage storage = bot_handler.storage
if not storage.contains(recipient): if not storage.contains(recipient):
@ -34,7 +34,7 @@ class VirtualFsHandler:
if sender not in fs["user_paths"]: if sender not in fs["user_paths"]:
fs["user_paths"][sender] = "/" fs["user_paths"][sender] = "/"
fs, msg = fs_command(fs, sender, command) fs, msg = fs_command(fs, sender, command)
prependix = "{}:\n".format(sender) prependix = f"{sender}:\n"
msg = prependix + msg msg = prependix + msg
storage.put(recipient, fs) storage.put(recipient, fs)
@ -170,7 +170,7 @@ def syntax_help(cmd_name: str) -> str:
cmd = cmd_name + " " + arg_syntax cmd = cmd_name + " " + arg_syntax
else: else:
cmd = cmd_name cmd = cmd_name
return "syntax: {}".format(cmd) return f"syntax: {cmd}"
def fs_new() -> Dict[str, Any]: def fs_new() -> Dict[str, Any]:
@ -190,7 +190,7 @@ def fs_mkdir(fs: Dict[str, Any], user: str, fn: str) -> Tuple[Dict[str, Any], An
return fs, "ERROR: file already exists" return fs, "ERROR: file already exists"
dir_path = os.path.dirname(path) dir_path = os.path.dirname(path)
if not is_directory(fs, dir_path): if not is_directory(fs, dir_path):
msg = "ERROR: {} is not a directory".format(dir_path) msg = f"ERROR: {dir_path} is not a directory"
return fs, msg return fs, msg
new_fs = fs.copy() new_fs = fs.copy()
new_dir = directory({path}.union(fs[dir_path]["fns"])) new_dir = directory({path}.union(fs[dir_path]["fns"]))
@ -211,7 +211,7 @@ def fs_ls(fs: Dict[str, Any], user: str, fn: str) -> Tuple[Dict[str, Any], Any]:
msg = "ERROR: file does not exist" msg = "ERROR: file does not exist"
return fs, msg return fs, msg
if not is_directory(fs, path): if not is_directory(fs, path):
return fs, "ERROR: {} is not a directory".format(path) return fs, f"ERROR: {path} is not a directory"
fns = fs[path]["fns"] fns = fs[path]["fns"]
if not fns: if not fns:
return fs, "WARNING: directory is empty" return fs, "WARNING: directory is empty"
@ -233,7 +233,7 @@ def fs_rm(fs: Dict[str, Any], user: str, fn: str) -> Tuple[Dict[str, Any], Any]:
msg = "ERROR: file does not exist" msg = "ERROR: file does not exist"
return fs, msg return fs, msg
if fs[path]["kind"] == "dir": if fs[path]["kind"] == "dir":
msg = "ERROR: {} is a directory, file required".format(nice_path(fs, path)) msg = f"ERROR: {nice_path(fs, path)} is a directory, file required"
return fs, msg return fs, msg
new_fs = fs.copy() new_fs = fs.copy()
new_fs.pop(path) new_fs.pop(path)
@ -251,7 +251,7 @@ def fs_rmdir(fs: Dict[str, Any], user: str, fn: str) -> Tuple[Dict[str, Any], An
msg = "ERROR: directory does not exist" msg = "ERROR: directory does not exist"
return fs, msg return fs, msg
if fs[path]["kind"] == "text": if fs[path]["kind"] == "text":
msg = "ERROR: {} is a file, directory required".format(nice_path(fs, path)) msg = f"ERROR: {nice_path(fs, path)} is a file, directory required"
return fs, msg return fs, msg
new_fs = fs.copy() new_fs = fs.copy()
new_fs.pop(path) new_fs.pop(path)
@ -273,7 +273,7 @@ def fs_write(fs: Dict[str, Any], user: str, fn: str, content: str) -> Tuple[Dict
return fs, msg return fs, msg
dir_path = os.path.dirname(path) dir_path = os.path.dirname(path)
if not is_directory(fs, dir_path): if not is_directory(fs, dir_path):
msg = "ERROR: {} is not a directory".format(dir_path) msg = f"ERROR: {dir_path} is not a directory"
return fs, msg return fs, msg
new_fs = fs.copy() new_fs = fs.copy()
new_dir = directory({path}.union(fs[dir_path]["fns"])) new_dir = directory({path}.union(fs[dir_path]["fns"]))
@ -291,7 +291,7 @@ def fs_read(fs: Dict[str, Any], user: str, fn: str) -> Tuple[Dict[str, Any], Any
msg = "ERROR: file does not exist" msg = "ERROR: file does not exist"
return fs, msg return fs, msg
if fs[path]["kind"] == "dir": if fs[path]["kind"] == "dir":
msg = "ERROR: {} is a directory, file required".format(nice_path(fs, path)) msg = f"ERROR: {nice_path(fs, path)} is a directory, file required"
return fs, msg return fs, msg
val = fs[path]["content"] val = fs[path]["content"]
return fs, val return fs, val
@ -305,17 +305,17 @@ def fs_cd(fs: Dict[str, Any], user: str, fn: str) -> Tuple[Dict[str, Any], Any]:
msg = "ERROR: invalid path" msg = "ERROR: invalid path"
return fs, msg return fs, msg
if fs[path]["kind"] == "text": if fs[path]["kind"] == "text":
msg = "ERROR: {} is a file, directory required".format(nice_path(fs, path)) msg = f"ERROR: {nice_path(fs, path)} is a file, directory required"
return fs, msg return fs, msg
fs["user_paths"][user] = path fs["user_paths"][user] = path
return fs, "Current path: {}".format(nice_path(fs, path)) return fs, f"Current path: {nice_path(fs, path)}"
def make_path(fs: Dict[str, Any], user: str, leaf: str) -> List[str]: def make_path(fs: Dict[str, Any], user: str, leaf: str) -> List[str]:
if leaf == "/": if leaf == "/":
return ["/", ""] return ["/", ""]
if leaf.endswith("/"): if leaf.endswith("/"):
return ["", "ERROR: {} is not a valid name".format(leaf)] return ["", f"ERROR: {leaf} is not a valid name"]
if leaf.startswith("/"): if leaf.startswith("/"):
return [leaf, ""] return [leaf, ""]
path = fs["user_paths"][user] path = fs["user_paths"][user]
@ -331,9 +331,9 @@ def nice_path(fs: Dict[str, Any], path: str) -> str:
if path not in fs: if path not in fs:
return "ERROR: the current directory does not exist" return "ERROR: the current directory does not exist"
if fs[path]["kind"] == "text": if fs[path]["kind"] == "text":
path_nice = "{}*{}*".format(path[: slash + 1], path[slash + 1 :]) path_nice = f"{path[: slash + 1]}*{path[slash + 1 :]}*"
elif path != "/": elif path != "/":
path_nice = "{}/".format(path) path_nice = f"{path}/"
return path_nice return path_nice

View file

@ -79,19 +79,15 @@ def get_xkcd_bot_response(message: Dict[str, str], quoted_name: str) -> str:
elif command.isdigit(): elif command.isdigit():
fetched = fetch_xkcd_query(XkcdBotCommand.COMIC_ID, command) fetched = fetch_xkcd_query(XkcdBotCommand.COMIC_ID, command)
else: else:
return commands_help % ( return commands_help % (f"xkcd bot only supports these commands, not `{command}`:",)
"xkcd bot only supports these commands, not `%s`:" % (command,),
)
except (requests.exceptions.ConnectionError, XkcdServerError): except (requests.exceptions.ConnectionError, XkcdServerError):
logging.exception("Connection error occurred when trying to connect to xkcd server") logging.exception("Connection error occurred when trying to connect to xkcd server")
return "Sorry, I cannot process your request right now, please try again later!" return "Sorry, I cannot process your request right now, please try again later!"
except XkcdNotFoundError: except XkcdNotFoundError:
logging.exception( logging.exception(f"XKCD server responded 404 when trying to fetch comic with id {command}")
"XKCD server responded 404 when trying to fetch comic with id %s" % (command,) return f"Sorry, there is likely no xkcd comic strip with id: #{command}"
)
return "Sorry, there is likely no xkcd comic strip with id: #%s" % (command,)
else: else:
return "#%s: **%s**\n[%s](%s)" % ( return "#{}: **{}**\n[{}]({})".format(
fetched["num"], fetched["num"],
fetched["title"], fetched["title"],
fetched["alt"], fetched["alt"],

View file

@ -130,7 +130,7 @@ def get_bot_response(
).strip() ).strip()
for title, id in video_list: for title, id in video_list:
reply = reply + "\n * %s - [Watch now](https://www.youtube.com/watch/%s)" % (title, id) reply = reply + f"\n * {title} - [Watch now](https://www.youtube.com/watch/{id})"
# Using link https://www.youtube.com/watch/<id> to # Using link https://www.youtube.com/watch/<id> to
# prevent showing multiple previews # prevent showing multiple previews
return reply return reply

View file

@ -229,7 +229,7 @@ class GameAdapter:
if sender not in self.user_cache.keys(): if sender not in self.user_cache.keys():
self.add_user_to_cache(message) self.add_user_to_cache(message)
logging.info("Added {} to user cache".format(sender)) logging.info(f"Added {sender} to user cache")
if self.is_single_player: if self.is_single_player:
if content.lower().startswith("start game with") or content.lower().startswith( if content.lower().startswith("start game with") or content.lower().startswith(
@ -300,7 +300,7 @@ class GameAdapter:
self.send_reply(message, self.help_message()) self.send_reply(message, self.help_message())
except Exception as e: except Exception as e:
logging.exception(str(e)) logging.exception(str(e))
self.bot_handler.send_reply(message, "Error {}.".format(e)) self.bot_handler.send_reply(message, f"Error {e}.")
def is_user_in_game(self, user_email: str) -> str: def is_user_in_game(self, user_email: str) -> str:
for instance in self.instances.values(): for instance in self.instances.values():
@ -344,7 +344,7 @@ class GameAdapter:
self.send_reply(message, self.confirm_invitation_accepted(game_id)) self.send_reply(message, self.confirm_invitation_accepted(game_id))
self.broadcast( self.broadcast(
game_id, game_id,
"@**{}** has accepted the invitation.".format(self.get_username_by_email(sender)), f"@**{self.get_username_by_email(sender)}** has accepted the invitation.",
) )
self.start_game_if_ready(game_id) self.start_game_if_ready(game_id)
@ -365,7 +365,7 @@ class GameAdapter:
if len(users) + 1 > self.max_players: if len(users) + 1 > self.max_players:
self.send_reply( self.send_reply(
message, message,
"The maximum number of players for this game is {}.".format(self.max_players), f"The maximum number of players for this game is {self.max_players}.",
) )
return return
game_id = self.generate_game_id() game_id = self.generate_game_id()
@ -411,7 +411,7 @@ class GameAdapter:
self.broadcast(game_id, "Wait... That's me!", include_private=True) self.broadcast(game_id, "Wait... That's me!", include_private=True)
if message["type"] == "stream": if message["type"] == "stream":
self.broadcast( self.broadcast(
game_id, "@**{}** accept".format(self.get_bot_username()), include_private=False game_id, f"@**{self.get_bot_username()}** accept", include_private=False
) )
game_id = self.set_invite_by_user(self.email, True, {"type": "stream"}) game_id = self.set_invite_by_user(self.email, True, {"type": "stream"})
self.start_game_if_ready(game_id) self.start_game_if_ready(game_id)
@ -427,7 +427,7 @@ class GameAdapter:
self.send_reply(message, self.confirm_invitation_declined(game_id)) self.send_reply(message, self.confirm_invitation_declined(game_id))
self.broadcast( self.broadcast(
game_id, game_id,
"@**{}** has declined the invitation.".format(self.get_username_by_email(sender)), f"@**{self.get_username_by_email(sender)}** has declined the invitation.",
) )
if len(self.get_players(game_id, parameter="")) < self.min_players: if len(self.get_players(game_id, parameter="")) < self.min_players:
self.cancel_game(game_id) self.cancel_game(game_id)
@ -440,7 +440,7 @@ class GameAdapter:
if game_id == "": if game_id == "":
self.send_reply(message, "You are not in a game. Type `help` for all commands.") self.send_reply(message, "You are not in a game. Type `help` for all commands.")
sender_name = self.get_username_by_email(sender) sender_name = self.get_username_by_email(sender)
self.cancel_game(game_id, reason="**{}** quit.".format(sender_name)) self.cancel_game(game_id, reason=f"**{sender_name}** quit.")
def command_join(self, message: Dict[str, Any], sender: str, content: str) -> None: def command_join(self, message: Dict[str, Any], sender: str, content: str) -> None:
if not self.is_user_not_player(sender, message): if not self.is_user_not_player(sender, message):
@ -472,7 +472,7 @@ class GameAdapter:
else: else:
self.send_reply( self.send_reply(
message, message,
"Join {} more players to start the game".format(self.max_players - num_players), f"Join {self.max_players - num_players} more players to start the game",
) )
def command_leaderboard(self, message: Dict[str, Any], sender: str, content: str) -> None: def command_leaderboard(self, message: Dict[str, Any], sender: str, content: str) -> None:
@ -483,9 +483,9 @@ class GameAdapter:
raw_headers = ["games_won", "games_drawn", "games_lost", "total_games"] raw_headers = ["games_won", "games_drawn", "games_lost", "total_games"]
headers = ["Player"] + [key.replace("_", " ").title() for key in raw_headers] headers = ["Player"] + [key.replace("_", " ").title() for key in raw_headers]
response += " | ".join(headers) response += " | ".join(headers)
response += "\n" + " | ".join([" --- " for header in headers]) response += "\n" + " | ".join(" --- " for header in headers)
for player, stat in top_stats: for player, stat in top_stats:
response += "\n **{}** | ".format(self.get_username_by_email(player)) response += f"\n **{self.get_username_by_email(player)}** | "
values = [str(stat[key]) for key in raw_headers] values = [str(stat[key]) for key in raw_headers]
response += " | ".join(values) response += " | ".join(values)
self.send_reply(message, response) self.send_reply(message, response)
@ -536,7 +536,7 @@ class GameAdapter:
self.instances[game_id] = GameInstance(self, False, subject, game_id, players, stream) self.instances[game_id] = GameInstance(self, False, subject, game_id, players, stream)
self.broadcast( self.broadcast(
game_id, game_id,
"The game has started in #{} {}".format(stream, self.instances[game_id].subject) f"The game has started in #{stream} {self.instances[game_id].subject}"
+ "\n" + "\n"
+ self.get_formatted_game_object(game_id), + self.get_formatted_game_object(game_id),
) )
@ -564,7 +564,7 @@ class GameAdapter:
return return
self.invites[game_id].update({user_email: "a"}) self.invites[game_id].update({user_email: "a"})
self.broadcast( self.broadcast(
game_id, "@**{}** has joined the game".format(self.get_username_by_email(user_email)) game_id, f"@**{self.get_username_by_email(user_email)}** has joined the game"
) )
self.start_game_if_ready(game_id) self.start_game_if_ready(game_id)
@ -873,7 +873,7 @@ class GameInstance:
def get_player_text(self) -> str: def get_player_text(self) -> str:
player_text = "" player_text = ""
for player in self.players: for player in self.players:
player_text += " @**{}**".format(self.gameAdapter.get_username_by_email(player)) player_text += f" @**{self.gameAdapter.get_username_by_email(player)}**"
return player_text return player_text
def get_start_message(self) -> str: def get_start_message(self) -> str:
@ -890,7 +890,7 @@ class GameInstance:
def handle_message(self, content: str, player_email: str) -> None: def handle_message(self, content: str, player_email: str) -> None:
if content == "forfeit": if content == "forfeit":
player_name = self.gameAdapter.get_username_by_email(player_email) player_name = self.gameAdapter.get_username_by_email(player_email)
self.broadcast("**{}** forfeited!".format(player_name)) self.broadcast(f"**{player_name}** forfeited!")
self.end_game("except:" + player_email) self.end_game("except:" + player_email)
return return
if content == "draw": if content == "draw":
@ -1032,7 +1032,7 @@ class GameInstance:
loser = winner.lstrip("except:") loser = winner.lstrip("except:")
else: else:
winner_name = self.gameAdapter.get_username_by_email(winner) winner_name = self.gameAdapter.get_username_by_email(winner)
self.broadcast("**{}** won! :tada:".format(winner_name)) self.broadcast(f"**{winner_name}** won! :tada:")
for u in self.players: for u in self.players:
values = {"total_games": 1, "games_won": 0, "games_lost": 0, "games_drawn": 0} values = {"total_games": 1, "games_won": 0, "games_lost": 0, "games_drawn": 0}
if loser == "": if loser == "":

View file

@ -139,13 +139,13 @@ class StateHandler:
self._client = client self._client = client
self.marshal = lambda obj: json.dumps(obj) self.marshal = lambda obj: json.dumps(obj)
self.demarshal = lambda obj: json.loads(obj) self.demarshal = lambda obj: json.loads(obj)
self.state_ = dict() self.state_: Dict[str, Any] = dict()
def put(self, key: str, value: Any) -> None: def put(self, key: str, value: Any) -> None:
self.state_[key] = self.marshal(value) self.state_[key] = self.marshal(value)
response = self._client.update_storage({"storage": {key: self.state_[key]}}) response = self._client.update_storage({"storage": {key: self.state_[key]}})
if response["result"] != "success": if response["result"] != "success":
raise StateHandlerError("Error updating state: {}".format(str(response))) raise StateHandlerError(f"Error updating state: {str(response)}")
def get(self, key: str) -> Any: def get(self, key: str) -> Any:
if key in self.state_: if key in self.state_:
@ -423,8 +423,8 @@ def is_private_message_but_not_group_pm(
def display_config_file_errors(error_msg: str, config_file: str) -> None: def display_config_file_errors(error_msg: str, config_file: str) -> None:
file_contents = open(config_file).read() file_contents = open(config_file).read()
print("\nERROR: {} seems to be broken:\n\n{}".format(config_file, file_contents)) print(f"\nERROR: {config_file} seems to be broken:\n\n{file_contents}")
print("\nMore details here:\n\n{}\n".format(error_msg)) print(f"\nMore details here:\n\n{error_msg}\n")
def prepare_message_handler(bot: str, bot_handler: BotHandler, bot_lib_module: Any) -> Any: def prepare_message_handler(bot: str, bot_handler: BotHandler, bot_lib_module: Any) -> Any:
@ -459,7 +459,7 @@ def run_message_handler_for_bot(
bot_details.update(getattr(lib_module.handler_class, "META", {})) bot_details.update(getattr(lib_module.handler_class, "META", {}))
# Make sure you set up your ~/.zuliprc # Make sure you set up your ~/.zuliprc
client_name = "Zulip{}Bot".format(bot_name.capitalize()) client_name = f"Zulip{bot_name.capitalize()}Bot"
try: try:
client = Client(config_file=config_file, client=client_name) client = Client(config_file=config_file, client=client_name)
@ -479,9 +479,7 @@ def run_message_handler_for_bot(
if hasattr(message_handler, "usage"): if hasattr(message_handler, "usage"):
print(message_handler.usage()) print(message_handler.usage())
else: else:
print( print(f"WARNING: {bot_name} is missing usage handler, please add one eventually")
"WARNING: {} is missing usage handler, please add one eventually".format(bot_name)
)
def handle_message(message: Dict[str, Any], flags: List[str]) -> None: def handle_message(message: Dict[str, Any], flags: List[str]) -> None:
logging.info("waiting for next message") logging.info("waiting for next message")

View file

@ -21,7 +21,7 @@ def provision_bot(path_to_bot: str, force: bool) -> None:
req_path = os.path.join(path_to_bot, "requirements.txt") req_path = os.path.join(path_to_bot, "requirements.txt")
if os.path.isfile(req_path): if os.path.isfile(req_path):
bot_name = os.path.basename(path_to_bot) bot_name = os.path.basename(path_to_bot)
logging.info("Installing dependencies for {}...".format(bot_name)) logging.info(f"Installing dependencies for {bot_name}...")
# pip install -r $BASEDIR/requirements.txt -t $BASEDIR/bot_dependencies --quiet # pip install -r $BASEDIR/requirements.txt -t $BASEDIR/bot_dependencies --quiet
rcode = subprocess.call(["pip", "install", "-r", req_path]) rcode = subprocess.call(["pip", "install", "-r", req_path])

View file

@ -63,7 +63,7 @@ def exit_gracefully_if_zulip_config_is_missing(config_file: Optional[str]) -> No
# but we'll catch those later. # but we'll catch those later.
return return
else: else:
error_msg = "ERROR: %s does not exist." % (config_file,) error_msg = f"ERROR: {config_file} does not exist."
else: else:
if zulip_env_vars_are_present(): if zulip_env_vars_are_present():

View file

@ -33,15 +33,13 @@ class MockMessageServer:
return message return message
def add_reaction(self, reaction_data): def add_reaction(self, reaction_data):
return dict( return dict(result="success", msg="", uri=f"https://server/messages/{uuid4()}/reactions")
result="success", msg="", uri="https://server/messages/{}/reactions".format(uuid4())
)
def update(self, message): def update(self, message):
self.messages[message["message_id"]] = message self.messages[message["message_id"]] = message
def upload_file(self, file): def upload_file(self, file):
return dict(result="success", msg="", uri="https://server/user_uploads/{}".format(uuid4())) return dict(result="success", msg="", uri=f"https://server/user_uploads/{uuid4()}")
class TerminalBotHandler: class TerminalBotHandler:

View file

@ -44,7 +44,7 @@ def main():
if lib_module is None: if lib_module is None:
raise OSError raise OSError
except OSError: except OSError:
print("Could not find and import bot '{}'".format(bot_name)) print(f"Could not find and import bot '{bot_name}'")
sys.exit(1) sys.exit(1)
try: try:

View file

@ -27,7 +27,7 @@ def read_bot_fixture_data(bot_name: str, test_name: str) -> Dict[str, Any]:
base_path = os.path.realpath( base_path = os.path.realpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "bots", bot_name, "fixtures") 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)) http_data_path = os.path.join(base_path, f"{test_name}.json")
with open(http_data_path, encoding="utf-8") as f: with open(http_data_path, encoding="utf-8") as f:
content = f.read() content = f.read()
http_data = json.loads(content) http_data = json.loads(content)

View file

@ -72,11 +72,11 @@ except ImportError:
except (ImportError, AssertionError): except (ImportError, AssertionError):
if version is not None: if version is not None:
print( print(
"{name}>={version} is not installed.".format(name=module_name, version=version), f"{module_name}>={version} is not installed.",
file=sys.stderr, file=sys.stderr,
) )
else: else:
print("{name} is not installed.".format(name=module_name), file=sys.stderr) print(f"{module_name} is not installed.", file=sys.stderr)
sys.exit(1) sys.exit(1)
check_dependency_manually("zulip") check_dependency_manually("zulip")

View file

@ -1,9 +1,7 @@
import configparser import configparser
import json import json
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from unittest import TestCase from unittest import TestCase, mock
import mock
from zulip_botserver import server from zulip_botserver import server

View file

@ -6,8 +6,7 @@ from importlib import import_module
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import Any, Dict from typing import Any, Dict
from unittest import mock
import mock
from zulip_bots.lib import BotHandler from zulip_bots.lib import BotHandler
from zulip_botserver import server from zulip_botserver import server

View file

@ -98,7 +98,7 @@ def read_config_file(
ignored_sections = parser.sections()[1:] ignored_sections = parser.sections()[1:]
if len(ignored_sections) > 0: if len(ignored_sections) > 0:
logging.warning("Sections except the '{}' will be ignored".format(bot_section)) logging.warning(f"Sections except the '{bot_section}' will be ignored")
return bots_config return bots_config
@ -106,7 +106,7 @@ def read_config_file(
def parse_config_file(config_file_path: str) -> configparser.ConfigParser: def parse_config_file(config_file_path: str) -> configparser.ConfigParser:
config_file_path = os.path.abspath(os.path.expanduser(config_file_path)) config_file_path = os.path.abspath(os.path.expanduser(config_file_path))
if not os.path.isfile(config_file_path): if not os.path.isfile(config_file_path):
raise OSError("Could not read config file {}: File not found.".format(config_file_path)) raise OSError(f"Could not read config file {config_file_path}: File not found.")
parser = configparser.ConfigParser() parser = configparser.ConfigParser()
parser.read(config_file_path) parser.read(config_file_path)
return parser return parser