-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[mypyc] Add mypyc run tests for singledispatch (#10589)
Add some run tests to run-singledispatch.test for situations that are likely to be common or are likely to be edge cases that we don't handle correctly. Change the tests that were previously marked as skipped to xfails so that the tests still get run even if they aren't passing.
- Loading branch information
1 parent
6ab0efc
commit 9d92fba
Showing
2 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,387 @@ | ||
# Test cases related to the functools.singledispatch decorator | ||
# Most of these tests are marked as xfails because mypyc doesn't support singledispatch yet | ||
# (These tests will be re-enabled when mypyc supports singledispatch) | ||
|
||
[case testSpecializedImplementationUsed-xfail] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register | ||
def fun_specialized(arg: str) -> bool: | ||
return True | ||
|
||
def test_specialize() -> None: | ||
assert fun('a') | ||
assert not fun(3) | ||
|
||
[case testSubclassesOfExpectedTypeUseSpecialized-xfail] | ||
from functools import singledispatch | ||
class A: pass | ||
class B(A): pass | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register | ||
def fun_specialized(arg: A) -> bool: | ||
return True | ||
|
||
def test_specialize() -> None: | ||
assert fun(B()) | ||
assert fun(A()) | ||
|
||
[case testSuperclassImplementationNotUsedWhenSubclassHasImplementation-xfail] | ||
from functools import singledispatch | ||
class A: pass | ||
class B(A): pass | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
# shouldn't be using this | ||
assert False | ||
|
||
@fun.register | ||
def fun_specialized(arg: A) -> bool: | ||
return False | ||
|
||
@fun.register | ||
def fun_specialized2(arg: B) -> bool: | ||
return True | ||
|
||
def test_specialize() -> None: | ||
assert fun(B()) | ||
assert not fun(A()) | ||
|
||
[case testMultipleUnderscoreFunctionsIsntError-xfail] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> str: | ||
return 'default' | ||
|
||
@fun.register | ||
def _(arg: str) -> str: | ||
return 'str' | ||
|
||
@fun.register | ||
def _(arg: int) -> str: | ||
return 'int' | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(0) == 'int' | ||
assert fun('a') == 'str' | ||
assert fun({'a': 'b'}) == 'default' | ||
|
||
[case testCanRegisterCompiledClasses-xfail] | ||
from functools import singledispatch | ||
class A: pass | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
@fun.register | ||
def fun_specialized(arg: A) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(A()) | ||
assert not fun(1) | ||
|
||
[case testTypeUsedAsArgumentToRegister] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register(int) | ||
def fun_specialized(arg) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(1) | ||
assert not fun('a') | ||
|
||
[case testUseRegisterAsAFunction] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
def fun_specialized_impl(arg) -> bool: | ||
return True | ||
|
||
fun.register(int, fun_specialized_impl) | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(0) | ||
assert not fun('a') | ||
|
||
[case testRegisterDoesntChangeFunction-xfail] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register | ||
def fun_specialized(arg: int) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert fun_specialized('a') | ||
|
||
[case testTypeAnnotationsDisagreeWithRegisterArgument-xfail] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register(int) | ||
def fun_specialized(arg: str) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(3) # type: ignore | ||
assert not fun('a') | ||
|
||
[case testNoneIsntATypeWhenUsedAsArgumentToRegister-xfail] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
try: | ||
@fun.register(None) | ||
def fun_specialized(arg) -> bool: | ||
return True | ||
except TypeError: | ||
pass | ||
|
||
[case testRegisteringTheSameFunctionSeveralTimes] | ||
from functools import singledispatch | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register(int) | ||
@fun.register(str) | ||
def fun_specialized(arg) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(0) | ||
assert fun('a') | ||
assert not fun([1, 2]) | ||
|
||
[case testTypeIsAnABC-xfail] | ||
from functools import singledispatch | ||
from collections.abc import Mapping | ||
|
||
@singledispatch | ||
def fun(arg) -> bool: | ||
return False | ||
|
||
@fun.register | ||
def fun_specialized(arg: Mapping) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert not fun(1) | ||
assert fun({'a': 'b'}) | ||
|
||
[case testArgumentDoesntMatchTypeOfAnySpecializedImplementationsOrDefaultImplementation-xfail] | ||
from functools import singledispatch | ||
class A: pass | ||
class B(A): pass | ||
|
||
@singledispatch | ||
def fun(arg: A) -> bool: | ||
return False | ||
|
||
@fun.register | ||
def fun_specialized(arg: B) -> bool: | ||
return True | ||
|
||
def test_singledispatch() -> None: | ||
assert fun(B()) | ||
assert fun(A()) | ||
assert not fun([1, 2]) | ||
|
||
|
||
[case testSingleDispatchMethod-xfail] | ||
from functools import singledispatchmethod | ||
class A: | ||
@singledispatchmethod | ||
def fun(self, arg) -> str: | ||
return 'default' | ||
|
||
@fun.register | ||
def fun_int(self, arg: int) -> str: | ||
return 'int' | ||
|
||
@fun.register | ||
def fun_str(self, arg: str) -> str: | ||
return 'str' | ||
|
||
def test_singledispatchmethod() -> None: | ||
x = A() | ||
assert x.fun(5) == 'int' | ||
assert x.fun('a') == 'str' | ||
assert x.fun([1, 2]) == 'default' | ||
|
||
[case testSingleDispatchMethodWithOtherDecorator-xfail] | ||
from functools import singledispatchmethod | ||
class A: | ||
@singledispatchmethod | ||
@staticmethod | ||
def fun(arg) -> str: | ||
return 'default' | ||
|
||
@fun.register | ||
@staticmethod | ||
def fun_int(arg: int) -> str: | ||
return 'int' | ||
|
||
@fun.register | ||
@staticmethod | ||
def fun_str(arg: str) -> str: | ||
return 'str' | ||
|
||
def test_singledispatchmethod() -> None: | ||
x = A() | ||
assert x.fun(5) == 'int' | ||
assert x.fun('a') == 'str' | ||
assert x.fun([1, 2]) == 'default' | ||
|
||
[case testSingledispatchTreeSumAndEqual-xfail] | ||
from functools import singledispatch | ||
|
||
class Tree: | ||
pass | ||
class Leaf(Tree): | ||
pass | ||
class Node(Tree): | ||
def __init__(self, value: int, left: Tree, right: Tree) -> None: | ||
self.value = value | ||
self.left = left | ||
self.right = right | ||
|
||
@singledispatch | ||
def calc_sum(x: Tree) -> int: | ||
raise TypeError('invalid type for x') | ||
|
||
@calc_sum.register | ||
def _(x: Leaf) -> int: | ||
return 0 | ||
|
||
@calc_sum.register | ||
def _(x: Node) -> int: | ||
return x.value + calc_sum(x.left) + calc_sum(x.right) | ||
|
||
@singledispatch | ||
def equal(to_compare: Tree, known: Tree) -> bool: | ||
raise TypeError('invalid type for x') | ||
|
||
@equal.register | ||
def _(to_compare: Leaf, known: Tree) -> bool: | ||
return isinstance(known, Leaf) | ||
|
||
@equal.register | ||
def _(to_compare: Node, known: Tree) -> bool: | ||
if isinstance(known, Node): | ||
if to_compare.value != known.value: | ||
return False | ||
else: | ||
return equal(to_compare.left, known.left) and equal(to_compare.right, known.right) | ||
return False | ||
|
||
def build(n: int) -> Tree: | ||
if n == 0: | ||
return Leaf() | ||
return Node(n, build(n - 1), build(n - 1)) | ||
|
||
def test_sum_and_equal(): | ||
tree = build(5) | ||
tree2 = build(5) | ||
tree2.right.right.right.value = 10 | ||
assert calc_sum(tree) == 57 | ||
assert calc_sum(tree2) == 62 | ||
assert equal(tree, tree) | ||
assert not equal(tree, tree2) | ||
tree3 = build(4) | ||
assert not equal(tree, tree3) | ||
|
||
[case testSimulateMypySingledispatch-xfail] | ||
from functools import singledispatch | ||
from mypy_extensions import trait | ||
from typing import Iterator, Union, TypeVar, Any, List, Type | ||
# based on use of singledispatch in stubtest.py | ||
class Error: | ||
def __init__(self, msg: str) -> None: | ||
self.msg = msg | ||
|
||
@trait | ||
class Node: pass | ||
|
||
class MypyFile(Node): pass | ||
class TypeInfo(Node): pass | ||
|
||
|
||
class SymbolNode(Node): pass | ||
class Expression(Node): pass | ||
class TypeVarLikeExpr(SymbolNode, Expression): pass | ||
class TypeVarExpr(TypeVarLikeExpr): pass | ||
|
||
class Missing: pass | ||
MISSING = Missing() | ||
|
||
T = TypeVar("T") | ||
|
||
MaybeMissing = Union[T, Missing] | ||
|
||
@singledispatch | ||
def verify(stub: Node, a: MaybeMissing[Any], b: List[str]) -> Iterator[Error]: | ||
yield Error('unknown node type') | ||
|
||
@verify.register(MypyFile) | ||
def verify_mypyfile(stub: MypyFile, a: MaybeMissing[int], b: List[str]) -> Iterator[Error]: | ||
if isinstance(a, Missing): | ||
yield Error("shouldn't be missing") | ||
return | ||
if not isinstance(a, int): | ||
# this check should be unnecessary because of the type signature and the previous check, | ||
# but stubtest.py has this check | ||
yield Error("should be an int") | ||
return | ||
yield from verify(TypeInfo(), str, ['abc', 'def']) | ||
|
||
@verify.register(TypeInfo) | ||
def verify_typeinfo(stub: TypeInfo, a: MaybeMissing[Type[Any]], b: List[str]) -> Iterator[Error]: | ||
yield Error('in TypeInfo') | ||
yield Error('hello') | ||
|
||
@verify.register(TypeVarExpr) | ||
def verify_typevarexpr(stub: TypeVarExpr, a: MaybeMissing[Any], b: List[str]) -> Iterator[Error]: | ||
if False: | ||
yield None | ||
|
||
def verify_list(stub, a, b) -> List[str]: | ||
"""Helper function that converts iterator of errors to list of messages""" | ||
return list(err.msg for err in verify(stub, a, b)) | ||
|
||
def test_verify() -> None: | ||
assert verify_list(Node(), 'a', ['a', 'b']) == ['unknown node type'] | ||
assert verify_list(MypyFile(), 'a', ['a', 'b']) == ['should be an int'] | ||
assert verify_list(MypyFile(), MISSING, ['a', 'b']) == ["shouldn't be missing"] | ||
assert verify_list(MypyFile(), 5, ['a', 'b']) == ['in TypeInfo', 'hello'] | ||
assert verify_list(TypeInfo(), str, ['a', 'b']) == ['in TypeInfo', 'hello'] | ||
assert verify_list(TypeVarExpr(), 'a', ['x', 'y']) == ['x', 'y'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters