Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some more type annotations #800

Merged
merged 4 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tools/provision
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ end_format = "\033[0m"
bold = "\033[1m"


def main():
def main() -> None:
usage = """./tools/provision

Creates a Python virtualenv. Its Python version is equal to
Expand Down Expand Up @@ -94,7 +94,7 @@ the Python version this command is executed with."""
# In order to install all required packages for the venv, `pip` needs to be executed by
# the venv's Python interpreter. `--prefix venv_dir` ensures that all modules are installed
# in the right place.
def install_dependencies(requirements_filename):
def install_dependencies(requirements_filename: str) -> None:
pip_path = os.path.join(venv_dir, venv_exec_dir, "pip")
# We first install a modern version of pip that supports --prefix
subprocess.call([pip_path, "install", "pip>=10"])
Expand Down
32 changes: 10 additions & 22 deletions tools/run-mypy
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import argparse
import os
import subprocess
import sys
from collections import OrderedDict
from pathlib import PurePath
from typing import Dict, List, cast
from typing import List, OrderedDict

from zulint import lister

Expand All @@ -23,13 +22,7 @@ exclude = [
"zulip_bots/zulip_bots/bots",
"zulip_bots/zulip_bots/bots_unmaintained",
# Excluded out of laziness:
"zulip_bots/zulip_bots/simple_lib.py",
"zulip_bots/zulip_bots/tests/test_lib.py",
# Excluded because this is a self-contained script
# we ask our users to download and run directly and
# py2 and py3 compatibility is required.
"zulip/integrations/trello/zulip_trello.py",
"tools",
]

# These files will be included even if excluded by a rule above.
Expand Down Expand Up @@ -97,8 +90,6 @@ force_include = [
"zulip_bots/zulip_bots/bots/susi/test_susi.py",
"zulip_bots/zulip_bots/bots/front/front.py",
"zulip_bots/zulip_bots/bots/front/test_front.py",
"tools/custom_check.py",
"tools/deploy",
]

parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.")
Expand Down Expand Up @@ -158,17 +149,14 @@ if args.all:
exclude = []

