Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions Lib/test/libregrtest/mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ ignore_missing_imports = True

[mypy-test.*]
ignore_missing_imports = True
disable_error_code = attr-defined,var-annotated
27 changes: 18 additions & 9 deletions Lib/test/support/__init__.py
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revealing the existence of this module to mypy necessitated fixing a few typing-related things to do with this module

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import types
import unittest
import warnings
from collections.abc import Callable

from .testresult import get_test_runner

Expand Down Expand Up @@ -183,12 +184,12 @@ def get_attribute(obj, name):
else:
return attribute

verbose = 1 # Flag set to 0 by regrtest.py
use_resources = None # Flag set to [] by regrtest.py
max_memuse = 0 # Disable bigmem tests (they will still be run with
# small sizes, to make sure they work.)
verbose = 1 # Flag set to 0 by regrtest.py
use_resources: tuple[str, ...] | None = None # Flag set to a tuple by regrtest.py
max_memuse = 0 # Disable bigmem tests (they will still be run with
# small sizes, to make sure they work.)
real_max_memuse = 0
junit_xml_list = None # list of testsuite XML elements
junit_xml_list: list | None = None # list of testsuite XML elements
failfast = False

# _original_stdout is meant to hold stdout at the time regrtest began.
Expand Down Expand Up @@ -1325,6 +1326,18 @@ def flush_std_streams():
sys.stderr.flush()


class WarningsPrinter:
def __init__(self, func: Callable[[str], None]) -> None:
self.func = func
# bpo-39983: Store the original sys.stderr at Python startup to be able to
# log warnings even if sys.stderr is captured temporarily by a test.
self.orig_stderr = sys.stderr

def __call__(self, msg: str) -> None:
return self.func(msg)


@WarningsPrinter
Comment on lines +1329 to +1340
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mypy doesn't like assigning attributes to functions, unfortunately; it much prefers the idea of a custom callable class

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This is the solution you said you preferred in #109382 (comment) :-)

def print_warning(msg):
# bpo-45410: Explicitly flush stdout to keep logs in order
flush_std_streams()
Expand All @@ -1333,10 +1346,6 @@ def print_warning(msg):
print(f"Warning -- {line}", file=stream)
stream.flush()

# bpo-39983: Store the original sys.stderr at Python startup to be able to
# log warnings even if sys.stderr is captured temporarily by a test.
print_warning.orig_stderr = sys.stderr


# Flag used by saved_test_environment of test.libregrtest.save_env,
# to check if a test modified the environment. The flag should be set to False
Expand Down
59 changes: 59 additions & 0 deletions Tools/scripts/run_mypy_on_regrtest.py
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstinner: where do you think this script should go?

  1. Where I have it now?
  2. Inside Lib/test/libregrtest?
  3. Somewhere else?

Also: thoughts on the name? It's a bit long and clunky right now...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to put it in Tools/build/. I would be fine with Lib/test/libregrtest.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
Script to run mypy on Lib/test/libregrtest.

This script is necessary due to the fact that,
if you invoke mypy directly on anything inside the Lib/ directory,
it (amusingly) thinks that everything in the stdlib is being "shadowed"
by the modules inside `Lib/`.
"""

import argparse
import os
import tempfile
import shutil
import subprocess
from pathlib import Path
from typing import TypeAlias

ReturnCode: TypeAlias = int


def run_mypy_on_libregrtest(stdlib_dir: Path) -> ReturnCode:
stdlib_test_dir = stdlib_dir / "test"
# Copy `Lib/test/support/` into a tempdir and point MYPYPATH towards the tempdir,
# so that mypy can see the classes and functions defined in `Lib/test/support/`
with tempfile.TemporaryDirectory() as td:
td_path = Path(td)
(td_path / "test").mkdir()
shutil.copytree(stdlib_test_dir / "support", td_path / "test" / "support")
mypy_command = [
"mypy",
"--config-file",
"libregrtest/mypy.ini",
]
result = subprocess.run(
mypy_command, cwd=stdlib_test_dir, env=os.environ | {"MYPYPATH": td}
)
return result.returncode


def main() -> ReturnCode:
parser = argparse.ArgumentParser("Script to run mypy on Lib/test/regrtest/")
parser.add_argument(
"--stdlib-dir",
"-s",
type=Path,
required=True,
help="path to the Lib/ dir where the Python stdlib is located",
)
args = parser.parse_args()
stdlib_dir = args.stdlib_dir
if not (stdlib_dir.exists() and stdlib_dir.is_dir()):
parser.error(
"--stdlib-dir must point to a directory that exists on your filesystem!"
)
return run_mypy_on_libregrtest(args.stdlib_dir)


if __name__ == "__main__":
raise SystemExit(main())