From 626e64caf48ee822ea2cd20b86585a6dbb9d6140 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 14 Nov 2023 15:44:56 +0100
Subject: [PATCH 1/2] Remove check_type_completeness and verify_types_*.json,
moving pyright --verifytypes directly into check.sh. Fix pyright type_tests
location.
---
check.sh | 16 +-
pyproject.toml | 5 +-
src/trio/_core/_asyncgens.py | 2 +-
src/trio/_core/_wakeup_socketpair.py | 4 +
src/trio/_tests/check_type_completeness.py | 194 ---------------------
src/trio/_tests/verify_types_darwin.json | 55 ------
src/trio/_tests/verify_types_linux.json | 55 ------
src/trio/_tests/verify_types_windows.json | 55 ------
8 files changed, 18 insertions(+), 368 deletions(-)
delete mode 100755 src/trio/_tests/check_type_completeness.py
delete mode 100644 src/trio/_tests/verify_types_darwin.json
delete mode 100644 src/trio/_tests/verify_types_linux.json
delete mode 100644 src/trio/_tests/verify_types_windows.json
diff --git a/check.sh b/check.sh
index a6f41a6d5d..badad99127 100755
--- a/check.sh
+++ b/check.sh
@@ -96,16 +96,20 @@ fi
codespell || EXIT_STATUS=$?
+PYRIGHT=0
echo "::group::Pyright interface tests"
-python src/trio/_tests/check_type_completeness.py --overwrite-file || EXIT_STATUS=$?
-if git status --porcelain src/trio/_tests/verify_types*.json | grep -q "M"; then
- echo "* Type completeness changed, please update!" >> "$GITHUB_STEP_SUMMARY"
- echo "::error::Type completeness changed, please update!"
- git --no-pager diff --color src/trio/_tests/verify_types*.json
+pyright --verifytypes --ignoreexternal --pythonplatform=Linux --verifytypes=trio \
+ || { echo "* Pyright --verifytypes (Linux) found errors." >> "$GITHUB_STEP_SUMMARY"; PYRIGHT=1; }
+pyright --verifytypes --ignoreexternal --pythonplatform=Darwin --verifytypes=trio \
+ || { echo "* Pyright --verifytypes (Mac) found errors." >> "$GITHUB_STEP_SUMMARY"; PYRIGHT=1; }
+pyright --verifytypes --ignoreexternal --pythonplatform=Windows --verifytypes=trio \
+ || { echo "* Pyright --verifytypes (Windows) found errors." >> "$GITHUB_STEP_SUMMARY"; PYRIGHT=1; }
+if [ $PYRIGHT -ne 0 ]; then
+ echo "::error:: Pyright --verifytypes returned errors."
EXIT_STATUS=1
fi
-pyright trio/_tests/type_tests || EXIT_STATUS=$?
+pyright src/trio/_tests/type_tests || EXIT_STATUS=$?
echo "::endgroup::"
# Finally, leave a really clear warning of any issues and exit
diff --git a/pyproject.toml b/pyproject.toml
index 9caa39c3af..d455c6ca39 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -155,6 +155,9 @@ disallow_untyped_decorators = true
disallow_untyped_defs = true
check_untyped_defs = true
+[tool.pyright]
+pythonVersion = "3.8"
+
[tool.pytest.ini_options]
addopts = ["--strict-markers", "--strict-config"]
faulthandler_timeout = 60
@@ -232,8 +235,6 @@ omit = [
"*/trio/_core/_tests/test_multierror_scripts/*",
# Omit the generated files in trio/_core starting with _generated_
"*/trio/_core/_generated_*",
- # Script used to check type completeness that isn't run in tests
- "*/trio/_tests/check_type_completeness.py",
]
# The test suite spawns subprocesses to test some stuff, so make sure
# this doesn't corrupt the coverage files
diff --git a/src/trio/_core/_asyncgens.py b/src/trio/_core/_asyncgens.py
index f26f5bd160..30736ac7ad 100644
--- a/src/trio/_core/_asyncgens.py
+++ b/src/trio/_core/_asyncgens.py
@@ -42,7 +42,7 @@ class AsyncGenerators:
# init task starting end-of-run asyncgen finalization.
trailing_needs_finalize: _ASYNC_GEN_SET = attr.ib(factory=_ASYNC_GEN_SET)
- prev_hooks = attr.ib(init=False)
+ prev_hooks: sys._asyncgen_hooks = attr.ib(init=False)
def install_hooks(self, runner: _run.Runner) -> None:
def firstiter(agen: AsyncGeneratorType[object, NoReturn]) -> None:
diff --git a/src/trio/_core/_wakeup_socketpair.py b/src/trio/_core/_wakeup_socketpair.py
index aff28a1bd8..fb821a23e7 100644
--- a/src/trio/_core/_wakeup_socketpair.py
+++ b/src/trio/_core/_wakeup_socketpair.py
@@ -11,6 +11,10 @@
class WakeupSocketpair:
def __init__(self) -> None:
+ # explicitly typed to please `pyright --verifytypes` without `--ignoreexternal`
+ self.wakeup_sock: socket.socket
+ self.write_sock: socket.socket
+
self.wakeup_sock, self.write_sock = socket.socketpair()
self.wakeup_sock.setblocking(False)
self.write_sock.setblocking(False)
diff --git a/src/trio/_tests/check_type_completeness.py b/src/trio/_tests/check_type_completeness.py
deleted file mode 100755
index 4b46844b60..0000000000
--- a/src/trio/_tests/check_type_completeness.py
+++ /dev/null
@@ -1,194 +0,0 @@
-#!/usr/bin/env python3
-from __future__ import annotations
-
-# this file is not run as part of the tests, instead it's run standalone from check.sh
-import argparse
-import json
-import subprocess
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
- from collections.abc import Mapping
-
-# the result file is not marked in MANIFEST.in so it's not included in the package
-failed = False
-
-
-def get_result_file_name(platform: str) -> Path:
- return Path(__file__).parent / f"verify_types_{platform.lower()}.json"
-
-
-# TODO: consider checking manually without `--ignoreexternal`, and/or
-# removing it from the below call later on.
-def run_pyright(platform: str) -> subprocess.CompletedProcess[bytes]:
- return subprocess.run(
- [
- "pyright",
- # Specify a platform and version to keep imported modules consistent.
- f"--pythonplatform={platform}",
- "--pythonversion=3.8",
- "--verifytypes=trio",
- "--outputjson",
- "--ignoreexternal",
- ],
- capture_output=True,
- )
-
-
-def check_less_than(
- key: str,
- current_dict: Mapping[str, int | float],
- last_dict: Mapping[str, int | float],
- /,
- invert: bool = False,
-) -> None:
- global failed
- current = current_dict[key]
- last = last_dict[key]
- assert isinstance(current, (float, int))
- assert isinstance(last, (float, int))
- if current == last:
- return
- if (current > last) ^ invert:
- failed = True
- print("ERROR: ", end="")
- strcurrent = f"{current:.4}" if isinstance(current, float) else str(current)
- strlast = f"{last:.4}" if isinstance(last, float) else str(last)
- print(
- f"{key} has gone {'down' if current None:
- global failed
- if current_dict[key] != 0:
- failed = True
- print(f"ERROR: {key} is {current_dict[key]}")
-
-
-def check_type(args: argparse.Namespace, platform: str) -> int:
- print("*" * 20, "\nChecking type completeness hasn't gone down...")
-
- res = run_pyright(platform)
- current_result = json.loads(res.stdout)
- py_typed_file: Path | None = None
-
- # check if py.typed file was missing
- if (
- current_result["generalDiagnostics"]
- and current_result["generalDiagnostics"][0]["message"]
- == "No py.typed file found"
- ):
- print("creating py.typed")
- py_typed_file = (
- Path(current_result["typeCompleteness"]["packageRootDirectory"])
- / "py.typed"
- )
- py_typed_file.write_text("")
-
- res = run_pyright(platform)
- current_result = json.loads(res.stdout)
-
- if res.stderr:
- print(res.stderr)
-
- last_result = json.loads(get_result_file_name(platform).read_text())
-
- for key in "errorCount", "warningCount", "informationCount":
- check_zero(key, current_result["summary"])
-
- for key, invert in (
- ("missingFunctionDocStringCount", False),
- ("missingClassDocStringCount", False),
- ("missingDefaultParamCount", False),
- ("completenessScore", True),
- ):
- check_less_than(
- key,
- current_result["typeCompleteness"],
- last_result["typeCompleteness"],
- invert=invert,
- )
-
- for key, invert in (
- ("withUnknownType", False),
- ("withAmbiguousType", False),
- ("withKnownType", True),
- ):
- check_less_than(
- key,
- current_result["typeCompleteness"]["exportedSymbolCounts"],
- last_result["typeCompleteness"]["exportedSymbolCounts"],
- invert=invert,
- )
-
- if args.overwrite_file:
- print("Overwriting file")
-
- # don't care about differences in time taken
- del current_result["time"]
- del current_result["summary"]["timeInSec"]
-
- # don't fail on version diff so pyright updates can be automerged
- del current_result["version"]
-
- for key in (
- # don't save path (because that varies between machines)
- "moduleRootDirectory",
- "packageRootDirectory",
- "pyTypedPath",
- ):
- del current_result["typeCompleteness"][key]
-
- # prune the symbols to only be the name of the symbols with
- # errors, instead of saving a huge file.
- new_symbols: list[dict[str, str]] = []
- for symbol in current_result["typeCompleteness"]["symbols"]:
- if symbol["diagnostics"]:
- # function name + message should be enough context for people!
- new_symbols.extend(
- {"name": symbol["name"], "message": diagnostic["message"]}
- for diagnostic in symbol["diagnostics"]
- )
- continue
-
- # Ensure order of arrays does not affect result.
- new_symbols.sort(key=lambda module: module.get("name", ""))
- current_result["generalDiagnostics"].sort()
- current_result["typeCompleteness"]["modules"].sort(
- key=lambda module: module.get("name", "")
- )
-
- del current_result["typeCompleteness"]["symbols"]
- current_result["typeCompleteness"]["diagnostics"] = new_symbols
-
- with open(get_result_file_name(platform), "w") as file:
- json.dump(current_result, file, sort_keys=True, indent=2)
- # add newline at end of file so it's easier to manually modify
- file.write("\n")
-
- if py_typed_file is not None:
- print("deleting py.typed")
- py_typed_file.unlink()
-
- print("*" * 20)
-
- return int(failed)
-
-
-def main(args: argparse.Namespace) -> int:
- res = 0
- for platform in "Linux", "Windows", "Darwin":
- res += check_type(args, platform)
- return res
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument("--overwrite-file", action="store_true", default=False)
-parser.add_argument("--full-diagnostics-file", type=Path, default=None)
-args = parser.parse_args()
-
-assert __name__ == "__main__", "This script should be run standalone"
-sys.exit(main(args))
diff --git a/src/trio/_tests/verify_types_darwin.json b/src/trio/_tests/verify_types_darwin.json
deleted file mode 100644
index 713263afb5..0000000000
--- a/src/trio/_tests/verify_types_darwin.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "generalDiagnostics": [],
- "summary": {
- "errorCount": 0,
- "filesAnalyzed": 8,
- "informationCount": 0,
- "warningCount": 0
- },
- "typeCompleteness": {
- "completenessScore": 1,
- "diagnostics": [],
- "exportedSymbolCounts": {
- "withAmbiguousType": 0,
- "withKnownType": 632,
- "withUnknownType": 0
- },
- "ignoreUnknownTypesFromImports": true,
- "missingClassDocStringCount": 0,
- "missingDefaultParamCount": 0,
- "missingFunctionDocStringCount": 0,
- "moduleName": "trio",
- "modules": [
- {
- "name": "trio"
- },
- {
- "name": "trio.abc"
- },
- {
- "name": "trio.from_thread"
- },
- {
- "name": "trio.lowlevel"
- },
- {
- "name": "trio.socket"
- },
- {
- "name": "trio.testing"
- },
- {
- "name": "trio.tests"
- },
- {
- "name": "trio.to_thread"
- }
- ],
- "otherSymbolCounts": {
- "withAmbiguousType": 0,
- "withKnownType": 699,
- "withUnknownType": 0
- },
- "packageName": "trio"
- }
-}
diff --git a/src/trio/_tests/verify_types_linux.json b/src/trio/_tests/verify_types_linux.json
deleted file mode 100644
index 2e8c2e2f44..0000000000
--- a/src/trio/_tests/verify_types_linux.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "generalDiagnostics": [],
- "summary": {
- "errorCount": 0,
- "filesAnalyzed": 8,
- "informationCount": 0,
- "warningCount": 0
- },
- "typeCompleteness": {
- "completenessScore": 1,
- "diagnostics": [],
- "exportedSymbolCounts": {
- "withAmbiguousType": 0,
- "withKnownType": 629,
- "withUnknownType": 0
- },
- "ignoreUnknownTypesFromImports": true,
- "missingClassDocStringCount": 0,
- "missingDefaultParamCount": 0,
- "missingFunctionDocStringCount": 0,
- "moduleName": "trio",
- "modules": [
- {
- "name": "trio"
- },
- {
- "name": "trio.abc"
- },
- {
- "name": "trio.from_thread"
- },
- {
- "name": "trio.lowlevel"
- },
- {
- "name": "trio.socket"
- },
- {
- "name": "trio.testing"
- },
- {
- "name": "trio.tests"
- },
- {
- "name": "trio.to_thread"
- }
- ],
- "otherSymbolCounts": {
- "withAmbiguousType": 0,
- "withKnownType": 699,
- "withUnknownType": 0
- },
- "packageName": "trio"
- }
-}
diff --git a/src/trio/_tests/verify_types_windows.json b/src/trio/_tests/verify_types_windows.json
deleted file mode 100644
index f0de7469a5..0000000000
--- a/src/trio/_tests/verify_types_windows.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "generalDiagnostics": [],
- "summary": {
- "errorCount": 0,
- "filesAnalyzed": 8,
- "informationCount": 0,
- "warningCount": 0
- },
- "typeCompleteness": {
- "completenessScore": 1,
- "diagnostics": [],
- "exportedSymbolCounts": {
- "withAmbiguousType": 0,
- "withKnownType": 632,
- "withUnknownType": 0
- },
- "ignoreUnknownTypesFromImports": true,
- "missingClassDocStringCount": 0,
- "missingDefaultParamCount": 0,
- "missingFunctionDocStringCount": 0,
- "moduleName": "trio",
- "modules": [
- {
- "name": "trio"
- },
- {
- "name": "trio.abc"
- },
- {
- "name": "trio.from_thread"
- },
- {
- "name": "trio.lowlevel"
- },
- {
- "name": "trio.socket"
- },
- {
- "name": "trio.testing"
- },
- {
- "name": "trio.tests"
- },
- {
- "name": "trio.to_thread"
- }
- ],
- "otherSymbolCounts": {
- "withAmbiguousType": 0,
- "withKnownType": 691,
- "withUnknownType": 0
- },
- "packageName": "trio"
- }
-}
From a1cd7b41bbb08faaac98efa5d826bf3d7be59f7f Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 14 Nov 2023 16:08:29 +0100
Subject: [PATCH 2/2] remove autogeneration of py.typed
---
src/trio/_tests/test_exports.py | 22 ----------------------
1 file changed, 22 deletions(-)
diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py
index bd04436640..8afb710eb6 100644
--- a/src/trio/_tests/test_exports.py
+++ b/src/trio/_tests/test_exports.py
@@ -146,13 +146,6 @@ def no_underscores(symbols: Iterable[str]) -> set[str]:
if getattr(module, name, None) is getattr(__future__, name):
runtime_names.remove(name)
- if tool in ("mypy", "pyright_verifytypes"):
- # create py.typed file
- py_typed_path = Path(trio.__file__).parent / "py.typed"
- py_typed_exists = py_typed_path.exists()
- if not py_typed_exists: # pragma: no branch
- py_typed_path.write_text("")
-
if tool == "pylint":
try:
from pylint.lint import PyLinter
@@ -236,10 +229,6 @@ def no_underscores(symbols: Iterable[str]) -> set[str]:
else: # pragma: no cover
raise AssertionError()
- # remove py.typed file
- if tool in ("mypy", "pyright_verifytypes") and not py_typed_exists:
- py_typed_path.unlink()
-
# mypy handles errors with an `assert` in its branch
if tool == "mypy":
return
@@ -289,16 +278,9 @@ def no_hidden(symbols: Iterable[str]) -> set[str]:
if (not symbol.startswith("_")) or symbol.startswith("__")
}
- py_typed_path = Path(trio.__file__).parent / "py.typed"
- py_typed_exists = py_typed_path.exists()
-
if tool == "mypy":
if sys.implementation.name != "cpython":
pytest.skip("mypy not installed in tests on pypy")
- # create py.typed file
- # remove this logic when trio is marked with py.typed proper
- if not py_typed_exists: # pragma: no branch
- py_typed_path.write_text("")
cache = Path.cwd() / ".mypy_cache"
@@ -536,10 +518,6 @@ def lookup_symbol(symbol: str) -> dict[str, str]:
"extra": extra,
}
- # clean up created py.typed file
- if tool == "mypy" and not py_typed_exists:
- py_typed_path.unlink()
-
# `assert not errors` will not print the full content of errors, even with
# `--verbose`, so we manually print it
if errors: # pragma: no cover