e49ecfcd39
This assumes that files passed in force_include without any extension are meant to be run with mypy, and thus assumed to be python scripts. Also, we ignore the most of the tools dir, except for the tools/deploy script for which #349 adds type annotations.
179 lines
8.1 KiB
Python
Executable file
179 lines
8.1 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
import subprocess
|
|
|
|
from collections import OrderedDict
|
|
from pathlib import PurePath
|
|
from server_lib import lister
|
|
from typing import cast, Dict, List
|
|
|
|
TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
os.chdir(os.path.dirname(TOOLS_DIR))
|
|
|
|
sys.path.append(os.path.dirname(TOOLS_DIR))
|
|
|
|
exclude = [
|
|
# Excluded because it's third-party code.
|
|
"zulip/integrations/perforce/git_p4.py",
|
|
# Excluded because we don't want to require bot authors to
|
|
# fully annotate their bots.
|
|
"zulip_bots/zulip_bots/bots",
|
|
"zulip_bots/zulip_bots/bots_unmaintained",
|
|
# Excluded out of laziness:
|
|
"zulip_bots/zulip_bots/terminal.py",
|
|
"zulip_bots/zulip_bots/simple_lib.py",
|
|
"zulip_bots/zulip_bots/lib_tests.py",
|
|
"tools",
|
|
]
|
|
|
|
# These files will be included even if excluded by a rule above.
|
|
force_include = [
|
|
# Include bots that we migrate to mypy.
|
|
"zulip_bots/zulip_bots/bots/helloworld/helloworld.py",
|
|
"zulip_bots/zulip_bots/bots/helloworld/test_helloworld.py",
|
|
"zulip_bots/zulip_bots/bots/followup/followup.py",
|
|
"zulip_bots/zulip_bots/bots/followup/test_followup.py",
|
|
"zulip_bots/zulip_bots/bots/giphy/giphy.py",
|
|
"zulip_bots/zulip_bots/bots/giphy/test_giphy.py",
|
|
"zulip_bots/zulip_bots/bots/github_detail/github_detail.py",
|
|
"zulip_bots/zulip_bots/bots/github_detail/test_github_detail.py",
|
|
"zulip_bots/zulip_bots/bots/google_search/google_search.py",
|
|
"zulip_bots/zulip_bots/bots/google_search/test_google_search.py",
|
|
"zulip_bots/zulip_bots/bots/help/help.py",
|
|
"zulip_bots/zulip_bots/bots/help/test_help.py",
|
|
"zulip_bots/zulip_bots/bots/incrementor/incrementor.py",
|
|
"zulip_bots/zulip_bots/bots/incrementor/test_incrementor.py",
|
|
"zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py",
|
|
"zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py",
|
|
"zulip_bots/zulip_bots/bots/virtual_fs/virtual_fs.py",
|
|
"zulip_bots/zulip_bots/bots/virtual_fs/test_virtual_fs.py",
|
|
"zulip_bots/zulip_bots/bots/weather/test_weather.py",
|
|
"zulip_bots/zulip_bots/bots/weather/weather.py",
|
|
"zulip_bots/zulip_bots/bots/youtube/youtube.py",
|
|
"zulip_bots/zulip_bots/bots/youtube/test_youtube.py",
|
|
"zulip_bots/zulip_bots/bots/converter/converter.py",
|
|
"zulip_bots/zulip_bots/bots/converter/test_converter.py",
|
|
"zulip_bots/zulip_bots/bots/define/define.py",
|
|
"zulip_bots/zulip_bots/bots/define/test_define.py",
|
|
"zulip_bots/zulip_bots/bots/encrypt/encrypt.py",
|
|
"zulip_bots/zulip_bots/bots/encrypt/test_encrypt.py",
|
|
"zulip_bots/zulip_bots/bots/chess/chess.py",
|
|
"zulip_bots/zulip_bots/bots/chess/test_chess.py",
|
|
"zulip_bots/zulip_bots/bots/xkcd/xkcd.py",
|
|
"zulip_bots/zulip_bots/bots/xkcd/test_xkcd.py",
|
|
"zulip_bots/zulip_bots/bots/witai/witai.py",
|
|
"zulip_bots/zulip_bots/bots/witai/test_witai.py",
|
|
"zulip_bots/zulip_bots/bots/wikipedia/wikipedia.py",
|
|
"zulip_bots/zulip_bots/bots/wikipedia/test_wikipedia.py",
|
|
"zulip_bots/zulip_bots/bots/yoda/yoda.py",
|
|
"zulip_bots/zulip_bots/bots/yoda/test_yoda.py",
|
|
"zulip_bots/zulip_bots/bots/dialogflow/dialogflow.py",
|
|
"zulip_bots/zulip_bots/bots/dialogflow/test_dialogflow.py",
|
|
"zulip_bots/zulip_bots/bots/mention/mention.py",
|
|
"zulip_bots/zulip_bots/bots/mention/test_mention.py",
|
|
"zulip_bots/zulip_bots/bots/baremetrics/baremetrics.py",
|
|
"zulip_bots/zulip_bots/bots/baremetrics/test_baremetrics.py",
|
|
"zulip_bots/zulip_bots/bots/salesforce/salesforce.py",
|
|
"zulip_bots/zulip_bots/bots/salesforce/test_salesforce.py",
|
|
"zulip_bots/zulip_bots/bots/idonethis/idonethis.py",
|
|
"zulip_bots/zulip_bots/bots/idonethis/test_idonethis.py",
|
|
"zulip_bots/zulip_bots/bots/connect_four/connect_four.py",
|
|
"zulip_bots/zulip_bots/bots/connect_four/test_connect_four.py",
|
|
"zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py",
|
|
"zulip_bots/zulip_bots/bots/tictactoe/test_tictactoe.py",
|
|
"zulip_bots/zulip_bots/bots/game_handler_bot/game_handler_bot.py",
|
|
"zulip_bots/zulip_bots/bots/game_handler_bot/test_game_handler_bot.py",
|
|
"zulip_bots/zulip_bots/bots/trello/trello.py",
|
|
"zulip_bots/zulip_bots/bots/trello/test_trello.py",
|
|
"zulip_bots/zulip_bots/bots/susi/susi.py",
|
|
"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/deploy"
|
|
]
|
|
|
|
parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.")
|
|
parser.add_argument('targets', nargs='*', default=[],
|
|
help="""files and directories to include in the result.
|
|
If this is not specified, the current directory is used""")
|
|
parser.add_argument('-m', '--modified', action='store_true', default=False, help='list only modified files')
|
|
parser.add_argument('-a', '--all', dest='all', action='store_true', default=False,
|
|
help="""run mypy on all python files, ignoring the exclude list.
|
|
This is useful if you have to find out which files fail mypy check.""")
|
|
parser.add_argument('--no-disallow-untyped-defs', dest='disallow_untyped_defs', action='store_false', default=True,
|
|
help="""Don't throw errors when functions are not annotated""")
|
|
parser.add_argument('--scripts-only', dest='scripts_only', action='store_true', default=False,
|
|
help="""Only type check extensionless python scripts""")
|
|
parser.add_argument('--no-strict-optional', dest='strict_optional', action='store_false', default=True,
|
|
help="""Don't use the --strict-optional flag with mypy""")
|
|
parser.add_argument('--warn-unused-ignores', dest='warn_unused_ignores', action='store_true', default=False,
|
|
help="""Use the --warn-unused-ignores flag with mypy""")
|
|
parser.add_argument('--no-ignore-missing-imports', dest='ignore_missing_imports', action='store_false', default=True,
|
|
help="""Don't use the --ignore-missing-imports flag with mypy""")
|
|
parser.add_argument('--quick', action='store_true', default=False,
|
|
help="""Use the --quick flag with mypy""")
|
|
args = parser.parse_args()
|
|
|
|
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))
|
|
|
|
for inpath in force_include:
|
|
try:
|
|
ext = os.path.splitext(inpath)[1].split('.')[1]
|
|
except IndexError:
|
|
ext = 'py' # type: str
|
|
files_dict[ext].append(inpath)
|
|
|
|
pyi_files = set(files_dict['pyi'])
|
|
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([('zulip', []), ('zulip_bots', []), ('zulip_botserver', []), ('tools', [])])
|
|
for file_path in python_files:
|
|
repo = PurePath(file_path).parts[0]
|
|
if repo in repo_python_files:
|
|
repo_python_files[repo].append(file_path)
|
|
|
|
mypy_command = "mypy"
|
|
|
|
extra_args = ["--check-untyped-defs",
|
|
"--follow-imports=silent",
|
|
"--scripts-are-modules",
|
|
"--disallow-any-generics",
|
|
"-i"]
|
|
if args.disallow_untyped_defs:
|
|
extra_args.append("--disallow-untyped-defs")
|
|
if args.warn_unused_ignores:
|
|
extra_args.append("--warn-unused-ignores")
|
|
if args.strict_optional:
|
|
extra_args.append("--strict-optional")
|
|
if args.ignore_missing_imports:
|
|
extra_args.append("--ignore-missing-imports")
|
|
if args.quick:
|
|
extra_args.append("--quick")
|
|
|
|
# run mypy
|
|
status = 0
|
|
for repo, python_files in repo_python_files.items():
|
|
print("Running mypy for `{}`.".format(repo), flush=True)
|
|
if python_files:
|
|
result = subprocess.call([mypy_command] + extra_args + python_files)
|
|
if result != 0:
|
|
status = result
|
|
else:
|
|
print("There are no files to run mypy on.")
|
|
sys.exit(status)
|