1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
#!/usr/bin/env python
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
# This script is used to execute verifytypes within a tox environment. It additionally installs
# the latest release of a package (if it exists) and compares its type completeness score with
# that of the current code. If type completeness worsens from the last release, the check fails.
import subprocess
import json
import argparse
import os
import logging
import sys
from ci_tools.environment_exclusions import is_ignored_package, VERIFYTYPES_OPT_OUT
logging.getLogger().setLevel(logging.INFO)
def install_editable(setup_path):
commands = [sys.executable, "-m", "pip", "install", "-e", setup_path]
subprocess.check_call(commands)
def install_latest_release(package_name):
from pypi_tools.pypi import PyPIClient
client = PyPIClient()
try:
latest_version = str(client.get_ordered_versions(package_name)[-1])
except (IndexError, KeyError):
logging.info(f"No released packages for {package_name} on PyPi yet.")
latest_version = None
if latest_version:
packages = [f"{package_name}=={latest_version}"]
commands = [
sys.executable,
"-m",
"pip",
"install",
]
commands.extend(packages)
subprocess.check_call(commands)
return latest_version
def get_type_complete_score(commands, check_pytyped=False):
try:
response = subprocess.run(
commands,
check=True,
capture_output=True,
)
except subprocess.CalledProcessError as e:
if e.returncode != 1:
logging.info(
f"Running verifytypes failed: {e.stderr}. See https://aka.ms/python/typing-guide for information."
)
exit(1)
report = json.loads(e.output)
if check_pytyped:
pytyped_present = report["typeCompleteness"].get("pyTypedPath", None)
if not pytyped_present:
print(
f"No py.typed file was found. See aka.ms/python/typing-guide for information."
)
exit(1)
return report["typeCompleteness"]["completenessScore"]
# library scores 100%
report = json.loads(response.stdout)
return report["typeCompleteness"]["completenessScore"]
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Run pyright verifytypes against target folder. "
)
parser.add_argument(
"-t",
"--target",
dest="target_package",
help="The target package directory on disk. The target module passed to run pyright will be <target_package>/azure.",
required=True,
)
args = parser.parse_args()
package_name = os.path.basename(os.path.abspath(args.target_package))
module = package_name.replace("-", ".")
setup_path = os.path.abspath(args.target_package)
if package_name in VERIFYTYPES_OPT_OUT or is_ignored_package(package_name):
logging.info(
f"{package_name} opts-out of verifytypes check. See https://aka.ms/python/typing-guide for information."
)
exit(0)
commands = [
sys.executable,
"-m",
"pyright",
"--verifytypes",
module,
"--ignoreexternal",
"--outputjson",
]
# get type completeness score from latest release
latest_version = install_latest_release(package_name)
if latest_version:
score_from_released = get_type_complete_score(commands)
else:
score_from_released = None
# get type completeness score from current code
install_editable(setup_path)
score_from_current = get_type_complete_score(commands, check_pytyped=True)
try:
subprocess.check_call(commands[:-1])
except subprocess.CalledProcessError:
pass # we don't fail on verifytypes, only if type completeness score worsens from last release
if score_from_released is not None:
score_from_released_rounded = round(score_from_released * 100, 1)
score_from_current_rounded = round(score_from_current * 100, 1)
print("\n-----Type completeness score comparison-----\n")
print(f"Previous release ({latest_version}): {score_from_released_rounded}%")
if score_from_current_rounded < score_from_released_rounded:
print(
f"\nERROR: The type completeness score of {package_name} has decreased since the last release. "
f"See the above output for areas to improve. See https://aka.ms/python/typing-guide for information."
)
exit(1)
|