py3: Switch almost all shebang lines to use `python3`.
This causes `upgrade-zulip-from-git`, as well as a no-option run of
`tools/build-release-tarball`, to produce a Zulip install running
Python 3, rather than Python 2. In particular this means that the
virtualenv we create, in which all application code runs, is Python 3.
One shebang line, on `zulip-ec2-configure-interfaces`, explicitly
keeps Python 2, and at least one external ops script, `wal-e`, also
still runs on Python 2. See discussion on the respective previous
commits that made those explicit. There may also be some other
third-party scripts we use, outside of this source tree and running
outside our virtualenv, that still run on Python 2.
2017-08-02 23:15:16 +02:00
|
|
|
#!/usr/bin/env python3
|
2016-04-07 07:22:04 +02:00
|
|
|
|
2016-07-21 02:32:48 +02:00
|
|
|
from __future__ import absolute_import
|
2016-04-07 07:22:04 +02:00
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import argparse
|
|
|
|
import subprocess
|
2016-04-27 09:28:20 +02:00
|
|
|
import six
|
2016-04-07 07:22:04 +02:00
|
|
|
|
2017-02-05 21:24:28 +01:00
|
|
|
import lister
|
2016-07-24 09:38:08 +02:00
|
|
|
from typing import cast, Dict, List
|
|
|
|
|
2016-07-12 19:27:13 +02:00
|
|
|
TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
os.chdir(os.path.dirname(TOOLS_DIR))
|
|
|
|
|
2017-08-06 01:16:07 +02:00
|
|
|
sys.path.append(os.path.dirname(TOOLS_DIR))
|
|
|
|
from lib.test_script import get_provisioning_status
|
|
|
|
|
2016-07-01 18:38:57 +02:00
|
|
|
exclude_common = """
|
2016-05-17 17:31:39 +02:00
|
|
|
zproject/settings.py
|
|
|
|
zproject/test_settings.py
|
2016-04-07 07:22:04 +02:00
|
|
|
""".split()
|
|
|
|
|
2017-06-01 00:28:10 +02:00
|
|
|
exclude_py2 = [] # type: List[str]
|
2016-07-01 18:38:57 +02:00
|
|
|
|
|
|
|
exclude_py3 = """
|
2016-07-21 22:23:35 +02:00
|
|
|
""".split()
|
|
|
|
|
2016-04-07 07:22:04 +02:00
|
|
|
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.""")
|
2016-06-04 21:03:14 +02:00
|
|
|
parser.add_argument('--linecoverage-report', dest='linecoverage_report', action='store_true', default=False,
|
|
|
|
help="""run the linecoverage report to see annotation coverage""")
|
2016-09-12 18:07:23 +02:00
|
|
|
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""")
|
2016-07-21 21:56:18 +02:00
|
|
|
parser.add_argument('--scripts-only', dest='scripts_only', action='store_true', default=False,
|
|
|
|
help="""Only type check extensionless python scripts""")
|
2017-05-23 20:41:07 +02:00
|
|
|
parser.add_argument('--strict-optional', dest='strict_optional', action='store_true', default=False,
|
|
|
|
help="""Use the --strict-optional flag with mypy""")
|
2017-07-28 01:09:37 +02:00
|
|
|
parser.add_argument('--warn-unused-ignores', dest='warn_unused_ignores', action='store_true', default=False,
|
|
|
|
help="""Use the --warn-unused-ignores flag with mypy""")
|
2017-05-23 20:41:07 +02:00
|
|
|
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""")
|
2017-08-06 01:16:07 +02:00
|
|
|
parser.add_argument('--force', default=False,
|
|
|
|
action="store_true",
|
|
|
|
help='Run tests despite possible provisioning problems.')
|
2016-07-01 18:38:57 +02:00
|
|
|
|
|
|
|
group = parser.add_mutually_exclusive_group()
|
|
|
|
group.add_argument('--py2', default=False, action='store_true', help="Use Python 2 mode")
|
|
|
|
group.add_argument('--py3', default=False, action='store_true', help="Use Python 3 mode")
|
2016-04-07 07:22:04 +02:00
|
|
|
args = parser.parse_args()
|
2016-07-01 18:38:57 +02:00
|
|
|
|
2017-08-06 01:16:07 +02:00
|
|
|
if not args.force:
|
|
|
|
ok, msg = get_provisioning_status()
|
|
|
|
if not ok:
|
|
|
|
print(msg)
|
|
|
|
print('If you really know what you are doing, use --force to run anyway.')
|
|
|
|
sys.exit(1)
|
|
|
|
|
2016-07-01 18:38:57 +02:00
|
|
|
if args.py2:
|
|
|
|
py_version = 2
|
|
|
|
elif args.py3:
|
|
|
|
py_version = 3
|
|
|
|
else:
|
|
|
|
py_version = 2
|
|
|
|
|
2016-04-07 07:22:04 +02:00
|
|
|
if args.all:
|
2017-06-01 00:28:10 +02:00
|
|
|
exclude = [] # type: List[str]
|
2016-10-16 07:46:29 +02:00
|
|
|
if py_version == 2:
|
|
|
|
exclude = exclude_common + exclude_py2
|
2016-07-01 18:38:57 +02:00
|
|
|
else:
|
2016-10-16 07:46:29 +02:00
|
|
|
exclude = exclude_common + exclude_py3
|
2016-04-07 07:22:04 +02:00
|
|
|
|
2016-05-24 15:42:55 +02:00
|
|
|
# find all non-excluded files in current directory
|
2016-07-24 09:38:08 +02:00
|
|
|
files_dict = cast(Dict[str, List[str]],
|
|
|
|
lister.list_files(targets=args.targets, ftypes=['py', 'pyi'],
|
2016-10-16 07:48:08 +02:00
|
|
|
use_shebang=True, modified_only=args.modified,
|
2016-07-24 09:38:08 +02:00
|
|
|
exclude = exclude + ['stubs'], group_by_ftype=True,
|
|
|
|
extless_only=args.scripts_only))
|
2016-06-15 14:50:25 +02:00
|
|
|
pyi_files = set(files_dict['pyi'])
|
|
|
|
python_files = [fpath for fpath in files_dict['py']
|
2016-07-21 21:56:18 +02:00
|
|
|
if not fpath.endswith('.py') or fpath + 'i' not in pyi_files]
|
2016-04-07 07:22:04 +02:00
|
|
|
|
2016-04-27 09:28:20 +02:00
|
|
|
# Use zulip-py3-venv's mypy if it's available and we're on python 2
|
|
|
|
PY3_VENV_DIR = "/srv/zulip-py3-venv"
|
|
|
|
MYPY_VENV_PATH = os.path.join(PY3_VENV_DIR, "bin", "mypy")
|
|
|
|
if six.PY2 and os.path.exists(MYPY_VENV_PATH):
|
|
|
|
mypy_command = MYPY_VENV_PATH
|
|
|
|
print("Using mypy from", mypy_command)
|
|
|
|
else:
|
|
|
|
mypy_command = "mypy"
|
|
|
|
|
2017-05-23 20:41:07 +02:00
|
|
|
extra_args = ["--check-untyped-defs",
|
2017-02-11 03:58:40 +01:00
|
|
|
"--follow-imports=silent",
|
2017-02-11 03:18:39 +01:00
|
|
|
"--scripts-are-modules",
|
|
|
|
"-i", "--cache-dir=var/mypy-cache"]
|
2016-07-01 18:38:57 +02:00
|
|
|
if py_version == 2:
|
|
|
|
extra_args.append("--py2")
|
2016-06-04 21:03:14 +02:00
|
|
|
if args.linecoverage_report:
|
|
|
|
extra_args.append("--linecoverage-report")
|
2016-07-16 16:44:41 +02:00
|
|
|
extra_args.append("var/linecoverage-report")
|
2016-06-02 21:46:24 +02:00
|
|
|
if args.disallow_untyped_defs:
|
|
|
|
extra_args.append("--disallow-untyped-defs")
|
2017-07-28 01:09:37 +02:00
|
|
|
if args.warn_unused_ignores:
|
|
|
|
extra_args.append("--warn-unused-ignores")
|
2017-05-23 20:41:07 +02:00
|
|
|
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")
|
2016-06-02 21:46:24 +02:00
|
|
|
|
|
|
|
|
2016-05-24 15:42:55 +02:00
|
|
|
# run mypy
|
2016-04-07 07:22:04 +02:00
|
|
|
if python_files:
|
2016-10-16 07:48:08 +02:00
|
|
|
rc = subprocess.call([mypy_command] + extra_args + python_files)
|
|
|
|
if args.linecoverage_report:
|
2017-06-20 05:32:16 +02:00
|
|
|
# Move the coverage report to where codecov will look for it.
|
2016-10-16 07:48:08 +02:00
|
|
|
try:
|
|
|
|
os.rename('var/linecoverage-report/coverage.txt', 'var/.coverage')
|
|
|
|
except OSError:
|
|
|
|
# maybe mypy crashed; exit with its error code
|
|
|
|
pass
|
2016-04-07 07:22:04 +02:00
|
|
|
sys.exit(rc)
|
|
|
|
else:
|
|
|
|
print("There are no files to run mypy on.")
|