Skip to content

Commit cbf18f8

Browse files
Add conformance tests for PEP 702's @deprecated (#1822)
* Add conformance tests for PEP 702's `@deprecated` * Reorder testcases to avoid irrelevant errors by Pyright * Address PR reviews * Avoid irrelevant errors * Also rename "library" file * Add manual test results * Add missing punctuations, remove a note * Another period * Rerun on pyre * Update conformance/results/pyre/directives_reveal_type.toml * Add new `__call__()` testcase per PR review * Prefer "OK" to "no error" --------- Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 0e57675 commit cbf18f8

12 files changed

+272
-9
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
conformant = "Unsupported"
2+
notes = """
3+
Does not support @deprecated.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 15: Expected 1 errors
8+
Line 23: Expected 1 errors
9+
Line 24: Expected 1 errors
10+
Line 29: Expected 1 errors
11+
Line 40: Expected 1 errors
12+
Line 41: Expected 1 errors
13+
Line 43: Expected 1 errors
14+
Line 46: Expected 1 errors
15+
Line 47: Expected 1 errors
16+
Line 57: Expected 1 errors
17+
Line 87: Expected 1 errors
18+
"""
19+
output = """
20+
"""

conformance/results/mypy/version.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "mypy 1.11.0"
2-
test_duration = 1.0
2+
test_duration = 1.4
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
conformant = "Unsupported"
2+
notes = """
3+
Does not support @deprecated.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 15: Expected 1 errors
8+
Line 23: Expected 1 errors
9+
Line 24: Expected 1 errors
10+
Line 29: Expected 1 errors
11+
Line 40: Expected 1 errors
12+
Line 41: Expected 1 errors
13+
Line 43: Expected 1 errors
14+
Line 46: Expected 1 errors
15+
Line 47: Expected 1 errors
16+
Line 57: Expected 1 errors
17+
Line 87: Expected 1 errors
18+
"""
19+
output = """
20+
"""

conformance/results/pyre/directives_reveal_type.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
conformant = "Partial"
2-
notes = """"
2+
notes = """
33
Produces errors rather than warnings on `reveal_type`.
44
"""
55
output = """

conformance/results/pyre/version.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "pyre 0.9.22"
2-
test_duration = 2.5
2+
test_duration = 1.8
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
conformant = "Partial"
2+
notes = """
3+
Does not report error for deprecated magic methods.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 40: Expected 1 errors
8+
Line 41: Expected 1 errors
9+
Line 47: Expected 1 errors
10+
Line 33: Unexpected errors ['directives_deprecated.py:33:7 - error: The class "Ham" is deprecated']
11+
"""
12+
output = """
13+
directives_deprecated.py:15:44 - error: The class "Ham" is deprecated
14+
  Use Spam instead (reportDeprecated)
15+
directives_deprecated.py:23:9 - error: The function "norwegian_blue" is deprecated
16+
  It is pining for the fiords (reportDeprecated)
17+
directives_deprecated.py:24:13 - error: The function "norwegian_blue" is deprecated
18+
  It is pining for the fiords (reportDeprecated)
19+
directives_deprecated.py:29:9 - error: The function "foo" is deprecated
20+
  Only str will be allowed (reportDeprecated)
21+
directives_deprecated.py:33:7 - error: The class "Ham" is deprecated
22+
  Use Spam instead (reportDeprecated)
23+
directives_deprecated.py:43:6 - error: The getter for property "greasy" is deprecated
24+
  All spam will be equally greasy (reportDeprecated)
25+
directives_deprecated.py:46:6 - error: The setter for property "shape" is deprecated
26+
  Shapes are becoming immutable (reportDeprecated)
27+
directives_deprecated.py:57:9 - error: The function "lorem" is deprecated
28+
  Deprecated (reportDeprecated)
29+
directives_deprecated.py:87:13 - error: The method "foo" in class "Fooable" is deprecated
30+
  Deprecated (reportDeprecated)
31+
"""
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "pyright 1.1.373"
2-
test_duration = 1.4
2+
test_duration = 1.2
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
conformant = "Unsupported"
2+
notes = """
3+
Does not support @deprecated.
4+
"""
5+
conformance_automated = "Fail"
6+
errors_diff = """
7+
Line 23: Expected 1 errors
8+
Line 24: Expected 1 errors
9+
Line 29: Expected 1 errors
10+
Line 40: Expected 1 errors
11+
Line 41: Expected 1 errors
12+
Line 43: Expected 1 errors
13+
Line 46: Expected 1 errors
14+
Line 47: Expected 1 errors
15+
Line 57: Expected 1 errors
16+
Line 87: Expected 1 errors
17+
Line 16: Unexpected errors ['File "directives_deprecated.py", line 16, in <module>: Can\\'t find module \\'_directives_deprecated_library\\'. [import-error]']
18+
Line 18: Unexpected errors ['File "directives_deprecated.py", line 18, in <module>: typing_extensions.deprecated not supported yet [not-supported-yet]']
19+
Line 66: Unexpected errors ['File "directives_deprecated.py", line 66, in <module>: typing.override not supported yet [not-supported-yet]']
20+
"""
21+
output = """
22+
File "directives_deprecated.py", line 15, in <module>: Can't find module '_directives_deprecated_library'. [import-error]
23+
File "directives_deprecated.py", line 16, in <module>: Can't find module '_directives_deprecated_library'. [import-error]
24+
File "directives_deprecated.py", line 18, in <module>: typing_extensions.deprecated not supported yet [not-supported-yet]
25+
File "directives_deprecated.py", line 66, in <module>: typing.override not supported yet [not-supported-yet]
26+
"""
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
version = "pytype 2024.04.11"
2-
test_duration = 32.7
2+
test_duration = 30.0

conformance/results/results.html

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,16 @@ <h3>Python Type System Conformance Test Results</h3>
159159
<div class="table_container"><table><tbody>
160160
<tr><th class="col1">&nbsp;</th>
161161
<th class='tc-header'><div class='tc-name'>mypy 1.11.0</div>
162-
<div class='tc-time'>1.0sec</div>
162+
<div class='tc-time'>1.4sec</div>
163163
</th>
164164
<th class='tc-header'><div class='tc-name'>pyright 1.1.373</div>
165-
<div class='tc-time'>1.4sec</div>
165+
<div class='tc-time'>1.2sec</div>
166166
</th>
167167
<th class='tc-header'><div class='tc-name'>pyre 0.9.22</div>
168-
<div class='tc-time'>2.5sec</div>
168+
<div class='tc-time'>1.8sec</div>
169169
</th>
170170
<th class='tc-header'><div class='tc-name'>pytype 2024.04.11</div>
171-
<div class='tc-time'>32.7sec</div>
171+
<div class='tc-time'>30.0sec</div>
172172
</th>
173173
</tr>
174174
<tr><th class="column" colspan="5">
@@ -972,6 +972,12 @@ <h3>Python Type System Conformance Test Results</h3>
972972
<th class="column col2 conformant">Pass</th>
973973
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Does not reject a call to "cast" with additional arguments.</p></span></div></th>
974974
</tr>
975+
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;directives_deprecated</th>
976+
<th class="column col2 not-conformant"><div class="hover-text">Unsupported<span class="tooltip-text" id="bottom"><p>Does not support @deprecated.</p></span></div></th>
977+
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Does not report error for deprecated magic methods.</p></span></div></th>
978+
<th class="column col2 not-conformant"><div class="hover-text">Unsupported<span class="tooltip-text" id="bottom"><p>Does not support @deprecated.</p></span></div></th>
979+
<th class="column col2 not-conformant"><div class="hover-text">Unsupported<span class="tooltip-text" id="bottom"><p>Does not support @deprecated.</p></span></div></th>
980+
</tr>
975981
<tr><th class="column col1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;directives_no_type_check</th>
976982
<th class="column col2 partially-conformant"><div class="hover-text">Partial<span class="tooltip-text" id="bottom"><p>Does not honor `@no_type_check` class decorator (allowed).</p><p>Does not reject invalid call of `@no_type_check` function.</p></span></div></th>
977983
<th class="column col2 conformant"><div class="hover-text">Pass*<span class="tooltip-text" id="bottom"><p>Does not honor `@no_type_check` class decorator (allowed).</p></span></div></th>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
Support module for directive_deprecated.
3+
"""
4+
5+
from typing import Self, overload
6+
7+
from typing_extensions import deprecated
8+
9+
10+
@deprecated("Use Spam instead")
11+
class Ham: ...
12+
13+
14+
@deprecated("It is pining for the fiords")
15+
def norwegian_blue(x: int) -> int: ...
16+
17+
18+
@overload
19+
@deprecated("Only str will be allowed")
20+
def foo(x: int) -> str: ...
21+
22+
23+
@overload
24+
def foo(x: str) -> str: ...
25+
26+
27+
def foo(x: int | str) -> str: ...
28+
29+
30+
class Spam:
31+
32+
@deprecated("There is enough spam in the world")
33+
def __add__(self, other: object) -> Self: ...
34+
35+
@property
36+
@deprecated("All spam will be equally greasy")
37+
def greasy(self) -> float: ...
38+
39+
@property
40+
def shape(self) -> str: ...
41+
42+
@shape.setter
43+
@deprecated("Shapes are becoming immutable")
44+
def shape(self, value: str) -> None: ...
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""
2+
Tests the warnings.deprecated function.
3+
"""
4+
5+
# pyright: reportDeprecated=true
6+
7+
# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#deprecated
8+
# See also https://peps.python.org/pep-0702/
9+
10+
# > Type checkers should produce a diagnostic whenever they encounter a usage of an object
11+
# > marked as deprecated. [...] For deprecated classes and functions, this includes:
12+
13+
# > * `from` imports
14+
15+
from _directives_deprecated_library import Ham # E: Use of deprecated class Ham
16+
import _directives_deprecated_library as library
17+
18+
from typing_extensions import deprecated
19+
20+
21+
# > * References through module, class, or instance attributes
22+
23+
library.norwegian_blue(1) # E: Use of deprecated function norwegian_blue
24+
map(library.norwegian_blue, [1, 2, 3]) # E: Use of deprecated function norwegian_blue
25+
26+
27+
# > For deprecated overloads, this includes all calls that resolve to the deprecated overload.
28+
29+
library.foo(1) # E: Use of deprecated overload for foo
30+
library.foo("x") # OK
31+
32+
33+
ham = Ham() # OK (already reported above)
34+
35+
36+
# > * Any syntax that indirectly triggers a call to the function.
37+
38+
spam = library.Spam()
39+
40+
_ = spam + 1 # E: Use of deprecated method Spam.__add__
41+
spam += 1 # E: Use of deprecated method Spam.__add__
42+
43+
spam.greasy # E: Use of deprecated property Spam.greasy
44+
spam.shape # OK
45+
46+
spam.shape = "cube" # E: Use of deprecated property setter Spam.shape
47+
spam.shape += "cube" # E: Use of deprecated property setter Spam.shape
48+
49+
50+
class Invocable:
51+
52+
@deprecated("Deprecated")
53+
def __call__(self) -> None: ...
54+
55+
invocable = Invocable()
56+
invocable() # E: Use of deprecated method __call__
57+
58+
59+
# > * Any usage of deprecated objects in their defining module
60+
61+
@deprecated("Deprecated")
62+
def lorem() -> None: ...
63+
64+
65+
ipsum = lorem() # E: Use of deprecated function lorem
66+
67+
68+
# > There are additional scenarios where deprecations could come into play.
69+
# > For example, an object may implement a `typing.Protocol`,
70+
# > but one of the methods required for protocol compliance is deprecated.
71+
# > As scenarios such as this one appear complex and relatively unlikely to come up in practice,
72+
# > this PEP does not mandate that type checkers detect them.
73+
74+
from typing import Protocol, override
75+
76+
77+
class Fooable(Protocol):
78+
79+
@deprecated("Deprecated")
80+
def foo(self) -> None: ...
81+
82+
def bar(self) -> None: ...
83+
84+
85+
class Fooer(Fooable):
86+
87+
@override
88+
def foo(self) -> None: # E?: Implementation of deprecated method foo
89+
...
90+
91+
def bar(self) -> None: ...
92+
93+
94+
def foo_it(fooable: Fooable) -> None:
95+
fooable.foo() # E: Use of deprecated method foo
96+
fooable.bar()
97+
98+
99+
# https://github.com/python/typing/pull/1822#discussion_r1693991644
100+
101+
class Fooable2(Protocol):
102+
103+
def foo(self) -> None: ...
104+
105+
106+
class Concrete:
107+
108+
@deprecated("Deprecated")
109+
def foo(self) -> None: ...
110+
111+
112+
def take_fooable(f: Fooable2) -> None: ...
113+
114+
115+
def caller(c: Concrete) -> None:
116+
take_fooable(c) # E?: Concrete is a Fooable2, but only because of a deprecated method

0 commit comments

Comments
 (0)