# find all non-excluded files in current directory
files_dict = cast(
Dict[str, List[str]],
lister.list_files(
targets=args.targets,
ftypes=["py", "pyi"],
use_shebang=True,
modified_only=args.modified,
exclude=exclude + ["stubs"],
group_by_ftype=True,
extless_only=args.scripts_only,
),
files_dict = lister.list_files(
targets=args.targets,
ftypes=["py", "pyi"],
use_shebang=True,
modified_only=args.modified,
exclude=exclude + ["stubs"],
group_by_ftype=True,
extless_only=args.scripts_only,
)

for inpath in force_include:
Expand All @@ -183,7 +171,7 @@ python_files = [
fpath for fpath in files_dict["py"] if not fpath.endswith(".py") or fpath + "i" not in pyi_files
]

repo_python_files = OrderedDict(
repo_python_files = OrderedDict[str, List[str]](
[("zulip", []), ("zulip_bots", []), ("zulip_botserver", []), ("tools", [])]
)
for file_path in python_files:
Expand Down
22 changes: 6 additions & 16 deletions zulip/integrations/google/get-google-credentials
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
#!/usr/bin/env python3
import argparse
import os
from typing import Optional

from oauth2client import client, tools
from oauth2client.file import Storage

try:
import argparse

flags: Optional[argparse.Namespace] = argparse.ArgumentParser(
parents=[tools.argparser]
).parse_args()
except ImportError:
flags = None
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()

# If modifying these scopes, delete your previously saved credentials
# at zulip/bots/gcal/
Expand Down Expand Up @@ -43,13 +36,10 @@ def get_credentials() -> client.Credentials:
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(os.path.join(HOME_DIR, CLIENT_SECRET_FILE), SCOPES)
flow.user_agent = APPLICATION_NAME
if flags:
# This attempts to open an authorization page in the default web browser, and asks the user
# to grant the bot access to their data. If the user grants permission, the run_flow()
# function returns new credentials.
credentials = tools.run_flow(flow, store, flags)
else: # Needed only for compatibility with Python 2.6
credentials = tools.run(flow, store)
# This attempts to open an authorization page in the default web browser, and asks the user
# to grant the bot access to their data. If the user grants permission, the run_flow()
# function returns new credentials.
credentials = tools.run_flow(flow, store, flags)
print("Storing credentials to " + credential_path)


Expand Down
8 changes: 4 additions & 4 deletions zulip/integrations/trello/zulip_trello.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
sys.exit(1)


def get_model_id(options):
def get_model_id(options: argparse.Namespace) -> str:
"""get_model_id

Get Model Id from Trello API
Expand Down Expand Up @@ -43,7 +43,7 @@ def get_model_id(options):
return board_info_json["id"]


def get_webhook_id(options, id_model):
def get_webhook_id(options: argparse.Namespace, id_model: str) -> str:
"""get_webhook_id

Get webhook id from Trello API
Expand Down Expand Up @@ -78,7 +78,7 @@ def get_webhook_id(options, id_model):
return webhook_info_json["id"]


def create_webhook(options):
def create_webhook(options: argparse.Namespace) -> None:
"""create_webhook

Create Trello webhook
Expand Down Expand Up @@ -107,7 +107,7 @@ def create_webhook(options):
)


def main():
def main() -> None:
description = """
zulip_trello.py is a handy little script that allows Zulip users to
quickly set up a Trello webhook.
Expand Down
43 changes: 22 additions & 21 deletions zulip_bots/zulip_bots/simple_lib.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,62 @@
import configparser
import sys
from typing import IO, Any, Dict, Optional
from uuid import uuid4

from zulip_bots.lib import BotIdentity


class SimpleStorage:
def __init__(self):
self.data = dict()
def __init__(self) -> None:
self.data: Dict[str, Any] = dict()

def contains(self, key):
def contains(self, key: str) -> bool:
return key in self.data

def put(self, key, value):
def put(self, key: str, value: Any) -> None:
self.data[key] = value

def get(self, key):
def get(self, key: str) -> Any:
return self.data[key]


class MockMessageServer:
# This class is needed for the incrementor bot, which
# actually updates messages!
def __init__(self):
def __init__(self) -> None:
self.message_id = 0
self.messages = dict()
self.messages: Dict[int, Dict[str, Any]] = dict()

def send(self, message):
def send(self, message: Dict[str, Any]) -> Dict[str, Any]:
self.message_id += 1
message["id"] = self.message_id
self.messages[self.message_id] = message
return message

def add_reaction(self, reaction_data):
def add_reaction(self, reaction_data: object) -> Dict[str, Any]:
return dict(result="success", msg="", uri=f"https://server/messages/{uuid4()}/reactions")

def update(self, message):
def update(self, message: Dict[str, Any]) -> None:
self.messages[message["message_id"]] = message

def upload_file(self, file):
def upload_file(self, file: IO[Any]) -> Dict[str, Any]:
return dict(result="success", msg="", uri=f"https://server/user_uploads/{uuid4()}")


class TerminalBotHandler:
def __init__(self, bot_config_file, message_server):
def __init__(self, bot_config_file: Optional[str], message_server: MockMessageServer) -> None:
self.bot_config_file = bot_config_file
self._storage = SimpleStorage()
self.message_server = message_server

@property
def storage(self):
def storage(self) -> SimpleStorage:
return self._storage

def identity(self):
def identity(self) -> BotIdentity:
return BotIdentity("bot name", "bot-email@domain")

def react(self, message, emoji_name):
def react(self, message: Dict[str, Any], emoji_name: str) -> Dict[str, Any]:
"""
Mock adding an emoji reaction and print it in the terminal.
"""
Expand All @@ -64,7 +65,7 @@ def react(self, message, emoji_name):
dict(message_id=message["id"], emoji_name=emoji_name, reaction_type="unicode_emoji")
)

def send_message(self, message):
def send_message(self, message: Dict[str, Any]) -> Dict[str, Any]:
"""
Print the message sent in the terminal and store it in a mock message server.
"""
Expand All @@ -90,7 +91,7 @@ def send_message(self, message):
# id to the message instead of actually displaying it.
return self.message_server.send(message)

def send_reply(self, message, response):
def send_reply(self, message: Dict[str, Any], response: str) -> Dict[str, Any]:
"""
Print the reply message in the terminal and store it in a mock message server.
"""
Expand All @@ -102,7 +103,7 @@ def send_reply(self, message, response):
response_message = dict(content=response)
return self.message_server.send(response_message)

def update_message(self, message):
def update_message(self, message: Dict[str, Any]) -> None:
"""
Update a previously sent message and print the result in the terminal.
Throw an IndexError if the message id is invalid.
Expand All @@ -117,14 +118,14 @@ def update_message(self, message):
)
)

def upload_file_from_path(self, file_path):
def upload_file_from_path(self, file_path: str) -> Dict[str, Any]:
with open(file_path) as file:
return self.upload_file(file)

def upload_file(self, file):
def upload_file(self, file: IO[Any]) -> Dict[str, Any]:
return self.message_server.upload_file(file)

def get_config_info(self, bot_name, optional=False):
def get_config_info(self, bot_name: str, optional: bool = False) -> Dict[str, Any]:
if self.bot_config_file is None:
if optional:
return dict()
Expand Down
Loading