pytest-revealtype-injector 0.7.0__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/PKG-INFO +20 -5
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/README.md +19 -4
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/__init__.py +1 -1
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/adapter/mypy_.py +6 -1
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/adapter/pyrefly_.py +29 -12
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/adapter/pyright_.py +10 -6
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/hooks.py +28 -5
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/main.py +6 -1
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/models.py +6 -1
- pytest_revealtype_injector-0.8.0/tests/test_marker.py +206 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/tests/test_options.py +1 -1
- pytest_revealtype_injector-0.7.0/tests/test_marker.py +0 -109
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/.gitignore +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/COPYING.mit +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/pyproject.toml +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/adapter/__init__.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/adapter/basedpyright_.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/adapter/ty_.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/log.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/plugin.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/src/pytest_revealtype_injector/py.typed +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/tests/conftest.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/tests/test_ast_mode.py +0 -0
- {pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/tests/test_import.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-revealtype-injector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Pytest plugin for replacing reveal_type() calls inside test functions with static and runtime type checking result comparison, for confirming type annotation validity.
|
|
5
5
|
Project-URL: homepage, https://github.com/abelcheung/pytest-revealtype-injector
|
|
6
6
|
Author-email: Abel Cheung <abelcheung@gmail.com>
|
|
@@ -34,12 +34,17 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
|
|
35
35
|
`pytest-revealtype-injector` is a `pytest` plugin for replacing [`reveal_type()`](https://docs.python.org/3/library/typing.html#typing.reveal_type) calls inside test functions as something more sophisticated. It does the following tasks in parallel:
|
|
36
36
|
|
|
37
|
-
- Launch external static type checkers
|
|
37
|
+
- Launch external static type checkers and store `reveal_type` results.
|
|
38
38
|
- Use [`typeguard`](https://github.com/agronholm/typeguard) to verify the aforementioned static type checker result _really_ matches runtime code result.
|
|
39
39
|
|
|
40
40
|
## Usage
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
TL;DR:
|
|
43
|
+
|
|
44
|
+
1. Install this plugin
|
|
45
|
+
2. Install type checkers: `basedpyright`, `mypy`, `pyrefly`, `pyright`, `ty`
|
|
46
|
+
- Disable any unwanted with `--revealtype-disable-adapter=<ADAPTER>` pytest CLI option
|
|
47
|
+
3. Create `pytest` functions which call `reveal_type()` with variable or function return result
|
|
43
48
|
|
|
44
49
|
### The longer story
|
|
45
50
|
|
|
@@ -78,9 +83,11 @@ import typing as typ # or...
|
|
|
78
83
|
from typing import reveal_type as rt
|
|
79
84
|
```
|
|
80
85
|
|
|
86
|
+
To supply config file specific for certain type checker, use `--revealtype-<ADAPTER>-config=<FILE>` pytest CLI option. For example, `--revealtype-pyrefly-config=tests/pyrefly.toml` instructs pyrefly to use `pyrefly.toml` under `tests` folder to override project root config.
|
|
87
|
+
|
|
81
88
|
### Limitations
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
There are 3 caveats.
|
|
84
91
|
|
|
85
92
|
1. This plugin only searches for global import in test files, so local import inside test function doesn't work. That means following code doesn't utilize this plugin at all:
|
|
86
93
|
|
|
@@ -125,7 +132,15 @@ Module level:
|
|
|
125
132
|
pytestmark = pytest.mark.notypechecker("basedpyright", "pyright")
|
|
126
133
|
```
|
|
127
134
|
|
|
128
|
-
|
|
135
|
+
Conversely, it is possible to only turn on usage of specific type checkers with `onlytypechecker` marker and exclude all others:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
@pytest.mark.onlytypechecker("mypy")
|
|
139
|
+
def test_for_mypy() -> None:
|
|
140
|
+
......
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Note that disabling all type checkers is disallowed, and such tests would be treated as failure. Disable the `reveal_type()` call instead.
|
|
129
144
|
|
|
130
145
|
## Logging
|
|
131
146
|
|
|
@@ -5,12 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
`pytest-revealtype-injector` is a `pytest` plugin for replacing [`reveal_type()`](https://docs.python.org/3/library/typing.html#typing.reveal_type) calls inside test functions as something more sophisticated. It does the following tasks in parallel:
|
|
7
7
|
|
|
8
|
-
- Launch external static type checkers
|
|
8
|
+
- Launch external static type checkers and store `reveal_type` results.
|
|
9
9
|
- Use [`typeguard`](https://github.com/agronholm/typeguard) to verify the aforementioned static type checker result _really_ matches runtime code result.
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
TL;DR:
|
|
14
|
+
|
|
15
|
+
1. Install this plugin
|
|
16
|
+
2. Install type checkers: `basedpyright`, `mypy`, `pyrefly`, `pyright`, `ty`
|
|
17
|
+
- Disable any unwanted with `--revealtype-disable-adapter=<ADAPTER>` pytest CLI option
|
|
18
|
+
3. Create `pytest` functions which call `reveal_type()` with variable or function return result
|
|
14
19
|
|
|
15
20
|
### The longer story
|
|
16
21
|
|
|
@@ -49,9 +54,11 @@ import typing as typ # or...
|
|
|
49
54
|
from typing import reveal_type as rt
|
|
50
55
|
```
|
|
51
56
|
|
|
57
|
+
To supply config file specific for certain type checker, use `--revealtype-<ADAPTER>-config=<FILE>` pytest CLI option. For example, `--revealtype-pyrefly-config=tests/pyrefly.toml` instructs pyrefly to use `pyrefly.toml` under `tests` folder to override project root config.
|
|
58
|
+
|
|
52
59
|
### Limitations
|
|
53
60
|
|
|
54
|
-
|
|
61
|
+
There are 3 caveats.
|
|
55
62
|
|
|
56
63
|
1. This plugin only searches for global import in test files, so local import inside test function doesn't work. That means following code doesn't utilize this plugin at all:
|
|
57
64
|
|
|
@@ -96,7 +103,15 @@ Module level:
|
|
|
96
103
|
pytestmark = pytest.mark.notypechecker("basedpyright", "pyright")
|
|
97
104
|
```
|
|
98
105
|
|
|
99
|
-
|
|
106
|
+
Conversely, it is possible to only turn on usage of specific type checkers with `onlytypechecker` marker and exclude all others:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
@pytest.mark.onlytypechecker("mypy")
|
|
110
|
+
def test_for_mypy() -> None:
|
|
111
|
+
......
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Note that disabling all type checkers is disallowed, and such tests would be treated as failure. Disable the `reveal_type()` call instead.
|
|
100
115
|
|
|
101
116
|
## Logging
|
|
102
117
|
|
|
@@ -5,11 +5,11 @@ import importlib
|
|
|
5
5
|
import json
|
|
6
6
|
import pathlib
|
|
7
7
|
import re
|
|
8
|
+
import sys
|
|
8
9
|
from collections.abc import (
|
|
9
10
|
Iterable,
|
|
10
11
|
)
|
|
11
12
|
from typing import (
|
|
12
|
-
ForwardRef,
|
|
13
13
|
Literal,
|
|
14
14
|
TypedDict,
|
|
15
15
|
cast,
|
|
@@ -27,6 +27,11 @@ from ..models import (
|
|
|
27
27
|
VarType,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
if sys.version_info >= (3, 14):
|
|
31
|
+
from annotationlib import ForwardRef
|
|
32
|
+
else:
|
|
33
|
+
from typing import ForwardRef
|
|
34
|
+
|
|
30
35
|
_logger = get_logger()
|
|
31
36
|
|
|
32
37
|
|
|
@@ -8,12 +8,11 @@ import shutil
|
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
10
|
from collections.abc import Iterable
|
|
11
|
-
from typing import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from typing_extensions import TypedDict
|
|
11
|
+
from typing import (
|
|
12
|
+
Literal,
|
|
13
|
+
TypedDict,
|
|
14
|
+
cast,
|
|
15
|
+
)
|
|
17
16
|
|
|
18
17
|
import schema as s
|
|
19
18
|
|
|
@@ -26,6 +25,16 @@ from ..models import (
|
|
|
26
25
|
VarType,
|
|
27
26
|
)
|
|
28
27
|
|
|
28
|
+
if sys.version_info >= (3, 11):
|
|
29
|
+
from typing import TypedDict
|
|
30
|
+
else:
|
|
31
|
+
from typing_extensions import TypedDict
|
|
32
|
+
|
|
33
|
+
if sys.version_info >= (3, 14):
|
|
34
|
+
from annotationlib import ForwardRef
|
|
35
|
+
else:
|
|
36
|
+
from typing import ForwardRef
|
|
37
|
+
|
|
29
38
|
_logger = get_logger()
|
|
30
39
|
|
|
31
40
|
|
|
@@ -53,15 +62,16 @@ class NameCollector(BareNameCollector):
|
|
|
53
62
|
# resolves to a pytest function or method
|
|
54
63
|
return ast.Name(id=node.attr, ctx=node.ctx)
|
|
55
64
|
|
|
65
|
+
|
|
56
66
|
class PyreflyAdapter(TypeCheckerAdapter):
|
|
57
67
|
id = "pyrefly"
|
|
58
68
|
_executable = "pyrefly"
|
|
59
|
-
_type_mesg_re = re.compile(r
|
|
69
|
+
_type_mesg_re = re.compile(r"revealed type: (?P<type>.+)")
|
|
60
70
|
_namecollector_class = NameCollector
|
|
61
71
|
_schema = s.Schema({
|
|
62
72
|
"line": int,
|
|
63
73
|
"column": int,
|
|
64
|
-
"stop_line": int,
|
|
74
|
+
"stop_line": int,
|
|
65
75
|
"stop_column": int,
|
|
66
76
|
"path": str,
|
|
67
77
|
"code": int,
|
|
@@ -79,8 +89,8 @@ class PyreflyAdapter(TypeCheckerAdapter):
|
|
|
79
89
|
raise FileNotFoundError(f"{self._executable} is required to run test suite")
|
|
80
90
|
|
|
81
91
|
cmd.extend(["check", "--output-format", "json"])
|
|
82
|
-
if
|
|
83
|
-
cmd.extend(["
|
|
92
|
+
if self.config_file is not None:
|
|
93
|
+
cmd.extend(["-c", str(self.config_file)])
|
|
84
94
|
cmd.extend(str(p) for p in paths)
|
|
85
95
|
|
|
86
96
|
_logger.debug(f"({self.id}) Run command: {cmd}")
|
|
@@ -90,12 +100,19 @@ class PyreflyAdapter(TypeCheckerAdapter):
|
|
|
90
100
|
# of exit status
|
|
91
101
|
if proc.returncode > 0:
|
|
92
102
|
raise TypeCheckerError(
|
|
93
|
-
"{} error with exit code {}: {}".format(
|
|
103
|
+
"{} error with exit code {}: {}".format(
|
|
104
|
+
self.id, proc.returncode, proc.stderr.decode()
|
|
105
|
+
),
|
|
106
|
+
None,
|
|
107
|
+
None,
|
|
108
|
+
)
|
|
94
109
|
|
|
95
110
|
try:
|
|
96
111
|
report = json.loads(proc.stdout)
|
|
97
112
|
except Exception as e:
|
|
98
|
-
raise TypeCheckerError(
|
|
113
|
+
raise TypeCheckerError(
|
|
114
|
+
f"Failed to parse pyrefly JSON output: {e}", None, None
|
|
115
|
+
) from e
|
|
99
116
|
|
|
100
117
|
assert isinstance(report, dict) and "errors" in report
|
|
101
118
|
items = cast(list[_PyreflyDiagItem], report["errors"])
|
|
@@ -10,16 +10,10 @@ from collections.abc import (
|
|
|
10
10
|
Iterable,
|
|
11
11
|
)
|
|
12
12
|
from typing import (
|
|
13
|
-
ForwardRef,
|
|
14
13
|
Literal,
|
|
15
14
|
cast,
|
|
16
15
|
)
|
|
17
16
|
|
|
18
|
-
if sys.version_info >= (3, 11):
|
|
19
|
-
from typing import NotRequired, TypedDict
|
|
20
|
-
else:
|
|
21
|
-
from typing_extensions import NotRequired, TypedDict
|
|
22
|
-
|
|
23
17
|
import schema as s
|
|
24
18
|
|
|
25
19
|
from ..log import get_logger
|
|
@@ -31,6 +25,16 @@ from ..models import (
|
|
|
31
25
|
VarType,
|
|
32
26
|
)
|
|
33
27
|
|
|
28
|
+
if sys.version_info >= (3, 11):
|
|
29
|
+
from typing import NotRequired, TypedDict
|
|
30
|
+
else:
|
|
31
|
+
from typing_extensions import NotRequired, TypedDict
|
|
32
|
+
|
|
33
|
+
if sys.version_info >= (3, 14):
|
|
34
|
+
from annotationlib import ForwardRef
|
|
35
|
+
else:
|
|
36
|
+
from typing import ForwardRef
|
|
37
|
+
|
|
34
38
|
_logger = get_logger()
|
|
35
39
|
|
|
36
40
|
|
|
@@ -20,20 +20,36 @@ def pytest_pyfunc_call(pyfuncitem: pytest.Function) -> Iterator[None]:
|
|
|
20
20
|
assert pyfuncitem.module is not None
|
|
21
21
|
adp_stash = pyfuncitem.config.stash[adapter_stash_key]
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
# Marker-based adapter filtering
|
|
24
|
+
notype_mark = pyfuncitem.get_closest_marker("notypechecker")
|
|
25
|
+
only_mark = pyfuncitem.get_closest_marker("onlytypechecker")
|
|
26
|
+
|
|
27
|
+
if notype_mark and only_mark:
|
|
28
|
+
pytest.fail(
|
|
29
|
+
"Cannot use both 'notypechecker' and 'onlytypechecker' markers on the same test."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if only_mark:
|
|
33
|
+
enabled_adapters = {a.id for a in adp_stash if a.id in only_mark.args}
|
|
34
|
+
for a in enabled_adapters:
|
|
35
|
+
_logger.info(
|
|
36
|
+
f"{a} adapter enabled by 'onlytypechecker' marker in {pyfuncitem.name} test"
|
|
37
|
+
)
|
|
38
|
+
adapters = {a for a in adp_stash if a.id in enabled_adapters}
|
|
39
|
+
|
|
40
|
+
elif notype_mark:
|
|
41
|
+
disabled_adapters = {a.id for a in adp_stash if a.id in notype_mark.args}
|
|
27
42
|
for a in disabled_adapters:
|
|
28
43
|
_logger.info(
|
|
29
44
|
f"{a} adapter disabled by 'notypechecker' marker in {pyfuncitem.name} test"
|
|
30
45
|
)
|
|
31
46
|
adapters = {a for a in adp_stash if a.id not in disabled_adapters}
|
|
47
|
+
|
|
32
48
|
else:
|
|
33
49
|
adapters = {a for a in adp_stash}
|
|
34
50
|
|
|
35
51
|
if not adapters:
|
|
36
|
-
pytest.fail("
|
|
52
|
+
pytest.fail("No type checker is enabled.")
|
|
37
53
|
|
|
38
54
|
# Monkeypatch reveal_type() with our own function, to guarantee
|
|
39
55
|
# each test func can receive different adapters
|
|
@@ -139,3 +155,10 @@ def pytest_configure(config: pytest.Config) -> None:
|
|
|
139
155
|
"specified type checker(s). Following type checkers are supported: "
|
|
140
156
|
+ ", ".join(all_ids),
|
|
141
157
|
)
|
|
158
|
+
# Marker to enable only specified adapters for a test
|
|
159
|
+
config.addinivalue_line(
|
|
160
|
+
"markers",
|
|
161
|
+
"onlytypechecker(name, ...): mark reveal_type() test to enable "
|
|
162
|
+
"only the specified type checker(s). Following type checkers are supported: "
|
|
163
|
+
+ ", ".join(all_ids),
|
|
164
|
+
)
|
|
@@ -4,7 +4,6 @@ import pathlib
|
|
|
4
4
|
import sys
|
|
5
5
|
from typing import (
|
|
6
6
|
Any,
|
|
7
|
-
ForwardRef,
|
|
8
7
|
TypeVar,
|
|
9
8
|
)
|
|
10
9
|
|
|
@@ -22,6 +21,12 @@ from .models import (
|
|
|
22
21
|
VarType,
|
|
23
22
|
)
|
|
24
23
|
|
|
24
|
+
if sys.version_info >= (3, 14):
|
|
25
|
+
from annotationlib import ForwardRef
|
|
26
|
+
else:
|
|
27
|
+
from typing import ForwardRef
|
|
28
|
+
|
|
29
|
+
|
|
25
30
|
_T = TypeVar("_T")
|
|
26
31
|
_logger = log.get_logger()
|
|
27
32
|
|
|
@@ -5,11 +5,11 @@ import ast
|
|
|
5
5
|
import importlib
|
|
6
6
|
import pathlib
|
|
7
7
|
import re
|
|
8
|
+
import sys
|
|
8
9
|
from collections.abc import Iterable
|
|
9
10
|
from typing import (
|
|
10
11
|
Any,
|
|
11
12
|
ClassVar,
|
|
12
|
-
ForwardRef,
|
|
13
13
|
NamedTuple,
|
|
14
14
|
TypeVar,
|
|
15
15
|
cast,
|
|
@@ -21,6 +21,11 @@ from schema import Schema
|
|
|
21
21
|
|
|
22
22
|
from .log import get_logger
|
|
23
23
|
|
|
24
|
+
if sys.version_info >= (3, 14):
|
|
25
|
+
from annotationlib import ForwardRef
|
|
26
|
+
else:
|
|
27
|
+
from typing import ForwardRef
|
|
28
|
+
|
|
24
29
|
|
|
25
30
|
class FilePos(NamedTuple):
|
|
26
31
|
file: str
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import textwrap
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestOnlyTypeCheckerMarker:
|
|
9
|
+
PYPROJECT_TOML = """
|
|
10
|
+
[tool.pyright]
|
|
11
|
+
reportUnreachable = false
|
|
12
|
+
"""
|
|
13
|
+
TEST_CONTENT = """
|
|
14
|
+
import sys
|
|
15
|
+
import pytest
|
|
16
|
+
from typing import cast
|
|
17
|
+
if sys.version_info >= (3, 11):
|
|
18
|
+
from typing import reveal_type
|
|
19
|
+
else:
|
|
20
|
+
from typing_extensions import reveal_type
|
|
21
|
+
|
|
22
|
+
# GLOBAL MARK
|
|
23
|
+
|
|
24
|
+
def test_foo() -> None:
|
|
25
|
+
MYPY = False
|
|
26
|
+
if MYPY:
|
|
27
|
+
x = 1
|
|
28
|
+
else:
|
|
29
|
+
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
30
|
+
reveal_type(x)
|
|
31
|
+
|
|
32
|
+
# CLASS MARK
|
|
33
|
+
class TestFoo:
|
|
34
|
+
# FUNC MARK
|
|
35
|
+
def test_foo(self) -> None:
|
|
36
|
+
MYPY = False
|
|
37
|
+
if MYPY:
|
|
38
|
+
x = 1
|
|
39
|
+
else:
|
|
40
|
+
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
41
|
+
reveal_type(x)
|
|
42
|
+
|
|
43
|
+
def test_foo2(self) -> None:
|
|
44
|
+
MYPY = False
|
|
45
|
+
if MYPY:
|
|
46
|
+
x = 1
|
|
47
|
+
else:
|
|
48
|
+
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
49
|
+
reveal_type(x)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def test_vanilla(self, pytester: pytest.Pytester) -> None:
|
|
53
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
54
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
55
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
56
|
+
self.TEST_CONTENT
|
|
57
|
+
)
|
|
58
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
59
|
+
result.assert_outcomes(passed=0, failed=3)
|
|
60
|
+
|
|
61
|
+
def test_function_marker(self, pytester: pytest.Pytester) -> None:
|
|
62
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
63
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
64
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
65
|
+
self.TEST_CONTENT.replace("# FUNC MARK", "@pytest.mark.onlytypechecker('mypy')")
|
|
66
|
+
)
|
|
67
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
68
|
+
result.assert_outcomes(passed=1, failed=2)
|
|
69
|
+
|
|
70
|
+
def test_class_marker(self, pytester: pytest.Pytester) -> None:
|
|
71
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
72
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
73
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
74
|
+
self.TEST_CONTENT.replace("# CLASS MARK", "@pytest.mark.onlytypechecker('mypy')")
|
|
75
|
+
)
|
|
76
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
77
|
+
result.assert_outcomes(passed=2, failed=1)
|
|
78
|
+
|
|
79
|
+
def test_global_marker(self, pytester: pytest.Pytester) -> None:
|
|
80
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
81
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
82
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
83
|
+
self.TEST_CONTENT.replace("# GLOBAL MARK", "pytestmark = pytest.mark.onlytypechecker('mypy')")
|
|
84
|
+
)
|
|
85
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
86
|
+
result.assert_outcomes(passed=3, failed=0)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TestNoTypeCheckerMarker:
|
|
90
|
+
PYPROJECT_TOML = """
|
|
91
|
+
[tool.pyright]
|
|
92
|
+
reportUnreachable = false
|
|
93
|
+
"""
|
|
94
|
+
TEST_CONTENT = """
|
|
95
|
+
import sys
|
|
96
|
+
import pytest
|
|
97
|
+
from typing import cast
|
|
98
|
+
if sys.version_info >= (3, 11):
|
|
99
|
+
from typing import reveal_type
|
|
100
|
+
else:
|
|
101
|
+
from typing_extensions import reveal_type
|
|
102
|
+
|
|
103
|
+
# GLOBAL MARK
|
|
104
|
+
|
|
105
|
+
def test_foo() -> None:
|
|
106
|
+
MYPY = False
|
|
107
|
+
if MYPY:
|
|
108
|
+
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
109
|
+
else:
|
|
110
|
+
x = 1
|
|
111
|
+
reveal_type(x)
|
|
112
|
+
|
|
113
|
+
# CLASS MARK
|
|
114
|
+
class TestFoo:
|
|
115
|
+
# FUNC MARK
|
|
116
|
+
def test_foo(self) -> None:
|
|
117
|
+
MYPY = False
|
|
118
|
+
if MYPY:
|
|
119
|
+
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
120
|
+
else:
|
|
121
|
+
x = 1
|
|
122
|
+
reveal_type(x)
|
|
123
|
+
|
|
124
|
+
def test_foo2(self) -> None:
|
|
125
|
+
MYPY = False
|
|
126
|
+
if MYPY:
|
|
127
|
+
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
128
|
+
else:
|
|
129
|
+
x = 1
|
|
130
|
+
reveal_type(x)
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def test_vanilla(self, pytester: pytest.Pytester) -> None:
|
|
134
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
135
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
136
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
137
|
+
self.TEST_CONTENT
|
|
138
|
+
)
|
|
139
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
140
|
+
result.assert_outcomes(passed=0, failed=3)
|
|
141
|
+
|
|
142
|
+
def test_function_marker(self, pytester: pytest.Pytester) -> None:
|
|
143
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
144
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
145
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
146
|
+
self.TEST_CONTENT.replace("# FUNC MARK", "@pytest.mark.notypechecker('mypy')")
|
|
147
|
+
)
|
|
148
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
149
|
+
result.assert_outcomes(passed=1, failed=2)
|
|
150
|
+
|
|
151
|
+
def test_class_marker(self, pytester: pytest.Pytester) -> None:
|
|
152
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
153
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
154
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
155
|
+
self.TEST_CONTENT.replace("# CLASS MARK", "@pytest.mark.notypechecker('mypy')")
|
|
156
|
+
)
|
|
157
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
158
|
+
result.assert_outcomes(passed=2, failed=1)
|
|
159
|
+
|
|
160
|
+
def test_global_marker(self, pytester: pytest.Pytester) -> None:
|
|
161
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
162
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
163
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
164
|
+
self.TEST_CONTENT.replace("# GLOBAL MARK", "pytestmark = pytest.mark.notypechecker('mypy')")
|
|
165
|
+
)
|
|
166
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
167
|
+
result.assert_outcomes(passed=3, failed=0)
|
|
168
|
+
|
|
169
|
+
class TestMarkerConflicts:
|
|
170
|
+
PYPROJECT_TOML = """
|
|
171
|
+
[tool.pyright]
|
|
172
|
+
reportUnreachable = false
|
|
173
|
+
"""
|
|
174
|
+
TEST_CONTENT = """
|
|
175
|
+
import pytest
|
|
176
|
+
|
|
177
|
+
# PLACEHOLDER
|
|
178
|
+
def test_foo() -> None:
|
|
179
|
+
pass
|
|
180
|
+
"""
|
|
181
|
+
def test_typechecker_exclusive(self, pytester: pytest.Pytester) -> None:
|
|
182
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
183
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
184
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
185
|
+
textwrap.dedent(self.TEST_CONTENT).replace(
|
|
186
|
+
"# PLACEHOLDER",
|
|
187
|
+
"@pytest.mark.notypechecker('mypy')\n"
|
|
188
|
+
"@pytest.mark.onlytypechecker('pyright')"
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
192
|
+
assert result.ret == pytest.ExitCode.INTERNAL_ERROR
|
|
193
|
+
result.assert_outcomes(passed=0, failed=0)
|
|
194
|
+
|
|
195
|
+
def test_no_typechecker_left(self, pytester: pytest.Pytester) -> None:
|
|
196
|
+
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
197
|
+
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
198
|
+
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
199
|
+
self.TEST_CONTENT.replace(
|
|
200
|
+
"# PLACEHOLDER",
|
|
201
|
+
"@pytest.mark.onlytypechecker()"
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
result = pytester.runpytest("--tb=short", "-vv")
|
|
205
|
+
assert result.ret == pytest.ExitCode.INTERNAL_ERROR
|
|
206
|
+
result.assert_outcomes(passed=0, failed=0)
|
|
@@ -81,7 +81,7 @@ class TestDisableTypeChecker:
|
|
|
81
81
|
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
82
82
|
self.content_fail
|
|
83
83
|
)
|
|
84
|
-
opts = self._gen_pytest_opts(["basedpyright", "pyright"])
|
|
84
|
+
opts = self._gen_pytest_opts(["basedpyright", "pyright", "pyrefly", "ty"])
|
|
85
85
|
result = pytester.runpytest(*opts)
|
|
86
86
|
assert result.ret == pytest.ExitCode.INTERNAL_ERROR
|
|
87
87
|
result.assert_outcomes(passed=0, failed=0)
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class TestFuncAndGlobalMarker:
|
|
7
|
-
PYPROJECT_TOML = """
|
|
8
|
-
[tool.pyright]
|
|
9
|
-
reportUnreachable = false
|
|
10
|
-
defineConstant = {"MYPY" = false}
|
|
11
|
-
|
|
12
|
-
[tool.mypy]
|
|
13
|
-
always_true = ["MYPY"]
|
|
14
|
-
"""
|
|
15
|
-
TEST_CONTENT = """
|
|
16
|
-
import sys
|
|
17
|
-
import pytest
|
|
18
|
-
from typing import cast
|
|
19
|
-
if sys.version_info >= (3, 11):
|
|
20
|
-
from typing import reveal_type
|
|
21
|
-
else:
|
|
22
|
-
from typing_extensions import reveal_type
|
|
23
|
-
|
|
24
|
-
# pytestmark = pytest.mark.notypechecker('mypy')
|
|
25
|
-
|
|
26
|
-
# @pytest.mark.notypechecker('mypy')
|
|
27
|
-
def test_foo() -> None:
|
|
28
|
-
MYPY = False
|
|
29
|
-
if MYPY:
|
|
30
|
-
x = cast(str, 1) # type: ignore[assignment]
|
|
31
|
-
else:
|
|
32
|
-
x = 1
|
|
33
|
-
reveal_type(x)
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
def test_vanilla(self, pytester: pytest.Pytester) -> None:
|
|
37
|
-
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
38
|
-
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
39
|
-
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
40
|
-
self.TEST_CONTENT
|
|
41
|
-
)
|
|
42
|
-
result = pytester.runpytest("--tb=short", "-vv")
|
|
43
|
-
result.assert_outcomes(passed=0, failed=1)
|
|
44
|
-
|
|
45
|
-
def test_function_marker(self, pytester: pytest.Pytester) -> None:
|
|
46
|
-
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
47
|
-
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
48
|
-
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
49
|
-
self.TEST_CONTENT.replace("# @pytest.mark", "@pytest.mark")
|
|
50
|
-
)
|
|
51
|
-
result = pytester.runpytest("--tb=short", "-vv")
|
|
52
|
-
result.assert_outcomes(passed=1, failed=0)
|
|
53
|
-
|
|
54
|
-
def test_global_marker(self, pytester: pytest.Pytester) -> None:
|
|
55
|
-
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
56
|
-
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
57
|
-
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
58
|
-
self.TEST_CONTENT.replace("# pytestmark", "pytestmark")
|
|
59
|
-
)
|
|
60
|
-
result = pytester.runpytest("--tb=short", "-vv")
|
|
61
|
-
result.assert_outcomes(passed=1, failed=0)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class TestClassMarker:
|
|
65
|
-
PYPROJECT_TOML = """
|
|
66
|
-
[tool.pyright]
|
|
67
|
-
reportUnreachable = false
|
|
68
|
-
defineConstant = {"MYPY" = false}
|
|
69
|
-
|
|
70
|
-
[tool.mypy]
|
|
71
|
-
always_true = ["MYPY"]
|
|
72
|
-
"""
|
|
73
|
-
TEST_CONTENT = """
|
|
74
|
-
import sys
|
|
75
|
-
import pytest
|
|
76
|
-
from typing import cast
|
|
77
|
-
if sys.version_info >= (3, 11):
|
|
78
|
-
from typing import reveal_type
|
|
79
|
-
else:
|
|
80
|
-
from typing_extensions import reveal_type
|
|
81
|
-
|
|
82
|
-
# @pytest.mark.notypechecker('pyright', 'basedpyright', 'ty')
|
|
83
|
-
class TestFoo:
|
|
84
|
-
def test_foo(self) -> None:
|
|
85
|
-
MYPY = False
|
|
86
|
-
if MYPY:
|
|
87
|
-
x = 1
|
|
88
|
-
else:
|
|
89
|
-
x = cast(str, 1) # pyright: ignore[reportInvalidCast]
|
|
90
|
-
reveal_type(x)
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
|
-
def test_vanilla(self, pytester: pytest.Pytester) -> None:
|
|
94
|
-
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
95
|
-
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
96
|
-
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
97
|
-
self.TEST_CONTENT
|
|
98
|
-
)
|
|
99
|
-
result = pytester.runpytest("--tb=short", "-vv")
|
|
100
|
-
result.assert_outcomes(passed=0, failed=1)
|
|
101
|
-
|
|
102
|
-
def test_marker_applied(self, pytester: pytest.Pytester) -> None:
|
|
103
|
-
pytester.makeconftest("pytest_plugins = ['pytest_revealtype_injector.plugin']")
|
|
104
|
-
pytester.makepyprojecttoml(self.PYPROJECT_TOML)
|
|
105
|
-
pytester.makepyfile( # pyright: ignore[reportUnknownMemberType]
|
|
106
|
-
self.TEST_CONTENT.replace("# @pytest.mark", "@pytest.mark")
|
|
107
|
-
)
|
|
108
|
-
result = pytester.runpytest("--tb=short", "-vv")
|
|
109
|
-
result.assert_outcomes(passed=1, failed=0)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_revealtype_injector-0.7.0 → pytest_revealtype_injector-0.8.0}/tests/test_ast_mode.py
RENAMED
|
File without changes
|
|
File without changes
|