Skip to content

Move test_cases to stdlib/@tests/test_cases #11865

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

Merged
merged 7 commits into from
May 10, 2024
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
2 changes: 1 addition & 1 deletion pyrightconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
],
"exclude": [
// test cases use a custom config file
"stubs/**/@tests/test_cases"
"**/@tests/test_cases",
],
"typeCheckingMode": "strict",
// Allowed in base settings for incomplete stubs, checked in stricter settings
Expand Down
2 changes: 1 addition & 1 deletion pyrightconfig.stricter.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
],
"exclude": [
// test cases use a custom pyrightconfig file
"stubs/**/@tests/test_cases",
"**/@tests/test_cases",
"stdlib/distutils/command",
"stdlib/distutils/dist.pyi",
"stdlib/importlib/readers.pyi",
Expand Down
3 changes: 1 addition & 2 deletions pyrightconfig.testcases.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
"typeshedPath": ".",
"include": [
"test_cases",
"stubs/**/@tests/test_cases"
"**/@tests/test_cases",
],
"typeCheckingMode": "strict",
// Extra strict settings
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 2 additions & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ the stubs in typeshed (including the standard library).
## regr\_test.py

This test runs mypy against the test cases for typeshed's stdlib and third-party
stubs. See [the README in the `test_cases` directory](../test_cases/README.md)
stubs. See [the REGRESSION.md document](./REGRESSION.md)
in this directory
for more information about what
these test cases are for and how they work. Run `python tests/regr_test.py --help`
for information on the various configuration options.
Expand Down
31 changes: 13 additions & 18 deletions test_cases/README.md → tests/REGRESSION.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
## Regression tests for typeshed

This directory contains code samples that act as a regression test for
typeshed's stdlib stubs.
Regression tests for the standard library stubs can be found in the
`stdlib/@tests/test_cases` directory. Stubs for third-party libraries that do
have test cases can be found in `@tests/test_cases` subdirectories for each
stubs package. For example, the test cases for `requests` can be found in the
`stubs/requests/@tests/test_cases` directory.

**This directory should *only* contain test cases for functions and classes which
**Regression test cases should only be written for functions and classes which
are known to have caused problems in the past, where the stubs are difficult to
get right.** 100% test coverage for typeshed is neither necessary nor
desirable, as it would lead to code duplication. Moreover, typeshed has
multiple other mechanisms for spotting errors in the stubs.

### Where are the third-party test cases?

Not all third-party stubs packages in typeshed have test cases, and not all of
them need test cases. For those that do have test cases, however, the samples
can be found in `@tests/test_cases` subdirectories for each stubs package. For
example, the test cases for `requests` can be found in the
`stubs/requests/@tests/test_cases` directory.

### The purpose of these tests

Different test cases in this directory serve different purposes. For some stubs in
typeshed, the type annotations are complex enough that it's useful to have
sanity checks that test whether a type checker understands the intent of
the annotations correctly. Examples of tests like these are
`stdlib/builtins/check_pow.py` and `stdlib/asyncio/check_gather.py`.
`builtins/check_pow.py` and `asyncio/check_gather.py`.

Other test cases, such as the samples for `ExitStack` in `stdlib/check_contextlib.py`
and the samples for `LogRecord` in `stdlib/check_logging.py`, do not relate to
Other test cases, such as the samples for `ExitStack` in `check_contextlib.py`
and the samples for `LogRecord` in `check_logging.py`, do not relate to
stubs where the annotations are particularly complex, but they *do* relate to
stubs where decisions have been taken that might be slightly unusual. These
test cases serve a different purpose: to check that type checkers do not emit
Expand Down Expand Up @@ -74,10 +69,10 @@ Use the same top-level name for the module / package you would like to test.
Use the `check_${thing}.py` naming pattern for individual test files.

By default, test cases go into a file with the same name as the stub file, prefixed with `check_`.
For example: `stdlib/check_contextlib.py`.
For example: `check_contextlib.py`.

If that file becomes too big, we instead create a directory with files named after individual objects being tested.
For example: `stdlib/builtins/check_dict.py`.
For example: `builtins/check_dict.py`.

### Differences to the rest of typeshed

Expand Down Expand Up @@ -114,7 +109,7 @@ with a specific Python version passed on the command line to the `tests/regr_tes
To mark a test-case file as being skippable on lower versions of Python,
append `-py3*` to the filename.
For example, if `foo` is a stdlib feature that's new in Python 3.11,
test cases for `foo` should be put in a file named `test_cases/stdlib/check_foo-py311.py`.
test cases for `foo` should be put in a file named `check_foo-py311.py`.
This means that mypy will only run the test case
if `--python-version 3.11`, `--python-version 3.12`, etc.
is passed on the command line to `tests/regr_test.py`,
Expand All @@ -123,4 +118,4 @@ is passed on the command line.

However, `if sys.version_info >= (3, target):` is still required for `pyright`
in the test file itself.
Example: [`check_exception_group-py311.py`](https://github.com/python/typeshed/blob/main/test_cases/stdlib/builtins/check_exception_group-py311.py)
Example: [`check_exception_group-py311.py`](../stdlib/@tests/test_cases/builtins/check_exception_group-py311.py)
18 changes: 11 additions & 7 deletions tests/check_typeshed_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from parse_metadata import read_metadata
from utils import (
REQS_FILE,
STDLIB_PATH,
TEST_CASES_DIR,
TESTS_DIR,
VERSIONS_RE,
Expand Down Expand Up @@ -59,7 +60,8 @@ def assert_consistent_filetypes(

def check_stdlib() -> None:
"""Check that the stdlib directory contains only the correct files."""
assert_consistent_filetypes(Path("stdlib"), kind=".pyi", allowed={"_typeshed/README.md", "VERSIONS"})
assert_consistent_filetypes(STDLIB_PATH, kind=".pyi", allowed={"_typeshed/README.md", "VERSIONS", TESTS_DIR})
check_tests_dir(tests_path("stdlib"))


def check_stubs() -> None:
Expand All @@ -81,11 +83,13 @@ def check_stubs() -> None:

tests_dir = tests_path(dist.name)
if tests_dir.exists() and tests_dir.is_dir():
py_files_present = any(file.suffix == ".py" for file in tests_dir.iterdir())
error_message = (
f"Test-case files must be in an `{TESTS_DIR}/{TEST_CASES_DIR}` directory, not in the `{TESTS_DIR}` directory"
)
assert not py_files_present, error_message
check_tests_dir(tests_dir)


def check_tests_dir(tests_dir: Path) -> None:
py_files_present = any(file.suffix == ".py" for file in tests_dir.iterdir())
error_message = f"Test-case files must be in an `{TESTS_DIR}/{TEST_CASES_DIR}` directory, not in the `{TESTS_DIR}` directory"
assert not py_files_present, error_message


def check_distutils() -> None:
Expand Down Expand Up @@ -146,7 +150,7 @@ def check_versions_file() -> None:

def _find_stdlib_modules() -> set[str]:
modules = set[str]()
for path, _, files in os.walk("stdlib"):
for path, _, files in os.walk(STDLIB_PATH):
for filename in files:
base_module = ".".join(os.path.normpath(path).split(os.sep)[1:])
if filename == "__init__.pyi":
Expand Down
3 changes: 2 additions & 1 deletion tests/mypy_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from parse_metadata import PackageDependencies, get_recursive_requirements, read_metadata
from utils import (
PYTHON_VERSION,
TESTS_DIR,
VERSIONS_RE as VERSION_LINE_RE,
colored,
get_gitignore_spec,
Expand Down Expand Up @@ -366,7 +367,7 @@ def test_stdlib(args: TestConfig) -> TestResult:
stdlib = Path("stdlib")
supported_versions = parse_versions(stdlib / "VERSIONS")
for name in os.listdir(stdlib):
if name == "VERSIONS" or name.startswith("."):
if name in ("VERSIONS", TESTS_DIR) or name.startswith("."):
continue
module = Path(name).stem
module_min_version, module_max_version = supported_versions[module]
Expand Down
12 changes: 6 additions & 6 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def colored(text: str, color: str | None = None, **kwargs: Any) -> str: # type:

PYTHON_VERSION: Final = f"{sys.version_info.major}.{sys.version_info.minor}"

STDLIB_PATH = Path("stdlib")
STUBS_PATH = Path("stubs")


Expand Down Expand Up @@ -139,15 +140,14 @@ def distribution_info(distribution_name: str) -> DistributionTests:


def tests_path(distribution_name: str) -> Path:
assert distribution_name != "stdlib"
return STUBS_PATH / distribution_name / TESTS_DIR
if distribution_name == "stdlib":
return STDLIB_PATH / TESTS_DIR
else:
return STUBS_PATH / distribution_name / TESTS_DIR


def test_cases_path(distribution_name: str) -> Path:
if distribution_name == "stdlib":
return Path(TEST_CASES_DIR)
else:
return tests_path(distribution_name) / TEST_CASES_DIR
return tests_path(distribution_name) / TEST_CASES_DIR


def get_all_testcase_directories() -> list[DistributionTests]:
Expand Down