Skip to content

Commit

Permalink
[PEP 695] Add daemon tests that use new type parameter syntax (#17327)
Browse files Browse the repository at this point in the history
Add some basic mypy daemon test coverage, and make it possible to write
daemon tests that only work on recent Python versions.

Everything tested seems to work already.

Work on #15238.
  • Loading branch information
JukkaL authored Jun 4, 2024
1 parent c762191 commit 16d5aaf
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 3 deletions.
5 changes: 5 additions & 0 deletions mypy/test/testdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from __future__ import annotations

import os
import sys
from collections import defaultdict

import pytest

from mypy import build
from mypy.errors import CompileError
from mypy.modulefinder import BuildSource
Expand All @@ -28,6 +31,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
src = "\n".join(testcase.input)
dump_all = "# __dump_all__" in src
options = parse_options(src, testcase, incremental_step=1)
if options.python_version > sys.version_info:
pytest.skip("Test case requires a newer Python version")
options.use_builtins_fixtures = True
options.show_traceback = True
options.cache_dir = os.devnull
Expand Down
7 changes: 5 additions & 2 deletions mypy/test/testdiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from __future__ import annotations

import os
import sys

import pytest

from mypy import build
from mypy.defaults import PYTHON3_VERSION
from mypy.errors import CompileError
from mypy.modulefinder import BuildSource
from mypy.nodes import MypyFile
Expand All @@ -24,6 +26,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
files_dict = dict(testcase.files)
second_src = files_dict["tmp/next.py"]
options = parse_options(first_src, testcase, 1)
if options.python_version > sys.version_info:
pytest.skip("Test case requires a newer Python version")

messages1, files1 = self.build(first_src, options)
messages2, files2 = self.build(second_src, options)
Expand Down Expand Up @@ -53,7 +57,6 @@ def build(self, source: str, options: Options) -> tuple[list[str], dict[str, Myp
options.use_builtins_fixtures = True
options.show_traceback = True
options.cache_dir = os.devnull
options.python_version = PYTHON3_VERSION
options.allow_empty_bodies = True
try:
result = build.build(
Expand Down
4 changes: 4 additions & 0 deletions mypy/test/testfinegrained.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import os
import re
import sys
import unittest
from typing import Any

Expand Down Expand Up @@ -82,6 +83,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
f.write(main_src)

options = self.get_options(main_src, testcase, build_cache=False)
if options.python_version > sys.version_info:
pytest.skip("Test case requires a newer Python version")

build_options = self.get_options(main_src, testcase, build_cache=True)
server = Server(options, DEFAULT_STATUS_FILE)

Expand Down
1 change: 0 additions & 1 deletion mypy/traverser.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ def visit_match_stmt(self, o: MatchStmt) -> None:

def visit_type_alias_stmt(self, o: TypeAliasStmt) -> None:
o.name.accept(self)
# TODO: params
o.value.accept(self)

def visit_member_expr(self, o: MemberExpr) -> None:
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/deps.test
Original file line number Diff line number Diff line change
Expand Up @@ -1431,3 +1431,19 @@ class B(A):
<m.Z> -> m
<dataclasses.dataclass> -> m
<dataclasses> -> m

[case testPEP695TypeAliasDeps]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
from a import C, E
type A = C
type A2 = A
type A3 = E
[file a.py]
class C: pass
class D: pass
type E = D
[out]
<m.A> -> m
<a.C> -> m
<a.E> -> m
<a> -> m
99 changes: 99 additions & 0 deletions test-data/unit/diff.test
Original file line number Diff line number Diff line change
Expand Up @@ -1530,3 +1530,102 @@ class C:
[out]
__main__.C.get_by_team_and_id
__main__.Optional

[case testPEP695TypeAlias]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
from typing_extensions import TypeAlias, TypeAliasType
type A = int
type B = str
type C = int
D = int
E: TypeAlias = int
F = TypeAliasType("F", int)
G = TypeAliasType("G", int)
type H = int

[file next.py]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
from typing_extensions import TypeAlias, TypeAliasType
type A = str
type B = str
type C[T] = int
type D = int
type E = int
type F = int
type G = str
type H[T] = int

[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]
[out]
__main__.A
__main__.C
__main__.D
__main__.E
__main__.G
__main__.H

[case testPEP695TypeAlias2]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
type A[T: int] = list[T]
type B[T: int] = list[T]
type C[T: (int, str)] = list[T]
type D[T: (int, str)] = list[T]
type E[T: int] = list[T]
type F[T: (int, str)] = list[T]

[file next.py]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
type A[T] = list[T]
type B[T: str] = list[T]
type C[T: (int, None)] = list[T]
type D[T] = list[T]
type E[T: int] = list[T]
type F[T: (int, str)] = list[T]

[out]
__main__.A
__main__.B
__main__.C
__main__.D

[case testPEP695GenericFunction]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
def f[T](x: T) -> T:
return x
def g[T](x: T, y: T) -> T:
return x
[file next.py]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
def f[T](x: T) -> T:
return x
def g[T, S](x: T, y: S) -> S:
return y
[out]
__main__.g

[case testPEP695GenericClass]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
class C[T]:
pass
class D[T]:
pass
class E[T]:
pass
class F[T]:
def f(self, x: object) -> T: ...
[file next.py]
# flags: --enable-incomplete-feature=NewGenericSyntax --python-version=3.12
class C[T]:
pass
class D[T: int]:
pass
class E:
pass
class F[T]:
def f(self, x: T) -> T: ...
[out]
__main__.D
__main__.E
__main__.F
__main__.F.f
81 changes: 81 additions & 0 deletions test-data/unit/fine-grained-python312.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
[case testPEP695TypeAliasDep]
# flags: --enable-incomplete-feature=NewGenericSyntax
import m
def g() -> m.C:
return m.f()
[file m.py]
type C = int

def f() -> int:
pass
[file m.py.2]
type C = str

def f() -> int:
pass
[out]
==
main:4: error: Incompatible return value type (got "int", expected "str")

[case testPEP695ChangeOldStyleToNewStyleTypeAlias]
# flags: --enable-incomplete-feature=NewGenericSyntax
from m import A
A()

[file m.py]
A = int

[file m.py.2]
type A = int
[typing fixtures/typing-full.pyi]
[builtins fixtures/tuple.pyi]
[out]
==
main:3: error: "TypeAliasType" not callable

[case testPEP695VarianceChangesDueToDependency]
# flags: --enable-incomplete-feature=NewGenericSyntax
from a import C

x: C[object] = C[int]()

[file a.py]
from b import A

class C[T]:
def f(self) -> A[T]: ...

[file b.py]
class A[T]:
def f(self) -> T: ...

[file b.py.2]
class A[T]:
def f(self) -> list[T]: ...

[out]
==
main:4: error: Incompatible types in assignment (expression has type "C[int]", variable has type "C[object]")

[case testPEP695TypeAliasChangesDueToDependency]
# flags: --enable-incomplete-feature=NewGenericSyntax
from a import A
x: A
x = 0
x = ''

[file a.py]
from b import B
type A = B[int, str]

[file b.py]
from typing import Union as B

[file b.py.2]
from builtins import tuple as B

[builtins fixtures/tuple.pyi]
[out]
==
main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]")
main:5: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]")

0 comments on commit 16d5aaf

Please sign in to comment.